From 614d3dfd62211e99c02650c150ba6d18f51dd0a1 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 11:28:47 +0200 Subject: [PATCH 01/21] Standardize assert --- mentos/CMakeLists.txt | 1 + mentos/inc/libc/assert.h | 14 ++------------ mentos/src/libc/assert.c | 25 ++++++++++--------------- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index bdbbfe9..fe6abb0 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -204,6 +204,7 @@ set(SOURCE_FILES src/kernel/sys.c + src/libc/assert.c src/libc/ctype.c src/libc/stdio.c src/libc/string.c diff --git a/mentos/inc/libc/assert.h b/mentos/inc/libc/assert.h index f5812e1..0148391 100644 --- a/mentos/inc/libc/assert.h +++ b/mentos/inc/libc/assert.h @@ -15,18 +15,8 @@ /// @param file The file where the assertion is located. /// @param line The line inside the file. /// @param function The function where the assertion is. -static void __assert_fail(const char *assertion, const char *file, - unsigned int line, const char *function) -{ - char message[1024]; - sprintf(message, - "FILE: %s\n" - "LINE: %d\n" - "FUNC: %s\n\n" - "Assertion `%s` failed.\n", - file, line, (function ? function : "NO_FUN"), assertion); - kernel_panic(message); -} +void __assert_fail(const char *assertion, const char *file, unsigned int line, + const char *function); /// @brief Assert function. #define assert(expression) \ diff --git a/mentos/src/libc/assert.c b/mentos/src/libc/assert.c index 87038b0..6e49f96 100644 --- a/mentos/src/libc/assert.c +++ b/mentos/src/libc/assert.c @@ -8,20 +8,15 @@ #include "stdio.h" #include "panic.h" -void __assert_fail(const char *assertion, - const char *file, - unsigned int line, - const char *function) +void __assert_fail(const char *assertion, const char *file, unsigned int line, + const char *function) { - char message[1024]; - sprintf(message, - "FILE: %s\n" - "LINE: %d\n" - "FUNC: %s\n\n" - "Assertion `%s` failed.\n", - file, - line, - (function ? function : "NO_FUN"), - assertion); - kernel_panic(message); + char message[1024]; + sprintf(message, + "FILE: %s\n" + "LINE: %d\n" + "FUNC: %s\n\n" + "Assertion `%s` failed.\n", + file, line, (function ? function : "NO_FUN"), assertion); + kernel_panic(message); } From 07bd4d79015029abba9ba5ef7d4e2cee08e76703 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 11:59:18 +0200 Subject: [PATCH 02/21] Improve atoi function, safeguard gets function from out-of-bound errors --- mentos/inc/libc/stdio.h | 2 ++ mentos/src/libc/stdio.c | 80 +++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/mentos/inc/libc/stdio.h b/mentos/inc/libc/stdio.h index 8300319..47be959 100644 --- a/mentos/inc/libc/stdio.h +++ b/mentos/inc/libc/stdio.h @@ -11,6 +11,8 @@ /// @brief The maximum number of digits of an integer. #define MAX_DIGITS_IN_INTEGER 11 +/// @brief The size of 'gets' buffer. +#define GETS_BUFFERSIZE 255 #ifndef EOF /// @brief Define the End-Of-File. diff --git a/mentos/src/libc/stdio.c b/mentos/src/libc/stdio.c index d665896..398e039 100644 --- a/mentos/src/libc/stdio.c +++ b/mentos/src/libc/stdio.c @@ -9,6 +9,7 @@ #include "keyboard.h" #include "unistd.h" #include "debug.h" +#include "assert.h" void putchar(int character) { @@ -41,65 +42,50 @@ int getchar(void) char *gets(char *str) { - int count = 0; - char tmp[255]; - memset(tmp, '\0', 255); - - do { - int c = getchar(); - - // Return Key. - if (c == '\n') { + // Check the input string. + if (str == NULL) + return NULL; + // Buffer for reading input. + char buffer[GETS_BUFFERSIZE]; + memset(buffer, '\0', GETS_BUFFERSIZE); + // Char pointer to the buffer. + char *cptr = buffer; + // Character storage and counter to prevent overflow. + int ch, counter = 0; + // Read until we find a newline or we exceed the buffer size. + while (((ch = getchar()) != '\n') && (counter++ < GETS_BUFFERSIZE)) { + // If we encounter EOF, stop. + if (ch == EOF) { + // EOF at start of line return NULL. + if (cptr == str) + return NULL; break; } - // Backspace key. - - if (c == '\b') { - if (count > 0) { - count--; - } - } else { - tmp[count++] = (char)c; - } - } while (count < 255); - - tmp[count] = '\0'; - /* tmp cant simply be returned, it is allocated in this stack frame and - * it will be lost! - */ - strcpy(str, tmp); - + // The character is stored at address, and the pointer is incremented. + *cptr++ = ch; + } + // Add the null-terminating character. + *cptr = '\0'; + // Copy the string we have read. + strcpy(str, buffer); + // Return a pointer to the original string. return str; } int atoi(const char *str) { - // Initialize sign as positive. - int sign = 1; - + assert(str && "Received NULL string."); + // Initialize sign. + int sign = (str[0] == '-') ? -1 : +1; // Initialize the result. int result = 0; - - // Initialize the index. - int i = 0; - - // If the number is negative, then update the sign. - if (str[0] == '-') { - sign = -1; - } - // Check that the rest of the numbers are digits. - for (i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) { - if (!isdigit(str[i])) { + for (int i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) + if (!isdigit(str[i])) return -1; - } - } - // Iterate through all digits and update the result. - for (i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) { + for (int i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) result = (result * 10) + str[i] - '0'; - } - return sign * result; } @@ -129,7 +115,7 @@ size_t scanf(const char *format, ...) for (; *format; format++) { if (*format == '%') { // Declare an input string. - char input[255]; + char input[GETS_BUFFERSIZE]; // Get the input string. gets(input); // Evaluate the length of the string. From dbeed54f2bb7bf6c8bfb22482882896b9458fba1 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 12:00:03 +0200 Subject: [PATCH 03/21] Just return 0 when passing NULL string to atoi --- mentos/src/libc/stdio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mentos/src/libc/stdio.c b/mentos/src/libc/stdio.c index 398e039..7e083af 100644 --- a/mentos/src/libc/stdio.c +++ b/mentos/src/libc/stdio.c @@ -9,7 +9,6 @@ #include "keyboard.h" #include "unistd.h" #include "debug.h" -#include "assert.h" void putchar(int character) { @@ -74,7 +73,9 @@ char *gets(char *str) int atoi(const char *str) { - assert(str && "Received NULL string."); + // Check the input string. + if (str == NULL) + return 0; // Initialize sign. int sign = (str[0] == '-') ? -1 : +1; // Initialize the result. From 14d64d25d363bd59f05a73331a281a4f6898f3c7 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 12:01:10 +0200 Subject: [PATCH 04/21] Remove getchar old code --- mentos/src/libc/stdio.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mentos/src/libc/stdio.c b/mentos/src/libc/stdio.c index 7e083af..a1b246c 100644 --- a/mentos/src/libc/stdio.c +++ b/mentos/src/libc/stdio.c @@ -22,7 +22,6 @@ void puts(char *str) int getchar(void) { -#if 1 char c; while (true) { read(STDIN_FILENO, &c, 1); @@ -30,13 +29,6 @@ int getchar(void) break; } return c; -#else - int tmpchar; - while ((tmpchar = keyboard_getc()) == -1) - ; - - return tmpchar; -#endif } char *gets(char *str) From d9fd3b1646b1bd8d9724c62a7a0cc0bfb17f8e07 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 12:04:50 +0200 Subject: [PATCH 05/21] Clean math code --- mentos/src/libc/math.c | 79 ++++++++---------------------------------- 1 file changed, 14 insertions(+), 65 deletions(-) diff --git a/mentos/src/libc/math.c b/mentos/src/libc/math.c index bac3b1a..19ca124 100644 --- a/mentos/src/libc/math.c +++ b/mentos/src/libc/math.c @@ -10,58 +10,23 @@ double round(double x) { double out; - __asm__("fldln2; fldl %1; frndint" : "=t"(out) : "m"(x)); - + __asm__ __volatile__("fldln2; fldl %1; frndint" : "=t"(out) : "m"(x)); return out; } double floor(double x) { if (x > -1.0 && x < 1.0) { - if (x >= 0) { + if (x >= 0) return 0.0; - } else { - return -1.0; - } - } - - int x_i = (int)x; - - if (x < 0) { - return (double)(x_i - 1); - } else { - return (double)x_i; + return -1.0; } + int i = (int)x; + if (x < 0) + return (double)(i - 1); + return (double)i; } -#if 0 -double pow(double base, double ex) -{ - // Power of 0. - if (ex == 0) - { - return 1; - } - // Negative exponenet. - else if (ex < 0) - { - return 1 / pow(base, -ex); - } - // Even exponenet. - else if ((int) ex % 2 == 0) - { - float half_pow = pow(base, ex / 2); - - return half_pow * half_pow; - } - // Integer exponenet. - else - { - return base * pow(base, ex - 1); - } -} -#else - double pow(double x, double y) { double out; @@ -85,20 +50,13 @@ double pow(double x, double y) return out; } -#endif - long find_nearest_pow_greater(double base, double value) { - if (base <= 1) { + if (base <= 1) return -1; - } - long pow_value = 0; - - while (pow(base, pow_value) < value) { + while (pow(base, pow_value) < value) pow_value++; - } - return pow_value; } @@ -110,16 +68,14 @@ double exp(double x) double fabs(double x) { double out; - __asm__("fldln2; fldl %1; fabs" : "=t"(out) : "m"(x)); - + __asm__ __volatile__("fldln2; fldl %1; fabs" : "=t"(out) : "m"(x)); return out; } double sqrt(double x) { double out; - __asm__("fldln2; fldl %1; fsqrt" : "=t"(out) : "m"(x)); - + __asm__ __volatile__("fldln2; fldl %1; fsqrt" : "=t"(out) : "m"(x)); return out; } @@ -130,8 +86,7 @@ int isinf(double x) double f; } ieee754; ieee754.f = x; - - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && + return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U && ((unsigned)ieee754.u == 0); } @@ -142,7 +97,6 @@ int isnan(double x) double f; } ieee754; ieee754.f = x; - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + ((unsigned)ieee754.u != 0) > 0x7ff00000; @@ -156,18 +110,15 @@ double log10(double x) double ln(double x) { double out; - __asm__("fldln2; fldl %1; fyl2x" : "=t"(out) : "m"(x)); - + __asm__ __volatile__("fldln2; fldl %1; fyl2x" : "=t"(out) : "m"(x)); return out; } double logx(double x, double y) { // Base may not equal 1 or be negative. - if (y == 1.f || y < 0.f || ln(y) == 0.f) { + if (y == 1.f || y < 0.f || ln(y) == 0.f) return 0.f; - } - return ln(x) / ln(y); } @@ -176,7 +127,6 @@ double logx(double x, double y) double modf(double x, double *intpart) { register double absvalue; - if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { // It must be an integer. (*intpart) = x; @@ -195,7 +145,6 @@ double modf(double x, double *intpart) (*intpart) = -(*intpart); } } - // Signed fractional part. return (x - (*intpart)); } From 3bfa61d189c2785bee509073adafb80ec3a65995 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 12:07:35 +0200 Subject: [PATCH 06/21] Re-format the code --- mentos/CMakeLists.txt | 4 +- mentos/inc/descriptor_tables/idt.h | 46 ++-- mentos/inc/devices/pci.h | 10 +- mentos/inc/libc/limits.h | 22 +- mentos/inc/libc/list_head.h | 126 +++++----- mentos/inc/libc/queue.h | 28 +-- mentos/inc/libc/stdarg.h | 2 +- mentos/inc/libc/stddef.h | 2 +- mentos/inc/multiboot.h | 216 ++++++++--------- mentos/inc/process/prio.h | 28 +-- mentos/inc/sys/dirent.h | 34 ++- mentos/inc/sys/utsname.h | 19 +- mentos/inc/system/signal_defs.h | 76 +++--- mentos/src/descriptor_tables/idt.c | 2 +- mentos/src/devices/pci.c | 120 +++++----- mentos/src/hardware/cpuid.c | 247 +++++++++---------- mentos/src/io/port_io.c | 33 +-- mentos/src/kernel/sys.c | 93 ++++---- mentos/src/libc/hashmap.c | 341 ++++++++++++--------------- mentos/src/libc/libgen.c | 87 ++++--- mentos/src/libc/ordered_array.c | 107 ++++----- mentos/src/libc/spinlock.c | 24 +- mentos/src/libc/stdlib.c | 31 +-- mentos/src/libc/strerror.c | 5 +- mentos/src/libc/unistd/getpid.c | 6 +- mentos/src/libc/unistd/open.c | 16 +- mentos/src/libc/unistd/read.c | 15 +- mentos/src/mem/zone_allocator.c | 52 ++-- mentos/src/process/process.c | 26 +- mentos/src/sys/unistd.c | 69 +++--- mentos/src/sys/utsname.c | 12 +- mentos/src/system/printk.c | 18 +- mentos/src/ui/command/cmd_cd.c | 84 +++---- mentos/src/ui/command/cmd_drv_load.c | 94 +++----- mentos/src/ui/command/cmd_echo.c | 26 +- mentos/src/ui/command/cmd_ipcrm.c | 66 +++--- mentos/src/ui/command/cmd_ipcs.c | 103 ++++---- mentos/src/ui/command/cmd_more.c | 54 ++--- mentos/src/ui/command/cmd_newfile.c | 67 +++--- mentos/src/ui/command/cmd_pwd.c | 12 +- mentos/src/ui/command/cmd_rmdir.c | 37 ++- mentos/src/ui/command/cmd_uname.c | 4 +- 42 files changed, 1143 insertions(+), 1321 deletions(-) diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index fe6abb0..0ca358e 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -307,8 +307,8 @@ set(SOURCE_FILES if (ENABLE_BUDDY_SYSTEM MATCHES "ON") set(SOURCE_FILES - ${SOURCE_FILES} - src/mem/buddysystem.c) + ${SOURCE_FILES} + src/mem/buddysystem.c) endif (ENABLE_BUDDY_SYSTEM MATCHES "ON") # ------------------------------------------------------------------------------ diff --git a/mentos/inc/descriptor_tables/idt.h b/mentos/inc/descriptor_tables/idt.h index ee388c3..ddb388b 100644 --- a/mentos/inc/descriptor_tables/idt.h +++ b/mentos/inc/descriptor_tables/idt.h @@ -14,19 +14,19 @@ #define IDT_SIZE 256 /// When an exception occurs whose entry is a Task Gate, a task switch results. -#define TASK_GATE 0x5 +#define TASK_GATE 0x5 /// Used to specify an interrupt service routine (16-bit). -#define INT16_GATE 0x6 +#define INT16_GATE 0x6 /// @brief Similar to an Interrupt gate (16-bit). -#define TRAP16_GATE 0x7 +#define TRAP16_GATE 0x7 /// Used to specify an interrupt service routine (32-bit). -#define INT32_GATE 0xE +#define INT32_GATE 0xE /// @brief Similar to an Interrupt gate (32-bit). -#define TRAP32_GATE 0xF +#define TRAP32_GATE 0xF /* * Trap and Interrupt gates are similar, and their descriptors are @@ -36,25 +36,23 @@ */ /// @brief This structure describes one interrupt gate. -typedef struct idt_descriptor_t -{ - unsigned short offset_low; - unsigned short seg_selector; - // This will ALWAYS be set to 0. - unsigned char null_par; - // |P|DPL|01110|. - // P present, DPL required Ring (2bits). - unsigned char options; - unsigned short offset_high; +typedef struct idt_descriptor_t { + unsigned short offset_low; + unsigned short seg_selector; + // This will ALWAYS be set to 0. + unsigned char null_par; + // |P|DPL|01110|. + // P present, DPL required Ring (2bits). + unsigned char options; + unsigned short offset_high; } __attribute__((packed)) idt_descriptor_t; /// @brief A pointer structure used for informing the CPU about our IDT. -typedef struct idt_pointer_t -{ - /// The size of the IDT (entry number). - unsigned short int limit; - /// The start address of the IDT. - unsigned int base; +typedef struct idt_pointer_t { + /// The size of the IDT (entry number). + unsigned short int limit; + /// The start address of the IDT. + unsigned int base; } __attribute__((packed)) idt_pointer_t; /// @brief Initialise the interrupt descriptor table. @@ -65,10 +63,8 @@ void init_idt(); /// @param handler Puntatore alla funzione che gestira' l'interrupt/Eccezione /// @param options Le opzioni del descrittore (PRESENT,NOTPRESENT,KERNEL,USER) /// @param seg_sel Il selettore del segmento della GDT. -void idt_set_gate(uint8_t index, - interrupt_handler_t handler, - uint16_t options, - uint8_t seg_sel); +void idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, + uint8_t seg_sel); //==== List of exceptions generated internally by the CPU ====================== extern void INT_0(); diff --git a/mentos/inc/devices/pci.h b/mentos/inc/devices/pci.h index aa05b6d..f517fd4 100644 --- a/mentos/inc/devices/pci.h +++ b/mentos/inc/devices/pci.h @@ -155,7 +155,7 @@ // TODO: doxygen comment. /// @brief typedef void (*pci_func_t)(uint32_t device, uint16_t vendor_id, - uint16_t device_id, void *extra); + uint16_t device_id, void *extra); // TODO: doxygen comment. /// @brief @@ -183,8 +183,8 @@ static inline int pci_extract_func(uint32_t device) static inline uint32_t pci_get_addr(uint32_t device, int field) { return 0x80000000 | (pci_extract_bus(device) << 16) | - (pci_extract_slot(device) << 11) | - (pci_extract_func(device) << 8) | ((field)&0xFC); + (pci_extract_slot(device) << 11) | (pci_extract_func(device) << 8) | + ((field)&0xFC); } // TODO: doxygen comment. @@ -212,7 +212,7 @@ const char *pci_vendor_lookup(unsigned short vendor_id); // TODO: doxygen comment. /// @brief const char *pci_device_lookup(unsigned short vendor_id, - unsigned short device_id); + unsigned short device_id); // TODO: doxygen comment. /// @brief void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra); @@ -220,7 +220,7 @@ void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra); // TODO: doxygen comment. /// @brief void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, - void *extra); + void *extra); // TODO: doxygen comment. /// @brief diff --git a/mentos/inc/libc/limits.h b/mentos/inc/libc/limits.h index 9b09ccf..36e92e6 100644 --- a/mentos/inc/libc/limits.h +++ b/mentos/inc/libc/limits.h @@ -7,35 +7,35 @@ #pragma once /// Number of bits in a `char'. -#define CHAR_BIT 8 +#define CHAR_BIT 8 /// Minimum value a `signed char' can hold. -#define SCHAR_MIN (-128) +#define SCHAR_MIN (-128) /// Maximum value a `signed char' can hold. -#define SCHAR_MAX 127 +#define SCHAR_MAX 127 /// Maximum value a `char' can hold. (Minimum is 0.) -#define CHAR_MAX 255 +#define CHAR_MAX 255 /// Minimum value a `signed short int' can hold. -#define SHRT_MIN (-32768) +#define SHRT_MIN (-32768) /// Maximum value a `signed short int' can hold. -#define SHRT_MAX 32767 +#define SHRT_MAX 32767 /// Minimum value a `signed int' can hold. -#define INT_MIN (-INT_MAX - 1) +#define INT_MIN (-INT_MAX - 1) /// Maximum values a `signed int' can hold. -#define INT_MAX 2147483647 +#define INT_MAX 2147483647 /// Maximum value an `unsigned int' can hold. (Minimum is 0.) -#define UINT_MAX 4294967295U +#define UINT_MAX 4294967295U /// Minimum value a `signed long int' can hold. -#define LONG_MAX 2147483647L +#define LONG_MAX 2147483647L /// Maximum value a `signed long int' can hold. -#define LONG_MIN (-LONG_MAX - 1L) +#define LONG_MIN (-LONG_MAX - 1L) diff --git a/mentos/inc/libc/list_head.h b/mentos/inc/libc/list_head.h index f3a0641..2af28ec 100644 --- a/mentos/inc/libc/list_head.h +++ b/mentos/inc/libc/list_head.h @@ -9,125 +9,121 @@ #include "stddef.h" /// @brief Structure used to implement the list_head data structure. -typedef struct list_head -{ - /// @brief The previous element. - struct list_head * prev; - /// @brief The subsequent element. - struct list_head * next; +typedef struct list_head { + /// @brief The previous element. + struct list_head *prev; + /// @brief The subsequent element. + struct list_head *next; } list_head; /// @brief Get the struct for this entry. /// @param ptr The &struct list_head pointer. /// @param type The type of the struct this is embedded in. /// @param member The name of the list_head within the struct. -#define list_entry(ptr, type, member) \ - ((type *) ((char *) (ptr) - (unsigned long) (&((type *) 0)->member))) +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) /// @brief Iterates over a list. /// @param pos The &struct list_head to use as a loop cursor. /// @param head The head for your list. -#define list_for_each(pos, head) \ - for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) +#define list_for_each(pos, head) \ + for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) /// @brief Iterates over a list backwards. /// @param pos The &struct list_head to use as a loop cursor. /// @param head The head for your list. -#define list_for_each_prev(pos, head) \ - for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) +#define list_for_each_prev(pos, head) \ + for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) /// @brief Iterates over a list safe against removal of list entry. /// @param pos The &struct list_head to use as a loop cursor. /// @param store Another &struct list_head to use as temporary storage. /// @param head The head for your list. -#define list_for_each_safe(pos, store, head) \ - for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); \ - (pos) = (store), (store) = (pos)->next) +#define list_for_each_safe(pos, store, head) \ + for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); \ + (pos) = (store), (store) = (pos)->next) /// @brief Initializes the list_head. /// @param head The head for your list. -#define list_head_init(head) \ - (head)->next = (head)->prev = (head) +#define list_head_init(head) (head)->next = (head)->prev = (head) /// @brief Insert element l2 after l1. -static inline void list_head_insert_after(list_head * l1, list_head * l2) +static inline void list_head_insert_after(list_head *l1, list_head *l2) { - // [La]->l1 La<-[l1]->Lb <-[l2]-> l1<-[Lb] + // [La]->l1 La<-[l1]->Lb <-[l2]-> l1<-[Lb] - list_head * l1_next = l1->next; - // [La]->l1 La<-[l1]->l2 <-[l2]-> l1<-[Lb] - l1->next = l2; - // [La]->l1 La<-[l1]->l2 l1<-[l2]-> l1<-[Lb] - l2->prev = l1; - // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l1<-[Lb] - l2->next = l1_next; - // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l2<-[Lb] - l1_next->prev = l2; + list_head *l1_next = l1->next; + // [La]->l1 La<-[l1]->l2 <-[l2]-> l1<-[Lb] + l1->next = l2; + // [La]->l1 La<-[l1]->l2 l1<-[l2]-> l1<-[Lb] + l2->prev = l1; + // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l1<-[Lb] + l2->next = l1_next; + // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l2<-[Lb] + l1_next->prev = l2; } /// @brief Insert element l2 before l1. -static inline void list_head_insert_before(list_head * l1, list_head * l2) +static inline void list_head_insert_before(list_head *l1, list_head *l2) { - // [La]->l1 [l2] La<-[l1]->Lb l1<-[Lb] + // [La]->l1 [l2] La<-[l1]->Lb l1<-[Lb] - list_head * l1_prev = l1->prev; - // [La]->l2 [l2] La<-[l1]->Lb l1<-[Lb] - l1_prev->next = l2; - // [La]->l2 La<-[l2] La<-[l1]->Lb l1<-[Lb] - l2->prev = l1_prev; - // [La]->l2 La<-[l2]->l1 La<-[l1]->Lb l1<-[Lb] - l2->next = l1; - // [La]->l2 La<-[l2]->l1 l2<-[l1]->Lb l1<-[Lb] - l1->prev = l2; + list_head *l1_prev = l1->prev; + // [La]->l2 [l2] La<-[l1]->Lb l1<-[Lb] + l1_prev->next = l2; + // [La]->l2 La<-[l2] La<-[l1]->Lb l1<-[Lb] + l2->prev = l1_prev; + // [La]->l2 La<-[l2]->l1 La<-[l1]->Lb l1<-[Lb] + l2->next = l1; + // [La]->l2 La<-[l2]->l1 l2<-[l1]->Lb l1<-[Lb] + l1->prev = l2; } /// @brief Remove l from the list. /// @param l The element to remove. -static inline void list_head_del(list_head * l) +static inline void list_head_del(list_head *l) { - // [La]->l La<-[l]->Lb l<-[Lb] + // [La]->l La<-[l]->Lb l<-[Lb] - // [La]->Lb La<-[l]->Lb l<-[Lb] - l->prev->next = l->next; - // [La]->Lb La<-[l]->Lb La<-[Lb] - l->next->prev = l->prev; - // [La]->Lb l<-[l]->l La<-[Lb] - l->next = l->prev = l; + // [La]->Lb La<-[l]->Lb l<-[Lb] + l->prev->next = l->next; + // [La]->Lb La<-[l]->Lb La<-[Lb] + l->next->prev = l->prev; + // [La]->Lb l<-[l]->l La<-[Lb] + l->next = l->prev = l; } /// @brief Tests whether the given list is empty. /// @param head The list to check. /// @return 1 if empty, 0 otherwise. -static inline int list_head_empty(list_head const * head) +static inline int list_head_empty(list_head const *head) { - return head->next == head; + return head->next == head; } /// Insert a new entry between two known consecutive entries. -static inline void __list_add(list_head * new, - list_head * prev, - list_head * next) +static inline void __list_add(list_head *new, list_head *prev, list_head *next) { - // [prev]-> <-[new]-> <-[next] + // [prev]-> <-[new]-> <-[next] - // [prev]-> <-[new]-> new<-[next] - next->prev = new; - // [prev]-> <-[new]->next new<-[next] - new->next = next; - // [prev]-> prev<-[new]->next new<-[next] - new->prev = prev; - // [prev]->new prev<-[new]->next new<-[next] - prev->next = new; + // [prev]-> <-[new]-> new<-[next] + next->prev = new; + // [prev]-> <-[new]->next new<-[next] + new->next = next; + // [prev]-> prev<-[new]->next new<-[next] + new->prev = prev; + // [prev]->new prev<-[new]->next new<-[next] + prev->next = new; } /// @brief Insert element l2 before l1. -static inline void list_head_add(list_head * new, list_head * head) +static inline void list_head_add(list_head *new, list_head *head) { - __list_add(new, head, head->next); + __list_add(new, head, head->next); } /// @brief Insert element l2 before l1. -static inline void list_head_add_tail(list_head * new, list_head * head) +static inline void list_head_add_tail(list_head *new, list_head *head) { - __list_add(new, head->prev, head); + __list_add(new, head->prev, head); } diff --git a/mentos/inc/libc/queue.h b/mentos/inc/libc/queue.h index ca7a1f9..599d507 100644 --- a/mentos/inc/libc/queue.h +++ b/mentos/inc/libc/queue.h @@ -10,24 +10,22 @@ #include "stdbool.h" /// @brief A node of the queue. -typedef struct queue_node_t -{ - /// The wrapped data. - void *data; - /// The next node of the queue. - struct queue_node_t *next; +typedef struct queue_node_t { + /// The wrapped data. + void *data; + /// The next node of the queue. + struct queue_node_t *next; } queue_node_t; /// @brief The queue. -typedef struct queue_t -{ - /// The front of the queue. - queue_node_t *front; - /// The back of the queue. - queue_node_t *back; - /// The size of the data contained inside the queue. - size_t data_size; -} *queue_t; +typedef struct queue_t { + /// The front of the queue. + queue_node_t *front; + /// The back of the queue. + queue_node_t *back; + /// The size of the data contained inside the queue. + size_t data_size; +} * queue_t; /// @brief Creates a queue. /// @param data_size The size of the stored elements. diff --git a/mentos/inc/libc/stdarg.h b/mentos/inc/libc/stdarg.h index c36e914..6890b22 100644 --- a/mentos/inc/libc/stdarg.h +++ b/mentos/inc/libc/stdarg.h @@ -17,7 +17,7 @@ typedef char *va_list; /// @brief Amount of space required in an argument list (ie. the stack) for an /// argument of type t. #define va_size(t) \ - (((sizeof(t) + sizeof(va_item) - 1) / sizeof(va_item)) * sizeof(va_item)) + (((sizeof(t) + sizeof(va_item) - 1) / sizeof(va_item)) * sizeof(va_item)) /// @brief The start of a variadic list. #define va_start(ap, last_arg) (ap = ((va_list)(&last_arg) + va_size(last_arg))) diff --git a/mentos/inc/libc/stddef.h b/mentos/inc/libc/stddef.h index 04a4a03..9098592 100644 --- a/mentos/inc/libc/stddef.h +++ b/mentos/inc/libc/stddef.h @@ -9,7 +9,7 @@ #ifndef NULL /// @brief Define NULL. -#define NULL ((void*)0) +#define NULL ((void *)0) #endif diff --git a/mentos/inc/multiboot.h b/mentos/inc/multiboot.h index 237f5b2..6a282d4 100644 --- a/mentos/inc/multiboot.h +++ b/mentos/inc/multiboot.h @@ -10,43 +10,43 @@ #include "stdint.h" -#define MULTIBOOT_FLAG_MEM 0x001 +#define MULTIBOOT_FLAG_MEM 0x001 -#define MULTIBOOT_FLAG_DEVICE 0x002 +#define MULTIBOOT_FLAG_DEVICE 0x002 #define MULTIBOOT_FLAG_CMDLINE 0x004 -#define MULTIBOOT_FLAG_MODS 0x008 +#define MULTIBOOT_FLAG_MODS 0x008 -#define MULTIBOOT_FLAG_AOUT 0x010 +#define MULTIBOOT_FLAG_AOUT 0x010 -#define MULTIBOOT_FLAG_ELF 0x020 +#define MULTIBOOT_FLAG_ELF 0x020 -#define MULTIBOOT_FLAG_MMAP 0x040 +#define MULTIBOOT_FLAG_MMAP 0x040 -#define MULTIBOOT_FLAG_CONFIG 0x080 +#define MULTIBOOT_FLAG_CONFIG 0x080 -#define MULTIBOOT_FLAG_LOADER 0x100 +#define MULTIBOOT_FLAG_LOADER 0x100 -#define MULTIBOOT_FLAG_APM 0x200 +#define MULTIBOOT_FLAG_APM 0x200 -#define MULTIBOOT_FLAG_VBE 0x400 +#define MULTIBOOT_FLAG_VBE 0x400 -#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 -#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 -#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_RESERVED 2 -#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 -#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_NVS 4 -#define MULTIBOOT_MEMORY_BADRAM 5 +#define MULTIBOOT_MEMORY_BADRAM 5 // +-------------------+ // 0 | flags | (required) @@ -93,130 +93,120 @@ // +-------------------+ /// The symbol table for a.out. -typedef struct multiboot_aout_symbol_table -{ - uint32_t tabsize; - uint32_t strsize; - uint32_t addr; - uint32_t reserved; +typedef struct multiboot_aout_symbol_table { + uint32_t tabsize; + uint32_t strsize; + uint32_t addr; + uint32_t reserved; } multiboot_aout_symbol_table_t; /// The section header table for ELF. -typedef struct multiboot_elf_section_header_table -{ - uint32_t num; - uint32_t size; - uint32_t addr; - uint32_t shndx; +typedef struct multiboot_elf_section_header_table { + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; } multiboot_elf_section_header_table_t; // TODO: doxygen comment. -typedef struct multiboot_mod_list -{ - // The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive. - uint32_t mod_start; - uint32_t mod_end; - // Module command line. - uint32_t cmdline; - // Padding to take it to 16 bytes (must be zero). - uint32_t pad; +typedef struct multiboot_mod_list { + // The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive. + uint32_t mod_start; + uint32_t mod_end; + // Module command line. + uint32_t cmdline; + // Padding to take it to 16 bytes (must be zero). + uint32_t pad; } multiboot_module_t; // TODO: doxygen comment. -typedef struct multiboot_mmap_entry -{ - uint32_t size; - uint64_t addr; - uint64_t len; - uint32_t type; +typedef struct multiboot_mmap_entry { + uint32_t size; + uint64_t addr; + uint64_t len; + uint32_t type; } __attribute__((packed)) multiboot_memory_map_t; // TODO: doxygen comment. -typedef struct multiboot_info -{ - /// Multiboot info version number. - uint32_t flags; +typedef struct multiboot_info { + /// Multiboot info version number. + uint32_t flags; - /// Available memory from BIOS. - uint32_t mem_lower; - uint32_t mem_upper; + /// Available memory from BIOS. + uint32_t mem_lower; + uint32_t mem_upper; - /// "root" partition. - uint32_t boot_device; + /// "root" partition. + uint32_t boot_device; - /// Kernel command line. - uint32_t cmdline; + /// Kernel command line. + uint32_t cmdline; - /// Boot-Module list. - uint32_t mods_count; - uint32_t mods_addr; + /// Boot-Module list. + uint32_t mods_count; + uint32_t mods_addr; - union - { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; + union { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; - /// Memory Mapping buffer. - uint32_t mmap_length; - uint32_t mmap_addr; + /// Memory Mapping buffer. + uint32_t mmap_length; + uint32_t mmap_addr; - /// Drive Info buffer. - uint32_t drives_length; - uint32_t drives_addr; + /// Drive Info buffer. + uint32_t drives_length; + uint32_t drives_addr; - /// ROM configuration table. - uint32_t config_table; + /// ROM configuration table. + uint32_t config_table; - /// Boot Loader Name. - uint32_t boot_loader_name; + /// Boot Loader Name. + uint32_t boot_loader_name; - /// APM table. - uint32_t apm_table; + /// APM table. + uint32_t apm_table; - /// Video. - uint32_t vbe_control_info; - uint32_t vbe_mode_info; - uint32_t vbe_mode; - uint32_t vbe_interface_seg; - uint32_t vbe_interface_off; - uint32_t vbe_interface_len; + /// Video. + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint32_t vbe_mode; + uint32_t vbe_interface_seg; + uint32_t vbe_interface_off; + uint32_t vbe_interface_len; - uint32_t framebuffer_addr; - uint32_t framebuffer_pitch; - uint32_t framebuffer_width; - uint32_t framebuffer_height; - uint32_t framebuffer_bpp; - uint32_t framebuffer_type; - union - { - struct - { - uint32_t framebuffer_palette_addr; - uint16_t framebuffer_palette_num_colors; - }; - struct - { - uint8_t framebuffer_red_field_position; - uint8_t framebuffer_red_mask_size; - uint8_t framebuffer_green_field_position; - uint8_t framebuffer_green_mask_size; - uint8_t framebuffer_blue_field_position; - uint8_t framebuffer_blue_mask_size; - }; - }; + uint32_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint32_t framebuffer_bpp; + uint32_t framebuffer_type; + union { + struct { + uint32_t framebuffer_palette_addr; + uint16_t framebuffer_palette_num_colors; + }; + struct { + uint8_t framebuffer_red_field_position; + uint8_t framebuffer_red_mask_size; + uint8_t framebuffer_green_field_position; + uint8_t framebuffer_green_mask_size; + uint8_t framebuffer_blue_field_position; + uint8_t framebuffer_blue_mask_size; + }; + }; } __attribute__((packed)) multiboot_info_t; // Be careful that the offset 0 is base_addr_low but no size. /// @brief The memory map. -typedef struct memory_map -{ - uint32_t size; - uint32_t base_addr_low; - uint32_t base_addr_high; - uint32_t length_low; - uint32_t length_high; - uint32_t type; +typedef struct memory_map { + uint32_t size; + uint32_t base_addr_low; + uint32_t base_addr_high; + uint32_t length_low; + uint32_t length_high; + uint32_t type; } memory_map_t; // TODO: doxygen comment. diff --git a/mentos/inc/process/prio.h b/mentos/inc/process/prio.h index b5fbfe2..82a4a63 100644 --- a/mentos/inc/process/prio.h +++ b/mentos/inc/process/prio.h @@ -4,11 +4,11 @@ /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. -#define MAX_NICE +19 +#define MAX_NICE +19 -#define MIN_NICE -20 +#define MIN_NICE -20 -#define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) +#define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) // Priority of a process goes from 0..MAX_PRIO-1, valid RT // priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH @@ -23,7 +23,7 @@ #define MAX_RT_PRIO 100 -#define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) +#define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) #define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2) @@ -32,24 +32,24 @@ // and back. #define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO) -#define PRIO_TO_NICE(prio) ((prio) - DEFAULT_PRIO) +#define PRIO_TO_NICE(prio) ((prio)-DEFAULT_PRIO) // 'User priority' is the nice value converted to something we // can work with better when scaling various scheduler parameters, // it's a [ 0 ... 39 ] range. -#define USER_PRIO(p) ((p)-MAX_RT_PRIO) +#define USER_PRIO(p) ((p)-MAX_RT_PRIO) #define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) #define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) static const int prio_to_weight[NICE_WIDTH] = { - /* 100 */ 88761, 71755, 56483, 46273, 36291, - /* 105 */ 29154, 23254, 18705, 14949, 11916, - /* 110 */ 9548, 7620, 6100, 4904, 3906, - /* 115 */ 3121, 2501, 1991, 1586, 1277, - /* 120 */ 1024, 820, 655, 526, 423, - /* 125 */ 335, 272, 215, 172, 137, - /* 130 */ 110, 87, 70, 56, 45, - /* 135 */ 36, 29, 23, 18, 15 + /* 100 */ 88761, 71755, 56483, 46273, 36291, + /* 105 */ 29154, 23254, 18705, 14949, 11916, + /* 110 */ 9548, 7620, 6100, 4904, 3906, + /* 115 */ 3121, 2501, 1991, 1586, 1277, + /* 120 */ 1024, 820, 655, 526, 423, + /* 125 */ 335, 272, 215, 172, 137, + /* 130 */ 110, 87, 70, 56, 45, + /* 135 */ 36, 29, 23, 18, 15 }; diff --git a/mentos/inc/sys/dirent.h b/mentos/inc/sys/dirent.h index ae2a3ae..9ac6e2c 100644 --- a/mentos/inc/sys/dirent.h +++ b/mentos/inc/sys/dirent.h @@ -12,32 +12,30 @@ #define NAME_MAX 30 /// @brief Contains the entries of a directory. -typedef struct dirent_t -{ - /// The inode of the entry. - ino_t d_ino; +typedef struct dirent_t { + /// The inode of the entry. + ino_t d_ino; - /// The type of the entry. - int d_type; + /// The type of the entry. + int d_type; - /// The nam of the entry. - char d_name[NAME_MAX + 1]; + /// The nam of the entry. + char d_name[NAME_MAX + 1]; } dirent_t; /// @brief Contains information concerning a directory. -typedef struct DIR -{ - /// Filesystem directory handle. - int handle; +typedef struct DIR { + /// Filesystem directory handle. + int handle; - /// The currently opened entry. - ino_t cur_entry; + /// The currently opened entry. + ino_t cur_entry; - /// Path to the directory. - char path[NAME_MAX + 1]; + /// Path to the directory. + char path[NAME_MAX + 1]; - /// Next directory item. - dirent_t entry; + /// Next directory item. + dirent_t entry; } DIR; /// @brief Opens a directory and returns a handler to it. diff --git a/mentos/inc/sys/utsname.h b/mentos/inc/sys/utsname.h index 461ebd6..c79cc74 100644 --- a/mentos/inc/sys/utsname.h +++ b/mentos/inc/sys/utsname.h @@ -10,19 +10,18 @@ #define SYS_LEN 257 /// @brief Holds information concerning the machine and the os. -typedef struct utsname_t -{ - /// The name of the system. - char sysname[SYS_LEN]; +typedef struct utsname_t { + /// The name of the system. + char sysname[SYS_LEN]; - /// The name of the node. - char nodename[SYS_LEN]; + /// The name of the node. + char nodename[SYS_LEN]; - /// The version of the OS. - char version[SYS_LEN]; + /// The version of the OS. + char version[SYS_LEN]; - /// The name of the machine. - char machine[SYS_LEN]; + /// The name of the machine. + char machine[SYS_LEN]; } utsname_t; /// @brief Sets the values of os_infos. diff --git a/mentos/inc/system/signal_defs.h b/mentos/inc/system/signal_defs.h index 3057c07..d06a730 100644 --- a/mentos/inc/system/signal_defs.h +++ b/mentos/inc/system/signal_defs.h @@ -7,115 +7,115 @@ // Signal names (from the Unix specification on signals) /// Hangup. -#define SIGHUP 1 +#define SIGHUP 1 /// Interupt. -#define SIGINT 2 +#define SIGINT 2 /// Quit. -#define SIGQUIT 3 +#define SIGQUIT 3 /// Illegal instruction. -#define SIGILL 4 +#define SIGILL 4 /// A breakpoint or trace instruction has been reached. -#define SIGTRAP 5 +#define SIGTRAP 5 /// Another process has requested that you abort. -#define SIGABRT 6 +#define SIGABRT 6 /// Emulation trap XXX. -#define SIGEMT 7 +#define SIGEMT 7 /// Floating-point arithmetic exception. -#define SIGFPE 8 +#define SIGFPE 8 /// You have been stabbed repeated with a large knife. -#define SIGKILL 9 +#define SIGKILL 9 /// Bus error (device error). -#define SIGBUS 10 +#define SIGBUS 10 /// Segmentation fault. -#define SIGSEGV 11 +#define SIGSEGV 11 /// Bad system call. -#define SIGSYS 12 +#define SIGSYS 12 /// Attempted to read or write from a broken pipe. -#define SIGPIPE 13 +#define SIGPIPE 13 /// This is your wakeup call. -#define SIGALRM 14 +#define SIGALRM 14 /// You have been Schwarzenegger'd. -#define SIGTERM 15 +#define SIGTERM 15 /// User Defined Signal #1. -#define SIGUSR1 16 +#define SIGUSR1 16 /// User Defined Signal #2. -#define SIGUSR2 17 +#define SIGUSR2 17 /// Child status report. -#define SIGCHLD 18 +#define SIGCHLD 18 /// We need moar powah!. -#define SIGPWR 19 +#define SIGPWR 19 /// Your containing terminal has changed size. -#define SIGWINCH 20 +#define SIGWINCH 20 /// An URGENT! event (On a socket). -#define SIGURG 21 +#define SIGURG 21 /// XXX OBSOLETE; socket i/o possible. -#define SIGPOLL 22 +#define SIGPOLL 22 /// Stopped (signal). -#define SIGSTOP 23 +#define SIGSTOP 23 /// ^Z (suspend). -#define SIGTSTP 24 +#define SIGTSTP 24 /// Unsuspended (please, continue). -#define SIGCONT 25 +#define SIGCONT 25 /// TTY input has stopped. -#define SIGTTIN 26 +#define SIGTTIN 26 /// TTY output has stopped. -#define SIGTTOUT 27 +#define SIGTTOUT 27 /// Virtual timer has expired. -#define SIGVTALRM 28 +#define SIGVTALRM 28 /// Profiling timer expired. -#define SIGPROF 29 +#define SIGPROF 29 /// CPU time limit exceeded. -#define SIGXCPU 30 +#define SIGXCPU 30 /// File size limit exceeded. -#define SIGXFSZ 31 +#define SIGXFSZ 31 /// Herp. -#define SIGWAITING 32 +#define SIGWAITING 32 /// Die in a fire. -#define SIGDIAF 33 +#define SIGDIAF 33 /// The sending process does not like you. -#define SIGHATE 34 +#define SIGHATE 34 /// Window server event. #define SIGWINEVENT 35 /// Everybody loves cats. -#define SIGCAT 36 +#define SIGCAT 36 -#define SIGTTOU 37 +#define SIGTTOU 37 -#define NUMSIGNALS 38 +#define NUMSIGNALS 38 -#define NSIG NUMSIGNALS +#define NSIG NUMSIGNALS diff --git a/mentos/src/descriptor_tables/idt.c b/mentos/src/descriptor_tables/idt.c index 3823834..ce15413 100644 --- a/mentos/src/descriptor_tables/idt.c +++ b/mentos/src/descriptor_tables/idt.c @@ -114,7 +114,7 @@ void init_idt() } void idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, - uint8_t seg_sel) + uint8_t seg_sel) { uint32_t base_prt = (uint32_t)handler; diff --git a/mentos/src/devices/pci.c b/mentos/src/devices/pci.c index 6074f26..086feb0 100644 --- a/mentos/src/devices/pci.c +++ b/mentos/src/devices/pci.c @@ -44,17 +44,17 @@ uint32_t pci_read_field(uint32_t device, int field, int size) uint32_t pci_find_type(uint32_t device) { return pci_read_field(device, PCI_CLASS, 1) << 16 | - pci_read_field(device, PCI_SUBCLASS, 1) << 8 | - pci_read_field(device, PCI_PROG_IF, 1); + pci_read_field(device, PCI_SUBCLASS, 1) << 8 | + pci_read_field(device, PCI_PROG_IF, 1); } struct { uint16_t id; const char *name; } _pci_vendors[] = { - { 0x1022, "AMD" }, { 0x106b, "Apple, Inc." }, + { 0x1022, "AMD" }, { 0x106b, "Apple, Inc." }, { 0x1234, "Bochs/QEMU" }, { 0x1274, "Ensoniq" }, - { 0x15ad, "VMWare" }, { 0x8086, "Intel Corporation" }, + { 0x15ad, "VMWare" }, { 0x8086, "Intel Corporation" }, { 0x80EE, "VirtualBox" }, }; @@ -97,18 +97,16 @@ struct { { 0x010100, "ISA Compatibility mode-only controller" }, { 0x010105, "PCI native mode-only controller" }, { 0x01010a, "ISA Compatibility mode controller, supports both channels " - "switched to PCI native mode" }, - { 0x01010f, - "PCI native mode controller, supports both channels switched " - "to ISA compatibility mode" }, + "switched to PCI native mode" }, + { 0x01010f, "PCI native mode controller, supports both channels switched " + "to ISA compatibility mode" }, { 0x010180, "ISA Compatibility mode-only controller, supports bus mastering" }, { 0x010185, "PCI native mode-only controller, supports bus mastering" }, { 0x01018a, "ISA Compatibility mode controller, supports both channels " - "switched to PCI native mode, supports bus mastering" }, - { 0x01018f, - "PCI native mode controller, supports both channels switched " - "\to ISA compatibility mode, supports bus mastering" }, + "switched to PCI native mode, supports bus mastering" }, + { 0x01018f, "PCI native mode controller, supports both channels switched " + "\to ISA compatibility mode, supports bus mastering" }, { 0x010200, "Floppy disk controller" }, { 0x010300, "IPI bus controller" }, @@ -154,10 +152,8 @@ struct { { 0x060600, "NuBus bridge" }, { 0x060700, "CardBus bridge" }, // { 0x0608xx , "RACEway bridge" }, - { 0x060940, - "PCI-to-PCI bridge, Semi-transparent, primary facing Host" }, - { 0x060980, - "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" }, + { 0x060940, "PCI-to-PCI bridge, Semi-transparent, primary facing Host" }, + { 0x060980, "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" }, { 0x060A00, "InfiniBand-to-PCI host bridge" }, { 0x068000, "Bridge device" }, @@ -273,8 +269,7 @@ struct { const char *pci_vendor_lookup(unsigned short vendor_id) { - for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); - ++i) { + for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); ++i) { if (_pci_vendors[i].id == vendor_id) { return _pci_vendors[i].name; } @@ -284,12 +279,11 @@ const char *pci_vendor_lookup(unsigned short vendor_id) } const char *pci_device_lookup(unsigned short vendor_id, - unsigned short device_id) + unsigned short device_id) { - for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); - ++i) { + for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); ++i) { if (_pci_devices[i].ven_id == vendor_id && - _pci_devices[i].dev_id == device_id) { + _pci_devices[i].dev_id == device_id) { return _pci_devices[i].name; } } @@ -299,8 +293,7 @@ const char *pci_device_lookup(unsigned short vendor_id, const char *pci_class_lookup(uint32_t class_code) { - for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); - ++i) { + for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); ++i) { if (_pci_classes[i].id == class_code) { return _pci_classes[i].name; } @@ -317,7 +310,7 @@ void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra) } void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, - void *extra) + void *extra) { uint32_t dev = pci_box_device(bus, slot, func); @@ -325,8 +318,7 @@ void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, pci_scan_hit(f, dev, extra); } if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { - pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), - extra); + pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), extra); } } @@ -378,7 +370,7 @@ void pci_scan(pci_func_t f, int type, void *extra) } static void find_isa_bridge(uint32_t device, uint16_t vendorid, - uint16_t deviceid, void *extra) + uint16_t deviceid, void *extra) { if (vendorid == 0x8086 && (deviceid == 0x7000 || deviceid == 0x7110)) { *((uint32_t *)extra) = device; @@ -423,11 +415,10 @@ int pci_get_interrupt(uint32_t device) uint32_t int_line = pci_read_field(device, PCI_INTERRUPT_LINE, 1); - dbg_print( - "Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?" - "int_line=%d\n", - pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq], - int_line); + dbg_print("Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?" + "int_line=%d\n", + pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq], + int_line); if (pci_remaps[pirq] == 0x80) { dbg_print("Not mapped, remapping?\n"); @@ -442,7 +433,7 @@ int pci_get_interrupt(uint32_t device) } static void scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, - void *extra) + void *extra) { (void)device; (void)vendorid; @@ -452,61 +443,56 @@ static void scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, } static void scan_hit_list(uint32_t device, uint16_t vendorid, uint16_t deviceid, - void *extra) + void *extra) { (void)extra; - dbg_print("%2x:%2x.%d\n", pci_extract_bus(device), - pci_extract_slot(device), pci_extract_func(device), deviceid); + dbg_print("%2x:%2x.%d\n", pci_extract_bus(device), pci_extract_slot(device), + pci_extract_func(device), deviceid); dbg_print(" Vendor : [0x%06x] %s\n", vendorid, - pci_vendor_lookup(vendorid)); + pci_vendor_lookup(vendorid)); dbg_print(" Device : [0x%06x] %s\n", deviceid, - pci_device_lookup(vendorid, deviceid)); + pci_device_lookup(vendorid, deviceid)); dbg_print(" Type : [0x%06x] %s\n", pci_find_type(device), - pci_class_lookup(pci_find_type(device))); + pci_class_lookup(pci_find_type(device))); dbg_print(" Status : 0x%4x\n", - pci_read_field(device, PCI_STATUS, 2)); + pci_read_field(device, PCI_STATUS, 2)); dbg_print(" Command : 0x%4x\n", - pci_read_field(device, PCI_COMMAND, 2)); + pci_read_field(device, PCI_COMMAND, 2)); dbg_print(" Revision : %2d\n", - pci_read_field(device, PCI_REVISION_ID, 1)); + pci_read_field(device, PCI_REVISION_ID, 1)); dbg_print(" Cache Line Size : %2d\n", - pci_read_field(device, PCI_CACHE_LINE_SIZE, 1)); + pci_read_field(device, PCI_CACHE_LINE_SIZE, 1)); dbg_print(" Latency Timer : %2d\n", - pci_read_field(device, PCI_LATENCY_TIMER, 1)); + pci_read_field(device, PCI_LATENCY_TIMER, 1)); dbg_print(" Header Type : %2d\n", - pci_read_field(device, PCI_HEADER_TYPE, 1)); - dbg_print(" BIST : %2d\n", - pci_read_field(device, PCI_BIST, 1)); - dbg_print(" BAR0 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_0, 4)); - dbg_print(" BAR1 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_1, 4)); + pci_read_field(device, PCI_HEADER_TYPE, 1)); + dbg_print(" BIST : %2d\n", pci_read_field(device, PCI_BIST, 1)); + dbg_print(" BAR0 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_0, 4)); + dbg_print(" BAR1 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_1, 4)); dbg_print(" BAR2 : 0x%08x\n", - pci_read_field(device, PCI_BASE_ADDRESS_2, 4)); - dbg_print(" BAR3 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_3, 4)); - dbg_print(" BAR4 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_4, 4)); + pci_read_field(device, PCI_BASE_ADDRESS_2, 4)); + dbg_print(" BAR3 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_3, 4)); + dbg_print(" BAR4 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_4, 4)); dbg_print(" BAR6 : 0x%08x\n", - pci_read_field(device, PCI_BASE_ADDRESS_5, 4)); + pci_read_field(device, PCI_BASE_ADDRESS_5, 4)); dbg_print(" Cardbus CIS : 0x%08x\n", - pci_read_field(device, PCI_CARDBUS_CIS, 4)); + pci_read_field(device, PCI_CARDBUS_CIS, 4)); dbg_print(" Subsystem V. ID : 0x%08x\n", - pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2)); + pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2)); dbg_print(" Subsystem ID : 0x%08x\n", - pci_read_field(device, PCI_SUBSYSTEM_ID, 2)); + pci_read_field(device, PCI_SUBSYSTEM_ID, 2)); dbg_print(" ROM Base Address: 0x%08x\n", - pci_read_field(device, PCI_ROM_ADDRESS, 4)); + pci_read_field(device, PCI_ROM_ADDRESS, 4)); dbg_print(" PCI Cp. LinkList: 0x%08x\n", - pci_read_field(device, PCI_CAPABILITY_LIST, 1)); + pci_read_field(device, PCI_CAPABILITY_LIST, 1)); dbg_print(" Max Latency : 0x%08x\n", - pci_read_field(device, PCI_MAX_LAT, 1)); + pci_read_field(device, PCI_MAX_LAT, 1)); dbg_print(" Min Grant : 0x%08x\n", - pci_read_field(device, PCI_MIN_GNT, 1)); + pci_read_field(device, PCI_MIN_GNT, 1)); dbg_print(" Interrupt Pin : %2d\n", - pci_read_field(device, PCI_INTERRUPT_PIN, 1)); + pci_read_field(device, PCI_INTERRUPT_PIN, 1)); dbg_print(" Interrupt Line : %2d\n", - pci_read_field(device, PCI_INTERRUPT_LINE, 1)); + pci_read_field(device, PCI_INTERRUPT_LINE, 1)); dbg_print(" Interrupt Number: %2d\n", pci_get_interrupt(device)); dbg_print("\n"); } diff --git a/mentos/src/hardware/cpuid.c b/mentos/src/hardware/cpuid.c index 4735565..412158e 100644 --- a/mentos/src/hardware/cpuid.c +++ b/mentos/src/hardware/cpuid.c @@ -9,164 +9,152 @@ void get_cpuid(cpuinfo_t *cpuinfo) { - register_t ereg; + register_t ereg; - ereg.eax = ereg.ebx = ereg.ecx = ereg.edx = 0; - cpuid_write_vendor(cpuinfo, &ereg); + ereg.eax = ereg.ebx = ereg.ecx = ereg.edx = 0; + cpuid_write_vendor(cpuinfo, &ereg); - ereg.eax = 1; - ereg.ebx = ereg.ecx = ereg.edx = 0; - cpuid_write_proctype(cpuinfo, &ereg); + ereg.eax = 1; + ereg.ebx = ereg.ecx = ereg.edx = 0; + cpuid_write_proctype(cpuinfo, &ereg); } void call_cpuid(register_t *registers) { - __asm__ ("cpuid\n\t" : - "=a" (registers->eax), - "=b" (registers->ebx), - "=c" (registers->ecx), - "=d" (registers->edx) : - "a" (registers->eax)); + __asm__("cpuid\n\t" + : "=a"(registers->eax), "=b"(registers->ebx), "=c"(registers->ecx), + "=d"(registers->edx) + : "a"(registers->eax)); } void cpuid_write_vendor(cpuinfo_t *cpuinfo, register_t *registers) { - call_cpuid(registers); + call_cpuid(registers); - cpuinfo->cpu_vendor[0] = (char) ((registers->ebx & 0x000000FF)); - cpuinfo->cpu_vendor[1] = (char) ((registers->ebx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[2] = (char) ((registers->ebx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[3] = (char) ((registers->ebx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[4] = (char) ((registers->edx & 0x000000FF)); - cpuinfo->cpu_vendor[5] = (char) ((registers->edx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[6] = (char) ((registers->edx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[7] = (char) ((registers->edx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[8] = (char) ((registers->ecx & 0x000000FF)); - cpuinfo->cpu_vendor[9] = (char) ((registers->ecx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[10] = (char) ((registers->ecx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[11] = (char) ((registers->ecx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[12] = '\0'; + cpuinfo->cpu_vendor[0] = (char)((registers->ebx & 0x000000FF)); + cpuinfo->cpu_vendor[1] = (char)((registers->ebx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[2] = (char)((registers->ebx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[3] = (char)((registers->ebx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[4] = (char)((registers->edx & 0x000000FF)); + cpuinfo->cpu_vendor[5] = (char)((registers->edx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[6] = (char)((registers->edx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[7] = (char)((registers->edx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[8] = (char)((registers->ecx & 0x000000FF)); + cpuinfo->cpu_vendor[9] = (char)((registers->ecx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[10] = (char)((registers->ecx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[11] = (char)((registers->ecx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[12] = '\0'; } void cpuid_write_proctype(cpuinfo_t *cpuinfo, register_t *registers) { - call_cpuid(registers); + call_cpuid(registers); - uint32_t type = cpuid_get_byte(registers->eax, 0xB, 0x3); + uint32_t type = cpuid_get_byte(registers->eax, 0xB, 0x3); - switch (type) - { - case 0: - cpuinfo->cpu_type = "Original OEM Processor"; - break; - case 1: - cpuinfo->cpu_type = "Intel Overdrive Processor"; - break; - case 2: - cpuinfo->cpu_type = "Dual processor"; - break; - case 3: - cpuinfo->cpu_type = "(Intel reserved bit)"; - break; - } + switch (type) { + case 0: + cpuinfo->cpu_type = "Original OEM Processor"; + break; + case 1: + cpuinfo->cpu_type = "Intel Overdrive Processor"; + break; + case 2: + cpuinfo->cpu_type = "Dual processor"; + break; + case 3: + cpuinfo->cpu_type = "(Intel reserved bit)"; + break; + } - uint32_t familyID = cpuid_get_byte(registers->eax, 0x7, 0xE); - cpuinfo->cpu_family = familyID; + uint32_t familyID = cpuid_get_byte(registers->eax, 0x7, 0xE); + cpuinfo->cpu_family = familyID; - if (familyID == 0x0F) - { - cpuinfo->cpu_family += cpuid_get_byte(registers->eax, 0x13, 0xFF); - } + if (familyID == 0x0F) { + cpuinfo->cpu_family += cpuid_get_byte(registers->eax, 0x13, 0xFF); + } - uint32_t model = cpuid_get_byte(registers->eax, 0x3, 0xE); - cpuinfo->cpu_model = model; + uint32_t model = cpuid_get_byte(registers->eax, 0x3, 0xE); + cpuinfo->cpu_model = model; - if (familyID == 0x06 || familyID == 0x0F) - { - uint32_t ext_model = cpuid_get_byte(registers->eax, 0xF, 0xE); - cpuinfo->cpu_model += (ext_model << 4); - } - cpuinfo->apic_id = cpuid_get_byte(registers->ebx, 0x17, 0xFF); + if (familyID == 0x06 || familyID == 0x0F) { + uint32_t ext_model = cpuid_get_byte(registers->eax, 0xF, 0xE); + cpuinfo->cpu_model += (ext_model << 4); + } + cpuinfo->apic_id = cpuid_get_byte(registers->ebx, 0x17, 0xFF); - cpuid_feature_ecx(cpuinfo, registers->ecx); - cpuid_feature_edx(cpuinfo, registers->edx); + cpuid_feature_ecx(cpuinfo, registers->ecx); + cpuid_feature_edx(cpuinfo, registers->edx); - /* Get brand string to identify the processor */ - if (familyID >= 0x0F && model >= 0x03) - { - cpuinfo->brand_string = cpuid_brand_string(registers); - } - else - { - cpuinfo->brand_string = cpuid_brand_index(registers); - } + /* Get brand string to identify the processor */ + if (familyID >= 0x0F && model >= 0x03) { + cpuinfo->brand_string = cpuid_brand_string(registers); + } else { + cpuinfo->brand_string = cpuid_brand_index(registers); + } } void cpuid_feature_ecx(cpuinfo_t *cpuinfo, uint32_t ecx) { - uint32_t temp = ecx; - uint32_t i; + uint32_t temp = ecx; + uint32_t i; - for (i = 0; i < ECX_FLAGS_SIZE; ++i) - { - temp = cpuid_get_byte(temp, i, 1); - cpuinfo->cpuid_ecx_flags[i] = temp; - temp = ecx; - } + for (i = 0; i < ECX_FLAGS_SIZE; ++i) { + temp = cpuid_get_byte(temp, i, 1); + cpuinfo->cpuid_ecx_flags[i] = temp; + temp = ecx; + } } void cpuid_feature_edx(cpuinfo_t *cpuinfo, uint32_t edx) { - uint32_t temp = edx; - uint32_t i; + uint32_t temp = edx; + uint32_t i; - for (i = 0; i < EDX_FLAGS_SIZE; ++i) - { - temp = cpuid_get_byte(temp, i, 1); - cpuinfo->cpuid_edx_flags[i] = temp; - temp = edx; - } + for (i = 0; i < EDX_FLAGS_SIZE; ++i) { + temp = cpuid_get_byte(temp, i, 1); + cpuinfo->cpuid_edx_flags[i] = temp; + temp = edx; + } } -inline uint32_t cpuid_get_byte(const uint32_t reg, - const uint32_t position, - const uint32_t value) +inline uint32_t cpuid_get_byte(const uint32_t reg, const uint32_t position, + const uint32_t value) { - return ((reg >> position) & value); + return ((reg >> position) & value); } char *cpuid_brand_index(register_t *r) { - char *indexes[21] = {"Reserved", - "Intel Celeron", - "Intel Pentium III", - "Intel Pentium III Xeon", - "Mobile Intel Pentium III", - "Mobile Intel Celeron", - "Intel Pentium 4", - "Intel Pentium 4", - "Intel Celeron", - "Intel Xeon MP", - "Intel Xeon MP", - "Mobile Intel Pentium 4", - "Mobile Intel Celeron", - "Mobile Genuine Intel", - "Intel Celeron M", - "Mobile Intel Celeron", - "Intel Celeron", - "Mobile Genuine Intel", - "Intel Pentium M", - "Mobile Intel Celeron", - NULL}; + char *indexes[21] = { "Reserved", + "Intel Celeron", + "Intel Pentium III", + "Intel Pentium III Xeon", + "Mobile Intel Pentium III", + "Mobile Intel Celeron", + "Intel Pentium 4", + "Intel Pentium 4", + "Intel Celeron", + "Intel Xeon MP", + "Intel Xeon MP", + "Mobile Intel Pentium 4", + "Mobile Intel Celeron", + "Mobile Genuine Intel", + "Intel Celeron M", + "Mobile Intel Celeron", + "Intel Celeron", + "Mobile Genuine Intel", + "Intel Pentium M", + "Mobile Intel Celeron", + NULL }; - int bx = (r->ebx & 0xFF); + int bx = (r->ebx & 0xFF); - if (bx > 0x17) - { - bx = 0; - } + if (bx > 0x17) { + bx = 0; + } - return indexes[bx]; + return indexes[bx]; } /// @@ -174,21 +162,20 @@ char *cpuid_brand_index(register_t *r) /// @return char *cpuid_brand_string(register_t *r) { - char *temp = ""; + char *temp = ""; - for (r->eax = 0x80000002; r->eax <= 0x80000004; (r->eax)++) - { - r->ebx = r->ecx = r->edx = 0; - call_cpuid(r); - temp = strncat(temp, (const char *) r->eax, - strlen((const char *) r->eax)); - temp = strncat(temp, (const char *) r->ebx, - strlen((const char *) r->ebx)); - temp = strncat(temp, (const char *) r->ecx, - strlen((const char *) r->ecx)); - temp = strncat(temp, (const char *) r->edx, - strlen((const char *) r->edx)); - } + for (r->eax = 0x80000002; r->eax <= 0x80000004; (r->eax)++) { + r->ebx = r->ecx = r->edx = 0; + call_cpuid(r); + temp = + strncat(temp, (const char *)r->eax, strlen((const char *)r->eax)); + temp = + strncat(temp, (const char *)r->ebx, strlen((const char *)r->ebx)); + temp = + strncat(temp, (const char *)r->ecx, strlen((const char *)r->ecx)); + temp = + strncat(temp, (const char *)r->edx, strlen((const char *)r->edx)); + } - return temp; + return temp; } diff --git a/mentos/src/io/port_io.c b/mentos/src/io/port_io.c index edfb4bd..ee85812 100644 --- a/mentos/src/io/port_io.c +++ b/mentos/src/io/port_io.c @@ -8,49 +8,52 @@ inline uint8_t inportb(uint16_t port) { - unsigned char data = 0; - __asm__ __volatile__("inb %%dx, %%al" : "=a" (data) : "d" (port)); + unsigned char data = 0; + __asm__ __volatile__("inb %%dx, %%al" : "=a"(data) : "d"(port)); - return data; + return data; } inline uint16_t inports(uint16_t port) { - uint16_t rv; - __asm__ __volatile__("inw %1, %0" : "=a" (rv) : "dN" (port)); + uint16_t rv; + __asm__ __volatile__("inw %1, %0" : "=a"(rv) : "dN"(port)); - return rv; + return rv; } -void inportsm(uint16_t port, uint8_t * data, unsigned long size) +void inportsm(uint16_t port, uint8_t *data, unsigned long size) { - __asm__ __volatile__("rep insw" : "+D" (data), "+c" (size) : "d" (port) : "memory"); + __asm__ __volatile__("rep insw" + : "+D"(data), "+c"(size) + : "d"(port) + : "memory"); } inline uint32_t inportl(uint16_t port) { - uint32_t rv; - __asm__ __volatile__("inl %%dx, %%eax" : "=a" (rv) : "dN" (port)); + uint32_t rv; + __asm__ __volatile__("inl %%dx, %%eax" : "=a"(rv) : "dN"(port)); - return rv; + return rv; } inline void outportb(uint16_t port, uint8_t data) { - __asm__ __volatile__("outb %%al, %%dx"::"a" (data), "d" (port)); + __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); } inline void outports(uint16_t port, uint16_t data) { - __asm__ __volatile__("outw %1, %0" : : "dN" (port), "a" (data)); + __asm__ __volatile__("outw %1, %0" : : "dN"(port), "a"(data)); } void outportsm(uint16_t port, uint8_t *data, uint16_t size) { - asm volatile ("rep outsw" : "+S" (data), "+c" (size) : "d" (port)); + asm volatile("rep outsw" : "+S"(data), "+c"(size) : "d"(port)); } inline void outportl(uint16_t port, uint32_t data) { - __asm__ __volatile__("outl %%eax, %%dx" : : "dN" (port), "a" (data)); + __asm__ __volatile__("outl %%eax, %%dx" : : "dN"(port), "a"(data)); } diff --git a/mentos/src/kernel/sys.c b/mentos/src/kernel/sys.c index 3972c1c..bb15def 100644 --- a/mentos/src/kernel/sys.c +++ b/mentos/src/kernel/sys.c @@ -13,66 +13,61 @@ static void machine_power_off() { - while (true) - { - cpu_relax(); - } + while (true) { + cpu_relax(); + } } /// @brief Shutdown everything and perform a clean system power_off. static void kernel_power_off() { -// kernel_shutdown_prepare(SYSTEM_POWER_OFF); -// if (pm_power_off_prepare) -// { -// pm_power_off_prepare(); -// } -// migrate_to_reboot_cpu(); -// syscore_shutdown(); - printf("Power down\n"); -// kmsg_dump(KMSG_DUMP_POWEROFF); - machine_power_off(); + // kernel_shutdown_prepare(SYSTEM_POWER_OFF); + // if (pm_power_off_prepare) + // { + // pm_power_off_prepare(); + // } + // migrate_to_reboot_cpu(); + // syscore_shutdown(); + printf("Power down\n"); + // kmsg_dump(KMSG_DUMP_POWEROFF); + machine_power_off(); } int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg) { - static mutex_t reboot_mutex; + static mutex_t reboot_mutex; - // For safety, we require "magic" arguments. - if (magic1 != LINUX_REBOOT_MAGIC1 || - (magic2 != LINUX_REBOOT_MAGIC2 && - magic2 != LINUX_REBOOT_MAGIC2A && - magic2 != LINUX_REBOOT_MAGIC2B && - magic2 != LINUX_REBOOT_MAGIC2C)) - { - return -EINVAL; - } + // For safety, we require "magic" arguments. + if (magic1 != LINUX_REBOOT_MAGIC1 || + (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && + magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) { + return -EINVAL; + } - mutex_lock(&reboot_mutex, 0); + mutex_lock(&reboot_mutex, 0); - switch (cmd) - { - case LINUX_REBOOT_CMD_RESTART: - break; - case LINUX_REBOOT_CMD_CAD_ON: - break; - case LINUX_REBOOT_CMD_CAD_OFF: - break; - case LINUX_REBOOT_CMD_HALT: - break; - case LINUX_REBOOT_CMD_POWER_OFF: - kernel_power_off(); - break; - case LINUX_REBOOT_CMD_RESTART2: - break; - case LINUX_REBOOT_CMD_KEXEC: - break; - case LINUX_REBOOT_CMD_SW_SUSPEND: - break; - default: - return -EINVAL; - } - mutex_unlock(&reboot_mutex); + switch (cmd) { + case LINUX_REBOOT_CMD_RESTART: + break; + case LINUX_REBOOT_CMD_CAD_ON: + break; + case LINUX_REBOOT_CMD_CAD_OFF: + break; + case LINUX_REBOOT_CMD_HALT: + break; + case LINUX_REBOOT_CMD_POWER_OFF: + kernel_power_off(); + break; + case LINUX_REBOOT_CMD_RESTART2: + break; + case LINUX_REBOOT_CMD_KEXEC: + break; + case LINUX_REBOOT_CMD_SW_SUSPEND: + break; + default: + return -EINVAL; + } + mutex_unlock(&reboot_mutex); - return 0; + return 0; } diff --git a/mentos/src/libc/hashmap.c b/mentos/src/libc/hashmap.c index feb00a5..c553e63 100644 --- a/mentos/src/libc/hashmap.c +++ b/mentos/src/libc/hashmap.c @@ -8,287 +8,260 @@ #include "string.h" #include "stdlib.h" -struct hashmap_entry_t -{ - char *key; - void *value; - struct hashmap_entry_t *next; +struct hashmap_entry_t { + char *key; + void *value; + struct hashmap_entry_t *next; }; -struct hashmap_t -{ - hashmap_hash_t hash_func; - hashmap_comp_t hash_comp; - hashmap_dupe_t hash_key_dup; - hashmap_free_t hash_key_free; - hashmap_free_t hash_val_free; - size_t size; - hashmap_entry_t **entries; +struct hashmap_t { + hashmap_hash_t hash_func; + hashmap_comp_t hash_comp; + hashmap_dupe_t hash_key_dup; + hashmap_free_t hash_key_free; + hashmap_free_t hash_val_free; + size_t size; + hashmap_entry_t **entries; }; size_t hashmap_string_hash(void *_key) { - size_t hash = 0; - char *key = (char *) _key; - int c; - /* - * This is the so-called "sdbm" hash. It comes from a piece of public - * domain code from a clone of ndbm. - */ - while ((c = *key++)) - { - hash = c + (hash << 6) + (hash << 16) - hash; - } - - return hash; + size_t hash = 0; + char *key = (char *)_key; + int c; + // This is the so-called "sdbm" hash. It comes from a piece of public + // domain code from a clone of ndbm. + while ((c = *key++)) { + hash = c + (hash << 6) + (hash << 16) - hash; + } + return hash; } bool_t hashmap_string_comp(void *a, void *b) { - return !strcmp(a, b); + return !strcmp(a, b); } void *hashmap_string_dupe(void *key) { - return strdup(key); + return strdup(key); } static size_t hashmap_int_hash(void *key) { - return (size_t) key; + return (size_t)key; } static bool_t hashmap_int_comp(void *a, void *b) { - return (int) a == (int) b; + return (int)a == (int)b; } static void *hashmap_int_dupe(void *key) { - return key; + return key; } static void hashmap_int_free(void *ptr) { - (void) ptr; + (void)ptr; } hashmap_t *hashmap_create(size_t size) { - hashmap_t *map = malloc(sizeof(hashmap_t)); + hashmap_t *map = malloc(sizeof(hashmap_t)); - map->hash_func = &hashmap_string_hash; - map->hash_comp = &hashmap_string_comp; - map->hash_key_dup = &hashmap_string_dupe; - map->hash_key_free = &free; - map->hash_val_free = &free; + map->hash_func = &hashmap_string_hash; + map->hash_comp = &hashmap_string_comp; + map->hash_key_dup = &hashmap_string_dupe; + map->hash_key_free = &free; + map->hash_val_free = &free; - map->size = size; - map->entries = malloc(sizeof(hashmap_entry_t *) * size); - memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); + map->size = size; + map->entries = malloc(sizeof(hashmap_entry_t *) * size); + memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); - return map; + return map; } hashmap_t *hashmap_create_int(size_t size) { - hashmap_t *map = malloc(sizeof(hashmap_t)); + hashmap_t *map = malloc(sizeof(hashmap_t)); - map->hash_func = &hashmap_int_hash; - map->hash_comp = &hashmap_int_comp; - map->hash_key_dup = &hashmap_int_dupe; - map->hash_key_free = &hashmap_int_free; - map->hash_val_free = &free; + map->hash_func = &hashmap_int_hash; + map->hash_comp = &hashmap_int_comp; + map->hash_key_dup = &hashmap_int_dupe; + map->hash_key_free = &hashmap_int_free; + map->hash_val_free = &free; - map->size = size; - map->entries = malloc(sizeof(hashmap_entry_t *) * size); - memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); + map->size = size; + map->entries = malloc(sizeof(hashmap_entry_t *) * size); + memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); - return map; + return map; } void hashmap_free(hashmap_t *map) { - for (size_t i = 0; i < map->size; ++i) - { - hashmap_entry_t *x = map->entries[i], * p; - while (x) - { - p = x; - x = x->next; - map->hash_key_free(p->key); - map->hash_val_free(p); - } - } + for (size_t i = 0; i < map->size; ++i) { + hashmap_entry_t *x = map->entries[i], *p; + while (x) { + p = x; + x = x->next; + map->hash_key_free(p->key); + map->hash_val_free(p); + } + } - free(map->entries); + free(map->entries); } void *hashmap_set(hashmap_t *map, void *key, void *value) { - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; + size_t hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; - if (x == NULL) - { - hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - map->entries[hash] = e; + if (x == NULL) { + hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); + e->key = map->hash_key_dup(key); + e->value = value; + e->next = NULL; + map->entries[hash] = e; - return NULL; - } + return NULL; + } - hashmap_entry_t *p = NULL; + hashmap_entry_t *p = NULL; - do - { - if (map->hash_comp(x->key, key)) - { - void *out = x->value; - x->value = value; + do { + if (map->hash_comp(x->key, key)) { + void *out = x->value; + x->value = value; - return out; - } - p = x; - x = x->next; - } while (x); + return out; + } + p = x; + x = x->next; + } while (x); - hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - p->next = e; + hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); + e->key = map->hash_key_dup(key); + e->value = value; + e->next = NULL; + p->next = e; - return NULL; + return NULL; } void *hashmap_get(hashmap_t *map, void *key) { - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; + size_t hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; - if (x == NULL) - { - return NULL; - } - do - { - if (map->hash_comp(x->key, key)) - { - return x->value; - } - x = x->next; - } while (x); + if (x == NULL) { + return NULL; + } + do { + if (map->hash_comp(x->key, key)) { + return x->value; + } + x = x->next; + } while (x); - return NULL; + return NULL; } void *hashmap_remove(hashmap_t *map, void *key) { - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; + size_t hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; - if (x == NULL) - { - return NULL; - } - if (map->hash_comp(x->key, key)) - { - void *out = x->value; - map->entries[hash] = x->next; - map->hash_key_free(x->key); - map->hash_val_free(x); + if (x == NULL) { + return NULL; + } + if (map->hash_comp(x->key, key)) { + void *out = x->value; + map->entries[hash] = x->next; + map->hash_key_free(x->key); + map->hash_val_free(x); - return out; - } + return out; + } - hashmap_entry_t * p = x; - x = x->next; - do - { - if (map->hash_comp(x->key, key)) - { - void *out = x->value; - p->next = x->next; - map->hash_key_free(x->key); - map->hash_val_free(x); + hashmap_entry_t *p = x; + x = x->next; + do { + if (map->hash_comp(x->key, key)) { + void *out = x->value; + p->next = x->next; + map->hash_key_free(x->key); + map->hash_val_free(x); - return out; - } - p = x; - x = x->next; - } while (x); + return out; + } + p = x; + x = x->next; + } while (x); - return NULL; + return NULL; } bool_t hashmap_is_empty(hashmap_t *map) { - for (size_t i = 0; i < map->size; ++i) - { - if (map->entries[i]) - { - return false; - } - } + for (size_t i = 0; i < map->size; ++i) { + if (map->entries[i]) { + return false; + } + } - return true; + return true; } bool_t hashmap_has(hashmap_t *map, void *key) { - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t * x = map->entries[hash]; + size_t hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; - if (x == NULL) - { - return false; - } - do - { - if (map->hash_comp(x->key, key)) - { - return true; - } - x = x->next; - } while (x); + if (x == NULL) { + return false; + } + do { + if (map->hash_comp(x->key, key)) { + return true; + } + x = x->next; + } while (x); - return false; + return false; } list_t *hashmap_keys(hashmap_t *map) { - list_t *l = list_create(); + list_t *l = list_create(); - for (size_t i = 0; i < map->size; ++i) - { - hashmap_entry_t * x = map->entries[i]; - while (x) - { - list_insert_back(l, x->key); - x = x->next; - } - } + for (size_t i = 0; i < map->size; ++i) { + hashmap_entry_t *x = map->entries[i]; + while (x) { + list_insert_back(l, x->key); + x = x->next; + } + } - return l; + return l; } list_t *hashmap_values(hashmap_t *map) { - list_t *l = list_create(); + list_t *l = list_create(); - for (size_t i = 0; i < map->size; ++i) - { - hashmap_entry_t *x = map->entries[i]; + for (size_t i = 0; i < map->size; ++i) { + hashmap_entry_t *x = map->entries[i]; - while (x) - { - list_insert_back(l, x->value); - x = x->next; - } - } + while (x) { + list_insert_back(l, x->value); + x = x->next; + } + } - return l; + return l; } diff --git a/mentos/src/libc/libgen.c b/mentos/src/libc/libgen.c index d6827d8..3625f48 100644 --- a/mentos/src/libc/libgen.c +++ b/mentos/src/libc/libgen.c @@ -10,71 +10,64 @@ int parse_path(char *out, char **cur, char sep, size_t max) { - if (**cur == '\0') - { - return 0; - } + if (**cur == '\0') { + return 0; + } - *out++ = **cur; - ++*cur; - --max; + *out++ = **cur; + ++*cur; + --max; - while ((max > 0) && (**cur != '\0') && (**cur != sep)) - { - *out++ = **cur; - ++*cur; - --max; - } + while ((max > 0) && (**cur != '\0') && (**cur != sep)) { + *out++ = **cur; + ++*cur; + --max; + } - *out = '\0'; + *out = '\0'; - return 1; + return 1; } char *dirname(const char *path) { - static char s[MAX_PATH_LENGTH]; + static char s[MAX_PATH_LENGTH]; - static char dot[2] = "."; + static char dot[2] = "."; - // Check the input path. - if (path == NULL) return dot; + // Check the input path. + if (path == NULL) + return dot; - // Copy the path to the support string. - strcpy(s, path); + // Copy the path to the support string. + strcpy(s, path); - // Get the last occurrence of '/'. - char *last_slash = strrchr(s, '/'); + // Get the last occurrence of '/'. + char *last_slash = strrchr(s, '/'); - if (last_slash == s) - { - // If the slash is acutally the first character of the string, move the - // pointer to the last slash after it. - ++last_slash; - } - else if ((last_slash != NULL) && (last_slash[1] == '\0')) - { - // If the slash is the last character, we need to search before it. - last_slash = memchr(s, '/', last_slash - s); - } + if (last_slash == s) { + // If the slash is acutally the first character of the string, move the + // pointer to the last slash after it. + ++last_slash; + } else if ((last_slash != NULL) && (last_slash[1] == '\0')) { + // If the slash is the last character, we need to search before it. + last_slash = memchr(s, '/', last_slash - s); + } - if (last_slash != NULL) - { - // If we have found it, close the string. - last_slash[0] = '\0'; - } - else - { - // Otherwise, return '.'. - return dot; - } + if (last_slash != NULL) { + // If we have found it, close the string. + last_slash[0] = '\0'; + } else { + // Otherwise, return '.'. + return dot; + } - return s; + return s; } char *basename(const char *path) { - char *p = strrchr(path, '/'); + char *p = strrchr(path, '/'); - return p ? p + 1 : (char *) path; + return p ? p + 1 : (char *)path; } diff --git a/mentos/src/libc/ordered_array.c b/mentos/src/libc/ordered_array.c index 1247cee..c9c3af9 100644 --- a/mentos/src/libc/ordered_array.c +++ b/mentos/src/libc/ordered_array.c @@ -11,87 +11,80 @@ int8_t standard_lessthan_predicate(array_type_t a, array_type_t b) { - return (a < b); + return (a < b); } ordered_array_t create_ordered_array(uint32_t max_size, - lessthan_predicate_t less_than) + lessthan_predicate_t less_than) { - ordered_array_t to_ret; - to_ret.array = malloc(max_size * sizeof(array_type_t)); - memset(to_ret.array, 0, max_size * sizeof(array_type_t)); - to_ret.size = 0; - to_ret.max_size = max_size; - to_ret.less_than = less_than; + ordered_array_t to_ret; + to_ret.array = malloc(max_size * sizeof(array_type_t)); + memset(to_ret.array, 0, max_size * sizeof(array_type_t)); + to_ret.size = 0; + to_ret.max_size = max_size; + to_ret.less_than = less_than; - return to_ret; + return to_ret; } -ordered_array_t place_ordered_array(void * addr, - uint32_t max_size, - lessthan_predicate_t less_than) +ordered_array_t place_ordered_array(void *addr, uint32_t max_size, + lessthan_predicate_t less_than) { - ordered_array_t to_ret; - to_ret.array = (array_type_t *) addr; - memset(to_ret.array, 0, max_size * sizeof(array_type_t)); - to_ret.size = 0; - to_ret.max_size = max_size; - to_ret.less_than = less_than; + ordered_array_t to_ret; + to_ret.array = (array_type_t *)addr; + memset(to_ret.array, 0, max_size * sizeof(array_type_t)); + to_ret.size = 0; + to_ret.max_size = max_size; + to_ret.less_than = less_than; - return to_ret; + return to_ret; } -void destroy_ordered_array(ordered_array_t * array) +void destroy_ordered_array(ordered_array_t *array) { - free(array->array); + free(array->array); } -void insert_ordered_array(array_type_t item, ordered_array_t * array) +void insert_ordered_array(array_type_t item, ordered_array_t *array) { - assert(array->less_than); + assert(array->less_than); - uint32_t iterator = 0; - while (iterator < array->size && array->less_than(array->array[iterator], - item)) - { - iterator++; - } + uint32_t iterator = 0; + while (iterator < array->size && + array->less_than(array->array[iterator], item)) { + iterator++; + } - // Just add at the end of the array. - if (iterator == array->size) - { - array->array[array->size++] = item; - } - else - { - array_type_t tmp = array->array[iterator]; - array->array[iterator] = item; + // Just add at the end of the array. + if (iterator == array->size) { + array->array[array->size++] = item; + } else { + array_type_t tmp = array->array[iterator]; + array->array[iterator] = item; - while (iterator < array->size) - { - iterator++; - array_type_t tmp2 = array->array[iterator]; - array->array[iterator] = tmp; - tmp = tmp2; - } - array->size++; - } + while (iterator < array->size) { + iterator++; + array_type_t tmp2 = array->array[iterator]; + array->array[iterator] = tmp; + tmp = tmp2; + } + array->size++; + } } -array_type_t lookup_ordered_array(uint32_t i, ordered_array_t * array) +array_type_t lookup_ordered_array(uint32_t i, ordered_array_t *array) { - assert(i < array->size); + assert(i < array->size); - return array->array[i]; + return array->array[i]; } -void remove_ordered_array(uint32_t i, ordered_array_t * array) +void remove_ordered_array(uint32_t i, ordered_array_t *array) { - while (i < array->size) - { - array->array[i] = array->array[i + 1]; - i++; - } + while (i < array->size) { + array->array[i] = array->array[i + 1]; + i++; + } - array->size--; + array->size--; } diff --git a/mentos/src/libc/spinlock.c b/mentos/src/libc/spinlock.c index 659511c..235906d 100644 --- a/mentos/src/libc/spinlock.c +++ b/mentos/src/libc/spinlock.c @@ -8,29 +8,27 @@ void spinlock_init(spinlock_t *spinlock) { - (*spinlock) = 0; + (*spinlock) = 0; } void spinlock_lock(spinlock_t *spinlock) { - while (true) - { - if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) - { - break; - } - while (*spinlock) cpu_relax(); - } + while (true) { + if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) { + break; + } + while (*spinlock) + cpu_relax(); + } } void spinlock_unlock(spinlock_t *spinlock) { - barrier(); - - atomic_set(spinlock, SPINLOCK_FREE); + barrier(); + atomic_set(spinlock, SPINLOCK_FREE); } bool_t spinlock_trylock(spinlock_t *spinlock) { - return (bool_t) (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0); + return (bool_t)(atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0); } diff --git a/mentos/src/libc/stdlib.c b/mentos/src/libc/stdlib.c index f064066..2af772e 100644 --- a/mentos/src/libc/stdlib.c +++ b/mentos/src/libc/stdlib.c @@ -9,43 +9,38 @@ #include "string.h" /// Used to align the memory. -#define ALIGN(x) \ - (((x) + (sizeof(size_t) - 1)) & ~(sizeof(size_t) - 1)) - +#define ALIGN(x) (((x) + (sizeof(size_t) - 1)) & ~(sizeof(size_t) - 1)) void *malloc(unsigned int size) { - void *_res; - DEFN_SYSCALL1(_res, __NR_brk, size); + void *_res; + DEFN_SYSCALL1(_res, __NR_brk, size); - return _res; + return _res; } void free(void *p) { - int _res; - DEFN_SYSCALL1(_res, __NR_free, p); + int _res; + DEFN_SYSCALL1(_res, __NR_free, p); } - void *calloc(size_t element_number, size_t element_size) { - void *ptr = malloc(element_number * element_size); - if (ptr) - { - memset(ptr, 0, element_number * element_size); - } + void *ptr = malloc(element_number * element_size); + if (ptr) { + memset(ptr, 0, element_number * element_size); + } - return ptr; + return ptr; } - /// Seed used to generate random numbers. int rseed = 0; inline void srand(int x) { - rseed = x; + rseed = x; } #ifndef MS_RAND @@ -57,7 +52,7 @@ inline void srand(int x) /// between 0 and RAND_MAX. inline int rand() { - return rseed = (rseed * 1103515245 + 12345) & RAND_MAX; + return rseed = (rseed * 1103515245 + 12345) & RAND_MAX; } // MS rand. diff --git a/mentos/src/libc/strerror.c b/mentos/src/libc/strerror.c index 668ea21..afcb482 100644 --- a/mentos/src/libc/strerror.c +++ b/mentos/src/libc/strerror.c @@ -328,7 +328,7 @@ char *strerror(int errnum) #ifdef ELIBMAX case ELIBMAX: strcpy(error, - "Attempting to link in more shared libraries than system limit"); + "Attempting to link in more shared libraries than system limit"); break; #endif #ifdef ELIBEXEC @@ -368,8 +368,7 @@ char *strerror(int errnum) #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: - strcpy(error, - "Address family not supported by protocol family"); + strcpy(error, "Address family not supported by protocol family"); break; #endif #ifdef EPROTOTYPE diff --git a/mentos/src/libc/unistd/getpid.c b/mentos/src/libc/unistd/getpid.c index 58616cb..c1059b8 100644 --- a/mentos/src/libc/unistd/getpid.c +++ b/mentos/src/libc/unistd/getpid.c @@ -11,9 +11,9 @@ pid_t getpid() { - pid_t ret; + pid_t ret; - DEFN_SYSCALL0(ret, __NR_getpid); + DEFN_SYSCALL0(ret, __NR_getpid); - return ret; + return ret; } diff --git a/mentos/src/libc/unistd/open.c b/mentos/src/libc/unistd/open.c index f6c8f3a..20b1e71 100644 --- a/mentos/src/libc/unistd/open.c +++ b/mentos/src/libc/unistd/open.c @@ -10,15 +10,13 @@ int open(const char *pathname, int flags, mode_t mode) { - ssize_t retval; + ssize_t retval; - DEFN_SYSCALL3(retval, __NR_open, pathname, flags, mode); + DEFN_SYSCALL3(retval, __NR_open, pathname, flags, mode); - if (retval < 0) - { - errno = -retval; - retval = -1; - - } - return retval; + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; } diff --git a/mentos/src/libc/unistd/read.c b/mentos/src/libc/unistd/read.c index f233796..396dafa 100644 --- a/mentos/src/libc/unistd/read.c +++ b/mentos/src/libc/unistd/read.c @@ -10,15 +10,14 @@ ssize_t read(int fd, void *buf, size_t nbytes) { - ssize_t retval; + ssize_t retval; - DEFN_SYSCALL3(retval, __NR_read, fd, buf, nbytes); + DEFN_SYSCALL3(retval, __NR_read, fd, buf, nbytes); - if (retval < 0) - { - errno = -retval; - retval = -1; - } + if (retval < 0) { + errno = -retval; + retval = -1; + } - return retval; + return retval; } diff --git a/mentos/src/mem/zone_allocator.c b/mentos/src/mem/zone_allocator.c index db8b353..ddb3989 100644 --- a/mentos/src/mem/zone_allocator.c +++ b/mentos/src/mem/zone_allocator.c @@ -190,40 +190,40 @@ static bool_t pmm_check() /// @param zone A memory zone. static void buddy_system_init(zone_t *zone) { - // Initialize the free_lists of each area of the zone. - for (unsigned int order = 0; order < MAX_ORDER; order++) { - free_area_t *area = zone->free_area + order; - area->nr_free = 0; - list_head_init(&area->free_list); - } + // Initialize the free_lists of each area of the zone. + for (unsigned int order = 0; order < MAX_ORDER; order++) { + free_area_t *area = zone->free_area + order; + area->nr_free = 0; + list_head_init(&area->free_list); + } - // Current base page descriptor of the zone. - page_t *page = zone->zone_mem_map; - // Address of the last page descriptor of the zone. - page_t *last_page = page + zone->size; + // Current base page descriptor of the zone. + page_t *page = zone->zone_mem_map; + // Address of the last page descriptor of the zone. + page_t *last_page = page + zone->size; - // Get the free area collecting the larges block of page frames. - const unsigned int order = MAX_ORDER - 1; - free_area_t *area = zone->free_area + order; + // Get the free area collecting the larges block of page frames. + const unsigned int order = MAX_ORDER - 1; + free_area_t *area = zone->free_area + order; - // Add all zone's pages to the largest free area block. - uint32_t block_size = 1UL << order; - while ((page + block_size) <= last_page) { - /* page has already the _count field set to -1, + // Add all zone's pages to the largest free area block. + uint32_t block_size = 1UL << order; + while ((page + block_size) <= last_page) { + /* page has already the _count field set to -1, * therefore only save the order of the page. */ - page->private = order; + page->private = order; - // Insert page as first element in the list. - list_head_add_tail(&page->lru, &area->free_list); - // Increase the number of free block of the free_area_t. - area->nr_free++; + // Insert page as first element in the list. + list_head_add_tail(&page->lru, &area->free_list); + // Increase the number of free block of the free_area_t. + area->nr_free++; - page += block_size; - } + page += block_size; + } - assert(page == last_page && - "Memory size is not aligned to MAX_ORDER size!"); + assert(page == last_page && + "Memory size is not aligned to MAX_ORDER size!"); } /// @brief Initializes the memory attributes. diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index ae2eabc..1f32f5b 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -284,19 +284,19 @@ int sys_execve(pt_regs *r) PUSH_ON_STACK(current->thread.useresp, int, argc); PUSH_ON_STACK(current->thread.useresp, uintptr_t, (uintptr_t)exit_handler); -// dbg_print("_ARGV:0x%09x {\n", _argv); -// for (int i = 0; _argv[i] != NULL; ++i) { -// dbg_print("\t[%d][0x%09x]%s\n", i, _argv[i], _argv[i]); -// } -// dbg_print("}\n"); -// -// if (_envp != NULL) { -// dbg_print("_ENVP:0x%09x {\n", _envp); -// for (int i = 0; _envp[i] != NULL; ++i) { -// dbg_print("\t[%d][0x%09x]%s\n", i, _envp[i], _envp[i]); -// } -// dbg_print("}\n"); -// } + // dbg_print("_ARGV:0x%09x {\n", _argv); + // for (int i = 0; _argv[i] != NULL; ++i) { + // dbg_print("\t[%d][0x%09x]%s\n", i, _argv[i], _argv[i]); + // } + // dbg_print("}\n"); + // + // if (_envp != NULL) { + // dbg_print("_ENVP:0x%09x {\n", _envp); + // for (int i = 0; _envp[i] != NULL; ++i) { + // dbg_print("\t[%d][0x%09x]%s\n", i, _envp[i], _envp[i]); + // } + // dbg_print("}\n"); + // } // Perform the switch to the new process. do_switch(current, r); diff --git a/mentos/src/sys/unistd.c b/mentos/src/sys/unistd.c index d8b88a6..a703cae 100644 --- a/mentos/src/sys/unistd.c +++ b/mentos/src/sys/unistd.c @@ -15,52 +15,45 @@ int close(int fildes) { - if (fildes < 0) - { - return -1; - } - if (fd_list[fildes].fs_spec_id >= -1) - { - int mp_id = fd_list[fildes].mountpoint_id; - if (mountpoint_list[mp_id].operations.close_f != NULL) - { - int fs_fd = fd_list[fildes].fs_spec_id; - mountpoint_list[mp_id].operations.close_f(fs_fd); - } - fd_list[fildes].fs_spec_id = -1; - fd_list[fildes].mountpoint_id = -1; - } - else - { - return -1; - } + if (fildes < 0) { + return -1; + } + if (fd_list[fildes].fs_spec_id >= -1) { + int mp_id = fd_list[fildes].mountpoint_id; + if (mountpoint_list[mp_id].operations.close_f != NULL) { + int fs_fd = fd_list[fildes].fs_spec_id; + mountpoint_list[mp_id].operations.close_f(fs_fd); + } + fd_list[fildes].fs_spec_id = -1; + fd_list[fildes].mountpoint_id = -1; + } else { + return -1; + } - return 0; + return 0; } int rmdir(const char *path) { - char absolute_path[MAX_PATH_LENGTH]; - strcpy(absolute_path, path); + char absolute_path[MAX_PATH_LENGTH]; + strcpy(absolute_path, path); - if (path[0] != '/') - { - get_absolute_path(absolute_path); - } + if (path[0] != '/') { + get_absolute_path(absolute_path); + } - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("rmdir: failed to remove '%s':" - "Cannot find mount-point\n", path); + int32_t mp_id = get_mountpoint_id(absolute_path); + if (mp_id < 0) { + printf("rmdir: failed to remove '%s':" + "Cannot find mount-point\n", + path); - return -1; - } + return -1; + } - if (mountpoint_list[mp_id].dir_op.rmdir_f == NULL) - { - return -1; - } + if (mountpoint_list[mp_id].dir_op.rmdir_f == NULL) { + return -1; + } - return mountpoint_list[mp_id].dir_op.rmdir_f(absolute_path); + return mountpoint_list[mp_id].dir_op.rmdir_f(absolute_path); } diff --git a/mentos/src/sys/utsname.c b/mentos/src/sys/utsname.c index 007d539..c72a8d7 100644 --- a/mentos/src/sys/utsname.c +++ b/mentos/src/sys/utsname.c @@ -10,11 +10,11 @@ int uname(utsname_t *os_infos) { - // Uname code goes here. - strcpy(os_infos->sysname, OS_NAME); - strcpy(os_infos->version, OS_VERSION); - strcpy(os_infos->nodename, "testbed"); - strcpy(os_infos->machine, "i686"); + // Uname code goes here. + strcpy(os_infos->sysname, OS_NAME); + strcpy(os_infos->version, OS_VERSION); + strcpy(os_infos->nodename, "testbed"); + strcpy(os_infos->machine, "i686"); - return 0; + return 0; } diff --git a/mentos/src/system/printk.c b/mentos/src/system/printk.c index 9518a64..a0a308b 100644 --- a/mentos/src/system/printk.c +++ b/mentos/src/system/printk.c @@ -9,15 +9,15 @@ #include "stdio.h" #include "video.h" -void printk(const char * format, ...) +void printk(const char *format, ...) { - char buffer[4096]; - va_list ap; - // Start variabile argument's list. - va_start (ap, format); - int len = vsprintf(buffer, format, ap); - va_end (ap); + char buffer[4096]; + va_list ap; + // Start variabile argument's list. + va_start(ap, format); + int len = vsprintf(buffer, format, ap); + va_end(ap); - for (size_t i = 0; (i < len); ++i) - video_putc(buffer[i]); + for (size_t i = 0; (i < len); ++i) + video_putc(buffer[i]); } diff --git a/mentos/src/ui/command/cmd_cd.c b/mentos/src/ui/command/cmd_cd.c index bfb4cab..06826e8 100644 --- a/mentos/src/ui/command/cmd_cd.c +++ b/mentos/src/ui/command/cmd_cd.c @@ -15,57 +15,45 @@ void cmd_cd(int argc, char **argv) { - DIR *dirp = NULL; - char path[MAX_PATH_LENGTH]; - memset(path, 0, MAX_PATH_LENGTH); + DIR *dirp = NULL; + char path[MAX_PATH_LENGTH]; + memset(path, 0, MAX_PATH_LENGTH); - char current_path[MAX_PATH_LENGTH]; - getcwd(current_path, MAX_PATH_LENGTH); + char current_path[MAX_PATH_LENGTH]; + getcwd(current_path, MAX_PATH_LENGTH); - if (argc <= 1) - { - strcpy(path, "/"); - } - else if (argc > 2) - { - printf("%s: too many arguments\n\n", argv[0]); + if (argc <= 1) { + strcpy(path, "/"); + } else if (argc > 2) { + printf("%s: too many arguments\n\n", argv[0]); - return; - } - else if (strncmp(argv[1], "..", 2) == 0) - { - if (strcmp(current_path, dirname(current_path)) == 0) - { - return; - } - strcpy(path, dirname(current_path)); - } - else if (strncmp(argv[1], ".", 1) == 0) - { - return; - } - else - { - // Copy the current path. - strcpy(path, current_path); - // Get the absolute path. - get_absolute_path(path); - // If the current directory is not the root, add a '/'. - if (strcmp(path, "/") != 0) - { - strncat(path, "/", 1); - } - // Concatenate the input dir. - strncat(path, argv[1], strlen(argv[1])); - } + return; + } else if (strncmp(argv[1], "..", 2) == 0) { + if (strcmp(current_path, dirname(current_path)) == 0) { + return; + } + strcpy(path, dirname(current_path)); + } else if (strncmp(argv[1], ".", 1) == 0) { + return; + } else { + // Copy the current path. + strcpy(path, current_path); + // Get the absolute path. + get_absolute_path(path); + // If the current directory is not the root, add a '/'. + if (strcmp(path, "/") != 0) { + strncat(path, "/", 1); + } + // Concatenate the input dir. + strncat(path, argv[1], strlen(argv[1])); + } - dirp = opendir(path); - if (dirp == NULL) - { - printf("%s: no such file or directory: %s\n\n", argv[0], argv[1]); + dirp = opendir(path); + if (dirp == NULL) { + printf("%s: no such file or directory: %s\n\n", argv[0], argv[1]); - return; - } - chdir(path); - closedir(dirp); + return; + } + chdir(path); + closedir(dirp); } diff --git a/mentos/src/ui/command/cmd_drv_load.c b/mentos/src/ui/command/cmd_drv_load.c index 549c681..685eef1 100644 --- a/mentos/src/ui/command/cmd_drv_load.c +++ b/mentos/src/ui/command/cmd_drv_load.c @@ -1,6 +1,6 @@ /// MentOS, The Mentoring Operating system project /// @file cmd_drv_load.c -/// @brief +/// @brief /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. @@ -11,59 +11,43 @@ void cmd_drv_load(int argc, char **argv) { - if (argc < 2) - { - printf( - "No driver inserted or bad usage! Type %s --help for the usage.\n", - argv[0]); - } - else - { - if ((_kstrncmp(argv[1], "-r", 2) == 0)) - { - if ((argv[2] != NULL)) - { - if (_kstrncmp(argv[2], "mouse", 5) == 0) - { - printf("Disattivamento %s in corso..\n", argv[2]); - mouse_disable(); - } - else - printf("FATAL: Driver %s not found.\n", argv[2]); - } - else - printf("Warning, no driver name inserted!\n"); - } - else if (_kstrncmp(argv[1], "mouse", 5) == 0) - { - // Enabling mouse. - mouse_install(); - } - else if ((_kstrncmp(argv[1], "--help", 6) == 0) || - (_kstrncmp(argv[1], "-h", 2) == 0)) - { - printf("---------------------------------------------------\n" - "Driver tool to load and kill driver\n" - "Simple to use, just type:\n" - "\n" - "Usage: %s - driver_name\n" - "\t-> %s module_name - to load driver\n" - "\t-> %s -r module_name - to kill driver\n" - "---------------------------------------------------\n", - argv[0], argv[0], argv[0]); - } - else - { - if ((_kstrncmp(argv[1], "-r", 2) == 0) && - (_kstrncmp(argv[2], "mouse", 5) == -1)) - { - printf("FATAL: Driver %s not found.\n", argv[2]); - } + if (argc < 2) { + printf( + "No driver inserted or bad usage! Type %s --help for the usage.\n", + argv[0]); + } else { + if ((_kstrncmp(argv[1], "-r", 2) == 0)) { + if ((argv[2] != NULL)) { + if (_kstrncmp(argv[2], "mouse", 5) == 0) { + printf("Disattivamento %s in corso..\n", argv[2]); + mouse_disable(); + } else + printf("FATAL: Driver %s not found.\n", argv[2]); + } else + printf("Warning, no driver name inserted!\n"); + } else if (_kstrncmp(argv[1], "mouse", 5) == 0) { + // Enabling mouse. + mouse_install(); + } else if ((_kstrncmp(argv[1], "--help", 6) == 0) || + (_kstrncmp(argv[1], "-h", 2) == 0)) { + printf("---------------------------------------------------\n" + "Driver tool to load and kill driver\n" + "Simple to use, just type:\n" + "\n" + "Usage: %s - driver_name\n" + "\t-> %s module_name - to load driver\n" + "\t-> %s -r module_name - to kill driver\n" + "---------------------------------------------------\n", + argv[0], argv[0], argv[0]); + } else { + if ((_kstrncmp(argv[1], "-r", 2) == 0) && + (_kstrncmp(argv[2], "mouse", 5) == -1)) { + printf("FATAL: Driver %s not found.\n", argv[2]); + } - else - { - printf("FATAL: Driver %s not found.\n", argv[1]); - } - } - } + else { + printf("FATAL: Driver %s not found.\n", argv[1]); + } + } + } } diff --git a/mentos/src/ui/command/cmd_echo.c b/mentos/src/ui/command/cmd_echo.c index 69d8d5c..d8967b5 100644 --- a/mentos/src/ui/command/cmd_echo.c +++ b/mentos/src/ui/command/cmd_echo.c @@ -1,6 +1,6 @@ /// MentOS, The Mentoring Operating system project /// @file echo.c -/// @brief +/// @brief /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. @@ -9,18 +9,14 @@ void cmd_echo(int argc, char **argv) { - int i = argc; - int j = 0; - if (argc == 1) - { - printf(""); - } - else - { - while (--i > 0) - { - printf("%s ", argv[++j]); - } - } - printf("\n"); + int i = argc; + int j = 0; + if (argc == 1) { + printf(""); + } else { + while (--i > 0) { + printf("%s ", argv[++j]); + } + } + printf("\n"); } diff --git a/mentos/src/ui/command/cmd_ipcrm.c b/mentos/src/ui/command/cmd_ipcrm.c index 2208de9..05856ca 100644 --- a/mentos/src/ui/command/cmd_ipcrm.c +++ b/mentos/src/ui/command/cmd_ipcrm.c @@ -10,49 +10,41 @@ extern struct shmid_ds *head; -void cmd_ipcrm(int argc, char **argv){ - if (argc != 2) - { - printf("Bad arguments: you have to specify only IPC id, see ipcs.\n"); +void cmd_ipcrm(int argc, char **argv) +{ + if (argc != 2) { + printf("Bad arguments: you have to specify only IPC id, see ipcs.\n"); - return; - } + return; + } - struct shmid_ds *shmid_ds = head; - struct shmid_ds *prev = NULL; + struct shmid_ds *shmid_ds = head; + struct shmid_ds *prev = NULL; - while (shmid_ds != NULL) - { - char strid[10]; - int_to_str(strid, (shmid_ds ->shm_perm).seq, 10); + while (shmid_ds != NULL) { + char strid[10]; + int_to_str(strid, (shmid_ds->shm_perm).seq, 10); - if (strcmp(strid, argv[1]) == 0) - { - break; - } + if (strcmp(strid, argv[1]) == 0) { + break; + } - prev = shmid_ds; - shmid_ds = shmid_ds -> next; - } + prev = shmid_ds; + shmid_ds = shmid_ds->next; + } - if (shmid_ds == NULL) - { - printf("No shared memory find. \n"); - } - else - { - kfree(shmid_ds -> shm_location); + if (shmid_ds == NULL) { + printf("No shared memory find. \n"); + } else { + kfree(shmid_ds->shm_location); - // shmid_ds = head. - if (prev == NULL) - { - head = head -> next; - } - else - { - prev -> next = shmid_ds -> next; - } + // shmid_ds = head. + if (prev == NULL) { + head = head->next; + } else { + prev->next = shmid_ds->next; + } - kfree(shmid_ds); - } + kfree(shmid_ds); + } } diff --git a/mentos/src/ui/command/cmd_ipcs.c b/mentos/src/ui/command/cmd_ipcs.c index f8fab60..9f39744 100644 --- a/mentos/src/ui/command/cmd_ipcs.c +++ b/mentos/src/ui/command/cmd_ipcs.c @@ -14,85 +14,70 @@ extern struct shmid_ds *head; static void print_sem_stat() { - printf("Semaphores: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - printf("%-20s %-10s %-20s \n\n", "", "Semaphores not implemented", ""); + printf("Semaphores: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", + "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Semaphores not implemented", ""); } static void print_shm_stat() { - struct shmid_ds *shm_list = head; + struct shmid_ds *shm_list = head; - printf("Shared Memory: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); + printf("Shared Memory: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", + "OWNER", "GROUP"); - while (shm_list != NULL) - { - char mode[12]; - strmode((shm_list->shm_perm).mode, mode); + while (shm_list != NULL) { + char mode[12]; + strmode((shm_list->shm_perm).mode, mode); - printf("%-10s %-10i %-10i %-10s %-10s %-10s \n", - "m", - (shm_list->shm_perm).seq, - (shm_list->shm_perm).key, - mode, - "-", "-"); + printf("%-10s %-10i %-10i %-10s %-10s %-10s \n", "m", + (shm_list->shm_perm).seq, (shm_list->shm_perm).key, mode, "-", + "-"); - shm_list = shm_list->next; - } + shm_list = shm_list->next; + } - printf("\n"); + printf("\n"); } static void print_msg_stat() { - printf("Message Queues: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - printf("%-20s %-10s %-20s \n\n", "", "Message Queues not implemented", ""); + printf("Message Queues: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", + "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Message Queues not implemented", ""); } void cmd_ipcs(int argc, char **argv) { - if (argc > 2) - { - printf("Too much arguments.\n"); + if (argc > 2) { + printf("Too much arguments.\n"); - return; - } + return; + } - char datehour[100] = ""; - strdatehour(datehour); + char datehour[100] = ""; + strdatehour(datehour); - printf("IPC status from "OS_NAME" as of %s\n", datehour); + printf("IPC status from " OS_NAME " as of %s\n", datehour); - if (argc == 2) - { - if (strcmp(argv[1], "-s") == 0) - { - print_sem_stat(); - } - else if (strcmp(argv[1], "-m") == 0) - { - print_shm_stat(); - } - else if (strcmp(argv[1], "-q") == 0) - { - print_msg_stat(); - } - else - { - printf("Option not recognize.\n"); - } - } - else - { - print_sem_stat(); - print_shm_stat(); - print_msg_stat(); - } + if (argc == 2) { + if (strcmp(argv[1], "-s") == 0) { + print_sem_stat(); + } else if (strcmp(argv[1], "-m") == 0) { + print_shm_stat(); + } else if (strcmp(argv[1], "-q") == 0) { + print_msg_stat(); + } else { + printf("Option not recognize.\n"); + } + } else { + print_sem_stat(); + print_shm_stat(); + print_msg_stat(); + } - return; + return; } diff --git a/mentos/src/ui/command/cmd_more.c b/mentos/src/ui/command/cmd_more.c index 7dd0ddc..0cc3575 100644 --- a/mentos/src/ui/command/cmd_more.c +++ b/mentos/src/ui/command/cmd_more.c @@ -13,39 +13,35 @@ void cmd_more(int argc, char **argv) { - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); - return; - } + return; + } - if (strcmp(argv[1], "--help") == 0) - { - printf("Prints the content of the given file.\n"); - printf("Usage:\n"); - printf(" %s \n\n", argv[0]); + if (strcmp(argv[1], "--help") == 0) { + printf("Prints the content of the given file.\n"); + printf("Usage:\n"); + printf(" %s \n\n", argv[0]); - return; - } + return; + } - int fd = open(argv[1], O_RDONLY, 42); - if (fd < 0) - { - printf("%s: Cannot stat file '%s': %s\n\n", - argv[0], argv[1],"unknown"/*strerror(errno)*/); + int fd = open(argv[1], O_RDONLY, 42); + if (fd < 0) { + printf("%s: Cannot stat file '%s': %s\n\n", argv[0], argv[1], + "unknown" /*strerror(errno)*/); - return; - } + return; + } - char c; - // Put on the standard output the characters. - while (read(fd, &c, 1) > 0) - { - putchar(c); - } - putchar('\n'); - putchar('\n'); - close(fd); + char c; + // Put on the standard output the characters. + while (read(fd, &c, 1) > 0) { + putchar(c); + } + putchar('\n'); + putchar('\n'); + close(fd); } diff --git a/mentos/src/ui/command/cmd_newfile.c b/mentos/src/ui/command/cmd_newfile.c index 8cdc39c..2ac8ef3 100644 --- a/mentos/src/ui/command/cmd_newfile.c +++ b/mentos/src/ui/command/cmd_newfile.c @@ -14,49 +14,44 @@ void cmd_newfile(int argc, char **argv) { - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); - return; - } + return; + } - if (strcmp(argv[1], "--help") == 0) - { - printf("Makes a new file, and prompt for it's content.\n"); - printf("Usage:\n"); - printf(" %s \n", argv[0]); + if (strcmp(argv[1], "--help") == 0) { + printf("Makes a new file, and prompt for it's content.\n"); + printf("Usage:\n"); + printf(" %s \n", argv[0]); - return; - } + return; + } - char text[256]; - printf("Filename: %s\n", argv[1]); - int fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0); - if (fd < 0) - { - printf("%s: Cannot create file '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); + char text[256]; + printf("Filename: %s\n", argv[1]); + int fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0); + if (fd < 0) { + printf("%s: Cannot create file '%s': %s\n\n", argv[0], argv[1], + "unknown" /*strerror(errno)*/); - return; - } + return; + } - printf("Type one line of text here (new line to complete):\n"); - scanf("%s", text); - if (write(fd, text, strlen(text)) == -1) - { - printf("%s: Cannot write on file '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); + printf("Type one line of text here (new line to complete):\n"); + scanf("%s", text); + if (write(fd, text, strlen(text)) == -1) { + printf("%s: Cannot write on file '%s': %s\n\n", argv[0], argv[1], + "unknown" /*strerror(errno)*/); - return; - } + return; + } - if (close(fd) == -1) - { - printf("%s: Cannot close file '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); + if (close(fd) == -1) { + printf("%s: Cannot close file '%s': %s\n\n", argv[0], argv[1], + "unknown" /*strerror(errno)*/); - return; - } + return; + } } diff --git a/mentos/src/ui/command/cmd_pwd.c b/mentos/src/ui/command/cmd_pwd.c index e4f123f..8fc3d10 100644 --- a/mentos/src/ui/command/cmd_pwd.c +++ b/mentos/src/ui/command/cmd_pwd.c @@ -1,6 +1,6 @@ /// MentOS, The Mentoring Operating system project /// @file cmd_pwd.c -/// @brief +/// @brief /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. @@ -11,9 +11,9 @@ void cmd_pwd(int argc, char **argv) { - (void) argc; - (void) argv; - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); - printf("%s\n", cwd); + (void)argc; + (void)argv; + char cwd[MAX_PATH_LENGTH]; + getcwd(cwd, MAX_PATH_LENGTH); + printf("%s\n", cwd); } diff --git a/mentos/src/ui/command/cmd_rmdir.c b/mentos/src/ui/command/cmd_rmdir.c index 6c703a0..e423560 100644 --- a/mentos/src/ui/command/cmd_rmdir.c +++ b/mentos/src/ui/command/cmd_rmdir.c @@ -12,29 +12,26 @@ void cmd_rmdir(int argc, char **argv) { - // Check the number of arguments. - if (argc != 2) - { - printf("Bad usage.\n"); - printf("Try 'rmdir --help' for more information.\n"); + // Check the number of arguments. + if (argc != 2) { + printf("Bad usage.\n"); + printf("Try 'rmdir --help' for more information.\n"); - return; - } + return; + } - if (strcmp(argv[1], "--help") == 0) - { - printf("Removes a directory.\n"); - printf("Usage:\n"); - printf(" rmdir \n"); + if (strcmp(argv[1], "--help") == 0) { + printf("Removes a directory.\n"); + printf("Usage:\n"); + printf(" rmdir \n"); - return; - } + return; + } - if (rmdir(argv[1]) != 0) - { - printf("%s: failed to remove '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); + if (rmdir(argv[1]) != 0) { + printf("%s: failed to remove '%s': %s\n\n", argv[0], argv[1], + "unknown" /*strerror(errno)*/); - return; - } + return; + } } diff --git a/mentos/src/ui/command/cmd_uname.c b/mentos/src/ui/command/cmd_uname.c index 8a069ab..e0a7013 100644 --- a/mentos/src/ui/command/cmd_uname.c +++ b/mentos/src/ui/command/cmd_uname.c @@ -42,10 +42,10 @@ void cmd_uname(int argc, char **argv) printf("Micro: %d\n", OS_MICRO_VERSION); // CPU Info. - printf("\nCPU:"); + printf("\nCPU:"); video_set_color(BRIGHT_RED); video_move_cursor(61, video_get_line()); - printf(sinfo.cpu_vendor); + printf(sinfo.cpu_vendor); video_set_color(WHITE); printf("\n"); From 9368b0d6eb35facd8c13cc8793065a76430464b1 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 12:26:30 +0200 Subject: [PATCH 07/21] Canonicalize sys_open and sys_close, add close System Call --- doc/syscall.txt | 361 ++++++++++++++++----------------- mentos/CMakeLists.txt | 2 + mentos/inc/fs/fcntl.h | 16 -- mentos/inc/fs/open.h | 25 +++ mentos/inc/fs/vfs_types.h | 6 - mentos/inc/sys/unistd.h | 8 +- mentos/src/fs/fcntl.c | 111 ++-------- mentos/src/fs/initrd.c | 1 + mentos/src/fs/open.c | 98 +++++++++ mentos/src/fs/read_write.c | 1 + mentos/src/libc/unistd/close.c | 20 ++ mentos/src/sys/unistd.c | 29 +-- mentos/src/system/syscall.c | 3 +- 13 files changed, 350 insertions(+), 331 deletions(-) create mode 100644 mentos/inc/fs/open.h create mode 100644 mentos/src/fs/open.c create mode 100644 mentos/src/libc/unistd/close.c diff --git a/doc/syscall.txt b/doc/syscall.txt index 6c8b52e..91417de 100644 --- a/doc/syscall.txt +++ b/doc/syscall.txt @@ -1,181 +1,180 @@ -| EAX | Name | Source | EBX | ECX | EDX | ESX | EDI | -|:----|:---------------------------|:----------------------------|:-------------------------|:-----------------------------|:------------------------|:----------------|:-----------------| -| 1 | sys_exit | kernel/exit.c | int | - | - | - | - | -| 2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 3 | sys_read | fs/read_write.c | unsigned int | char * | size_t | - | - | -| 4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t | - | - | -| 5 | sys_open | fs/open.c | const char * | int | int | - | - | -| 6 | sys_close | fs/open.c | unsigned int | - | - | - | - | -| 7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int | - | - | -| 8 | sys_creat | fs/open.c | const char * | int | - | - | - | -| 9 | sys_link | fs/namei.c | const char * | const char * | - | - | - | -| 10 | sys_unlink | fs/namei.c | const char * | - | - | - | - | -| 11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 12 | sys_chdir | fs/open.c | const char * | - | - | - | - | -| 13 | sys_time | kernel/time.c | int * | - | - | - | - | -| 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | -| 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | -| 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | -| 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | -| 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | -| 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | -| 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | -| 22 | sys_oldumount | fs/super.c | char * | - | - | - | - | -| 23 | sys_setuid | kernel/sys.c | uid_t | - | - | - | - | -| 24 | sys_getuid | kernel/sched.c | - | - | - | - | - | -| 25 | sys_stime | kernel/time.c | int * | - | - | - | - | -| 26 | sys_ptrace | arch/i386/kernel/ptrace.c | long | long | long | long | - | -| 27 | sys_alarm | kernel/sched.c | unsigned int | - | - | - | - | -| 28 | sys_fstat | fs/stat.c | unsigned int | struct __old_kernel_stat * | - | - | - | -| 29 | sys_pause | arch/i386/kernel/sys_i386.c | - | - | - | - | - | -| 30 | sys_utime | fs/open.c | char * | struct utimbuf * | - | - | - | -| 33 | sys_access | fs/open.c | const char * | int | - | - | - | -| 34 | sys_nice | kernel/sched.c | int | - | - | - | - | -| 36 | sys_sync | fs/buffer.c | - | - | - | - | - | -| 37 | sys_kill | kernel/signal.c | int | int | - | - | - | -| 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | -| 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | -| 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | -| 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | -| 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | -| 43 | sys_times | kernel/sys.c | struct tms * | - | - | - | - | -| 45 | sys_brk | mm/mmap.c | unsigned long | - | - | - | - | -| 46 | sys_setgid | kernel/sys.c | gid_t | - | - | - | - | -| 47 | sys_getgid | kernel/sched.c | - | - | - | - | - | -| 48 | sys_signal | kernel/signal.c | int | __sighandler_t | - | - | - | -| 49 | sys_geteuid | kernel/sched.c | - | - | - | - | - | -| 50 | sys_getegid | kernel/sched.c | - | - | - | - | - | -| 51 | sys_acct | kernel/acct.c | const char * | - | - | - | - | -| 52 | sys_umount | fs/super.c | char * | int | - | - | - | -| 54 | sys_ioctl | fs/ioctl.c | unsigned int | unsigned int | unsigned long | - | - | -| 55 | sys_fcntl | fs/fcntl.c | unsigned int | unsigned int | unsigned long | - | - | -| 57 | sys_setpgid | kernel/sys.c | pid_t | pid_t | - | - | - | -| 59 | sys_olduname | arch/i386/kernel/sys_i386.c | struct oldold_utsname * | - | - | - | - | -| 60 | sys_umask | kernel/sys.c | int | - | - | - | - | -| 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | -| 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | -| 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | -| 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | -| 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | -| 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | -| 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | -| 68 | sys_sgetmask | kernel/signal.c | - | - | - | - | - | -| 69 | sys_ssetmask | kernel/signal.c | int | - | - | - | - | -| 70 | sys_setreuid | kernel/sys.c | uid_t | uid_t | - | - | - | -| 71 | sys_setregid | kernel/sys.c | gid_t | gid_t | - | - | - | -| 72 | sys_sigsuspend | arch/i386/kernel/signal.c | int | int | old_sigset_t | - | - | -| 73 | sys_sigpending | kernel/signal.c | old_sigset_t * | - | - | - | - | -| 74 | sys_sethostname | kernel/sys.c | char * | int | - | - | - | -| 75 | sys_setrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | -| 76 | sys_getrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | -| 77 | sys_getrusage | kernel/sys.c | int | struct rusage * | - | - | - | -| 78 | sys_gettimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | -| 79 | sys_settimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | -| 80 | sys_getgroups | kernel/sys.c | int | gid_t * | - | - | - | -| 81 | sys_setgroups | kernel/sys.c | int | gid_t * | - | - | - | - -| 83 | sys_symlink | fs/namei.c | const char * | const char * | - | - | - | -| 84 | sys_lstat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | -| 85 | sys_readlink | fs/stat.c | const char * | char * | int | - | - | -| 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | -| 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | -| 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | -| 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | -| 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | -| 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | -| 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | -| 93 | sys_ftruncate | fs/open.c | unsigned int | unsigned long | - | - | - | -| 94 | sys_fchmod | fs/open.c | unsigned int | mode_t | - | - | - | -| 95 | sys_fchown | fs/open.c | unsigned int | uid_t | gid_t | - | - | -| 96 | sys_getpriority | kernel/sys.c | int | int | - | - | - | -| 97 | sys_setpriority | kernel/sys.c | int | int | int | - | - | -| 99 | sys_statfs | fs/open.c | const char * | struct statfs * | - | - | - | -| 100 | sys_fstatfs | fs/open.c | unsigned int | struct statfs * | - | - | - | -| 101 | sys_ioperm | arch/i386/kernel/ioport.c | unsigned long | unsigned long | int | - | - | -| 102 | sys_socketcall | net/socket.c | int | unsigned long * | - | - | - | -| 103 | sys_syslog | kernel/printk.c | int | char * | int | - | - | -| 104 | sys_setitimer | kernel/itimer.c | int | struct itimerval * | struct itimerval * | - | - | -| 105 | sys_getitimer | kernel/itimer.c | int | struct itimerval * | - | - | - | -| 106 | sys_newstat | fs/stat.c | char * | struct stat * | - | - | - | -| 107 | sys_newlstat | fs/stat.c | char * | struct stat * | - | - | - | -| 108 | sys_newfstat | fs/stat.c | unsigned int | struct stat * | - | - | - | -| 109 | sys_uname | arch/i386/kernel/sys_i386.c | struct old_utsname * | - | - | - | - | -| 110 | sys_iopl | arch/i386/kernel/ioport.c | unsigned long | - | - | - | - | -| 111 | sys_vhangup | fs/open.c | - | - | - | - | - | -| 112 | sys_idle | arch/i386/kernel/process.c | - | - | - | - | - | -| 113 | sys_vm86old | arch/i386/kernel/vm86.c | unsigned long | struct vm86plus_struct * | - | - | - | -| 114 | sys_wait4 | kernel/exit.c | pid_t | unsigned long * | int options | struct rusage * | - | -| 115 | sys_swapoff | mm/swapfile.c | const char * | - | - | - | - | -| 116 | sys_sysinfo | kernel/info.c | struct sysinfo * | - | - | - | - | -| 117 | sys_ipc(*Note) | arch/i386/kernel/sys_i386.c | uint | int | int | int | void * | -| 118 | sys_fsync | fs/buffer.c | unsigned int | - | - | - | - | -| 119 | sys_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | -| 120 | sys_clone | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 121 | sys_setdomainname | kernel/sys.c | char * | int | - | - | - | -| 122 | sys_newuname | kernel/sys.c | struct new_utsname * | - | - | - | - | -| 123 | sys_modify_ldt | arch/i386/kernel/ldt.c | int | void * | unsigned long | - | - | -| 124 | sys_adjtimex | kernel/time.c | struct timex * | - | - | - | - | -| 125 | sys_mprotect | mm/mprotect.c | unsigned long | size_t | unsigned long | - | - | -| 126 | sys_sigprocmask | kernel/signal.c | int | old_sigset_t * | old_sigset_t * | - | - | -| 127 | sys_create_module | kernel/module.c | const char * | size_t | - | - | - | -| 128 | sys_init_module | kernel/module.c | const char * | struct module * | - | - | - | -| 129 | sys_delete_module | kernel/module.c | const char * | - | - | - | - | -| 130 | sys_get_kernel_syms | kernel/module.c | struct kernel_sym * | - | - | - | - | -| 131 | sys_quotactl | fs/dquot.c | int | const char * | int | caddr_t | - | -| 132 | sys_getpgid | kernel/sys.c | pid_t | - | - | - | - | -| 133 | sys_fchdir | fs/open.c | unsigned int | - | - | - | - | -| 134 | sys_bdflush | fs/buffer.c | int | long | - | - | - | -| 135 | sys_sysfs | fs/super.c | int | unsigned long | unsigned long | - | - | -| 136 | sys_personality | kernel/exec_domain.c | unsigned long | - | - | - | - | -| 138 | sys_setfsuid | kernel/sys.c | uid_t | - | - | - | - | -| 139 | sys_setfsgid | kernel/sys.c | gid_t | - | - | - | - | -| 140 | sys_llseek | fs/read_write.c | unsigned int | unsigned long | unsigned long | loff_t * | unsigned int | -| 141 | sys_getdents | fs/readdir.c | unsigned int | void * | unsigned int | - | - | -| 142 | sys_select | fs/select.c | int | fd_set * | fd_set * | fd_set * | struct timeval * | -| 143 | sys_flock | fs/locks.c | unsigned int | unsigned int | - | - | - | -| 144 | sys_msync | mm/filemap.c | unsigned long | size_t | int | - | - | -| 145 | sys_readv | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | -| 146 | sys_writev | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | -| 147 | sys_getsid | kernel/sys.c | pid_t | - | - | - | - | -| 148 | sys_fdatasync | fs/buffer.c | unsigned int | - | - | - | - | -| 149 | sys_sysctl | kernel/sysctl.c | struct __sysctl_args * | - | - | - | - | -| 150 | sys_mlock | mm/mlock.c | unsigned long | size_t | - | - | - | -| 151 | sys_munlock | mm/mlock.c | unsigned long | size_t | - | - | - | -| 152 | sys_mlockall | mm/mlock.c | int | - | - | - | - | -| 153 | sys_munlockall | mm/mlock.c | - | - | - | - | - | -| 154 | sys_sched_setparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | -| 155 | sys_sched_getparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | -| 156 | sys_sched_setscheduler | kernel/sched.c | pid_t | int | struct sched_param * | - | - | -| 157 | sys_sched_getscheduler | kernel/sched.c | pid_t | - | - | - | - | -| 158 | sys_sched_yield | kernel/sched.c | - | - | - | - | - | -| 159 | sys_sched_get_priority_max | kernel/sched.c | int | - | - | - | - | -| 160 | sys_sched_get_priority_min | kernel/sched.c | int | - | - | - | - | -| 161 | sys_sched_rr_get_interval | kernel/sched.c | pid_t | struct timespec * | - | - | - | -| 162 | sys_nanosleep | kernel/sched.c | struct timespec * | struct timespec * | - | - | - | -| 163 | sys_mremap | mm/mremap.c | unsigned long | unsigned long | unsigned long | unsigned long | - | -| 164 | sys_setresuid | kernel/sys.c | uid_t | uid_t | uid_t | - | - | -| 165 | sys_getresuid | kernel/sys.c | uid_t * | uid_t * | uid_t * | - | - | -| 166 | sys_vm86 | arch/i386/kernel/vm86.c | struct vm86_struct * | - | - | - | - | -| 167 | sys_query_module | kernel/module.c | const char * | int | char * | size_t | size_t * | -| 168 | sys_poll | fs/select.c | struct pollfd * | unsigned int | long | - | - | -| 169 | sys_nfsservctl | fs/filesystems.c | int | void * | void * | - | - | -| 170 | sys_setresgid | kernel/sys.c | gid_t | gid_t | gid_t | - | - | -| 171 | sys_getresgid | kernel/sys.c | gid_t * | gid_t * | gid_t * | - | - | -| 172 | sys_prctl | kernel/sys.c | int | unsigned long | unsigned long | unsigned long | unsigned long | -| 173 | sys_rt_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | -| 174 | sys_rt_sigaction | kernel/signal.c | int | const struct sigaction * | struct sigaction * | size_t | - | -| 175 | sys_rt_sigprocmask | kernel/signal.c | int | sigset_t * | sigset_t * | size_t | - | -| 176 | sys_rt_sigpending | kernel/signal.c | sigset_t * | size_t | - | - | - | -| 177 | sys_rt_sigtimedwait | kernel/signal.c | const sigset_t * | siginfo_t * | const struct timespec * | size_t | - | -| 178 | sys_rt_sigqueueinfo | kernel/signal.c | int | int | siginfo_t * | - | - | -| 179 | sys_rt_sigsuspend | arch/i386/kernel/signal.c | sigset_t * | size_t | - | - | - | -| 180 | sys_pread | fs/read_write.c | unsigned int | char * | size_t | loff_t | - | -| 181 | sys_pwrite | fs/read_write.c | unsigned int | const char * | size_t | loff_t | - | -| 182 | sys_chown | fs/open.c | const char * | uid_t | gid_t | - | - | -| 183 | sys_getcwd | fs/dcache.c | char * | unsigned long | - | - | - | -| 184 | sys_capget | kernel/capability.c | cap_user_header_t | cap_user_data_t | - | - | - | -| 185 | sys_capset | kernel/capability.c | cap_user_header_t | const cap_user_data_t | - | - | - | -| 186 | sys_sigaltstack | arch/i386/kernel/signal.c | const stack_t * | stack_t * | - | - | - | -| 187 | sys_sendfile | mm/filemap.c | int | int | off_t * | size_t | - | -| 190 | sys_vfork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| | | | | | | | | \ No newline at end of file +| S | EAX | Name | Source | EBX | ECX | EDX | ESX | EDI | +|:--|:----|:---------------------------|:----------------------------|:-------------------------|:-----------------------------|:------------------------|:----------------|:-----------------| +| 1 | 1 | sys_exit | kernel/exit.c | int | - | - | - | - | +| | 2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | +| 1 | 3 | sys_read | fs/read_write.c | unsigned int | char * | size_t | - | - | +| 1 | 4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t | - | - | +| 1 | 5 | sys_open | fs/open.c | const char * | int | int | - | - | +| 1 | 6 | sys_close | fs/open.c | int | - | - | - | - | +| 1 | 7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int | - | - | +| | 8 | sys_creat | fs/open.c | const char * | int | - | - | - | +| | 9 | sys_link | fs/namei.c | const char * | const char * | - | - | - | +| | 10 | sys_unlink | fs/namei.c | const char * | - | - | - | - | +| 1 | 11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | +| 1 | 12 | sys_chdir | fs/open.c | const char * | - | - | - | - | +| | 13 | sys_time | kernel/time.c | int * | - | - | - | - | +| | 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | +| | 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | +| | 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | +| | 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | +| | 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | +| 1 | 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | +| | 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | +| | 22 | sys_oldumount | fs/super.c | char * | - | - | - | - | +| | 23 | sys_setuid | kernel/sys.c | uid_t | - | - | - | - | +| | 24 | sys_getuid | kernel/sched.c | - | - | - | - | - | +| | 25 | sys_stime | kernel/time.c | int * | - | - | - | - | +| | 26 | sys_ptrace | arch/i386/kernel/ptrace.c | long | long | long | long | - | +| | 27 | sys_alarm | kernel/sched.c | unsigned int | - | - | - | - | +| | 28 | sys_fstat | fs/stat.c | unsigned int | struct __old_kernel_stat * | - | - | - | +| | 29 | sys_pause | arch/i386/kernel/sys_i386.c | - | - | - | - | - | +| | 30 | sys_utime | fs/open.c | char * | struct utimbuf * | - | - | - | +| | 33 | sys_access | fs/open.c | const char * | int | - | - | - | +| 1 | 34 | sys_nice | kernel/sched.c | int | - | - | - | - | +| | 36 | sys_sync | fs/buffer.c | - | - | - | - | - | +| | 37 | sys_kill | kernel/signal.c | int | int | - | - | - | +| | 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | +| | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | +| | 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | +| | 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | +| | 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | +| | 43 | sys_times | kernel/sys.c | struct tms * | - | - | - | - | +| | 45 | sys_brk | mm/mmap.c | unsigned long | - | - | - | - | +| | 46 | sys_setgid | kernel/sys.c | gid_t | - | - | - | - | +| | 47 | sys_getgid | kernel/sched.c | - | - | - | - | - | +| | 48 | sys_signal | kernel/signal.c | int | __sighandler_t | - | - | - | +| | 49 | sys_geteuid | kernel/sched.c | - | - | - | - | - | +| | 50 | sys_getegid | kernel/sched.c | - | - | - | - | - | +| | 51 | sys_acct | kernel/acct.c | const char * | - | - | - | - | +| | 52 | sys_umount | fs/super.c | char * | int | - | - | - | +| | 54 | sys_ioctl | fs/ioctl.c | unsigned int | unsigned int | unsigned long | - | - | +| | 55 | sys_fcntl | fs/fcntl.c | unsigned int | unsigned int | unsigned long | - | - | +| | 57 | sys_setpgid | kernel/sys.c | pid_t | pid_t | - | - | - | +| | 59 | sys_olduname | arch/i386/kernel/sys_i386.c | struct oldold_utsname * | - | - | - | - | +| | 60 | sys_umask | kernel/sys.c | int | - | - | - | - | +| | 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | +| | 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | +| | 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | +| | 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | +| | 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | +| | 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | +| | 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | +| | 68 | sys_sgetmask | kernel/signal.c | - | - | - | - | - | +| | 69 | sys_ssetmask | kernel/signal.c | int | - | - | - | - | +| | 70 | sys_setreuid | kernel/sys.c | uid_t | uid_t | - | - | - | +| | 71 | sys_setregid | kernel/sys.c | gid_t | gid_t | - | - | - | +| | 72 | sys_sigsuspend | arch/i386/kernel/signal.c | int | int | old_sigset_t | - | - | +| | 73 | sys_sigpending | kernel/signal.c | old_sigset_t * | - | - | - | - | +| | 74 | sys_sethostname | kernel/sys.c | char * | int | - | - | - | +| | 75 | sys_setrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | +| | 76 | sys_getrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | +| | 77 | sys_getrusage | kernel/sys.c | int | struct rusage * | - | - | - | +| | 78 | sys_gettimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | +| | 79 | sys_settimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | +| | 80 | sys_getgroups | kernel/sys.c | int | gid_t * | - | - | - | +| | 81 | sys_setgroups | kernel/sys.c | int | gid_t * | - | - | - | +| | 83 | sys_symlink | fs/namei.c | const char * | const char * | - | - | - | +| | 84 | sys_lstat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | +| | 85 | sys_readlink | fs/stat.c | const char * | char * | int | - | - | +| | 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | +| | 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | +| 1 | 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | +| | 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | +| | 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | +| | 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | +| | 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | +| | 93 | sys_ftruncate | fs/open.c | unsigned int | unsigned long | - | - | - | +| | 94 | sys_fchmod | fs/open.c | unsigned int | mode_t | - | - | - | +| | 95 | sys_fchown | fs/open.c | unsigned int | uid_t | gid_t | - | - | +| | 96 | sys_getpriority | kernel/sys.c | int | int | - | - | - | +| | 97 | sys_setpriority | kernel/sys.c | int | int | int | - | - | +| | 99 | sys_statfs | fs/open.c | const char * | struct statfs * | - | - | - | +| | 100 | sys_fstatfs | fs/open.c | unsigned int | struct statfs * | - | - | - | +| | 101 | sys_ioperm | arch/i386/kernel/ioport.c | unsigned long | unsigned long | int | - | - | +| | 102 | sys_socketcall | net/socket.c | int | unsigned long * | - | - | - | +| | 103 | sys_syslog | kernel/printk.c | int | char * | int | - | - | +| | 104 | sys_setitimer | kernel/itimer.c | int | struct itimerval * | struct itimerval * | - | - | +| | 105 | sys_getitimer | kernel/itimer.c | int | struct itimerval * | - | - | - | +| | 106 | sys_newstat | fs/stat.c | char * | struct stat * | - | - | - | +| | 107 | sys_newlstat | fs/stat.c | char * | struct stat * | - | - | - | +| | 108 | sys_newfstat | fs/stat.c | unsigned int | struct stat * | - | - | - | +| | 109 | sys_uname | arch/i386/kernel/sys_i386.c | struct old_utsname * | - | - | - | - | +| | 110 | sys_iopl | arch/i386/kernel/ioport.c | unsigned long | - | - | - | - | +| | 111 | sys_vhangup | fs/open.c | - | - | - | - | - | +| | 112 | sys_idle | arch/i386/kernel/process.c | - | - | - | - | - | +| | 113 | sys_vm86old | arch/i386/kernel/vm86.c | unsigned long | struct vm86plus_struct * | - | - | - | +| | 114 | sys_wait4 | kernel/exit.c | pid_t | unsigned long * | int options | struct rusage * | - | +| | 115 | sys_swapoff | mm/swapfile.c | const char * | - | - | - | - | +| | 116 | sys_sysinfo | kernel/info.c | struct sysinfo * | - | - | - | - | +| | 117 | sys_ipc(*Note) | arch/i386/kernel/sys_i386.c | uint | int | int | int | void * | +| | 118 | sys_fsync | fs/buffer.c | unsigned int | - | - | - | - | +| | 119 | sys_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | +| | 120 | sys_clone | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | +| | 121 | sys_setdomainname | kernel/sys.c | char * | int | - | - | - | +| | 122 | sys_newuname | kernel/sys.c | struct new_utsname * | - | - | - | - | +| | 123 | sys_modify_ldt | arch/i386/kernel/ldt.c | int | void * | unsigned long | - | - | +| | 124 | sys_adjtimex | kernel/time.c | struct timex * | - | - | - | - | +| | 125 | sys_mprotect | mm/mprotect.c | unsigned long | size_t | unsigned long | - | - | +| | 126 | sys_sigprocmask | kernel/signal.c | int | old_sigset_t * | old_sigset_t * | - | - | +| | 127 | sys_create_module | kernel/module.c | const char * | size_t | - | - | - | +| | 128 | sys_init_module | kernel/module.c | const char * | struct module * | - | - | - | +| | 129 | sys_delete_module | kernel/module.c | const char * | - | - | - | - | +| | 130 | sys_get_kernel_syms | kernel/module.c | struct kernel_sym * | - | - | - | - | +| | 131 | sys_quotactl | fs/dquot.c | int | const char * | int | caddr_t | - | +| | 132 | sys_getpgid | kernel/sys.c | pid_t | - | - | - | - | +| | 133 | sys_fchdir | fs/open.c | unsigned int | - | - | - | - | +| | 134 | sys_bdflush | fs/buffer.c | int | long | - | - | - | +| | 135 | sys_sysfs | fs/super.c | int | unsigned long | unsigned long | - | - | +| | 136 | sys_personality | kernel/exec_domain.c | unsigned long | - | - | - | - | +| | 138 | sys_setfsuid | kernel/sys.c | uid_t | - | - | - | - | +| | 139 | sys_setfsgid | kernel/sys.c | gid_t | - | - | - | - | +| | 140 | sys_llseek | fs/read_write.c | unsigned int | unsigned long | unsigned long | loff_t * | unsigned int | +| | 141 | sys_getdents | fs/readdir.c | unsigned int | void * | unsigned int | - | - | +| | 142 | sys_select | fs/select.c | int | fd_set * | fd_set * | fd_set * | struct timeval * | +| | 143 | sys_flock | fs/locks.c | unsigned int | unsigned int | - | - | - | +| | 144 | sys_msync | mm/filemap.c | unsigned long | size_t | int | - | - | +| | 145 | sys_readv | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | +| | 146 | sys_writev | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | +| | 147 | sys_getsid | kernel/sys.c | pid_t | - | - | - | - | +| | 148 | sys_fdatasync | fs/buffer.c | unsigned int | - | - | - | - | +| | 149 | sys_sysctl | kernel/sysctl.c | struct __sysctl_args * | - | - | - | - | +| | 150 | sys_mlock | mm/mlock.c | unsigned long | size_t | - | - | - | +| | 151 | sys_munlock | mm/mlock.c | unsigned long | size_t | - | - | - | +| | 152 | sys_mlockall | mm/mlock.c | int | - | - | - | - | +| | 153 | sys_munlockall | mm/mlock.c | - | - | - | - | - | +| | 154 | sys_sched_setparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | +| | 155 | sys_sched_getparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | +| | 156 | sys_sched_setscheduler | kernel/sched.c | pid_t | int | struct sched_param * | - | - | +| | 157 | sys_sched_getscheduler | kernel/sched.c | pid_t | - | - | - | - | +| | 158 | sys_sched_yield | kernel/sched.c | - | - | - | - | - | +| | 159 | sys_sched_get_priority_max | kernel/sched.c | int | - | - | - | - | +| | 160 | sys_sched_get_priority_min | kernel/sched.c | int | - | - | - | - | +| | 161 | sys_sched_rr_get_interval | kernel/sched.c | pid_t | struct timespec * | - | - | - | +| | 162 | sys_nanosleep | kernel/sched.c | struct timespec * | struct timespec * | - | - | - | +| | 163 | sys_mremap | mm/mremap.c | unsigned long | unsigned long | unsigned long | unsigned long | - | +| | 164 | sys_setresuid | kernel/sys.c | uid_t | uid_t | uid_t | - | - | +| | 165 | sys_getresuid | kernel/sys.c | uid_t * | uid_t * | uid_t * | - | - | +| | 166 | sys_vm86 | arch/i386/kernel/vm86.c | struct vm86_struct * | - | - | - | - | +| | 167 | sys_query_module | kernel/module.c | const char * | int | char * | size_t | size_t * | +| | 168 | sys_poll | fs/select.c | struct pollfd * | unsigned int | long | - | - | +| | 169 | sys_nfsservctl | fs/filesystems.c | int | void * | void * | - | - | +| | 170 | sys_setresgid | kernel/sys.c | gid_t | gid_t | gid_t | - | - | +| | 171 | sys_getresgid | kernel/sys.c | gid_t * | gid_t * | gid_t * | - | - | +| | 172 | sys_prctl | kernel/sys.c | int | unsigned long | unsigned long | unsigned long | unsigned long | +| | 173 | sys_rt_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | +| | 174 | sys_rt_sigaction | kernel/signal.c | int | const struct sigaction * | struct sigaction * | size_t | - | +| | 175 | sys_rt_sigprocmask | kernel/signal.c | int | sigset_t * | sigset_t * | size_t | - | +| | 176 | sys_rt_sigpending | kernel/signal.c | sigset_t * | size_t | - | - | - | +| | 177 | sys_rt_sigtimedwait | kernel/signal.c | const sigset_t * | siginfo_t * | const struct timespec * | size_t | - | +| | 178 | sys_rt_sigqueueinfo | kernel/signal.c | int | int | siginfo_t * | - | - | +| | 179 | sys_rt_sigsuspend | arch/i386/kernel/signal.c | sigset_t * | size_t | - | - | - | +| | 180 | sys_pread | fs/read_write.c | unsigned int | char * | size_t | loff_t | - | +| | 181 | sys_pwrite | fs/read_write.c | unsigned int | const char * | size_t | loff_t | - | +| | 182 | sys_chown | fs/open.c | const char * | uid_t | gid_t | - | - | +| 1 | 183 | sys_getcwd | fs/dcache.c | char * | unsigned long | - | - | - | +| | 184 | sys_capget | kernel/capability.c | cap_user_header_t | cap_user_data_t | - | - | - | +| | 185 | sys_capset | kernel/capability.c | cap_user_header_t | const cap_user_data_t | - | - | - | +| | 186 | sys_sigaltstack | arch/i386/kernel/signal.c | const stack_t * | stack_t * | - | - | - | +| | 187 | sys_sendfile | mm/filemap.c | int | int | off_t * | size_t | - | +| 1 | 190 | sys_vfork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | +| | | | | | | | | | \ No newline at end of file diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index 0ca358e..bd1a51b 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -192,6 +192,7 @@ set(SOURCE_FILES src/fs/initrd.c src/fs/vfs.c src/fs/read_write.c + src/fs/open.c src/sys/module.c src/sys/unistd.c @@ -237,6 +238,7 @@ set(SOURCE_FILES src/libc/unistd/waitpid.c src/libc/unistd/chdir.c src/libc/unistd/getcwd.c + src/libc/unistd/close.c src/mem/kheap.c src/mem/paging.c diff --git a/mentos/inc/fs/fcntl.h b/mentos/inc/fs/fcntl.h index 15f9324..7c8a835 100644 --- a/mentos/inc/fs/fcntl.h +++ b/mentos/inc/fs/fcntl.h @@ -10,31 +10,15 @@ /// Open for reading only. #define O_RDONLY 0x00 - /// Open for writing only. #define O_WRONLY 0x01 - /// Open for reading and writing. #define O_RDWR 0x02 - /// If the file exists, this flag has no effect. Otherwise, the file is created. #define O_CREAT 0x40 - /// The file offset will be set to the end of the file prior to each write. #define O_APPEND 0x400 -/// @brief Given a pathname for a file, open() returns a file -/// descriptor, a small, nonnegative integer for use in -/// subsequent system calls. -/// @param pathname A pathname for a file. -/// @param flags Used to set the file status flags and file access modes -/// of the open file description. -/// @param mode Specifies the file mode bits be applied when a new file -/// is created. -/// @return Returns a file descriptor, a small, nonnegative integer -/// for use in subsequent system calls. -int sys_open(const char *pathname, int flags, mode_t mode); - /// @brief Deletes the file whose name is specified in filename. /// @param pathname A path to a file. /// @return On success, zero is returned. On error, -1 is returned, diff --git a/mentos/inc/fs/open.h b/mentos/inc/fs/open.h new file mode 100644 index 0000000..2b19bc2 --- /dev/null +++ b/mentos/inc/fs/open.h @@ -0,0 +1,25 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.h +/// @brief +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "stddef.h" +#include "vfs.h" + +/// @brief Given a pathname for a file, open() returns a file +/// descriptor, a small, nonnegative integer for use in +/// subsequent system calls. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes +/// of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file +/// is created. +/// @return Returns a file descriptor, a small, nonnegative integer +/// for use in subsequent system calls. +int sys_open(const char *pathname, int flags, mode_t mode); + +/// @brief +/// @param fd +/// @return +int sys_close(int fd); \ No newline at end of file diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index ef8ef9a..db8cf0d 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -12,22 +12,16 @@ /// Identifies a file. #define FS_FILE 0x01 - /// Identifies a directory. #define FS_DIRECTORY 0x02 - /// Identifies a character devies. #define FS_CHARDEVICE 0x04 - /// Identifies a block devies. #define FS_BLOCKDEVICE 0x08 - /// Identifies a pipe. #define FS_PIPE 0x10 - /// Identifies a symbolic link. #define FS_SYMLINK 0x20 - /// Identifies a mount-point. #define FS_MOUNTPOINT 0x40 diff --git a/mentos/inc/sys/unistd.h b/mentos/inc/sys/unistd.h index 9b3dc3a..d49cce1 100644 --- a/mentos/inc/sys/unistd.h +++ b/mentos/inc/sys/unistd.h @@ -47,10 +47,10 @@ ssize_t write(int fd, void *buf, size_t nbytes); /// use in subsequent system calls. int open(const char *pathname, int flags, mode_t mode); -/// @brief Close a file descriptor. -/// @param fildes The file descriptor. -/// @return The result of the operation. -int close(int fildes); +/// @brief Close a file descriptor. +/// @param fd The file descriptor. +/// @return The result of the operation. +int close(int fd); /// @brief Removes the given directory. /// @param path The path to the directory to remove. diff --git a/mentos/src/fs/fcntl.c b/mentos/src/fs/fcntl.c index a06e84f..6a8be48 100644 --- a/mentos/src/fs/fcntl.c +++ b/mentos/src/fs/fcntl.c @@ -4,109 +4,26 @@ /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "vfs.h" -#include "debug.h" -#include "kheap.h" -#include "shell.h" -#include "errno.h" #include "fcntl.h" #include "string.h" -#include "kernel.h" - -int sys_open(const char *pathname, int flags, mode_t mode) -{ - // Allocate a variable for the path. - char absolute_path[MAX_PATH_LENGTH]; - - // Copy the path to the working variable. - strcpy(absolute_path, pathname); - - // If the first character is not the '/' then get the absolute path. - if (absolute_path[0] != '/') - { - if (!get_absolute_path(absolute_path)) - { - dbg_print("Cannot get the absolute path.\n"); - return -1; - } - } - - // Search for an unused fd. - for (current_fd = 0; current_fd < MAX_OPEN_FD; ++current_fd) - { - if (fd_list[current_fd].mountpoint_id == -1) - { - break; - } - } - - // Check if there is not fd available. - if (current_fd == MAX_OPEN_FD) - { - //errno = EMFILE; - return -1; - } - - // Get the mountpoint. - mountpoint_t *mp = get_mountpoint(absolute_path); - if (mp == NULL) - { - //errno = ENODEV; - return -1; - } - - // Check if the function is implemented. - if (mp->operations.open_f == NULL) - { - //errno = ENOSYS; - // Reset the file descriptor. - close(current_fd); - return -1; - } - - int32_t fs_spec_id = mp->operations.open_f(absolute_path, flags, mode); - if (fs_spec_id == -1) - { - // Reset the file descriptor. - close(current_fd); - return -1; - } - - // Set the file descriptor id. - fd_list[current_fd].fs_spec_id = fs_spec_id; - - // Set the mount point id. - fd_list[current_fd].mountpoint_id = mp->mp_id; - - // Reset the offset. - fd_list[current_fd].offset = 0; - - // Set the flags. - fd_list[current_fd].flags_mask = flags; - - // Return the file descriptor and increment it. - return (current_fd++); -} +#include "vfs.h" int remove(const char *pathname) { - char absolute_path[MAX_PATH_LENGTH]; - strcpy(absolute_path, pathname); - if (pathname[0] != '/') - { - get_absolute_path(absolute_path); - } + char absolute_path[MAX_PATH_LENGTH]; + strcpy(absolute_path, pathname); + if (pathname[0] != '/') { + get_absolute_path(absolute_path); + } - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - return -1; - } + int32_t mp_id = get_mountpoint_id(absolute_path); + if (mp_id < 0) { + return -1; + } - if (mountpoint_list[mp_id].operations.remove_f == NULL) - { - return -1; - } + if (mountpoint_list[mp_id].operations.remove_f == NULL) { + return -1; + } - return mountpoint_list[mp_id].operations.remove_f(absolute_path); + return mountpoint_list[mp_id].operations.remove_f(absolute_path); } diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index 9ac4fe4..23e2815 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -15,6 +15,7 @@ #include "bitops.h" #include "initrd.h" #include "string.h" +#include "open.h" char *module_start[MAX_MODULES]; diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c new file mode 100644 index 0000000..72cec8d --- /dev/null +++ b/mentos/src/fs/open.c @@ -0,0 +1,98 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.c +/// @brief +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "open.h" + +#include "string.h" +#include "debug.h" +#include "stdio.h" +#include "vfs.h" + +int sys_open(const char *pathname, int flags, mode_t mode) +{ + // Allocate a variable for the path. + char absolute_path[MAX_PATH_LENGTH]; + + // Copy the path to the working variable. + strcpy(absolute_path, pathname); + + // If the first character is not the '/' then get the absolute path. + if (absolute_path[0] != '/') { + if (!get_absolute_path(absolute_path)) { + dbg_print("Cannot get the absolute path.\n"); + return -1; + } + } + + // Search for an unused fd. + for (current_fd = 0; current_fd < MAX_OPEN_FD; ++current_fd) { + if (fd_list[current_fd].mountpoint_id == -1) { + break; + } + } + + // Check if there is not fd available. + if (current_fd == MAX_OPEN_FD) { + //errno = EMFILE; + return -1; + } + + // Get the mountpoint. + mountpoint_t *mp = get_mountpoint(absolute_path); + if (mp == NULL) { + //errno = ENODEV; + return -1; + } + + // Check if the function is implemented. + if (mp->operations.open_f == NULL) { + //errno = ENOSYS; + // Reset the file descriptor. + sys_close(current_fd); + return -1; + } + + int32_t fs_spec_id = mp->operations.open_f(absolute_path, flags, mode); + if (fs_spec_id == -1) { + // Reset the file descriptor. + sys_close(current_fd); + return -1; + } + + // Set the file descriptor id. + fd_list[current_fd].fs_spec_id = fs_spec_id; + + // Set the mount point id. + fd_list[current_fd].mountpoint_id = mp->mp_id; + + // Reset the offset. + fd_list[current_fd].offset = 0; + + // Set the flags. + fd_list[current_fd].flags_mask = flags; + + // Return the file descriptor and increment it. + return (current_fd++); +} + +int sys_close(int fd) +{ + if (fd < 0) { + return -1; + } + if (fd_list[fd].fs_spec_id >= -1) { + int mp_id = fd_list[fd].mountpoint_id; + if (mountpoint_list[mp_id].operations.close_f != NULL) { + int fs_fd = fd_list[fd].fs_spec_id; + mountpoint_list[mp_id].operations.close_f(fs_fd); + } + fd_list[fd].fs_spec_id = -1; + fd_list[fd].mountpoint_id = -1; + } else { + return -1; + } + return 0; +} diff --git a/mentos/src/fs/read_write.c b/mentos/src/fs/read_write.c index fcf2531..41346fc 100644 --- a/mentos/src/fs/read_write.c +++ b/mentos/src/fs/read_write.c @@ -12,6 +12,7 @@ #include "unistd.h" #include "keyboard.h" #include "video.h" +#include "open.h" ssize_t sys_read(int fd, void *buf, size_t nbytes) { diff --git a/mentos/src/libc/unistd/close.c b/mentos/src/libc/unistd/close.c new file mode 100644 index 0000000..5be29f7 --- /dev/null +++ b/mentos/src/libc/unistd/close.c @@ -0,0 +1,20 @@ +/// MentOS, The Mentoring Operating system project +/// @file close.c +/// @brief +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "unistd.h" +#include "errno.h" +#include "syscall.h" + +int close(int fd) +{ + int retval; + DEFN_SYSCALL1(retval, __NR_close, fd); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} diff --git a/mentos/src/sys/unistd.c b/mentos/src/sys/unistd.c index a703cae..91a72f5 100644 --- a/mentos/src/sys/unistd.c +++ b/mentos/src/sys/unistd.c @@ -4,34 +4,11 @@ /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "vfs.h" -#include "stdio.h" -#include "fcntl.h" -#include "kheap.h" -#include "errno.h" -#include "initrd.h" -#include "string.h" #include "unistd.h" -int close(int fildes) -{ - if (fildes < 0) { - return -1; - } - if (fd_list[fildes].fs_spec_id >= -1) { - int mp_id = fd_list[fildes].mountpoint_id; - if (mountpoint_list[mp_id].operations.close_f != NULL) { - int fs_fd = fd_list[fildes].fs_spec_id; - mountpoint_list[mp_id].operations.close_f(fs_fd); - } - fd_list[fildes].fs_spec_id = -1; - fd_list[fildes].mountpoint_id = -1; - } else { - return -1; - } - - return 0; -} +#include "string.h" +#include "stdio.h" +#include "vfs.h" int rmdir(const char *path) { diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index ad9939e..a325879 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -18,6 +18,7 @@ #include "irqflags.h" #include "scheduler.h" #include "read_write.h" +#include "open.h" /// @brief The signature of a function call. typedef int (*SystemCall)(); @@ -48,6 +49,7 @@ void syscall_init() sys_call_table[__NR_read] = (SystemCall)sys_read; sys_call_table[__NR_write] = (SystemCall)sys_write; sys_call_table[__NR_open] = (SystemCall)sys_open; + sys_call_table[__NR_close] = (SystemCall)sys_close; sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; sys_call_table[__NR_vfork] = (SystemCall)sys_vfork; @@ -57,7 +59,6 @@ void syscall_init() sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; - sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; sys_call_table[__NR_brk] = (SystemCall)umalloc; // TODO: sys_brk sys_call_table[__NR_free] = (SystemCall)ufree; // TODO: sys_brk From c0e5808656ef686e5f31297a9d55c686b262e900 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Fri, 10 May 2019 17:18:34 +0200 Subject: [PATCH 08/21] Standardize most system calls --- doc/syscall.txt | 8 +- mentos/CMakeLists.txt | 6 +- mentos/inc/fs/open.h | 25 -- mentos/inc/fs/read_write.h | 23 -- mentos/inc/kernel/sys.h | 15 -- mentos/inc/process/process.h | 12 - mentos/inc/process/scheduler.h | 14 - mentos/inc/sys/dirent.h | 5 +- mentos/inc/sys/stat.h | 1 - mentos/inc/system/syscall.h | 433 ++++-------------------------- mentos/inc/system/syscall_types.h | 220 +++++++++++++++ mentos/src/fs/initrd.c | 3 +- mentos/src/fs/open.c | 3 +- mentos/src/fs/read_write.c | 2 - mentos/src/fs/readdir.c | 28 ++ mentos/src/{sys => fs}/stat.c | 4 +- mentos/src/kernel/sys.c | 1 - mentos/src/libc/unistd/mkdir.c | 21 ++ mentos/src/libc/unistd/stat.c | 21 ++ mentos/src/sys/dirent.c | 112 ++++---- mentos/src/system/syscall.c | 6 +- mentos/src/ui/shell/shell.c | 2 +- 22 files changed, 408 insertions(+), 557 deletions(-) delete mode 100644 mentos/inc/fs/open.h delete mode 100644 mentos/inc/fs/read_write.h delete mode 100644 mentos/inc/kernel/sys.h create mode 100644 mentos/inc/system/syscall_types.h create mode 100644 mentos/src/fs/readdir.c rename mentos/src/{sys => fs}/stat.c (95%) create mode 100644 mentos/src/libc/unistd/mkdir.c create mode 100644 mentos/src/libc/unistd/stat.c diff --git a/doc/syscall.txt b/doc/syscall.txt index 91417de..3b00589 100644 --- a/doc/syscall.txt +++ b/doc/syscall.txt @@ -16,7 +16,7 @@ | | 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | | | 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | | | 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | -| | 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | +| 1 | 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | | | 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | | 1 | 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | | | 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | @@ -34,7 +34,7 @@ | | 36 | sys_sync | fs/buffer.c | - | - | - | - | - | | | 37 | sys_kill | kernel/signal.c | int | int | - | - | - | | | 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | -| | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | +| 1 | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | | | 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | | | 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | | | 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | @@ -55,7 +55,7 @@ | | 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | | | 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | | | 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | -| | 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | +| 1 | 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | | | 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | | | 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | | | 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | @@ -79,7 +79,7 @@ | | 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | | | 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | | 1 | 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | -| | 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | +| 1 | 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | | | 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | | | 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | | | 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index bd1a51b..a3657dd 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -193,6 +193,8 @@ set(SOURCE_FILES src/fs/vfs.c src/fs/read_write.c src/fs/open.c + src/fs/stat.c + src/fs/readdir.c src/sys/module.c src/sys/unistd.c @@ -239,6 +241,8 @@ set(SOURCE_FILES src/libc/unistd/chdir.c src/libc/unistd/getcwd.c src/libc/unistd/close.c + src/libc/unistd/stat.c + src/libc/unistd/mkdir.c src/mem/kheap.c src/mem/paging.c @@ -248,9 +252,7 @@ set(SOURCE_FILES src/misc/clock.c src/misc/debug.c src/sys/dirent.c - src/sys/stat.c src/sys/utsname.c - #src/sys/shm.c src/elf/elf.c src/descriptor_tables/gdt.c diff --git a/mentos/inc/fs/open.h b/mentos/inc/fs/open.h deleted file mode 100644 index 2b19bc2..0000000 --- a/mentos/inc/fs/open.h +++ /dev/null @@ -1,25 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file open.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "stddef.h" -#include "vfs.h" - -/// @brief Given a pathname for a file, open() returns a file -/// descriptor, a small, nonnegative integer for use in -/// subsequent system calls. -/// @param pathname A pathname for a file. -/// @param flags Used to set the file status flags and file access modes -/// of the open file description. -/// @param mode Specifies the file mode bits be applied when a new file -/// is created. -/// @return Returns a file descriptor, a small, nonnegative integer -/// for use in subsequent system calls. -int sys_open(const char *pathname, int flags, mode_t mode); - -/// @brief -/// @param fd -/// @return -int sys_close(int fd); \ No newline at end of file diff --git a/mentos/inc/fs/read_write.h b/mentos/inc/fs/read_write.h deleted file mode 100644 index 1e7a403..0000000 --- a/mentos/inc/fs/read_write.h +++ /dev/null @@ -1,23 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file read_write.c -/// @brief Read and write functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// @brief Read data from a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer. -/// @param nbytes The number of bytes to read. -/// @return The number of read characters. -ssize_t sys_read(int fd, void *buf, size_t nbytes); - -/// @brief Write data into a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer collecting data to written. -/// @param nbytes The number of bytes to write. -/// @return The number of written bytes. -ssize_t sys_write(int fd, void *buf, size_t nbytes); diff --git a/mentos/inc/kernel/sys.h b/mentos/inc/kernel/sys.h deleted file mode 100644 index 6b51478..0000000 --- a/mentos/inc/kernel/sys.h +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file sys.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// TODO: doxygen comment. -/// @brief -/// @param magic1 -/// @param magic2 -/// @param cmd -/// @param arg -int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg); diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index e7ae4a6..c59fd81 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -195,20 +195,8 @@ typedef struct task_struct { } task_struct; -/// @brief Create a child process. -pid_t sys_vfork(pt_regs *r); - // TODO: doxygen comment. char *get_current_dir_name(); -// TODO: doxygen comment. -void sys_getcwd(char *path, size_t size); - -// TODO: doxygen comment.s -void sys_chdir(char const *path); - -/// @brief Replaces the current process image with a new process image. -int sys_execve(pt_regs *r); - /// @brief Create and spawn the init process. struct task_struct *create_init_process(); diff --git a/mentos/inc/process/scheduler.h b/mentos/inc/process/scheduler.h index 0b1ca60..7aacb1c 100644 --- a/mentos/inc/process/scheduler.h +++ b/mentos/inc/process/scheduler.h @@ -62,21 +62,7 @@ void do_switch(task_struct *process, pt_regs *f); /// @param process The process that has to be executed void enter_user_jmp(uintptr_t location, uintptr_t stack); -/// Returns the process ID (PID) of the calling process. -pid_t sys_getpid(); - -/// Returns the parent process ID (PPID) of the calling process. -pid_t sys_getppid(); - /// @brief Sets the priority value of the given task. int set_user_nice(task_struct *p, long nice); -/// @brief Adds the increment to the priority value of the task. -int sys_nice(int increment); -/// @brief Suspends execution of the calling thread until a child specified -/// by pid argument has changed state. -pid_t sys_waitpid(pid_t pid, int *status, int options); - -/// The exit() function causes normal process termination. -void sys_exit(int exit_code); diff --git a/mentos/inc/sys/dirent.h b/mentos/inc/sys/dirent.h index 9ac6e2c..f2581ed 100644 --- a/mentos/inc/sys/dirent.h +++ b/mentos/inc/sys/dirent.h @@ -26,14 +26,11 @@ typedef struct dirent_t { /// @brief Contains information concerning a directory. typedef struct DIR { /// Filesystem directory handle. - int handle; - + int fd; /// The currently opened entry. ino_t cur_entry; - /// Path to the directory. char path[NAME_MAX + 1]; - /// Next directory item. dirent_t entry; } DIR; diff --git a/mentos/inc/sys/stat.h b/mentos/inc/sys/stat.h index 2037030..05aa9b6 100644 --- a/mentos/inc/sys/stat.h +++ b/mentos/inc/sys/stat.h @@ -38,5 +38,4 @@ typedef struct stat_t /// @return Returns a negative value on failure. int stat(const char *path, stat_t *buf); -// TODO: doxygen comment. int mkdir(const char *path, mode_t mode); diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index be57315..fa37331 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -6,7 +6,11 @@ #pragma once +#include "syscall_types.h" #include "kernel.h" +#include "dirent.h" +#include "types.h" +#include "stat.h" /// @brief Initialize the system calls. void syscall_init(); @@ -14,400 +18,67 @@ void syscall_init(); /// @brief Handler for the system calls. void syscall_handler(pt_regs *r); -// Return result in eax ("=a"). -// System call number in eax ("a"). -#define DEFN_SYSCALL0(__res, num) \ - __asm__ __volatile__("INT $0x80" : "=a"(__res) : "a"(num)) +/// The exit() function causes normal process termination. +void sys_exit(int exit_code); -// Return result in eax ("=a"). -// System call number in eax ("a"). -// p1 in ebx ("+b"). -#define DEFN_SYSCALL1(__res, num, p1) \ - __asm__ __volatile__("INT $0x80" : "=a"(__res), "+b"(p1) : "a"(num)) +/// @brief Read data from a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer. +/// @param nbytes The number of bytes to read. +/// @return The number of read characters. +ssize_t sys_read(int fd, void *buf, size_t nbytes); -#define DEFN_SYSCALL2(__res, num, p1, p2) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2) \ - : "a"(num)); +/// @brief Write data into a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer collecting data to written. +/// @param nbytes The number of bytes to write. +/// @return The number of written bytes. +ssize_t sys_write(int fd, void *buf, size_t nbytes); -#define DEFN_SYSCALL3(__res, num, p1, p2, p3) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3) \ - : "a"(num)) +/// @brief Given a pathname for a file, open() returns a file +/// descriptor, a small, nonnegative integer for use in +/// subsequent system calls. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes +/// of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file +/// is created. +/// @return Returns a file descriptor, a small, nonnegative integer +/// for use in subsequent system calls. +int sys_open(const char *pathname, int flags, mode_t mode); -#define DEFN_SYSCALL4(__res, num, p1, p2, p3, p4) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3), "+S"(p4) \ - : "a"(num)) +/// @brief +/// @param fd +/// @return +int sys_close(int fd); -#define DEFN_SYSCALL5(fn, num, P1, P2, P3, P4, P5) \ - int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \ - { \ - int __res; \ - __asm__ __volatile__("push eax\n movl %2,%%ebx; INT 0x80; pop %%ebx" \ - : "=a"(__res) \ - : "0"(num), "r"((int)(p1)), "c"((int)(p2)), \ - "d"((int)(p3)), "S"((int)(p4)), \ - "D"((int)(p5))); \ - return __res; \ - } +/// @brief Suspends execution of the calling thread until a child specified +/// by pid argument has changed state. +pid_t sys_waitpid(pid_t pid, int *status, int options); -#define __NR_exit 1 +/// @brief Replaces the current process image with a new process image. +int sys_execve(pt_regs *r); -#define __NR_fork 2 +void sys_chdir(char const *path); -#define __NR_read 3 +/// Returns the process ID (PID) of the calling process. +pid_t sys_getpid(); -#define __NR_write 4 +/// @brief Adds the increment to the priority value of the task. +int sys_nice(int increment); -#define __NR_open 5 +/// Returns the parent process ID (PPID) of the calling process. +pid_t sys_getppid(); -#define __NR_close 6 +int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg); -#define __NR_waitpid 7 +void sys_getcwd(char *path, size_t size); -#define __NR_creat 8 +/// @brief Create a child process. +pid_t sys_vfork(pt_regs *r); -#define __NR_link 9 +int sys_stat(const char *path, stat_t *buf); -#define __NR_unlink 10 +int sys_mkdir(const char *path, mode_t mode); -#define __NR_execve 11 - -#define __NR_chdir 12 - -#define __NR_time 13 - -#define __NR_mknod 14 - -#define __NR_chmod 15 - -#define __NR_lchown 16 - -#define __NR_stat 18 - -#define __NR_lseek 19 - -#define __NR_getpid 20 - -#define __NR_mount 21 - -#define __NR_oldumount 22 - -#define __NR_setuid 23 - -#define __NR_getuid 24 - -#define __NR_stime 25 - -#define __NR_ptrace 26 - -#define __NR_alarm 27 - -#define __NR_fstat 28 - -#define __NR_pause 29 - -#define __NR_utime 30 - -#define __NR_access 33 - -#define __NR_nice 34 - -#define __NR_sync 36 - -#define __NR_kill 37 - -#define __NR_rename 38 - -#define __NR_mkdir 39 - -#define __NR_rmdir 40 - -#define __NR_dup 41 - -#define __NR_pipe 42 - -#define __NR_times 43 - -#define __NR_brk 45 - -#define __NR_setgid 46 - -#define __NR_getgid 47 - -#define __NR_signal 48 - -#define __NR_geteuid 49 - -#define __NR_getegid 50 - -#define __NR_acct 51 - -#define __NR_umount 52 - -#define __NR_ioctl 54 - -#define __NR_fcntl 55 - -#define __NR_setpgid 57 - -#define __NR_olduname 59 - -#define __NR_umask 60 - -#define __NR_chroot 61 - -#define __NR_ustat 62 - -#define __NR_dup2 63 - -#define __NR_getppid 64 - -#define __NR_getpgrp 65 - -#define __NR_setsid 66 - -#define __NR_sigaction 67 - -#define __NR_sgetmask 68 - -#define __NR_ssetmask 69 - -#define __NR_setreuid 70 - -#define __NR_setregid 71 - -#define __NR_sigsuspend 72 - -#define __NR_sigpending 73 - -#define __NR_sethostname 74 - -#define __NR_setrlimit 75 - -#define __NR_getrlimit 76 - -#define __NR_getrusage 77 - -#define __NR_gettimeofday 78 - -#define __NR_settimeofday 79 - -#define __NR_getgroups 80 - -#define __NR_setgroups 81 - -#define __NR_symlink 83 - -#define __NR_lstat 84 - -#define __NR_readlink 85 - -#define __NR_uselib 86 - -#define __NR_swapon 87 - -#define __NR_reboot 88 - -#define __NR_readdir 89 - -#define __NR_mmap 90 - -#define __NR_munmap 91 - -#define __NR_truncate 92 - -#define __NR_ftruncate 93 - -#define __NR_fchmod 94 - -#define __NR_fchown 95 - -#define __NR_getpriority 96 - -#define __NR_setpriority 97 - -#define __NR_statfs 99 - -#define __NR_fstatfs 100 - -#define __NR_ioperm 101 - -#define __NR_socketcall 102 - -#define __NR_syslog 103 - -#define __NR_setitimer 104 - -#define __NR_getitimer 105 - -#define __NR_newstat 106 - -#define __NR_newlstat 107 - -#define __NR_newfstat 108 - -#define __NR_uname 109 - -#define __NR_iopl 110 - -#define __NR_vhangup 111 - -#define __NR_idle 112 - -#define __NR_vm86old 113 - -#define __NR_wait4 114 - -#define __NR_swapoff 115 - -#define __NR_sysinfo 116 - -#define __NR_ipc 117 - -#define __NR_fsync 118 - -#define __NR_sigreturn 119 - -#define __NR_clone 120 - -#define __NR_setdomainname 121 - -#define __NR_newuname 122 - -#define __NR_modify_ldt 123 - -#define __NR_adjtimex 124 - -#define __NR_mprotect 125 - -#define __NR_sigprocmask 126 - -#define __NR_create_module 127 - -#define __NR_init_module 128 - -#define __NR_delete_module 129 - -#define __NR_get_kernel_syms 130 - -#define __NR_quotactl 131 - -#define __NR_getpgid 132 - -#define __NR_fchdir 133 - -#define __NR_bdflush 134 - -#define __NR_sysfs 135 - -#define __NR_personality 136 - -#define __NR_setfsuid 138 - -#define __NR_setfsgid 139 - -#define __NR_llseek 140 - -#define __NR_getdents 141 - -#define __NR_select 142 - -#define __NR_flock 143 - -#define __NR_msync 144 - -#define __NR_readv 145 - -#define __NR_writev 146 - -#define __NR_getsid 147 - -#define __NR_fdatasync 148 - -#define __NR_sysctl 149 - -#define __NR_mlock 150 - -#define __NR_munlock 151 - -#define __NR_mlockall 152 - -#define __NR_munlockall 153 - -#define __NR_sched_setparam 154 - -#define __NR_sched_getparam 155 - -#define __NR_sched_setscheduler 156 - -#define __NR_sched_getscheduler 157 - -#define __NR_sched_yield 158 - -#define __NR_sched_get_priority_max 159 - -#define __NR_sched_get_priority_min 160 - -#define __NR_sched_rr_get_interval 161 - -#define __NR_nanosleep 162 - -#define __NR_mremap 163 - -#define __NR_setresuid 164 - -#define __NR_getresuid 165 - -#define __NR_vm86 166 - -#define __NR_query_module 167 - -#define __NR_poll 168 - -#define __NR_nfsservctl 169 - -#define __NR_setresgid 170 - -#define __NR_getresgid 171 - -#define __NR_prctl 172 - -#define __NR_rt_sigreturn 173 - -#define __NR_rt_sigaction 174 - -#define __NR_rt_sigprocmask 175 - -#define __NR_rt_sigpending 176 - -#define __NR_rt_sigtimedwait 177 - -#define __NR_rt_sigqueueinfo 178 - -#define __NR_rt_sigsuspend 179 - -#define __NR_pread 180 - -#define __NR_pwrite 181 - -#define __NR_chown 182 - -#define __NR_getcwd 183 - -#define __NR_capget 184 - -#define __NR_capset 185 - -#define __NR_sigaltstack 186 - -#define __NR_sendfile 187 - -#define __NR_vfork 188 - -#define __NR_free 189 // TODO: remove me! - -#define SYSCALL_NUMBER 190 - -//----------------------------------------------------------------------------- +dirent_t *sys_readdir(DIR *dirp); \ No newline at end of file diff --git a/mentos/inc/system/syscall_types.h b/mentos/inc/system/syscall_types.h new file mode 100644 index 0000000..c964358 --- /dev/null +++ b/mentos/inc/system/syscall_types.h @@ -0,0 +1,220 @@ +/// MentOS, The Mentoring Operating system project +/// @file syscall_number.h +/// @brief System Call numbers. +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +// Return result in eax ("=a"). +// System call number in eax ("a"). +#define DEFN_SYSCALL0(__res, num) \ + __asm__ __volatile__("INT $0x80" : "=a"(__res) : "a"(num)) + +// Return result in eax ("=a"). +// System call number in eax ("a"). +// p1 in ebx ("+b"). +#define DEFN_SYSCALL1(__res, num, p1) \ + __asm__ __volatile__("INT $0x80" : "=a"(__res), "+b"(p1) : "a"(num)) + +#define DEFN_SYSCALL2(__res, num, p1, p2) \ + __asm__ __volatile__("INT $0x80" \ + : "=a"(__res), "+b"(p1), "+c"(p2) \ + : "a"(num)); + +#define DEFN_SYSCALL3(__res, num, p1, p2, p3) \ + __asm__ __volatile__("INT $0x80" \ + : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3) \ + : "a"(num)) + +#define DEFN_SYSCALL4(__res, num, p1, p2, p3, p4) \ + __asm__ __volatile__("INT $0x80" \ + : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3), "+S"(p4) \ + : "a"(num)) + +#define DEFN_SYSCALL5(__res, num, p1, p2, p3, p4, p5) \ + __asm__ __volatile__("INT $0x80" \ + : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3), \ + "+S"(p4), "+D"(p5) \ + : "a"(num)) + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_stat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_oldumount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_fstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount 52 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_setpgid 57 +#define __NR_olduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_symlink 83 +#define __NR_lstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_newstat 106 +#define __NR_newlstat 107 +#define __NR_newfstat 108 +#define __NR_uname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86old 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_newuname 122 +#define __NR_modify_ldt 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR_llseek 140 +#define __NR_getdents 141 +#define __NR_select 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR_sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_vm86 166 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread 180 +#define __NR_pwrite 181 +#define __NR_chown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_vfork 188 +#define __NR_free 189 // TODO: remove me! + +#define SYSCALL_NUMBER 190 \ No newline at end of file diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index 23e2815..93053ee 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -15,7 +15,6 @@ #include "bitops.h" #include "initrd.h" #include "string.h" -#include "open.h" char *module_start[MAX_MODULES]; @@ -85,7 +84,7 @@ DIR *initfs_opendir(const char *path) } DIR *pdir = kmalloc(sizeof(DIR)); - pdir->handle = -1; + pdir->fd = -1; pdir->cur_entry = 0; strcpy(pdir->path, path); diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c index 72cec8d..58a3dbc 100644 --- a/mentos/src/fs/open.c +++ b/mentos/src/fs/open.c @@ -4,8 +4,7 @@ /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "open.h" - +#include "syscall.h" #include "string.h" #include "debug.h" #include "stdio.h" diff --git a/mentos/src/fs/read_write.c b/mentos/src/fs/read_write.c index 41346fc..9bbfb98 100644 --- a/mentos/src/fs/read_write.c +++ b/mentos/src/fs/read_write.c @@ -5,14 +5,12 @@ /// See LICENSE.md for details. #include -#include "read_write.h" #include "vfs.h" #include "stdio.h" #include "fcntl.h" #include "unistd.h" #include "keyboard.h" #include "video.h" -#include "open.h" ssize_t sys_read(int fd, void *buf, size_t nbytes) { diff --git a/mentos/src/fs/readdir.c b/mentos/src/fs/readdir.c new file mode 100644 index 0000000..6ae887f --- /dev/null +++ b/mentos/src/fs/readdir.c @@ -0,0 +1,28 @@ +/// MentOS, The Mentoring Operating system project +/// @file readdir.c +/// @brief Function for accessing directory entries. +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "dirent.h" +#include "syscall.h" +#include "stdio.h" +#include "vfs.h" + +dirent_t *sys_readdir(DIR *dirp) +{ + if (dirp == NULL) { + printf("readdir: cannot read directory :" + "Directory pointer is not valid\n"); + + return NULL; + } + if (mountpoint_list[dirp->fd].dir_op.readdir_f == NULL) { + printf("readdir: cannot read directory '%s':" + "No readdir function\n", + dirp->path); + + return NULL; + } + return mountpoint_list[dirp->fd].dir_op.readdir_f(dirp); +} diff --git a/mentos/src/sys/stat.c b/mentos/src/fs/stat.c similarity index 95% rename from mentos/src/sys/stat.c rename to mentos/src/fs/stat.c index 3fbb349..ccedb79 100644 --- a/mentos/src/sys/stat.c +++ b/mentos/src/fs/stat.c @@ -10,7 +10,7 @@ #include "string.h" #include "initrd.h" -int stat(const char *path, stat_t *buf) +int sys_stat(const char *path, stat_t *buf) { // Reset the structure. buf->st_dev = 0; @@ -51,7 +51,7 @@ int stat(const char *path, stat_t *buf) return 0; } -int mkdir(const char *path, mode_t mode) +int sys_mkdir(const char *path, mode_t mode) { char absolute_path[MAX_PATH_LENGTH]; strcpy(absolute_path, path); diff --git a/mentos/src/kernel/sys.c b/mentos/src/kernel/sys.c index bb15def..e1c022e 100644 --- a/mentos/src/kernel/sys.c +++ b/mentos/src/kernel/sys.c @@ -4,7 +4,6 @@ /// @copyright (c) 2019 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys.h" #include "stdio.h" #include "errno.h" #include "mutex.h" diff --git a/mentos/src/libc/unistd/mkdir.c b/mentos/src/libc/unistd/mkdir.c new file mode 100644 index 0000000..6ccad7a --- /dev/null +++ b/mentos/src/libc/unistd/mkdir.c @@ -0,0 +1,21 @@ +/// MentOS, The Mentoring Operating system project +/// @file mkdir.c +/// @brief Make directory functions. +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "syscall.h" +#include "unistd.h" +#include "errno.h" +#include "stat.h" + +int mkdir(const char *path, mode_t mode) +{ + ssize_t retval; + DEFN_SYSCALL2(retval, __NR_mkdir, path, mode); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} diff --git a/mentos/src/libc/unistd/stat.c b/mentos/src/libc/unistd/stat.c new file mode 100644 index 0000000..d301501 --- /dev/null +++ b/mentos/src/libc/unistd/stat.c @@ -0,0 +1,21 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.c +/// @brief Stat functions. +/// @copyright (c) 2019 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "syscall.h" +#include "unistd.h" +#include "errno.h" +#include "stat.h" + +int stat(const char *path, stat_t *buf) +{ + ssize_t retval; + DEFN_SYSCALL2(retval, __NR_stat, path, buf); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} diff --git a/mentos/src/sys/dirent.c b/mentos/src/sys/dirent.c index 971e12f..9b40f6a 100644 --- a/mentos/src/sys/dirent.c +++ b/mentos/src/sys/dirent.c @@ -11,88 +11,74 @@ #include "stdio.h" #include "initrd.h" #include "debug.h" +#include "syscall_types.h" +#include "assert.h" +#include "errno.h" DIR *opendir(const char *path) { - char absolute_path[MAX_PATH_LENGTH]; - DIR *pdir = NULL; + char absolute_path[MAX_PATH_LENGTH]; + DIR *pdir = NULL; - strcpy(absolute_path, path); - // If the first character is not the '/' then get the absolute path. - if (absolute_path[0] != '/') - { - if (!get_absolute_path(absolute_path)) - { - dbg_print("Cannot get the absolute path.\n"); + strcpy(absolute_path, path); + // If the first character is not the '/' then get the absolute path. + if (absolute_path[0] != '/') { + if (!get_absolute_path(absolute_path)) { + dbg_print("Cannot get the absolute path.\n"); - return NULL; - } - } + return NULL; + } + } - // Get the mount point id. - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("opendir: cannot open directory '%s':" - "Cannot find mount-point\n", absolute_path); + // Get the mount point id. + int32_t mp_id = get_mountpoint_id(absolute_path); + if (mp_id < 0) { + printf("opendir: cannot open directory '%s':" + "Cannot find mount-point\n", + absolute_path); - return NULL; - } + return NULL; + } - if (mountpoint_list[mp_id].dir_op.opendir_f == NULL) - { - printf("opendir: cannot open directory '%s':" - "No opendir function\n", absolute_path); + if (mountpoint_list[mp_id].dir_op.opendir_f == NULL) { + printf("opendir: cannot open directory '%s':" + "No opendir function\n", + absolute_path); - return NULL; - } + return NULL; + } - pdir = mountpoint_list[mp_id].dir_op.opendir_f(absolute_path); - // If the directiry is correctly open, set the handle. - if (pdir != NULL) - { - pdir->handle = mp_id; - } + pdir = mountpoint_list[mp_id].dir_op.opendir_f(absolute_path); + // If the directiry is correctly open, set the handle. + if (pdir != NULL) { + pdir->fd = mp_id; + } - return pdir; + return pdir; } int closedir(DIR *dirp) { - if (dirp == NULL) - { - printf("closedir: cannot close directory :" - "Directory pointer is not valid\n"); + if (dirp == NULL) { + printf("closedir: cannot close directory :" + "Directory pointer is not valid\n"); - return -1; - } - if (mountpoint_list[dirp->handle].dir_op.closedir_f == NULL) - { - printf("closedir: cannot close directory '%s':" - "No closedir function\n", dirp->path); + return -1; + } + if (mountpoint_list[dirp->fd].dir_op.closedir_f == NULL) { + printf("closedir: cannot close directory '%s':" + "No closedir function\n", + dirp->path); - return -1; - } - - return mountpoint_list[dirp->handle].dir_op.closedir_f(dirp); + return -1; + } + return mountpoint_list[dirp->fd].dir_op.closedir_f(dirp); } dirent_t *readdir(DIR *dirp) { - if (dirp == NULL) - { - printf("readdir: cannot read directory :" - "Directory pointer is not valid\n"); - - return NULL; - } - if (mountpoint_list[dirp->handle].dir_op.readdir_f == NULL) - { - printf("readdir: cannot read directory '%s':" - "No readdir function\n", dirp->path); - - return NULL; - } - - return mountpoint_list[dirp->handle].dir_op.readdir_f(dirp); + dirent_t *dent; + // Call the readdir system call. + DEFN_SYSCALL1(dent, __NR_readdir, dirp); + return dent; } diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index a325879..d794e52 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -5,7 +5,6 @@ /// See LICENSE.md for details. #include "syscall.h" -#include "sys.h" #include "shm.h" #include "isr.h" #include "errno.h" @@ -17,8 +16,6 @@ #include "process.h" #include "irqflags.h" #include "scheduler.h" -#include "read_write.h" -#include "open.h" /// @brief The signature of a function call. typedef int (*SystemCall)(); @@ -50,6 +47,9 @@ void syscall_init() sys_call_table[__NR_write] = (SystemCall)sys_write; sys_call_table[__NR_open] = (SystemCall)sys_open; sys_call_table[__NR_close] = (SystemCall)sys_close; + sys_call_table[__NR_stat] = (SystemCall)sys_stat; + sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; + sys_call_table[__NR_readdir] = (SystemCall)sys_readdir; sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; sys_call_table[__NR_vfork] = (SystemCall)sys_vfork; diff --git a/mentos/src/ui/shell/shell.c b/mentos/src/ui/shell/shell.c index 6d7c9de..641dc93 100644 --- a/mentos/src/ui/shell/shell.c +++ b/mentos/src/ui/shell/shell.c @@ -400,7 +400,7 @@ int shell(int argc, char **argv, char **envp) dbg_print("I'm shell, I'll let my pawn, login, handle any intruder...\n"); shell_login(); - sys_chdir("/"); + chdir("/"); current_user.uid = 1; current_user.gid = 0; From 552e2a073fccc327879dbc14e333a5070a13f5f1 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 May 2019 13:59:07 +0200 Subject: [PATCH 09/21] Update limit values --- mentos/inc/libc/limits.h | 6 ++++++ mentos/inc/process/process.h | 3 ++- mentos/inc/sys/dirent.h | 4 +--- mentos/inc/ui/shell/shell.h | 3 ++- mentos/src/fs/fcntl.c | 3 ++- mentos/src/fs/open.c | 3 ++- mentos/src/fs/stat.c | 5 +++-- mentos/src/fs/vfs.c | 9 +++++---- mentos/src/libc/libgen.c | 3 ++- mentos/src/process/process.c | 2 +- mentos/src/sys/dirent.c | 2 +- mentos/src/sys/unistd.c | 3 ++- mentos/src/ui/command/cmd_cd.c | 8 ++++---- mentos/src/ui/command/cmd_ls.c | 4 ++-- mentos/src/ui/command/cmd_pwd.c | 4 ++-- mentos/src/ui/shell/shell.c | 4 ++-- 16 files changed, 39 insertions(+), 27 deletions(-) diff --git a/mentos/inc/libc/limits.h b/mentos/inc/libc/limits.h index 36e92e6..bd5c67f 100644 --- a/mentos/inc/libc/limits.h +++ b/mentos/inc/libc/limits.h @@ -39,3 +39,9 @@ /// Maximum value a `signed long int' can hold. #define LONG_MIN (-LONG_MAX - 1L) + +/// Maximum number of characters in a file name. +#define NAME_MAX 255 + +/// Maximum number of characters in a path name. +#define PATH_MAX 4096 \ No newline at end of file diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index c59fd81..aa26b82 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -17,6 +17,7 @@ #include "unistd.h" #include "list_head.h" #include "signal_defs.h" +#include "limits.h" /// The maximum length of a name for a task_struct. #define TASK_NAME_MAX_LENGTH 100 @@ -170,7 +171,7 @@ typedef struct task_struct { int error_no; /// The current working directory. - char cwd[MAX_PATH_LENGTH]; + char cwd[PATH_MAX]; //==== Future work ========================================================= // - task's attributes: diff --git a/mentos/inc/sys/dirent.h b/mentos/inc/sys/dirent.h index f2581ed..f81fd7a 100644 --- a/mentos/inc/sys/dirent.h +++ b/mentos/inc/sys/dirent.h @@ -7,9 +7,7 @@ #pragma once #include "stddef.h" - -/// The maximum length for a name. -#define NAME_MAX 30 +#include "limits.h" /// @brief Contains the entries of a directory. typedef struct dirent_t { diff --git a/mentos/inc/ui/shell/shell.h b/mentos/inc/ui/shell/shell.h index b6d2015..e51ff1b 100644 --- a/mentos/inc/ui/shell/shell.h +++ b/mentos/inc/ui/shell/shell.h @@ -8,6 +8,7 @@ #include "stdint.h" #include "kernel.h" +#include "limits.h" /// Maximum length of credentials. #define CREDENTIALS_LENGTH 50 @@ -55,7 +56,7 @@ typedef struct userenv_t char username[CREDENTIALS_LENGTH]; /// The current path. - char cur_path[MAX_PATH_LENGTH]; + char cur_path[PATH_MAX]; /// The user identifier. unsigned int uid; diff --git a/mentos/src/fs/fcntl.c b/mentos/src/fs/fcntl.c index 6a8be48..68e3b80 100644 --- a/mentos/src/fs/fcntl.c +++ b/mentos/src/fs/fcntl.c @@ -7,10 +7,11 @@ #include "fcntl.h" #include "string.h" #include "vfs.h" +#include "limits.h" int remove(const char *pathname) { - char absolute_path[MAX_PATH_LENGTH]; + char absolute_path[PATH_MAX]; strcpy(absolute_path, pathname); if (pathname[0] != '/') { get_absolute_path(absolute_path); diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c index 58a3dbc..a8cf3d5 100644 --- a/mentos/src/fs/open.c +++ b/mentos/src/fs/open.c @@ -6,6 +6,7 @@ #include "syscall.h" #include "string.h" +#include "limits.h" #include "debug.h" #include "stdio.h" #include "vfs.h" @@ -13,7 +14,7 @@ int sys_open(const char *pathname, int flags, mode_t mode) { // Allocate a variable for the path. - char absolute_path[MAX_PATH_LENGTH]; + char absolute_path[PATH_MAX]; // Copy the path to the working variable. strcpy(absolute_path, pathname); diff --git a/mentos/src/fs/stat.c b/mentos/src/fs/stat.c index ccedb79..9365317 100644 --- a/mentos/src/fs/stat.c +++ b/mentos/src/fs/stat.c @@ -9,6 +9,7 @@ #include "stdio.h" #include "string.h" #include "initrd.h" +#include "limits.h" int sys_stat(const char *path, stat_t *buf) { @@ -23,7 +24,7 @@ int sys_stat(const char *path, stat_t *buf) buf->st_mtime = 0; buf->st_ctime = 0; - char absolute_path[MAX_PATH_LENGTH]; + char absolute_path[PATH_MAX]; strcpy(absolute_path, path); if (path[0] != '/') @@ -53,7 +54,7 @@ int sys_stat(const char *path, stat_t *buf) int sys_mkdir(const char *path, mode_t mode) { - char absolute_path[MAX_PATH_LENGTH]; + char absolute_path[PATH_MAX]; strcpy(absolute_path, path); int result = -1; diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index a358375..22e1b0c 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -12,6 +12,7 @@ #include "shell.h" #include "string.h" #include "initrd.h" +#include "limits.h" int current_fd; char *module_start[MAX_MODULES]; @@ -106,7 +107,7 @@ mountpoint_t *get_mountpoint_from_id(int32_t mp_id) int get_relative_path(uint32_t mp_id, char *path) { - char relative_path[MAX_PATH_LENGTH]; + char relative_path[PATH_MAX]; // Get the size of the path. size_t path_size = strlen(path); @@ -133,9 +134,9 @@ int get_relative_path(uint32_t mp_id, char *path) int get_absolute_path(char *path) { if (path[0] != '/') { - char abspath[MAX_PATH_LENGTH]; - memset(abspath, '\0', MAX_PATH_LENGTH); - getcwd(abspath, MAX_PATH_LENGTH); + char abspath[PATH_MAX]; + memset(abspath, '\0', PATH_MAX); + getcwd(abspath, PATH_MAX); int abs_size = strlen(abspath); if (abspath[abs_size - 1] == '/') { strncat(abspath, path, strlen(path)); diff --git a/mentos/src/libc/libgen.c b/mentos/src/libc/libgen.c index 3625f48..47f2bab 100644 --- a/mentos/src/libc/libgen.c +++ b/mentos/src/libc/libgen.c @@ -7,6 +7,7 @@ #include "libgen.h" #include "string.h" #include "initrd.h" +#include "limits.h" int parse_path(char *out, char **cur, char sep, size_t max) { @@ -31,7 +32,7 @@ int parse_path(char *out, char **cur, char sep, size_t max) char *dirname(const char *path) { - static char s[MAX_PATH_LENGTH]; + static char s[PATH_MAX]; static char dot[2] = "."; diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index 1f32f5b..4f07c0c 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -71,7 +71,7 @@ task_struct *create_init_process() // Enable the interrupts. init_proc->thread.eflags = init_proc->thread.eflags | EFLAG_IF; // Clear the current working directory. - memset(init_proc->cwd, '\0', MAX_PATH_LENGTH); + memset(init_proc->cwd, '\0', PATH_MAX); // Set the state of the process as running. init_proc->state = TASK_RUNNING; // Active the current process. diff --git a/mentos/src/sys/dirent.c b/mentos/src/sys/dirent.c index 9b40f6a..18a851e 100644 --- a/mentos/src/sys/dirent.c +++ b/mentos/src/sys/dirent.c @@ -17,7 +17,7 @@ DIR *opendir(const char *path) { - char absolute_path[MAX_PATH_LENGTH]; + char absolute_path[PATH_MAX]; DIR *pdir = NULL; strcpy(absolute_path, path); diff --git a/mentos/src/sys/unistd.c b/mentos/src/sys/unistd.c index 91a72f5..fb76d9d 100644 --- a/mentos/src/sys/unistd.c +++ b/mentos/src/sys/unistd.c @@ -7,12 +7,13 @@ #include "unistd.h" #include "string.h" +#include "limits.h" #include "stdio.h" #include "vfs.h" int rmdir(const char *path) { - char absolute_path[MAX_PATH_LENGTH]; + char absolute_path[PATH_MAX]; strcpy(absolute_path, path); if (path[0] != '/') { diff --git a/mentos/src/ui/command/cmd_cd.c b/mentos/src/ui/command/cmd_cd.c index 06826e8..a38e5f9 100644 --- a/mentos/src/ui/command/cmd_cd.c +++ b/mentos/src/ui/command/cmd_cd.c @@ -16,11 +16,11 @@ void cmd_cd(int argc, char **argv) { DIR *dirp = NULL; - char path[MAX_PATH_LENGTH]; - memset(path, 0, MAX_PATH_LENGTH); + char path[PATH_MAX]; + memset(path, 0, PATH_MAX); - char current_path[MAX_PATH_LENGTH]; - getcwd(current_path, MAX_PATH_LENGTH); + char current_path[PATH_MAX]; + getcwd(current_path, PATH_MAX); if (argc <= 1) { strcpy(path, "/"); diff --git a/mentos/src/ui/command/cmd_ls.c b/mentos/src/ui/command/cmd_ls.c index 8747243..8e10f1d 100644 --- a/mentos/src/ui/command/cmd_ls.c +++ b/mentos/src/ui/command/cmd_ls.c @@ -92,8 +92,8 @@ void cmd_ls(int argc, char **argv) print_ls(dirp, flags); } if (no_directory) { - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); DIR *dirp = opendir(cwd); if (dirp == NULL) { printf("%s: cannot access '%s': %s\n\n", argv[0], cwd, "unknown"); diff --git a/mentos/src/ui/command/cmd_pwd.c b/mentos/src/ui/command/cmd_pwd.c index 8fc3d10..73a515e 100644 --- a/mentos/src/ui/command/cmd_pwd.c +++ b/mentos/src/ui/command/cmd_pwd.c @@ -13,7 +13,7 @@ void cmd_pwd(int argc, char **argv) { (void)argc; (void)argv; - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); printf("%s\n", cwd); } diff --git a/mentos/src/ui/shell/shell.c b/mentos/src/ui/shell/shell.c index 641dc93..7a0d4c1 100644 --- a/mentos/src/ui/shell/shell.c +++ b/mentos/src/ui/shell/shell.c @@ -215,8 +215,8 @@ static void shell_print_prompt() video_set_color(BRIGHT_BLUE); printf(current_user.username); video_set_color(WHITE); - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); printf("~:%s# ", cwd); // Update the lower-bounds for the video. lower_bound_x = video_get_column(); From 25ffb602ea30f91bde109af99b449ed9c48f3008 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 May 2019 16:19:26 +0200 Subject: [PATCH 10/21] Improve multiboot info --- mentos/boot.asm | 36 ++++--- mentos/inc/kernel.h | 3 - mentos/inc/multiboot.h | 72 ++++++++------ mentos/src/multiboot.c | 217 +++++++++++++++++++++++++++++++---------- 4 files changed, 225 insertions(+), 103 deletions(-) diff --git a/mentos/boot.asm b/mentos/boot.asm index e5bc0de..0c26b1f 100644 --- a/mentos/boot.asm +++ b/mentos/boot.asm @@ -7,20 +7,26 @@ [BITS 32] ; All instructions should be 32-bit. [EXTERN kmain] ; The start point of our C code -; Grub is informed with this flag to load -; the kernel and kernel modules on a page boundary. -MBOOT_PAGE_ALIGN equ 1<<0 -; Grub is informed with this flag to provide the kernel -; with memory information. -MBOOT_MEM_INFO equ 1<<1 -; This is the multiboot magic value. -MBOOT_HEADER_MAGIC equ 0x1BADB002 +; The magic field should contain this. +MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 +; This should be in %eax. +MULTIBOOT_BOOTLOADER_MAGIC equ 0x2BADB002 + +; = Specify what GRUB should PROVIDE ========================================= +; Align the kernel and kernel modules on i386 page (4KB) boundaries. +MULTIBOOT_PAGE_ALIGN equ 0x00000001 +; Provide the kernel with memory information. +MULTIBOOT_MEMORY_INFO equ 0x00000002 +; Must pass video information to OS. +MULTIBOOT_VIDEO_MODE equ 0x00000004 +; ----------------------------------------------------------------------------- + ; This is the flag combination that we prepare for Grub ; to read at kernel load time. -MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO +MULTIBOOT_HEADER_FLAGS equ (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_VIDEO_MODE) ; Grub reads this value to make sure it loads a kernel ; and not just garbage. -MBOOT_CHECKSUM equ - (MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS) +MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) LOAD_MEMORY_ADDRESS equ 0x00000000 ; reserve (1024*1024) for the stack on a doubleword boundary @@ -33,9 +39,12 @@ section .multiboot_header align 4 ; This is the GRUB Multiboot header. multiboot_header: - dd MBOOT_HEADER_MAGIC - dd MBOOT_HEADER_FLAGS - dd MBOOT_CHECKSUM + ; magic + dd MULTIBOOT_HEADER_MAGIC + ; flags + dd MULTIBOOT_HEADER_FLAGS + ; checksum + dd MULTIBOOT_CHECKSUM ; ----------------------------------------------------------------------------- ; SECTION (data) @@ -61,7 +70,6 @@ kernel_entry: mov esp, stack_top ; pass the initial ESP push esp - ;mov ebp, esp ; pass Multiboot info structure push ebx ; pass Multiboot magic number diff --git a/mentos/inc/kernel.h b/mentos/inc/kernel.h index 190b5ca..dd7c2f8 100644 --- a/mentos/inc/kernel.h +++ b/mentos/inc/kernel.h @@ -21,9 +21,6 @@ /// The maximum number of modules. #define MAX_MODULES 10 -/// This should be in %eax. -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 - /// Our kernel now loads at 0xC0000000, so what low memory address such as /// 0xb800 you used to access, should be LOAD_MEMORY_ADDRESS + 0xb800 #define LOAD_MEMORY_ADDRESS 0x00000000 diff --git a/mentos/inc/multiboot.h b/mentos/inc/multiboot.h index 6a282d4..08450fa 100644 --- a/mentos/inc/multiboot.h +++ b/mentos/inc/multiboot.h @@ -10,42 +10,45 @@ #include "stdint.h" -#define MULTIBOOT_FLAG_MEM 0x001 +// The magic field should contain this. +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 +// This should be in %eax. +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 -#define MULTIBOOT_FLAG_DEVICE 0x002 - -#define MULTIBOOT_FLAG_CMDLINE 0x004 - -#define MULTIBOOT_FLAG_MODS 0x008 - -#define MULTIBOOT_FLAG_AOUT 0x010 - -#define MULTIBOOT_FLAG_ELF 0x020 - -#define MULTIBOOT_FLAG_MMAP 0x040 - -#define MULTIBOOT_FLAG_CONFIG 0x080 - -#define MULTIBOOT_FLAG_LOADER 0x100 - -#define MULTIBOOT_FLAG_APM 0x200 - -#define MULTIBOOT_FLAG_VBE 0x400 +/// Is there basic lower/upper memory information? +#define MULTIBOOT_FLAG_MEM 0x00000001U +/// is there a boot device set? +#define MULTIBOOT_FLAG_DEVICE 0x00000002U +/// is the command-line defined? +#define MULTIBOOT_FLAG_CMDLINE 0x00000004U +/// are there modules to do something with? +#define MULTIBOOT_FLAG_MODS 0x00000008U +/// is there a symbol table loaded? +#define MULTIBOOT_FLAG_AOUT 0x00000010U +/// is there an ELF section header table? +#define MULTIBOOT_FLAG_ELF 0x00000020U +/// is there a full memory map? +#define MULTIBOOT_FLAG_MMAP 0x00000040U +/// Is there drive info? +#define MULTIBOOT_FLAG_DRIVE_INFO 0x00000080U +/// Is there a config table? +#define MULTIBOOT_FLAG_CONFIG_TABLE 0x00000100U +/// Is there a boot loader name? +#define MULTIBOOT_FLAG_BOOT_LOADER_NAME 0x00000200U +/// Is there a APM table? +#define MULTIBOOT_FLAG_APM_TABLE 0x00000400U +/// Is there video information? +#define MULTIBOOT_FLAG_VBE_INFO 0x00000800U +#define MULTIBOOT_FLAG_FRAMEBUFFER_INFO 0x00001000U #define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 - #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 - #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 #define MULTIBOOT_MEMORY_AVAILABLE 1 - #define MULTIBOOT_MEMORY_RESERVED 2 - #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 - #define MULTIBOOT_MEMORY_NVS 4 - #define MULTIBOOT_MEMORY_BADRAM 5 // +-------------------+ @@ -109,7 +112,7 @@ typedef struct multiboot_elf_section_header_table { } multiboot_elf_section_header_table_t; // TODO: doxygen comment. -typedef struct multiboot_mod_list { +typedef struct multiboot_module { // The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive. uint32_t mod_start; uint32_t mod_end; @@ -119,13 +122,18 @@ typedef struct multiboot_mod_list { uint32_t pad; } multiboot_module_t; -// TODO: doxygen comment. -typedef struct multiboot_mmap_entry { +typedef struct multiboot_memory_map { uint32_t size; - uint64_t addr; - uint64_t len; + uint32_t base_addr_low, base_addr_high; + uint32_t length_low, length_high; uint32_t type; -} __attribute__((packed)) multiboot_memory_map_t; +} multiboot_memory_map_t; + +typedef struct multiboot_color { + uint8_t red; + uint8_t green; + uint8_t blue; +} multiboot_color_t; // TODO: doxygen comment. typedef struct multiboot_info { diff --git a/mentos/src/multiboot.c b/mentos/src/multiboot.c index 5fc60fa..daef81c 100644 --- a/mentos/src/multiboot.c +++ b/mentos/src/multiboot.c @@ -7,74 +7,183 @@ #include "multiboot.h" #include "bitops.h" #include "debug.h" +#include "panic.h" + +#define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit))) + +static inline multiboot_memory_map_t *first_mmap_entry(multiboot_info_t *info) +{ + if (!has_flag(info->flags, MULTIBOOT_FLAG_MMAP)) + return NULL; + return (multiboot_memory_map_t *)((uintptr_t)info->mmap_addr); +} + +static inline multiboot_memory_map_t * +next_mmap_entry(multiboot_info_t *info, multiboot_memory_map_t *entry) +{ + uintptr_t next = ((uintptr_t)entry) + entry->size + sizeof(entry->size); + if (next >= info->mmap_addr + info->mmap_length) + return NULL; + return (multiboot_memory_map_t *)next; +} + +static inline multiboot_memory_map_t * +next_mmap_entry_of_type(multiboot_info_t *info, multiboot_memory_map_t *entry, + uint32_t type) +{ + do { + entry = next_mmap_entry(info, entry); + } while (entry && entry->type != type); + return entry; +} + +static inline multiboot_memory_map_t * +first_mmap_entry_of_type(multiboot_info_t *info, uint32_t type) +{ + multiboot_memory_map_t *entry = first_mmap_entry(info); + if (entry && (entry->type == type)) + return entry; + return next_mmap_entry_of_type(info, entry, type); +} + +static inline char *mmap_type_name(multiboot_memory_map_t *entry) +{ + if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) + return "AVAILABLE"; + if (entry->type == MULTIBOOT_MEMORY_RESERVED) + return "RESERVED"; + return "NONE"; +} + +static inline multiboot_module_t *first_module(multiboot_info_t *info) +{ + if (!has_flag(info->flags, MULTIBOOT_FLAG_MODS)) + return NULL; + if (!info->mods_count) + return NULL; + return (multiboot_module_t *)(uintptr_t)info->mods_addr; +} + +static inline multiboot_module_t *next_module(multiboot_info_t *info, + multiboot_module_t *mod) +{ + multiboot_module_t *first = + (multiboot_module_t *)((uintptr_t)info->mods_addr); + ++mod; + if ((mod - first) >= info->mods_count) + return NULL; + return mod; +} void dump_multiboot(multiboot_info_t *mbi) { dbg_print("\n--------------------------------------------------\n"); dbg_print("MULTIBOOT header at 0x%x:\n", mbi); - dbg_print("Flags : 0x%x\n", mbi->flags); + + // Print out the flags. + dbg_print("flags = 0x%x\n", mbi->flags); + + // Are mem_* valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_MEM)) { - dbg_print("Mem Lo: 0x%x\n", mbi->mem_lower * K); - dbg_print("Mem Hi: 0x%x (%dMB)\n", mbi->mem_upper * K, - (mbi->mem_upper / 1024)); + dbg_print("mem_lower = %u Kb (%u Mb), " + "mem_upper = %u Kb (%u Mb), " + "total = %u Kb (%u Mb)\n", + mbi->mem_lower, mbi->mem_lower / K, mbi->mem_upper, + mbi->mem_upper / K, mbi->mem_lower + mbi->mem_upper, + (mbi->mem_lower + mbi->mem_upper) / K); } + + // Is boot_device valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_DEVICE)) { - dbg_print("Boot d: 0x%x\n", mbi->boot_device); - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { - dbg_print("cmdlin: 0x%x (%s)\n", mbi->cmdline, (char *)mbi->cmdline); - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_MODS)) { - dbg_print("Mods : 0x%x\n", mbi->mods_count); - - multiboot_module_t *mod = (multiboot_module_t *)mbi->mods_addr; - - if (mbi->mods_count > 0) { - for (uint32_t i = 0; i < mbi->mods_count && i < MAX_MODULES; - i++, mod++) { - uint32_t start = mod->mod_start; - uint32_t end = mod->mod_end; - dbg_print("\tModule %d is at 0x%x:0x%x\n", i + 1, start, end); - } - - /* Last implementation - for (uint32_t i = 0; i < mbi->mods_count; ++i) - { - // uint32_t start = *((uint32_t *) (mbi->mods_addr + 8 * i)); - uint32_t start = mbi->mods_addr + 8 * i; - // uint32_t end = *((uint32_t *) (mbi->mods_addr + 8 * i + 4)); - uint32_t end = mbi->mods_addr + 8 * i + 4; - dbg_print("\tModule %d is at 0x%x:0x%x\n", i + 1, start, end); - } - */ + dbg_print("boot_device = 0x%x (0x%x)", mbi->boot_device, + ((mbi->boot_device) & 0xFF000000)); + switch ((mbi->boot_device) & 0xFF000000) { + case 0x00000000: + dbg_print("(floppy)\n"); + break; + case 0x80000000: + dbg_print("(disk)\n"); + break; + default: + dbg_print("(unknown)\n"); } } + + // Is the command line passed? + if (has_flag(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { + dbg_print("cmdline: %s\n", (char *)mbi->cmdline); + } + + // Are mods_* valid? + if (has_flag(mbi->flags, MULTIBOOT_FLAG_MODS)) { + dbg_print("mods_count = %d, mods_addr = 0x%x\n", (int)mbi->mods_count, + (int)mbi->mods_addr); + multiboot_module_t *mod = first_module(mbi); + for (int i = 0; mod; ++i, mod = next_module(mbi, mod)) { + dbg_print(" [%2d] mod_start = 0x%x, mod_end = 0x%x, cmdline = %s\n", + i, mod->mod_start, mod->mod_end, (char *)mod->cmdline); + } + } + // Bits 4 and 5 are mutually exclusive! + if (has_flag(mbi->flags, MULTIBOOT_FLAG_AOUT) && + has_flag(mbi->flags, MULTIBOOT_FLAG_ELF)) { + kernel_panic("Both bits 4 and 5 are set.\n"); + return; + } + + // Is the symbol table of a.out valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_AOUT)) { - dbg_print("AOUT t : 0x%x\n", mbi->u.aout_sym.tabsize); - dbg_print("AOUT s : 0x%x\n", mbi->u.aout_sym.strsize); - dbg_print("AOUT a : 0x%x\n", mbi->u.aout_sym.addr); - dbg_print("AOUT r : 0x%x\n", mbi->u.aout_sym.reserved); + multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym); + dbg_print("multiboot_aout_symbol_table: tabsize = 0x%0x, " + "strsize = 0x%x, addr = 0x%x\n", + multiboot_aout_sym->tabsize, multiboot_aout_sym->strsize, + multiboot_aout_sym->addr); } + + // Is the section header table of ELF valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_ELF)) { - dbg_print("ELF n : 0x%x\n", mbi->u.elf_sec.num); - dbg_print("ELF s : 0x%x\n", mbi->u.elf_sec.size); - dbg_print("ELF a : 0x%x\n", mbi->u.elf_sec.addr); - dbg_print("ELF h : 0x%x\n", mbi->u.elf_sec.shndx); + multiboot_elf_section_header_table_t *multiboot_elf_sec = + &(mbi->u.elf_sec); + dbg_print("multiboot_elf_sec: num = %u, size = 0x%x," + " addr = 0x%x, shndx = 0x%x\n", + multiboot_elf_sec->num, multiboot_elf_sec->size, + multiboot_elf_sec->addr, multiboot_elf_sec->shndx); + } + + // Are mmap_* valid? + if (has_flag(mbi->flags, MULTIBOOT_FLAG_MMAP)) { + dbg_print("mmap_addr = 0x%x, mmap_length = 0x%x\n", mbi->mmap_addr, + mbi->mmap_length); + multiboot_memory_map_t *mmap = first_mmap_entry(mbi); + for (int i = 0; mmap; ++i, mmap = next_mmap_entry(mbi, mmap)) { + dbg_print(" [%2d] base_addr = 0x%09x%09x," + " length = 0x%09x%09x, type = 0x%x (%s)\n", + i, mmap->base_addr_high, mmap->base_addr_low, + mmap->length_high, mmap->length_low, mmap->type, + mmap_type_name(mmap)); + } + } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_DRIVE_INFO)) { + dbg_print("Drives: 0x%x\n", mbi->drives_length); + dbg_print("Addr : 0x%x\n", mbi->drives_addr); + } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_CONFIG_TABLE)) { + dbg_print("Config: 0x%x\n", mbi->config_table); + } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_BOOT_LOADER_NAME)) { + dbg_print("boot_loader_name: %s\n", (char *)mbi->boot_loader_name); + } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_APM_TABLE)) { + dbg_print("APM : 0x%x\n", mbi->apm_table); + } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_VBE_INFO)) { + dbg_print("VBE Co: 0x%x\n", mbi->vbe_control_info); + dbg_print("VBE Mo: 0x%x\n", mbi->vbe_mode_info); + dbg_print("VBE In: 0x%x\n", mbi->vbe_mode); + dbg_print("VBE se: 0x%x\n", mbi->vbe_interface_seg); + dbg_print("VBE of: 0x%x\n", mbi->vbe_interface_off); + dbg_print("VBE le: 0x%x\n", mbi->vbe_interface_len); } - dbg_print("MMap : 0x%x\n", mbi->mmap_length); - dbg_print("Addr : 0x%x\n", mbi->mmap_addr); - dbg_print("Drives: 0x%x\n", mbi->drives_length); - dbg_print("Addr : 0x%x\n", mbi->drives_addr); - dbg_print("Config: 0x%x\n", mbi->config_table); - dbg_print("Loader: 0x%x (%s)\n", mbi->boot_loader_name, - (char *)mbi->boot_loader_name); - dbg_print("APM : 0x%x\n", mbi->apm_table); - dbg_print("VBE Co: 0x%x\n", mbi->vbe_control_info); - dbg_print("VBE Mo: 0x%x\n", mbi->vbe_mode_info); - dbg_print("VBE In: 0x%x\n", mbi->vbe_mode); - dbg_print("VBE se: 0x%x\n", mbi->vbe_interface_seg); - dbg_print("VBE of: 0x%x\n", mbi->vbe_interface_off); - dbg_print("VBE le: 0x%x\n", mbi->vbe_interface_len); dbg_print("--------------------------------------------------\n"); dbg_print("\n"); } From c673fb9351b7cc45487f8477678b045f92482425 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 May 2019 17:03:37 +0200 Subject: [PATCH 11/21] Improve multiboot code --- mentos/inc/multiboot.h | 23 ++++------------------ mentos/src/multiboot.c | 44 +++++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/mentos/inc/multiboot.h b/mentos/inc/multiboot.h index 08450fa..458a643 100644 --- a/mentos/inc/multiboot.h +++ b/mentos/inc/multiboot.h @@ -124,17 +124,13 @@ typedef struct multiboot_module { typedef struct multiboot_memory_map { uint32_t size; - uint32_t base_addr_low, base_addr_high; - uint32_t length_low, length_high; + uint32_t base_addr_low; + uint32_t base_addr_high; + uint32_t length_low; + uint32_t length_high; uint32_t type; } multiboot_memory_map_t; -typedef struct multiboot_color { - uint8_t red; - uint8_t green; - uint8_t blue; -} multiboot_color_t; - // TODO: doxygen comment. typedef struct multiboot_info { /// Multiboot info version number. @@ -206,16 +202,5 @@ typedef struct multiboot_info { }; } __attribute__((packed)) multiboot_info_t; -// Be careful that the offset 0 is base_addr_low but no size. -/// @brief The memory map. -typedef struct memory_map { - uint32_t size; - uint32_t base_addr_low; - uint32_t base_addr_high; - uint32_t length_low; - uint32_t length_high; - uint32_t type; -} memory_map_t; - // TODO: doxygen comment. void dump_multiboot(multiboot_info_t *mboot_ptr); diff --git a/mentos/src/multiboot.c b/mentos/src/multiboot.c index daef81c..3597250 100644 --- a/mentos/src/multiboot.c +++ b/mentos/src/multiboot.c @@ -81,22 +81,22 @@ void dump_multiboot(multiboot_info_t *mbi) dbg_print("MULTIBOOT header at 0x%x:\n", mbi); // Print out the flags. - dbg_print("flags = 0x%x\n", mbi->flags); + dbg_print("%-16s = 0x%x\n", "flags", mbi->flags); // Are mem_* valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_MEM)) { - dbg_print("mem_lower = %u Kb (%u Mb), " - "mem_upper = %u Kb (%u Mb), " - "total = %u Kb (%u Mb)\n", - mbi->mem_lower, mbi->mem_lower / K, mbi->mem_upper, - mbi->mem_upper / K, mbi->mem_lower + mbi->mem_upper, + dbg_print("%-16s = %u Kb (%u Mb)\n", "mem_lower", mbi->mem_lower, + mbi->mem_lower / K); + dbg_print("%-16s = %u Kb (%u Mb)\n", "mem_upper", mbi->mem_upper, + mbi->mem_upper / K); + dbg_print("%-16s = %u Kb (%u Mb)\n", "total", + mbi->mem_lower + mbi->mem_upper, (mbi->mem_lower + mbi->mem_upper) / K); } // Is boot_device valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_DEVICE)) { - dbg_print("boot_device = 0x%x (0x%x)", mbi->boot_device, - ((mbi->boot_device) & 0xFF000000)); + dbg_print("%-16s = 0x%x (0x%x)", "boot_device", mbi->boot_device); switch ((mbi->boot_device) & 0xFF000000) { case 0x00000000: dbg_print("(floppy)\n"); @@ -111,16 +111,19 @@ void dump_multiboot(multiboot_info_t *mbi) // Is the command line passed? if (has_flag(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { - dbg_print("cmdline: %s\n", (char *)mbi->cmdline); + dbg_print("%-16s = %s\n", "cmdline", (char *)mbi->cmdline); } // Are mods_* valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_MODS)) { - dbg_print("mods_count = %d, mods_addr = 0x%x\n", (int)mbi->mods_count, - (int)mbi->mods_addr); + dbg_print("%-16s = %d\n", "mods_count", mbi->mods_count); + dbg_print("%-16s = 0x%x\n", "mods_addr", mbi->mods_addr); multiboot_module_t *mod = first_module(mbi); for (int i = 0; mod; ++i, mod = next_module(mbi, mod)) { - dbg_print(" [%2d] mod_start = 0x%x, mod_end = 0x%x, cmdline = %s\n", + dbg_print(" [%2d] " + "mod_start = 0x%x, " + "mod_end = 0x%x, " + "cmdline = %s\n", i, mod->mod_start, mod->mod_end, (char *)mod->cmdline); } } @@ -152,30 +155,39 @@ void dump_multiboot(multiboot_info_t *mbi) // Are mmap_* valid? if (has_flag(mbi->flags, MULTIBOOT_FLAG_MMAP)) { - dbg_print("mmap_addr = 0x%x, mmap_length = 0x%x\n", mbi->mmap_addr, - mbi->mmap_length); + dbg_print("%-16s = 0x%x\n", "mmap_addr", mbi->mmap_addr); + dbg_print("%-16s = 0x%x (%d entries)\n", "mmap_length", + mbi->mmap_length, + mbi->mmap_length / sizeof(multiboot_memory_map_t)); multiboot_memory_map_t *mmap = first_mmap_entry(mbi); for (int i = 0; mmap; ++i, mmap = next_mmap_entry(mbi, mmap)) { - dbg_print(" [%2d] base_addr = 0x%09x%09x," - " length = 0x%09x%09x, type = 0x%x (%s)\n", + dbg_print(" [%2d] " + "base_addr = 0x%09x%09x, " + "length = 0x%09x%09x, " + "type = 0x%x (%s)\n", i, mmap->base_addr_high, mmap->base_addr_low, mmap->length_high, mmap->length_low, mmap->type, mmap_type_name(mmap)); } } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_DRIVE_INFO)) { dbg_print("Drives: 0x%x\n", mbi->drives_length); dbg_print("Addr : 0x%x\n", mbi->drives_addr); } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_CONFIG_TABLE)) { dbg_print("Config: 0x%x\n", mbi->config_table); } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_BOOT_LOADER_NAME)) { dbg_print("boot_loader_name: %s\n", (char *)mbi->boot_loader_name); } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_APM_TABLE)) { dbg_print("APM : 0x%x\n", mbi->apm_table); } + if (has_flag(mbi->flags, MULTIBOOT_FLAG_VBE_INFO)) { dbg_print("VBE Co: 0x%x\n", mbi->vbe_control_info); dbg_print("VBE Mo: 0x%x\n", mbi->vbe_mode_info); From c16a750251369d438bd9fcefa9e8b0de4594f7c4 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 May 2019 17:13:35 +0200 Subject: [PATCH 12/21] Use standard limits for VFS file and path length --- initscp/CMakeLists.txt | 4 --- initscp/inc/initfscp.h | 42 ---------------------------- initscp/src/initfscp.c | 59 ++++++++++++++++++++++++++++++++++----- mentos/inc/fs/initrd.h | 2 +- mentos/inc/fs/vfs_types.h | 2 +- mentos/inc/kernel.h | 6 ---- mentos/src/fs/initrd.c | 4 +-- 7 files changed, 56 insertions(+), 63 deletions(-) delete mode 100644 initscp/inc/initfscp.h diff --git a/initscp/CMakeLists.txt b/initscp/CMakeLists.txt index 5b8cac6..c35827b 100644 --- a/initscp/CMakeLists.txt +++ b/initscp/CMakeLists.txt @@ -13,10 +13,6 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") -# ------------------------------------------------------------------------------ -# Add the includes. -include_directories(inc) - # ------------------------------------------------------------------------------ # Add the source files. set(SOURCE_FILES src/initfscp.c) diff --git a/initscp/inc/initfscp.h b/initscp/inc/initfscp.h deleted file mode 100644 index 87157e4..0000000 --- a/initscp/inc/initfscp.h +++ /dev/null @@ -1,42 +0,0 @@ -/// @file initfscp.h -/// @brief - -#pragma once - -#define MAX_FILENAME_LENGTH 64 -#define MAX_FILES 32 -#define INITFSCP_VER "0.3.0" - -#define FS_FILE 0x01 ///< Identifies a file. -#define FS_DIRECTORY 0x02 ///< Identifies a directory. -#define FS_CHARDEVICE 0x04 ///< Identifies a character devies. -#define FS_BLOCKDEVICE 0x08 ///< Identifies a block devies. -#define FS_PIPE 0x10 ///< Identifies a pipe. -#define FS_SYMLINK 0x20 ///< Identifies a symbolic link. -#define FS_MOUNTPOINT 0x40 ///< Identifies a mount-point. - -#define RESET "\033[00m" -#define BLACK "\033[30m" -#define RED "\033[31m" -#define GREEN "\033[32m" -#define YELLOW "\033[33m" -#define BLUE "\033[34m" -#define MAGENTA "\033[35m" -#define CYAN "\033[36m" -#define WHITE "\033[37m" - -/// @brief Information concerning a file. -typedef struct initrd_file_t { - /// Number used as delimiter, it must be set to 0xBF. - int magic; - /// The name of the file. - char fileName[MAX_FILENAME_LENGTH]; - /// The type of the file. - short int file_type; - /// The uid of the owner. - int uid; - /// Offset of the starting address. - unsigned int offset; - /// Dimension of the file. - unsigned int length; -} initrd_file_t; diff --git a/initscp/src/initfscp.c b/initscp/src/initfscp.c index 4b7ec7e..8bae56c 100755 --- a/initscp/src/initfscp.c +++ b/initscp/src/initfscp.c @@ -4,16 +4,60 @@ #include #include #include -#include #include #include #include #include #include +#define INITFSCP_VER "0.3.0" +#define INITFSCP_NAME_MAX 255 +#define INITFSCP_MAX_FILES 32 + +#define RESET "\033[00m" +#define BLACK "\033[30m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define MAGENTA "\033[35m" +#define CYAN "\033[36m" +#define WHITE "\033[37m" + +/// Identifies a file. +#define FS_FILE 0x01 +/// Identifies a directory. +#define FS_DIRECTORY 0x02 +/// Identifies a character devies. +#define FS_CHARDEVICE 0x04 +/// Identifies a block devies. +#define FS_BLOCKDEVICE 0x08 +/// Identifies a pipe. +#define FS_PIPE 0x10 +/// Identifies a symbolic link. +#define FS_SYMLINK 0x20 +/// Identifies a mount-point. +#define FS_MOUNTPOINT 0x40 + +/// @brief Information concerning a file. +typedef struct initrd_file_t { + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The name of the file. + char fileName[INITFSCP_NAME_MAX]; + /// The type of the file. + short int file_type; + /// The uid of the owner. + int uid; + /// Offset of the starting address. + unsigned int offset; + /// Dimension of the file. + unsigned int length; +} initrd_file_t; + static FILE *target_fs = NULL; -static initrd_file_t headers[MAX_FILES]; -static char mount_points[MAX_FILES][MAX_FILENAME_LENGTH]; +static initrd_file_t headers[INITFSCP_MAX_FILES]; +static char mount_points[INITFSCP_MAX_FILES][INITFSCP_NAME_MAX]; static int mountpoint_idx = 0; static int header_idx = 0; static int header_offset = 0; @@ -91,9 +135,9 @@ static bool open_target_fs(int argc, char *argv[]) static bool init_headers() { printf("%-64s", "Initializing headers structures..."); - for (size_t i = 0; i < MAX_FILES; i++) { + for (size_t i = 0; i < INITFSCP_MAX_FILES; i++) { headers[i].magic = 0xBF; - memset(headers[i].fileName, 0, MAX_FILENAME_LENGTH); + memset(headers[i].fileName, 0, INITFSCP_NAME_MAX); headers[i].file_type = 0; headers[i].uid = 0; headers[i].offset = 0; @@ -218,7 +262,7 @@ static bool create_file_headers(char *mountpoint, char *directory) static bool write_file_system(char *mountpoint) { printf("Copying data to filesystem...\n"); - for (int i = 0; i < MAX_FILES; i++) { + for (int i = 0; i < INITFSCP_MAX_FILES; i++) { // -------------------------------------------------------------------- char absolute_path[256]; memset(absolute_path, 0, 256); @@ -288,7 +332,8 @@ int main(int argc, char *argv[]) // ------------------------------------------------------------------------ // Create file headers. - header_offset = sizeof(struct initrd_file_t) * MAX_FILES + sizeof(int); + header_offset = + sizeof(struct initrd_file_t) * INITFSCP_MAX_FILES + sizeof(int); printf("Creating headers...\n"); for (uint32_t i = 1; i < argc; ++i) { if (is_option_source(argv[i]) && ((i + 1) < argc)) { diff --git a/mentos/inc/fs/initrd.h b/mentos/inc/fs/initrd.h index 636df7f..e5111d3 100644 --- a/mentos/inc/fs/initrd.h +++ b/mentos/inc/fs/initrd.h @@ -29,7 +29,7 @@ typedef struct initrd_file_t { /// Number used as delimiter, it must be set to 0xBF. int magic; /// The name of the file. - char fileName[MAX_FILENAME_LENGTH]; + char fileName[NAME_MAX]; /// The type of the file. short int file_type; /// The uid of the owner. diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index db8cf0d..19bb9d3 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -97,7 +97,7 @@ typedef struct mountpoint_t { /// The id of the mountpoint. int32_t mp_id; /// Name of the mountpoint. - char mountpoint[MAX_FILENAME_LENGTH]; + char mountpoint[NAME_MAX]; /// Maschera dei permessi. unsigned int pmask; /// User ID. diff --git a/mentos/inc/kernel.h b/mentos/inc/kernel.h index dd7c2f8..38fe385 100644 --- a/mentos/inc/kernel.h +++ b/mentos/inc/kernel.h @@ -12,12 +12,6 @@ #include "stdbool.h" #include "multiboot.h" -/// The maximum length of a file name. -#define MAX_FILENAME_LENGTH 64 - -/// The maximum length of a path. -#define MAX_PATH_LENGTH 256 - /// The maximum number of modules. #define MAX_MODULES 10 diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index 93053ee..c80762e 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -217,7 +217,7 @@ int initrd_rmdir(const char *path) } // Remove the directory. direntry->magic = 0; - memset(direntry->fileName, 0, MAX_FILENAME_LENGTH); + memset(direntry->fileName, 0, NAME_MAX); direntry->file_type = 0; direntry->uid = 0; direntry->offset = 0; @@ -320,7 +320,7 @@ int initfs_remove(const char *path) // Remove the directory. file->magic = 0; - memset(file->fileName, 0, MAX_FILENAME_LENGTH); + memset(file->fileName, 0, NAME_MAX); file->file_type = 0; file->uid = 0; file->offset = 0; From bc2623ce999b8618fb5a2e1cf72d81817c5b83a5 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 May 2019 17:22:21 +0200 Subject: [PATCH 13/21] Clean up stdlib --- mentos/inc/libc/stdlib.h | 22 ++++------------------ mentos/src/libc/stdlib.c | 29 ++--------------------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/mentos/inc/libc/stdlib.h b/mentos/inc/libc/stdlib.h index 1142c18..e508a61 100644 --- a/mentos/inc/libc/stdlib.h +++ b/mentos/inc/libc/stdlib.h @@ -11,29 +11,15 @@ /// @brief Malloc based on the number of elements. void *calloc(size_t element_number, size_t element_size); -/// @brief Allows to set the seed of the random value generator. -void srand(int x); - void *malloc(unsigned int size); void free(void * p); +/// The maximum value returned by the rand function. +#define RAND_MAX ((1U << 31U) - 1U) -#ifndef MS_RAND - -/// @brief The maximum value of the random. -#define RAND_MAX ((1U << 31) - 1) +/// @brief Allows to set the seed of the random value generator. +void srand(int x); /// @brief Generates a random value. int rand(); - -// MS rand -#else - -#define RAND_MAX_32 ((1U << 31) - 1) - -#define RAND_MAX ((1U << 15) - 1) - -int rand(); - -#endif diff --git a/mentos/src/libc/stdlib.c b/mentos/src/libc/stdlib.c index 2af772e..8cde7c1 100644 --- a/mentos/src/libc/stdlib.c +++ b/mentos/src/libc/stdlib.c @@ -8,9 +8,6 @@ #include "stdlib.h" #include "string.h" -/// Used to align the memory. -#define ALIGN(x) (((x) + (sizeof(size_t) - 1)) & ~(sizeof(size_t) - 1)) - void *malloc(unsigned int size) { void *_res; @@ -36,38 +33,16 @@ void *calloc(size_t element_number, size_t element_size) } /// Seed used to generate random numbers. -int rseed = 0; +static int rseed = 0; inline void srand(int x) { rseed = x; } -#ifndef MS_RAND - -/// The maximum value returned by the rand function. -#define RAND_MAX ((1U << 31) - 1) - /// @brief Returns a pseudo-random integral number in the range /// between 0 and RAND_MAX. inline int rand() { - return rseed = (rseed * 1103515245 + 12345) & RAND_MAX; + return rseed = (rseed * 1103515245U + 12345U) & RAND_MAX; } - -// MS rand. -#else -/// The maximum 32bit value returned by the rand function. -#define RAND_MAX_32 ((1U << 31) - 1) - -/// The maximum value returned by the rand function. -#define RAND_MAX ((1U << 15) - 1) - -/// @brief Returns a pseudo-random integral number in the range -/// between 0 and RAND_MAX. -inline int rand() -{ - return (rseed = (rseed * 214013 + 2531011) & RAND_MAX_32) >> 16; -} -<<<<<<< HEAD -#endif From 6fd063984a403ed984032777e866673003a08b0e Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 May 2019 17:22:47 +0200 Subject: [PATCH 14/21] Add new defines to vfs types --- mentos/inc/fs/vfs_types.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index 19bb9d3..dbbb06e 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -10,20 +10,25 @@ #include "kernel.h" #include "dirent.h" +#define PATH_SEPARATOR '/' +#define PATH_SEPARATOR_STRING "/" +#define PATH_UP ".." +#define PATH_DOT "." + /// Identifies a file. -#define FS_FILE 0x01 +#define FS_FILE 0x01U /// Identifies a directory. -#define FS_DIRECTORY 0x02 +#define FS_DIRECTORY 0x02U /// Identifies a character devies. -#define FS_CHARDEVICE 0x04 +#define FS_CHARDEVICE 0x04U /// Identifies a block devies. -#define FS_BLOCKDEVICE 0x08 +#define FS_BLOCKDEVICE 0x08U /// Identifies a pipe. -#define FS_PIPE 0x10 +#define FS_PIPE 0x10U /// Identifies a symbolic link. -#define FS_SYMLINK 0x20 +#define FS_SYMLINK 0x20U /// Identifies a mount-point. -#define FS_MOUNTPOINT 0x40 +#define FS_MOUNTPOINT 0x40U /// Function used to open a directory. typedef DIR *(*opendir_callback)(const char *); From b01eccca2ef1a36dc4a7b54f531e282cc491a282 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Mon, 4 Oct 2021 11:44:02 +0200 Subject: [PATCH 15/21] Update MentOs code to the latest development version. --- .clang-format | 23 +- CMakeLists.txt | 197 +- CODING_STYLE.md | 5 +- README.md | 222 +- TODO.md | 1 + doc/CMakeCache.txt | 38 - doc/CMakeFiles/cmake.check_cache | 1 - doc/CMakeLists.txt | 37 + doc/{dreamos.doxyfile => Doxygen} | 290 ++- doc/HOWTO_use_grub_script.md | 17 - doc/doxygen.css | 1471 +++++++++++ doc/doxygen.scss | 2169 +++++++++++++++++ doc/footer.html | 38 + doc/header.html | 63 + doc/resources/gdt_bits.png | Bin 0 -> 8181 bytes doc/signal.md | 49 + doc/syscall.md | 207 ++ doc/syscall.txt | 180 -- files/bin/cat | Bin 0 -> 43644 bytes files/bin/clear | Bin 0 -> 37588 bytes files/bin/cpuid | Bin 0 -> 37564 bytes files/bin/date | Bin 0 -> 38084 bytes files/bin/echo | Bin 0 -> 37612 bytes files/bin/env | Bin 0 -> 37604 bytes files/bin/init | Bin 0 -> 37984 bytes files/bin/ipcrm | Bin 0 -> 37564 bytes files/bin/ipcs | Bin 0 -> 37564 bytes files/bin/kill | Bin 0 -> 43684 bytes files/bin/login | Bin 0 -> 52604 bytes files/bin/logo | Bin 0 -> 37588 bytes files/bin/ls | Bin 0 -> 52980 bytes files/bin/mkdir | Bin 0 -> 43588 bytes files/bin/nice | Bin 0 -> 37640 bytes files/bin/poweroff | Bin 0 -> 37640 bytes files/bin/ps | Bin 0 -> 47928 bytes files/bin/pwd | Bin 0 -> 37640 bytes files/bin/rm | Bin 0 -> 48096 bytes files/bin/rmdir | Bin 0 -> 43588 bytes files/bin/shell | Bin 0 -> 62076 bytes files/bin/showpid | Bin 0 -> 37644 bytes files/bin/sleep | Bin 0 -> 38084 bytes files/bin/tests/t_abort | Bin 0 -> 43620 bytes files/bin/tests/t_alarm | Bin 0 -> 43636 bytes files/bin/tests/t_exec | Bin 0 -> 42112 bytes files/bin/tests/t_exec_callee | Bin 0 -> 37588 bytes files/bin/tests/t_fork | Bin 0 -> 37700 bytes files/bin/tests/t_getenv | Bin 0 -> 37604 bytes files/bin/tests/t_gid | Bin 0 -> 38172 bytes files/bin/tests/t_groups | Bin 0 -> 38252 bytes files/bin/tests/t_itimer | Bin 0 -> 48180 bytes files/bin/tests/t_kill | Bin 0 -> 43760 bytes files/bin/tests/t_mem | Bin 0 -> 37604 bytes files/bin/tests/t_periodic1 | Bin 0 -> 48076 bytes files/bin/tests/t_periodic2 | Bin 0 -> 43656 bytes files/bin/tests/t_periodic3 | Bin 0 -> 43656 bytes files/bin/tests/t_setenv | Bin 0 -> 37984 bytes files/bin/tests/t_sigaction | Bin 0 -> 43624 bytes files/bin/tests/t_sigfpe | Bin 0 -> 43596 bytes files/bin/tests/t_siginfo | Bin 0 -> 43600 bytes files/bin/tests/t_sigmask | Bin 0 -> 43600 bytes files/bin/tests/t_sigusr | Bin 0 -> 48180 bytes files/bin/tests/t_sleep | Bin 0 -> 38100 bytes files/bin/tests/t_stopcont | Bin 0 -> 38252 bytes files/bin/touch | Bin 0 -> 37676 bytes files/bin/uname | Bin 0 -> 37608 bytes files/etc/group | 2 + files/etc/hostname | 1 + files/etc/passwd | 2 + files/passwd | 3 - files/test2.txt | 2 + initscp/CMakeLists.txt | 8 +- initscp/src/initfscp.c | 653 ++--- libc/CMakeLists.txt | 140 ++ libc/inc/assert.h | 20 + libc/inc/bits/ioctls.h | 9 + libc/inc/bits/stat.h | 36 + libc/inc/bits/termios-struct.h | 45 + {mentos/inc/libc => libc/inc}/ctype.h | 20 +- libc/inc/debug.h | 44 + libc/inc/fcntl.h | 46 + libc/inc/fcvt.h | 37 + libc/inc/grp.h | 76 + libc/inc/io/mm_io.h | 27 + libc/inc/io/port_io.h | 43 + libc/inc/ipc/ipc.h | 25 + libc/inc/ipc/msg.h | 70 + libc/inc/ipc/sem.h | 40 + {mentos/inc/sys => libc/inc/ipc}/shm.h | 119 +- libc/inc/libgen.h | 41 + libc/inc/limits.h | 58 + libc/inc/math.h | 158 ++ libc/inc/pwd.h | 56 + libc/inc/sched.h | 29 + libc/inc/signal.h | 273 +++ {mentos/inc/libc => libc/inc}/stdarg.h | 8 +- libc/inc/stdbool.h | 13 + libc/inc/stddef.h | 71 + {mentos/inc/libc => libc/inc}/stdint.h | 4 +- libc/inc/stdio.h | 129 + libc/inc/stdlib.h | 71 + libc/inc/strerror.h | 14 + libc/inc/string.h | 311 +++ libc/inc/sys/bitops.h | 41 + libc/inc/sys/dirent.h | 50 + libc/inc/sys/errno.h | 138 ++ libc/inc/sys/ioctl.h | 15 + libc/inc/sys/reboot.h | 36 + libc/inc/sys/stat.h | 35 + libc/inc/sys/types.h | 70 + libc/inc/sys/unistd.h | 223 ++ libc/inc/sys/utsname.h | 30 + {mentos => libc}/inc/sys/wait.h | 27 +- libc/inc/system/syscall_types.h | 308 +++ libc/inc/termios.h | 22 + libc/inc/time.h | 110 + libc/src/abort.c | 81 + libc/src/assert.c | 19 + libc/src/crt0.S | 15 + libc/src/ctype.c | 63 + libc/src/debug.c | 105 + libc/src/fcvt.c | 104 + libc/src/grp.c | 238 ++ libc/src/io/mm_io.c | 37 + libc/src/io/port_io.c | 59 + libc/src/ipc/ipc.c | 36 + libc/src/libc_start.c | 40 + libc/src/libgen.c | 141 ++ libc/src/math.c | 180 ++ libc/src/pwd.c | 162 ++ libc/src/sched.c | 13 + libc/src/setenv.c | 146 ++ libc/src/stdio.c | 207 ++ libc/src/stdlib.c | 91 + libc/src/strerror.c | 480 ++++ libc/src/string.c | 702 ++++++ libc/src/sys/errno.c | 15 + libc/src/sys/ioctl.c | 11 + libc/src/sys/unistd.c | 37 + libc/src/sys/utsname.c | 12 + libc/src/termios.c | 38 + libc/src/time.c | 399 +++ libc/src/unistd/chdir.c | 13 + libc/src/unistd/close.c | 11 + libc/src/unistd/exec.c | 243 ++ libc/src/unistd/exit.c | 15 + libc/src/unistd/fork.c | 11 + libc/src/unistd/getcwd.c | 11 + libc/src/unistd/getdents.c | 12 + libc/src/unistd/getgid.c | 11 + libc/src/unistd/getpid.c | 11 + libc/src/unistd/getppid.c | 11 + libc/src/unistd/getsid.c | 11 + libc/src/unistd/interval.c | 6 + libc/src/unistd/kill.c | 11 + libc/src/unistd/lseek.c | 11 + libc/src/unistd/mkdir.c | 11 + libc/src/unistd/nice.c | 11 + libc/src/unistd/open.c | 11 + libc/src/unistd/read.c | 11 + libc/src/unistd/reboot.c | 11 + libc/src/unistd/rmdir.c | 11 + libc/src/unistd/setgid.c | 11 + libc/src/unistd/setsid.c | 11 + libc/src/unistd/signal.c | 103 + libc/src/unistd/stat.c | 15 + libc/src/unistd/unlink.c | 11 + libc/src/unistd/waitpid.c | 37 + libc/src/unistd/write.c | 11 + libc/src/vscanf.c | 138 ++ libc/src/vsprintf.c | 693 ++++++ mentos/CMakeLists.txt | 551 +++-- mentos/{boot.asm => boot.S} | 44 +- mentos/boot.lds | 59 + mentos/inc/bits/ioctls.h | 9 + mentos/inc/bits/termios-struct.h | 45 + mentos/inc/boot.h | 85 + mentos/inc/descriptor_tables/gdt.h | 147 +- mentos/inc/descriptor_tables/idt.h | 54 +- mentos/inc/descriptor_tables/isr.h | 135 +- mentos/inc/descriptor_tables/tss.h | 80 +- mentos/inc/devices/fpu.h | 127 +- mentos/inc/devices/pci.h | 105 +- mentos/inc/drivers/ata.h | 316 +-- mentos/inc/drivers/fdc.h | 21 +- mentos/inc/drivers/keyboard/keyboard.h | 26 +- mentos/inc/drivers/keyboard/keymap.h | 363 ++- mentos/inc/drivers/mouse.h | 18 +- mentos/inc/drivers/rtc.h | 16 + mentos/inc/elf/elf.h | 300 ++- mentos/inc/fs/fcntl.h | 26 - mentos/inc/fs/initrd.h | 130 +- mentos/inc/fs/ioctl.h | 13 + mentos/inc/fs/procfs.h | 69 + mentos/inc/fs/vfs.h | 179 +- mentos/inc/fs/vfs_types.h | 239 +- mentos/inc/hardware/cmd_cpuid.h | 86 - mentos/inc/hardware/cpuid.h | 96 + mentos/inc/hardware/pic8259.h | 12 +- mentos/inc/hardware/timer.h | 250 +- mentos/inc/io/mm_io.h | 14 +- mentos/inc/io/port_io.h | 28 +- mentos/inc/io/proc_modules.h | 17 + mentos/inc/io/vga/vga.h | 24 + mentos/inc/io/vga/vga_font.h | 917 +++++++ mentos/inc/io/vga/vga_mode.h | 421 ++++ mentos/inc/io/vga/vga_palette.h | 291 +++ mentos/inc/io/video.h | 163 +- mentos/inc/kernel.h | 309 +-- mentos/inc/{libc => klib}/compiler.h | 2 +- mentos/inc/klib/hashmap.h | 136 ++ mentos/inc/klib/irqflags.h | 52 + mentos/inc/klib/list.h | 139 ++ mentos/inc/klib/list_head.h | 173 ++ mentos/inc/{libc => klib}/mutex.h | 10 +- mentos/inc/klib/ndtree.h | 198 ++ mentos/inc/klib/rbtree.h | 180 ++ mentos/inc/{libc => klib}/spinlock.h | 16 +- mentos/inc/klib/stdatomic.h | 166 ++ mentos/inc/libc/assert.h | 24 - mentos/inc/libc/bitset.h | 42 - mentos/inc/libc/fcvt.h | 27 - mentos/inc/libc/hashmap.h | 80 - mentos/inc/libc/irqflags.h | 72 - mentos/inc/libc/libgen.h | 28 - mentos/inc/libc/limits.h | 47 - mentos/inc/libc/list.h | 107 - mentos/inc/libc/list_head.h | 129 - mentos/inc/libc/math.h | 131 - mentos/inc/libc/ordered_array.h | 53 - mentos/inc/libc/queue.h | 76 - mentos/inc/libc/rbtree.h | 134 - mentos/inc/libc/stdatomic.h | 109 - mentos/inc/libc/stdbool.h | 16 - mentos/inc/libc/stddef.h | 87 - mentos/inc/libc/stdio.h | 50 - mentos/inc/libc/stdlib.h | 25 - mentos/inc/libc/strerror.h | 12 - mentos/inc/libc/string.h | 159 -- mentos/inc/libc/tree.h | 77 - mentos/inc/link_access.h | 45 + mentos/inc/mem/buddysystem.h | 129 +- mentos/inc/mem/gfp.h | 232 +- mentos/inc/mem/kheap.h | 86 +- mentos/inc/mem/paging.h | 291 ++- mentos/inc/mem/slab.h | 169 ++ mentos/inc/mem/vmem_map.h | 77 + mentos/inc/mem/zone_allocator.h | 259 +- mentos/inc/misc/bitops.h | 30 - mentos/inc/misc/clock.h | 98 - mentos/inc/misc/debug.h | 138 +- mentos/inc/multiboot.h | 312 ++- mentos/inc/proc_access.h | 272 +++ mentos/inc/process/prio.h | 59 +- mentos/inc/process/process.h | 301 ++- mentos/inc/process/scheduler.h | 129 +- mentos/inc/process/wait.h | 111 + mentos/inc/sys/dirent.h | 48 - mentos/inc/sys/errno.h | 503 +--- mentos/inc/sys/ipc.h | 54 +- mentos/inc/sys/kernel_levels.h | 31 + mentos/inc/sys/module.h | 24 +- mentos/inc/sys/reboot.h | 19 +- mentos/inc/sys/stat.h | 41 - mentos/inc/sys/types.h | 69 +- mentos/inc/sys/unistd.h | 95 - mentos/inc/sys/utsname.h | 29 +- mentos/inc/system/panic.h | 4 +- mentos/inc/system/printk.h | 11 +- mentos/inc/system/signal.h | 325 +++ mentos/inc/system/signal_defs.h | 121 - mentos/inc/system/syscall.h | 153 +- mentos/inc/system/syscall_types.h | 405 ++- mentos/inc/ui/command/commands.h | 93 - mentos/inc/ui/init/init.h | 10 - mentos/inc/ui/shell/shell.h | 78 - mentos/inc/ui/shell/shell_login.h | 14 - mentos/inc/version.h | 14 +- mentos/kernel.lds | 41 +- mentos/libmentos_memory.a | Bin 0 -> 15858 bytes mentos/src/boot.c | 299 +++ .../{exception.asm => exception.S} | 19 +- mentos/src/descriptor_tables/exception.c | 144 +- mentos/src/descriptor_tables/gdt.S | 30 + mentos/src/descriptor_tables/gdt.asm | 23 - mentos/src/descriptor_tables/gdt.c | 214 +- mentos/src/descriptor_tables/idt.S | 17 + mentos/src/descriptor_tables/idt.asm | 12 - mentos/src/descriptor_tables/idt.c | 198 +- .../{interrupt.asm => interrupt.S} | 14 +- mentos/src/descriptor_tables/interrupt.c | 164 +- mentos/src/descriptor_tables/tss.S | 17 + mentos/src/descriptor_tables/tss.asm | 12 - mentos/src/descriptor_tables/tss.c | 47 +- mentos/src/devices/fpu.c | 226 +- mentos/src/devices/pci.c | 728 +++--- mentos/src/drivers/ata.c | 1711 ++++++------- mentos/src/drivers/fdc.c | 2 +- mentos/src/drivers/keyboard/keyboard.c | 446 ++-- mentos/src/drivers/keyboard/keymap.c | 477 ++-- mentos/src/drivers/mouse.c | 261 +- mentos/src/drivers/rtc.c | 138 ++ mentos/src/elf/elf.c | 449 +++- mentos/src/fs/fcntl.c | 30 - mentos/src/fs/initrd.c | 1000 +++++--- mentos/src/fs/ioctl.c | 35 + mentos/src/fs/namei.c | 24 + mentos/src/fs/open.c | 137 +- mentos/src/fs/procfs.c | 779 ++++++ mentos/src/fs/read_write.c | 118 +- mentos/src/fs/readdir.c | 56 +- mentos/src/fs/stat.c | 86 +- mentos/src/fs/vfs.c | 613 +++-- mentos/src/hardware/cpuid.c | 250 +- mentos/src/hardware/pic8259.c | 134 +- mentos/src/hardware/timer.c | 794 +++++- mentos/src/io/mm_io.c | 14 +- mentos/src/io/port_io.c | 48 +- mentos/src/io/proc_running.c | 488 ++++ mentos/src/io/proc_system.c | 206 ++ mentos/src/io/proc_video.c | 106 + mentos/src/io/stdio.c | 177 ++ mentos/src/io/vga/vga.c | 659 +++++ mentos/src/io/video.c | 580 +++-- mentos/src/ipc/msg.c | 35 + mentos/src/ipc/sem.c | 29 + mentos/src/ipc/shm.c | 396 +++ mentos/src/kernel.c | 522 ++-- mentos/src/kernel/sys.c | 92 +- mentos/src/klib/assert.c | 21 + mentos/src/klib/ctype.c | 63 + mentos/src/klib/fcvt.c | 104 + mentos/src/klib/hashmap.c | 254 ++ mentos/src/klib/libgen.c | 142 ++ mentos/src/klib/list.c | 331 +++ mentos/src/klib/math.c | 180 ++ mentos/src/klib/mutex.c | 34 + mentos/src/klib/ndtree.c | 378 +++ mentos/src/klib/rbtree.c | 564 +++++ mentos/src/klib/spinlock.c | 34 + mentos/src/klib/strerror.c | 480 ++++ mentos/src/klib/string.c | 701 ++++++ mentos/src/klib/time.c | 115 + mentos/src/klib/vscanf.c | 105 + mentos/src/klib/vsprintf.c | 675 +++++ mentos/src/libc/assert.c | 22 - mentos/src/libc/bitset.c | 120 - mentos/src/libc/ctype.c | 63 - mentos/src/libc/fcvt.c | 114 - mentos/src/libc/hashmap.c | 267 -- mentos/src/libc/libgen.c | 74 - mentos/src/libc/list.c | 344 --- mentos/src/libc/math.c | 150 -- mentos/src/libc/mutex.c | 38 - mentos/src/libc/ordered_array.c | 90 - mentos/src/libc/queue.c | 162 -- mentos/src/libc/rbtree.c | 613 ----- mentos/src/libc/spinlock.c | 34 - mentos/src/libc/stdio.c | 160 -- mentos/src/libc/stdlib.c | 48 - mentos/src/libc/strerror.c | 470 ---- mentos/src/libc/string.c | 850 ------- mentos/src/libc/tree.c | 217 -- mentos/src/libc/unistd/chdir.c | 19 - mentos/src/libc/unistd/close.c | 20 - mentos/src/libc/unistd/execve.c | 24 - mentos/src/libc/unistd/exit.c | 17 - mentos/src/libc/unistd/getcwd.c | 19 - mentos/src/libc/unistd/getpid.c | 19 - mentos/src/libc/unistd/getppid.c | 18 - mentos/src/libc/unistd/mkdir.c | 21 - mentos/src/libc/unistd/nice.c | 17 - mentos/src/libc/unistd/open.c | 22 - mentos/src/libc/unistd/read.c | 23 - mentos/src/libc/unistd/reboot.c | 23 - mentos/src/libc/unistd/stat.c | 21 - mentos/src/libc/unistd/vfork.c | 26 - mentos/src/libc/unistd/waitpid.c | 41 - mentos/src/libc/unistd/write.c | 25 - mentos/src/libc/vsprintf.c | 787 ------ mentos/src/mem/buddysystem.c | 476 +++- mentos/src/mem/kheap.c | 1132 ++++----- mentos/src/mem/paging.c | 656 ++++- mentos/src/mem/slab.c | 354 +++ mentos/src/mem/vmem_map.c | 176 ++ mentos/src/mem/zone_allocator.c | 801 +++--- mentos/src/misc/bitops.c | 38 - mentos/src/misc/clock.c | 178 -- mentos/src/misc/debug.c | 245 +- mentos/src/multiboot.c | 311 +-- mentos/src/process/process.c | 704 ++++-- mentos/src/process/scheduler.c | 876 ++++--- mentos/src/process/scheduler_algorithm.c | 160 +- mentos/src/process/{user.asm => user.S} | 49 +- mentos/src/process/wait.c | 42 + mentos/src/sys/dirent.c | 84 - mentos/src/sys/module.c | 68 +- mentos/src/sys/shm.c | 362 --- mentos/src/sys/unistd.c | 37 - mentos/src/sys/utsname.c | 52 +- mentos/src/system/errno.c | 14 +- mentos/src/system/panic.c | 12 +- mentos/src/system/printk.c | 23 +- mentos/src/system/signal.c | 798 ++++++ mentos/src/system/syscall.c | 187 +- mentos/src/ui/command/cmd_cd.c | 59 - mentos/src/ui/command/cmd_clear.c | 15 - mentos/src/ui/command/cmd_cpuid.c | 118 - mentos/src/ui/command/cmd_credits.c | 28 - mentos/src/ui/command/cmd_date.c | 19 - mentos/src/ui/command/cmd_drv_load.c | 53 - mentos/src/ui/command/cmd_echo.c | 22 - mentos/src/ui/command/cmd_ipcrm.c | 50 - mentos/src/ui/command/cmd_ipcs.c | 83 - mentos/src/ui/command/cmd_logo.c | 38 - mentos/src/ui/command/cmd_ls.c | 104 - mentos/src/ui/command/cmd_mkdir.c | 29 - mentos/src/ui/command/cmd_more.c | 47 - mentos/src/ui/command/cmd_newfile.c | 57 - mentos/src/ui/command/cmd_nice.c | 43 - mentos/src/ui/command/cmd_poweroff.c | 21 - mentos/src/ui/command/cmd_ps.c | 54 - mentos/src/ui/command/cmd_pwd.c | 19 - mentos/src/ui/command/cmd_rm.c | 51 - mentos/src/ui/command/cmd_rmdir.c | 37 - mentos/src/ui/command/cmd_showpid.c | 15 - mentos/src/ui/command/cmd_sleep.c | 43 - mentos/src/ui/command/cmd_tester.c | 353 --- mentos/src/ui/command/cmd_touch.c | 43 - mentos/src/ui/command/cmd_uname.c | 97 - mentos/src/ui/command/cmd_whoami.c | 15 - mentos/src/ui/init/init.c | 40 - mentos/src/ui/shell/shell.c | 479 ---- mentos/src/ui/shell/shell_login.c | 152 -- programs/CMakeLists.txt | 127 + programs/cat.c | 40 + programs/clear.c | 13 + programs/cpuid.c | 118 + programs/date.c | 22 + programs/echo.c | 117 + programs/env.c | 17 + programs/init.c | 28 + programs/ipcrm.c | 46 + programs/ipcs.c | 62 + programs/kill.c | 107 + programs/login.c | 193 ++ programs/logo.c | 18 + programs/ls.c | 152 ++ programs/mkdir.c | 30 + programs/nice.c | 35 + programs/poweroff.c | 20 + programs/ps.c | 129 + programs/pwd.c | 17 + programs/rm.c | 78 + programs/rmdir.c | 31 + programs/shell.c | 772 ++++++ programs/showpid.c | 14 + programs/sleep.c | 19 + programs/tests/CMakeLists.txt | 126 + programs/tests/t_abort.c | 46 + programs/tests/t_alarm.c | 51 + programs/tests/t_exec.c | 65 + programs/tests/t_exec_callee.c | 19 + programs/tests/t_fork.c | 66 + programs/tests/t_getenv.c | 21 + programs/tests/t_gid.c | 53 + programs/tests/t_groups.c | 39 + programs/tests/t_itimer.c | 62 + programs/tests/t_kill.c | 66 + programs/tests/t_mem.c | 26 + programs/tests/t_periodic1.c | 47 + programs/tests/t_periodic2.c | 35 + programs/tests/t_periodic3.c | 35 + programs/tests/t_setenv.c | 29 + programs/tests/t_sigaction.c | 49 + programs/tests/t_sigfpe.c | 50 + programs/tests/t_siginfo.c | 51 + programs/tests/t_sigmask.c | 50 + programs/tests/t_sigusr.c | 56 + programs/tests/t_sleep.c | 22 + programs/tests/t_stopcont.c | 60 + programs/touch.c | 37 + programs/uname.c | 40 + scripts/get_text_address.sh | 6 + scripts/replace_links.sh | 3 + 484 files changed, 42358 insertions(+), 20285 deletions(-) create mode 100644 TODO.md delete mode 100644 doc/CMakeCache.txt delete mode 100644 doc/CMakeFiles/cmake.check_cache create mode 100644 doc/CMakeLists.txt rename doc/{dreamos.doxyfile => Doxygen} (91%) delete mode 100644 doc/HOWTO_use_grub_script.md create mode 100644 doc/doxygen.css create mode 100644 doc/doxygen.scss create mode 100644 doc/footer.html create mode 100644 doc/header.html create mode 100644 doc/resources/gdt_bits.png create mode 100644 doc/signal.md create mode 100644 doc/syscall.md delete mode 100644 doc/syscall.txt create mode 100644 files/bin/cat create mode 100644 files/bin/clear create mode 100644 files/bin/cpuid create mode 100644 files/bin/date create mode 100644 files/bin/echo create mode 100644 files/bin/env create mode 100644 files/bin/init create mode 100644 files/bin/ipcrm create mode 100644 files/bin/ipcs create mode 100644 files/bin/kill create mode 100644 files/bin/login create mode 100644 files/bin/logo create mode 100644 files/bin/ls create mode 100644 files/bin/mkdir create mode 100644 files/bin/nice create mode 100644 files/bin/poweroff create mode 100644 files/bin/ps create mode 100644 files/bin/pwd create mode 100644 files/bin/rm create mode 100644 files/bin/rmdir create mode 100644 files/bin/shell create mode 100644 files/bin/showpid create mode 100644 files/bin/sleep create mode 100644 files/bin/tests/t_abort create mode 100644 files/bin/tests/t_alarm create mode 100644 files/bin/tests/t_exec create mode 100644 files/bin/tests/t_exec_callee create mode 100644 files/bin/tests/t_fork create mode 100644 files/bin/tests/t_getenv create mode 100644 files/bin/tests/t_gid create mode 100644 files/bin/tests/t_groups create mode 100644 files/bin/tests/t_itimer create mode 100644 files/bin/tests/t_kill create mode 100644 files/bin/tests/t_mem create mode 100644 files/bin/tests/t_periodic1 create mode 100644 files/bin/tests/t_periodic2 create mode 100644 files/bin/tests/t_periodic3 create mode 100644 files/bin/tests/t_setenv create mode 100644 files/bin/tests/t_sigaction create mode 100644 files/bin/tests/t_sigfpe create mode 100644 files/bin/tests/t_siginfo create mode 100644 files/bin/tests/t_sigmask create mode 100644 files/bin/tests/t_sigusr create mode 100644 files/bin/tests/t_sleep create mode 100644 files/bin/tests/t_stopcont create mode 100644 files/bin/touch create mode 100644 files/bin/uname create mode 100644 files/etc/group create mode 100644 files/etc/hostname create mode 100644 files/etc/passwd delete mode 100644 files/passwd create mode 100644 libc/CMakeLists.txt create mode 100644 libc/inc/assert.h create mode 100644 libc/inc/bits/ioctls.h create mode 100644 libc/inc/bits/stat.h create mode 100644 libc/inc/bits/termios-struct.h rename {mentos/inc/libc => libc/inc}/ctype.h (53%) create mode 100644 libc/inc/debug.h create mode 100644 libc/inc/fcntl.h create mode 100644 libc/inc/fcvt.h create mode 100644 libc/inc/grp.h create mode 100644 libc/inc/io/mm_io.h create mode 100644 libc/inc/io/port_io.h create mode 100644 libc/inc/ipc/ipc.h create mode 100644 libc/inc/ipc/msg.h create mode 100644 libc/inc/ipc/sem.h rename {mentos/inc/sys => libc/inc/ipc}/shm.h (60%) create mode 100644 libc/inc/libgen.h create mode 100644 libc/inc/limits.h create mode 100644 libc/inc/math.h create mode 100644 libc/inc/pwd.h create mode 100644 libc/inc/sched.h create mode 100644 libc/inc/signal.h rename {mentos/inc/libc => libc/inc}/stdarg.h (75%) create mode 100644 libc/inc/stdbool.h create mode 100644 libc/inc/stddef.h rename {mentos/inc/libc => libc/inc}/stdint.h (88%) create mode 100644 libc/inc/stdio.h create mode 100644 libc/inc/stdlib.h create mode 100644 libc/inc/strerror.h create mode 100644 libc/inc/string.h create mode 100644 libc/inc/sys/bitops.h create mode 100644 libc/inc/sys/dirent.h create mode 100644 libc/inc/sys/errno.h create mode 100644 libc/inc/sys/ioctl.h create mode 100644 libc/inc/sys/reboot.h create mode 100644 libc/inc/sys/stat.h create mode 100644 libc/inc/sys/types.h create mode 100644 libc/inc/sys/unistd.h create mode 100644 libc/inc/sys/utsname.h rename {mentos => libc}/inc/sys/wait.h (61%) create mode 100644 libc/inc/system/syscall_types.h create mode 100644 libc/inc/termios.h create mode 100644 libc/inc/time.h create mode 100644 libc/src/abort.c create mode 100644 libc/src/assert.c create mode 100644 libc/src/crt0.S create mode 100644 libc/src/ctype.c create mode 100644 libc/src/debug.c create mode 100644 libc/src/fcvt.c create mode 100644 libc/src/grp.c create mode 100644 libc/src/io/mm_io.c create mode 100644 libc/src/io/port_io.c create mode 100644 libc/src/ipc/ipc.c create mode 100644 libc/src/libc_start.c create mode 100644 libc/src/libgen.c create mode 100644 libc/src/math.c create mode 100644 libc/src/pwd.c create mode 100644 libc/src/sched.c create mode 100644 libc/src/setenv.c create mode 100644 libc/src/stdio.c create mode 100644 libc/src/stdlib.c create mode 100644 libc/src/strerror.c create mode 100644 libc/src/string.c create mode 100644 libc/src/sys/errno.c create mode 100644 libc/src/sys/ioctl.c create mode 100644 libc/src/sys/unistd.c create mode 100644 libc/src/sys/utsname.c create mode 100644 libc/src/termios.c create mode 100644 libc/src/time.c create mode 100644 libc/src/unistd/chdir.c create mode 100644 libc/src/unistd/close.c create mode 100644 libc/src/unistd/exec.c create mode 100644 libc/src/unistd/exit.c create mode 100644 libc/src/unistd/fork.c create mode 100644 libc/src/unistd/getcwd.c create mode 100644 libc/src/unistd/getdents.c create mode 100644 libc/src/unistd/getgid.c create mode 100644 libc/src/unistd/getpid.c create mode 100644 libc/src/unistd/getppid.c create mode 100644 libc/src/unistd/getsid.c create mode 100644 libc/src/unistd/interval.c create mode 100644 libc/src/unistd/kill.c create mode 100644 libc/src/unistd/lseek.c create mode 100644 libc/src/unistd/mkdir.c create mode 100644 libc/src/unistd/nice.c create mode 100644 libc/src/unistd/open.c create mode 100644 libc/src/unistd/read.c create mode 100644 libc/src/unistd/reboot.c create mode 100644 libc/src/unistd/rmdir.c create mode 100644 libc/src/unistd/setgid.c create mode 100644 libc/src/unistd/setsid.c create mode 100644 libc/src/unistd/signal.c create mode 100644 libc/src/unistd/stat.c create mode 100644 libc/src/unistd/unlink.c create mode 100644 libc/src/unistd/waitpid.c create mode 100644 libc/src/unistd/write.c create mode 100644 libc/src/vscanf.c create mode 100644 libc/src/vsprintf.c rename mentos/{boot.asm => boot.S} (76%) create mode 100644 mentos/boot.lds create mode 100644 mentos/inc/bits/ioctls.h create mode 100644 mentos/inc/bits/termios-struct.h create mode 100644 mentos/inc/boot.h create mode 100644 mentos/inc/drivers/rtc.h delete mode 100644 mentos/inc/fs/fcntl.h create mode 100644 mentos/inc/fs/ioctl.h create mode 100644 mentos/inc/fs/procfs.h delete mode 100644 mentos/inc/hardware/cmd_cpuid.h create mode 100644 mentos/inc/hardware/cpuid.h create mode 100644 mentos/inc/io/proc_modules.h create mode 100644 mentos/inc/io/vga/vga.h create mode 100644 mentos/inc/io/vga/vga_font.h create mode 100644 mentos/inc/io/vga/vga_mode.h create mode 100644 mentos/inc/io/vga/vga_palette.h rename mentos/inc/{libc => klib}/compiler.h (90%) create mode 100644 mentos/inc/klib/hashmap.h create mode 100644 mentos/inc/klib/irqflags.h create mode 100644 mentos/inc/klib/list.h create mode 100644 mentos/inc/klib/list_head.h rename mentos/inc/{libc => klib}/mutex.h (74%) create mode 100644 mentos/inc/klib/ndtree.h create mode 100644 mentos/inc/klib/rbtree.h rename mentos/inc/{libc => klib}/spinlock.h (56%) create mode 100644 mentos/inc/klib/stdatomic.h delete mode 100644 mentos/inc/libc/assert.h delete mode 100644 mentos/inc/libc/bitset.h delete mode 100644 mentos/inc/libc/fcvt.h delete mode 100644 mentos/inc/libc/hashmap.h delete mode 100644 mentos/inc/libc/irqflags.h delete mode 100644 mentos/inc/libc/libgen.h delete mode 100644 mentos/inc/libc/limits.h delete mode 100644 mentos/inc/libc/list.h delete mode 100644 mentos/inc/libc/list_head.h delete mode 100644 mentos/inc/libc/math.h delete mode 100644 mentos/inc/libc/ordered_array.h delete mode 100644 mentos/inc/libc/queue.h delete mode 100644 mentos/inc/libc/rbtree.h delete mode 100644 mentos/inc/libc/stdatomic.h delete mode 100644 mentos/inc/libc/stdbool.h delete mode 100644 mentos/inc/libc/stddef.h delete mode 100644 mentos/inc/libc/stdio.h delete mode 100644 mentos/inc/libc/stdlib.h delete mode 100644 mentos/inc/libc/strerror.h delete mode 100644 mentos/inc/libc/string.h delete mode 100644 mentos/inc/libc/tree.h create mode 100644 mentos/inc/link_access.h create mode 100644 mentos/inc/mem/slab.h create mode 100644 mentos/inc/mem/vmem_map.h delete mode 100644 mentos/inc/misc/bitops.h delete mode 100644 mentos/inc/misc/clock.h create mode 100644 mentos/inc/proc_access.h create mode 100644 mentos/inc/process/wait.h delete mode 100644 mentos/inc/sys/dirent.h create mode 100644 mentos/inc/sys/kernel_levels.h delete mode 100644 mentos/inc/sys/stat.h delete mode 100644 mentos/inc/sys/unistd.h create mode 100644 mentos/inc/system/signal.h delete mode 100644 mentos/inc/system/signal_defs.h delete mode 100644 mentos/inc/ui/command/commands.h delete mode 100644 mentos/inc/ui/init/init.h delete mode 100644 mentos/inc/ui/shell/shell.h delete mode 100644 mentos/inc/ui/shell/shell_login.h create mode 100644 mentos/libmentos_memory.a create mode 100644 mentos/src/boot.c rename mentos/src/descriptor_tables/{exception.asm => exception.S} (86%) create mode 100644 mentos/src/descriptor_tables/gdt.S delete mode 100644 mentos/src/descriptor_tables/gdt.asm create mode 100644 mentos/src/descriptor_tables/idt.S delete mode 100644 mentos/src/descriptor_tables/idt.asm rename mentos/src/descriptor_tables/{interrupt.asm => interrupt.S} (87%) create mode 100644 mentos/src/descriptor_tables/tss.S delete mode 100644 mentos/src/descriptor_tables/tss.asm create mode 100644 mentos/src/drivers/rtc.c delete mode 100644 mentos/src/fs/fcntl.c create mode 100644 mentos/src/fs/ioctl.c create mode 100644 mentos/src/fs/namei.c create mode 100644 mentos/src/fs/procfs.c create mode 100644 mentos/src/io/proc_running.c create mode 100644 mentos/src/io/proc_system.c create mode 100644 mentos/src/io/proc_video.c create mode 100644 mentos/src/io/stdio.c create mode 100644 mentos/src/io/vga/vga.c create mode 100644 mentos/src/ipc/msg.c create mode 100644 mentos/src/ipc/sem.c create mode 100644 mentos/src/ipc/shm.c create mode 100644 mentos/src/klib/assert.c create mode 100644 mentos/src/klib/ctype.c create mode 100644 mentos/src/klib/fcvt.c create mode 100644 mentos/src/klib/hashmap.c create mode 100644 mentos/src/klib/libgen.c create mode 100644 mentos/src/klib/list.c create mode 100644 mentos/src/klib/math.c create mode 100644 mentos/src/klib/mutex.c create mode 100644 mentos/src/klib/ndtree.c create mode 100644 mentos/src/klib/rbtree.c create mode 100644 mentos/src/klib/spinlock.c create mode 100644 mentos/src/klib/strerror.c create mode 100644 mentos/src/klib/string.c create mode 100644 mentos/src/klib/time.c create mode 100644 mentos/src/klib/vscanf.c create mode 100644 mentos/src/klib/vsprintf.c delete mode 100644 mentos/src/libc/assert.c delete mode 100644 mentos/src/libc/bitset.c delete mode 100644 mentos/src/libc/ctype.c delete mode 100644 mentos/src/libc/fcvt.c delete mode 100644 mentos/src/libc/hashmap.c delete mode 100644 mentos/src/libc/libgen.c delete mode 100644 mentos/src/libc/list.c delete mode 100644 mentos/src/libc/math.c delete mode 100644 mentos/src/libc/mutex.c delete mode 100644 mentos/src/libc/ordered_array.c delete mode 100644 mentos/src/libc/queue.c delete mode 100644 mentos/src/libc/rbtree.c delete mode 100644 mentos/src/libc/spinlock.c delete mode 100644 mentos/src/libc/stdio.c delete mode 100644 mentos/src/libc/stdlib.c delete mode 100644 mentos/src/libc/strerror.c delete mode 100644 mentos/src/libc/string.c delete mode 100644 mentos/src/libc/tree.c delete mode 100644 mentos/src/libc/unistd/chdir.c delete mode 100644 mentos/src/libc/unistd/close.c delete mode 100644 mentos/src/libc/unistd/execve.c delete mode 100644 mentos/src/libc/unistd/exit.c delete mode 100644 mentos/src/libc/unistd/getcwd.c delete mode 100644 mentos/src/libc/unistd/getpid.c delete mode 100644 mentos/src/libc/unistd/getppid.c delete mode 100644 mentos/src/libc/unistd/mkdir.c delete mode 100644 mentos/src/libc/unistd/nice.c delete mode 100644 mentos/src/libc/unistd/open.c delete mode 100644 mentos/src/libc/unistd/read.c delete mode 100644 mentos/src/libc/unistd/reboot.c delete mode 100644 mentos/src/libc/unistd/stat.c delete mode 100644 mentos/src/libc/unistd/vfork.c delete mode 100644 mentos/src/libc/unistd/waitpid.c delete mode 100644 mentos/src/libc/unistd/write.c delete mode 100644 mentos/src/libc/vsprintf.c create mode 100644 mentos/src/mem/slab.c create mode 100644 mentos/src/mem/vmem_map.c delete mode 100644 mentos/src/misc/bitops.c delete mode 100644 mentos/src/misc/clock.c rename mentos/src/process/{user.asm => user.S} (69%) create mode 100644 mentos/src/process/wait.c delete mode 100644 mentos/src/sys/dirent.c delete mode 100644 mentos/src/sys/shm.c delete mode 100644 mentos/src/sys/unistd.c create mode 100644 mentos/src/system/signal.c delete mode 100644 mentos/src/ui/command/cmd_cd.c delete mode 100644 mentos/src/ui/command/cmd_clear.c delete mode 100644 mentos/src/ui/command/cmd_cpuid.c delete mode 100644 mentos/src/ui/command/cmd_credits.c delete mode 100644 mentos/src/ui/command/cmd_date.c delete mode 100644 mentos/src/ui/command/cmd_drv_load.c delete mode 100644 mentos/src/ui/command/cmd_echo.c delete mode 100644 mentos/src/ui/command/cmd_ipcrm.c delete mode 100644 mentos/src/ui/command/cmd_ipcs.c delete mode 100644 mentos/src/ui/command/cmd_logo.c delete mode 100644 mentos/src/ui/command/cmd_ls.c delete mode 100644 mentos/src/ui/command/cmd_mkdir.c delete mode 100644 mentos/src/ui/command/cmd_more.c delete mode 100644 mentos/src/ui/command/cmd_newfile.c delete mode 100644 mentos/src/ui/command/cmd_nice.c delete mode 100644 mentos/src/ui/command/cmd_poweroff.c delete mode 100644 mentos/src/ui/command/cmd_ps.c delete mode 100644 mentos/src/ui/command/cmd_pwd.c delete mode 100644 mentos/src/ui/command/cmd_rm.c delete mode 100644 mentos/src/ui/command/cmd_rmdir.c delete mode 100644 mentos/src/ui/command/cmd_showpid.c delete mode 100644 mentos/src/ui/command/cmd_sleep.c delete mode 100644 mentos/src/ui/command/cmd_tester.c delete mode 100644 mentos/src/ui/command/cmd_touch.c delete mode 100644 mentos/src/ui/command/cmd_uname.c delete mode 100644 mentos/src/ui/command/cmd_whoami.c delete mode 100644 mentos/src/ui/init/init.c delete mode 100644 mentos/src/ui/shell/shell.c delete mode 100644 mentos/src/ui/shell/shell_login.c create mode 100644 programs/CMakeLists.txt create mode 100644 programs/cat.c create mode 100644 programs/clear.c create mode 100644 programs/cpuid.c create mode 100644 programs/date.c create mode 100644 programs/echo.c create mode 100644 programs/env.c create mode 100644 programs/init.c create mode 100644 programs/ipcrm.c create mode 100644 programs/ipcs.c create mode 100644 programs/kill.c create mode 100644 programs/login.c create mode 100644 programs/logo.c create mode 100644 programs/ls.c create mode 100644 programs/mkdir.c create mode 100644 programs/nice.c create mode 100644 programs/poweroff.c create mode 100644 programs/ps.c create mode 100644 programs/pwd.c create mode 100644 programs/rm.c create mode 100644 programs/rmdir.c create mode 100644 programs/shell.c create mode 100644 programs/showpid.c create mode 100644 programs/sleep.c create mode 100644 programs/tests/CMakeLists.txt create mode 100644 programs/tests/t_abort.c create mode 100644 programs/tests/t_alarm.c create mode 100644 programs/tests/t_exec.c create mode 100644 programs/tests/t_exec_callee.c create mode 100644 programs/tests/t_fork.c create mode 100644 programs/tests/t_getenv.c create mode 100644 programs/tests/t_gid.c create mode 100644 programs/tests/t_groups.c create mode 100644 programs/tests/t_itimer.c create mode 100644 programs/tests/t_kill.c create mode 100644 programs/tests/t_mem.c create mode 100644 programs/tests/t_periodic1.c create mode 100644 programs/tests/t_periodic2.c create mode 100644 programs/tests/t_periodic3.c create mode 100644 programs/tests/t_setenv.c create mode 100644 programs/tests/t_sigaction.c create mode 100644 programs/tests/t_sigfpe.c create mode 100644 programs/tests/t_siginfo.c create mode 100644 programs/tests/t_sigmask.c create mode 100644 programs/tests/t_sigusr.c create mode 100644 programs/tests/t_sleep.c create mode 100644 programs/tests/t_stopcont.c create mode 100644 programs/touch.c create mode 100644 programs/uname.c create mode 100644 scripts/get_text_address.sh create mode 100644 scripts/replace_links.sh diff --git a/.clang-format b/.clang-format index db7b4db..fcc0cd7 100644 --- a/.clang-format +++ b/.clang-format @@ -11,23 +11,24 @@ --- AccessModifierOffset: -4 AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false +AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 -AlignOperands: true -AlignTrailingComments: false +AlignOperands: AlignAfterOperator +AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: true +AllowShortBlocksOnASingleLine: Always AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: false +AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true -BinPackParameters: true +BinPackParameters: false BraceWrapping: AfterClass: false AfterControlStatement: false @@ -52,7 +53,7 @@ BreakConstructorInitializersBeforeComma: false #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false -ColumnLimit: 80 +ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' #CompactNamespaces: false # Unknown to clang-format-4.0 ConstructorInitializerAllOnOneLineOrOnePerLine: false @@ -463,7 +464,7 @@ SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp03 +Standard: Auto TabWidth: 4 -UseTab: Always +UseTab: Never ... diff --git a/CMakeLists.txt b/CMakeLists.txt index ced3f65..129a4c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,77 +1,158 @@ -cmake_minimum_required(VERSION 2.8.4) +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(MentOs) -# ------------------------------------------------------------------------------ +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Add operating system specific option. +message(STATUS "Crosscompiling : ${CMAKE_CROSSCOMPILING}") +message(STATUS "System name : ${CMAKE_HOST_SYSTEM_NAME}") +message(STATUS "Kernel version : ${CMAKE_SYSTEM_VERSION}") +if((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") OR APPLE) + # Set the Apple MacOSx compilers. + if(CMAKE_VERSION VERSION_LESS "3.6.0") + include(CMakeForceCompiler) + cmake_force_c_compiler(i386-elf-gcc Clang) + cmake_force_cxx_compiler(i386-elf-g++ Clang) + else() + set(CMAKE_C_COMPILER i386-elf-gcc) + set(CMAKE_CXX_COMPILER i386-elf-g++) + set(CMAKE_AR i386-elf-ar) + endif() + # Speicfy the linker. + set(CMAKE_LINKER i386-elf-ld) + # Specify the linker flags. + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib") + +elseif((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") OR WIN32) + # Windows set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + +else() + # Generic Unix System + find_program(LSB_RELEASE_EXEC lsb_release) + execute_process( + COMMAND "${LSB_RELEASE_EXEC}" --short --release + OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "LSB version : ${LSB_RELEASE_VERSION_SHORT}") + if(${LSB_RELEASE_VERSION_SHORT} MATCHES "^18") + # Ubuntu 18 set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + elseif(${LSB_RELEASE_VERSION_SHORT} MATCHES "^19") + # Ubuntu 19 + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) + elseif(${LSB_RELEASE_VERSION_SHORT} MATCHES "^20") + # Ubuntu 20 + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) + else() + # set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + endif() +endif() + +# ============================================================================= # Add the debugging option. -set(DEBUGGING_TYPE "DEBUG_STDIO" CACHE STRING - "Chose the type of debugging: DEBUG_STDIO DEBUG_LOG") -set_property( - CACHE DEBUGGING_TYPE PROPERTY STRINGS - DEBUG_STDIO - DEBUG_LOG) -if ("${DEBUGGING_TYPE}" STREQUAL "DEBUG_STDIO" OR - "${DEBUGGING_TYPE}" STREQUAL "DEBUG_LOG") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${DEBUGGING_TYPE}") - message(STATUS "Setting debugging type to ${DEBUGGING_TYPE}.") -else () - message(FATAL_ERROR "Debugging type ${DEBUGGING_TYPE} is not valid.") -endif () +set(DEBUGGING_TYPE + "DEBUG_STDIO" + CACHE STRING "Chose the type of debugging: DEBUG_STDIO DEBUG_LOG") +set_property(CACHE DEBUGGING_TYPE PROPERTY STRINGS DEBUG_STDIO DEBUG_LOG) +if("${DEBUGGING_TYPE}" STREQUAL "DEBUG_STDIO" OR "${DEBUGGING_TYPE}" STREQUAL "DEBUG_LOG") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${DEBUGGING_TYPE}") + message(STATUS "Setting debugging type to ${DEBUGGING_TYPE}.") +else() + message(FATAL_ERROR "Debugging type ${DEBUGGING_TYPE} is not valid.") +endif() -# ------------------------------------------------------------------------------ +# ============================================================================= # Add the sub-directories. +add_subdirectory(programs) +add_subdirectory(programs/tests) add_subdirectory(mentos) add_subdirectory(initscp) +add_subdirectory(libc) +add_subdirectory(doc) -# ------------------------------------------------------------------------------ -# Generate the initrd filesystem. +# ============================================================================= +# Target to generate the initrd filesystem. add_custom_target( - initfs - COMMAND echo "---------------------------------------------" - COMMAND echo "Initializing 'initfs'..." - COMMAND echo "---------------------------------------------" - COMMAND initfscp -s ${CMAKE_SOURCE_DIR}/files -t ${CMAKE_BINARY_DIR}/initfs -m /dev - COMMAND echo "---------------------------------------------" - COMMAND echo "Done!" - COMMAND echo "---------------------------------------------" - DEPENDS initfscp -) + initrd + COMMAND echo "---------------------------------------------" + COMMAND echo "Initializing 'initrd'..." + COMMAND echo "---------------------------------------------" + COMMAND initfscp -s ${CMAKE_SOURCE_DIR}/files -t ${CMAKE_BINARY_DIR}/initrd -m /dev + COMMAND echo "---------------------------------------------" + COMMAND echo "Done!" + COMMAND echo "---------------------------------------------" + DEPENDS initfscp + DEPENDS all_programs all_tests) -# ------------------------------------------------------------------------------ +# ============================================================================= +# Update GDB symbol file. +add_custom_target( + gdb_file + COMMAND echo "symbol-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "exec-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel.bin > ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/programs/tests/test_* >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/programs/prog_* >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "break boot.c: boot_main" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "break kernel.c: kmain" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "target remote localhost:1234" >> ${CMAKE_BINARY_DIR}/.gdbinit + DEPENDS ${PROJECT_NAME}_bootloader + DEPENDS all_programs all_tests + DEPENDS libc) + +# ============================================================================= +# Update GDB symbol file. +add_custom_target( + gdb_file_clion_embedded + COMMAND echo "symbol-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "exec-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel.bin > ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/programs/prog_* >> ${CMAKE_BINARY_DIR}/.gdbinit + DEPENDS ${PROJECT_NAME}_bootloader + DEPENDS all_programs all_tests + DEPENDS libc) + +# ============================================================================= # Set memory size. -SET(MEMORY_SIZE 1096M) +set(MEMORY_SIZE 1096M) -# ------------------------------------------------------------------------------ +# ============================================================================= # Builds the code and runs qemu with the built Os. -SET(EMULATOR qemu-system-i386) -if (${DEBUGGING_TYPE} STREQUAL DEBUG_LOG) - SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial file:serial.log) -elseif (${DEBUGGING_TYPE} STREQUAL DEBUG_STDIO) - SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial stdio) -endif () -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -k it) -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m ${MEMORY_SIZE}) -SET(EMULATOR_KERNEL -kernel mentos/kernel.bin) -SET(EMULATOR_FS -initrd initfs) +set(EMULATOR qemu-system-i386) +if(${DEBUGGING_TYPE} STREQUAL DEBUG_LOG) + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial file:serial.log) +elseif(${DEBUGGING_TYPE} STREQUAL DEBUG_STDIO) + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial stdio) +endif() +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m ${MEMORY_SIZE}) +set(EMULATOR_KERNEL -kernel mentos/kernel-bootloader.bin) +set(EMULATOR_FS -initrd initrd) add_custom_target( - qemu - COMMAND ${EMULATOR} ${EMULATOR_FLAGS} ${EMULATOR_KERNEL} ${EMULATOR_FS} - DEPENDS MentOs - DEPENDS initfs -) + qemu + COMMAND ${EMULATOR} ${EMULATOR_FLAGS} ${EMULATOR_KERNEL} ${EMULATOR_FS} + DEPENDS kernel-bootloader.bin initrd all_programs all_tests libc) -# ------------------------------------------------------------------------------ +# ============================================================================= # Builds the code and runs qemu with the built Os. -SET(EMULATOR_FLAGS_GDB ${EMULATOR_FLAGS} -s -S) -SET(GDB_PROG cgdb) -SET(GDB_ARGS -x ${CMAKE_SOURCE_DIR}/.gdb.debug) +set(EMULATOR_FLAGS_GDB ${EMULATOR_FLAGS} -s -S) add_custom_target( - qemu-gdb - COMMAND xterm -geometry 160x30-0-0 -e ${EMULATOR} ${EMULATOR_FLAGS_GDB} ${EMULATOR_KERNEL} ${EMULATOR_FS} & - COMMAND xterm -geometry 160x30-0+0 -e ${GDB_PROG} ${GDB_ARGS} & - DEPENDS MentOs - DEPENDS initfs -) + qemu-gdb + COMMAND ${EMULATOR} ${EMULATOR_FLAGS_GDB} ${EMULATOR_KERNEL} ${EMULATOR_FS} & + DEPENDS gdb_file kernel-bootloader.bin initrd) +add_custom_target(all-execs-clion-embedded DEPENDS gdb_file_clion_embedded kernel-bootloader.bin initrd) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index fe053ac..d8b7271 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -1,7 +1,4 @@ -Code Styles for this project -============================ - - +# Coding Style If you'd like to contribute you are welcome, thus refer to the following coding styles please: diff --git a/README.md b/README.md index 90432c0..3487958 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,89 @@ -MentOS -====== +# MentOS [![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/made-with-c.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/for-you.svg)](https://forthebadge.com) -What is MentOS ------------------ +## 1. What is MentOS -MentOS (Mentoring Operating system) is an open source educational operating -system. -The goal of MentOS is to provide a project environment that is realistic -enough to show how a real Operating System work, yet simple enough that -students can understand and modify it in significant ways. +MentOS (Mentoring Operating System) is an open source educational operating +system. The goal of MentOS is to provide a project environment that is realistic +enough to show how a real Operating System work, yet simple enough that students +can understand and modify it in significant ways. -There are so many operating systems, why did we write MentOS? -It is true, there are a lot of education operating system, BUT -how many of them follow the guideline de fined by Linux? +There are so many operating systems, why did we write MentOS? It is true, there +are a lot of education operating system, BUT how many of them follow the +guideline de fined by Linux? + +MentOS aims to have the same Linux's data structures and algorithms. It has a +well-documented source code, and you can compile it on your laptop in a few +seconds! -MentOS aims to have the same Linux's data structures and algorithms. It -has a well-documented source code, and you can compile it on your laptop -in a few seconds! If you are a beginner in Operating-System developing, perhaps MentOS is the right operating system to start with. +## 2. Prerequisites -Developers ----------------- -Main Developers: +MentOS is compatible with the main **unix-based** operating systems. It has been +tested with *Ubuntu*, *WSL1*, *WSL2*, and *MacOS*. - * [Enrico Fraccaroli](https://github.com/Galfurian) - * [Alessandro Danese](https://github.com/alessandroDanese88) - * [Luigi Capogrosso](https://github.com/luigicapogrosso) - * [Mirco De Marchi](https://github.com/mircodemarchi) +### 2.1. Generic Prerequisites -Prerequisites ------------------ -For compiling the main system: +#### 3.1.1. Compile - * nasm - * gcc - * g++ - * make - * cmake - * git +For compiling the system: -To run and try: + - nasm + - gcc + - make + - cmake + - git + - ccmake (suggested) - * qemu-system-x86 +Under **MacOS**, for compiling, you have additional dependencies: -For debugging: + - i386-elf-binutils + - i386-elf-gcc - * ccmake - * gdb or cgdb - * xterm +#### 3.1.2. Execute + +To execute the operating system, you need to install: + + - qemu-system-i386 (or qemu-system-x86) + +#### 3.1.3. Debug + +For debugging we suggest using: + + - cgdb + - xterm + +### 2.2. installation Prerequisites + +Under **Ubuntu**, you can type the following commands: + +```bash +sudo apt-get update && sudo apt-get upgrade -y +sudo apt-get install -y build-essential git cmake qemu-system-i386 +sudo apt-get install -y cgdb xterm +``` + +Under **MacOS** you also need to install the i386-elf cross-compiler. The +simplest installation method is through Homebrew package manager. +Install [Homebrew](https://brew.sh/index_it) if you don't already have it, and +then type the following commands: + +```bash +brew update && brew upgrade +brew install i386-elf-binutils i386-elf-gcc git cmake qemu nasm +brew install cgdb xterm #<- for debug only +``` + +## 3. Compiling MentOS -Compiling MentOS ------------------ Compile and boot MentOS with qemu: -``` +```bash cd mkdir build cd build @@ -68,19 +92,36 @@ make make qemu ``` -If you want to access to the shell, use one of the usernames listed in files/passwd. +To login, use one of the usernames listed in `files/etc/passwd`. -Change the scheduling algorithm ------------------ +## 4. Change the scheduling algorithm MentOS provides three different scheduling algorithms: -* Round-Robin -* Priority -* Completely Fair Scheduling + - Round-Robin + - Priority + - Completely Fair Scheduling If you want to change the scheduling algorithm: + +```bash + +cd build + +# Round Robin scheduling algorithm +cmake -DSCHEDULER_TYPE=SCHEDULER_RR .. +# Priority scheduling algorithm +cmake -DSCHEDULER_TYPE=SCHEDULER_PRIORITY .. +# Completely Fair Scheduling algorithm +cmake -DSCHEDULER_TYPE=SCHEDULER_CFS .. + +make +make qemu ``` + +Otherwise you can use `ccmake`: + +```bash cd build cmake .. ccmake .. @@ -98,62 +139,20 @@ SCHEDULER_TYPE SCHEDULER_RR ``` Select SCHEDULER_TYPE, and type Enter to scroll the three available algorithms -(SCHEDULER_RR, SCHEDULER_PRIORITY, SCHEDULER_CFS). -Afterwards, -``` -type c -type g +(SCHEDULER_RR, SCHEDULER_PRIORITY, SCHEDULER_CFS). Afterwards, + +```bash + + make make qemu ``` -Enable to Buddy System ------------------ +## 5. Use Debugger -MentOS provides a Buddy System to manage the allocation and deallocation of -page frames in the physical memory. - -If you want to enable the MentOS's Buddy System: -``` -cd build -cmake .. -ccmake .. -``` - -Now you should see something like this: - -``` -BUILD_DOCUMENTATION ON -CMAKE_BUILD_TYPE -CMAKE_INSTALL_PREFIX /usr/local -DEBUGGING_TYPE DEBUG_STDIO -ENABLE_BUDDY_SYSTEM OFF -SCHEDULER_TYPE SCHEDULER_RR -``` - -Select ENABLE_BUDDY_SYSTEM, and type Enter. -You should see something like this: -``` -BUILD_DOCUMENTATION ON -CMAKE_BUILD_TYPE -CMAKE_INSTALL_PREFIX /usr/local -DEBUGGING_TYPE DEBUG_STDIO -ENABLE_BUDDY_SYSTEM ON -SCHEDULER_TYPE SCHEDULER_RR -``` - -Afterwards, -``` -type c -type g -make -make qemu -``` - -Use Debugger ------------------ If you want to use GDB to debug MentOS: -``` + +```bash cd build cmake .. make @@ -161,8 +160,29 @@ make qemu-gdb ``` If you did everything correctly, you should have 3 windows with: -``` -1) - Kernel Booting on qemu -2) - Shell with video printing of statistics previously discussed -3) - Debugger cgdb with code screening -``` + +1. Kernel Booting on qemu +2. Shell with video printing of statistics previously discussed +3. Debugger cgdb with code screening + +## Developers + +Main Developers: + +* [Enrico Fraccaroli](https://github.com/Galfurian) + - Mr. Wolf +* [Alessandro Danese](https://github.com/alessandroDanese88), [Luigi Capogrosso](https://github.com/luigicapogrosso), [Mirco De Marchi](https://github.com/mircodemarchi) + - Protection ring + - libc +* Andrea Cracco + - Buddy System, Heap, Paging, Slab, Caching, Zone + - Process Image, ELF + - VFS: procfs + - Bootloader +* Linda Sacchetto, Marco Berti + - Real time scheduler +* Daniele Nicoletti, Filippo Ziche + - Real time scheduler (Asynchronous EDF) + - Soft IRQs + - Timer + - Signals diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..da8887d --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +# TODOs diff --git a/doc/CMakeCache.txt b/doc/CMakeCache.txt deleted file mode 100644 index 59c17fd..0000000 --- a/doc/CMakeCache.txt +++ /dev/null @@ -1,38 +0,0 @@ -# This is the CMakeCache file. -# For build in directory: /home/enrico/Repositories/University/DreamOs/doc -# It was generated by CMake: /usr/bin/cmake -# You can edit this file to change values found and used by cmake. -# If you do not want to change any of the values, simply exit the editor. -# If you do want to change a value, simply edit, save, and exit the editor. -# The syntax for the file is as follows: -# KEY:TYPE=VALUE -# KEY is the name of a variable in the cache. -# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. -# VALUE is the current value for the KEY. - -######################## -# EXTERNAL cache entries -######################## - - -######################## -# INTERNAL cache entries -######################## - -//This is the directory where this CMakeCache.txt was created -CMAKE_CACHEFILE_DIR:INTERNAL=/home/enrico/Repositories/University/DreamOs/doc -//Major version of cmake used to create the current loaded cache -CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 -//Minor version of cmake used to create the current loaded cache -CMAKE_CACHE_MINOR_VERSION:INTERNAL=5 -//Patch version of cmake used to create the current loaded cache -CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 -//Path to CMake executable. -CMAKE_COMMAND:INTERNAL=/usr/bin/cmake -//Path to cpack program executable. -CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack -//Path to ctest program executable. -CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest -//Path to CMake installation. -CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.5 - diff --git a/doc/CMakeFiles/cmake.check_cache b/doc/CMakeFiles/cmake.check_cache deleted file mode 100644 index 3dccd73..0000000 --- a/doc/CMakeFiles/cmake.check_cache +++ /dev/null @@ -1 +0,0 @@ -# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..c5d3905 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,37 @@ +# Find Doxygen +find_package(Doxygen) + +if (DOXYGEN_FOUND) + set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doc_doxygen) + set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/html/index.html) + + # Replace variables inside @@ with the current values. + set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxygen) + set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxygen) + configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) + + # Copy the files needd by the documentation. + set(DOXYFILE_CSS ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.css) + set(DOXYFILE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/header.html) + set(DOXYFILE_FOOTER ${CMAKE_CURRENT_SOURCE_DIR}/footer.html) + + # Doxygen won't create this for us. + file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) + + add_custom_command( + OUTPUT ${DOXYGEN_INDEX_FILE} + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} + + MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} + COMMENT "Generating docs" + ) + add_custom_target( + Doxygen + DEPENDS + ${DOXYGEN_INDEX_FILE} + ${DOXYFILE_IN} + ${DOXYFILE_CSS} + ${DOXYFILE_HEADER} + ${DOXYFILE_FOOTER} + ) +endif (DOXYGEN_FOUND) diff --git a/doc/dreamos.doxyfile b/doc/Doxygen similarity index 91% rename from doc/dreamos.doxyfile rename to doc/Doxygen index dded7d7..5cd223a 100644 --- a/doc/dreamos.doxyfile +++ b/doc/Doxygen @@ -1,4 +1,4 @@ -# Doxyfile 1.8.11 +# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "MentOS" +PROJECT_NAME = MentOS # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = 0.3.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = "The Mentoring Operating System" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -152,7 +160,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -179,6 +187,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -226,7 +244,12 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = @@ -242,7 +265,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -264,17 +287,26 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # @@ -285,7 +317,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,6 +325,15 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -318,7 +359,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -424,6 +465,12 @@ EXTRACT_ALL = NO EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -478,8 +525,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -502,7 +549,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -689,7 +736,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -734,10 +781,11 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. @@ -759,7 +807,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = mentos.log +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -771,12 +819,18 @@ WARN_LOGFILE = mentos.log # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = . ../README.md ../src/mentos +INPUT = @CMAKE_SOURCE_DIR@/README.md \ + @CMAKE_SOURCE_DIR@/CODING_STYLE.md \ + @CMAKE_SOURCE_DIR@/LICENSE.md \ + @CMAKE_SOURCE_DIR@/TODO.md \ + @CMAKE_SOURCE_DIR@/doc/signal.md \ + @CMAKE_SOURCE_DIR@/doc/syscall.md \ + @CMAKE_SOURCE_DIR@/mentos # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -793,10 +847,12 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, -# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. -FILE_PATTERNS = +FILE_PATTERNS = *.c *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -864,7 +920,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/resources" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -920,7 +976,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = ../README.md +USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -933,7 +989,7 @@ USE_MDFILE_AS_MAINPAGE = ../README.md # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -949,7 +1005,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -981,12 +1037,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -1014,7 +1070,7 @@ VERBATIM_HEADERS = YES # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse-libclang=ON option for CMake. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO @@ -1027,6 +1083,16 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1095,7 +1161,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = "@CMAKE_CURRENT_SOURCE_DIR@/header.html" # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1105,7 +1171,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = "@CMAKE_CURRENT_SOURCE_DIR@/footer.html" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1117,7 +1183,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = "@CMAKE_CURRENT_SOURCE_DIR@/doxygen.css" # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1145,7 +1211,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1181,6 +1247,17 @@ HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1204,13 +1281,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1249,7 +1326,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1325,7 +1402,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1333,7 +1410,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1342,7 +1419,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1350,7 +1427,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1358,7 +1435,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1451,7 +1528,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1462,8 +1539,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1490,8 +1573,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1533,7 +1616,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1552,7 +1635,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1565,7 +1648,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1617,21 +1700,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1752,7 +1849,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1766,6 +1863,14 @@ LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1805,9 +1910,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1816,8 +1921,8 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = @@ -1903,6 +2008,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1935,9 +2047,9 @@ DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sf.net) file that captures the -# structure of the code including all documentation. Note that this feature is -# still experimental and incomplete at the moment. +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1997,7 +2109,7 @@ ENABLE_PREPROCESSING = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -2005,7 +2117,7 @@ MACRO_EXPANSION = NO # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_ONLY_PREDEF = NO +EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. @@ -2037,7 +2149,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = __attribute__(x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2104,12 +2216,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2123,15 +2229,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2162,7 +2259,7 @@ HAVE_DOT = YES # Minimum value: 0, maximum value: 32, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_NUM_THREADS = 0 +DOT_NUM_THREADS = 2 # When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make @@ -2361,6 +2458,11 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. diff --git a/doc/HOWTO_use_grub_script.md b/doc/HOWTO_use_grub_script.md deleted file mode 100644 index 1f41fb2..0000000 --- a/doc/HOWTO_use_grub_script.md +++ /dev/null @@ -1,17 +0,0 @@ -* * * -### Come usare lo script per generare un'entry di GRUB # -* * * - -#### 1. Inserire la partizione dei sorgenti -Questo significa dire al programma in quale partizione si trova la vostra directory dei sorgenti. -Ad esempio se siete su **/dev/hda2**, dovrete inserire **hda2** - -#### 2. Inserire il mountpoint -Tale partizione ha un mountpoint (se non lo conoscete, date da shell il comando mount senza argomenti oppure date uno sguardo in **/etc/fstab**). Ad esempio potrà essere **/** o anche **/home/vostroutente** se tenete la home directory in una partizione separata. - -#### 3. Attenzione al path -Il path dell'immagine di MentOS sarà calcolato automaticamente, ma per fare questo lo script dev'essere nella stessa directory dell'immagine. -Dopodiché vi basta confermare ed avere la vostra entry nel file **menu.lst** - -**ATTENZIONE: Usatelo solo se sapete cosa state facendo. Lo script e' stato testato e funziona, ma non rispondiamo di eventuali blocchi del vostro computer.** - diff --git a/doc/doxygen.css b/doc/doxygen.css new file mode 100644 index 0000000..9011869 --- /dev/null +++ b/doc/doxygen.css @@ -0,0 +1,1471 @@ +@charset "UTF-8"; +/* The standard CSS for doxygen 1.8.16 */ +/* The following are overrides over hard-coded styles in Doxygen */ +.sm-dox { + background: #4b5263; } + +.sm-dox a, .sm-dox a:focus, .sm-dox a:active, .sm-dox a:hover, .sm-dox a.highlighted { + color: #A9B7C6; + background: none; } + +.sm-dox a, .sm-dox a:focus, .sm-dox a:hover, .sm-dox a:active { + text-shadow: none; + outline: none; } + +.memberdecls tr:not(:first-child) { + background-color: #333842; } + +/* Stylesheet generated by Doxygen */ +body, table, div, p, dl { + font: 400 14px/22px Roboto, sans-serif; + display: block; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; } + +ul { + display: block; + list-style-type: disc; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 30px; } + +p.reference, p.definition { + font: 400 14px/22px Roboto, sans-serif; } + +/* @group Heading Levels */ +h1.groupheader { + font-size: 150%; } + +.title { + font: 400 14px/28px Roboto, sans-serif; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; } + +h2.groupheader { + border-radius: 4px; + background-color: #49515f; + font-size: 150%; + font-weight: normal; + padding: 0.5em; } + +h3.groupheader { + font-size: 100%; } + +h1, h2, h3, h4, h5, h6 { + margin-right: 15px; + display: block; + margin-block-start: 0.25em; + margin-block-end: 0.25em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + color: #d7dee4; } + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; } + +dt { + font-weight: bold; } + +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; } + +p.startli, p.startdd { + margin-top: 2px; } + +p.starttd { + margin-top: 0px; } + +p.endli { + margin-bottom: 0px; } + +p.enddd { + margin-bottom: 4px; } + +p.endtd { + margin-bottom: 2px; } + +/* @end */ +caption { + font-weight: bold; } + +span.legend { + font-size: 70%; + text-align: center; } + +h3.version { + font-size: 90%; + text-align: center; } + +div.qindex, div.navtab { + background-color: #333842; + text-align: center; } + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; } + +div.navtab { + margin-right: 15px; } + +/* @group Link Styling */ +a { + color: #728dc2; + font-weight: normal; + text-decoration: none; } + +.contents a:visited { + color: #607fbb; } + +a:hover { + color: #96aad2; + text-decoration: underline; } + +a.qindex { + font-weight: bold; } + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #FFFFFF; + border: 1px double #869DCA; } + +.contents a.qindexHL:visited { + color: #FFFFFF; } + +a.el { + font-weight: bold; } + +a.line, a.line:visited { + color: #A9B7C6; } + +a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { + color: #4665A2; } + +/* @end */ +dl.el { + margin-left: -1cm; } + +ul { + overflow: hidden; + /*Fixed: list item bullets overlap floating elements*/ } + +#side-nav ul { + overflow: visible; + /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ } + +#main-nav ul { + overflow: visible; + /* reset ul rule for the navigation bar drop down lists */ } + +#main-nav li { + overflow: visible; } + +.fragment { + text-align: left; + direction: ltr; + overflow-x: auto; + /*Fixed: fragment lines overlap floating elements*/ + overflow-y: hidden; } + +pre.fragment { + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + line-height: 125%; + font-family: monaco, Consolas, "Lucida Console", monospace; + font-size: 105%; + color: #A9B7C6; } + +div.fragment { + padding: 0.5em; + background-color: #333842; + border: 1px solid #333842; + color: #A9B7C6; } + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; + /* Moz */ + white-space: -pre-wrap; + /* Opera 4-6 */ + white-space: -o-pre-wrap; + /* Opera 7 */ + white-space: pre-wrap; + /* CSS3 */ + word-wrap: break-word; + /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + transition: 0.5s; } + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; } + +span.lineno { + padding-right: 4px; + text-align: right; + white-space: pre; } + +span.lineno a:hover { + background-color: #565e72; } + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +div.ah, span.ah { + background-color: #16181d; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border-radius: 0.5em; } + +div.classindex ul { + list-style: none; + padding-left: 0; } + +div.classindex span.ai { + display: inline-block; } + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; } + +div.groupText { + margin-left: 16px; + font-style: italic; } + +body { + background-color: #282c34; + color: #A9B7C6; + margin: 0; } + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; } + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; } + +tr.memlist { + background-color: #EEF1F7; } + +p.formulaDsp { + text-align: center; } + +img.formulaInl, img.inline { + vertical-align: middle; } + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; } + +div.center img { + border: 0px; } + +address.footer { + font-style: normal; + padding-right: 12px; } + +hr.footer { + flex: 1 1 auto; } + +img.footer { + height: 22px; + border: 0px; + vertical-align: middle; } + +div.footer ul { + list-style-type: none; + list-style-image: none; + margin: 0; + padding: 0; } + +/* @group Code Colorization */ +span.keyword { + color: #CC7832; } + +span.keywordtype { + color: #CC7832; } + +span.keywordflow { + color: #CC7832; } + +span.comment { + color: #808080; } + +span.preprocessor { + color: #BBB529; } + +span.stringliteral { + color: #6A8759; } + +span.charliteral { + color: #008080; } + +span.vhdldigit { + color: #ff00ff; } + +span.vhdlchar { + color: #000000; } + +span.vhdlkeyword { + color: #700070; } + +span.vhdllogic { + color: #ff0000; } + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; } + +blockquote.DocNodeRTL { + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; } + +/* @end */ +/* +.search { + color: #003399; + font-weight:bold; + +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; + +} + +input.search { + font-size: 75%; + color: #000080; + font-weight:normal; + background-color: #e8eef2; + +} +* / + +td.tiny { + font-size: 75%; + +} + +.dirtab { + padding: 4px; + border-collapse:collapse; + border: 1px solid #A3B4D7; + +} + +th.dirtab { + background: #EBEFF6; + font-weight:bold; + +} + +hr { + height: 0px; + border:none; + border-top: 1px solid #4A6AAA; + +} + +hr.footer { + display:none; + +} + +div.footer { + display:flex; + align-items:center; + justify-content:space-between; + padding: 1em; + background-color: darken($content-bg-color, 5%); + +} + +/* @group Member Descriptions */ +table.memberdecls { + border-spacing: 0px; + padding: 0px; } + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; } + +.memItemLeft, .memItemRight { + font-family: monaco, Consolas, "Lucida Console", monospace; } + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + border: none; + margin: 4px; + padding: 1px 0 0 8px; } + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; } + +.memSeparator { + background-color: #282c34; + line-height: 1px; } + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; } + +.memItemRight { + width: 100%; } + +.memTemplParams { + color: #4665A2; + white-space: nowrap; + font-size: 80%; } + +/* @end */ +/* @group Member Details */ +/* Styles for detailed member documentation */ +.memtitle { + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin: 0; + background-color: #333842; + padding: 0.5em; + font-weight: 300; + display: flex; + align-items: center; + float: left; + clear: both; + display: block; } + +.permalink { + font-size: 65%; + display: inline-block; + vertical-align: middle; } + +.permalink a:hover { + text-decoration: none; } + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; } + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; } + +.mempage { + width: 100%; } + +.memitem { + background-color: #333842; + clear: both; + margin-bottom: 1em; } + +.memitem.glow { + box-shadow: 0 0 15px cyan; } + +.memname { + font-family: monaco, Consolas, "Lucida Console", monospace; + font-weight: 400; + margin-left: 6px; } + +.memname td { + vertical-align: bottom; } + +.memproto, dl.reflist dt { + padding: 0.5em 0; } + +.overload { + font-family: "courier new", courier, monospace; + font-size: 65%; } + +.memdoc, dl.reflist dd { + padding: 1em; } + +dl.reflist dt { + padding: 5px; } + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; } + +.paramkey { + text-align: right; } + +.paramtype { + white-space: nowrap; } + +.paramname { + color: #e5c07b; + white-space: nowrap; } + +.paramname em { + font-style: normal; + font-weight: bold; } + +.paramname code { + line-height: 14px; } + +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; } + +.params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { + font-weight: bold; + vertical-align: top; } + +.params .paramtype, .tparams .paramtype { + font-style: italic; + vertical-align: top; } + +.params .paramdir, .tparams .paramdir { + font-family: "courier new", courier, monospace; + vertical-align: top; } + +table.mlabels { + border-spacing: 0px; } + +td.mlabels-left { + width: 100%; + padding: 0px; } + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; } + +span.mlabels { + margin-left: 8px; } + +span.mlabel { + background-color: #728DC1; + border-top: 1px solid #5373B4; + border-left: 1px solid #5373B4; + border-right: 1px solid #C4CFE5; + border-bottom: 1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; } + +/* @end */ +/* these are for tree view inside a (index) page */ +div.directory { + width: 100%; } + +.directory table { + border-collapse: collapse; } + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; } + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 3px; } + +.directory td.entry a { + outline: none; } + +.directory td.entry a img { + border: none; } + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; } + +.directory img { + vertical-align: -30%; } + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; + background-color: #333842; } + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #728dc2; } + +.directory .levels span:hover { + color: #96aad2; } + +.arrow { + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + vertical-align: top; } + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; } + +.icona { + width: 24px; + height: 22px; + display: inline-block; } + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url("folderopen.png"); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url("folderclosed.png"); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url("doc.png"); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } + +table.directory { + font: 400 14px Roboto, sans-serif; } + +/* @end */ +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +table.doxtable caption { + caption-side: top; } + +table.doxtable { + border-collapse: collapse; + margin-top: 4px; + margin-bottom: 4px; } + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; } + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; } + +table.fieldtable { + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; } + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; } + +.fieldtable td.fieldname { + padding-top: 3px; } + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ } + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; } + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; } + +.fieldtable tr:last-child td { + border-bottom: none; } + +.fieldtable th { + background-image: url("nav_f.png"); + background-repeat: repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align: left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; } + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url("tab_b.png"); + z-index: 101; + overflow: hidden; + font-size: 13px; } + +.navpath ul { + font-size: 11px; + background-color: #3e4451; + color: #8AA0CC; + overflow: hidden; + margin: 0px; + padding: 0px; + display: flex; + align-items: center; } + +.navpath li { + list-style-type: none; + padding-top: 0.25em; + padding-bottom: 0.25em; + padding-right: 0.25em; + display: flex; + padding: 0.25em; + color: #a9b7c6; } + +.navpath li:not(:first-child)::before { + content: "〉"; + display: block; + font-size: 1em; + font-weight: bold; + width: 1em; } + +.navpath li.navelem a { + display: block; + text-decoration: none; + outline: none; + color: #a9b7c6; + font-family: 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; + text-decoration: none; } + +.navpath li.navelem a:hover { + color: #d7dee4; } + +.navpath li.footer { + list-style-type: none; + float: right; + padding-left: 10px; + padding-right: 15px; + background-image: none; + background-repeat: no-repeat; + background-position: right; + color: #364D7C; + font-size: 8pt; } + +div.summary { + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; } + +div.summary a { + white-space: nowrap; } + +table.classindex { + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; } + +div.ingroups { + font-size: 8pt; + width: 50%; + text-align: left; } + +div.ingroups a { + white-space: nowrap; } + +div.header { + background-color: #282c34; + margin: 0px; + color: #A9B7C6; } + +div.headertitle { + padding: 5px 5px 5px 10px; + border-bottom: 1px solid #C4CFE5; } + +.PageDocRTL-title div.headertitle { + text-align: right; + direction: rtl; } + +dl { + padding: 0 0 0 0; } + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ +dl.section { + margin-left: 0px; + padding-left: 0px; } + +dl.section.DocNodeRTL { + margin-right: 0px; + padding-right: 0px; } + +dl.note { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; } + +dl.note.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; } + +dl.warning, dl.attention { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; } + +dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; } + +dl.pre, dl.post, dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; } + +dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; } + +dl.deprecated { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; } + +dl.deprecated.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; } + +dl.todo { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; } + +dl.todo.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; } + +dl.test { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; } + +dl.test.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; } + +dl.bug { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; } + +dl.bug.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; } + +dl.section dd { + margin-bottom: 6px; } + +#projectlogo { + text-align: center; + vertical-align: bottom; + border-collapse: separate; } + +#projectlogo img { + border: 0px none; + height: 8em; + padding: 1em; } + +#projectalign { + vertical-align: middle; } + +#projectname { + font: 300% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 2px 0px; } + +#projectbrief { + font: 120% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; } + +#projectnumber { + font: 50% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; } + +#titlearea { + padding: 0px; + margin: 0px; + width: 100%; + color: #A9B7C6; + background-color: #282c34; } + +.image { + text-align: center; + background-color: #5f697c; + margin-left: 5em; + margin-right: 5em; + padding: 0.5em; + border-radius: 4px; } + +.dotgraph { + text-align: center; } + +.mscgraph { + text-align: center; } + +.plantumlgraph { + text-align: center; } + +.diagraph { + text-align: center; } + +.caption { + font-weight: bold; } + +div.zoom { + border: 1px solid #90A5CE; } + +dl.citelist { + margin-bottom: 50px; } + +dl.citelist dt { + color: #334975; + float: left; + font-weight: bold; + margin-right: 10px; + padding: 5px; } + +dl.citelist dd { + margin: 2px 0; + padding: 5px 0; } + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; } + +.PageDocRTL-title div.toc { + float: left !important; + text-align: right; } + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana, DejaVu Sans, Geneva, sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; } + +.PageDocRTL-title div.toc li { + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; } + +div.toc h3 { + font: bold 12px/1.2 Arial, FreeSans, sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; } + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; } + +div.toc li.level1 { + margin-left: 0px; } + +div.toc li.level2 { + margin-left: 15px; } + +div.toc li.level3 { + margin-left: 30px; } + +div.toc li.level4 { + margin-left: 45px; } + +.PageDocRTL-title div.toc li.level1 { + margin-left: 0 !important; + margin-right: 0; } + +.PageDocRTL-title div.toc li.level2 { + margin-left: 0 !important; + margin-right: 15px; } + +.PageDocRTL-title div.toc li.level3 { + margin-left: 0 !important; + margin-right: 30px; } + +.PageDocRTL-title div.toc li.level4 { + margin-left: 0 !important; + margin-right: 45px; } + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +.inherit_header td { + padding: 6px 0px 2px 5px; } + +.inherit { + display: none; } + +/* tooltip related style info */ +.ttc { + position: absolute; + display: none; } + +#powerTip { + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; } + +#powerTip div.ttdoc { + color: grey; + font-style: italic; } + +#powerTip div.ttname a { + font-weight: bold; } + +#powerTip div.ttname { + font-weight: bold; } + +#powerTip div.ttdeci { + color: #006318; } + +#powerTip div { + margin: 0px; + padding: 0px; + font: 12px/16px Roboto, sans-serif; } + +#powerTip:before, #powerTip:after { + content: ""; + position: absolute; + margin: 0px; } + +#powerTip.n:after, #powerTip.n:before, +#powerTip.s:after, #powerTip.s:before, +#powerTip.w:after, #powerTip.w:before, +#powerTip.e:after, #powerTip.e:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.nw:after, #powerTip.nw:before, +#powerTip.sw:after, #powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; } + +#powerTip.n:after, #powerTip.s:after, +#powerTip.w:after, #powerTip.e:after, +#powerTip.nw:after, #powerTip.ne:after, +#powerTip.sw:after, #powerTip.se:after { + border-color: rgba(255, 255, 255, 0); } + +#powerTip.n:before, #powerTip.s:before, +#powerTip.w:before, #powerTip.e:before, +#powerTip.nw:before, #powerTip.ne:before, +#powerTip.sw:before, #powerTip.se:before { + border-color: rgba(128, 128, 128, 0); } + +#powerTip.n:after, #powerTip.n:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.nw:after, #powerTip.nw:before { + top: 100%; } + +#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { + border-top-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; } + +#powerTip.n:before { + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; } + +#powerTip.n:after, #powerTip.n:before { + left: 50%; } + +#powerTip.nw:after, #powerTip.nw:before { + right: 14px; } + +#powerTip.ne:after, #powerTip.ne:before { + left: 14px; } + +#powerTip.s:after, #powerTip.s:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.sw:after, #powerTip.sw:before { + bottom: 100%; } + +#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { + border-bottom-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; } + +#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; } + +#powerTip.s:after, #powerTip.s:before { + left: 50%; } + +#powerTip.sw:after, #powerTip.sw:before { + right: 14px; } + +#powerTip.se:after, #powerTip.se:before { + left: 14px; } + +#powerTip.e:after, #powerTip.e:before { + left: 100%; } + +#powerTip.e:after { + border-left-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; } + +#powerTip.e:before { + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; } + +#powerTip.w:after, #powerTip.w:before { + right: 100%; } + +#powerTip.w:after { + border-right-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; } + +#powerTip.w:before { + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; } + +@media print { + #top { + display: none; } + #side-nav { + display: none; } + #nav-path { + display: none; } + body { + overflow: visible; } + h1, h2, h3, h4, h5, h6 { + page-break-after: avoid; } + .summary { + display: none; } + .memitem { + page-break-inside: avoid; } + #doc-content { + margin-left: 0 !important; + height: auto !important; + width: auto !important; + overflow: inherit; + display: inline; } } + +/* @group Markdown */ +/* +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTableHead tr { + +} + +table.markdownTableBodyLeft td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft { + text-align: left + +} + +th.markdownTableHeadRight { + text-align: right + +} + +th.markdownTableHeadCenter { + text-align: center + +} +* / + +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTable tr { + +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left + +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right + +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center + +} + +.DocNodeRTL { + text-align:right; + direction:rtl; + +} + +.DocNodeLTR { + text-align:left; + direction:ltr; + +} + +table.DocNodeRTL { + width:auto; + margin-right: 0; + margin-left:auto; + +} + +table.DocNodeLTR { + width:auto; + margin-right:auto; + margin-left: 0; + +} + +tt, code, kbd, samp + +{ +display:inline-block; +direction:ltr; + +} +/* @end */ +u { + text-decoration: underline; } diff --git a/doc/doxygen.scss b/doc/doxygen.scss new file mode 100644 index 0000000..9ac2100 --- /dev/null +++ b/doc/doxygen.scss @@ -0,0 +1,2169 @@ +/* The standard CSS for doxygen 1.8.16 */ + +$black-color: #282c34; +$white-color: #A9B7C6; +$magenta-color: #c678dd; +$cyan-color: #56b6c2; +$yellow-color: #e5c07b; +$blue-color: #61afef; +$gutter-color: #4b5263; + +// $code-font: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; +$code-font: monaco, Consolas, "Lucida Console", monospace; + +$title-fg-color: $white-color; +$title-bg-color: $black-color; + +$menu-bg-color: $gutter-color; + +$breadcrumb-fg-color: lighten($white-color, 0%); +$breadcrumb-bg-color: lighten($black-color, 10%); +$breadcrumb-arrow-color: $white-color; + +$content-fg-color: $white-color; +$content-bg-color: $black-color; + +$headers-fg-color: lighten($white-color, 15%); + +$header-bg-color: lighten($black-color, 15%); + +$table-bg-color: lighten($black-color, 5%); + +$image-bg-color: lighten($black-color, 25%); + +$code-border-color: $gutter-color; +$code-fg-color: $white-color; +$code-bg-color: lighten($black-color, 5%); + +$link-color: lighten(#4665A2, 15%); +$link-visited-color: lighten(#4665A2, 10%); +$link-hover-color: lighten(#4665A2, 25%); + +$footer-fg-color: darken($white-color, 10%); +$footer-bg-color: darken($black-color, 10%); + +$ah-bg-color: darken($black-color, 8%); + +/* The following are overrides over hard-coded styles in Doxygen */ + +.sm-dox { + background: $menu-bg-color; + +} + +.sm-dox a, .sm-dox a:focus, .sm-dox a:active, .sm-dox a:hover, .sm-dox a.highlighted { + color: $white-color; + background: none; + +} + +.sm-dox a, .sm-dox a:focus, .sm-dox a:hover, .sm-dox a:active { + text-shadow: none; + outline: none; + +} + +.memberdecls tr:not(:first-child) { + background-color: $table-bg-color; +} + +/* Stylesheet generated by Doxygen */ + +body, table, div, p, dl { + font: 400 14px/22px Roboto, sans-serif; + display: block; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; +} + +ul { + display: block; + list-style-type: disc; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 30px; +} + +p.reference, p.definition { + font: 400 14px/22px Roboto, sans-serif; + +} + +/* @group Heading Levels */ + +h1.groupheader { + font-size: 150%; + +} + +.title { + font: 400 14px/28px Roboto, sans-serif; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; + +} + +h2.groupheader { + // border-bottom: 1px solid $tabel-fg-color; + // color: #354C7B; + border-radius: 4px; + background-color: $header-bg-color; + font-size: 150%; + font-weight: normal; + //margin-top: 1.75em; + padding: 0.5em; + // width: 100%; +} + +h3.groupheader { + font-size: 100%; + +} + +h1, h2, h3, h4, h5, h6 { + // -webkit-transition: text-shadow 0.5s linear; + // -moz-transition: text-shadow 0.5s linear; + // -ms-transition: text-shadow 0.5s linear; + // -o-transition: text-shadow 0.5s linear; + // transition: text-shadow 0.5s linear; + margin-right: 15px; + display: block; + margin-block-start: 0.25em; + margin-block-end: 0.25em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + color: $headers-fg-color; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; + +} + +dt { + font-weight: bold; + +} + +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; + +} + +p.startli, p.startdd { + margin-top: 2px; + +} + +p.starttd { + margin-top: 0px; + +} + +p.endli { + margin-bottom: 0px; + +} + +p.enddd { + margin-bottom: 4px; + +} + +p.endtd { + margin-bottom: 2px; + +} + +p.interli { + +} + +p.interdd { + +} + +p.intertd { + +} + +/* @end */ + +caption { + font-weight: bold; + +} + +span.legend { + font-size: 70%; + text-align: center; + +} + +h3.version { + font-size: 90%; + text-align: center; + +} + +div.qindex, div.navtab { + background-color: $table-bg-color; + // border: 1px solid #A3B4D7; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; + +} + +div.navtab { + margin-right: 15px; + +} + +/* @group Link Styling */ + +a { + color: $link-color; + font-weight: normal; + text-decoration: none; + +} + +.contents a:visited { + color: $link-visited-color; + +} + +a:hover { + color: $link-hover-color; + text-decoration: underline; + +} + +a.qindex { + font-weight: bold; + +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #FFFFFF; + border: 1px double #869DCA; + +} + +.contents a.qindexHL:visited { + color: #FFFFFF; + +} + +a.el { + font-weight: bold; + +} + +a.elRef { + +} + +a.line, a.line:visited { + color: $white-color; + +} + +// a.code { +// color: $link-color; +// } + +a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { + color: #4665A2; + +} + +/* @end */ + +dl.el { + margin-left: -1cm; + +} + +ul { + overflow: hidden; + /*Fixed: list item bullets overlap floating elements*/ +} + +#side-nav ul { + overflow: visible; + /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ +} + +#main-nav ul { + overflow: visible; + /* reset ul rule for the navigation bar drop down lists */ +} + +#main-nav li { + overflow: visible; +} + +.fragment { + text-align: left; + direction: ltr; + overflow-x: auto; + /*Fixed: fragment lines overlap floating elements*/ + overflow-y: hidden; + +} + +pre.fragment { + // border: 1px solid $code-border-color; + // background-color: $code-bg-color; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + line-height: 125%; + font-family: $code-font; + font-size: 105%; + color: $code-fg-color; +} + +div.fragment { + // padding: 0 0 1px 0; [>Fixed: last line underline overlap border<] + // margin: 4px 8px 4px 2px; + padding: 0.5em; + background-color: $code-bg-color; + border: 1px solid $code-bg-color; + color: $code-fg-color; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; + /* Moz */ + white-space: -pre-wrap; + /* Opera 4-6 */ + white-space: -o-pre-wrap; + /* Opera 7 */ + white-space: pre-wrap; + /* CSS3 */ + word-wrap: break-word; + /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + transition: 0.5s; + +} + +// div.line:hover { +// background-color: lighten($code-bg-color, 10%); +// } + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; + +} + +span.lineno { + padding-right: 4px; + text-align: right; + white-space: pre; + //background-color: $gutter-color; + //border-right: 2px solid darken($gutter-color, 10%); +} + +span.lineno a { + //background-color: lighten($gutter-color, 5%); + +} + +span.lineno a:hover { + background-color: lighten($gutter-color, 5%); +} + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + +div.ah, span.ah { + background-color: $ah-bg-color; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border-radius: 0.5em; + +} + +div.classindex ul { + list-style: none; + padding-left: 0; + +} + +div.classindex span.ai { + display: inline-block; + +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; + +} + +div.groupText { + margin-left: 16px; + font-style: italic; + +} + +body { + background-color: $content-bg-color; + color: $content-fg-color; + margin: 0; + +} + +div.contents { + //margin: 1em; + //margin-top: 2em; + //margin-left: 0.5em; + //margin-right: 0.5em; + //margin-bottom: 2em; + + // margin-top: 10px; + // margin-left: 12px; + // margin-right: 8px; + //margin-bottom: 1em; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; + +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; + +} + +tr.memlist { + background-color: #EEF1F7; + +} + +p.formulaDsp { + text-align: center; + +} + +img.formulaDsp { + +} + +img.formulaInl, img.inline { + vertical-align: middle; + +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; + +} + +div.center img { + border: 0px; + +} + +address.footer { + font-style: normal; + padding-right: 12px; + +} + +hr.footer { + flex: 1 1 auto; + +} + +img.footer { + height: 22px; + border: 0px; + vertical-align: middle; +} + +div.footer ul { + list-style-type: none; + list-style-image: none; + margin: 0; + padding: 0; +} + +/* @group Code Colorization */ + +span.keyword { + color: #CC7832 //#008000 + +} + +span.keywordtype { + color: #CC7832 //#aa6e32 + +} + +span.keywordflow { + color: #CC7832 + +} + +span.comment { + color: #808080 //#5c6370 + +} + +span.preprocessor { + color: #BBB529 + +} + +span.stringliteral { + color: #6A8759 + +} + +span.charliteral { + color: #008080 + +} + +span.vhdldigit { + color: #ff00ff + +} + +span.vhdlchar { + color: #000000 + +} + +span.vhdlkeyword { + color: #700070 + +} + +span.vhdllogic { + color: #ff0000 + +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; + +} + +blockquote.DocNodeRTL { + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; + +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight:bold; + +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; + +} + +input.search { + font-size: 75%; + color: #000080; + font-weight:normal; + background-color: #e8eef2; + +} +* / + +td.tiny { + font-size: 75%; + +} + +.dirtab { + padding: 4px; + border-collapse:collapse; + border: 1px solid #A3B4D7; + +} + +th.dirtab { + background: #EBEFF6; + font-weight:bold; + +} + +hr { + height: 0px; + border:none; + border-top: 1px solid #4A6AAA; + +} + +hr.footer { + display:none; + +} + +div.footer { + display:flex; + align-items:center; + justify-content:space-between; + padding: 1em; + background-color: darken($content-bg-color, 5%); + +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; + +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; + +} + +.memItemLeft, .memItemRight { + font-family: $code-font; + +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + border: none; + margin: 4px; + padding: 1px 0 0 8px; + +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + // color: $table-fg-color; +} + +.memSeparator { + // display: none; + background-color: $content-bg-color; + line-height: 1px; + // border-bottom: 1px solid transparent; + // line-height: 1px; + // margin: 0px; + // padding: 0px; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; + +} + +.memItemRight { + width: 100%; + +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; + font-size: 80%; + +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtitle { + // padding: 8px; + // border-top: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // margin-bottom: -1px; + // background-image: url('nav_f.png'); + // background-repeat: repeat-x; + // background-color: #E2E8F2; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin: 0; + background-color: $table-bg-color; + padding: 0.5em; + font-weight: 300; + display: flex; + align-items: center; + float: left; + clear: both; + display: block; +} + +.permalink { + font-size: 65%; + display: inline-block; + vertical-align: middle; +} + +.permalink a:hover { + text-decoration: none; + +} + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; + +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; + +} + +.mempage { + width: 100%; + +} + +.memitem { + background-color: $table-bg-color; + clear: both; + margin-bottom: 1em; + // padding: 0; + // margin-bottom: 10px; + // margin-right: 5px; + // -webkit-transition: box-shadow 0.5s linear; + // -moz-transition: box-shadow 0.5s linear; + // -ms-transition: box-shadow 0.5s linear; + // -o-transition: box-shadow 0.5s linear; + // transition: box-shadow 0.5s linear; + // display: table !important; + // width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; + +} + +.memname { + font-family: $code-font; + font-weight: 400; + margin-left: 6px; + +} + +.memname td { + vertical-align: bottom; + +} + +.memproto, dl.reflist dt { + padding: 0.5em 0; + // background-color: darken(#d19a66, 40%); + // border-top: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // padding: 6px 0px 6px 0px; + // color: #253555; + // font-weight: bold; + // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + // background-color: #DFE5F1; + // [> opera specific markup <] + // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // border-top-right-radius: 4px; + // [> firefox specific markup <] + // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + // -moz-border-radius-topright: 4px; + // [> webkit specific markup <] + // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // -webkit-border-top-right-radius: 4px; +} + +.overload { + font-family: "courier new", courier, monospace; + font-size: 65%; + +} + +.memdoc, dl.reflist dd { + padding: 1em; + // border-bottom: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // padding: 6px 10px 2px 10px; + // background-color: #FBFCFD; + // border-top-width: 0; + // background-image:url('nav_g.png'); + // background-repeat:repeat-x; + // background-color: #FFFFFF; + // [> opera specific markup <] + // border-bottom-left-radius: 4px; + // border-bottom-right-radius: 4px; + // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // [> firefox specific markup <] + // -moz-border-radius-bottomleft: 4px; + // -moz-border-radius-bottomright: 4px; + // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + // [> webkit specific markup <] + // -webkit-border-bottom-left-radius: 4px; + // -webkit-border-bottom-right-radius: 4px; + // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; + +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; + +} + +.paramkey { + text-align: right; + +} + +.paramtype { + white-space: nowrap; + +} + +.paramname { + color: $yellow-color; + white-space: nowrap; + +} + +.paramname em { + font-style: normal; + font-weight: bold; +} + +.paramname code { + line-height: 14px; + +} + +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; + +} + +.params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { + font-weight: bold; + vertical-align: top; + +} + +.params .paramtype, .tparams .paramtype { + font-style: italic; + vertical-align: top; + +} + +.params .paramdir, .tparams .paramdir { + font-family: "courier new", courier, monospace; + vertical-align: top; + +} + +table.mlabels { + border-spacing: 0px; + +} + +td.mlabels-left { + width: 100%; + padding: 0px; + +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; + +} + +span.mlabels { + margin-left: 8px; + +} + +span.mlabel { + background-color: #728DC1; + + border-top: 1px solid #5373B4; + + border-left: 1px solid #5373B4; + + border-right: 1px solid #C4CFE5; + + border-bottom: 1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; + +} + + +/* @end */ + +/* these are for tree view inside a (index) page */ + +div.directory { + //margin: 10px 0px; + // border-top: 1px solid #9CAFD4; + // border-bottom: 1px solid #9CAFD4; + width: 100%; +} + +.directory table { + + border-collapse: collapse; + +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; + +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 3px; + +} + +.directory td.entry a { + + outline: none; + +} + +.directory td.entry a img { + border: none; + +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + // border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory img { + vertical-align: -30%; + +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; + background-color: $table-bg-color; + +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: $link-color; + +} + +.directory .levels span:hover { + color: $link-hover-color; + +} + +.arrow { + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + //width: 16px; + //height: 22px; + vertical-align: top; + +} + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; + +} + +.icona { + width: 24px; + height: 22px; + display: inline-block; + +} + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + + background-image: url('folderopen.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + + vertical-align: top; + display: inline-block; + +} + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + + background-image: url('folderclosed.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + + vertical-align: top; + display: inline-block; + +} + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + + background-image: url('doc.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + + vertical-align: top; + display: inline-block; + +} + +table.directory { + font: 400 14px Roboto, sans-serif; + +} + +/* @end */ + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + + +table.doxtable caption { + caption-side: top; + +} + +table.doxtable { + + border-collapse: collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +table.fieldtable { + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; + +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; + +} + +.fieldtable td.fieldname { + padding-top: 3px; + +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ +} + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; + +} + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; + +} + +.fieldtable tr:last-child td { + border-bottom: none; + +} + +.fieldtable th { + + background-image: url('nav_f.png'); + + background-repeat: repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + + text-align: left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; + +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; + +} + +.navpath ul { + font-size: 11px; + background-color: $breadcrumb-bg-color; + color: #8AA0CC; + // border:solid 1px #C2CDE4; + overflow: hidden; + margin: 0px; + padding: 0px; + display: flex; + align-items: center; +} + +.navpath li { + list-style-type: none; + // float:left; + // padding-left:10px; + padding-top: 0.25em; + padding-bottom: 0.25em; + padding-right: 0.25em; + display: flex; + padding: 0.25em; + color: $breadcrumb-fg-color; +} + +.navpath li:not(:first-child)::before { + content: "〉"; + display: block; + font-size: 1em; + font-weight: bold; + width: 1em; +} + +.navpath li.navelem a { + display: block; + text-decoration: none; + outline: none; + color: $breadcrumb-fg-color; + font-family: 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; + // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; +} + +.navpath li.navelem a:hover { + color: lighten($breadcrumb-fg-color, 15%); +} + +.navpath li.footer { + list-style-type: none; + + float: right; + + padding-left: 10px; + + padding-right: 15px; + + background-image: none; + + background-repeat: no-repeat; + + background-position: right; + + color: #364D7C; + font-size: 8pt; +} + + +div.summary { + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a { + white-space: nowrap; +} + +table.classindex { + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; +} + +div.ingroups { + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a { + white-space: nowrap; +} + +div.header { + background-color: $content-bg-color; + margin: 0px; + // border-bottom: 1px solid #C4CFE5; + color: $content-fg-color; +} + +div.headertitle { + padding: 5px 5px 5px 10px; + border-bottom: 1px solid #C4CFE5; +} + +.PageDocRTL-title div.headertitle { + text-align: right; + direction: rtl; + +} + +dl { + padding: 0 0 0 0; + +} + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ +dl.section { + margin-left: 0px; + padding-left: 0px; + +} + +dl.section.DocNodeRTL { + margin-right: 0px; + padding-right: 0px; + +} + +dl.note { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; + +} + +dl.note.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; + +} + +dl.warning, dl.attention { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; + +} + +dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; + +} + +dl.pre, dl.post, dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; + +} + +dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; + +} + +dl.deprecated { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; + +} + +dl.deprecated.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; + +} + +dl.todo { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; + +} + +dl.todo.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; + +} + +dl.test { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; + +} + +dl.test.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; + +} + +dl.bug { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; + +} + +dl.bug.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; + +} + +dl.section dd { + margin-bottom: 6px; + +} + + +#projectlogo { + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img { + border: 0px none; + height: 8em; + padding: 1em; +} + +#projectalign { + vertical-align: middle; +} + +#projectname { + font: 300% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief { + font: 120% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber { + font: 50% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea { + padding: 0px; + margin: 0px; + width: 100%; + color: $title-fg-color; + background-color: $title-bg-color; +} + +.image { + text-align: center; + background-color: $image-bg-color; + margin-left: 5em; + margin-right: 5em; + padding: 0.5em; + border-radius: 4px; +} + +.dotgraph { + text-align: center; +} + +.mscgraph { + text-align: center; +} + +.plantumlgraph { + text-align: center; +} + +.diagraph { + text-align: center; +} + +.caption { + font-weight: bold; +} + +div.zoom { + border: 1px solid #90A5CE; +} + +dl.citelist { + + margin-bottom: 50px; + +} + +dl.citelist dt { + + color: #334975; + + float: left; + + font-weight: bold; + + margin-right: 10px; + + padding: 5px; + +} + +dl.citelist dd { + + margin: 2px 0; + + padding: 5px 0; + +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; + +} + +.PageDocRTL-title div.toc { + float: left !important; + text-align: right; + +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana, DejaVu Sans, Geneva, sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; + +} + +.PageDocRTL-title div.toc li { + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; + +} + +div.toc h3 { + font: bold 12px/1.2 Arial, FreeSans, sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; + +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; + +} + +div.toc li.level1 { + margin-left: 0px; + +} + +div.toc li.level2 { + margin-left: 15px; + +} + +div.toc li.level3 { + margin-left: 30px; + +} + +div.toc li.level4 { + margin-left: 45px; + +} + +.PageDocRTL-title div.toc li.level1 { + margin-left: 0 !important; + margin-right: 0; + +} + +.PageDocRTL-title div.toc li.level2 { + margin-left: 0 !important; + margin-right: 15px; + +} + +.PageDocRTL-title div.toc li.level3 { + margin-left: 0 !important; + margin-right: 30px; + +} + +.PageDocRTL-title div.toc li.level4 { + margin-left: 0 !important; + margin-right: 45px; + +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + +.inherit_header td { + padding: 6px 0px 2px 5px; + +} + +.inherit { + display: none; + +} + +tr.heading h2 { + //margin: 0.5em; + //margin: 0; + //margin-bottom: 0.5em; + +} + +/* tooltip related style info */ + +.ttc { + position: absolute; + display: none; + +} + +#powerTip { + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; + +} + +#powerTip div.ttdoc { + color: grey; + font-style: italic; + +} + +#powerTip div.ttname a { + font-weight: bold; + +} + +#powerTip div.ttname { + font-weight: bold; + +} + +#powerTip div.ttdeci { + color: #006318; + +} + +#powerTip div { + margin: 0px; + padding: 0px; + font: 12px/16px Roboto, sans-serif; + +} + +#powerTip:before, #powerTip:after { + content: ""; + position: absolute; + margin: 0px; + +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.s:after, #powerTip.s:before, +#powerTip.w:after, #powerTip.w:before, +#powerTip.e:after, #powerTip.e:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.nw:after, #powerTip.nw:before, +#powerTip.sw:after, #powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + +} + +#powerTip.n:after, #powerTip.s:after, +#powerTip.w:after, #powerTip.e:after, +#powerTip.nw:after, #powerTip.ne:after, +#powerTip.sw:after, #powerTip.se:after { + border-color: rgba(255, 255, 255, 0); + +} + +#powerTip.n:before, #powerTip.s:before, +#powerTip.w:before, #powerTip.e:before, +#powerTip.nw:before, #powerTip.ne:before, +#powerTip.sw:before, #powerTip.se:before { + border-color: rgba(128, 128, 128, 0); + +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.nw:after, #powerTip.nw:before { + top: 100%; + +} + +#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { + border-top-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; + +} + +#powerTip.n:before { + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; + +} + +#powerTip.n:after, #powerTip.n:before { + left: 50%; + +} + +#powerTip.nw:after, #powerTip.nw:before { + right: 14px; + +} + +#powerTip.ne:after, #powerTip.ne:before { + left: 14px; + +} + +#powerTip.s:after, #powerTip.s:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.sw:after, #powerTip.sw:before { + bottom: 100%; + +} + +#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { + border-bottom-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; + +} + +#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; + +} + +#powerTip.s:after, #powerTip.s:before { + left: 50%; + +} + +#powerTip.sw:after, #powerTip.sw:before { + right: 14px; + +} + +#powerTip.se:after, #powerTip.se:before { + left: 14px; + +} + +#powerTip.e:after, #powerTip.e:before { + left: 100%; + +} + +#powerTip.e:after { + border-left-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; + +} + +#powerTip.e:before { + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; + +} + +#powerTip.w:after, #powerTip.w:before { + right: 100%; + +} + +#powerTip.w:after { + border-right-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; + +} + +#powerTip.w:before { + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; + +} + +@media print { + #top { + display: none; + } + + #side-nav { + display: none; + } + + #nav-path { + display: none; + } + + body { + overflow: visible; + } + + h1, h2, h3, h4, h5, h6 { + page-break-after: avoid; + } + + .summary { + display: none; + } + + .memitem { + page-break-inside: avoid; + } + + #doc-content { + margin-left: 0 !important; + + height: auto !important; + + width: auto !important; + + overflow: inherit; + + display: inline; + + } +} + +/* @group Markdown */ + +/* +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTableHead tr { + +} + +table.markdownTableBodyLeft td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft { + text-align: left + +} + +th.markdownTableHeadRight { + text-align: right + +} + +th.markdownTableHeadCenter { + text-align: center + +} +* / + +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTable tr { + +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left + +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right + +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center + +} + +.DocNodeRTL { + text-align:right; + direction:rtl; + +} + +.DocNodeLTR { + text-align:left; + direction:ltr; + +} + +table.DocNodeRTL { + width:auto; + margin-right: 0; + margin-left:auto; + +} + +table.DocNodeLTR { + width:auto; + margin-right:auto; + margin-left: 0; + +} + +tt, code, kbd, samp + +{ +display:inline-block; +direction:ltr; + +} +/* @end */ + +u { + text-decoration: underline; + +} diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000..94684c9 --- /dev/null +++ b/doc/footer.html @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 0000000..d46710a --- /dev/null +++ b/doc/header.html @@ -0,0 +1,63 @@ + + + + + + + + + $projectname: $title + + $title + + + + $treeview + $search + $mathjax + + $extrastylesheet + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber + +
+ +
$projectbrief
+ +
+
$projectbrief
+
$searchbox
+
+ + \ No newline at end of file diff --git a/doc/resources/gdt_bits.png b/doc/resources/gdt_bits.png new file mode 100644 index 0000000000000000000000000000000000000000..b326c18edb04176781f6fd1042fbb245d0423f58 GIT binary patch literal 8181 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 VB3?WWc%2W@dZPQdLe1vYe*sPu865xs literal 0 HcmV?d00001 diff --git a/doc/signal.md b/doc/signal.md new file mode 100644 index 0000000..ff79493 --- /dev/null +++ b/doc/signal.md @@ -0,0 +1,49 @@ +# Signal + +## Legenda + +The **first column** of the table represent the implementation status, +specifically: + +1. Implemented +2. Tested + +If the column is empty it means that it's not implemented yet. + +## Implementation Status + +``` + S | Number | Signal | Standard | Action | Comment +---+--------+-----------+----------+--------+------------------------------ + | 1 | SIGHUP | P1990 | Term | Hangup detected on controlling terminal or death of controlling process + | 2 | SIGINT | P1990 | Term | Interrupt from keyboard + | 3 | SIGQUIT | P1990 | Core | Quit from keyboard + | 4 | SIGILL | P1990 | Core | Illegal Instruction + | 5 | SIGTRAP | P2001 | Core | Trace/breakpoint trap + 2 | 6 | SIGABRT | P1990 | Core | Abort signal from abort(3) + | 7 | SIGEMT | - | Term | Emulator trap + 2 | 8 | SIGFPE | P1990 | Core | Floating-point exception + 2 | 9 | SIGKILL | P1990 | Term | Kill signal + | 10 | SIGBUS | P2001 | Core | Bus error (bad memory access) + | 11 | SIGSEGV | P1990 | Core | Invalid memory reference + | 12 | SIGSYS | P2001 | Core | Bad system call (SVr4); see also seccomp(2) + | 13 | SIGPIPE | P1990 | Term | Broken pipe: write to pipe with no readers; see pipe(7) + 2 | 14 | SIGALRM | P1990 | Term | Timer signal from alarm(2) + 2 | 15 | SIGTERM | P1990 | Term | Termination signal + 2 | 16 | SIGUSR1 | P1990 | Term | User-defined signal 1 + 2 | 17 | SIGUSR2 | P1990 | Term | User-defined signal 2 + 1 | 18 | SIGCHLD | P1990 | Ign | Child stopped or terminated + | 19 | SIGPWR | - | Term | Power failure (System V) + | 20 | SIGWINCH | - | Ign | Window resize signal (4.3BSD, Sun) + | 21 | SIGURG | P2001 | Ign | Urgent condition on socket (4.2BSD) + | 22 | SIGPOLL | P2001 | Term | Pollable event (Sys V); synonym for SIGIO + 2 | 23 | SIGSTOP | P1990 | Stop | Stop process + | 24 | SIGTSTP | P1990 | Stop | Stop typed at terminal + 2 | 25 | SIGCONT | P1990 | Cont | Continue if stopped + | 26 | SIGTTIN | P1990 | Stop | Terminal input for background process + | 27 | SIGTTOU | P1990 | Stop | Terminal output for background process + | 28 | SIGVTALRM | P2001 | Term | Virtual alarm clock (4.2BSD) + 2 | 29 | SIGPROF | P2001 | Term | Profiling timer expired + | 30 | SIGXCPU | P2001 | Core | CPU time limit exceeded (4.2BSD); see setrlimit(2) + | 31 | SIGXFSZ | P2001 | Core | File size limit exceeded (4.2BSD); see setrlimit(2) +``` \ No newline at end of file diff --git a/doc/syscall.md b/doc/syscall.md new file mode 100644 index 0000000..3a01c3c --- /dev/null +++ b/doc/syscall.md @@ -0,0 +1,207 @@ +# System Call + +## Legenda + +The **first column** of the table represent the implementation status, +specifically: + +1. Implemented +2. Tested + +If the column is empty it means that it's not implemented yet. + +## Implementation Status + +``` + S | EAX | Name | Source | EBX | ECX | EDX | ESX | EDI | +---+-----+----------------------------+-----------------------------+--------------------------+------------------------------+-------------------------+-----------------+------------------| + 2 | 1 | sys_exit | kernel/exit.c | int | - | - | - | - | + 2 | 2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | + 2 | 3 | sys_read | fs/read_write.c | unsigned int | char * | size_t | - | - | + 2 | 4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t | - | - | + 2 | 5 | sys_open | fs/open.c | const char * | int | int | - | - | + 2 | 6 | sys_close | fs/open.c | int | - | - | - | - | + 2 | 7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int | - | - | + | 8 | sys_creat | fs/open.c | const char * | int | - | - | - | + | 9 | sys_link | fs/namei.c | const char * | const char * | - | - | - | + 2 | 10 | sys_unlink | fs/namei.c | const char * | - | - | - | - | + 2 | 11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | + 2 | 12 | sys_chdir | fs/open.c | const char * | - | - | - | - | + 2 | 13 | sys_time | kernel/time.c | int * | - | - | - | - | + | 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | + | 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | + | 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | + 2 | 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | + 2 | 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | + 2 | 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | + | 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | + | 22 | sys_oldumount | fs/super.c | char * | - | - | - | - | + | 23 | sys_setuid | kernel/sys.c | uid_t | - | - | - | - | + | 24 | sys_getuid | kernel/sched.c | - | - | - | - | - | + | 25 | sys_stime | kernel/time.c | int * | - | - | - | - | + | 26 | sys_ptrace | arch/i386/kernel/ptrace.c | long | long | long | long | - | + | 27 | sys_alarm | kernel/sched.c | unsigned int | - | - | - | - | + 2 | 28 | sys_fstat | fs/stat.c | unsigned int | struct __old_kernel_stat * | - | - | - | + | 29 | sys_pause | arch/i386/kernel/sys_i386.c | - | - | - | - | - | + | 30 | sys_utime | fs/open.c | char * | struct utimbuf * | - | - | - | + | 33 | sys_access | fs/open.c | const char * | int | - | - | - | + 1 | 34 | sys_nice | kernel/sched.c | int | - | - | - | - | + | 36 | sys_sync | fs/buffer.c | - | - | - | - | - | + 1 | 37 | sys_kill | kernel/signal.c | int | int | - | - | - | + | 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | + 2 | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | + 2 | 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | + | 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | + | 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | + | 43 | sys_times | kernel/sys.c | struct tms * | - | - | - | - | + 2 | 45 | sys_brk | mm/mmap.c | unsigned long | - | - | - | - | + | 46 | sys_setgid | kernel/sys.c | gid_t | - | - | - | - | + | 47 | sys_getgid | kernel/sched.c | - | - | - | - | - | + 1 | 48 | sys_signal | kernel/signal.c | int | __sighandler_t | - | - | - | + | 49 | sys_geteuid | kernel/sched.c | - | - | - | - | - | + | 50 | sys_getegid | kernel/sched.c | - | - | - | - | - | + | 51 | sys_acct | kernel/acct.c | const char * | - | - | - | - | + | 52 | sys_umount | fs/super.c | char * | int | - | - | - | + 1 | 54 | sys_ioctl | fs/ioctl.c | unsigned int | unsigned int | unsigned long | - | - | + | 55 | sys_fcntl | fs/fcntl.c | unsigned int | unsigned int | unsigned long | - | - | + | 57 | sys_setpgid | kernel/sys.c | pid_t | pid_t | - | - | - | + | 59 | sys_olduname | arch/i386/kernel/sys_i386.c | struct oldold_utsname * | - | - | - | - | + | 60 | sys_umask | kernel/sys.c | int | - | - | - | - | + | 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | + | 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | + | 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | + 2 | 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | + | 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | + | 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | + 1 | 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | + | 68 | sys_sgetmask | kernel/signal.c | - | - | - | - | - | + | 69 | sys_ssetmask | kernel/signal.c | int | - | - | - | - | + | 70 | sys_setreuid | kernel/sys.c | uid_t | uid_t | - | - | - | + | 71 | sys_setregid | kernel/sys.c | gid_t | gid_t | - | - | - | + | 72 | sys_sigsuspend | arch/i386/kernel/signal.c | int | int | old_sigset_t | - | - | + | 73 | sys_sigpending | kernel/signal.c | old_sigset_t * | - | - | - | - | + | 74 | sys_sethostname | kernel/sys.c | char * | int | - | - | - | + | 75 | sys_setrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | + | 76 | sys_getrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | + | 77 | sys_getrusage | kernel/sys.c | int | struct rusage * | - | - | - | + | 78 | sys_gettimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | + | 79 | sys_settimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | + | 80 | sys_getgroups | kernel/sys.c | int | gid_t * | - | - | - | + | 81 | sys_setgroups | kernel/sys.c | int | gid_t * | - | - | - | + | 83 | sys_symlink | fs/namei.c | const char * | const char * | - | - | - | + | 84 | sys_lstat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | + | 85 | sys_readlink | fs/stat.c | const char * | char * | int | - | - | + | 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | + | 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | + 1 | 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | + 1 | 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | + | 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | + | 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | + | 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | + | 93 | sys_ftruncate | fs/open.c | unsigned int | unsigned long | - | - | - | + | 94 | sys_fchmod | fs/open.c | unsigned int | mode_t | - | - | - | + | 95 | sys_fchown | fs/open.c | unsigned int | uid_t | gid_t | - | - | + | 96 | sys_getpriority | kernel/sys.c | int | int | - | - | - | + | 97 | sys_setpriority | kernel/sys.c | int | int | int | - | - | + | 99 | sys_statfs | fs/open.c | const char * | struct statfs * | - | - | - | + | 100 | sys_fstatfs | fs/open.c | unsigned int | struct statfs * | - | - | - | + | 101 | sys_ioperm | arch/i386/kernel/ioport.c | unsigned long | unsigned long | int | - | - | + | 102 | sys_socketcall | net/socket.c | int | unsigned long * | - | - | - | + | 103 | sys_syslog | kernel/printk.c | int | char * | int | - | - | + | 104 | sys_setitimer | kernel/itimer.c | int | struct itimerval * | struct itimerval * | - | - | + | 105 | sys_getitimer | kernel/itimer.c | int | struct itimerval * | - | - | - | + | 106 | sys_newstat | fs/stat.c | char * | struct stat * | - | - | - | + | 107 | sys_newlstat | fs/stat.c | char * | struct stat * | - | - | - | + | 108 | sys_newfstat | fs/stat.c | unsigned int | struct stat * | - | - | - | + 1 | 109 | sys_uname | arch/i386/kernel/sys_i386.c | struct old_utsname * | - | - | - | - | + | 110 | sys_iopl | arch/i386/kernel/ioport.c | unsigned long | - | - | - | - | + | 111 | sys_vhangup | fs/open.c | - | - | - | - | - | + | 112 | sys_idle | arch/i386/kernel/process.c | - | - | - | - | - | + | 113 | sys_vm86old | arch/i386/kernel/vm86.c | unsigned long | struct vm86plus_struct * | - | - | - | + | 114 | sys_wait4 | kernel/exit.c | pid_t | unsigned long * | int options | struct rusage * | - | + | 115 | sys_swapoff | mm/swapfile.c | const char * | - | - | - | - | + | 116 | sys_sysinfo | kernel/info.c | struct sysinfo * | - | - | - | - | + * | 117 | sys_ipc(*Note) | arch/i386/kernel/sys_i386.c | uint | int | int | int | void * | + | 118 | sys_fsync | fs/buffer.c | unsigned int | - | - | - | - | + | 119 | sys_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | + | 120 | sys_clone | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | + | 121 | sys_setdomainname | kernel/sys.c | char * | int | - | - | - | + | 122 | sys_newuname | kernel/sys.c | struct new_utsname * | - | - | - | - | + | 123 | sys_modify_ldt | arch/i386/kernel/ldt.c | int | void * | unsigned long | - | - | + | 124 | sys_adjtimex | kernel/time.c | struct timex * | - | - | - | - | + | 125 | sys_mprotect | mm/mprotect.c | unsigned long | size_t | unsigned long | - | - | + 1 | 126 | sys_sigprocmask | kernel/signal.c | int | old_sigset_t * | old_sigset_t * | - | - | + | 127 | sys_create_module | kernel/module.c | const char * | size_t | - | - | - | + | 128 | sys_init_module | kernel/module.c | const char * | struct module * | - | - | - | + | 129 | sys_delete_module | kernel/module.c | const char * | - | - | - | - | + | 130 | sys_get_kernel_syms | kernel/module.c | struct kernel_sym * | - | - | - | - | + | 131 | sys_quotactl | fs/dquot.c | int | const char * | int | caddr_t | - | + | 132 | sys_getpgid | kernel/sys.c | pid_t | - | - | - | - | + 1 | 133 | sys_fchdir | fs/open.c | unsigned int | - | - | - | - | + | 134 | sys_bdflush | fs/buffer.c | int | long | - | - | - | + | 135 | sys_sysfs | fs/super.c | int | unsigned long | unsigned long | - | - | + | 136 | sys_personality | kernel/exec_domain.c | unsigned long | - | - | - | - | + | 138 | sys_setfsuid | kernel/sys.c | uid_t | - | - | - | - | + | 139 | sys_setfsgid | kernel/sys.c | gid_t | - | - | - | - | + | 140 | sys_llseek | fs/read_write.c | unsigned int | unsigned long | unsigned long | loff_t * | unsigned int | + 2 | 141 | sys_getdents | fs/readdir.c | unsigned int | void * | unsigned int | - | - | + | 142 | sys_select | fs/select.c | int | fd_set * | fd_set * | fd_set * | struct timeval * | + | 143 | sys_flock | fs/locks.c | unsigned int | unsigned int | - | - | - | + | 144 | sys_msync | mm/filemap.c | unsigned long | size_t | int | - | - | + | 145 | sys_readv | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | + | 146 | sys_writev | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | + | 147 | sys_getsid | kernel/sys.c | pid_t | - | - | - | - | + | 148 | sys_fdatasync | fs/buffer.c | unsigned int | - | - | - | - | + | 149 | sys_sysctl | kernel/sysctl.c | struct __sysctl_args * | - | - | - | - | + | 150 | sys_mlock | mm/mlock.c | unsigned long | size_t | - | - | - | + | 151 | sys_munlock | mm/mlock.c | unsigned long | size_t | - | - | - | + | 152 | sys_mlockall | mm/mlock.c | int | - | - | - | - | + | 153 | sys_munlockall | mm/mlock.c | - | - | - | - | - | + 1 | 154 | sys_sched_setparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | + 1 | 155 | sys_sched_getparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | + | 156 | sys_sched_setscheduler | kernel/sched.c | pid_t | int | struct sched_param * | - | - | + | 157 | sys_sched_getscheduler | kernel/sched.c | pid_t | - | - | - | - | + | 158 | sys_sched_yield | kernel/sched.c | - | - | - | - | - | + | 159 | sys_sched_get_priority_max | kernel/sched.c | int | - | - | - | - | + | 160 | sys_sched_get_priority_min | kernel/sched.c | int | - | - | - | - | + | 161 | sys_sched_rr_get_interval | kernel/sched.c | pid_t | struct timespec * | - | - | - | + | 162 | sys_nanosleep | kernel/sched.c | struct timespec * | struct timespec * | - | - | - | + | 163 | sys_mremap | mm/mremap.c | unsigned long | unsigned long | unsigned long | unsigned long | - | + | 164 | sys_setresuid | kernel/sys.c | uid_t | uid_t | uid_t | - | - | + | 165 | sys_getresuid | kernel/sys.c | uid_t * | uid_t * | uid_t * | - | - | + | 166 | sys_vm86 | arch/i386/kernel/vm86.c | struct vm86_struct * | - | - | - | - | + | 167 | sys_query_module | kernel/module.c | const char * | int | char * | size_t | size_t * | + | 168 | sys_poll | fs/select.c | struct pollfd * | unsigned int | long | - | - | + | 169 | sys_nfsservctl | fs/filesystems.c | int | void * | void * | - | - | + | 170 | sys_setresgid | kernel/sys.c | gid_t | gid_t | gid_t | - | - | + | 171 | sys_getresgid | kernel/sys.c | gid_t * | gid_t * | gid_t * | - | - | + | 172 | sys_prctl | kernel/sys.c | int | unsigned long | unsigned long | unsigned long | unsigned long | + | 173 | sys_rt_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | + | 174 | sys_rt_sigaction | kernel/signal.c | int | const struct sigaction * | struct sigaction * | size_t | - | + | 175 | sys_rt_sigprocmask | kernel/signal.c | int | sigset_t * | sigset_t * | size_t | - | + | 176 | sys_rt_sigpending | kernel/signal.c | sigset_t * | size_t | - | - | - | + | 177 | sys_rt_sigtimedwait | kernel/signal.c | const sigset_t * | siginfo_t * | const struct timespec * | size_t | - | + | 178 | sys_rt_sigqueueinfo | kernel/signal.c | int | int | siginfo_t * | - | - | + | 179 | sys_rt_sigsuspend | arch/i386/kernel/signal.c | sigset_t * | size_t | - | - | - | + | 180 | sys_pread | fs/read_write.c | unsigned int | char * | size_t | loff_t | - | + | 181 | sys_pwrite | fs/read_write.c | unsigned int | const char * | size_t | loff_t | - | + | 182 | sys_chown | fs/open.c | const char * | uid_t | gid_t | - | - | + 1 | 183 | sys_getcwd | fs/dcache.c | char * | unsigned long | - | - | - | + | 184 | sys_capget | kernel/capability.c | cap_user_header_t | cap_user_data_t | - | - | - | + | 185 | sys_capset | kernel/capability.c | cap_user_header_t | const cap_user_data_t | - | - | - | + | 186 | sys_sigaltstack | arch/i386/kernel/signal.c | const stack_t * | stack_t * | - | - | - | + | 187 | sys_sendfile | mm/filemap.c | int | int | off_t * | size_t | - | + | | | | | | | | | + 2 | 188 | sys_waitperiod | kernel/sched.c | | | | | | + | 189 | sys_msgctl | | int | | | | | + | 190 | sys_msgget | | | | | | | + | 191 | sys_msgrcv | | | | | | | + | 192 | sys_msgsnd | | | | | | | + | 193 | sys_semctl | | | | | | | + | 194 | sys_semget | | | | | | | + | 195 | sys_semop | | | | | | | + | 196 | sys_shmat | | | | | | | + | 197 | sys_shmctl | | | | | | | + | 198 | sys_shmdt | | | | | | | + | 199 | sys_shmget | | | | | | | +``` \ No newline at end of file diff --git a/doc/syscall.txt b/doc/syscall.txt deleted file mode 100644 index 3b00589..0000000 --- a/doc/syscall.txt +++ /dev/null @@ -1,180 +0,0 @@ -| S | EAX | Name | Source | EBX | ECX | EDX | ESX | EDI | -|:--|:----|:---------------------------|:----------------------------|:-------------------------|:-----------------------------|:------------------------|:----------------|:-----------------| -| 1 | 1 | sys_exit | kernel/exit.c | int | - | - | - | - | -| | 2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 1 | 3 | sys_read | fs/read_write.c | unsigned int | char * | size_t | - | - | -| 1 | 4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t | - | - | -| 1 | 5 | sys_open | fs/open.c | const char * | int | int | - | - | -| 1 | 6 | sys_close | fs/open.c | int | - | - | - | - | -| 1 | 7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int | - | - | -| | 8 | sys_creat | fs/open.c | const char * | int | - | - | - | -| | 9 | sys_link | fs/namei.c | const char * | const char * | - | - | - | -| | 10 | sys_unlink | fs/namei.c | const char * | - | - | - | - | -| 1 | 11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 1 | 12 | sys_chdir | fs/open.c | const char * | - | - | - | - | -| | 13 | sys_time | kernel/time.c | int * | - | - | - | - | -| | 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | -| | 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | -| | 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | -| 1 | 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | -| | 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | -| 1 | 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | -| | 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | -| | 22 | sys_oldumount | fs/super.c | char * | - | - | - | - | -| | 23 | sys_setuid | kernel/sys.c | uid_t | - | - | - | - | -| | 24 | sys_getuid | kernel/sched.c | - | - | - | - | - | -| | 25 | sys_stime | kernel/time.c | int * | - | - | - | - | -| | 26 | sys_ptrace | arch/i386/kernel/ptrace.c | long | long | long | long | - | -| | 27 | sys_alarm | kernel/sched.c | unsigned int | - | - | - | - | -| | 28 | sys_fstat | fs/stat.c | unsigned int | struct __old_kernel_stat * | - | - | - | -| | 29 | sys_pause | arch/i386/kernel/sys_i386.c | - | - | - | - | - | -| | 30 | sys_utime | fs/open.c | char * | struct utimbuf * | - | - | - | -| | 33 | sys_access | fs/open.c | const char * | int | - | - | - | -| 1 | 34 | sys_nice | kernel/sched.c | int | - | - | - | - | -| | 36 | sys_sync | fs/buffer.c | - | - | - | - | - | -| | 37 | sys_kill | kernel/signal.c | int | int | - | - | - | -| | 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | -| 1 | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | -| | 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | -| | 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | -| | 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | -| | 43 | sys_times | kernel/sys.c | struct tms * | - | - | - | - | -| | 45 | sys_brk | mm/mmap.c | unsigned long | - | - | - | - | -| | 46 | sys_setgid | kernel/sys.c | gid_t | - | - | - | - | -| | 47 | sys_getgid | kernel/sched.c | - | - | - | - | - | -| | 48 | sys_signal | kernel/signal.c | int | __sighandler_t | - | - | - | -| | 49 | sys_geteuid | kernel/sched.c | - | - | - | - | - | -| | 50 | sys_getegid | kernel/sched.c | - | - | - | - | - | -| | 51 | sys_acct | kernel/acct.c | const char * | - | - | - | - | -| | 52 | sys_umount | fs/super.c | char * | int | - | - | - | -| | 54 | sys_ioctl | fs/ioctl.c | unsigned int | unsigned int | unsigned long | - | - | -| | 55 | sys_fcntl | fs/fcntl.c | unsigned int | unsigned int | unsigned long | - | - | -| | 57 | sys_setpgid | kernel/sys.c | pid_t | pid_t | - | - | - | -| | 59 | sys_olduname | arch/i386/kernel/sys_i386.c | struct oldold_utsname * | - | - | - | - | -| | 60 | sys_umask | kernel/sys.c | int | - | - | - | - | -| | 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | -| | 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | -| | 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | -| 1 | 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | -| | 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | -| | 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | -| | 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | -| | 68 | sys_sgetmask | kernel/signal.c | - | - | - | - | - | -| | 69 | sys_ssetmask | kernel/signal.c | int | - | - | - | - | -| | 70 | sys_setreuid | kernel/sys.c | uid_t | uid_t | - | - | - | -| | 71 | sys_setregid | kernel/sys.c | gid_t | gid_t | - | - | - | -| | 72 | sys_sigsuspend | arch/i386/kernel/signal.c | int | int | old_sigset_t | - | - | -| | 73 | sys_sigpending | kernel/signal.c | old_sigset_t * | - | - | - | - | -| | 74 | sys_sethostname | kernel/sys.c | char * | int | - | - | - | -| | 75 | sys_setrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | -| | 76 | sys_getrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | -| | 77 | sys_getrusage | kernel/sys.c | int | struct rusage * | - | - | - | -| | 78 | sys_gettimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | -| | 79 | sys_settimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | -| | 80 | sys_getgroups | kernel/sys.c | int | gid_t * | - | - | - | -| | 81 | sys_setgroups | kernel/sys.c | int | gid_t * | - | - | - | -| | 83 | sys_symlink | fs/namei.c | const char * | const char * | - | - | - | -| | 84 | sys_lstat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | -| | 85 | sys_readlink | fs/stat.c | const char * | char * | int | - | - | -| | 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | -| | 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | -| 1 | 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | -| 1 | 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | -| | 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | -| | 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | -| | 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | -| | 93 | sys_ftruncate | fs/open.c | unsigned int | unsigned long | - | - | - | -| | 94 | sys_fchmod | fs/open.c | unsigned int | mode_t | - | - | - | -| | 95 | sys_fchown | fs/open.c | unsigned int | uid_t | gid_t | - | - | -| | 96 | sys_getpriority | kernel/sys.c | int | int | - | - | - | -| | 97 | sys_setpriority | kernel/sys.c | int | int | int | - | - | -| | 99 | sys_statfs | fs/open.c | const char * | struct statfs * | - | - | - | -| | 100 | sys_fstatfs | fs/open.c | unsigned int | struct statfs * | - | - | - | -| | 101 | sys_ioperm | arch/i386/kernel/ioport.c | unsigned long | unsigned long | int | - | - | -| | 102 | sys_socketcall | net/socket.c | int | unsigned long * | - | - | - | -| | 103 | sys_syslog | kernel/printk.c | int | char * | int | - | - | -| | 104 | sys_setitimer | kernel/itimer.c | int | struct itimerval * | struct itimerval * | - | - | -| | 105 | sys_getitimer | kernel/itimer.c | int | struct itimerval * | - | - | - | -| | 106 | sys_newstat | fs/stat.c | char * | struct stat * | - | - | - | -| | 107 | sys_newlstat | fs/stat.c | char * | struct stat * | - | - | - | -| | 108 | sys_newfstat | fs/stat.c | unsigned int | struct stat * | - | - | - | -| | 109 | sys_uname | arch/i386/kernel/sys_i386.c | struct old_utsname * | - | - | - | - | -| | 110 | sys_iopl | arch/i386/kernel/ioport.c | unsigned long | - | - | - | - | -| | 111 | sys_vhangup | fs/open.c | - | - | - | - | - | -| | 112 | sys_idle | arch/i386/kernel/process.c | - | - | - | - | - | -| | 113 | sys_vm86old | arch/i386/kernel/vm86.c | unsigned long | struct vm86plus_struct * | - | - | - | -| | 114 | sys_wait4 | kernel/exit.c | pid_t | unsigned long * | int options | struct rusage * | - | -| | 115 | sys_swapoff | mm/swapfile.c | const char * | - | - | - | - | -| | 116 | sys_sysinfo | kernel/info.c | struct sysinfo * | - | - | - | - | -| | 117 | sys_ipc(*Note) | arch/i386/kernel/sys_i386.c | uint | int | int | int | void * | -| | 118 | sys_fsync | fs/buffer.c | unsigned int | - | - | - | - | -| | 119 | sys_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | -| | 120 | sys_clone | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| | 121 | sys_setdomainname | kernel/sys.c | char * | int | - | - | - | -| | 122 | sys_newuname | kernel/sys.c | struct new_utsname * | - | - | - | - | -| | 123 | sys_modify_ldt | arch/i386/kernel/ldt.c | int | void * | unsigned long | - | - | -| | 124 | sys_adjtimex | kernel/time.c | struct timex * | - | - | - | - | -| | 125 | sys_mprotect | mm/mprotect.c | unsigned long | size_t | unsigned long | - | - | -| | 126 | sys_sigprocmask | kernel/signal.c | int | old_sigset_t * | old_sigset_t * | - | - | -| | 127 | sys_create_module | kernel/module.c | const char * | size_t | - | - | - | -| | 128 | sys_init_module | kernel/module.c | const char * | struct module * | - | - | - | -| | 129 | sys_delete_module | kernel/module.c | const char * | - | - | - | - | -| | 130 | sys_get_kernel_syms | kernel/module.c | struct kernel_sym * | - | - | - | - | -| | 131 | sys_quotactl | fs/dquot.c | int | const char * | int | caddr_t | - | -| | 132 | sys_getpgid | kernel/sys.c | pid_t | - | - | - | - | -| | 133 | sys_fchdir | fs/open.c | unsigned int | - | - | - | - | -| | 134 | sys_bdflush | fs/buffer.c | int | long | - | - | - | -| | 135 | sys_sysfs | fs/super.c | int | unsigned long | unsigned long | - | - | -| | 136 | sys_personality | kernel/exec_domain.c | unsigned long | - | - | - | - | -| | 138 | sys_setfsuid | kernel/sys.c | uid_t | - | - | - | - | -| | 139 | sys_setfsgid | kernel/sys.c | gid_t | - | - | - | - | -| | 140 | sys_llseek | fs/read_write.c | unsigned int | unsigned long | unsigned long | loff_t * | unsigned int | -| | 141 | sys_getdents | fs/readdir.c | unsigned int | void * | unsigned int | - | - | -| | 142 | sys_select | fs/select.c | int | fd_set * | fd_set * | fd_set * | struct timeval * | -| | 143 | sys_flock | fs/locks.c | unsigned int | unsigned int | - | - | - | -| | 144 | sys_msync | mm/filemap.c | unsigned long | size_t | int | - | - | -| | 145 | sys_readv | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | -| | 146 | sys_writev | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | -| | 147 | sys_getsid | kernel/sys.c | pid_t | - | - | - | - | -| | 148 | sys_fdatasync | fs/buffer.c | unsigned int | - | - | - | - | -| | 149 | sys_sysctl | kernel/sysctl.c | struct __sysctl_args * | - | - | - | - | -| | 150 | sys_mlock | mm/mlock.c | unsigned long | size_t | - | - | - | -| | 151 | sys_munlock | mm/mlock.c | unsigned long | size_t | - | - | - | -| | 152 | sys_mlockall | mm/mlock.c | int | - | - | - | - | -| | 153 | sys_munlockall | mm/mlock.c | - | - | - | - | - | -| | 154 | sys_sched_setparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | -| | 155 | sys_sched_getparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | -| | 156 | sys_sched_setscheduler | kernel/sched.c | pid_t | int | struct sched_param * | - | - | -| | 157 | sys_sched_getscheduler | kernel/sched.c | pid_t | - | - | - | - | -| | 158 | sys_sched_yield | kernel/sched.c | - | - | - | - | - | -| | 159 | sys_sched_get_priority_max | kernel/sched.c | int | - | - | - | - | -| | 160 | sys_sched_get_priority_min | kernel/sched.c | int | - | - | - | - | -| | 161 | sys_sched_rr_get_interval | kernel/sched.c | pid_t | struct timespec * | - | - | - | -| | 162 | sys_nanosleep | kernel/sched.c | struct timespec * | struct timespec * | - | - | - | -| | 163 | sys_mremap | mm/mremap.c | unsigned long | unsigned long | unsigned long | unsigned long | - | -| | 164 | sys_setresuid | kernel/sys.c | uid_t | uid_t | uid_t | - | - | -| | 165 | sys_getresuid | kernel/sys.c | uid_t * | uid_t * | uid_t * | - | - | -| | 166 | sys_vm86 | arch/i386/kernel/vm86.c | struct vm86_struct * | - | - | - | - | -| | 167 | sys_query_module | kernel/module.c | const char * | int | char * | size_t | size_t * | -| | 168 | sys_poll | fs/select.c | struct pollfd * | unsigned int | long | - | - | -| | 169 | sys_nfsservctl | fs/filesystems.c | int | void * | void * | - | - | -| | 170 | sys_setresgid | kernel/sys.c | gid_t | gid_t | gid_t | - | - | -| | 171 | sys_getresgid | kernel/sys.c | gid_t * | gid_t * | gid_t * | - | - | -| | 172 | sys_prctl | kernel/sys.c | int | unsigned long | unsigned long | unsigned long | unsigned long | -| | 173 | sys_rt_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | -| | 174 | sys_rt_sigaction | kernel/signal.c | int | const struct sigaction * | struct sigaction * | size_t | - | -| | 175 | sys_rt_sigprocmask | kernel/signal.c | int | sigset_t * | sigset_t * | size_t | - | -| | 176 | sys_rt_sigpending | kernel/signal.c | sigset_t * | size_t | - | - | - | -| | 177 | sys_rt_sigtimedwait | kernel/signal.c | const sigset_t * | siginfo_t * | const struct timespec * | size_t | - | -| | 178 | sys_rt_sigqueueinfo | kernel/signal.c | int | int | siginfo_t * | - | - | -| | 179 | sys_rt_sigsuspend | arch/i386/kernel/signal.c | sigset_t * | size_t | - | - | - | -| | 180 | sys_pread | fs/read_write.c | unsigned int | char * | size_t | loff_t | - | -| | 181 | sys_pwrite | fs/read_write.c | unsigned int | const char * | size_t | loff_t | - | -| | 182 | sys_chown | fs/open.c | const char * | uid_t | gid_t | - | - | -| 1 | 183 | sys_getcwd | fs/dcache.c | char * | unsigned long | - | - | - | -| | 184 | sys_capget | kernel/capability.c | cap_user_header_t | cap_user_data_t | - | - | - | -| | 185 | sys_capset | kernel/capability.c | cap_user_header_t | const cap_user_data_t | - | - | - | -| | 186 | sys_sigaltstack | arch/i386/kernel/signal.c | const stack_t * | stack_t * | - | - | - | -| | 187 | sys_sendfile | mm/filemap.c | int | int | off_t * | size_t | - | -| 1 | 190 | sys_vfork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| | | | | | | | | | \ No newline at end of file diff --git a/files/bin/cat b/files/bin/cat new file mode 100644 index 0000000000000000000000000000000000000000..c843bfe4c9b998f43db35695a111ed8b89657668 GIT binary patch literal 43644 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 literal 0 HcmV?d00001 diff --git a/files/bin/clear b/files/bin/clear new file mode 100644 index 0000000000000000000000000000000000000000..a769a7dccbc20e3edd789214a7247da4759209de GIT binary patch literal 37588 zcmeI53w%`7weZhO5*RQzqehK_GFq_UBLqbe!OClRD31tF z`~AM}%d}_ati9G=YwfkyUVHDgcW|R`&J2gcp;;fNmZAyNn%z)8j^D0r24g;%AusVrN})$H@ce38L%lo$e3~XtnjOCsAO}28Zic4GlT^PJd@XpM zMqUr|4B!#|Lf0h^vgH}UyF5}Z&&?kPiugIBOEqogygaRS%CZNqesWvY zd)bXWf9(AB-uer^8V;)e({F)(3-nu{-va#>=(j+>1^O+}Z-IUb^jo0c0{s^Ff5rk| zx-a~5-JB7rp|;N0T|If>j~CvokD_&H`bJF)b)?sA)0$$j1>xQL3`#?LJUgW6b;aGc zEnZSr(6c*|I%2o*keZ>6RJ|h>i={oG>ZY!A)aAJrtaMO0Gh0>O7Fp8sY7+e=Bn39@ zUT+AC1zq~~GZZDfasXF4^p-JwR?X0(?Bh4>o!iM;2@crQu|K3|V~dcl(_zDGZVX kg8Ir>yo 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 hO?>ljekI~klU~S+e^h~xO+bi+kub{9-}zXl?)i literal 0 HcmV?d00001 diff --git a/files/bin/echo b/files/bin/echo new file mode 100644 index 0000000000000000000000000000000000000000..c66825e7b9999ffbacf81895c0b7918512e39ed1 GIT binary patch literal 37612 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 T=qe!O5NZ`A-+1SjN%y}26yy0r literal 0 HcmV?d00001 diff --git a/files/bin/env b/files/bin/env new file mode 100644 index 0000000000000000000000000000000000000000..9902273cdafb38ee41830a3d7f7a466c0ce14ede GIT binary patch literal 37604 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 L?8F%HN2mK=NzCqs literal 0 HcmV?d00001 diff --git a/files/bin/ipcrm b/files/bin/ipcrm new file mode 100644 index 0000000000000000000000000000000000000000..ae2982f9b4179b0a2c69207c4f6cf38c13de87c1 GIT binary patch literal 37564 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 Bqa^?U literal 0 HcmV?d00001 diff --git a/files/bin/ipcs b/files/bin/ipcs new file mode 100644 index 0000000000000000000000000000000000000000..9cedb3b23d5c5adf505138f0443564ada45cbec6 GIT binary patch literal 37564 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 literal 0 HcmV?d00001 diff --git a/files/bin/kill b/files/bin/kill new file mode 100644 index 0000000000000000000000000000000000000000..f82bb05c9fdbd82bbd94c5d53ebbdea7d87b4be6 GIT binary patch literal 43684 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 literal 0 HcmV?d00001 diff --git a/files/bin/login b/files/bin/login new file mode 100644 index 0000000000000000000000000000000000000000..2efd82b91cf19bb8f7edab161936a726729174ac GIT binary patch literal 52604 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 J(~nN~zW^VIGDQFY literal 0 HcmV?d00001 diff --git a/files/bin/ls b/files/bin/ls new file mode 100644 index 0000000000000000000000000000000000000000..cffd9686c258066860862e3b616371ea5bc76bac GIT binary patch literal 52980 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

=NEw;e7|9$XnPVh#jAV|H%rTNVMl#1p<`~Hw zBbZ|ZbBth)5zH}yIYuzY2<8~U93z-x1ak~$j^WHPoH>Rw$8hEt&K$#;V>ojRXO7{_ zk;xpH%#q0)naq*N9GT3K$sC!?k;xpH%)t!gCp)=?LR!8*hp!ssh_h{44vs;)y*c@w zLJwZL@GbL{=j8g!Jeqt{1gG`$y_!dS=M@zD^OpNHymNxHmU-oVEzet4ev=z7YG{B^ zK&CKhrf8nwrbeO(GDRC?iblv3t&k~NAoCKTt7!X7;Sia^BQk|cWD0l46#kGY93oSA zM5b_wOn5>L{I9f3_$W{G<>ZK4c{mP^EAMiAo}1ht3p}OyNb}?9d`?atUZQbZ92yP{ z;;jI@3jy9jzGtK5OrJG(@}yZgbEi$4?^=+vVAAASt{miBdCBPXLbpGsJTJ#zq?ayB z&s(XL>wcOPQoMQPt2Dh-p1Ui^T>_ilh_l#0O3Lz4g5T>eTbAS1mix-Rc#EV!D}YIH zYF+c^oo)XRDD=u4s*MR(G=5J08MbVTvG|8^cGQIBQoz*sXrG}%J zxf-ih07>RcK+qR1LTz)!(eqBsl#h(QmBP2}-v9!D*uf~_5P`C^?x{pWq z&9?L4jj#f(xU6ucw!-Vd%Noey$tx+>3Zxa6dGH<;67e2}216H!O0(9iJVXP+KaDR& zK@H*aOe>{~^0F1?J=jqMx(?VZGq5R%qSZPx$C8z7E4=zuOg;{;)Boe$SQM-!uZyk=9X@OmJzO%lanjXXXfx+rYHeDRCBK^ zXDKYy>n<qc98 z3VHUwB-gi0)6(%yhd*}-eqnR*TV&n&@jjH6ju#Ew>4l|w`Vt*)unmQ~r+dqUuIcWg zoB}W26w%TP%lzr(3`r&c8*Ax#cvA9W!b(KIf%XMfPmZ#c1xF{N=h9en5H~fgtriZc5X&PP7l*La^ TT%xXmLK)a#&y)B3vH1QMbD1~7 literal 0 HcmV?d00001 diff --git a/files/bin/nice b/files/bin/nice new file mode 100644 index 0000000000000000000000000000000000000000..c6a0bc1f587222b6520e706873384226eab206ca GIT binary patch literal 37640 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 H(&+vt5Y0&4 literal 0 HcmV?d00001 diff --git a/files/bin/poweroff b/files/bin/poweroff new file mode 100644 index 0000000000000000000000000000000000000000..b1f673d769650b755edc2e7d549c52d4efea1122 GIT binary patch literal 37640 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 Stqftkwi|13?VWyfy8i`bsq1I} literal 0 HcmV?d00001 diff --git a/files/bin/ps b/files/bin/ps new file mode 100644 index 0000000000000000000000000000000000000000..d67e1a9536a2b98d6e7c08bc67ab52c89f52f76f GIT binary patch literal 47928 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

w21u*msNb**;+d2X*5v?rGPZZ{>3Ps)Mnso( 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 literal 0 HcmV?d00001 diff --git a/files/bin/rm b/files/bin/rm new file mode 100644 index 0000000000000000000000000000000000000000..b43a78b86cf3d7c369b5462c0b934ea4ff76042c GIT binary patch literal 48096 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 literal 0 HcmV?d00001 diff --git a/files/bin/rmdir b/files/bin/rmdir new file mode 100644 index 0000000000000000000000000000000000000000..85ab8ec23cdfa3f9bcb18d6a962a75c94e2782f4 GIT binary patch literal 43588 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 literal 0 HcmV?d00001 diff --git a/files/bin/shell b/files/bin/shell new file mode 100644 index 0000000000000000000000000000000000000000..ff6806178daa80ec5bbad7730afb330d81291234 GIT binary patch literal 62076 zcmeFa31Ade)-PV21zHStMARrKtp){ANB~&`m91G6Bmz+oNeD@YBxEz`Vo?H#O(bm- zCvM|1I2!kT7ZFhdL5PSL1vQMI5u#S?I7ZD38I9BLch0@leY+FD`QH1#_x;}kt*$!v zEce`V&pmgo%4)~-$rg)6Gyg?sks3iYH>Fe$$9;XPzzo;yS~qQg7NecWcup;ivTC^E zz(7|NCB|w3kGrO&RMYjdThr)@R`Cx5r~)2W#vo0jD}=uu_!g6y`_G~|zekV|xJ zbRloL;_*xurPFoIH?9oaI{oX86jF<5dgF@3HRbAL?b3)}dTzda-_8kxK7Z}}!fRLA zd!76-P{;orx4>}=9Jj!63mmt=aSI%`z;O#4x4>}=9Jj!63mmt=|1}m!_I%@Ri8A8) zV0!+Kk2KBeuvR!aN=rI4R~&xru3q>(!4-qw9K>S`ZYEBcCVv@<sBMRn8kY>jqZ|0zutq(-HeqD(8c)m7~@;4T_AstWB(3E6P4YU>@JDbTqoP!DF55P z{eIgUHKQG|t_a`d5bW3YSC-c!)m;;lMBF3JVeax8y`R_7+p|MpH%){b&vwtpzdR11 zZ18iyofU&eK>_AQ<8GaqFQa7FZLCkDagU1=y^f|zXWRUg)CE*WuwifGk~7GLCvg#D zD&4jkzdy;C=alNVxl7tK{e(()Z=|E*SX`U&_+W`|A(Ok$B__Y#V%&6*OmB6Uv}*dP zOx|KdVA%-PU@Q5qMhSDQu%cgYHEyR;Z;M6kOBYHA3PjCZ7%J~QDeskGL3w7SQJv9J zjyon^D{HZ1Vad2Yj*2$yLnO9Xvj|yPZv05)8!74!;ws(oT;j!89^wjXahJ4cuA$&S zflT_RE@Uh!x<9!Y*QAXPUwS=1WAnFds_wfKcC4-UMZcjUSSXXN8$O z4uccF`kquBn<=wiabl%gh-vkmR9Q~gXj-N7I9k09!)v9Vq$v6wXC?1`or-$xHM3AL zt$w|&(mjfGo3Mc7*{dp@?ekL>)c#^S`!~44b+BGS1Z;Nx>0~qi2WYhAdw=))$#*O7 zX}}ZL-kYn*2S5Mi*&U8HLhW$0idzHSpf&&$X>d1;XptTbbmC{-d{c!cQQ)!g7N?2!X-fXocg%c^xerB3e9Ky^a`5 z{dVn!<~Yh}Mw_+S7RDK`7aWZ6Dxmo( za3L890b)wNgM~p+#5dcT`(OjL6eYIuTq2MK8{}w3y-{^lvgwnI9evr5>IOJKKJ1 z{*Lq_b4HMsuZC%Psjd8dC8@KP=XBEY%uZT1LW1UlmW@Z%veDG?iSwnFjijZp&=Cvg zZ2R3e@aQzY9#y{XRlcYypQXy}Fi@SwXvbdL+I(2mEV|3ZjXq%grvBm9pRh38`uojt z?LTU{_M7F}Eo6F=_e<;l&J7XqSZRoMtVSCTo(lx~*lLEo9RkN)*2hu1^+SZ)jmE6&e z7TYbqb3UzO@Lc{AKZnYWg`*CvN30VLOUlIrl&kYvJ@o_YJPtT@C>xYUe`zEkW^#jg z4BZ#vb~qUlnk-wXchnF7Kn%+y%`#5H4bvDP-hWqyRJ1 z@2+Pice`IdlepZ=IkbLv9Vd&i&}s0qHX6Q+aae8ZO3}}8%~044*0$1TQD6M@$yDdJ zY=@WFZ3=+vnt&O zSmwcN6(igTTI%L+Uc!kIvgS(2?37!%Q+~uGVuN#9rTcppx^$l4fWgrL!{DtVS8q3yCPVle| zqvn!r&@KfbHpgx)Zz@HD)=IaAG<41Vm?Tteb5APL)~)1Y#0uA$>SZB< z&9YUbS5EcI1k6LMvyzMzLYgSblaLZ9GMrOnujUkQtmd5NQcg5GYYG_9-QI7RE8JSH z@sMn4ewE0J!USd)ZOFhhYo(XU=1p$1?8J~E57EoC?9 zqqaV_@oN7`L=TExeY%E1@CxQH&9N{$KZFo?d}@eh8oC9GTstB8_jG>>R{|I zqQQ1Y2hT73dYuu0-V&B;u0=XnC+WjQjnas3E7AlM@HWV-bf3p9aH>>tz2KavBN~!( zqqV}>Y-`s9xGM{3m}A=Jkg(0@?Y6aP0g|qo_;)>o6~>4Rrs@M^1}#=}d;?gCbxBMU ztcd2Gf_k#8Jd&P*`7ROpigrgMj9u=MU9iZ0jH)=a@2YeUW}zd6P(kT+>@r(=^UFk9 z(w1T?e-ckd859)OpQsNDs=#>;XGuTIWc4^@B&Xad>Z4f~qLTIVjYuSgaSPf^NqecF zon$LdpeHKQXcl&RKs@|u^IXoWIYv-h^&ZAb2x#uW*##(lf~|ZSJp~PNJr;6M_0)|A z6X*S%{lVkl`L1ov3*hmBA{Y&@Is^J77jMnpX?A)vN2}0(Ity#D=;zrsa8LzL90nSZOM4-KX0~I8gf1-6#JB_ugr`W= z8Q(;+OP8b49RYOZJ)2R;SveX8i=tiHMWM;(ccY<+Lck|LK%_~=cv%xK0qZVlg4aBR zprx4Ys*%!`dzHi=&BS_l$zJeeF?T(lQaG2rymn<VU1+Tuee}^i*%$FUGj4qWAqOnsaxeBAbi>R!TD);F-bFHd1?~6^&N2q}NFS+XW`a zZgMTfn2Eim5ldT|3t_qujaDx0i3Kkd%*$-huF)7ZLNe@UbsLRFGXZnd{h(b$w3w~h z*389|*Rh`s;F^gUlX!9{W@x}-n6bRYbh$O>n;A72cgAr>su}M1moNi)gDK9o=4aHI zD$=a}bAn>AWGJlLV2tT3_EIVKX`(7=Tdk*vh!h&&A_OA^xKa>Gk|c|;B>H+Ug-Hp? zj=t)2S%#RL)y6v%xVFXm9LPgkbp?KVxJIK}G+UmnU7Eo~Z3&}nW1ZwUrOFpuA@jX~o zz)XbNO)Hg@rzJHQHAf(qB;#|@|9X>SJrUa-hk?f$Z)@n<584_k+br4r&1T6GN=puF zE|*}7rBy7sd;#H>IxpsN2Ce8|oXbl{zAK4dKg;WgC0q-rDuVYF4b%zAV_VE;*K`ZepDly{154F`Zdyo+>2HV~JO&5-(thQ8}>~ z)j0Mh85^+pd8pkTOX|<@q8Vt5-R_FdODv z_D$tPq4yRM%_`mN8P`Ujn1{O`=9GhG3NO35pXJ0q3lUu4_c-OP09lTPzVP){_9LSM z{$vdOXNcFQ_vSpvX-9%Y)cl?0*hQzlVd5iT_mKmzsDz&YQdovJtkx79|vJP7C2_5IhqCJVPqo zB*){ZsdN+4I)(2u&6~iMo9MeivFefAS7`s zVb!AH_*YPStCwXwD1^Kc5Hd=XA(RjmKTwXf!o-M%Lv7mFfiV{M+gSBYHr|52Vo?Y7 z_3KSWsvLh2w=f?p6h{pMt^uFzZziLvnxImWQ9fB9(cUe_tHSv?fb^Dbx*5x-hH;8m zpOK5xzQL@0t1KZ#B4>mounVV(L6Y0iiuh1J+R?glXY=D2Ggmb~#J~6B7i%~3<}avB z>!|fc#n*cK#+!?F91#pyG5WpNsETL-BiTAD$AY+!A_^Id3u5CX%GeBg%zv2y=CB&C zVD*O^1+4*FQNZ)MX6Sxg+ z;i~@byjBZMB4J+4&p#29FyBew;HcZ?O>U`(HTpvr5%qewP6n++FZNQo@5O2UB&SqGNTTKr-@ zg}%!-0JC6EJ$sC$#Ab3={T5F*FEpT_(wYd=Q17wdiGMqmiT zJH+7YC$|PbH%RZ*nSi$=F_(;u)rW&7K_3hnve0r@5BW4#KNC+F{gE0h6%+4>9gbMo zAFdNi8+^pseWR6XKYAnPNod}&G;+p%9)MCL!q}@}W|aN@!zHnvWUQ2nCz^VI@Dv&) zu0o!%10DO_juy+|1=uug1vg@LBi%S$7{))~yIgoof%A+6UgRXBSSezYQEY2|2vUDw zra}{p&q=-Oa2rSq&c+x=tgW(^h%v^sRs5Bopv!c4AXcogti^ZzJf_{h3U+QJuMv8c z8n<5w<+O82Y$i}p4F1)Mx}{kbcm~3SW(XOpk0OgqV@wFjIp(GGcR#buyd!|2wZA{=2o?N+CjEt<2#JH zqqn^vn5UZ5I%PYKvz;88ZLna^6X$KT`(WFCDYs z!rWr2=Zc1__`2c8U1JYw)Q5Q6pBH6$GL-kmE_e-v_bW_WqBnyVrLl{U8c>R68SfH) zJ@CjBOU%9~cU~;~0jDDnK4U5;SUi|tIINXZEuI;O*}W=a;qzGX#dH!haXv=UnGh@W z43by>o==9Dbupjv7{xjuOxc7tO?M-PyRk)VGP&ZB4-0o8($PvJVmR6>((p_0sI9J@ zg2uL?UL@Hz)Y0Blph(xwHhwxx@q6cUa73$_M?hYJh*6_fSoU_?UMTINzw{P0x?{q3b#Nh;0XvhEPjlm!xbX@yCVn>=t#n^AmM!j z!p(}(XMx#w0}91E6}(Q3+H8l%&^}&!z)!?cB=Qra;g?|e$zJgU^-!t~-zjv42@lMs zKg}K(Py@Ywg7L;bpvT?M$U@1GzrG1Z7AU@SViG#{NG+eSuXTq_3k=Lg`=KY z=YL#67nQJ6(SH2}Mf?1zKVGy||4S>+H{w62BZ~3u$hPjQt;Q%oVv0xA)A7Vne z(0pkS5W)E8&J^~AtRc6$v6tJ6{x{LrO}vQL~H#EJOw&8Qq)HLEsqCX{YW7> z0;g~|;8i#d|!iYJ2E`l~Hntw^F5DkJX{*k&j`$(HTQCdT`KK3(^Di66yim zF$b!`IFtAfKT3nR-~eir+1vvo0@6esEPcr^MjG{^TMZv9k!tT^Um|)vL<^m`-)r}s zj)L*n58XrukNwbfV(h0lsg9|mHK^h;k~0T#Xaw0CJ!Egqbq){j#NY!F2ySlMFw4&) zNGwld`0*)|brmy6wRjR~38+?e)Yf+piUzbH?r*WW7G3*@Suk=PWeD&y$Olm^v~%di zAWO4NQ@o!fJVp}Ajv#@CIvA><4aFeJe)9*!C6l+M8!p^BN5rlj1H zp~bXH+(3#FZ5$=AM%Kzv0vU3YkYW5QqzkrA{r6Sa&nl~p$Br%)brP@oN6~=DW?QMLG?TE74O;}dKS$E3p49*NC zF>`t>60Rxs(A$YdfgJHGdSY(mnBSo= z=vnD7mgr~9^cGak7)Pt#p9@ci#K?>2orw;FQfX8i??Om!BPVRp^tXJ~p-y-RtKiW! z4?-b$EVY1Eyt;udBv$W-)h1Y@vg*XG*g%0%u;SEf2wS1K6#3ACkWRawqEp_kCHMwG zQS3!(+F?D`Q(rkXA%OMlq>dqobRa5$9is`bBS6xcv9CWMl&!+?BYZ;4)QAg@$vdr9Vvwta) zm}J?JNZgI%m|*#l3G7GD#NCLoJG3}T7IfOa`~e)KFbqdZC$a-m|o zW`}`L#Z~+oi;@2=%z4#cM6h=ukXYSDlvlg#%bK1G(THK+&I^Q>63^-2p{jf%u%bog z_Ks_p6WTzHQD0;wj&B~|N^{px%_5F(q}rP8+4EuPe9@%H@8M+a?wY6_8Y#ff!Mf*A zz2zH<4-W`gST)9EM68?A;a!UE^iqSB5NI^m*P*R{J60_a0^qKyo3gkf7IziIQIr=e zZm?rddX;F5D4MT?8b|;oUd*u?+0Y(BMaOkLM?tHxPcALVU=?E=HWP4wh}tkI)N1?* z%NO9~Br_tOCX|$dP88D79>P<1dZI^p&Z({}hyhGj#O zsshzx@gD}r(!Qm|>z5p9r z_NDHi+4|Fc;GJRJ*put}1Fma?)i!tgElW@J>@iA*QpRnE|K^FsKnoJl!G+Y4vFi|2 zc$&mGtb-ge?6zcw*C_oCq@#8smSIE|6C;Mb=E?A}EV+~V2k!TH^%Bp1<$T+q^5k#F>4>6(UZzD;j;TQXW;Z0;Hx4}gGgAv3mY zF(N!$Xw5geY+`?u&A_`R(uiQa__h-?ZPW7~-S#KJFZZqQ)B$A$l(BS(ewH z+Y6q9e$00wfF%OJB%$5oevJ#<=5fBN5pBj<_D>qOd<>&Aha^sHCW+7)M{R8lA*c`?+jzp;>^rUa(Ps2d&HK1_4Dkc zN(fE%?B4S4egj)QTg@g^BOHu}CbCRCGzpjYM;V*HVxK_)VZHxGYE3oO>igeHt(V~e zW?ldHv6c^~)1)(O*DweTbM~O0)1k9(F*Be^QOMhv-1iKJ&C9mxccO?3tlk+h8z15B z=Zj9=kL4ne9Ig9-xOij60cyPGlQ>$0;1_otO*jbEf@3v2NH8~T^^=Vk27yeYGxX7j zMqMxO_ckC48t%CRv`~-663}$Er?ZBsB<^S$isOgR>?al7LLQcDHfE7X9U^}TDfB7E zNnHsB ziA^%G^(yuhi5+TU531OIh|{sny-nu|rhsK8bDbCd)fX#l9u6e>1UJD)vE%-E3lSRk5oj z_OB-P0Tr7jv85*VWfeP7Vi%d%8Wr1LVkeo{k5p`Ti9O%M9#XN*;(T(m)x`d$Vn2}B z@98u)#SQCr#m>)5>@E{~wu=3e#J*r+FIKTPN^F&h9j#(#OKg#eouXndk=QvVcCLy& zU1CR=*kTp?FRXNsx5t^-TUG2A68kGHk5PI5q+;KeSowZUrTYmL`;f%GWAeVBV#_4< zVH5k7icOc;TTE=digieAhKX%bu@^|}WD^^)L#d+^BzAy_?WtmYSe_wk^f0k@75kyY zwqQ)l))=K?UzFJ0CU&Zdy+dMOGO^dH*kXyj)5PYe*kp-ynb;L7cBI5!YhrIxv1dr^ zC=>gjiv8_dQQk96>~kvifW&s7SLE{URk80%?3X6CL&a80Y>kQSTc^~5TVfwEu{Wq# zr^K!?vA3w$DH5A$V*jFI2TJTz6Z@iyJyBvWG_gBX><`}vYs8q?gDUnTiTyi9XKalQ z75lQp(ou89Ui7|F-n%6B6%#vM#p)7!w~1Y%Vy~0fr6zWbiXAPnb4~2yDz=ZrUTR`D zs@UI~M0xv~*m@QFmBiA4TrSa1Dt3#+Hk#NIb}Hq4RART9*z;6uxx_wZVy{rKizW6} z6FW!6P7_$)5>{!ciW)3YNsL;nqE3;hOBl6YMYSJdBlz$-KjPbF6?H(Ms>HZZUC9!o zRtzk)vh5#2Q7jX0#Jf*43VzcvN7FtmmCJc6PWAj^TyH0euvH>CW|d1-Lf|+SxRM3N z1_VZQ5_rN<1bT-D2Emvj8`D3=I@3Ush-GZ7CeaH#n2cjfSst`>Q4$sx)l+v&h1Gc6 zJy1cBN%tr8dfuBh}hXk z27b0WvR*q!r zhZP^BV7uAW<%Xax!&#SY;>8Ku24)#M#AQ;Z1SxZw@&Q2!eNC-uaHyKhy>+AChNIb7 z&7?gjs*+0gM&dCE-@_a(RI~CUvXZAr^=}t!)T;4b1F}$XOFj)3IrNAk!?YPG#L7)| zxy0H0J}ioAuDpkgNneqO%85WZHgVZAL~bzT3FMW{*!u}5rb55X#3KX5gJ>(x)q`H8 zphFIP3{Z2~cta9jI^w$2zk|d`I&L14cO=AZWpTz{vl6LAMBq#j?UKlmdjD2`yBdLu&*jt(H}Y5?Il|q6L3yiT4L-0UGUu+B`Wo-f$6- zY~L>O=U2)C2N38pJ#B%-Gdcr{ZA)z9(z1OIlm^EOEqDhxK`bD0(4Z5CbXw~$LYQ#H z&7^(*=5ASNTY|uw!7oWsCdkjxEg* zp3gn4zdY-8yNH68g!{T1X{YQ2uY0+YgfAnxqTJ5D|HjIvh0c@5#;=lVp8rHBV>aHq z+K62R+HJDqR^d3b-SMM9()QeFYOpJMBJJYC3$W0Sh=ysQBwXtT|V zG@QQe%^{dxTtGVQ(&4Z_c2w~eMDGY6PV&ZDbv^+TeB(pYCt}@(E4gX=r0D7d-)Qd) zRGcBUQ-7i=q`d)2V%u;s&WBceOM)bavCxN?J2ZUYGjRNO$Uu97C$ zIneuroW{0eF^>0NdzaU&{;ss-NAgwk$eGx74U_>NFo52{10A&CdjXg0oB#{bymwK} zP>MVbKI$=;jJ^d7?KD7t19G_GQgO2#H(oco}?{1bRosqmx@fe~UPD)e zbdC)9M6bFNW)Q~f8(z9tE_f{Z&XwQF@5=aaE<+pyGzv0+?UpTeQ%C_MY;`fG= zdj7_HU$ouUKpj+TP$cgdNRbgo7a2n8`Lfe4Lapa-I4$8zLx4SD)%^btvoNjJTG{P)Y@bN@EcmnEhezBfPBZM|k2uWBL-;b*iTSjsI*C{9e>y)vPH-Lsnhy;x|5<_pY zmNa#&#ON+=jH79F&+RPS>CTI@_#}`C(t(aRk&n|>E;e`&i11!ympRS~$?x|IWPbY* z_URA`H;mZr+Qorqyhq7_%Uu$utx`z_LoFcPTPdp~9Jz@#mVU;aC=eEUO%LD7=>DdI zVo>y(PHHZud^&d0i1<4A0NZ=t-@j@;&~Ve~Dx~9pkf{4!cz}WYbW%u|h0idoI5{8= z>KWG z`0L;{$eaXlj@}IM#7}#vyj!huD3$U#~Kr!K;iMvLTQL&v<7UoHm+z^BF;XU)F*wy)U*puZ~3BLz;Nt=Fg|e-GEn6gAizmu*t0{&usE6+ zGKMwVq1waVhlyLDhovKoXe&nN9yT}s@MZ?1e zvIxEy8SE%jmvTEA;JpoFK+AKjBgtvbpv8Fl)cT2V8EnboW6K7P?{c;CVNYHnH1P2# ze3B|BmXG>;g_R66eDdu6U@tBCE*Gbem4a@AK?cF~(SL9A#5QY`hk~vPqU(VEntJmY zhP~N_2Nrfwr~)|YEfwJGzh(d7?4Kak5M{$?|HjAZnf4~o!W*cJ&5w(M+IhJMiLsPe zB@)Lq---vShPrW70`e$w97ik;i^RH{tp{k>M_>OYlT>EJw;$*mPY0!X@mX@t>m=kQ zy|O=zE(7MrsTGokx1qZT15~BtxHJHcY2FD4-2;4*0(~kQ#-^HauWtYnvvj3kd~g$=#w880Bq>y+yzZfMA+#2Z?+s8)#TzpG<}b%rTK4nC z#0UPWz*itCtKFCfqq!ST>bubwVGIXT*%pvR9L1VEBf#o5lQ|I-3mgP(kU~#$v5#ZGaBOa(?`gE|bf& zHn~i`|bkqTFh`{AhPf5m)@3Dl9CK zYK&wo{I`a zQPk((lG*5(2DkAS2gR<-W%{{Dfa_75qQAP)N^gCj|BRxyo+vbZ%dgD^BFW=iST#~{ zQQvr#idvOL-pEen zeo@AFW6vv8#^)f->u!O|!#ESf6SYlFf>c^@GakVR2d+gyq8lgtU?%WK2=^!9t7z?5 zKqmJSjO`YEsCb0Qc5oVbm2r(|;5prrjL%LI3|wfDpUE7dCwJ0BehRS(wU0Fhj-ZxAREQT+LzC^;d#kTo=H(z#H^fE>aS7f4Hm9~O4^mUf6vBpuh^;}@-br$rF zcy+1s>nxDq$I8c%UuR*@y@zFehGoNzy&;ItI*?zGO{zL=02je34qlosfb9)O6q(k*wVSL!;LtW_ES2!EL!`$=sNHhiNtHK6r$)wWK0y-Bb_4_LRgzoA?ELhtnl+n_cO#q zgPjGUIU#5^yP*`vH}ohm#?4~b$JBHRghGq)6c(YN4O1Zy#u)bo5-J^aOcH}BGe~F@ zLIZoAfS7-B>Q7?u#ZlLXRX$S@d?b;+tO+(6Hz1ta%8TKu9M)Qld4kJ$p#$G~qxqm1 zkf>o>nA!h4)m+0OwymtZ6G7hjsCqg?9m&jrjYF&P3*eYrcz+kZ6wxJ(D0*qm?r;imLApui!|j}R4@twyaFNg-Q%nA}Phm1MjQPex&U zvBtDewP+x4gujduW&G(mvUgUREG570ORpZ`+Z4SFEmV*%D^R%8xG#^DcDz((J+%wP zdju4x!y?!#0gtJ2L_p;~qbF7@8SWQ$qa73ZvMn_Wc?QTcHf%|?Qgrjhm*J17;B7MTK>0*5NJ)USxkMAF1$k4(QVi<83#uU^^9`t<1t19?>aXBcpjp!CNOu#Mm zZ_$(3)&FHlO{}N3?2v|RJO>Bz!a<`@)%W|h+qPjy1TJA7e;{PJct^AtJ*}5aD3MC5* z2W-Z_b!|o-Yo$MpFfuL$Mof-zZ^dA=EBC84cH{GKsuBC4N&`m3mf`nN-d^~VGrP}%HjfPM|5Rs(cM-(LE%4pzUXc- zP>O~ZqTB7<-L|zIv%3`{FJqCXL8KhUSI%hNNMQvpoM9*ykjyIrG`GXQ z=AV+I&KBgD2I7pY9R8kS6dYR#j7W?PkHdrRx*koBW)O&wIeueqSYOX$?SUAUlx@uc zC5_u-pk!Ag*MI%eMbX3DwzqKrizH=R&xWjx)Ri1z6#V=jl642m`eYu&h*uQfVAVRm z0GRTJ#7$svZiw4RGsz>U*6R<+`X^pPDBC(Rs9LWhs8)*}yVLlrlhvMNamHNIDrO^& z-yZ2zN7u2$3GaLNQF7?}gghGg(=q2RUv)gMD}m@mm61RDNHl9W84@@47wU2Q=v)_{ zprbu@fj3}zq-^Wq>qLPQj$B~(vBZf2?|y^|{G^aa1)g}M0uSvXycRSMB^wInd5HvU-+mOa-B z7XEJR4v?V&=m*qj%KjjA9Z^3XN=+19Qsbkt_Yu8r(qGNv`l+-(VOT1Y`pGXkL_9gK6KJMV%c~0?mIjZH3^%TT24%HzPysn%rwBP=|A~(~ z*y*EJIQtsQJGwM{0gWBQ8?1tj-ntBjoa}B|1*UI(2xOe7cAQGZobrI9UY>No`6pmV zV@ktuhkC?$ai|RYTJ=UfdK)+)(y@Jp+42ELBOSE5=_J8#Tk`~1eGvy51lpH*paCQY z+zlW5{a^k|%90yt%V6y{K*|i0pmKkj^W;E!e|w*WfQj zA_4p9GjXF6%B<|t)5wf?W*XC;^a(4_BvQdN?9}6qS81rkI0Jx8`z}SvTOow^bC`MT z=+#eh8hRM@n|5)=+6U2UGG29VpX#^Sl*n#;t~p=dJqmUL#2sDg>o1(BRjCX)eafG)}!gXZh6N9AMr2|lEnjMe?| zUFyaXpzg+H@S^Zl%I)8G(J`n($ZEFruv$x!w7bD}Z`t?50f(`#T0o z0r}fRj)03l?M$B&MB6s@8T%Udz0Zxr&LBUYN==7bMN!ngzx&1#m2U*$OTM+ek^F7x zZnW9trhR!$Uo7a@m-j7U#6cCzfQAzNw6A7g$NsPPy$=($ z?lZpJf+u6&?+5mM`F=eB~hDScgic)=tbl|Uae_FV%_)kKGZ^prQ?hr?+3{)kf?*#4H6$?foUposa_cW4u{vVM^pB@t8 zZ1>P-J}q^ei|t+l0y4RIwckh4;Nuyx>@p_tnk`46^U>xwG8;%c;I32ZhE_{~+FxRX zx3`BNG*FKLCkmIxX=c|20heEf=!yM4 z!&t`tV&z^qhJFt;=-tpN;}77^(s6;31;v2{jgn=ONZ4P}7+MUg(Qq${@%r>oFQt~# zP7S{(8V^4Dfy>t%D6NNS_U-)Y=L7qSVyMTD1?E7UZ=S?#`Em>T%mZiAPlQPOvYAux zNnc7sqYf#Vi9Zh_+#IBtReCoOPAVfuv`7cRQ+;tO*xEWYrHr15j6YC{%f=U+NRFD{}7 z?NV*(%oz?1>+Q+29kVoNQBgsWruCUWY-nEpV%$dNjl=H<0QNrfM-R`N-#=k!#-)Lu z1x#X}-@kYP;Ya56?{9~tC#PlSIy3C90(-I3Wq0N;%`Pg)&vWLx(sJ!f(~7dw7Uenz z_t5$mUkaK6dtO@p3VT}7Vx35ed-TxKGcL8y$#NDs?b*fl`~rJcL7vl|kzM3WcNG+^ zAfezUB9hRbIFhcLgnTI}H1Bl1s0c#s%L3%%G`B=eTo(7edJ5ZkHz#o3GVQShv^{ES>k7|_3XAm@H1@*O@h4<<~= z)2?&orc)^aB?{9g1nyS+O#1v?V_=nvND`Y zv(s5xT1G|@EG@UlnU=8v zK9HY}M&iuSCZuHu_40MD1!R++m7NR8`Pw9Bu`4@2&6Qn{Z zJXr!Km6qCYfxbA)UR;=#?$nZ;MS0o9#l)WB%+Hp&nQBkSg-$?laROo|1&W8V73f7M z>LR@u9z`Z}E=MtowTVRq#l;s(&fM($B>{7aG}a_dvG&vgSO*~@1O89#ZlWkYmrGhC zJD*Cjm<>9u*sgL<%U_z7o1GDeJR0Nk)AA^EttfP|{j{r{E(#v@Y^upj6ko(77uh;9 zd$CS7n_~vCf7*VI#>0F}Cf^%G4ke|Ck$QF(m)Qe1AtW78? zSOV<}vkRSQ(ypwaU1m9p^<3$OtO)rCJTz@F{52E07nA3(&9rIo2v>GyHp*1w%tMpU zfV-t-kWU0nS!@q0fjZoc4TCYs3NTEPyA1zK3~io8Vyx!LKgF-PJ>dS)iP zjYE??Z7GHx+%U$AC~D74%gcu2Q!d4NA$6KC=b{x96-12*0xeL3%P>}fO*jBRnPQHx z=sQKEMGG$y6%S9#FUY@G;sfkli2*IxP8T3+XFdW=CN*`z0E_7^j$2C5DspCGkdXoZ z;~pRo)=@D?Cmk|f%is^_Lx|1{_Z;sQ(QrU22@fYVg1v!E=R$>8LgDD#hln;<%ppNK zwx-Pt7$8Itg&t{&88S3*;G%9zDIvAw3_G=DjPJ}o2E}3DU*xnGr)N1cbPOIfvk8gz zrD^}j_xpce?)RTk;P)runu#k9mlxO5xOU)*EcE-&!L#pdvLvq>tkF$;_9jU{ey5#!?hS!Ij$|Zi2f^i z56K&8eW2fHT-V{!rLII%EcJ9^%j62!=sCbs@a)F*DELMKzC+RsMA`vd#Mck@ISabb z^KUZkMBI}BpMk4drtQGOwPgn@QqP-i6Oyuy`MyoA{_lVN_s z0{dl`*)JG!f#8~Wq#1}gjzDNdUIn?j3k?D}LfjB-2>u^Dn0|&pC-jK9gu-L^xL!+0 z96D_HB_l?T8lAQ%J;Rx~I4e76Np4<#LE()>#V&p6vgIpoBEBHq_z4pyIVMk;I_=7< zrq8%~X3{mYX3t5!_PV+AuGhw1ojiT|<=VV;Xdm)QRi4c>^*&qTXDD<|JoovN-~V^z zdQLu@cNO=aKHsj$JrZxcq{*HQ zGgO;6b^0W%Q_Rw?n|AfYsao=^DO%D@aL=AIGfA5>drp!zaV9dGGiTb>_?elkT|0-x zB+Z&RS(`gCDOsC4c{UFF2L8p5+jutqwA9AGUJ-7$|DtbFuuLs>r4_m0pLuB*b%ck# zg_H`%lA}6`e3%d{?Mz3h3o(NzN?QRUxO8D>8cGe)q^1V5Po->7&g|=YdwaYaJx&xk zUfvTX(rb$J&vMf&&9c&J=@SzbRe|Gqbefkg>jKQ%iD3Lm*7Ar6J)>8>2MU23i{uM| zU#Q|EZeRjC(r&<&13cXW{FB^~5p&+FB_RWlT#D-v&=|M}Xr`@=yfw-l9nnxbe*5?x z<4K?$=;v^yr1U+)kSmk~%8qLeXofre{uGt=YIkJhR0}Ig zG|Pd@&-D9W!E-=QD$6sXEPau@9yA@G@ez%gXT+g^eK!NYW3k`AlJI8zkPMfQVMlT! zX!>PgtP)N$Ge{E^p=q-~lM_y}C`dB^H1~n#v2dF8L7M5HiOcr;UkImpLeP*M3qkV; zX#6;y7$}pdpF`ck>SsM@b&R7fLKyU(O8MVl5I9ieLzY?1o%Zn2RO`pU>1kz0^HzIOb36WPt_jY2IR^ygvx2Dj&b zlr{=!rzmN5q)kU!B+?Ry%B(NwkRf#-nnKXnKr<$cra+;&8#L#GW^5Qup+ZA%#a<4Y zFIAc;sQZruksay#LDLSJ4Jr-!hx9R`(NL({Fow;CZB(5j7e%ljqKOC1UW^|n;8~?v zD)k{>oeG+tL4%J{25BOS*;fgl1^kQ^7;CHebkP_IUjh7F;76+Xf*}4W;0u7qsjHxT zJ&3OZejV_as`#QH-T?lIqvUUm*0h(8f*%0vmP|FRho#aI3Fs9h9kEh0e_^pYzKZ9@LlywcCe2F{t)mu(i$u$=`b#+Lk4J0 z^k6MVrMV_(i#5PU0l$s#rY$3C0=9Sz_@{txQtJYFL^cTD8UVIB&}4c2{sQ#*0eg$F z(n5@71LIyh&~2c77W49>*)}dl(>?=Dh~21MQ-by%3z}%m-;X4R`lW@SNdQfV9M&sh zlBrfK=qf-rA9U!Z`JWxX>ydU9(o#`}fih9KXZK(wNPZ1yZoS{{$IA>s8Xm||i$K=^y2GHmRh2ba%&Q{x z2qF#4S*VQGlQ2HR`(Jj|$0Fmny_0_pAbd5(;;KC1UuUs2;m%x+IYf(hIs1|k&Db&p z<>(p32C*Y~IcOYD`u)%2IY2`;s}?pRoz{b9H)vi|Y0$VSM%j_J8TbWH`TgIiI+K5V z5sbNwps_!Lz7gXdHJ`|_(E(keY??Od#jsciyGKk8>M{T{D?!uM_>y#)3jCjer`Ih5 z`H+k(A%lD?7c{M)F@4L9v{IxsB8?Xo&9+Z=C=z^R`^P{t;w8-C)N%^jPgUnZL|YHq zw3q$KZ=YIZGmL9fM({#qvuKW(Yu$X-2}P>JO}KQCG13Uh68^a@J%Xy zI<1>T>^7G|?7-asy2)>1Pe-MrHt`oxDO3-opxFZ&Uav7@h4Ae`;lBMGXusZsxrypS zf_9WjOL;edw&N|Vy{okFx5(6BZ0G>Z#J7)3LuIv}g0jAIbej>~RM5Q+x^g@RY{t4p zEH&dY*|rdLz1}^t4^{zxCh$k|_18c%;TSXxpveKvOjRFhx61gb12p#>LyjE_gl`{1 z&Lq$nN1-A6WP#?Cnxos%4!SDP(HBpTruS>0$vz4V<<|h3yN{6{(P^h@+PlY~qk0Z;;;h(=_c4e7kId zDo2@@jRXGGBjD}eSqS{6z`v;SlOH}B)ME{37QT1%*!>h}%0M$jm7~lj>VU5Teyxg+ z5bF+94*IOze*zdC>)0Q{Z6A8C$075Eo{Kaze~z`q0h zd8+*RLI0=#e#=q#p8}r#((94<>ww<_{2Wz&#Popv2Jqc?bd9%z$BIsR4Dcc4r+T_l z)Dzk0BGBZ4rmHfNjOoCy1pYoe2ige9SQnI02%1*VY*1+;UJ!<$wB!GdTj00_j$7dW zXBLRPGo|`;Txa7tA6Ejd(YVIpqM}U0{Tf`?<64L-6ITwd0$eU!H{&YDRf+4*xM&>F z^k7PL%wGln71b%#6LC$!H62$Hu4G)-ls`x;(86&o4DS=^&YOBxIV%~`cIjN6A}ZG7vb!tZcj`YJZx~n#fdtD ziK~Vt3{Dt6P$m;~=sy~QYgH-LHry;)q(*n`L)M;ol)QW^Z1-!LK zO7$za6TT65*NG|BJ_)zro*I)cbheGEmyI>nx&0Y5(+J}l+U1YD)ylL6c7 zQ>tl9V9~m1RL*gLiJo95;Ea(e)!QUL&G{b&Kh-PM5AA(VxJ>vJrIY~PdT9{81#sz@ zAiNgvLkfN!;AREB1Mul%gY>3j7w}JO!=+>{a0R06(q3I|1)d;Ew=Djtl0u7w|a>ybtgK1^yE7%?f-7 z@cjyG0DeP(e*pZc0{;y77X>~H_~h}y{5t>-QD6(^2Qw778{j1hd?MiY6gU>}Aq74a z@MRN%^3Mc3UxE7pUZKG8fbUh{fq-9E;K6`@Qs7~LZ4-m?M*+TAfiDAml>$!yoUOo9 z0IyYGI>+$10$&5zufW#={%CSAzxja2ObWsa0pF$Ioq%6f;2gj|C~yJbT1CDK@Yf2w z0`NRV{wl!B6xa>;9tExd{Hg+10{&Qm?*#m#0^bX`rz5C8ox2#Mz}0}KDex137c20y zfXfy5CBR!0nC6#XDexx1zbo*&fcq%+*ampC0>2OVItAVhSXbaZfQyy(O!34yC0HJc zCs!!`PVwY9g`VQc6osDRiCcjiApcQCUy3gy6@H3mcPKEe-w#yy4+1`*z~2Gxr}!_O zOF2uakDmblrqKTr@QDii8{lLmKXj?u844T?xLTp_0eFXkw*h`v!S@F2QQB)9;5~|d zeF1-?#49`CClz`+A7fGI2LL{(R^945Ad^qX)lEOzlDGkufg~O zf(gz9e9bKMj}o5?xMU9cH)KF~7vTHnrc~2j1;MKTPo9S{lY}b({{!%MQrHA4>07ft zyK7e1-B94u0gqjjQcZg#WPdu3v;i>fhY(EuvhAUiY8-I@B>cttaL`|dgT6(PcPZdj z1t$NBQ|JjE5C)G5gQteUbHm`QFnDs%ix z;Mc&9U%=6H!#@Fw+Jb+SMp-E}5)%1em5GNi{A++bx<}F=j{Zq`ky4&L0AtWg|1f`` zTO`Jt>c2=0e|iZbM!|n5y;p=qyAAN5K4SdZ3mV=Dxb>oxYC5Yz@cn@GOfg<3_z}Q+ zN>i$bNc@w4`VLm7EuK~V(SV}dWr6B&zfQv3nsiyNf1eZeP_%tyeA^1Ju z_s<6&rXqMZ;BC`_`ZfXns6@L>~>gwHWWx zzB<7t0UnVk;w8bS0dBn@rFxy@KO6AE#h5orxIf^xkYBlkF9bXs@LCBw0Dl5|(%D3k zmjL+mJd|I;BLSb8lTz)F@a2H#z<%dRcoN{#?3fcv_)5U~Ey4250KDW%%vB{m8Sp1M z=EV}e0dO+v?)9#Qbl!vb9|9b6NlNvV5*~&6-g#M2pC^EihCk#< z{PTd%gFXP+KWhOe3{9!tE%9{LFG8``Zos3?O{u0cER^48fHSU2situV!CwI`LH!p> zd^6xlSt->tFC+YqfZv6Efad(7pZbE%|132^3FDb;jFl;}?dTzGLx^-PIB3-E4~ z*R=n6fS1RoR4guepGip}e%VP5Gw) zehmIV>&yhF174M%Qce4e1TO*n(vqNkZv;Hxyp-xYB%VSD{XG%fw387ZRse5Z7R-M& z;7w^*JCXGCDb;5b{5rrx2d7kDE%DdFezz+AcNg$u-6_?y#!dR%2l!$5quCxG2D}pO zbDYFK2{;A(v>!_JIS5%bh_3)KB(iAF0skrNX|^x=tZm(hl#ML9>B@KFP89MV7Q(cDb;kwfb{(W z_jtJ&)7}Zu{{;9=*zacvw*$UF@%NLU|4b!bodNjwK`GU9 zCA}SR9LnR6aDTu#h%X5e9t3zZ;)iLUVSsNy{$}~c0M0JNTAK9#34o_A5Bke=zUR%%I{FfGW2C^} z0{pY6PqY120IpQxdlle6!#-wwxexHU)3BZ>_A%>Y2jH{N{>}FHG2lO={!RR+fZs)XFP8H51O7MS(G3!A27LC?Ab%_1zr&ux zB>q>xd;10ZtL^ZgQnW8KzMxCit{NGPmnQ(e{QQ*aFC_oTfFDNxR3+g!z#k~_;B3J2 z&|cZNJV?> zt|bAkgZ&?s^5+A-4(-kCUorrvqCF0f_-w#Gz@JV3n*%*w>Klv?#lV-MJ&cz0Hvx`; zznbx)4Db#8Q>qV2Je`$28UFa9gdYGr81*wj!qtFp#rVV2=Lx_aXz!-~yZ|^6@nnjm ze;x1xXg^I7-VAsx^f%jAE#P9s-#!GK2m6`+wg>PT7%zM-`M&@>7yfAG-w60GO8e3> zT&eWEhE#s3NlVYmpeq$Gf)*DPrDo@6IG1Z#_>2YKMK$lK%ZjpH&QM$`-sQ{)OUlFx zs9I`jc5!O4vk+f|fDC}?^vQu#e0w6J7!Y0qOm(Jb72q+mAUDHV1U$Z{lbtV+^f?wt zP35;lQ&aJBJKm_r4PUJQ2R;^qFTiDM*jj3kbc#4(aMMiR$J z;ut|3BZy-Jaf~325yUZqI7Sf12;vw)93zP165_aoI4&WMONiqV;<$u3E+LLfh~pCC zxP&-{6UT7k7)~6+iDNi%3@47^#4(&Wh7-qd;uuC8!-!)TaSS7lVZ3Kn~}XZ8{c{xo?LiCpE0T<3{g>4{wHiCpc8sBN?%@fEA!Q)+ScO-?Ex+CH!@w0pE0e5c15pvAkC z`0z+7{4>9RTLROX%@m(ufsDoUi61R>%Ji8N#!pY3IeGGI$DGtT<0njaq(a{EQ6mPU zRizfDr@FHA{3V0amurPO+Gtvlh8NY-3s-1*zI;VE6R(kL#W$jDFH6gI6=r7;MP5OM zlkTp9C8H($fIyy*KL$S@0!B!x*94)iZP_%|7h%tm7|7%Fsw%iHO(Th>~nTV;bi^Oo!o|RR;Qwmkn_nySpXySP?^^ zblm5WwSp|rXP7kiy>%z$Nr%*}u2(*P(&vB`XvINt4?kti;<_B}bDE-5&R~`*iV9^> z%Ef4&Q)PRWQ2>55iB1(uOhA%y3#bdY0{+9F>q1VM!nCLiU`K$Jlpj#o2rv^6W3xzQ zPIcWT28BLBMkBDtsn6KZnM>Pm~7u$8kI?lMP|8%F}Ht1`k4-$*{u^jvWcBifIZw844~fz1FYMeSz1|-2rHUV+PF5v1y4h0>}6XIduf0 zDpX*<#A=vNReM6$Z(&}o2RE*Yw{^eD`m2FuV9|7)Gw9k-38jv<;&lCIcnKZ1BmwuA zfIoZ1H=AF4;S-k*uQ=nfdnZo&a(_s_c1}LwzB|0*EPmVu`uTqN#y*`k{Yt>?bCBP; z(PD@c=c8wlA_P4tR{U}RdBEd&Buxl<66)6gUjsazdb~E_ z8GwiQ6I};AkWEh}-sz!odanN@P>i3GJ_AugY7q^@p`}91S>O@cl7&kyt!{nkgXQJ^ zi|=T-K4(tBr$F_8`Yq6Jfqo10TcFwnk$=?DB*@S$w@V7OmT|SKBOvHWK^fV7nu)Ht1}K#qzYT&j7s7wTs$c>+QU0 z>9X3wuD#)m%)O|3!{T7OOZy@gi*4Ea8`M6a&@Np>Muj~cp@!Wpc3Xq3KD0lqIKrZK zt}X0wga$iz3g;{JwrEhx4k;asl10Jx)I9C2b6DKAdiCY7(gA@>Z(X*RRED%0N!)U+ zhQ&>0adoVg^4N>l;N=wpLfN5!qpLPAIrwqcs0Q2a;747%+w7sggrZ+P6%M+ILWzcz zj9`mpSnaZH8i4Q2O@q1VZT~>JgR9vBSy)L8DH@l% zP<|HVyVL=|*dv4S?ogA7>VVP;sUldd33X;zX|2t(g*>hMXQk9-M{>gRU13Fgi3m>o zEv!UqQ|%^+bLY_Q`6+eb>Nv>>$YVwaKs8xO-!uTfAtgPmq>ob4Sd%35#qdZfI*J83 z_6v7dNvq9^g*<87>NkH{Q~iYqI8C&3r{|095C8*H8z!^fr<>^gB0F+1x9fasS@rBuk>X=9Z|6_!TYtWjYTsBl^<`4!P?B4bs zJgP=|RF^&&4k(=<5s_8F>P``WB|6xS9q6#8tHPd6bh@npBcNVOcJ6ElD_z`WI+5(M zCJUK!XMM268QRk`VNmI`K~Bo1dtruP3mGq?d+S`>a957faocf2xpOU6B5Asnh+SCe zoD9+m`~H8Sn9xq`*R9dE1>-1K-NBYZnkMhwpKta#(4vU1AF@Yp1@;PdU^!k-0>RoIie+(ErXv%c3uh*cQ1yboj() z2tJ)DyZ<>N^uefx+6*|gF3P#{OsXI5P8#}P_ctR(HLPhU4jmrVKoP=Zxw53B#9(Rf zn`HuN=*aHxMvRJv_LzZi)ZWksAx|_?h^7tNwDfjpGeNO?FWC#Ig>G6Q=&7 z!}B{{;$d-64oZ9IO^l&Yhpoe6cj%2!Lv0=+w<}n0M{Mq1XZP15LT}Kp>^KQ^Dt5O2 z8FswbxQL3TI-wJ?P^IDK;NLD1!EpVfba^5X5C7VoOk#mchpy z(DmN}&^4LWP@o2SG%y%7@HQn&MKg6i5>wudl0qL*dd)hDN;>d_?FcDdp!cDHk}5m! zrT&&h3quDq!s3DYu#4n7cfvg!w9=7!s2}aH+BTp~!f@WQ_p2(hWd<)j!N3(#?nAg$ z?g}ezUO&)!*!>+tJ*<4c*sq*GJOL@Kd}lT*I}k+v)_i#F89z)gd+2cdb+hq7Xw3y9ebi?$30@?gtUO?~AEB zwBKL~;gAi6wD26pHJuWyA7dP+Vx*j zH>dh3?!$+oZ4R8uqPxk_Mh4Cbdoa|a93Y|`Ali=Nv{av9K^Ck>3;_wzW8W6k+`&%Q zy0O^Xg}0{PhApD)@HMfP;C#&P+Dl_h%=#`-p8l$ zNJLkqvDs1do4;`{gp`&D!Ut=3sd}NQ1VV2zJTyG53lzcX7O`RAiDfMMfYKBffPsd{ zd6MEJv;1V4R~j;#B5qSAHVt}xtIVcgbrWPl7`#yIS3cESG-Xu)BbU`Jqmj#Rxhghz z^tN3=x9Hq3g$_nliC}9^4|Yy;{^(XLf}}<34U1wICmko9CR9B(WA7F8wP5+yA~t^} zRWqYD!uz7G?XKRa+Z;Ijwzj4{SluqvLA5~@B6*r~{hbM!U_gcO2X)A^u(72d-8e9h9RFMDZN(Z&% zFl8oUsuok5Vrt837nF@^N0ENOz$echiRv}gpO&d3tPR}<^NohOeGmVs`n4@|K1JtF!^8z@v?F~Sf9^jJ{=nL2|POBLYMJ_?%D)r2` z1)ML42SduCh(X6jL4masV`@iCJwyth{Z~Wb$nulfH$*OF_QMj`J72&~v7x(whOpAm zO$K+>^Pd|Ms9l!0hGo~b&tNZj0#+f?9}?o|mUg;E5=_m61Ey)Qc8aJkm0=q}2^;KN z9q0qJB(HW`6yBJp-NwepTpFd&rWgK!+=Eu-Q7i9Yd%E@{jGIy_`338fnPsZZ63Z5C ztjP|Q}X3ty<9gguV$%ghP~LCB?V zBCuOzBZSjTMlzT9ZxOQXt>6@%pBnbKc40CSVb!rX^wrxnOXF`- zkXzjZJLNR!tp&OIH6*#(Ayhp%`7BxlUaMmGT`g+2eU9Z!p7tbiFi>BDFrBf+n2pFE zQ;|}`j@_Xy;rwQ#5`_-+2b6W$G&1Om+ISV*p1Rthy(bq(k$CP3aeli?8;Lez;y}g0 zfivMHa{qAkcTi%}!?ZwFIxw6t5_N^hP}k8oqCwDop^~OO(MCosDg}Y1X<={!HLX_0 zs=7<2a2IkOfb$a69_M}aGH(>Q1g>W0Ttd6Ch(di%vB9RcgEH^TyQMe=A@ICJLJZtI z?HZahX)vfyA&ugEM)%sd>tt$QYNYkka4<-b{>gUepg>KDEDfA6WoQq#Beb#V zaO82x6Je!zg;%ITWJEm0JDQde-3gFC6#>dlyVMEbm91h^Lh`1pr{xE~Em5(O>_Cu{ z^98A)bl49lF4!OTbqN&4rq@4@&9QMRBJ*#dHj8ae1U^U|RGp%c}vkf#Y& z;5X`Z5Mp`WBsM{&Yn0LyRN8GPmNn^Kh6x-@(EHr@zsuMM=SDhP%Z^Oc%N;ZslYyOE zKaW$tX_P{fi7199H{xd$<;JziipyEMmx!?na&G?xzd@H=cpzn<;9K-d>qgKvZGxS7 z^(pnL(S{DkBr7f;wk1HxE{LLUDJr7QD1^KDlE>ygc)|BKYpW;wyCsiP&H@_K4rUrmUPMHWLdOb#j^5TgIXoA(^%~6M6vOjpLe3>)2MlB4fhb~?UGb-y*ZI<+y)O2>5aNR$2k7d z&Y2r?yP;kn1)Uf{a@&DKXI!JDRCl;u_wmHKKkT8dW~n=Z>K-lP*0^OzHIr#LuhLHyZTuO4O^-pZJn6cO~vy6R_0T*USNImuYc zIv`9r<@A(GDUBeGN{mW@Ovqv80Yo}l79)mZyrK->1W(!%ctg_gxeBkGcODEij5=sG z>4wGHWydIff4%~a=rClM{Id1>Sa8#H@H06P(t)P24yWRvEY8^aJ;M(}3V-YoOH+ezB$+u{IK!CWX{@CSe`o+?k&OB9ad%*fNlI z2O_ll{bQlSShFxfQVx0pCsqxzAyR#Hr`e>LhD@{;g5+@>cp5@xr8GESfgG}clmijp zgc^b^Nam?;LQ6gTCag87g~O-TTkrvK5-i(D!D<9qcH#rRHgNIr;_4q49*X>Aq_jbbDhQZkKJbzwkER#MkqfboRo znXMs5ariSMSf(RZ!10k7jz>F}JQ0RRv_vL=8p{wH79sjeYGN%6Us?nJ8?S}2VvY~( zLu5o!e!k2Ms73@z810|%gkTxwW=!UF&cn?Vm2U(Ss7j+5q0Sg96mkz?p2B*EG9t7z z;Zd#+j{igrt8JKHF7q4ME5G{h&yQ@~8ATI!-J)yPbBv2z*yoJMfd+ZTxD_!>alU{C zTWonZ30eq2wE5EGDA-b7*{ocy2@MI>}P5F!nN?G?f{{=KBpc>}2Fzp}vN<5atvdY*ESgs}9MXtXC)c zF@nP=Lpr+V1=7mX8KSX=8Q8K)PViRhw4WgpmU8pWNU3;JX-6-6joM}QOQHwx#B6qj zb~DvK7xj6cr!Sx;8N=ON5s)U<#S)z~(KJDqPQ)S*gD4Vh-fbmpLO6vCt7KkA=5!ekM=B zd**#IKWvUCdpgyNm?I7@DW4*6NrE};x6?s&h?_u*Ei|G-s4qnxF%8et)6@S+)7`2Y zsJGjxKKFnTK&=h`SR$qAFhx7s8c;%e+;M!uiR5uRBqC2^Yd}7O{Fv|NpaOcGcQGzL zg`lL?F{p&QT>;Mb;oU+Mk;h`#bAlHU(cFTIM97gNmxe0_fzwcq{1QbvOk2s&9XE=iLJrm{}2K^?Al9 zZ%5}$R-)=izBLCg>>@9st2$i>rMgi)T{Qd9&WKL_{t&54bixZsp<|1o5ImOdfkZlo^iVRQ#N2*4dhTlNX-FjIVsZ}&(ToyCDP%9>cZuox!EQ* z^aib7;z%6DP<5;(P#po1a>kL7fY7$>$`|knxl^NBc%|E=oSNqAE^xc(y7$R~mYXA# z=ehyvrJTi$q4n=}B&-z;HwW4&Kd%j7@8%$v0TY3%hIvL}yEF}prrek;o&pfiJG_oxt6< z5P9Qa;ER2TpgR_QilFpTAMKELLHZQI*F$=3g>DqSJ)DXz-m#1>kZZsD1m-NiUIu#{ zL(A6~I&lPdK#&6b9`Ab&)piVlEqni%$Gng=#%4t3P3hQrtOLhg=G_q;#ZNh-x9wv@FVl@*ax|A4gG{g zyPJ>;3CWn!_k0WXPv5d9CmW07k71t8vPQUu>Y3GG*$LWBezPCPHcCgX+4)P}l zad$E3Yq!x}8K-u%9x&9nN2;NQ6K)G8&dr!sH2+7D?8d*MiF~zdWMnWYfVSeE910B; znwODcjT#ZHYsQit)*?f<2dguMiZwxpjV>dm(eWH`V4P7^02%~?wy&fK&^Wp_hCh>xs8IZv+bm~?{mDY~s9 z3sH{QOe(wfEHWeBnZ~du&2*p{PYn|gjNcH>O3-2)+(CH8TC^O?nrwMLlPXWi+59+{ ziNvv$u04X2s+8rqy30n{Wjj{eVyZi)X3%9)H4XQB`OZXCbMk`E9Un?G@7+oq&g;$r zgT5yYZ}ZZ|!a*BB)sJ3>n+-Jw(Iw`Hl85sH-l8G38)*G-9xI`q59fc2v59_8Jn@I~ zTJL_-Rf^}{B#sr}$cx+&wZWxrxhU{p*nA94We`6L;)tc z#vn=)szG#aoajL&I!6*6sDBIp&IoRsqq8V5O1omI;D|Lq5U;&Xj@I*!Lft*}*J8gw zb8Yu~4m%MIPil4dK7RQKOne(ya9Gl~kJ}qRZ&tL%uI6Q(%?aB|w^mE!^NE~RWg72r z5J$8|t+A!?bsmsz2KjXu4O1S0{^Z8LMy3!|q^N>D`CO^4MT=xNv~trR@bqQ5oPH+D6B)c86~6m8Uw?8ckc_-%9Jk=IYM zutOV7TqAS!rs;dgnA#OnJDY17;50@6_Vaw~vaBr9wZG!xVao5QvPC7?n;)Gd%cZ?a z`b+Wn6Y!E@)F#CG?{lTI@vJLgX+^I$v3Qk)MEVDjX} z(60|rG$=H<>hX`Sa&^$M8ux27Z5{iI1Ep;nQ|Y_; zusQ&vGPC)x9>P76qIp@1US*anXsT{WY=%SI_5qrqkX952bT6fm(@6v0pMsYNeZl4H zn`>HNn#MPde%ajUPo>p`3z+8g$W1zC_Yqu+aW$VyUv^n`q>W3EWu!6;8VO4X1Bhq! zW3)c$)FTG%tC~e4hMub#YhJtqcjTkjXkM+cou~c%BVL=r`>-2`hFu7de0gWe)pR)55jhJJsh-t9MUcI+3r34s?ImZc zB`p3-i+JbOOR-iv5Ih8B=p)ratj7rl7g9z+z9=2IYTUv6TMe8YS9GyF&eMK{109|WVzMwPpFa5ng8yU+62;vkjw%prz+aRKcNbeqILrDqT9ClI=2Dc7`VdWIJxbh zZlv}_dDQ~p{Yf5vY1+O7zj@kkWvYbcc3cqP^fFHAao^jq z`fA#|Z8G{gFihKkWB&uS^?I>FrjU1*;#*WtNJr8h? z(D-5RIfitf)ljco}ve&jtH(2VB8yuqN&ALA7HeB&*g+d8{tv3jK5mF00q2n zh=%hJFQT-s!R{lO(q|<2IOku@Nb(pr)*7j}sn66?Q&&$TZ{(C?v7r`Hj@X-H?sI65 z%QPZ6I<9DL(DR$~3_~XbolK)Xwp(-uFh^-$du}&MXi6wsL(dlglC^VKt3y+%18~0w zvDXzDP#au=fi2a)Y{;39_zGH!sT{t0BoL+>_i(gwKrd-Hr@el;oxD}D>LL9 zjZH2BSNeNQ?OZ)*AV>`00xyqW*@LfOVNVd{NC*or z!yhN4E)IQ|aAlirg5QV4YoB77-EqpX?YRl;z=r>+1#s+%opLI6awV|fkK}xVBtNWZ zc46}BdEeTB(07u4=pM_yRkHl&%fxc$6jqT~Zt9I?-Kkk>Paq4JvI@*7)@{PgT*rvb zPS_h~QQ@sSrh)ZINl^N$os=u|Dtz%xP8u9yKut4_xqa8>5cR-BYNF_o8n@!kCi=fj zmI?(HPy#YA7b`LB5Nq>B;JWVIJ@^BAXc*489a~a5GkX_*rMtUrxb2PL$0J6)@e0O( z2)+CM@z9rp-VZg>qBZq)tF#1*YL6pXm$K53ckO>u(uw!ZKb5S-+7uGib(P*UEI+$7 zigG__)((kyX!mEsF>STr9|w4eo3#7=k3$XTAJ#3*u^cV!|MXj+-va#>=(j+>1^O+} zZ-IUb^jqNnQ4371EV{J#(iNAEy|nDos!OL-zF;K|LsMil-y7OT>qm6ms-%=)`j$9pVvKD=LePb~Hi%k!N6Zv0wt|JC>V zEqr^cJ4UB@{O(xnDZGHVsE<7K}D+`S)80gudH zZ*{cHX}iwDzk#;v7gJABPmEht;q#93mitSKD#k7HR#sG%1}gj;#;N6{YrTE|8^-y( z<$;Q-f=YG8xU$j}MdPacMf_4w6)5xvvWkSSu(aHL(M9eH@>hBNUUzAgyS&1^oJyCw zOVskBKxsvJ)&)Wo`d6+!nT|@Ay9@pP!VMtumanZmnTAT^GzA5uhZJ8xV$k%`8`Hvr zUrm`Nt&pkzX3zX@wy(+?fK0Rv-=+SF9-5~kmb*a;y=4U<7A!27?O7ni?0LD$Rql~h zg8|Ltm)v;^sHAxC;OSLWUOySdy?kU9OjKA}<}J<|eA3uERo|TgKi88d<}Jt<*L&vW zi+Q=ZB7f2JJTZO7qI{vu&lj`v6mcCD&+sf3idV zc>20b%$%D$OXS_SNZdGY!OXeBvuKXUTZr0=^B3lc{KffsV&*~!%g>*;0AC9|;)Z-? zlecK$Y_Vi!o<}U1z4&JS?{82a*)}c`w%Z)G;i<{V+p(q2Mp*`)RZqoYII9t2ZmLbp zbEVi-F~gapt{>n^va7e1?MCVJz0+TtiE8}c^vDD02Gqg5PyS@$yBJR?%2rd!^wS;N z>@$X>D0d_UlkEk2r|+Ns8Y+(g>MlG_f$k3E@Pwk%&Hho+9m&CzB-Ku&2bdUW((PqD zw%^5KZb|_d^hxP9;&%y(nh5_)EH=ijsSS*7I3|@1BNl8PJ zU)u{Zq8S64ub+#>p2oXLGb@;6uVWI{DGxNaKOc)dW2Lb_&XU|Hs|0@N&RFbI{91V= zeP}b~+y~qt+@PDA(v4eZm^rzoS`tEq$Zjai0oxPf{CbgiHxzclH3K}G=5@;$cG*QqDd?gl} z(v7ClLX(Ef_6X1%vC_a7?0=F(Zj?^~O(SS_T4~4^eiNs;1vG13jm63#)2egQ3Of@K zpBm6Sk3*Fi-T184^~pf_qo8>oH2-3yu~%_N5`G`>S$kr!(N=ts>}SHa0-po?L@T}` zj;BG82Rt&daXr;IJ`;E!@ICnv@t+0!kNS{b4E());I{(*ufR{S$`8clKMMSdec<;2 z|7YNTV6E?o*KY;>J>XYb@%92HApMgu&d%8ziem?Nata!U@2=V_d@OJ|LqXfKU;6~Y7 z!2b>SkF9v}$^VS|WE3>d;0XT1?lczPPQzhQ$^KaEFWqV0lzd2+NuW9BKrBWv+w^Vw zRw$j^c$zQF`lRcuxULU_=G{JMNRN8Zd<&YM`i}4|z|T9_ zyKjquXe;nOs~&bQE9yp>8~EP?KNqpms%O#^8LvskEYSSr^;m2s-c6c+iT7(U@ZSKx z#EQ4)nYP*r{AF*%VsvC=*0;|z@s9$(4EO|F+2@-0eZbcOk5i?1{k3uVt-wD8d_s)b z7{}wk9wy!f9+z_C_3d*^{+Ymc0biAXUt{8D0l)CgSPaW7Oa0jPy!k zj3LnMNYG`wsY^ESh3~{-?^y99XHR@=lV`C zKHxXvsE6k|W4%-sx7TyP-wFIUEB}>o{2|~U0v^{BE&VaWY(od|dx1~rZ)(HLxUJH$ z_xvkpesASLG>^yIIR!M;?{=HJ?bn&@yao7Yf$uFY-v<2C!1pvSxgqyq;P(OleE0eD zNjZO#t?EJJIn=vvw*bGc4?O-edg3AA&#|`idTC3F{chmz1^!g)lewV#Bj^&=ePpkH zi`%peG;{yl`x=Sbvja4xpy{n|p9OwB@ISKZYnO8X)o%d)DDc}7@YQCYM1h}$ziY!E zwOM>)fr)pe!uNo`+KRWV1+syE0QlbY%LD$`z+Y&szf{_j^s5B^$v*1u0De~=^`8a) z55VIuv@H7Pn)){Y|3xo&H>yN|zwj^J+E4v-ZQQTZhQOyl(^DIX#{}SufqxM1X1pdI zcg1-u0nK61B&-kZzhga#$9mw)565DhtTDzo@$gF?mw@JB(Ch=vjn+6J_b+p8#=--1 zM0*glUo^&ItE^*1(%#g4uhRkA(YSpYLA?b1kH&p9{d6I|1kL^3Y3_{EOaTpD!%2wy z)P@<7CLMK`gXYJe`JuH9pm76t8*s;t^gSLP0ZsDJSd7|e>bhL|6Y;1AJ{9K>n+b$M3y*zqcZ<2PNFos~UdlwN#t3utR7&^ByU zgiR#j5hC3J4+HGR-vuW#et;mlObbl;rFr<5#8>0jj@lRF_xFJ59Fbt!W91~(`P^Vk z@C3j&C8Nzcp3XJ44XE?c*&pF&0InNc=X)Bz1n1&+!D)5AXLOiyC*K05Gd#lI47loa zqi<|tIpBW`jl(5?uR0?R(_XC5LQnrp$w~`M=LbtIFrCkNEbvyqJ_~#&;0rDCD0lgs z1*Sdx{TBFPz+)`%uK+*pjO+IV;HND3-vK`AisPRHe5VEf65vNH_UmnIG z((ud=#o|7|hQH^a;IM@s;UBf&34X2{yssO4$O2DAg=yedgL*bGKwL)*0OK1l4uJS0 zVL94_=%`6{P;nA8<`2f7gmHL?&w72FCkYyb_~`tSbbf9V#(@hy^(IWMj(?JbLli=v zS!t608Frgk2iP%8_U9SUa5LbyFRJrVKN3uT5%}|K5ZiS4Zoo4q*7?TiFrAnG)B-;Q zc;Z;ZEc{aaI{Yrotn<+s4#90`;+Qk*e7ETJe}nSYA;7~lg#SI@_b;#W&Cv1mkHD%| z#O3V<{Llr6$2$HX;8$nX`6$j%{X>Ay94+G!!E_G)_W5U?xw zOz$ z3wYRsIv?fGiGC8`HjK|}b$BY^k1wqAkwX!FHsEQN{=WtAkTG?>cXWIqV85k3D*+G7 zz?2l`NM zk?<{mQ+`nAqw{2fQ=m8fyB9RFhT=d{1AY(ulj1(%>CVF38FBf42kg8A^RkZb0{mOZ z+o{7g4B2Y*ui>AA0N(_Eou=cT1{~Gx;{tyd;Qv5-4S#zX@MDOdbS^{k2Sc$3Eb`9= zK5Ig}JsE)g7XQcu{1Ez!<~gcA2Jo}+Z<;p=&IWuL@GEtAJKBHwlsX^ft_e?fEAEEB z8uawvd$=9;|GiE>8}MWBCpx1b`UQaRhdt>0kYEpBMa1LF8o-}h#^+kVAB@C$MyIa^ z{Ls1a_HF~51OA5owSbSK{YL*i05}`t(-?nsfS1>Au4820%B@BsLy z0e=PfVfcHMuFp4sKg9Sm;^9E(cNgq+xsD$S_}>vvX6W!!=&$YYPh&it4g7F-osaGm zko`vhu15UZp~Dveo{au9#>W`ICop~~*UbG5_|q3aeEFBd4QWR zUO0);G7Y4 zzK?bM2Ebe3@6YIP81QMZpJ9)C0XJIW;m-h{j`1`{r>_J257>w9Q;@tT0bhasHvH*# zfOBAP0~W=Bf}&N0{sO*)DGJoeO0U15xO8P{ph{G&DXlCh3{;ega@DuO>lfa_;$puj zE?bLz-e%0@DqNe^WfkRKgF@f}>`Jew+E4}Bm1U(>0Z}j~cj1ibxdjVn&t9zL7vxW$k*gG--ug*b zX07xF3Mz{V0;|;WHCaXLMWq@btAmTbsB(i)%k?{?C50W!HuTV>bzd|i9CVbgCFe+W?^;HHofDaXw7ps-R=k@t2)_Mhmqg|rN zi~qn>Nm)gOpGt7+n+1U%pej*TPV}So41wVpjtHolA-ROT&b zX+^92qDTtYyIHL&Tv6tgT~=CET3#Z`Dpsx+>-?nwHeYEGX73W+cxy__m|sz0K)}8w z+-PbwH6Ecg0K=pH*sV@r(R#d^&=+-sWW?}w9^uJg42J{Sj6m>22 zZ9zdHUo|eEE52CWI;}6|#uR(GwZvQ+N3E0=2IrG6a)XRIIZ$C*UGI>} z75+7%#P4PIsq|MA`3kGnU|CSL!51i8fnQjie^(iA0q=SQpz?}pj+K=i?U;SypI zO`7@#Ab-xs_dq;$JhTt$z;7lVI)dnEt~EY{GahcMj%cre{L_TMl^QV(59vnD!gV6n4L|%zy$kDO cLh2vlMW3N~xUCG~zty=zh$8)+ehj+*1xI`p$^ZZW literal 0 HcmV?d00001 diff --git a/files/bin/sleep b/files/bin/sleep new file mode 100644 index 0000000000000000000000000000000000000000..336ead5c33526d678269aa18be98dd70c22c6fb3 GIT binary patch literal 38084 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 b_qA+#5nl2qDp0a1h}h-(5?`kNEV};$l!ipt literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_abort b/files/bin/tests/t_abort new file mode 100644 index 0000000000000000000000000000000000000000..bfeaa334fb326c0d975f6a268bc7f140730db5b3 GIT binary patch literal 43620 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 E0ro*~rvLx| literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_gid b/files/bin/tests/t_gid new file mode 100644 index 0000000000000000000000000000000000000000..63e903929f17cd3a336917776f3fed842bda82b2 GIT binary patch literal 38172 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 Ie3*Ry3o-&gI{*Lx literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_mem b/files/bin/tests/t_mem new file mode 100644 index 0000000000000000000000000000000000000000..d002510590c43faf6959e7c809ea929588ae9ad2 GIT binary patch literal 37604 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 literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_periodic1 b/files/bin/tests/t_periodic1 new file mode 100644 index 0000000000000000000000000000000000000000..490bb35312ff218ff01b23c29fd6604ca2ac2386 GIT binary patch literal 48076 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 J!asDr{{=*T#1jAj literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_periodic2 b/files/bin/tests/t_periodic2 new file mode 100644 index 0000000000000000000000000000000000000000..e82564c622ece6502d2ace2fd5825f920f878185 GIT binary patch literal 43656 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

Qhr=^l-$Mi>Own)Z~~{7j;7f`TIF^H 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 literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_setenv b/files/bin/tests/t_setenv new file mode 100644 index 0000000000000000000000000000000000000000..c18206125461ffda86c3041517fa7db3b3622262 GIT binary patch literal 37984 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 literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_sigaction b/files/bin/tests/t_sigaction new file mode 100644 index 0000000000000000000000000000000000000000..954b5bbe299ffbd6e7663919712c12b20b93b234 GIT binary patch literal 43624 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 IUnbxG0HXdD_y7O^ literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_sigfpe b/files/bin/tests/t_sigfpe new file mode 100644 index 0000000000000000000000000000000000000000..02879bad78c7399e30a58a699495f25e4542289a GIT binary patch literal 43596 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 Oe-!f=c~4&^-~R%{inxFP literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_siginfo b/files/bin/tests/t_siginfo new file mode 100644 index 0000000000000000000000000000000000000000..544f5ff43d45f6c6e8a98ebe9d2631bfb2f59238 GIT binary patch literal 43600 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 literal 0 HcmV?d00001 diff --git a/files/bin/tests/t_sigmask b/files/bin/tests/t_sigmask new file mode 100644 index 0000000000000000000000000000000000000000..caed7e91d984b0068b0a643e52c5a855bf870e27 GIT binary patch literal 43600 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

}L$*-ZdM~B!#EtVa8 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

_yWPOQDB4Po;X(@Rh^}|@? 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 literal 0 HcmV?d00001 diff --git a/files/bin/touch b/files/bin/touch new file mode 100644 index 0000000000000000000000000000000000000000..42a40e242a9b7a798f0eb80eda66fccb6204dfb3 GIT binary patch literal 37676 zcmeHw4Rlo1wf~(-0s{tSK+q^Cg9Zx%At(wF)bMfn&;T)H6hCkX$s`2x<;>iWPy(St z+DwPE>C@Nhd;Z(9TAx0pQ`;({RZIW_Dr!{PCQ2Ym)Eg(3RAWhPblz|8bMMTZB!Is4 z-g;~OSJK{@yU*Td?|t^!XP+)HSpEI z<7vR_aXiED5PzcUp$D?*Nya-pR8G%rAA0lfHQ;YJN=PlD8HXnh&$3kx$@9eGzufuU zwF~E7{>+xEKAq6JeqQtMfg1W7vcQl9hAc2-fguYFSzyQlLlzjaz>o!oEHGq&Aq)I} zV}Z}Dm;bXaBRSsJ(H*|O&*A^@_BFTpx+bdQv7g^@uw%O<`QPx}WcJ<=SQ;OYd|j5z zI-j*U9L`j0r%ICVkj+p&mCMUS`93g^IuGIl6ut&gx$c2X&}3z*$%$OKra`&U*Ogf3 zTgz3i6IHq7NiMn4Q1S;Z8KbeM9taggdB<;(Hc6T4x@pYVFL(H=I;6^3u>|Rv>Vg=8 ziqQ}jRyx!QQOu%WVL^|7hdJ#dL3OU4JMX@CU0qJ!!NA1igJd+A%GYI6SBynoI9_d&J*7?Z`S^*6nN53E^;f$H7mkU|x$ZBf>9LtG~YimdaFrFiTjw zN#mv!mxx(U{=SrLSw9Z8(?o= zRbTqHk@&o8d%REXvqKN2pyo0~udk|?DSDZr*B&v~Xw;It7Fc?|-0S@{zNgE*QKOfU z&{JDs1u(XlL_2mict?phbnd)n*9QA!pma}OHjmwQp@mwTu;d*LxXXiKl~4){rrc!P(a z44(wUPmbvr1+D4}T?pHvS|1ZD|H*BPsIkV^m8vczJ)Zuq)<{j{VY!=!skP409FV(B z&E(_w_EmLHDi4Lyg^-5cIP3lf>)z}H5RrU9zV_jNqyXY?eCL$E#ouV89Pw%`NJT@0 z`pVu&lNReT(OL*Xiv>y-vLvC|x*u}L0zwW%eCcoYwVM!(UW1le7Y5cvMlE z?^z6?26jkevuJ1pF~=lK)?3ww0Wn`rEWZLHHz3zApOxQlzBxa# z^`Z;V#Ah~Z+6^A#LRX%1M&v+qkkZjB$7WbR*%t)_u z%$QEneqK&(pb)IbrorOjwvaK#ecNb=x~!qrA2F{j)tO1@cjNbY65deM)e(NXCbw9k@Oj$S{FJIe&SdqFC!zZmt63#fVeaF47ANE4m}wZ+m2hOv%l z<{*KW*u@g5wt`r{;MH~Fe(nujjE3>r4-VGHYd=h#So;~AR5=drQ58+Z#|u8Rf*gk# za-8Iy%*#8m_&@}Ln_Ks0g?R-TaM-Z?_!E;27Be{h;k+whazwTIbB?|bplC!Js$E94 zPkHo)k?R^%q!tG+^4t!Z#da zgCY`n(yhztZv&%6jlC>F4=ft(U$V#L#O)_hZUX|C7PHj;UGX6fP7~ImB8_V0l0ba} znvdYa;7IO(&KFSxeJpjiXd4ad{>dmz7nqi4ROSGkHgjyNpCv*Few1g9pmgcmjBi*@DBu1*c?-|rL!3&Va1}(`YIgu9g6qM zU62@%yG+e~2!tQ&b0d14cQIdG!Mhmv;VGyzU+(cv@fCMt0_`SO(ui7gBHA(#k%GCU z3my!Sw3z7jUVw66gQ*FP#WdD~8NrJ#X3}9GLOD00Tf*?jm}-Feo4qpJFEFL5>|D8iSb?VOE=G^ztrr&V0FBnZk{S7xs}C!3)7wr9L7d zjy{@wXlGQ_B`JRl?T&TAVp6ER1hnv28UfvY2S$s{rcCmY=@RSeUqh-O3r6uDRF7TE zR_LsT9A8zUq@;nhn3S8O)X;P&`aJ1yMzQ&u>z1cRu>MzRVu&JTL?u+mY68^}AbZH8 zrT{|QYUR`L39(b7T6lGw#(J-~2hopQ7H-$Q`5kJUVgKUmN>YD`?Nq{r+!$K_);hXs zW!1W0?#iS-9fnT!9io65ng|nttBQF>VEa_HTgP&j(7YJ2l)8)A(&WvDiUZno@f}g z9s?ORhT%9sN7lA#1J$_}b*MX^1%S{8`DF4ydap(v={1^bu3TBHxFj^&*9LSRw`ZJ=*^qjXv(8Xteh}=4Jh@j19>HLIrK+^(H|GAuw<< zH$lf9b>iPS48UcLuFC8lVs-^!M+UGyNb@t=G73XZoiF%?s%VfzP~$o1S=2&kk5D8g zue-Fu)~{KT9^}BHVNye zF?+H;#uB;?ro}|u8)MqO&=^=Ywx~YRJ?dweyAoXBN9M0T1i5ezE$yiOiIB?)$(X{o zi&r%*+;Jc+6$|7KV4m%wMxdJNts{@R9cHzu3q#aPHdNG>P}t%)di>3e?PgQ6slksd z9s(~WA`&`ML{d|?Pfg{(3zR5=F*4OFE@e|-&U}I5R7}q94idLseftvUFaGY&pA7iyL_xuQqOYCC`iOup z*-%77FW%>x_%))jY1=S-CU4UgLVw5T;A%Yj_Q57JR)}A%e5y%4Eszvo(#V4JDASHL2%DqIf!&jpZ^57Z#Kue>e_d7ITiuGK)oICVMHjOkv~$J85oyDG z);qc+r77z>+^)%yDkuv)K$fHuEuoz?S!04OQD%f<_@5$FRD+HffecWnKL zmQ0#vbt`uV937@twY3oJhDopkO=yc@5ZgM`_8nS(_r1=@FrlpElUQmbTwXvUh4jW! zIN(U~ePFM78y;0HJgQF{42R`jkVv78v~H!98)jcDe0$Jg$8W&!MW>sZF#;OYc-|5A zahGXCqRX(jj*!v1vEAxNK4vgN+6hBW!nS{b8GP+zyovo=r{y!!B=Ev8?h2e z)2yUztg$NHdg5GcP&*Q1Xpp9=e@5PRM`NEWve4G7(YXWTh}Pz02`qDAi7r^;SPCMB zZV5C|UtLZJ{Dcflq-#z7bzRhV*$Kxw=Q5Y>Tg5cAe z(D=_u{&$i1p9rVcL|OME{+4O9o{zFD$j{H$S(?wyGKSQD zqVelV)4Kf!B7tz)LI1m0?}c)(b{w(o?*By*y4AYtCSv%e@#2) zA_|8!9eD9K*VY`zcU_H2klb}xR?+QJ^@~)x%sWE7$jZe)z%wwRFukwAE-DIqf#Sp@ zys-xqf>UOld>l_Zma0kNXGl``84|3HPoh}}k)R!mdU590w-rmCr1^5k_EAmD+~q4y zGKDlCAEeXdBq7ITtvLx99EeP3k(d=D?@?akL*8+OeOgBIz_2w*Gb8T-2RpybSCu4f zGm@l04v@YBBq|e|2Alfyr`i%I$%NE0<_RnpWtasEXADRvrtznRb2~yrN*mj|^{*#8 z+gi3CbsGOM$^R-1%bo$K^RYAi&#?0~q*BPWQ3~7^*SQki zEBZR$T9XdoEWO;{ufn*E&VDsWKcjnbfNS0Pd+s50%l2G@A;~zpQ>5tmUQC0L!c?(O zYd_P1oB`%NBmufO3D8AEG^^UQiBAYpPImqPl5|$r(prI{F}9=aY0ZkU6A_!w&FUa! zZw;D|i$*=SNjjIp$YUgJ#hM4sjzp7TbIB|AMW569*9q6fk`il+vDIIuSac+N2$`YC49j`L`La52 zhK+Kell^`}Xo>o(HtJ^gJrwuhL*1Pgq7Hsrl z}r>P`z~Qw34_7qgFuKKtLc~nC~55XYQ;rf`r z5BY7K63H;2k3`W&fPSB5^OdT-(~1{nHX~L6Lv)|Bl>`z~e( z#3-WO0$O+jwXyS2(NKHmBX~ifjSA~UVS4BH@j~5jB%NA79z~8L+br1X`#LRcwCu}* zDrAznyyV`tiOFP^v78NOS)(CK)Wm={x(AZK#1AvkAYp*I6nI_$Cw9IH2-5?6k^;Sx z4P!N$@mOdo3O6daLBXRG7O*}mJ>-{*VL3JQ#!$Oi?-Na2hBsv9S?ejxki(ACU- zd<=W*v)Cy%_ZQF{z+D!Dqd*3C)ib;-CX-OR=u88~5C*W;d;)vH6R-*q{ty#KcXZJY z{CtVYSm=#YZ)>55YQKXrPjvi_xQad?DRnj7@WxDat2l~)BnoX>;TUo}Tx{&0&&};o3@!C)FGQ;M<`gH{xh{@zk~S@OH5hBeIU!zh^Oaa z$qUH^V6fvr5jgIng+*W`)O#&Pz488c)ZK$|V^0W-OFo!iAN?Pli%J{mSEyNmxOmj!r0*wfo3Gp)wR&l*{O_ zY#Seb^4hv9ag#-TTeO94TWHBH%pSEibqYKU(-A6;^x_tkHdoyEHP~%?gqGbh9b932 zYYLIm-g}aD8o2x!r~djF8P&5UwrK%y12ux*f|)q^Am>Il7p9OnyU>Fdn4&8z4gxS- zLQ~;okxULQ4$^Ta_6{N(n~sQVHt)fr7z9?_)-@6$9Av6DiE~XX3ZF!B2B#POPb!kt zzrI6SznH~Zay-y%MDi#-rh*eDBK2VmHXg1DRwwFAWdgFp)QpHxg@ULMniNeOPp8F+ zf#ZJWNVkhA=WSEwfLE%5tqz?HE~ubI4Zp2b=xQfIXM`)VN#?_{jixNrloTwxQFHh{ zn%H1b3=LQmIfxGAAjl=EQ3zvi=LJXC!)K~&e#dcGf#0atL5M~FacLW5+NNPmf&=z5 z>yB$)hGhYmp!d1)U!bMjc&J^hriSKfiEmnsg7)|}*qPU~La%DI08!Mv}3tz8xF)R>$%#N zvsG)PYI9JH!pu|X9zOn5tOStk`JHTd3xX zsCj-3ClTqt2vWiIu2`;78}z&n(gIDNQyhP3KhBN$o~|BUgtdf_GRNVqJ*v?@sykh) z`&ew<`vLE-KVP_P3vV_AlEBBv$nbiD>=R5spu zQJevd9h(n?hqMEmk&LwaR5M}>Rulr6beO(DH4(1ZCO2yt3FIk(p?;z=q$Qf+?IQV$ z^=GrX03i#`j&clFgr_#Sof4v3MvMB^o&SNVA#+=cK!@_%T0qj~d+6j52sM;IOlHrx%R&|82i)(6Qd|FuCUP2PN@yf0fVSc$2MTo++SZX`ttt_1 zZo>)-)*?eA04GZdI={z6JHl}^=>Z4E6FpAQU{rQ~E=(9@B@EeuH3}Q@bA&HpND^`O zw(?ySEf$EoDimoqTX@*3?nkutK)mQ~C*+6x0sQmOKSwC#^^nPiNb6!RWC0jIw7 zR?TI**=2h+nZk-atW2cDuQCp~c;4T1t5!}w*`paa;@~0&qxYLEq3>dj)4&lyNm3@&`NDAP zO-XeRY{hCN3qRmW#CN8eE0Qd{9MFj_i4x6WqMLN0aZyf}Mv2xk(ItZDaKlmjYb`h# zLTAyYNZ1v7H%qt~jwY5z#_r)2|1CJJ*l}RX3at1yqv@|#3p(0~&{BUdLfC*-8<|iK z9Yk6w;d-#(u!MC2!ctf3r){#@+Sj(Ow=L#os$G4M$cGa-E;=|=z9fk_qPD87?X9oy zfV4Bnuj6T$@(6UtxBfM>fT%*(6Ta#r>$8rJs^36-aCEd5cf7Z*ueJCi!ieN5mdte9 z3|X-EkSdFNFXoD^T^&VgR4+f58ka}axG=(()wrQmJ(m&@oy%DX+C&eoMD2S};-AZ6 zO~?7=b{cka2L(^G=x?nJt$oMeZ+!(O>TXp}G~!Kd{i?0?#47_VY*DWvu8$H|vaogy zHLUc7mEN}MdN_?9fZaSL*&_27_2w7vqVmRmDv92Fe}F8T+C=1OASV(_>-(+fg&0dG zslOtUiw5QRzoo#U5-aJ-F34uQnzaj+A(;er(1ENq_rHf@o%-zZ7l+ZD`e zREoXC;A;?S_gAgpS@unM(hs5pJL|tTU{Ps0!cwe1BOqc+9$p!TDZWh5xoYu`(=I)f z&bbO>j%NYvR4kY)41I%9O{n8lYX9-JhBR%GV_PqJ=x_b(2_+_~hnq-=pXwDfCD_X_ zSm9-2^zpIg1;XWV!s-1^#N{_5Qe)Sus+-wgEIi{U(q~(XG7O_KxvfPD;SNF3wys^P zvP2XduWFBNhDF_l1W`hGNvxOB$mykl?@qu=h(6%*4Qca7dBwp zl0tWCn8p*>Y}(o`r4O?xJJHD{*g8`g291CPgaO2}`YBqU^lA|U!P3?ywPNVm+LHJk zxD)TcLYKI>JF8wMOJY#wNnAa3kts=r4q^d!6w8T0Od z4FQt3#0zeu-}m)|F2aUbyBLj%AluJdZKJ)!Z1wda694+*sCetntFcx)>^lZ!=ufDH z7>*xFaUmt}#Eaa6V}Ty#-=X8oNT*{1g4qb!DVijF&0wPEDwMzj)JW*echsm&Aie!f zCgsdvSKtAvASmkQ5HHG%kJq>j>&L(r2t>(EM>HdK;TOqMv9yJJJu|=>rya0fc`%UBOvVx^wxVD9{W}i4t6^-OOivgpdH1F9bbU;jxhJq{}_qw8J>SSnw zrh*SHH1tp#;Zd}VzfrjY1-x(Q4rC%;bkn{DyN`If@<4(=;-7FtBFUrNSYxE(raEvR zddI=!o5>py5s)v})xxh9a5jax&!Rr|rRWrqWv|py7b|o_#{`}CU)5-_-J;A|n%q@> zX+KJ6N+?sameuH|RlNGrOQhA0ZlVrA5)ZN078+LPOCslBHL+;M_6+|Zf*nQpXA(Xp zp>}l`Y~DW)ovhx^+|t1<;A;`RvPitq*yJK`rMpw=rCQKHkQl}VF1-N%ZUPk`X}`WipDw}lu)CEJiW46aSYHTeiN=9*U25|{UdBk zJ!&uqE)?da1-&WOIDFN))jk(UZI#q9IxoFIxzwb z8O+z4piJz?2Nv73UAlC>qUEuE*}gBj>0}SShJ|e)$|*#-4hM-iSb1;2VaS6x**xbCiP@dPS@Dho7lIww z@U>`g9E_cOK6YXy(DMSh^1=Dc4=b8|SQ8AsZ|%XaM+WGJ?ip~Jw3}I$MOkh;8_V72 zXQ}=c6MDiGU`DZS8}i+jN!zWkH-63**jb+r)&p{nTdB(F8$=Z^9NQu$4GuA&#;g&J zY~R&YMBNxmO%#Jt;~&?{cifutTiNoP;F!@&mXG3k?GkzlSX;{p{{7 z+^B2poM3v@_rauTukI(0@xS%XNB+-7yyI`BtySXpjM8E(ss=G@CrsAm-SXd*bmr~J z=aaQYT}-0(rfG)5^0Qx~DEEVA)eFSKjh{@wwAGG_yw7u!8sGWA-#ocRvoJ?Zv~=ii z$O1za7_z{S1%@mzWPu?I3|ZiR!2&mzGIFFO@RH;x z%ggb)JfO)d%rDGkyxm(^jH1$fdyc)Ju-sK*&o3--rPx>G+r1lI_PkOcOG>@2ib9Xq zGs9l!wdcBB%%#u^EgYVl0@n>AB|HASrAqEb`-1$!B3DUHvFoOh(nx7aYK0{AYmbyV z`?kZYqA};-?8@2yQBrH92prmN&oy+I6Ugt;xTF8I^6&J3yt=)q0VsBdGSAR z1>~{lvr2o{zm2$W`<`mxd$zhwn*4>2hQl)GNv{4-9V+Wzw-R-dQGSC#_n)FZ(H%3; zr{TSSKYUx5`UL+~hxC1yd)F(TI`zTDsdO)md?s@m6du5y`}ETSxN~U z%?+S(Ri9#<#Ffoi__!HaeGs8rQ)2z68lwG*{{gn=pxT1Z?vzc z()IRyr6iYq99M^O+y&(W>8NzQJ;&|NsZ6;-a+Q>q4WyycD2>xedI<4O5`z{O-Z3sP z@}-1yYCW}|zogW~|IPM!TwcgT+wfWFE*+$K8e+X2q|jSbkfc?sol70KMY427hJ1s4 zif1IC#r%@7VilF-jU2hq<8iskDE9SJJTOrXMk9rb^YzuY#4mSbN-J>Z@ixbbY-vSC zhLoMPFjHE%C@Wi%S7u8~GiB*kDqiGRBdw8_-7c-UbB&a_0>uk6vQ|pjGCmw@vS!oA zHPYhcI6TR`BTKqt#j3^2B}djWDRVVyugS*6s_Zq{nbP9b5SE?2Vii7CJEYsQnN4Qa z>ZQ`!#hDIi?b0=O@qa&r`o^>+OEPV>m?k8~$Jb&rI}c?O@jU)SI6Rv+x62bv(h6IG zS&6u%GrG9!LFqgfBSqH6gKQY1#a*%7Hf2Y4CyAH?7V13SvHf!_oC z2S)r7U!3{RFCV0~5={kYHU-1s_xjVk8KrpyG?RZD4*#V;&1-^&+S&k`W1u11MQye- zqH89|LhZp2rJw%Vx-nacIRXBzRO>yoG*$)MSF4jR&9IcWBQX0W~^{6^s8e-{qp zd^f766190P@RNZrHsZ}LCa|OIao|gUUyk-f+6g`57MNI3l5rF?|ML5Am@;?~n(szs zbOHYi@Z_5jyg8EzsD2`H$)5sGNz*8PaRfgD_@qDdvz2*y1fK!?a^R83j@B=a$}a*w zANWN^{FW$w7x1;fg4!>cd)9R0n zI)g{td(m;R5j5{MA%4N`Mmcc{5+b_n0?pSr-iz_qRnTQ=RF|hfbNx}QU5&c9MWgK~ zYX&|Ocsg>3$RQpZ1rPG;9?(1i8Zrz0QGZ%+8};8%7UM6XKbPqJNpz{8d-*(ccF?T_ zT{q}5@E*~F?3y0cV=HKqka5@bpnCNvn}ISOUxxn36V)W5c^WkHK!Y=p7~Z7@nikNk z2F-$gG-U>wKF}0^=7f<3{$V!E!FK$fWik#t_8MuZ9Zv{Gc9bsx%^yHhV)RMs{|#m) zBASh$`Kq~J><3M`rVowDU7(rpRyh1!yhm(i_HgGCeh={Fz)v^gb4A}1{wVM}fFEqk z621%ghk!rZ9Gr+9@-x5>=37*M2Jmm5Lw*ME@16r+1pJ4W39Bjj7vDeRmPX+!o@Ru0t-zM5X z<0BJz75HN#j=oX-QzKG2+NoDo$0 zuPo4>=?q$Ka|X{Q|5w@0;A426#PbxMr|~?8XFr|;c$)CMhUZN@$MCe^X~XjYo{#Z# z;rSb$zvKB5kK}L$C8s(zC?#ISK07t# z+LY9pvlRwsZ@VTnC3Vg;t$1JyXsaZleOTg4CMiy$?*{yWHJ;(&{JWW-T<8o^PR$I8 z8TdYAz*GHZL;ZPx?GgMi=^DVv1~?t?sqxO>CVUgWTkyT|B4?0tyae9{_?;wY&<@4~ z-vRjZOPoQ<*%G`C@T7^(AmwEVb^)GmcLslhZ-Ptltpfh723O+yXOm%Pjb9buQOSB= znj|0K`tL+xKj32q_{V_%d1Vy;uYgU4`u`623j=;P;L`^9F~GwO^rYvDS4H{#0q`n= zyypO?8sL`zHyP+(2E1@ewEk;=Wdr?NfGZ93bpDrYz_$Uu!GJ#n_@@Sboq*E}^e~&W zHY@5M1aIsISM-Bx4R8+Vuein;q`VsS&uPF%BK|NOCCE=0=n39ufO`RNUE>Un#W(q9 z90-~X^+y3dW`HLGetf<&NVyJ@Hx2L=3!Fj9YY3S#I~ka)qeuz+c19Utb_1>0DQ@H&fp>qPya>4KVBb|*9iEL;m%;D zhCc#0K}LMk;A4OT5FJf-1}XPM_z>Xx=Q@Km8r%)|6Ud_+ zDBuR%;JLG%!RZ=)3g9}x3p98p;JaZj$}toDJiy(E z-xOyFz8Ua`7H66t z8|MsC+$TK6ksHQE@h<>wg#Nn!y$N_G>`yscqW=@%yI}8!H27V>C*bdtdn0^1V2jxq zq`P?AaAb*n=oXn z(Z9NXjsScQ{54&}{|0coW*-~)+W!{8@8@U@2VSq`{uq%*iyqpt$&hkxkp ztpU6M{B`~70MA$wZST(jmt%bD<8L?MJ=W-Wr~fXK9{+6U&!++Z8{{q4I~97 zCDQkg!2cNb*8RH?@L%Rck@%9$r4e)op|4#yLfd0BYJ_ekD@uR~bz`uZh zdo=l<0X~88rpLc9;J4AfYc>1`=(ij3W03|w2|cfXKkDPh3jE~>&ft3*{$jw@hQMuFWT8&y|4l;g6Iv zC;MdqehdAp_ZRg`)r{zPxCi+4@Q*Z&J{NG8A%1THydWNHV-0^l;KT6m-)gWA@Mzdi zx5p0w&&K%C{r|^+UophPp8+0&@w7~<|8u}QL7$<)zXtpd*pJppr2ihk3()^Mzuy7g zZtyoL&+E+HnB#WxB}b_!$Ky>&m7KV`SX5M+>n!v*%X5kf^PFW~x8(M?TzQgHDZ?dV zXI^1Jq1PjM?kg;F=6FjBr4prhgUcQe~#_a3n(C78@@OjPbYxzBu%hULi%4c6Ump`wg&v~=yb1s+6W6HTqIhQHt zUe8#joXec&GS9i!Q2F&~lGEjOmy~h~>Gr47$*|sq}!hpa{2{CFin?)r%HpI9D%Sx<<}+W-nZnAv;m8V&3&B1un0% zEZ6DXsFd87l3O8_DPFQVxVUr6DkY^vyQ7((vjH}}-|Zy~?pZ^E*X=F6&*_%RJ!S5~ z5^uhg50h?C@+EhvQj$mbqRn7bSl}uy^Hzcn6_w;EWm2)LxVW_3B|$jaCFQybi(vNB zQa6>96{0Q#fgd1`R8&IrqzrWPNM3hgu~g_OD&34e^_Gfnr;}Kb1#z#I{DVZfUFCx{ zGN(eqZQUG+T9gk?p0b==w5G5Cox}{tp zT=P-Ile3}7CAzE-x7_okqSAs2X|ub~%jPT0EiRMtHRIh^Sj7BrdtHKk^SRN~YHB=K zqe1w;ggfeaP=j5a#S@RR(#_%>0&S49~~A)#(pSrNK|M?IB6R7nmTgBUQ4r;Md~T%=%04gd^w4}{Y2MuieKXrV`R zk<*1A1>|60kuDX5+=x6Ex009(qbOyRXxeuqrp2}ix&IWwL@cg||f5T4>5Mb!12Pp+5O3DVMuqbaPd_Kip z$_l5rHahd&_?>~2Qc&tmDWh;C5HPfql3Q9#cgqn)zuDgekYOs5#0~}P0~_v_EREI$ z$bXaZIUEm`BfKx7`?tw>^go1W{+okO;zxA6Us{iSQUU?uk2h3!rFmPYqkYso7uJ`$ zFyM54qzchtIu`q@YQFA35dRS!J+uzg>Fk&n?bVX>OO)wiT-xxgHR))mC>7A>+)0a;7U)h3?hX4Qo literal 0 HcmV?d00001 diff --git a/files/bin/uname b/files/bin/uname new file mode 100644 index 0000000000000000000000000000000000000000..84d5369a62115d7e9edd66609342d198c1a9172d GIT binary patch literal 37608 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 #include #include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include #define INITFSCP_VER "0.3.0" -#define INITFSCP_NAME_MAX 255 -#define INITFSCP_MAX_FILES 32 -#define RESET "\033[00m" -#define BLACK "\033[30m" -#define RED "\033[31m" -#define GREEN "\033[32m" -#define YELLOW "\033[33m" -#define BLUE "\033[34m" +#define INITRD_NAME_MAX 255 +#define INITRD_MAX_FILES 128 +#define INITRD_MAX_FS_SIZE 1048576 + +#define RESET "\033[00m" +#define BLACK "\033[30m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" #define MAGENTA "\033[35m" -#define CYAN "\033[36m" -#define WHITE "\033[37m" +#define CYAN "\033[36m" +#define WHITE "\033[37m" -/// Identifies a file. -#define FS_FILE 0x01 -/// Identifies a directory. -#define FS_DIRECTORY 0x02 -/// Identifies a character devies. -#define FS_CHARDEVICE 0x04 -/// Identifies a block devies. -#define FS_BLOCKDEVICE 0x08 -/// Identifies a pipe. -#define FS_PIPE 0x10 -/// Identifies a symbolic link. -#define FS_SYMLINK 0x20 -/// Identifies a mount-point. -#define FS_MOUNTPOINT 0x40 +static const char dt_char_array[] = { + '?', // DT_UNKNOWN = 0, + 'p', //DT_FIFO = 1, + 'c', //DT_CHR = 2, + '*', + 'd', //DT_DIR = 4, + '*', + 'b', //DT_BLK = 6, + '*', + '-', //DT_REG = 8, + '*', + 'l', //DT_LNK = 10, + '*', + 's', //DT_SOCK = 12, + '*', + '?', //DT_WHT = 14 +}; /// @brief Information concerning a file. typedef struct initrd_file_t { - /// Number used as delimiter, it must be set to 0xBF. - int magic; - /// The name of the file. - char fileName[INITFSCP_NAME_MAX]; - /// The type of the file. - short int file_type; - /// The uid of the owner. - int uid; - /// Offset of the starting address. - unsigned int offset; - /// Dimension of the file. - unsigned int length; -} initrd_file_t; + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The inode of the file. + unsigned int inode; + /// The name of the file. + char name[INITRD_NAME_MAX]; + /// The type of the file. + short int file_type; + /// The permissions mask. + unsigned int mask; + /// The id of the owner. + unsigned int uid; + /// The id of the group. + unsigned int gid; + /// Time of last access. + unsigned int atime; + /// Time of last data modification. + unsigned int mtime; + /// Time of last status change. + unsigned int ctime; + /// Offset of the starting address. + unsigned int offset; + /// Dimension of the file. + unsigned int length; +} __attribute__((aligned(16))) initrd_file_t; -static FILE *target_fs = NULL; -static initrd_file_t headers[INITFSCP_MAX_FILES]; -static char mount_points[INITFSCP_MAX_FILES][INITFSCP_NAME_MAX]; -static int mountpoint_idx = 0; -static int header_idx = 0; -static int header_offset = 0; +/// @brief The details regarding the filesystem. +/// @brief Contains the number of files inside the initrd filesystem. +typedef struct initrd_t { + /// Number of files. + unsigned int nfiles; + /// List of headers. + initrd_file_t headers[INITRD_MAX_FILES]; +} __attribute__((aligned(16))) initrd_t; + +// Prepare a variable to keep track of the offsets inside the FS. By +// default skip the initial space for the FS details structure. +static int fs_offset = sizeof(initrd_t); static inline void usage(char *prgname) { - printf("Usage:\n"); - printf(" %s --help For this screen\n", prgname); - printf(" %s --source [-s] The source directory.\n", prgname); - printf(" %s --target [-t] The target file for the initfs.\n", prgname); + printf("Usage:\n"); + printf(" %s --help For this screen\n", prgname); + printf(" %s --source [-s] The source directory.\n", prgname); + printf(" %s --target [-t] The target file for the initrd.\n", prgname); } static inline void version(char *prgname) { - printf("%s version: %s\n", prgname, INITFSCP_VER); + printf("%s version: %s\n", prgname, INITFSCP_VER); } -static inline bool is_option_mount_point(char *arg) +static inline int is_option_mount_point(char *arg) { - if (arg == NULL) - return false; - return ((strcmp(arg, "-m") == 0) || (strcmp(arg, "--mountpoint") == 0)); + return ((strcmp(arg, "-m") == 0) || (strcmp(arg, "--mountpoint") == 0)); } -static inline bool is_option_source(char *arg) +static inline int is_option_source(char *arg) { - if (arg == NULL) - return false; - return ((strcmp(arg, "-s") == 0) || (strcmp(arg, "--source") == 0)); + return ((strcmp(arg, "-s") == 0) || (strcmp(arg, "--source") == 0)); } -static inline bool is_option_target(char *arg) +static inline int is_option_target(char *arg) { - if (arg == NULL) - return false; - return ((strcmp(arg, "-t") == 0) || (strcmp(arg, "--target") == 0)); + return ((strcmp(arg, "-t") == 0) || (strcmp(arg, "--target") == 0)); } -static inline bool is_mount_point(char *name) +static int open_target_fs(int argc, char *argv[]) { - if (name == NULL) - return false; - // Check if the name matches with a mount-point. - for (int i = 0; i < mountpoint_idx; ++i) { - if (strcmp(name, mount_points[i]) == 0) { - return true; - } - } - return false; + printf("%-64s", "Opening target filesystem..."); + int fd = -1; + for (int i = 1; i < argc; ++i) { + if (is_option_target(argv[i]) && ((i + 1) < argc)) { + fd = open(argv[i + 1], O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO); + break; + } + } + if (fd == -1) + printf("[" RED "FAILED" RESET "]\n\n"); + else + printf("[" GREEN "DONE" RESET "]\n\n"); + return fd; } -static FILE *openfile(const char *dirname, struct dirent *dir, const char *mode) +static void init_headers(initrd_t *initrd) { - char pathname[1024]; - FILE *fp; - sprintf(pathname, "%s/%s", dirname, dir->d_name); - fp = fopen(pathname, mode); - return fp; + printf("%-64s", "Initializing headers structures..."); + for (size_t i = 0; i < INITRD_MAX_FILES; i++) { + initrd->headers[i].magic = 0xBF; + initrd->headers[i].inode = i; + initrd->headers[i].file_type = 0; + initrd->headers[i].mask = 0; + initrd->headers[i].uid = 0; + initrd->headers[i].gid = 0; + initrd->headers[i].atime = time(NULL); + initrd->headers[i].mtime = time(NULL); + initrd->headers[i].ctime = time(NULL); + initrd->headers[i].offset = 0; + initrd->headers[i].length = 0; + + memset(initrd->headers[i].name, 0, INITRD_NAME_MAX); + } + printf("[" GREEN "DONE" RESET "]\n\n"); } -static bool open_target_fs(int argc, char *argv[]) +static int create_file_headers(initrd_t *initrd, char *mountpoint, char *directory) { - printf("%-64s", "Opening target filesystem..."); - for (int i = 1; i < argc; ++i) { - if (is_option_target(argv[i]) && ((i + 1) < argc)) { - target_fs = fopen(argv[i + 1], "w"); - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; - } - } - printf("[" RED "FAILED" RESET "]\n\n"); - return false; + assert(mountpoint && "Received null mountpoint."); + assert(directory && "Received null directory."); + + // ------------------------------------------------------------------------ + char directory_path_abs[PATH_MAX], entry_path_abs[PATH_MAX], fs_abs_path[PATH_MAX]; + memset(directory_path_abs, 0, PATH_MAX); + memset(entry_path_abs, 0, PATH_MAX); + memset(fs_abs_path, 0, PATH_MAX); + strcpy(directory_path_abs, mountpoint); + strcat(directory_path_abs, directory); + + int dir_fd = open(directory_path_abs, O_RDONLY); + if (dir_fd == -1) { + printf("[" RED "FAILED" RESET "]\n"); + printf("Could not open source directory %s.\n", directory_path_abs); + return 0; + } + + // ------------------------------------------------------------------------ + char buffer[BUFSIZ], *ebuf; + off_t basep; + int nbytes; + while ((nbytes = getdirentries(dir_fd, buffer, BUFSIZ, &basep)) > 0) { + ebuf = buffer + nbytes; + char *cp = buffer; + while (cp < ebuf) { + struct dirent *entry = (struct dirent *)cp; + if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) { + cp += entry->d_reclen; + continue; + } + // Create the host machine absolute path. + strcpy(entry_path_abs, directory_path_abs); + strcat(entry_path_abs, "/"); + strcat(entry_path_abs, entry->d_name); + // Create the filesystem absolute path. + strcpy(fs_abs_path, directory); + strcat(fs_abs_path, "/"); + strcat(fs_abs_path, entry->d_name); + // Print the entry. + printf("[%3d] ENTRY: %-52s", initrd->nfiles, fs_abs_path); + // Stat the file. + struct stat _stat; + if (stat(entry_path_abs, &_stat) == -1) { + printf("[" RED "FAILED" RESET "]\n"); + printf("Error while executing stat on file : %s\n", entry_path_abs); + return 0; + } + printf("[%c%c%c%c%c%c%c%c%c%c]", + dt_char_array[entry->d_type], + (_stat.st_mode & S_IRUSR) != 0 ? 'r' : '-', + (_stat.st_mode & S_IWUSR) != 0 ? 'w' : '-', + (_stat.st_mode & S_IXUSR) != 0 ? 'x' : '-', + (_stat.st_mode & S_IRGRP) != 0 ? 'r' : '-', + (_stat.st_mode & S_IWGRP) != 0 ? 'w' : '-', + (_stat.st_mode & S_IXGRP) != 0 ? 'x' : '-', + (_stat.st_mode & S_IROTH) != 0 ? 'r' : '-', + (_stat.st_mode & S_IWOTH) != 0 ? 'w' : '-', + (_stat.st_mode & S_IXOTH) != 0 ? 'x' : '-'); + + if (entry->d_type != DT_DIR) { + // ---------------------------------------------------------------- + // Copy the name and the main info. + strcpy(initrd->headers[initrd->nfiles].name, fs_abs_path); + initrd->headers[initrd->nfiles].file_type = DT_REG; + initrd->headers[initrd->nfiles].length = _stat.st_size; + initrd->headers[initrd->nfiles].offset = fs_offset; + initrd->headers[initrd->nfiles].mask = _stat.st_mode; + initrd->headers[initrd->nfiles].uid = _stat.st_uid; + initrd->headers[initrd->nfiles].gid = _stat.st_gid; + initrd->headers[initrd->nfiles].atime = _stat.st_atim.tv_sec; + initrd->headers[initrd->nfiles].mtime = _stat.st_mtim.tv_sec; + initrd->headers[initrd->nfiles].ctime = _stat.st_ctim.tv_sec; + + // ---------------------------------------------------------------- + printf("[OFFSET:%6d]", initrd->headers[initrd->nfiles].offset); + printf("[SIZE:%6d]", initrd->headers[initrd->nfiles].length); + printf("[" GREEN "DONE" RESET "]\n"); + + fs_offset += initrd->headers[initrd->nfiles].length; + ++initrd->nfiles; + } else { + // ---------------------------------------------------------------- + strcpy(initrd->headers[initrd->nfiles].name, fs_abs_path); + initrd->headers[initrd->nfiles].file_type = DT_DIR; + initrd->headers[initrd->nfiles].length = _stat.st_size; + initrd->headers[initrd->nfiles].offset = fs_offset; + initrd->headers[initrd->nfiles].mask = _stat.st_mode; + initrd->headers[initrd->nfiles].uid = _stat.st_uid; + initrd->headers[initrd->nfiles].gid = _stat.st_gid; + initrd->headers[initrd->nfiles].atime = _stat.st_atim.tv_sec; + initrd->headers[initrd->nfiles].mtime = _stat.st_mtim.tv_sec; + initrd->headers[initrd->nfiles].ctime = _stat.st_ctim.tv_sec; + + // ---------------------------------------------------------------- + printf("[OFFSET:%6d]", initrd->headers[initrd->nfiles].offset); + printf("[SIZE:%6d]", initrd->headers[initrd->nfiles].length); + printf("[" GREEN "DONE" RESET "]\n"); + + // ---------------------------------------------------------------- + ++initrd->nfiles; + + // ---------------------------------------------------------------- + if (!create_file_headers(initrd, mountpoint, fs_abs_path)) { + break; + } + } + cp += entry->d_reclen; + } + } + close(dir_fd); + return 1; } -static bool init_headers() +static int write_file_system(initrd_t *initrd, int target_fd, char *mountpoint) { - printf("%-64s", "Initializing headers structures..."); - for (size_t i = 0; i < INITFSCP_MAX_FILES; i++) { - headers[i].magic = 0xBF; - memset(headers[i].fileName, 0, INITFSCP_NAME_MAX); - headers[i].file_type = 0; - headers[i].uid = 0; - headers[i].offset = 0; - headers[i].length = 0; - } - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; -} + printf("Copying data to filesystem...\n"); + for (int i = 0; i < INITRD_MAX_FILES; i++) { + // -------------------------------------------------------------------- + char absolute_path[256]; + memset(absolute_path, 0, 256); + strcpy(absolute_path, mountpoint); + strcat(absolute_path, initrd->headers[i].name); -static bool init_mount_points(int argc, char *argv[]) -{ - printf("Initializing mount points...\n"); - for (int i = 1; i < argc; ++i) { - if (is_option_mount_point(argv[i]) && ((i + 1) < argc)) { - strcpy(mount_points[mountpoint_idx], argv[i + 1]); - printf("[%3d] MPNT: %-52s", mountpoint_idx, - mount_points[mountpoint_idx]); - printf("[" CYAN "DONE" RESET "]\n"); - ++mountpoint_idx; - } - } - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; -} + // -------------------------------------------------------------------- + if (initrd->headers[i].file_type == DT_REG) { + FILE *fd2 = fopen(absolute_path, "r+"); + if (fd2 == NULL) { + continue; + } + printf("[%3d] FILE: %-92s", i, absolute_path); + char *buffer = (char *)malloc(initrd->headers[i].length); + fread(buffer, 1, initrd->headers[i].length, fd2); + // Write the content. + write(target_fd, buffer, initrd->headers[i].length); -static bool create_file_headers(char *mountpoint, char *directory) -{ - assert(mountpoint && "Received null mountpoint."); - assert(directory && "Received null directory."); - - // ------------------------------------------------------------------------ - char absolute_path[256]; - memset(absolute_path, 0, 256); - strcpy(absolute_path, mountpoint); - strcat(absolute_path, directory); - - // ------------------------------------------------------------------------ - DIR *source_dir = opendir(absolute_path); - if (source_dir == NULL) { - printf("[" RED "FAILED" RESET "]\n"); - printf("Could not open source directory %s.\n", absolute_path); - return false; - } - struct dirent *entry; - while ((entry = readdir(source_dir)) != NULL) { - if ((strcmp(entry->d_name, ".") == 0) || - (strcmp(entry->d_name, "..") == 0)) { - continue; - } - if (entry->d_type != DT_DIR) { - char relative_filename[256]; - strcpy(relative_filename, directory); - strcat(relative_filename, "/"); - strcat(relative_filename, entry->d_name); - - // ---------------------------------------------------------------- - FILE *fd = openfile(absolute_path, entry, "r+"); - printf("[%3d] FILE: %-52s", header_idx, relative_filename); - if (fd == NULL) { - printf("[" RED "FAILED" RESET "]\n"); - printf("Error while opening file : %s\n", relative_filename); - } - fseek(fd, 0, SEEK_END); - long length = ftell(fd); - fclose(fd); - - // ---------------------------------------------------------------- - strcpy(headers[header_idx].fileName, relative_filename); - headers[header_idx].file_type = FS_FILE; - headers[header_idx].length = length; - headers[header_idx].offset = header_offset; - - // ---------------------------------------------------------------- - printf("[" BLUE "OPEN" RESET "]"); - printf("[OFFSET:%6d]", headers[header_idx].offset); - printf("[SIZE:%6d]", headers[header_idx].length); - printf("[" GREEN "DONE" RESET "]\n"); - - header_offset += headers[header_idx].length; - ++header_idx; - } else { - // ---------------------------------------------------------------- - char sub_directory[256]; - memset(sub_directory, 0, 256); - strcpy(sub_directory, directory); - strcat(sub_directory, "/"); - strcat(sub_directory, entry->d_name); - - // ---------------------------------------------------------------- - strcpy(headers[header_idx].fileName, sub_directory); - headers[header_idx].file_type = FS_DIRECTORY; - headers[header_idx].length = 0; - headers[header_idx].offset = 0 /*++header_offset*/; - - // ---------------------------------------------------------------- - // Check if the directory is a mountpoint. - if (is_mount_point(headers[header_idx].fileName)) { - headers[header_idx].file_type = FS_MOUNTPOINT; - } - - // ---------------------------------------------------------------- - printf("[%3d] %3s : %-52s", header_idx, - (headers[header_idx].file_type == FS_DIRECTORY) ? "DIR" : - "MPT", - sub_directory); - printf("[" BLUE "OPEN" RESET "]"); - printf("[OFFSET:%6d]", headers[header_idx].offset); - printf("[SIZE:%6d]", headers[header_idx].length); - printf("[" GREEN "DONE" RESET "]\n"); - - // ---------------------------------------------------------------- - ++header_idx; - - // ---------------------------------------------------------------- - create_file_headers(mountpoint, sub_directory); - } - } - closedir(source_dir); - return true; -} - -static bool write_file_system(char *mountpoint) -{ - printf("Copying data to filesystem...\n"); - for (int i = 0; i < INITFSCP_MAX_FILES; i++) { - // -------------------------------------------------------------------- - char absolute_path[256]; - memset(absolute_path, 0, 256); - strcpy(absolute_path, mountpoint); - strcat(absolute_path, headers[i].fileName); - - // -------------------------------------------------------------------- - if (headers[i].file_type == FS_FILE) { - FILE *fd2 = fopen(absolute_path, "r+"); - if (fd2 == NULL) { - continue; - } - printf("[%3d] FILE: %-92s", i, absolute_path); - char *buffer = (char *)malloc(headers[i].length); - fread(buffer, 1, headers[i].length, fd2); - fwrite(buffer, 1, headers[i].length, target_fs); - printf("[" GREEN "DONE" RESET "]\n"); - fclose(fd2); - free(buffer); - } - } - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; + printf("[" GREEN "DONE" RESET "]\n"); + fclose(fd2); + free(buffer); + } + } + printf("[" GREEN "DONE" RESET "]\n\n"); + return 1; } int main(int argc, char *argv[]) { - printf("Welcome to MentOS initfs file copier tool\n\n"); - if (argc <= 1) { - if (argv[0][0] == '.') { - version(argv[0] + 2); - } - usage(argv[0]); - return EXIT_FAILURE; - } - if (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v")) { - version(argv[0] + 2); - return EXIT_SUCCESS; - } - if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { - usage(argv[0]); - return EXIT_SUCCESS; - } + printf("Welcome to MentOS initrd file copier tool\n\n"); + if (argc <= 1) { + if (argv[0][0] == '.') { + version(argv[0] + 2); + } + usage(argv[0]); + return EXIT_FAILURE; + } + if (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v")) { + version(argv[0] + 2); + return EXIT_SUCCESS; + } + if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + usage(argv[0]); + return EXIT_SUCCESS; + } - // ------------------------------------------------------------------------ - // Open the fs. - if (!open_target_fs(argc, argv)) { - printf("Could not open target FileSystem.\n"); - return EXIT_FAILURE; - } + // Create the filesystem details structure. + initrd_t initrd; + // Target fs file descriptor. + int target_fd; - // ------------------------------------------------------------------------ - // Initialize the headers. - if (!init_headers()) { - printf("Could not initialize headers.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } + // ------------------------------------------------------------------------ + // Open the fs. + if ((target_fd = open_target_fs(argc, argv)) == -1) { + printf("Could not open target FileSystem.\n"); + return EXIT_FAILURE; + } - // ------------------------------------------------------------------------ - // Initialize the mountpoints. - if (!init_mount_points(argc, argv)) { - printf("Could not initialize mount points.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } + // ------------------------------------------------------------------------ + // Initialize the headers. + init_headers(&initrd); - // ------------------------------------------------------------------------ - // Create file headers. - header_offset = - sizeof(struct initrd_file_t) * INITFSCP_MAX_FILES + sizeof(int); - printf("Creating headers...\n"); - for (uint32_t i = 1; i < argc; ++i) { - if (is_option_source(argv[i]) && ((i + 1) < argc)) { - if (!create_file_headers(argv[i + 1], "")) { - printf("Could not create file headers.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } - } - } - printf("[" GREEN "DONE" RESET "]\n\n"); + // ------------------------------------------------------------------------ + // Create file headers. + printf("Creating headers...\n"); + for (unsigned int i = 1; i < argc; ++i) { + if (is_option_source(argv[i]) && ((i + 1) < argc)) { + // ---------------------------------------------------------------- + strcpy(initrd.headers[initrd.nfiles].name, "/"); + initrd.headers[initrd.nfiles].file_type = DT_DIR; + initrd.headers[initrd.nfiles].length = 0; + initrd.headers[initrd.nfiles].offset = fs_offset; + initrd.headers[initrd.nfiles].mask = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + initrd.headers[initrd.nfiles].uid = 0; + initrd.headers[initrd.nfiles].gid = 0; + initrd.headers[initrd.nfiles].atime = time(NULL); + initrd.headers[initrd.nfiles].mtime = time(NULL); + initrd.headers[initrd.nfiles].ctime = time(NULL); - // ------------------------------------------------------------------------ - // Copying information about headers on filesystem. - printf("%-64s", "Copying information about headers to filesystem..."); - fwrite(&header_idx, sizeof(int), 1, target_fs); - fwrite(headers, sizeof(struct initrd_file_t), 32, target_fs); - printf("[" GREEN "DONE" RESET "]\n\n"); + printf("[%3d] DIR : %-52s", initrd.nfiles, initrd.headers[initrd.nfiles].name); + printf("[" BLUE "OPEN" RESET "]"); + printf("[OFFSET:%6d]", initrd.headers[initrd.nfiles].offset); + printf("[SIZE:%6d]", initrd.headers[initrd.nfiles].length); + printf("[" GREEN "DONE" RESET "]\n"); + ++initrd.nfiles; - // ------------------------------------------------------------------------ - // Write headers on filesystem. - for (uint32_t i = 1; i < argc; ++i) { - if (is_option_source(argv[i]) && ((i + 1) < argc)) { - if (!write_file_system(argv[i + 1])) { - printf("Could not write on filesystem.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } - } - } - fclose(target_fs); - return 0; + // ---------------------------------------------------------------- + if (!create_file_headers(&initrd, argv[i + 1], "")) { + printf("Could not create file headers.\n"); + close(target_fd); + return EXIT_FAILURE; + } + } + } + printf("[" GREEN "DONE" RESET "]\n\n"); + + // ------------------------------------------------------------------------ + // Copying information about headers on filesystem. + printf("%-64s", "Copying the filesystem's details structure..."); + write(target_fd, &initrd, sizeof(initrd_t)); + printf("[" GREEN "DONE" RESET "]\n\n"); + + // ------------------------------------------------------------------------ + // Write headers on filesystem. + for (unsigned int i = 1; i < argc; ++i) { + if (is_option_source(argv[i]) && ((i + 1) < argc)) { + if (!write_file_system(&initrd, target_fd, argv[i + 1])) { + printf("Could not write on filesystem.\n"); + close(target_fd); + return EXIT_FAILURE; + } + } + } + + // ------------------------------------------------------------------------ + lseek(target_fd, 0L, SEEK_SET); + size_t fs_size = lseek(target_fd, 0L, SEEK_END); + printf("FS size : %ld\n", fs_size); + if (fs_size < INITRD_MAX_FS_SIZE - 1) { + printf("Extend FS size to : %d\n", INITRD_MAX_FS_SIZE - 1); + lseek(target_fd, INITRD_MAX_FS_SIZE - 1, SEEK_SET); + write(target_fd, 0, 1); + } + close(target_fd); + return 0; } diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt new file mode 100644 index 0000000..22e668c --- /dev/null +++ b/libc/CMakeLists.txt @@ -0,0 +1,140 @@ +# ============================================================================= +# Author: Enrico Fraccaroli +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(libc C ASM) + +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Enable the assembly language. +enable_language(ASM) +# Find the NASM compiler. +find_program(ASM_COMPILER + NAMES nasm + HINTS /usr/bin/ ${CMAKE_SOURCE_DIR}/third_party/nasm/bin +) +# Check that we have found the compiler. +if(NOT ASM_COMPILER) + message(FATAL_ERROR "ASM compiler not found!") +endif(NOT ASM_COMPILER) +# Set the asm compiler. +set(CMAKE_ASM_COMPILER ${ASM_COMPILER}) +# Set the assembly compiler flags. +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -f elf") +set(CMAKE_ASM_COMPILE_OBJECT " -o ") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O0") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -F dwarf") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the C compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +# ============================================================================= +# Add the library. +add_library( + ${PROJECT_NAME} + + ${PROJECT_SOURCE_DIR}/src/stdio.c + ${PROJECT_SOURCE_DIR}/src/ctype.c + ${PROJECT_SOURCE_DIR}/src/string.c + ${PROJECT_SOURCE_DIR}/src/stdlib.c + ${PROJECT_SOURCE_DIR}/src/math.c + ${PROJECT_SOURCE_DIR}/src/fcvt.c + ${PROJECT_SOURCE_DIR}/src/time.c + ${PROJECT_SOURCE_DIR}/src/strerror.c + ${PROJECT_SOURCE_DIR}/src/termios.c + ${PROJECT_SOURCE_DIR}/src/libgen.c + ${PROJECT_SOURCE_DIR}/src/vsprintf.c + ${PROJECT_SOURCE_DIR}/src/vscanf.c + ${PROJECT_SOURCE_DIR}/src/pwd.c + ${PROJECT_SOURCE_DIR}/src/grp.c + ${PROJECT_SOURCE_DIR}/src/sched.c + ${PROJECT_SOURCE_DIR}/src/setenv.c + ${PROJECT_SOURCE_DIR}/src/assert.c + ${PROJECT_SOURCE_DIR}/src/abort.c + + ${PROJECT_SOURCE_DIR}/src/io/mm_io.c + ${PROJECT_SOURCE_DIR}/src/io/port_io.c + + ${PROJECT_SOURCE_DIR}/src/ipc/ipc.c + + ${PROJECT_SOURCE_DIR}/src/sys/unistd.c + ${PROJECT_SOURCE_DIR}/src/sys/errno.c + ${PROJECT_SOURCE_DIR}/src/sys/utsname.c + ${PROJECT_SOURCE_DIR}/src/sys/ioctl.c + + ${PROJECT_SOURCE_DIR}/src/unistd/getppid.c + ${PROJECT_SOURCE_DIR}/src/unistd/getpid.c + ${PROJECT_SOURCE_DIR}/src/unistd/exit.c + ${PROJECT_SOURCE_DIR}/src/unistd/setsid.c + ${PROJECT_SOURCE_DIR}/src/unistd/getsid.c + ${PROJECT_SOURCE_DIR}/src/unistd/setgid.c + ${PROJECT_SOURCE_DIR}/src/unistd/getgid.c + ${PROJECT_SOURCE_DIR}/src/unistd/fork.c + ${PROJECT_SOURCE_DIR}/src/unistd/read.c + ${PROJECT_SOURCE_DIR}/src/unistd/write.c + ${PROJECT_SOURCE_DIR}/src/unistd/exec.c + ${PROJECT_SOURCE_DIR}/src/unistd/nice.c + ${PROJECT_SOURCE_DIR}/src/unistd/open.c + ${PROJECT_SOURCE_DIR}/src/unistd/reboot.c + ${PROJECT_SOURCE_DIR}/src/unistd/waitpid.c + ${PROJECT_SOURCE_DIR}/src/unistd/chdir.c + ${PROJECT_SOURCE_DIR}/src/unistd/getcwd.c + ${PROJECT_SOURCE_DIR}/src/unistd/close.c + ${PROJECT_SOURCE_DIR}/src/unistd/stat.c + ${PROJECT_SOURCE_DIR}/src/unistd/rmdir.c + ${PROJECT_SOURCE_DIR}/src/unistd/mkdir.c + ${PROJECT_SOURCE_DIR}/src/unistd/unlink.c + ${PROJECT_SOURCE_DIR}/src/unistd/getdents.c + ${PROJECT_SOURCE_DIR}/src/unistd/lseek.c + ${PROJECT_SOURCE_DIR}/src/unistd/kill.c + ${PROJECT_SOURCE_DIR}/src/unistd/signal.c + ${PROJECT_SOURCE_DIR}/src/unistd/interval.c + ${PROJECT_SOURCE_DIR}/src/libc_start.c + ${PROJECT_SOURCE_DIR}/src/debug.c + + ${PROJECT_SOURCE_DIR}/src/crt0.S +) + +# Add the includes. +target_include_directories(${PROJECT_NAME} PUBLIC inc) + +# Remove the 'lib' prefix. +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") diff --git a/libc/inc/assert.h b/libc/inc/assert.h new file mode 100644 index 0000000..8b285a0 --- /dev/null +++ b/libc/inc/assert.h @@ -0,0 +1,20 @@ +/// MentOS, The Mentoring Operating system project +/// @file assert.h +/// @brief Defines the function and pre-processor macro for assertions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Function used to log the information of a failed assertion. +/// @param assertion The failed assertion. +/// @param file The file where the assertion is located. +/// @param function The function where the assertion is. +/// @param line The line inside the file. +void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line); + +/// @brief Extract the filename from the full path provided by __FILE__. +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) + +/// @brief Assert function. +#define assert(expression) ((expression) ? (void)0 : __assert_fail(#expression, __FILENAME__, __func__, __LINE__)) diff --git a/libc/inc/bits/ioctls.h b/libc/inc/bits/ioctls.h new file mode 100644 index 0000000..2226e89 --- /dev/null +++ b/libc/inc/bits/ioctls.h @@ -0,0 +1,9 @@ +/// @file ioctls.h +/// @brief Input/Output ConTroL (IOCTL) numbers. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define TCGETS 0x5401U ///< Get the current serial port settings. +#define TCSETS 0x5402U ///< Set the current serial port settings. diff --git a/libc/inc/bits/stat.h b/libc/inc/bits/stat.h new file mode 100644 index 0000000..2ff288b --- /dev/null +++ b/libc/inc/bits/stat.h @@ -0,0 +1,36 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.h +/// @brief Defines the structure used by the functiosn fstat(), lstat(), and stat(). +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#if !defined(__SYS_STAT_H) && !defined(__KERNEL__) +#error "Never include directly; use instead." +#endif + +#include "stddef.h" +#include "time.h" + +/// @brief Data structure which contains information about a file. +typedef struct stat_t { + /// ID of device containing file. + dev_t st_dev; + /// File serial number. + ino_t st_ino; + /// Mode of file. + mode_t st_mode; + /// File user id. + uid_t st_uid; + /// File group id. + gid_t st_gid; + /// File Size. + off_t st_size; + /// Time of last access. + time_t st_atime; + /// Time of last data modification. + time_t st_mtime; + /// Time of last status change. + time_t st_ctime; +} stat_t; diff --git a/libc/inc/bits/termios-struct.h b/libc/inc/bits/termios-struct.h new file mode 100644 index 0000000..88e16a4 --- /dev/null +++ b/libc/inc/bits/termios-struct.h @@ -0,0 +1,45 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios-struct.h +/// @brief Definition of the `termios` structure. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Type for control characters. +typedef unsigned char cc_t; +/// Type for speed. +typedef unsigned int speed_t; +/// Type for flags. +typedef unsigned int tcflag_t; +/// The number of control characters. +#define NCCS 32 + +/// @brief Stores information about a terminal IOs. +typedef struct termios { + tcflag_t c_iflag; ///< input mode flags + tcflag_t c_oflag; ///< output mode flags + tcflag_t c_cflag; ///< control mode flags + tcflag_t c_lflag; ///< local mode flags + cc_t c_line; ///< line discipline + cc_t c_cc[NCCS]; ///< control characters + speed_t c_ispeed; ///< input speed + speed_t c_ospeed; ///< output speed +} termios; + +/// @brief These flags generally control higher-level aspects of input processing than the input +/// modes flags described in Input Modes, such as echoing, signals, and the choice of canonical +/// or noncanonical input. +enum { + ISIG = 000001U, ///< Controls whether the INTR, QUIT, and SUSP characters are recognized. + ICANON = 000002U, ///< Enables canonical input processing mode. + ECHO = 000010U, ///< Echo input characters. + ECHOE = 000020U, ///< If ICANON is set, the ERASE character erases the preceding character. + ECHOK = 000040U, ///< If ICANON is set, the KILL character erases the current line. + ECHONL = 000100U, ///< If ICANON is set, echo the NL character even if ECHO is not set. + NOFLSH = 000200U, ///< Do not clear in/out queues when receiving INTR, QUIT, and SUSP. + TOSTOP = 000400U, ///< Allows SIGTTOU signals generated by background processes. + ECHOCTL = 001000U, ///< If this and ECHO are set, control characters with ‘^’ are echoed. + ECHOKE = 004000U, ///< If ICANON is set, KILL is echoed by erasing each character on the line. + IEXTEN = 100000U, ///< Enables implementation-defined input processing. +}; \ No newline at end of file diff --git a/mentos/inc/libc/ctype.h b/libc/inc/ctype.h similarity index 53% rename from mentos/inc/libc/ctype.h rename to libc/inc/ctype.h index 2e5dccf..0471672 100644 --- a/mentos/inc/libc/ctype.h +++ b/libc/inc/ctype.h @@ -1,34 +1,52 @@ /// MentOS, The Mentoring Operating system project /// @file ctype.h /// @brief Functions related to character handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once /// @brief Check if the given value is a digit. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isdigit(int c); /// @brief Check if the given value is a letter. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isalpha(int c); /// @brief Check if the given value is either a letter or a digit. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isalnum(int c); /// @brief Check if the given value is an hexadecimal digit. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isxdigit(int c); /// @brief Check if the given value is a lower case letter. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int islower(int c); /// @brief Check if the given value is an upper case letter. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isupper(int c); /// @brief Transforms the given value into a lower case letter. +/// @param c The input letter. +/// @return The input letter turned into a lower case letter. int tolower(int c); /// @brief Transforms the given value into an upper case letter. +/// @param c The input letter. +/// @return The input letter turned into an upper case letter. int toupper(int c); /// @brief Check if the given value is a whitespace. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isspace(int c); diff --git a/libc/inc/debug.h b/libc/inc/debug.h new file mode 100644 index 0000000..7e515fe --- /dev/null +++ b/libc/inc/debug.h @@ -0,0 +1,44 @@ +/// MentOS, The Mentoring Operating system project +/// @file debug.h +/// @brief Debugging primitives. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifndef __DEBUG_HEADER__ +/// Header for identifying outputs coming from a mechanism. +#define __DEBUG_HEADER__ +#endif + +/// @brief Extract the filename from the full path provided by __FILE__. +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) + +/// @brief Prints the given character to debug output. +/// @param c The character to print. +void dbg_putchar(char c); + +/// @brief Prints the given string to debug output. +/// @param s The string to print. +void dbg_puts(const char *s); + +/// @brief Prints the given string to the debug output. +/// @param file The name of the file. +/// @param fun The name of the function. +/// @param line The line inside the file. +/// @param format The format to used, see printf. +/// @param ... The list of arguments. +void dbg_printf(const char *file, const char *fun, int line, const char *format, ...); + +/// @brief Transforms the given amount of bytes to a readable string. +/// @param bytes The bytes to turn to string. +/// @return String representing the bytes in human readable form. +const char *to_human_size(unsigned long bytes); + +/// @brief Transforms the given value to a binary string. +/// @param value The decimal value. +/// @return String representing the binary value. +const char *dec_to_binary(unsigned long value); + +/// Prints a debugging message. +#define pr_debug(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__ __VA_ARGS__) diff --git a/libc/inc/fcntl.h b/libc/inc/fcntl.h new file mode 100644 index 0000000..255ffd3 --- /dev/null +++ b/libc/inc/fcntl.h @@ -0,0 +1,46 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcntl.h +/// @brief Headers of functions fcntl() and open(). +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define O_RDONLY 00000000U ///< Open for reading only. +#define O_WRONLY 00000001U ///< Open for writing only. +#define O_RDWR 00000002U ///< Open for reading and writing. +#define O_CREAT 00000100U ///< Create if nonexistant. +#define O_EXCL 00000200U ///< Error if already exists. +#define O_TRUNC 00001000U ///< Truncate to zero length. +#define O_APPEND 00002000U ///< Set append mode. +#define O_NONBLOCK 00004000U ///< No delay. +#define O_DIRECTORY 00200000U ///< If file exists has no effect. Otherwise, the file is created. + +/// @defgroup ModeBitsAccessPermission Mode Bits for Access Permission +/// @brief The file modes. +/// @{ + +#define S_IRWXU 0000700U ///< RWX mask for user. +#define S_IRUSR 0000400U ///< R for user. +#define S_IWUSR 0000200U ///< W for user. +#define S_IXUSR 0000100U ///< X for user. + +#define S_IRWXG 0000070U ///< RWX mask for group. +#define S_IRGRP 0000040U ///< R for group. +#define S_IWGRP 0000020U ///< W for group. +#define S_IXGRP 0000010U ///< X for group. + +#define S_IRWXO 0000007U ///< RWX mask for group. +#define S_IROTH 0000004U ///< R for group. +#define S_IWOTH 0000002U ///< W for group. +#define S_IXOTH 0000001U ///< X for group. + +#define S_ISDIR(m) (((m)&0170000) == 0040000) ///< directory. +#define S_ISCHR(m) (((m)&0170000) == 0020000) ///< char special +#define S_ISBLK(m) (((m)&0170000) == 0060000) ///< block special +#define S_ISREG(m) (((m)&0170000) == 0100000) ///< regular file +#define S_ISFIFO(m) (((m)&0170000) == 0010000) ///< fifo +#define S_ISLNK(m) (((m)&0170000) == 0120000) ///< symbolic link +#define S_ISSOCK(m) (((m)&0170000) == 0140000) ///< socket + +/// @} diff --git a/libc/inc/fcvt.h b/libc/inc/fcvt.h new file mode 100644 index 0000000..017d3ef --- /dev/null +++ b/libc/inc/fcvt.h @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcvt.h +/// @brief Declare the functions required to turn double values into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Thif function transforms `value` into a string of digits inside `buf`, +/// representing the whole part followed by the decimal part. +/// @details +/// For instance, 512.765 will result in: +/// decpt = 3 +/// sign = 0 +/// buf = "512765" +/// @param arg The argument to turn into string. +/// @param chars The total number of digits. +/// @param decpt The position of the decimal point. +/// @param sign The sign of the number. +/// @param buf Buffer where the digits should be placed. +/// @param buf_size Dimension of the buffer. +void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size); + +/// @brief Thif function transforms `value` into a string of digits inside `buf`, +/// representing the whole part followed by the decimal part. +/// @details +/// For instance, 512.765 will result in: +/// decpt = 3 +/// sign = 0 +/// buf = "512765" +/// @param arg The argument to turn into string. +/// @param decimals The total number of digits to write after the decimal point. +/// @param decpt The position of the decimal point. +/// @param sign The sign of the number. +/// @param buf Buffer where the digits should be placed. +/// @param buf_size Dimension of the buffer. +void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size); diff --git a/libc/inc/grp.h b/libc/inc/grp.h new file mode 100644 index 0000000..42d2eb5 --- /dev/null +++ b/libc/inc/grp.h @@ -0,0 +1,76 @@ +/// MentOS, The Mentoring Operating system project +/// @file grp.h +/// @brief Defines the structures and functions for managing groups. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// Maximum number of users per group +#define MAX_MEMBERS_PER_GROUP 64 + +///@brief Contains user group informations +typedef struct group{ + + char* gr_name; // the name of the group + gid_t gr_gid; // numerical group ID + char* gr_passwd; // password + + //pointer to a null-terminated array of character pointers to member names + char* gr_mem[MAX_MEMBERS_PER_GROUP + 1]; + +} group; + +///@brief Provides access to the group database entry for `uid`. +///@param gid The gid to search inside the database. +///@return A pointer to the structure containing the database entry. +struct group* getgrgid(gid_t gid); + +///@brief Provides access to the group database entry for `name`. +///@param name The name to search inside the database. +///@return A pointer to the structure containing the database entry. +struct group* getgrnam(const char* name); + +///@brief Provides the same information as getgrgid but it stores the +/// results inside group, and the string information are store store +/// inside `buf`. +///@param gid The uid to search inside the database. +///@param group The structure containing pointers to the entry fields. +///@param buf The buffer where the strings should be stored. +///@param buflen The lenght of the buffer. +///@param result A pointer to the result or NULL is stored here. +///@return If the entry was found returns zero and set *result to group, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getgrgid_r(gid_t gid, struct group* group, char* buf, size_t buflen, struct group ** result); + +///@brief Provides the same information as getgrnam but it stores the +/// results inside group, and the string information are store store +/// inside `buf`. +///@param name The name to search inside the database. +///@param group The structure containing pointers to the entry fields. +///@param buf The buffer where the strings should be stored. +///@param buflen The lenght of the buffer. +///@param result A pointer to the result or NULL is stored here. +///@return If the entry was found returns zero and set *result to group, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getgrnam_r(const char* name, struct group* group, char* buf, size_t buflen, struct group** result); + + +///@brief Returns a pointer to a structure containing the broken-out +/// fields of an entry in the group database. +/// When first called returns a pointer to a group structure +/// containing the first entry in the group database. +/// Thereafter, it returns a pointer to a group structure +/// containing the next group structure in the group database, +/// so successive calls may be used to search the entire database. +struct group* getgrent(void); + +///@brief Rewinds the group database to allow repeated searches. +void endgrent(void); + +///@brief May be called to close the group database when processing is complete. +void setgrent(void); \ No newline at end of file diff --git a/libc/inc/io/mm_io.h b/libc/inc/io/mm_io.h new file mode 100644 index 0000000..c81dce1 --- /dev/null +++ b/libc/inc/io/mm_io.h @@ -0,0 +1,27 @@ +/// MentOS, The Mentoring Operating system project +/// @file mm_io.h +/// @brief Memory Mapped IO functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" + +/// @brief Reads a 8-bit value from the given address. +uint8_t in_memb(uint32_t addr); + +/// @brief Reads a 16-bit value from the given address. +uint16_t in_mems(uint32_t addr); + +/// @brief Reads a 32-bit value from the given address. +uint32_t in_meml(uint32_t addr); + +/// @brief Writes a 8-bit value at the given address. +void out_memb(uint32_t addr, uint8_t value); + +/// @brief Writes a 16-bit value at the given address. +void out_mems(uint32_t addr, uint16_t value); + +/// @brief Writes a 32-bit value at the given address. +void out_meml(uint32_t addr, uint32_t value); diff --git a/libc/inc/io/port_io.h b/libc/inc/io/port_io.h new file mode 100644 index 0000000..fe20b5a --- /dev/null +++ b/libc/inc/io/port_io.h @@ -0,0 +1,43 @@ +/// MentOS, The Mentoring Operating system project +/// @file port_io.h +/// @brief Byte I/O on ports prototypes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" + +/// @brief Used for reading from the I/O ports. +/// @param port The input port. +/// @return The read value. +uint8_t inportb(uint16_t port); + +/// @brief Used for reading 2 bytes from the I/O ports. +/// @param port The input port. +/// @return The read value. +uint16_t inports(uint16_t port); + +void inportsm(uint16_t port, uint8_t *data, unsigned long size); + +/// @brief Used for reading 4 bytes from the I/O ports. +/// @param port The input port. +/// @return The read value. +uint32_t inportl(uint16_t port); + +/// @brief Use this to write to I/O ports to send bytes to devices. +/// @param port The output port. +/// @param data The data to write. +void outportb(uint16_t port, uint8_t data); + +/// @brief Use this to write to I/O ports to send 2 bytes to devices. +/// @param port The output port. +/// @param data The data to write. +void outports(uint16_t port, uint16_t data); + +void outportsm(uint16_t port, uint8_t *data, uint16_t size); + +/// @brief Use this to write to I/O ports to send 4 bytes to devices. +/// @param port The output port. +/// @param data The data to write. +void outportl(uint16_t port, uint32_t data); diff --git a/libc/inc/ipc/ipc.h b/libc/inc/ipc/ipc.h new file mode 100644 index 0000000..f0867c5 --- /dev/null +++ b/libc/inc/ipc/ipc.h @@ -0,0 +1,25 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipc.h +/// @brief Inter-Process Communication (IPC) structures. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Permission details of an IPC object. +struct ipc_perm { + /// Key supplied to msgget(2). + key_t __key; + /// Effective UID of owner. + uid_t uid; + /// Effective GID of owner. + gid_t gid; + /// Effective UID of creator. + uid_t cuid; + /// Effective GID of creator. + gid_t cgid; + /// Permissions. + unsigned short mode; + /// Sequence number. + unsigned short __seq; +}; \ No newline at end of file diff --git a/libc/inc/ipc/msg.h b/libc/inc/ipc/msg.h new file mode 100644 index 0000000..b832c0b --- /dev/null +++ b/libc/inc/ipc/msg.h @@ -0,0 +1,70 @@ +/// MentOS, The Mentoring Operating system project +/// @file msg.h +/// @brief Definition of structure for managing message queues. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" +#include "stddef.h" +#include "time.h" +#include "ipc.h" + +/// Type for storing the number of messages in a message queue. +typedef unsigned int msgqnum_t; + +/// Type for storing the number of bytes in a message queue. +typedef unsigned int msglen_t; + +/// @brief Buffer to use with the message queue IPC. +struct msgbuf { + /// Type of the message. + long mtype; + /// Text of the message. + char mtext[1]; +}; + +/// @brief Message queue data structure. +struct msqid_ds { + /// Ownership and permissions. + struct ipc_perm msg_perm; + /// Time of last msgsnd(2). + time_t msg_stime; + /// Time of last msgrcv(2). + time_t msg_rtime; + /// Time of creation or last modification by msgctl(). + time_t msg_ctime; + /// Number of bytes in queue. + unsigned long msg_cbytes; + /// Number of messages in queue. + msgqnum_t msg_qnum; + /// Maximum number of bytes in queue. + msglen_t msg_qbytes; + /// PID of last msgsnd(2). + pid_t msg_lspid; + /// PID of last msgrcv(2). + pid_t msg_lrpid; +}; + +#ifdef __KERNEL__ + +long sys_msgget(key_t key, int msgflg); + +long sys_msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); + +long sys_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); + +long sys_msgctl(int msqid, int cmd, struct msqid_ds *buf); + +#else + +long msgget(key_t key, int msgflg); + +long msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); + +long msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); + +long msgctl(int msqid, int cmd, struct msqid_ds *buf); + +#endif \ No newline at end of file diff --git a/libc/inc/ipc/sem.h b/libc/inc/ipc/sem.h new file mode 100644 index 0000000..e683e58 --- /dev/null +++ b/libc/inc/ipc/sem.h @@ -0,0 +1,40 @@ +/// MentOS, The Mentoring Operating system project +/// @file sem.h +/// @brief Definition of structure for managing semaphores. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" +#include "stddef.h" +#include "time.h" +#include "ipc.h" + +/// @brief Buffer to use with the semaphore IPC. +struct sembuf { + /// Semaphore index in array. + unsigned short sem_num; + /// Semaphore operation. + short sem_op; + /// Operation flags. + short sem_flg; +}; + +#ifdef __KERNEL__ + +long sys_semget(key_t key, int nsems, int semflg); + +long sys_semop(int semid, struct sembuf *sops, unsigned nsops); + +long sys_semctl(int semid, int semnum, int cmd, unsigned long arg); + +#else + +long semget(key_t key, int nsems, int semflg); + +long semop(int semid, struct sembuf *sops, unsigned nsops); + +long semctl(int semid, int semnum, int cmd, unsigned long arg); + +#endif \ No newline at end of file diff --git a/mentos/inc/sys/shm.h b/libc/inc/ipc/shm.h similarity index 60% rename from mentos/inc/sys/shm.h rename to libc/inc/ipc/shm.h index 8a80360..dec48bf 100644 --- a/mentos/inc/sys/shm.h +++ b/libc/inc/ipc/shm.h @@ -1,14 +1,48 @@ /// MentOS, The Mentoring Operating system project /// @file shm.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Definition of structure for managing shared memories. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once +#include "sys/types.h" +#include "stddef.h" +#include "time.h" +#include "ipc.h" + +/// Data type for storing the number of attaches. +typedef unsigned long shmatt_t; + +/// @brief Shared memory data structure. +struct shmid_ds { + /// Operation permission struct. + struct ipc_perm shm_perm; + /// Size of segment in bytes. + size_t shm_segsz; + /// Time of last shmat(). + time_t shm_atime; + /// Time of last shmdt(). + time_t shm_dtime; + /// Time of last change by shmctl(). + time_t shm_ctime; + /// Pid of creator. + pid_t shm_cpid; + /// Pid of last shmop. + pid_t shm_lpid; + /// Number of current attaches. + shmatt_t shm_nattch; + /// Pointer to the next shared memory data structure. + struct shmid_ds *next; + /// Where shm created is memorized, should be a file. + void *shm_location; +}; + +#if 0 + #include "ipc.h" #include "debug.h" -#include "clock.h" +#include "time.h" #include "kheap.h" #include "stddef.h" #include "paging.h" @@ -17,10 +51,10 @@ //======== Permission flag for shmget ========================================== // or S_IRUGO from . -#define SHM_R 0400 +#define SHM_R 0400 // or S_IWUGO from . -#define SHM_W 0200 +#define SHM_W 0200 //============================================================================== //======== Flags for shmat ===================================================== @@ -28,76 +62,43 @@ #define SHM_RDONLY 010000 // Round attach address to SHMLBA. -#define SHM_RND 020000 +#define SHM_RND 020000 // Take-over region on attach. -#define SHM_REMAP 040000 +#define SHM_REMAP 040000 // Execution access. -#define SHM_EXEC 0100000 +#define SHM_EXEC 0100000 //============================================================================== //======== Commands for shmctl ================================================= // Lock segment (root only). -#define SHM_LOCK 11 +#define SHM_LOCK 11 // Unlock segment (root only). #define SHM_UNLOCK 12 //============================================================================== // Ipcs ctl commands. -#define SHM_STAT 13 +#define SHM_STAT 13 #define SHM_INFO 14 -#define SHM_STAT_ANY 15 +#define SHM_STAT_ANY 15 //======== shm_mode upper byte flags =========================================== // segment will be destroyed on last detach. -#define SHM_DEST 01000 +#define SHM_DEST 01000 // Segment will not be swapped. -#define SHM_LOCKED 02000 +#define SHM_LOCKED 02000 // Segment is mapped via hugetlb. -#define SHM_HUGETLB 04000 +#define SHM_HUGETLB 04000 // Don't check for reservations. #define SHM_NORESERVE 010000 -typedef unsigned long shmatt_t; - -struct shmid_ds { - // Operation permission struct. - struct ipc_perm shm_perm; - - // Size of segment in bytes. - size_t shm_segsz; - - // Time of last shmat(). - time_t shm_atime; - - // Time of last shmdt(). - time_t shm_dtime; - - // Time of last change by shmctl(). - time_t shm_ctime; - - // Pid of creator. - pid_t shm_cpid; - - // Pid of last shmop. - pid_t shm_lpid; - - // Number of current attaches. - shmatt_t shm_nattch; - - struct shmid_ds *next; - - // Where shm created is memorized, should be a file. - void *shm_location; -}; - /// @@brief Syscall Service Routine: Shared memory control operation. int syscall_shmctl(int *args); @@ -130,3 +131,27 @@ struct shmid_ds *find_shm_fromkey(key_t key); /// @@brief shmid_ds on list. struct shmid_ds *find_shm_fromvaddr(void *shmvaddr); + +#endif + +#ifdef __KERNEL__ + +long sys_shmat(int shmid, char *shmaddr, int shmflg); + +long sys_shmget(key_t key, size_t size, int flag); + +long sys_shmdt(char *shmaddr); + +long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf); + +#else + +long shmat(int shmid, char *shmaddr, int shmflg); + +long shmget(key_t key, size_t size, int flag); + +long shmdt(char *shmaddr); + +long shmctl(int shmid, int cmd, struct shmid_ds *buf); + +#endif \ No newline at end of file diff --git a/libc/inc/libgen.h b/libc/inc/libgen.h new file mode 100644 index 0000000..508bfb2 --- /dev/null +++ b/libc/inc/libgen.h @@ -0,0 +1,41 @@ +/// MentOS, The Mentoring Operating system project +/// @file libgen.h +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "stddef.h" + +// TODO: doxygen comment. +/// @brief +/// @param out +/// @param cur +/// @param sep +/// @param max +/// @result +int parse_path(char *out, char **cur, char sep, size_t max); + +// TODO: doxygen comment. +/// @brief +/// @param path +/// @result +char *dirname(const char *path); + +// TODO: doxygen comment. +/// @brief +/// @param path +/// @result +char *basename(const char *path); + +/// @brief Return the canonicalized absolute pathname. +/// @param path The path we are canonicalizing. +/// @param resolved The canonicalized path. +/// @return +/// If there is no error, realpath() returns a pointer to the resolved. +/// Otherwise, it returns NULL, the contents of the array resolved +/// are undefined, and errno is set to indicate the error. +/// @details +/// If resolved is NULL, then realpath() uses malloc +/// to allocate a buffer of up to PATH_MAX bytes to hold the +/// resolved pathname, and returns a pointer to this buffer. +char *realpath(const char *path, char *resolved); \ No newline at end of file diff --git a/libc/inc/limits.h b/libc/inc/limits.h new file mode 100644 index 0000000..b2222f6 --- /dev/null +++ b/libc/inc/limits.h @@ -0,0 +1,58 @@ +/// MentOS, The Mentoring Operating system project +/// @file limits.h +/// @brief OS numeric limits. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Number of bits in a `char'. +#define CHAR_BIT 8 + +/// Minimum value a `signed char' can hold. +#define SCHAR_MIN -128 + +/// Maximum value a `signed char' can hold. +#define SCHAR_MAX +127 + +/// Minimum value a `signed char' can hold. +#define CHAR_MIN SCHAR_MIN + +/// Maximum value a `signed char' can hold. +#define CHAR_MAX SCHAR_MAX + +/// Maximum value a `char' can hold. +#define UCHAR_MAX 255 + +/// Minimum value a `signed short int' can hold. +#define SHRT_MIN (-32768) + +/// Maximum value a `signed short int' can hold. +#define SHRT_MAX (+32767) + +/// Maximum value a `unsigned short int' can hold. +#define USHRT_MAX 65535 + +/// Minimum value a `signed int' can hold. +#define INT_MIN (-2147483648) + +/// Maximum values a `signed int' can hold. +#define INT_MAX (+2147483647) + +/// Maximum value an `unsigned int' can hold. +#define UINT_MAX (+2147483647) + +/// Maximum value a `signed long int' can hold. +#define LONG_MIN (-2147483648L) + +/// Minimum value a `signed long int' can hold. +#define LONG_MAX (+2147483647L) + +/// Maximum number of characters in a file name. +#define NAME_MAX 255 + +/// Maximum number of characters in a path name. +#define PATH_MAX 4096 + +/// Maximum pid number. +#define PID_MAX_LIMIT 32768 diff --git a/libc/inc/math.h b/libc/inc/math.h new file mode 100644 index 0000000..fc99027 --- /dev/null +++ b/libc/inc/math.h @@ -0,0 +1,158 @@ +/// MentOS, The Mentoring Operating system project +/// @file math.h +/// @brief Mathematical constants and functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief The absolute value. +#define abs(a) (((a) < 0) ? -(a) : (a)) + +/// @brief The max of the two values. +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/// @brief The min of the two values. +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +/// @brief The sign the the passed value. +#define sign(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0)) + +/// @brief e +#define M_E 2.7182818284590452354 + +/// @brief log_2 e +#define M_LOG2E 1.4426950408889634074 + +/// @brief log_10 e +#define M_LOG10E 0.43429448190325182765 + +/// @brief log_e 2 +#define M_LN2 0.69314718055994530942 + +/// @brief log_e 10 +#define M_LN10 2.30258509299404568402 + +/// @brief pi +#define M_PI 3.14159265358979323846 + +/// @brief pi / 2 +#define M_PI_2 1.57079632679489661923 + +/// @brief pi / 4 +#define M_PI_4 0.78539816339744830962 + +/// @brief 1 / pi +#define M_1_PI 0.31830988618379067154 + +/// @brief 2 / pi +#define M_2_PI 0.63661977236758134308 + +/// @brief 2 / sqrt(pi) +#define M_2_SQRTPI 1.12837916709551257390 + +/// @brief sqrt(2) +#define M_SQRT2 1.41421356237309504880 + +/// @brief 1 / sqrt(2) +#define M_SQRT1_2 0.70710678118654752440 + +/// @brief Returns the integral value that is nearest to x, with +/// halfway cases rounded away from zero. +/// @param x Value to round. +/// @result The value of x rounded to the nearest integral +/// (as a floating-point value). +double round(double x); + +/// @brief Rounds x upward, returning the smallest integral value +/// that is not less than x. +/// @param x Value to round up. +/// @return The smallest integral value that is not less than x +/// (as a floating-point value). +double ceil(double x); + +/// @brief Rounds x downward, returning the largest integral value +/// that is not greater than x. +/// @param x Value to round down. +/// @return The value of x rounded downward (as a floating-point value). +double floor(double x); + +/// @brief Returns base raised to the power exponent: +/// @param base Base value. +/// @param exponent Exponent value. +/// @result The result of raising base to the power exponent. +/// @details +/// If the base is finite negative and the exponent is finite but not an +/// integer value, it causes a domain error. +/// If both base and exponent are zero, it may also cause a domain error +/// on certain implementations. +/// If base is zero and exponent is negative, it may cause a domain error +/// or a pole error (or none, depending on the library implementation). +/// The function may also cause a range error if the result is too great +/// or too small to be represented by a value of the return type. +double pow(double base, double exponent); + +/// @brief Returns the base-e exponential function of x, which is e raised +/// to the power x: e^x. +/// @param x Value of the exponent. +/// @return Exponential value of x. +/// @details +/// If the magnitude of the result is too large to be represented by a value +/// of the return type, the function returns HUGE_VAL (or HUGE_VALF or +/// HUGE_VALL) with the proper sign, and an overflow range error occurs and +/// the global variable errno is set to ERANGE. +double exp(double x); + +/// @brief Returns the absolute value of x: |x|. +/// @param x Value whose absolute value is returned. +/// @result The absolute value of x. +double fabs(double x); + +/// @brief Returns the absolute value of x: |x|. +/// @param x Value whose absolute value is returned. +/// @result The absolute value of x. +float fabsf(float x); + +/// @brief Returns the square root of x. +/// @param x Value whose square root is computed. +/// @return Square root of x. If x is negative, the global variable errno +/// is set to EDOM. +double sqrt(double x); + +/// @brief Returns the square root of x. +/// @param x Value whose square root is computed. +/// @return Square root of x. If x is negative, the global variable errno +/// is set to EDOM. +float sqrtf(float x); + +/// @brief Checks if the input value is Infinite (INF). +/// @param x The value to check. +/// @return 1 if INF, 0 otherwise. +int isinf(double x); + +/// @brief Checks if the input value is Not A Number (NAN). +/// @param x The value to check. +/// @return 1 if NAN, 0 otherwise. +int isnan(double x); + +/// @brief Logarithm function in base 10. +/// @param x Topic of the logarithm function. +/// @return Return the result. +double log10(double x); + +/// @brief Natural logarithm function. +/// @param x Topic of the logarithm function. +/// @return Return the result. +double ln(double x); + +/// @brief Logarithm function in base x. +/// @param x Base of the logarithm. +/// @param y Topic of the logarithm function. +/// @return Return the result. +double logx(double x, double y); + +/// @brief Breaks x into an integral and a fractional part, both parts have the same sign as x. +/// @param x The value we want to break. +/// @param intpart Where we store the integer part. +/// @return the fractional part. +double modf(double x, double *intpart); diff --git a/libc/inc/pwd.h b/libc/inc/pwd.h new file mode 100644 index 0000000..29e9098 --- /dev/null +++ b/libc/inc/pwd.h @@ -0,0 +1,56 @@ +/// MentOS, The Mentoring Operating system project +/// @file pwd.h +/// @brief Contains the structure and functions for managing passwords. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Stores user account information. +typedef struct passwd_t { + char *pw_name; ///< User's login name. + char *pw_passwd; ///< Encrypted password (not currently). + uid_t pw_uid; ///< User ID. + gid_t pw_gid; ///< Group ID. + char *pw_gecos; ///< User's full name. + char *pw_dir; ///< User's login directory. + char *pw_shell; ///< User's login shell. +} passwd_t; + +/// @brief Provides access to the password database entry for `name`. +/// @param name The name to search inside the database. +/// @return A pointer to the structure containing the database entry. +passwd_t *getpwnam(const char *name); + +/// @brief Provides access to the password database entry for `uid`. +/// @param uid The uid to search inside the database. +/// @return A pointer to the structure containing the database entry. +passwd_t *getpwuid(uid_t uid); + +/// @brief Provides the same information as getpwnam but it stores the +/// results inside pwd, and the string information are store store +/// inside `buf`. +/// @param name The name to search inside the database. +/// @param pwd The structure containing pointers to the entry fields. +/// @param buf The buffer where the strings should be stored. +/// @param buflen The lenght of the buffer. +/// @param result A pointer to the result or NULL is stored here. +/// @return If the entry was found returns zero and set *result to pwd, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getpwnam_r(const char *name, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result); + +/// @brief Provides the same information as getpwuid but it stores the +/// results inside pwd, and the string information are store store +/// inside `buf`. +/// @param uid The uid to search inside the database. +/// @param pwd The structure containing pointers to the entry fields. +/// @param buf The buffer where the strings should be stored. +/// @param buflen The lenght of the buffer. +/// @param result A pointer to the result or NULL is stored here. +/// @return If the entry was found returns zero and set *result to pwd, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getpwuid_r(uid_t uid, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result); diff --git a/libc/inc/sched.h b/libc/inc/sched.h new file mode 100644 index 0000000..d82546e --- /dev/null +++ b/libc/inc/sched.h @@ -0,0 +1,29 @@ +/// MentOS, The Mentoring Operating system project +/// @file sched.h +/// @brief Structures and functions for managing the scheduler. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/types.h" +#include "time.h" +#include "stdbool.h" + +/// @brief Structure that describes scheduling parameters. +typedef struct sched_param_t { + /// Static execution priority. + int sched_priority; + /// Expected period of the task + time_t period; + /// Absolute deadline + time_t deadline; + /// Absolute time of arrival of the task + time_t arrivaltime; + /// Is task periodic? + bool_t is_periodic; +} sched_param_t; + +int sched_setparam(pid_t pid, const sched_param_t *param); + +int sched_getparam(pid_t pid, sched_param_t *param); + +int waitperiod(); diff --git a/libc/inc/signal.h b/libc/inc/signal.h new file mode 100644 index 0000000..eebb828 --- /dev/null +++ b/libc/inc/signal.h @@ -0,0 +1,273 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.h +/// @brief Signals definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" + +/// @brief List of signals. +typedef enum { + SIGHUP = 1, ///< Hang up detected on controlling terminal or death of controlling process. + SIGINT = 2, ///< Issued if the user sends an interrupt signal (Ctrl + C). + SIGQUIT = 3, ///< Issued if the user sends a quit signal (Ctrl + D). + SIGILL = 4, ///< Illegal Instruction. + SIGTRAP = 5, ///< Trace/breakpoint trap. + SIGABRT = 6, ///< Abort signal from abort(). + SIGEMT = 7, ///< Emulator trap. + SIGFPE = 8, ///< Floating-point arithmetic exception. + SIGKILL = 9, ///< If a process gets this signal it must quit immediately and will not perform any clean-up operations. + SIGBUS = 10, ///< Bus error (bad memory access). + SIGSEGV = 11, ///< Invalid memory reference. + SIGSYS = 12, ///< Bad system call (SVr4). + SIGPIPE = 13, ///< Broken pipe: write to pipe with no readers. + SIGALRM = 14, ///< Alarm clock signal (used for timers). + SIGTERM = 15, ///< Software termination signal (sent by kill by default). + SIGUSR1 = 16, ///< User-defined signal 1. + SIGUSR2 = 17, ///< User-defined signal 2. + SIGCHLD = 18, ///< Child stopped or terminated. + SIGPWR = 19, ///< Power failure. + SIGWINCH = 20, ///< Window resize signal. + SIGURG = 21, ///< Urgent condition on socket. + SIGPOLL = 22, ///< Pollable event. + SIGSTOP = 23, ///< Stop process. + SIGTSTP = 24, ///< Stop typed at terminal. + SIGCONT = 25, ///< Continue if stopped. + SIGTTIN = 26, ///< Terminal input for background process. + SIGTTOU = 27, ///< Terminal output for background process. + SIGVTALRM = 28, ///< Virtual alarm clock. + SIGPROF = 29, ///< Profiling timer expired. + SIGXCPU = 30, ///< CPU time limit exceeded. + SIGXFSZ = 31, ///< File size limit exceeded. + NSIG +} signal_type_t; + +/// @brief Codes that indentify the sender of a signal. +typedef enum { + SI_NOINFO, ///< Unable to determine complete signal information. + + // Signal : - + // Enabled fields : si_pid, si_uid + SI_USER, ///< Signal sent by kill(), pthread_kill(), raise(), abort() or alarm(). + + // Signal : - + // Enabled fields : - + SI_KERNEL, ///< Generic kernel function + + // Signal : - + // Enabled fields : si_pid, si_uid, si_value + SI_QUEUE, ///< Signal was sent by sigqueue(). + SI_TIMER, ///< Signal was generated by expiration of a timer set by timer_settimer(). + SI_ASYNCIO, ///< Signal was generated by completion of an asynchronous I/O request. + SI_MESGQ, ///< Signal was generated by arrival of a message on an empty message queue. + + // Signal : SIGILL + // Enabled fields : si_addr (address of failing instruction) + ILL_ILLOPC, ///< Illegal opcode. + ILL_ILLOPN, ///< Illegal operand. + ILL_ILLADR, ///< Illegal addressing mode. + ILL_ILLTRP, ///< Illegal trap. + ILL_PRVOPC, ///< Privileged opcode. + ILL_PRVREG, ///< Privileged register. + ILL_COPROC, ///< Coprocessor error. + ILL_BADSTK, ///< Internal stack error. + + // Signal : SIGFPE + // Enabled fields : si_addr (address of failing instruction) + FPE_INTDIV, ///< Integer divide-by-zero. + FPE_INTOVF, ///< Integer overflow. + FPE_FLTDIV, ///< Floating point divide-by-zero. + FPE_FLTOVF, ///< Floating point overflow. + FPE_FLTUND, ///< Floating point underflow. + FPE_FLTRES, ///< Floating point inexact result. + FPE_FLTINV, ///< Invalid floating point operation. + FPE_FLTSUB, ///< Subscript out of range. + + // Signal : SIGSEGV + // Enabled fields : si_addr (address of faulting memory reference) + SEGV_MAPERR, ///< Address not mapped. + SEGV_ACCERR, ///< Invalid permissions. + + // Signal : SIGBUS + // Enabled fields : si_addr (address of faulting memory reference) + BUS_ADRALN, ///< Invalid address alignment. + BUS_ADRERR, ///< Non-existent physical address. + BUS_OBJERR, ///< Object-specific hardware error. + + // Signal : SIGTRAP + // Enabled fields : - + TRAP_BRKPT, ///< Process breakpoint. + TRAP_TRACE, ///< Process trace trap. + + // Signal : SIGCHLD + // Enabled fields : si_pid (child process ID) + // si_uid (real user ID of process that sent the signal) + // si_status (exit value or signal) + CLD_EXITED, ///< Child has exited. + CLD_KILLED, ///< Child has terminated abnormally and did not create a core file. + CLD_DUMPED, ///< Child has terminated abnormally and created a core file. + CLD_TRAPPED, ///< Traced child has trapped. + CLD_STOPPED, ///< Child has stopped. + CLD_CONTINUED, ///< Stopped child has continued. + + // Signal : SIGIO/SIGPOLL + // Enabled fields : si_band + POLL_IN, ///< Data input available. + POLL_OUT, ///< Output buffers available. + POLL_MSG, ///< Input message available. + POLL_ERR, ///< I/O error. + POLL_PRI, ///< High priority input available. + POLL_HUP, ///< Device disconnected. +} signal_sender_code_t; + +/// @brief Defines what to do with the provided signal mask. +typedef enum { + /// @brief The set of blocked signals is the union of the current set + /// and the set argument. + SIG_BLOCK, + /// @brief The signals in set are removed from the current set of + /// blocked signals. It is permissible to attempt to unblock + /// a signal which is not blocked. + SIG_UNBLOCK, + /// @brief The set of blocked signals is set to the argument set. + SIG_SETMASK +} sigmask_how_t; + +/// @defgroup SigactionFlags Flags associated with a sigaction. +/// @{ + +#define SA_NOCLDSTOP 0x00000001U ///< Turn off SIGCHLD when children stop. +#define SA_NOCLDWAIT 0x00000002U ///< Flag on SIGCHLD to inhibit zombies. +#define SA_SIGINFO 0x00000004U ///< sa_sigaction specifies the signal-handling function for signum. +#define SA_ONSTACK 0x08000000U ///< Indicates that a registered stack_t will be used. +#define SA_RESTART 0x10000000U ///< Flag to get restarting signals (which were the default long ago) +#define SA_NODEFER 0x40000000U ///< Prevents the current signal from being masked in the handler. +#define SA_RESETHAND 0x80000000U ///< Clears the handler when the signal is delivered. + +/// @} + +/// Type of a signal handler. +typedef void (*sighandler_t)(int); + +#define SIG_DFL ((sighandler_t)0) ///< Default signal handling. +#define SIG_IGN ((sighandler_t)1) ///< Ignore signal. +#define SIG_ERR ((sighandler_t)-1) ///< Error return from signal. + +/// @brief Structure used to mask and unmask signals. +/// @details +/// Each unsigned long consists of 32 bits, thus, the maximum number of signals +/// that may be declared is 64. +/// Signals are divided into two cathegories, identified by the two unsigned longs: +/// [ 1, 31] corresponds to normal signals; +/// [32, 64] corresponds to real-time signals. +typedef struct sigset_t { + /// Signals divided into two cathegories. + unsigned long sig[2]; +} sigset_t; + +/// @brief Holds the information on how to handle a specific signal. +typedef struct sigaction_t { + /// This field specifies the type of action to be performed; its value can be a pointer + /// to the signal handler, SIG_DFL (that is, the value 0) to specify that the default + /// action is performed, or SIG_IGN (that is, the value 1) to specify that the signal is + /// ignored. + sighandler_t sa_handler; + /// This sigset_t variable specifies the signals to be masked when running the signal handler + sigset_t sa_mask; + /// This set of flags specifies how the signal must be handled; + unsigned int sa_flags; +} sigaction_t; + +/// @brief Data passed with signal info. +typedef union sigval { + int sival_int; ///< Integer value. + void *sival_ptr; ///< Pointer value. +} sigval_t; + +/// @brief Stores information about an occurrence of a specific signal. +typedef struct siginfo_t { + /// The signal number. + int si_signo; + /// A code identifying who raised the signal (see signal_sender_code_t). + int si_code; + /// Signal value. + sigval_t si_value; + /// The error code of the instruction that caused the signal to be raised, or 0 if there was no error. + int si_errno; + /// Process ID of sending process. + pid_t si_pid; + /// Real user ID of sending process. + uid_t si_uid; + /// Address at which fault occurred. + void *si_addr; + /// Exit value or signal for process termination. + int si_status; + /// Band event for SIGPOLL/SIGIO. + int si_band; +} siginfo_t; + +/// @brief Send signal to a process. +/// @param pid The pid of the process to which we send the signal. +/// @param sig The type of signal to send. +/// @return On success 0, on error -1 and errno is set appropriately. +int kill(pid_t pid, int sig); + +/// @brief Sets the disposition of the signal signum to handler. +/// @param signum The signal number. +/// @param handler The handler for the signal. +/// @return The previous value of the signal handler, or SIG_ERR on error. +sighandler_t signal(int signum, sighandler_t handler); + +/// @brief Examine and change a signal action. +/// @param signum Specifies the signal and can be any valid signal except SIGKILL and SIGSTOP. +/// @param act If non-NULL, the new action for signal signum is installed from act. +/// @param oldact If non-NULL, the previous action is saved in oldact. +/// @return returns 0 on success; on error, -1 is returned, and errno is set to indicate the error. +int sigaction(int signum, const sigaction_t *act, sigaction_t *oldact); + +/// @brief Examine and change blocked signals. +/// @param how Determines the behavior of the call. +/// @param set The set of signals to manage by the function. +/// @param oldset If non-NULL, the previous value of the signal mask is stored here. +/// @return returns 0 on success, and -1 on error (errno is set to indicate the cause). +/// @details +/// If set is NULL, then the signal mask is unchanged (i.e., how is +/// ignored), but the current value of the signal mask is +/// nevertheless returned in oldset (if it is not NULL). +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); + +/// @brief Returns the string describing the given signal. +/// @param sig The signal to inquire. +/// @return String representing the signal. +const char *strsignal(int sig); + +/// @brief Prepare an empty set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigemptyset(sigset_t *set); + +/// @brief Prepare a full set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigfillset(sigset_t *set); + +/// @brief Adds the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigaddset(sigset_t *set, int signum); + +/// @brief Removes the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigdelset(sigset_t *set, int signum); + +/// @brief Checks if the given signal is part of the set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 1 if signum is a member of set, +/// 0 if signum is not a member, and -1 on error. +int sigismember(sigset_t *set, int signum); diff --git a/mentos/inc/libc/stdarg.h b/libc/inc/stdarg.h similarity index 75% rename from mentos/inc/libc/stdarg.h rename to libc/inc/stdarg.h index 6890b22..097546e 100644 --- a/mentos/inc/libc/stdarg.h +++ b/libc/inc/stdarg.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file stdarg.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Contains the macros required to manage variable number of arguments. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -16,8 +16,8 @@ typedef char *va_list; /// @brief Amount of space required in an argument list (ie. the stack) for an /// argument of type t. -#define va_size(t) \ - (((sizeof(t) + sizeof(va_item) - 1) / sizeof(va_item)) * sizeof(va_item)) +#define va_size(t) \ + (((sizeof(t) + sizeof(va_item) - 1) / sizeof(va_item)) * sizeof(va_item)) /// @brief The start of a variadic list. #define va_start(ap, last_arg) (ap = ((va_list)(&last_arg) + va_size(last_arg))) diff --git a/libc/inc/stdbool.h b/libc/inc/stdbool.h new file mode 100644 index 0000000..97b48b5 --- /dev/null +++ b/libc/inc/stdbool.h @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdbool.h +/// @brief Defines the boolean values. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Define boolean value. +typedef enum bool_t { + false, ///< [0] False. + true ///< [1] True. +} __attribute__((__packed__)) bool_t; diff --git a/libc/inc/stddef.h b/libc/inc/stddef.h new file mode 100644 index 0000000..ac2c81b --- /dev/null +++ b/libc/inc/stddef.h @@ -0,0 +1,71 @@ +/// MentOS, The Mentoring Operating system project +/// @file stddef.h +/// @brief Define basic data types. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifndef NULL +/// @brief Define NULL. +#define NULL ((void *)0) +#endif + +#ifndef EOF +/// @brief Define End-Of-File. +#define EOF (-1) +#endif + +/// @brief Define the size of a buffer. +#define BUFSIZ 512 + +/// Is the signed integer type of the result of subtracting two pointers. +typedef long signed int ptrdiff_t; + +/// Define the byte type. +typedef unsigned char byte_t; + +/// Define the generic size type. +typedef unsigned long size_t; + +/// Define the generic signed size type. +typedef long ssize_t; + +/// Define the type of an inode. +typedef unsigned int ino_t; + +/// Used for device IDs. +typedef unsigned int dev_t; + +/// The type of user-id. +typedef unsigned int uid_t; + +/// The type of group-id. +typedef unsigned int gid_t; + +/// The type of offset. +typedef long int off_t; + +/// The type of mode. +typedef unsigned int mode_t; + +/// This data-type is used to set protection bits of pages. +typedef unsigned int pgprot_t; + +/// It evaluates to the offset (in bytes) of a given member within +/// a struct or union type, an expression of type size_t. +#define offsetof(type, member) \ + ((size_t) & (((type *)0)->member)) + +/// Retrieve an enclosing structure from a pointer to a nested element. +#if 1 +#define container_of(ptr, type, member) \ + ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)ptr - offsetof(type, member))) +#endif + +/// Returns the alignment, in bytes, of the specified type. +#define alignof(type) offsetof( \ + struct { char c; type member; }, member) diff --git a/mentos/inc/libc/stdint.h b/libc/inc/stdint.h similarity index 88% rename from mentos/inc/libc/stdint.h rename to libc/inc/stdint.h index fa4b624..d2b6449 100644 --- a/mentos/inc/libc/stdint.h +++ b/libc/inc/stdint.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file stdint.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Standard integer data-types. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once diff --git a/libc/inc/stdio.h b/libc/inc/stdio.h new file mode 100644 index 0000000..eceb086 --- /dev/null +++ b/libc/inc/stdio.h @@ -0,0 +1,129 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdio.h +/// @brief Standard I/0 functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdarg.h" +#include "stddef.h" + +/// @brief The maximum number of digits of an integer. +#define MAX_DIGITS_IN_INTEGER 11 +/// @brief The size of 'gets' buffer. +#define GETS_BUFFERSIZE 255 + +#ifndef EOF +/// @brief Define the End-Of-File. +#define EOF (-1) +#endif + +#define SEEK_SET 0 ///< The file offset is set to offset bytes. +#define SEEK_CUR 1 ///< The file offset is set to its current location plus offset bytes. +#define SEEK_END 2 ///< The file offset is set to the size of the file plus offset bytes. + +/// @brief Writes the given character to the standard output (stdout). +/// @param character The character to send to stdout. +void putchar(int character); + +/// @brief Writes the string pointed by str to the standard output (stdout) +/// and appends a newline character. +/// @param str The string to send to stdout. +void puts(char *str); + +/// @brief Returns the next character from the standard input (stdin). +/// @return The character received from stdin. +int getchar(); + +/// @brief Reads characters from the standard input (stdin). +/// @param str Where the characters are stored. +/// @return The string received from standard input. +char *gets(char *str); + +#ifndef __KERNEL__ +/// @brief Same as getchar but reads from the given file descriptor. +/// @param fd The file descriptor from which it reads. +/// @return The read character. +int fgetc(int fd); + +/// @brief Same as gets but reads from the given file descriptor. +/// @param buf The buffer where the string should be placed. +/// @param n The amount of characters to read. +/// @param fd The file descriptor from which it reads. +/// @return The read string. +char *fgets(char *buf, int n, int fd); +#endif + +/// @brief Convert the given string to an integer. +/// @param str The string to convert. +/// @return The integer contained inside the string. +int atoi(const char *str); + +/// @brief Converts the initial part of `str` to a long int value according to +/// the given base, which. +/// @param str This is the string containing the integral number. +/// @param endptr Set to the character after the numerical value. +/// @param base The base must be between 2 and 36 inclusive, or special 0. +/// @return Integral number as a long int value, else zero value is returned. +long strtol(const char *str, char **endptr, int base); + +/// @brief Write formatted output to stdout. +/// @param fmt The format string. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int printf(const char *fmt, ...); + +/// @brief Write formatted output to `str`. +/// @param str The buffer where the formatted string will be placed. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int sprintf(char *str, const char *fmt, ...); + +#ifndef __KERNEL__ +/// @brief The same as sprintf, but it putput on file. +/// @param fd The file descriptor associated with the file. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int fprintf(int fd, const char *fmt, ...); +#endif + +/// @brief Write formatted data from variable argument list to string. +/// @param str Pointer to a buffer where the resulting C-string is stored. +/// @param fmt Format string, following the same specifications as printf. +/// @param args A variable arguments list. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int vsprintf(char *str, const char *fmt, va_list args); + +#ifndef __KERNEL__ +/// @brief Read formatted input from stdin. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments where the values are stored. +/// @return On success, the function returns the number of items of the +/// argument list successfully filled. EOF otherwise. +int scanf(const char *fmt, ...); +#endif + +/// @brief Read formatted data from string. +/// @param str String processed as source to retrieve the data. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments where the values are stored. +/// @return On success, the function returns the number of items of the +/// argument list successfully filled. EOF otherwise. +int sscanf(const char *str, const char *fmt, ...); + +#ifndef __KERNEL__ +/// @brief The same as sscanf but the source is a file. +/// @param fd The file descriptor associated with the file. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments where the values are stored. +/// @return On success, the function returns the number of items of the +/// argument list successfully filled. EOF otherwise. +int fscanf(int fd, const char *fmt, ...); +#endif \ No newline at end of file diff --git a/libc/inc/stdlib.h b/libc/inc/stdlib.h new file mode 100644 index 0000000..77be723 --- /dev/null +++ b/libc/inc/stdlib.h @@ -0,0 +1,71 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdlib.h +/// @brief Useful generic functions and macros. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Returns the number of usable bytes in the block pointed to by ptr. +/// @param ptr The pointer for which we want to retrieve the usable size. +/// @return The number of usable bytes in the block of allocated memory +/// pointed to by ptr. If ptr is not a valid pointer, 0 is returned. +size_t malloc_usable_size(void *ptr); + +/// @brief Provides dynamically allocated memory. +/// @param size The amount of memory to allocate. +/// @return A pointer to the allocated memory. +void *malloc(unsigned int size); + +/// @brief Allocates a block of memory for an array of num elements. +/// @param num The number of elements. +/// @param size The size of an element. +/// @return A pointer to the allocated memory. +void *calloc(size_t num, size_t size); + +/// @brief Reallocates the given area of memory. +/// @param ptr The pointer to the memory to reallocate. +/// @param size The new size for the memory. +/// @return A pointer to the new portion of memory. +/// @details +/// It must be previously allocated by malloc(), calloc() or realloc() and +/// not yet freed with a call to free or realloc. Otherwise, the results +/// are undefined. +void *realloc(void *ptr, size_t size); + +/// @brief Frees dynamically allocated memory. +/// @param ptr The pointer to the allocated memory. +void free(void *ptr); + +/// The maximum value returned by the rand function. +#define RAND_MAX ((1U << 31U) - 1U) + +/// @brief Allows to set the seed of the random value generator. +/// @param x The new seed. +void srand(int x); + +/// @brief Generates a random value. +/// @return The random value. +int rand(); + +/// @brief Cause an abnormal program termination with core-dump. +void abort(); + +/// @brief Tries to adds the variable to the environment. +/// @param name Name of the variable. +/// @param value Value of the variable. +/// @param overwrite Override existing variable value or not. +/// @return Zero on success, or -1 on error with errno indicating the cause. +int setenv(const char *name, const char *value, int overwrite); + +/// @brief Tries to remove the variable from the environment. +/// @param name Name of the variable. +/// @return Zero on success, or -1 on error with errno indicating the cause. +int unsetenv(const char *name); + +/// @brief Returns the value of the given variable. +/// @param name Name of the variable. +/// @return A pointer to the value, or NULL if there is no match. +char *getenv(const char *name); diff --git a/libc/inc/strerror.h b/libc/inc/strerror.h new file mode 100644 index 0000000..575de9b --- /dev/null +++ b/libc/inc/strerror.h @@ -0,0 +1,14 @@ +/// MentOS, The Mentoring Operating system project +/// @file strerror.h +/// @brief Contains the function that transfornms an errno into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include + +/// @brief Returns the string representing the error number. +/// @param errnum The error number. +/// @return The string representing the error number. +char *strerror(int errnum); diff --git a/libc/inc/string.h b/libc/inc/string.h new file mode 100644 index 0000000..315bc13 --- /dev/null +++ b/libc/inc/string.h @@ -0,0 +1,311 @@ +/// MentOS, The Mentoring Operating system project +/// @file string.h +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Copies the first num characters of source to destination. +/// @param destination Pointer to the destination array where the content is to be copied. +/// @param source String to be copied. +/// @param num Maximum number of characters to be copied from source. +/// @return destination is returned. +char *strncpy(char *destination, const char *source, size_t num); + +/// @brief Compares up to n characters of s1 to those of s2. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @param n Maximum number of characters to compare. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int strncmp(const char *s1, const char *s2, size_t n); + +/// @brief Case insensitive string compare. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int stricmp(const char *s1, const char *s2); + +/// @brief Case-insensitively compare up to n characters of s1 to those of s2. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @param n Maximum number of characters to compare. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int strnicmp(const char *s1, const char *s2, size_t n); + +/// @brief Returns a pointer to the first occurrence of ch in str. +/// @param s The string where the search is performed. +/// @param ch Character to be located. +/// @return A pointer to the first occurrence of character in str. +char *strchr(const char *s, int ch); + +/// @brief Returns a pointer to the last occurrence of ch in str. +/// @param s The string where the search is performed. +/// @param ch Character to be located. +/// @return A pointer to the last occurrence of character in str. +char *strrchr(const char *s, int ch); + +/// @brief Returns a pointer to the first occurrence of s2 in s1, +/// or NULL if s2 is not part of s1. +/// @param s1 String to be scanned +/// @param s2 String containing the sequence of characters to match. +/// @return A pointer to the first occurrence in s1 of the entire +/// sequence of characters specified in s2, or a null pointer +/// if the sequence is not present in s1. +char *strstr(const char *s1, const char *s2); + +/// @brief Returns the length of the initial portion of string which consists +/// only of characters that are part of control. +/// @param string String to be scanned. +/// @param control String containing the characters to match. +/// @return The number of characters in the initial segment of string which +/// consist only of characters from control. +size_t strspn(const char *string, const char *control); + +/// @brief Calculates the length of the initial segment of string which +/// consists entirely of characters not in control. +/// @param string String to be scanned. +/// @param control String containing the characters to match. +/// @return The number of characters in the initial segment of string which +/// consist only of characters that are not inside control. +size_t strcspn(const char *string, const char *control); + +/// @brief Finds the first character in the string string that matches any +/// character specified in control. +/// @param string String to be scanned. +/// @param control String containing the characters to match. +/// @return +/// A pointer to the first occurrence in string of any of the characters +/// that are part of control, or a null pointer if none of the characters +/// of control is found in string before the terminating null-character. +char *strpbrk(const char *string, const char *control); + +/// @brief Make a copy of the given string. +/// @param s String to duplicate. +/// @return On success, returns a pointer to the duplicated string. +/// On failure, returns NULL with errno indicating the cause. +char *strdup(const char *s); + +/// @brief Make a copy of at most n bytes of the given string. +/// @param s String to duplicate. +/// @param n The number of character to duplicate. +/// @return On success, returns a pointer to the duplicated string. +/// On failure, returns NULL with errno indicating the cause. +char *strndup(const char *s, size_t n); + +/// @brief Appends a copy of the string src to the string dst. +/// @param dst Pointer to the destination array, which should be large enough +/// to contain the concatenated resulting string. +/// @param src String to be appended. This should not overlap dst. +/// @return destination is returned. +char *strcat(char *dst, const char *src); + +/// @brief Appends a copy of the string src to the string dst, up to n bytes. +/// @param dst Pointer to the destination array, which should be large enough +/// to contain the concatenated resulting string. +/// @param src String to be appended. This should not overlap dst. +/// @param n The number of bytes to copy. +/// @return destination is returned. +char *strncat(char *dst, const char *src, size_t n); + +/// @brief Fill the string s with the character c. +/// @param s The string that you want to fill. +/// @param c The character that you want to fill the string with. +/// @return The address of the string, s. +char *strset(char *s, int c); + +/// @brief Fill the string s with the character c, up to the given length n. +/// @param s The string that you want to fill. +/// @param c The character that you want to fill the string with. +/// @param n The maximum number of bytes to fill. +/// @return The address of the string, s. +char *strnset(char *s, int c, size_t n); + +/// @brief Reverse the string s. +/// @param s The given string which is needed to be reversed. +/// @return The address of the string, s. +char *strrev(char *s); + +/// @brief Splits string into tokens. +/// @param str String to truncate. +/// @param delim String containing the delimiter characters. +/// @return If a token is found, a pointer to the beginning of the token. +/// Otherwise, a null pointer. +/// @details +/// Notice that str is modified by being broken into smaller strings (tokens). +/// A null pointer may be specified, in which case the function continues +/// scanning where a previous successful call to the function ended. +char *strtok(char *str, const char *delim); + +/// @brief This function is a reentrant version strtok(). +/// @param str String to truncate. +/// @param delim String containing the delimiter characters. +/// @param saveptr Pointer used internally to maintain context between calls. +/// @return +/// @details +/// The saveptr argument is a pointer to a char * variable that is used +/// internally by strtok_r() in order to maintain context between successive +/// calls that parse the same string. +/// On the first call to strtok_r(), str should point to the string to be +/// parsed, and the value of saveptr is ignored. In subsequent calls, str +/// should be NULL, and saveptr should be unchanged since the previous call. +char *strtok_r(char *str, const char *delim, char **saveptr); + +/// @brief Copies the values of num bytes from the location pointed by source +/// to the memory block pointed by destination. +/// @param dst Pointer to the destination array where the content is to be +/// copied, type-casted to a pointer of type void*. +/// @param src Pointer to the source of data to be copied, type-casted to +/// a pointer of type const void*. +/// @param n Number of bytes to copy. +/// @return A pointer to dst is returned. +void *memmove(void *dst, const void *src, size_t n); + +/// @brief Searches for the first occurrence of the character c (an unsigned +/// char) in the first n bytes of the string pointed to, by the +/// argument str. +/// @param ptr Pointer to the block of memory where the search is performed. +/// @param c Value to be located. +/// @param n Number of bytes to be analyzed. +/// @return A pointer to the first occurrence of value in the block of memory. +void *memchr(const void *ptr, int c, size_t n); + +/// @brief Converts a given string into lowercase. +/// @param s String which we want to convert into lowercase. +/// @return A pointer to s. +char *strlwr(char *s); + +/// @brief Converts a given string into uppercase. +/// @param s String which we want to convert into uppercase. +/// @return A pointer to s. +char *strupr(char *s); + +/// @brief The memccpy function copies no more than n bytes from memory +/// area src to memory area dest, stopping when the character c is +/// found. +/// @param dst Points to the destination memory area. +/// @param src Points to the source memory area. +/// @param c The delimiter used to stop. +/// @param n The maximum number of copied bytes. +/// @return A pointer to the next character in dst after c, or NULL if c +/// was not found in the first n characters of src. +void *memccpy(void *dst, const void *src, int c, size_t n); + +/// @brief Copy a block of memory, handling overlap. +/// @param dst Pointer to the destination. +/// @param src Pointer to the source. +/// @param num Number of bytes to be copied. +/// @return Pointer to the destination. +void *memcpy(void *dst, const void *src, size_t num); + +/// @brief Compares the first n bytes of str1 and str2. +/// @param ptr1 First pointer to block of memory. +/// @param ptr2 Second pointer to block of memory. +/// @param n Number of bytes to compare. +/// @return +/// Returns an integral value indicating the relationship between +/// the memory blocks: +/// <0 the first byte that does not match has a lower +/// value in ptr1 than in ptr2 +/// 0 the contents of both memory blocks are equal +/// >0 the first byte that does not match has a greater +/// value in ptr1 than in ptr2 +int memcmp(const void *ptr1, const void *ptr2, size_t n); + +/// @brief Sets the first num bytes of the block of memory pointed by ptr +/// to the specified value. +/// @param ptr Pointer to the block of memory to set. +/// @param value Value to be set. +/// @param num Number of bytes to be set to the given value. +/// @return The same ptr. +void *memset(void *ptr, int value, size_t num); + +/// @brief Copy the string src into the array dst. +/// @param dst The destination array where the content is to be copied. +/// @param src String to be copied. +/// @return A pointer to dst is returned. +char *strcpy(char *dst, const char *src); + +/// @brief Checks if the two strings are equal. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int strcmp(const char *s1, const char *s2); + +/// @brief Returns the length of the string s. +/// @param s Pointer to the null-terminated byte string to be examined. +/// @return The length of the null-terminated string str. +size_t strlen(const char *s); + +/// @brief Returns the number of characters inside s, excluding the +/// terminating null byte ('\0'), but at most count. +/// @param s Pointer to the null-terminated byte string to be examined. +/// @param maxlen The upperbound on the length. +/// @return Returns strlen(s), if that is less than maxlen, or maxlen +/// if there is no null terminating ('\0') among the first maxlen +/// characters pointed to by s. +size_t strnlen(const char *s, size_t maxlen); + +/// @brief Removes any whitespace characters from the beginning and end of str. +/// @param str The string to trim. +/// @return A pointer to str. +char *trim(char *str); + +/// @brief Separate the given string based on a given delimiter. +/// @param stringp The string to separate. +/// @param delim The delimiter used to separate the string. +/// @return Returns a pointer to stringp. +/// @details +/// Finds the first token in stringp, that is delimited by one of the bytes in +/// the string delim. This token is terminated by overwriting the delimiter +/// with a null byte ('\0'), and *stringp is updated to point past the token. +/// In case no delimiter was found, the token is taken to be the entire string +/// *stringp, and *stringp is made NULL. +char *strsep(char **stringp, const char *delim); + +/// @brief Move the number "num" into a string. +/// @param buffer The string containing the number. +/// @param num The number to convert. +/// @param base The base used to convert. +/// @return A pointer to buffer. +char *itoa(char *buffer, unsigned int num, unsigned int base); + +/// @brief Replaces the occurrences of find with replace inside str. +/// @param str The string to manipulate. +/// @param find The character to replace. +/// @param replace The character used to replace. +/// @return A pointer to str. +char *replace_char(char *str, char find, char replace); + +/// @brief Converts a file mode (the type and permission information associated +/// with an inode) into a symbolic string which is stored in the location +/// referenced by p. +/// @param mode File mode that encodes access permissions and file type. +/// @param p Buffer used to hold the string representation of file mode m. +void strmode(mode_t mode, char *p); \ No newline at end of file diff --git a/libc/inc/sys/bitops.h b/libc/inc/sys/bitops.h new file mode 100644 index 0000000..1c65468 --- /dev/null +++ b/libc/inc/sys/bitops.h @@ -0,0 +1,41 @@ +/// MentOS, The Mentoring Operating system project +/// @file bitops.h +/// @brief Bitmasks functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define bit_set(V, B) ((V) | (1U << (B))) ///< Sets the given bit. +#define bit_clear(V, B) ((V) & ~(1U << (B))) ///< Clears the given bit. +#define bit_flip(V, B) ((V) ^ (1U << (B))) ///< Flips the given bit. +#define bit_set_assign(V, B) ((V) |= (1U << (B))) ///< Sets the given bit, permanently. +#define bit_clear_assign(V, B) ((V) &= ~(1U << (B))) ///< Clears the given bit, permanently. +#define bit_flip_assign(V, B) ((V) ^= (1U << (B))) ///< Flips the given bit, permanently. +#define bit_check(V, B) ((V) & (1U << (B))) ///< Checks if the given bit is 1. + +#define bitmask_set(V, M) ((V) | (M)) ///< Sets the bits identified by the mask. +#define bitmask_clear(V, M) ((V) & ~(M)) ///< Clears the bits identified by the mask. +#define bitmask_flip(V, M) ((V) ^ (M)) ///< Flips the bits identified by the mask. +#define bitmask_set_assign(V, M) ((V) |= (M)) ///< Sets the bits identified by the mask, permanently. +#define bitmask_clear_assign(V, M) ((V) &= ~(M)) ///< Clears the bits identified by the mask, permanently. +#define bitmask_flip_assign(V, M) ((V) ^= (M)) ///< Flips the bits identified by the mask, permanently. +#define bitmask_check(V, M) ((V) & (M)) ///< Checks if the bits identified by the mask are all 1. + +/// @brief Finds the first bit at zero, starting from the less significative bit. +static inline int find_first_zero(unsigned long value) +{ + for (int i = 0; i < 32; ++i) + if (!bit_check(value, i)) + return i; + return 0; +} + +/// @brief Finds the first bit not zero, starting from the less significative bit. +static inline int find_first_non_zero(unsigned long value) +{ + for (int i = 0; i < 32; ++i) + if (bit_check(value, i)) + return i; + return 0; +} \ No newline at end of file diff --git a/libc/inc/sys/dirent.h b/libc/inc/sys/dirent.h new file mode 100644 index 0000000..1c6d185 --- /dev/null +++ b/libc/inc/sys/dirent.h @@ -0,0 +1,50 @@ +/// MentOS, The Mentoring Operating system project +/// @file dirent.h +/// @brief Functions used to manage directories. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "limits.h" +#include "stddef.h" + +/// File types for `d_type'. +enum { + DT_UNKNOWN = 0, + DT_FIFO = 1, + DT_CHR = 2, + DT_DIR = 4, + DT_BLK = 6, + DT_REG = 8, + DT_LNK = 10, + DT_SOCK = 12, + DT_WHT = 14 +}; + +static const char dt_char_array[] = { + '?', // DT_UNKNOWN = 0, + 'p', //DT_FIFO = 1, + 'c', //DT_CHR = 2, + '*', + 'd', //DT_DIR = 4, + '*', + 'b', //DT_BLK = 6, + '*', + '-', //DT_REG = 8, + '*', + 'l', //DT_LNK = 10, + '*', + 's', //DT_SOCK = 12, + '*', + '?', //DT_WHT = 14 +}; + +/// Directory entry. +typedef struct dirent_t { + ino_t d_ino; ///< Inode number. + off_t d_off; ///< Offset to next linux_dirent. + unsigned short d_reclen; ///< Length of this linux_dirent. + unsigned short d_type; ///< type of the directory entry. + char d_name[NAME_MAX]; ///< Filename (null-terminated) +} dirent_t; \ No newline at end of file diff --git a/libc/inc/sys/errno.h b/libc/inc/sys/errno.h new file mode 100644 index 0000000..1608340 --- /dev/null +++ b/libc/inc/sys/errno.h @@ -0,0 +1,138 @@ +/// MentOS, The Mentoring Operating system project +/// @file errno.h +/// @brief System call errors definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +extern int *__geterrno(); + +/// Provide easy access to the error number. +#define errno (*__geterrno()) + +#define EPERM 1 ///< Operation not permitted. +#define ENOENT 2 ///< No such file or directory. +#define ESRCH 3 ///< No such process. +#define EINTR 4 ///< Interrupted system call. +#define EIO 5 ///< I/O error. +#define ENXIO 6 ///< No such device or address. +#define E2BIG 7 ///< Arg list too long. +#define ENOEXEC 8 ///< Exec format error. +#define EBADF 9 ///< Bad file number. +#define ECHILD 10 ///< No child processes. +#define EAGAIN 11 ///< Try again. +#define ENOMEM 12 ///< Out of memory. +#define EACCES 13 ///< Permission denied. +#define EFAULT 14 ///< Bad address. +#define ENOTBLK 15 ///< Block device required. +#define EBUSY 16 ///< Device or resource busy. +#define EEXIST 17 ///< File exists. +#define EXDEV 18 ///< Cross-device link. +#define ENODEV 19 ///< No such device. +#define ENOTDIR 20 ///< Not a directory. +#define EISDIR 21 ///< Is a directory. +#define EINVAL 22 ///< Invalid argument. +#define ENFILE 23 ///< File table overflow. +#define EMFILE 24 ///< Too many open files. +#define ENOTTY 25 ///< Not a typewriter. +#define ETXTBSY 26 ///< Text file busy. +#define EFBIG 27 ///< File too large. +#define ENOSPC 28 ///< No space left on device. +#define ESPIPE 29 ///< Illegal seek. +#define EROFS 30 ///< Read-only file system. +#define EMLINK 31 ///< Too many links. +#define EPIPE 32 ///< Broken pipe. +#define EDOM 33 ///< Math argument out of domain of func. +#define ERANGE 34 ///< Math result not representable. +#define EDEADLK 35 ///< Resource deadlock would occur. +#define ENAMETOOLONG 36 ///< File name too long. +#define ENOLCK 37 ///< No record locks available. +#define ENOSYS 38 ///< Function not implemented. +#define ENOTEMPTY 39 ///< Directory not empty. +#define ELOOP 40 ///< Too many symbolic links encountered. +#define EWOULDBLOCK EAGAIN ///< Operation would block. +#define ENOMSG 42 ///< No message of desired type. +#define EIDRM 43 ///< Identifier removed. +#define ECHRNG 44 ///< Channel number out of range. +#define EL2NSYNC 45 ///< Level 2 not synchronized. +#define EL3HLT 46 ///< Level 3 halted. +#define EL3RST 47 ///< Level 3 reset. +#define ELNRNG 48 ///< Link number out of range. +#define EUNATCH 49 ///< Protocol driver not attached. +#define ENOCSI 50 ///< No CSI structure available. +#define EL2HLT 51 ///< Level 2 halted. +#define EBADE 52 ///< Invalid exchange. +#define EBADR 53 ///< Invalid request descriptor. +#define EXFULL 54 ///< Exchange full. +#define ENOANO 55 ///< No anode. +#define EBADRQC 56 ///< Invalid request code. +#define EBADSLT 57 ///< Invalid slot. +#define EDEADLOCK EDEADLK ///< Resource deadlock would occur. +#define EBFONT 59 ///< Bad font file format. +#define ENOSTR 60 ///< Device not a stream. +#define ENODATA 61 ///< No data available. +#define ETIME 62 ///< Timer expired. +#define ENOSR 63 ///< Out of streams resources. +#define ENONET 64 ///< Machine is not on the network. +#define ENOPKG 65 ///< Package not installed. +#define EREMOTE 66 ///< Object is remote. +#define ENOLINK 67 ///< Link has been severed. +#define EADV 68 ///< Advertise error. +#define ESRMNT 69 ///< Srmount error. +#define ECOMM 70 ///< Communication error on send. +#define EPROTO 71 ///< Protocol error. +#define EMULTIHOP 72 ///< Multihop attempted. +#define EDOTDOT 73 ///< RFS specific error. +#define EBADMSG 74 ///< Not a data message. +#define EOVERFLOW 75 ///< Value too large for defined data type. +#define ENOTUNIQ 76 ///< Name not unique on network. +#define EBADFD 77 ///< File descriptor in bad state. +#define EREMCHG 78 ///< Remote address changed. +#define ELIBACC 79 ///< Can not access a needed shared library. +#define ELIBBAD 80 ///< Accessing a corrupted shared library. +#define ELIBSCN 81 ///< .lib section in a.out corrupted. +#define ELIBMAX 82 ///< Attempting to link in too many shared libraries. +#define ELIBEXEC 83 ///< Cannot exec a shared library directly. +#define EILSEQ 84 ///< Illegal byte sequence. +#define ERESTART 85 ///< Interrupted system call should be restarted. +#define ESTRPIPE 86 ///< Streams pipe error. +#define EUSERS 87 ///< Too many users. +#define ENOTSOCK 88 ///< Socket operation on non-socket. +#define EDESTADDRREQ 89 ///< Destination address required. +#define EMSGSIZE 90 ///< Message too long. +#define EPROTOTYPE 91 ///< Protocol wrong type for socket. +#define ENOPROTOOPT 92 ///< Protocol not available. +#define EPROTONOSUPPORT 93 ///< Protocol not supported. +#define ESOCKTNOSUPPORT 94 ///< Socket type not supported. +#define EOPNOTSUPP 95 ///< Operation not supported on transport endpoint. +#define EPFNOSUPPORT 96 ///< Protocol family not supported. +#define EAFNOSUPPORT 97 ///< Address family not supported by protocol. +#define EADDRINUSE 98 ///< Address already in use. +#define EADDRNOTAVAIL 99 ///< Cannot assign requested address. +#define ENETDOWN 100 ///< Network is down. +#define ENETUNREACH 101 ///< Network is unreachable. +#define ENETRESET 102 ///< Network dropped connection because of reset. +#define ECONNABORTED 103 ///< Software caused connection abort. +#define ECONNRESET 104 ///< Connection reset by peer. +#define ENOBUFS 105 ///< No buffer space available. +#define EISCONN 106 ///< Transport endpoint is already connected. +#define ENOTCONN 107 ///< Transport endpoint is not connected. +#define ESHUTDOWN 108 ///< Cannot send after transport endpoint shutdown. +#define ETOOMANYREFS 109 ///< Too many references: cannot splice. +#define ETIMEDOUT 110 ///< Connection timed out. +#define ECONNREFUSED 111 ///< Connection refused. +#define EHOSTDOWN 112 ///< Host is down. +#define EHOSTUNREACH 113 ///< No route to host. +#define EALREADY 114 ///< Operation already in progress. +#define EINPROGRESS 115 ///< Operation now in progress. +#define ESTALE 116 ///< Stale NFS file handle. +#define EUCLEAN 117 ///< Structure needs cleaning. +#define ENOTNAM 118 ///< Not a XENIX named type file. +#define ENAVAIL 119 ///< No XENIX semaphores available. +#define EISNAM 120 ///< Is a named type file. +#define EREMOTEIO 121 ///< Remote I/O error. +#define EDQUOT 122 ///< Quota exceeded. +#define ENOMEDIUM 123 ///< No medium found. +#define EMEDIUMTYPE 124 ///< Wrong medium type. +#define ENOTSCHEDULABLE 125 ///< The periodc process cannot be scheduled. diff --git a/libc/inc/sys/ioctl.h b/libc/inc/sys/ioctl.h new file mode 100644 index 0000000..d039852 --- /dev/null +++ b/libc/inc/sys/ioctl.h @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.h +/// @brief Input/Output ConTroL (IOCTL) functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Perform the I/O control operation specified by REQUEST on FD. +/// One argument may follow; its presence and type depend on REQUEST. +/// @param fd Must be an open file descriptor. +/// @param request The device-dependent request code +/// @param data An untyped pointer to memory. +/// @return Return value depends on REQUEST. Usually -1 indicates error. +int ioctl(int fd, unsigned long int request, void *data); \ No newline at end of file diff --git a/libc/inc/sys/reboot.h b/libc/inc/sys/reboot.h new file mode 100644 index 0000000..4a6dd3d --- /dev/null +++ b/libc/inc/sys/reboot.h @@ -0,0 +1,36 @@ +/// MentOS, The Mentoring Operating system project +/// @file reboot.h +/// @brief Defines the values required to issue a reboot. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC1 0xfee1dead +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2 672274793 +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2A 85072278 +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2B 369367448 +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2C 537993216 + +// Commands accepted by the _reboot() system call. +/// Restart system using default command and mode. +#define LINUX_REBOOT_CMD_RESTART 0x01234567 +/// Stop OS and give system control to ROM monitor, if any. +#define LINUX_REBOOT_CMD_HALT 0xCDEF0123 +/// Ctrl-Alt-Del sequence causes RESTART command. +#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF +/// Ctrl-Alt-Del sequence sends SIGINT to init task. +#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 +/// Stop OS and remove all power from system, if possible. +#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC +/// Restart system using given command string. +#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 +/// Suspend system using software suspend if compiled in. +#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 +/// Restart system using a previously loaded Linux kernel +#define LINUX_REBOOT_CMD_KEXEC 0x45584543 diff --git a/libc/inc/sys/stat.h b/libc/inc/sys/stat.h new file mode 100644 index 0000000..39fb7b2 --- /dev/null +++ b/libc/inc/sys/stat.h @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.h +/// @brief Stat functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once +#define __SYS_STAT_H + +#include "bits/stat.h" +#include "stddef.h" +#include "time.h" + +/// @brief Retrieves information about the file at the given location. +/// @param path The path to the file that is being inquired. +/// @param buf A structure where data about the file will be stored. +/// @return Returns a negative value on failure. +int stat(const char *path, stat_t *buf); + +/// @brief Retrieves information about the file at the given location. +/// @param fd The file descriptor of the file that is being inquired. +/// @param buf A structure where data about the file will be stored. +/// @return Returns a negative value on failure. +int fstat(int fd, stat_t *buf); + +/// @brief Creates a new directory at the given path. +/// @param path The path of the new directory. +/// @param mode The permission of the new directory. +/// @return Returns a negative value on failure. +int mkdir(const char *path, mode_t mode); + +/// @brief Removes the given directory. +/// @param path The path to the directory to remove. +/// @return Returns a negative value on failure. +int rmdir(const char *path); \ No newline at end of file diff --git a/libc/inc/sys/types.h b/libc/inc/sys/types.h new file mode 100644 index 0000000..42336b2 --- /dev/null +++ b/libc/inc/sys/types.h @@ -0,0 +1,70 @@ +/// MentOS, The Mentoring Operating system project +/// @file types.h +/// @brief Collection of Kernel datatype +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// The type of process id. +typedef signed int pid_t; + +/// The type of process user variable. +typedef unsigned int user_t; + +/// The type of process status. +typedef unsigned int status_t; + +/// Type for system keys. +typedef int key_t; + +/// Defines the list of flags of a process. +typedef enum eflags_list { + /// Carry flag. + EFLAG_CF = (1 << 0), + + /// Parity flag. + EFLAG_PF = (1 << 2), + + /// Auxiliary carry flag. + EFLAG_AF = (1 << 4), + + /// Zero flag. + EFLAG_ZF = (1 << 6), + + /// Sign flag. + EFLAG_SF = (1 << 7), + + /// Trap flag. + EFLAG_TF = (1 << 8), + + /// Interrupt enable flag. + EFLAG_IF = (1 << 9), + + /// Direction flag. + EFLAG_DF = (1 << 10), + + /// Overflow flag. + EFLAG_OF = (1 << 11), + + /// Nested task flag. + EFLAG_NT = (1 << 14), + + /// Resume flag. + EFLAG_RF = (1 << 16), + + /// Virtual 8086 mode flag. + EFLAG_VM = (1 << 17), + + /// Alignment check flag (486+). + EFLAG_AC = (1 << 18), + + /// Virutal interrupt flag. + EFLAG_VIF = (1 << 19), + + /// Virtual interrupt pending flag. + EFLAG_VIP = (1 << 20), + + /// ID flag. + EFLAG_ID = (1 << 21), +} eflags_list; diff --git a/libc/inc/sys/unistd.h b/libc/inc/sys/unistd.h new file mode 100644 index 0000000..b305d0f --- /dev/null +++ b/libc/inc/sys/unistd.h @@ -0,0 +1,223 @@ +/// MentOS, The Mentoring Operating system project +/// @file unistd.h +/// @brief Functions used to manage files. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" +#include "sys/dirent.h" +#include "stddef.h" + +#define STDIN_FILENO 0 ///< Standard input. +#define STDOUT_FILENO 1 ///< Standard output. +#define STDERR_FILENO 2 ///< Standard error output. + +/// @brief Read data from a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer. +/// @param nbytes The number of bytes to read. +/// @return The number of read characters. +ssize_t read(int fd, void *buf, size_t nbytes); + +/// @brief Write data into a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer collecting data to written. +/// @param nbytes The number of bytes to write. +/// @return The number of written bytes. +ssize_t write(int fd, void *buf, size_t nbytes); + +/// @brief Opens the file specified by pathname. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes +/// of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file +/// is created. +/// @return Returns a file descriptor, a small, nonnegative integer for +/// use in subsequent system calls. +int open(const char *pathname, int flags, mode_t mode); + +/// @brief Close a file descriptor. +/// @param fd The file descriptor. +/// @return The result of the operation. +int close(int fd); + +/// @brief Repositions the file offset inside a file. +/// @param fd The file descriptor of the file. +/// @param offset The offest to use for the operation. +/// @param whence The type of operation. +/// @return Upon successful completion, returns the resulting offset +/// location as measured in bytes from the beginning of the file. On +/// error, the value (off_t) -1 is returned and errno is set to +/// indicate the error. +off_t lseek(int fd, off_t offset, int whence); + +/// @brief Delete a name and possibly the file it refers to. +/// @param path The path to the file. +/// @return +int unlink(const char *path); + +/// @brief Wrapper for exit system call. +/// @param status The exit status. +extern void exit(int status); + +/// @brief Returns the process ID (PID) of the calling process. +/// @return pid_t process identifier. +extern pid_t getpid(); + +///@brief Return session id of the given process. +/// If pid == 0 return the SID of the calling process +/// If pid != 0 return the SID corresponding to the process having identifier == pid +///@param pid process identifier from wich we want the SID +///@return On success return SID of the session +/// Otherwise return -1 with errno set on: EPERM or ESRCH +extern pid_t getsid(pid_t pid); + +///@brief creates a new session if the calling process is not a +/// process group leader. The calling process is the leader of the +/// new session (i.e., its session ID is made the same as its process +/// ID). The calling process also becomes the process group leader +/// of a new process group in the session (i.e., its process group ID +/// is made the same as its process ID). +///@return On success return SID of the session just created +/// Otherwise return -1 with errno : EPERM +extern pid_t setsid(); + +///@brief returns the group ID of the calling process. +///@return GID of the current process +extern pid_t getgid(); + +///@brief sets the effective group ID of the calling process. +///@param pid process identifier to +///@return On success, zero is returned. +/// Otherwise returns -1 with errno set to :EINVAL or EPERM +extern int setgid(pid_t pid); + +/// @brief Returns the parent process ID (PPID) of the calling process. +/// @return pid_t parent process identifier. +extern pid_t getppid(); + +/// @brief Clone the calling process, but without copying the whole address space. +/// The calling process is suspended until the new process exits or is +/// replaced by a call to `execve'. +/// @return Return -1 for errors, 0 to the new process, and the process ID of +/// the new process to the old process. +extern pid_t fork(); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param path The absolute path to the binary file to execute. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execl(const char *path, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execlp(const char *file, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param path The absolute path to the binary file to execute. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list which contains as last argument the environment. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execle(const char *path, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list which contains as last argument the environment. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execlpe(const char *file, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument vector). +/// @param path The absolute path to the binary file to execute. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execv(const char *path, char *const argv[]); + +/// @brief Replaces the current process image with a new process image (argument vector). +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execvp(const char *file, char *const argv[]); + +/// @brief Replaces the current process image with a new process +/// image (argument vector), allows the caller to specify +/// the environment of the executed program via `envp`. +/// @param path The absolute path to the binary file to execute. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param envp A vector of one or more pointers to null-terminated strings that represent +/// the environment list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execve(const char *path, char *const argv[], char *const envp[]); + +/// @brief Replaces the current process image with a new process +/// image (argument vector), allows the caller to specify +/// the environment of the executed program via `envp`. +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param envp A vector of one or more pointers to null-terminated strings that represent +/// the environment list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execvpe(const char *file, char *const argv[], char *const envp[]); + +/// @brief Adds inc to the nice value for the calling thread. +/// @param inc The value to add to the nice. +/// @return On success, the new nice value is returned. On error, -1 is +/// returned, and errno is set appropriately. +int nice(int inc); + +/// @brief Reboots the system, or enables/disables the reboot keystroke. +/// @param magic1 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC1. +/// @param magic2 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC2. +/// @param cmd The command to send to the reboot. +/// @param arg Argument passed with some specific commands. +/// @return For the values of cmd that stop or restart the system, a +/// successful call to reboot() does not return. For the other cmd +/// values, zero is returned on success. In all cases, -1 is +/// returned on failure, and errno is set appropriately. +int reboot(int magic1, int magic2, unsigned int cmd, void *arg); + +/// @brief Get current working directory. +/// @param buf The array where the CWD will be copied. +/// @param size The size of the array. +/// @return On success, returns the same pointer to buf. +/// On failure, returnr NULL, and errno is set to indicate the error. +char *getcwd(char *buf, size_t size); + +/// @brief Changes the current working directory to the given path. +/// @param path The new current working directory. +int chdir(char const *path); + +/// @brief Is identical to chdir(), the only difference is that the +/// directory is given as an open file descriptor. +/// @param fd The file descriptor of the open directory. +int fchdir(int fd); + +/// Provide access to the directory entries. +/// @param fd The fd pointing to the opened directory. +/// @param dirp The buffer where de data should be placed. +/// @param count The size of the buffer. +/// @return On success, the number of bytes read is returned. On end of +/// directory, 0 is returned. On error, -1 is returned, and errno is set +/// appropriately. +int getdents(int fd, dirent_t *dirp, unsigned int count); + +/// @brief Send signal to calling thread after desired seconds. +int alarm(int seconds); \ No newline at end of file diff --git a/libc/inc/sys/utsname.h b/libc/inc/sys/utsname.h new file mode 100644 index 0000000..c3330b9 --- /dev/null +++ b/libc/inc/sys/utsname.h @@ -0,0 +1,30 @@ +/// MentOS, The Mentoring Operating system project +/// @file utsname.h +/// @brief Functions used to provide information about the machine & OS. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Maximum length of the string used by utsname. +#define SYS_LEN 257 + +/// @brief Holds information concerning the machine and the os. +typedef struct utsname_t { + /// The name of the system. + char sysname[SYS_LEN]; + /// The name of the node. + char nodename[SYS_LEN]; + /// Operating system release (e.g., "2.6.28"). + char release[SYS_LEN]; + /// The version of the OS. + char version[SYS_LEN]; + /// The name of the machine. + char machine[SYS_LEN]; +} utsname_t; + + +/// @brief Returns system information in the structure pointed to by buf. +/// @param buf Buffer where the info will be placed. +/// @return 0 on success, a negative value on failure. +int uname(utsname_t* buf); diff --git a/mentos/inc/sys/wait.h b/libc/inc/sys/wait.h similarity index 61% rename from mentos/inc/sys/wait.h rename to libc/inc/sys/wait.h index c82ae56..62bc49a 100644 --- a/mentos/inc/sys/wait.h +++ b/libc/inc/sys/wait.h @@ -1,13 +1,11 @@ /// MentOS, The Mentoring Operating system project /// @file wait.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Event management functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "types.h" - /// @brief Return immediately if no child is there to be waited for. #define WNOHANG 0x00000001 @@ -43,10 +41,30 @@ /// This macro can only be evaluated if WIFSTOPPED() returned nonzero. #define WSTOPSIG(status) (WEXITSTATUS(status)) +//==== Task States ============================================================ +#define TASK_RUNNING 0x00 ///< The process is either: 1) running on CPU or 2) waiting in a run queue. +#define TASK_INTERRUPTIBLE (1 << 0) ///< The process is sleeping, waiting for some event to occur. +#define TASK_UNINTERRUPTIBLE (1 << 1) ///< Similar to TASK_INTERRUPTIBLE, but it doesn't process signals. +#define TASK_STOPPED (1 << 2) ///< Stopped, it's not running, and not able to run. +#define TASK_TRACED (1 << 3) ///< Is being monitored by other processes such as debuggers. +#define EXIT_ZOMBIE (1 << 4) ///< The process has terminated. +#define EXIT_DEAD (1 << 5) ///< The final state. +//============================================================================== + +/// @brief Suspends the execution of the calling thread until ANY child has +/// changed state. +/// @param status Variable where the new status of the child is stored. +/// @return On error, -1 is returned, otherwise it returns the pid of the +/// child that has unlocked the wait. extern pid_t wait(int *status); /// @brief Suspends the execution of the calling thread until a child /// specified by pid argument has changed state. +/// @param pid Se details below for more information. +/// @param status Variable where the new status of the child is stored. +/// @param options Waitpid options. +/// @return On error, -1 is returned, otherwise it returns the pid of the +/// child that has unlocked the wait. /// @details /// By default, waitpid() waits only for terminated children, but this /// behavior is modifiable via the options argument, as described below. @@ -56,5 +74,4 @@ extern pid_t wait(int *status); /// equal to that of the calling process. /// > 0 meaning wait for the child whose process ID is equal to the /// value of pid. -/// @return On error, -1 is returned. extern pid_t waitpid(pid_t pid, int *status, int options); diff --git a/libc/inc/system/syscall_types.h b/libc/inc/system/syscall_types.h new file mode 100644 index 0000000..98bf013 --- /dev/null +++ b/libc/inc/system/syscall_types.h @@ -0,0 +1,308 @@ +/// MentOS, The Mentoring Operating system project +/// @file syscall_types.h +/// @brief System Call numbers. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define __NR_exit 1 ///< System-call number for `exit` +#define __NR_fork 2 ///< System-call number for `fork` +#define __NR_read 3 ///< System-call number for `read` +#define __NR_write 4 ///< System-call number for `write` +#define __NR_open 5 ///< System-call number for `open` +#define __NR_close 6 ///< System-call number for `close` +#define __NR_waitpid 7 ///< System-call number for `waitpid` +#define __NR_creat 8 ///< System-call number for `creat` +#define __NR_link 9 ///< System-call number for `link` +#define __NR_unlink 10 ///< System-call number for `unlink` +#define __NR_execve 11 ///< System-call number for `execve` +#define __NR_chdir 12 ///< System-call number for `chdir` +#define __NR_time 13 ///< System-call number for `time` +#define __NR_mknod 14 ///< System-call number for `mknod` +#define __NR_chmod 15 ///< System-call number for `chmod` +#define __NR_lchown 16 ///< System-call number for `lchown` +#define __NR_stat 18 ///< System-call number for `stat` +#define __NR_lseek 19 ///< System-call number for `lseek` +#define __NR_getpid 20 ///< System-call number for `getpid` +#define __NR_mount 21 ///< System-call number for `mount` +#define __NR_oldumount 22 ///< System-call number for `oldumount` +#define __NR_setuid 23 ///< System-call number for `setuid` +#define __NR_getuid 24 ///< System-call number for `getuid` +#define __NR_stime 25 ///< System-call number for `stime` +#define __NR_ptrace 26 ///< System-call number for `ptrace` +#define __NR_alarm 27 ///< System-call number for `alarm` +#define __NR_fstat 28 ///< System-call number for `fstat` +#define __NR_pause 29 ///< System-call number for `pause` +#define __NR_utime 30 ///< System-call number for `utime` +#define __NR_access 33 ///< System-call number for `access` +#define __NR_nice 34 ///< System-call number for `nice` +#define __NR_sync 36 ///< System-call number for `sync` +#define __NR_kill 37 ///< System-call number for `kill` +#define __NR_rename 38 ///< System-call number for `rename` +#define __NR_mkdir 39 ///< System-call number for `mkdir` +#define __NR_rmdir 40 ///< System-call number for `rmdir` +#define __NR_dup 41 ///< System-call number for `dup` +#define __NR_pipe 42 ///< System-call number for `pipe` +#define __NR_times 43 ///< System-call number for `times` +#define __NR_brk 45 ///< System-call number for `brk` +#define __NR_setgid 46 ///< System-call number for `setgid` +#define __NR_getgid 47 ///< System-call number for `getgid` +#define __NR_signal 48 ///< System-call number for `signal` +#define __NR_geteuid 49 ///< System-call number for `geteuid` +#define __NR_getegid 50 ///< System-call number for `getegid` +#define __NR_acct 51 ///< System-call number for `acct` +#define __NR_umount 52 ///< System-call number for `umount` +#define __NR_ioctl 54 ///< System-call number for `ioctl` +#define __NR_fcntl 55 ///< System-call number for `fcntl` +#define __NR_setpgid 57 ///< System-call number for `setpgid` +#define __NR_olduname 59 ///< System-call number for `olduname` +#define __NR_umask 60 ///< System-call number for `umask` +#define __NR_chroot 61 ///< System-call number for `chroot` +#define __NR_ustat 62 ///< System-call number for `ustat` +#define __NR_dup2 63 ///< System-call number for `dup2` +#define __NR_getppid 64 ///< System-call number for `getppid` +#define __NR_getpgrp 65 ///< System-call number for `getpgrp` +#define __NR_setsid 66 ///< System-call number for `setsid` +#define __NR_sigaction 67 ///< System-call number for `sigaction` +#define __NR_sgetmask 68 ///< System-call number for `sgetmask` +#define __NR_ssetmask 69 ///< System-call number for `ssetmask` +#define __NR_setreuid 70 ///< System-call number for `setreuid` +#define __NR_setregid 71 ///< System-call number for `setregid` +#define __NR_sigsuspend 72 ///< System-call number for `sigsuspend` +#define __NR_sigpending 73 ///< System-call number for `sigpending` +#define __NR_sethostname 74 ///< System-call number for `sethostname` +#define __NR_setrlimit 75 ///< System-call number for `setrlimit` +#define __NR_getrlimit 76 ///< System-call number for `getrlimit` +#define __NR_getrusage 77 ///< System-call number for `getrusage` +#define __NR_gettimeofday 78 ///< System-call number for `gettimeofday` +#define __NR_settimeofday 79 ///< System-call number for `settimeofday` +#define __NR_getgroups 80 ///< System-call number for `getgroups` +#define __NR_setgroups 81 ///< System-call number for `setgroups` +#define __NR_symlink 83 ///< System-call number for `symlink` +#define __NR_lstat 84 ///< System-call number for `lstat` +#define __NR_readlink 85 ///< System-call number for `readlink` +#define __NR_uselib 86 ///< System-call number for `uselib` +#define __NR_swapon 87 ///< System-call number for `swapon` +#define __NR_reboot 88 ///< System-call number for `reboot` +#define __NR_readdir 89 ///< System-call number for `readdir` +#define __NR_mmap 90 ///< System-call number for `mmap` +#define __NR_munmap 91 ///< System-call number for `munmap` +#define __NR_truncate 92 ///< System-call number for `truncate` +#define __NR_ftruncate 93 ///< System-call number for `ftruncate` +#define __NR_fchmod 94 ///< System-call number for `fchmod` +#define __NR_fchown 95 ///< System-call number for `fchown` +#define __NR_getpriority 96 ///< System-call number for `getpriority` +#define __NR_setpriority 97 ///< System-call number for `setpriority` +#define __NR_statfs 99 ///< System-call number for `statfs` +#define __NR_fstatfs 100 ///< System-call number for `fstatfs` +#define __NR_ioperm 101 ///< System-call number for `ioperm` +#define __NR_socketcall 102 ///< System-call number for `socketcall` +#define __NR_syslog 103 ///< System-call number for `syslog` +#define __NR_setitimer 104 ///< System-call number for `setitimer` +#define __NR_getitimer 105 ///< System-call number for `getitimer` +#define __NR_newstat 106 ///< System-call number for `newstat` +#define __NR_newlstat 107 ///< System-call number for `newlstat` +#define __NR_newfstat 108 ///< System-call number for `newfstat` +#define __NR_uname 109 ///< System-call number for `uname` +#define __NR_iopl 110 ///< System-call number for `iopl` +#define __NR_vhangup 111 ///< System-call number for `vhangup` +#define __NR_idle 112 ///< System-call number for `idle` +#define __NR_vm86old 113 ///< System-call number for `vm86old` +#define __NR_wait4 114 ///< System-call number for `wait4` +#define __NR_swapoff 115 ///< System-call number for `swapoff` +#define __NR_sysinfo 116 ///< System-call number for `sysinfo` +#define __NR_ipc 117 ///< System-call number for `ipc` +#define __NR_fsync 118 ///< System-call number for `fsync` +#define __NR_sigreturn 119 ///< System-call number for `sigreturn` +#define __NR_clone 120 ///< System-call number for `clone` +#define __NR_setdomainname 121 ///< System-call number for `setdomainname` +#define __NR_newuname 122 ///< System-call number for `newuname` +#define __NR_modify_ldt 123 ///< System-call number for `modify_ldt` +#define __NR_adjtimex 124 ///< System-call number for `adjtimex` +#define __NR_mprotect 125 ///< System-call number for `mprotect` +#define __NR_sigprocmask 126 ///< System-call number for `sigprocmask` +#define __NR_create_module 127 ///< System-call number for `create_module` +#define __NR_init_module 128 ///< System-call number for `init_module` +#define __NR_delete_module 129 ///< System-call number for `delete_module` +#define __NR_get_kernel_syms 130 ///< System-call number for `get_kernel_syms` +#define __NR_quotactl 131 ///< System-call number for `quotactl` +#define __NR_getpgid 132 ///< System-call number for `getpgid` +#define __NR_fchdir 133 ///< System-call number for `fchdir` +#define __NR_bdflush 134 ///< System-call number for `bdflush` +#define __NR_sysfs 135 ///< System-call number for `sysfs` +#define __NR_personality 136 ///< System-call number for `personality` +#define __NR_setfsuid 138 ///< System-call number for `setfsuid` +#define __NR_setfsgid 139 ///< System-call number for `setfsgid` +#define __NR_llseek 140 ///< System-call number for `llseek` +#define __NR_getdents 141 ///< System-call number for `getdents` +#define __NR_select 142 ///< System-call number for `select` +#define __NR_flock 143 ///< System-call number for `flock` +#define __NR_msync 144 ///< System-call number for `msync` +#define __NR_readv 145 ///< System-call number for `readv` +#define __NR_writev 146 ///< System-call number for `writev` +#define __NR_getsid 147 ///< System-call number for `getsid` +#define __NR_fdatasync 148 ///< System-call number for `fdatasync` +#define __NR_sysctl 149 ///< System-call number for `sysctl` +#define __NR_mlock 150 ///< System-call number for `mlock` +#define __NR_munlock 151 ///< System-call number for `munlock` +#define __NR_mlockall 152 ///< System-call number for `mlockall` +#define __NR_munlockall 153 ///< System-call number for `munlockall` +#define __NR_sched_setparam 154 ///< System-call number for `sched_setparam` +#define __NR_sched_getparam 155 ///< System-call number for `sched_getparam` +#define __NR_sched_setscheduler 156 ///< System-call number for `sched_setscheduler` +#define __NR_sched_getscheduler 157 ///< System-call number for `sched_getscheduler` +#define __NR_sched_yield 158 ///< System-call number for `sched_yield` +#define __NR_sched_get_priority_max 159 ///< System-call number for `sched_get_priority_max` +#define __NR_sched_get_priority_min 160 ///< System-call number for `sched_get_priority_min` +#define __NR_sched_rr_get_interval 161 ///< System-call number for `sched_rr_get_interval` +#define __NR_nanosleep 162 ///< System-call number for `nanosleep` +#define __NR_mremap 163 ///< System-call number for `mremap` +#define __NR_setresuid 164 ///< System-call number for `setresuid` +#define __NR_getresuid 165 ///< System-call number for `getresuid` +#define __NR_vm86 166 ///< System-call number for `vm86` +#define __NR_query_module 167 ///< System-call number for `query_module` +#define __NR_poll 168 ///< System-call number for `poll` +#define __NR_nfsservctl 169 ///< System-call number for `nfsservctl` +#define __NR_setresgid 170 ///< System-call number for `setresgid` +#define __NR_getresgid 171 ///< System-call number for `getresgid` +#define __NR_prctl 172 ///< System-call number for `prctl` +#define __NR_rt_sigreturn 173 ///< System-call number for `rt_sigreturn` +#define __NR_rt_sigaction 174 ///< System-call number for `rt_sigaction` +#define __NR_rt_sigprocmask 175 ///< System-call number for `rt_sigprocmask` +#define __NR_rt_sigpending 176 ///< System-call number for `rt_sigpending` +#define __NR_rt_sigtimedwait 177 ///< System-call number for `rt_sigtimedwait` +#define __NR_rt_sigqueueinfo 178 ///< System-call number for `rt_sigqueueinfo` +#define __NR_rt_sigsuspend 179 ///< System-call number for `rt_sigsuspend` +#define __NR_pread 180 ///< System-call number for `pread` +#define __NR_pwrite 181 ///< System-call number for `pwrite` +#define __NR_chown 182 ///< System-call number for `chown` +#define __NR_getcwd 183 ///< System-call number for `getcwd` +#define __NR_capget 184 ///< System-call number for `capget` +#define __NR_capset 185 ///< System-call number for `capset` +#define __NR_sigaltstack 186 ///< System-call number for `sigaltstack` +#define __NR_sendfile 187 ///< System-call number for `sendfile` +#define __NR_waitperiod 188 ///< System-call number for `waitperiod` +#define __NR_msgctl 189 ///< System-call number for `msgctl` +#define __NR_msgget 190 ///< System-call number for `msgget` +#define __NR_msgrcv 191 ///< System-call number for `msgrcv` +#define __NR_msgsnd 192 ///< System-call number for `msgsnd` +#define __NR_semctl 193 ///< System-call number for `semctl` +#define __NR_semget 194 ///< System-call number for `semget` +#define __NR_semop 195 ///< System-call number for `semop` +#define __NR_shmat 196 ///< System-call number for `shmat` +#define __NR_shmctl 197 ///< System-call number for `shmctl` +#define __NR_shmdt 198 ///< System-call number for `shmdt` +#define __NR_shmget 199 ///< System-call number for `shmget` +#define SYSCALL_NUMBER 200 ///< The total number of system-calls. + +/// @brief Handle the value returned from a system call. +/// @param type Specifies the type of the returned value. +/// @param res The name of the variable where the result of the SC is stored. +#define __syscall_return(type, res) \ + do { \ + if ((unsigned int)(res) >= (unsigned int)(-125)) { \ + errno = -(res); \ + (res) = -1; \ + } \ + return (type)(res); \ + } while (0) + +/// @brief Heart of the code that calls a system call with 0 parameters. +#define __inline_syscall0(res, name) \ + __asm__ __volatile__("int $0x80" \ + : "=a"(res) \ + : "0"(__NR_##name)) + +/// @brief Heart of the code that calls a system call with 1 parameter. +#define __inline_syscall1(res, name, arg1) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 2 parameters. +#define __inline_syscall2(res, name, arg1, arg2) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 3 parameters. +#define __inline_syscall3(res, name, arg1, arg2, arg3) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)), \ + "d"((int)(arg3)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 4 parameters. +#define __inline_syscall4(res, name, arg1, arg2, arg3, arg4) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)), \ + "d"((int)(arg3)), "S"((int)(arg4)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 5 parameters. +#define __inline_syscall5(res, name, arg1, arg2, arg3, arg4, arg5) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; movl %1,%%eax ; " \ + "int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "i"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)), \ + "d"((int)(arg3)), "S"((int)(arg4)), "D"((int)(arg5)) \ + : "memory"); + +/// @brief System call with 0 parameters. +#define _syscall0(type, name) \ + type name(void) \ + { \ + long __res; \ + __inline_syscall0(__res, name); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 1 parameter. +#define _syscall1(type, name, type1, arg1) \ + type name(type1 arg1) \ + { \ + long __res; \ + __inline_syscall1(__res, name, arg1); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 2 parameters. +#define _syscall2(type, name, type1, arg1, type2, arg2) \ + type name(type1 arg1, type2 arg2) \ + { \ + long __res; \ + __inline_syscall2(__res, name, arg1, arg2); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 3 parameters. +#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1 arg1, type2 arg2, type3 arg3) \ + { \ + long __res; \ + __inline_syscall3(__res, name, arg1, arg2, arg3); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 4 parameters. +#define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ + { \ + long __res; \ + __inline_syscall4(__res, name, arg1, arg2, arg3, arg4); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 5 parameters. +#define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ + type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ + { \ + long __res; \ + __inline_syscall5(__res, name, arg1, arg2, arg3, arg4, arg5); \ + __syscall_return(type, __res); \ + } diff --git a/libc/inc/termios.h b/libc/inc/termios.h new file mode 100644 index 0000000..c573a31 --- /dev/null +++ b/libc/inc/termios.h @@ -0,0 +1,22 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios.h +/// @brief Defines the termios functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "bits/termios-struct.h" + +/// @brief Put the state of FD into *TERMIOS_P. +/// @param fd +/// @param termios_p +/// @return +extern int tcgetattr(int fd, termios *termios_p); + +/// @brief Set the state of FD to *TERMIOS_P. +/// @param fd +/// @param optional_actions +/// @param termios_p +/// @return +extern int tcsetattr(int fd, int optional_actions, const termios *termios_p); diff --git a/libc/inc/time.h b/libc/inc/time.h new file mode 100644 index 0000000..b7e8072 --- /dev/null +++ b/libc/inc/time.h @@ -0,0 +1,110 @@ +/// MentOS, The Mentoring Operating system project +/// @file time.h +/// @brief Time-related functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// Domains of interval timers +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +/// Used to store time values. +typedef unsigned int time_t; + +/// Used to get information about the current time. +typedef struct tm_t { + /// Seconds [0 to 59] + int tm_sec; + /// Minutes [0 to 59] + int tm_min; + /// Hours [0 to 23] + int tm_hour; + /// Day of the month [1 to 31] + int tm_mday; + /// Month [0 to 11] + int tm_mon; + /// Year [since 1900] + int tm_year; + /// Day of the week [0 to 6] + int tm_wday; + /// Day in the year [0 to 365] + int tm_yday; + /// Is Daylight Saving Time. + int tm_isdst; +} tm_t; + +/// Rappresents time +typedef struct _timeval { + time_t tv_sec; /* seconds */ + time_t tv_usec; /* microseconds */ +} timeval; + +/// Rappresents a time interval +typedef struct _itimerval { + timeval it_interval; /* next value */ + timeval it_value; /* current value */ +} itimerval; + +/// @brief Specify intervals of time with nanosecond precision. +typedef struct timespec { + time_t tv_sec; ///< Seconds. + long tv_nsec; ///< Nanoseconds. +} timespec; + +/// @brief Returns the current time. +/// @param t Where the time should be stored. +/// @return The current time. +time_t time(time_t *t); + +/// @brief Return the difference between the two time values. +/// @param time1 The first time value. +/// @param time2 The second time value. +/// @return The difference in terms of seconds. +time_t difftime(time_t time1, time_t time2); + +/// @brief The current time broken down into a tm_t structure. +/// @param timep A pointer to a variable holding the current time. +/// @return The time broken down. +tm_t *localtime(const time_t *timep); + +/// @brief Formats the time tm according to the format specification format +/// and places the result in the character array s of size max. +/// @param s The destination buffer. +/// @param max The maximum length of the buffer. +/// @param format The buffer used to generate the time. +/// @param tm The broken-down time. +/// @return The number of bytes (excluding the terminating null) placed in s. +size_t strftime(char *s, size_t max, const char *format, const tm_t *tm); + +/// @brief Suspends the execution of the calling thread. +/// @param req The amount of time we want to sleep. +/// @param rem The remaining time we did not sleep. +/// @return If the call is interrupted by a signal handler, nanosleep() +/// returns -1, sets errno to EINTR, and writes the remaining time +/// into the structure pointed to by rem unless rem is NULL. +/// @details +/// The execution is suspended until either at least the time specified +/// in *req has elapsed, or the delivery of a signal that triggers the +/// invocation of a handler in the calling thread or that terminates +/// the process. +int nanosleep(const timespec *req, timespec *rem); + +/// @brief Causes the calling thread to sleep either until the number of +/// real-time seconds specified in seconds have elapsed or +/// until a signal arrives which is not ignored. +/// @param seconds The number of seconds we want to sleep. +/// @return Zero if the requested time has elapsed, or the number of seconds +/// left to sleep, if the call was interrupted by a signal handler. +unsigned int sleep(unsigned int seconds); + +/// @brief Fills the structure pointed to by curr_value with the current setting for the timer specified by which. +int getitimer(int which, itimerval *curr_value); + +/// @brief The system provides each process with three interval timers, each decrementing in a distinct time domain. +/// When any timer expires, a signal is sent to the process, and the timer (potentially) restarts. +int setitimer(int which, const itimerval *new_value, itimerval *old_value); diff --git a/libc/src/abort.c b/libc/src/abort.c new file mode 100644 index 0000000..5b1181b --- /dev/null +++ b/libc/src/abort.c @@ -0,0 +1,81 @@ +/// MentOS, The Mentoring Operating system project +/// @file abort.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "string.h" +#include "signal.h" +#include "stdio.h" + +// Since there could be signal handlers listening for the abort, we +// need to keep track at which stage of the abort we are. +static int stage = 0; + +void abort(void) +{ + sigaction_t action; + sigset_t sigset; + + /* Unblock SIGABRT. */ + if (stage == 0) { + ++stage; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGABRT); + sigprocmask(SIG_UNBLOCK, &sigset, 0); + } + + /* Send signal which possibly calls a user handler. */ + if (stage == 1) { + + // We must allow recursive calls of abort + int save_stage = stage; + + stage = 0; + kill(getpid(), SIGABRT); + stage = save_stage + 1; + } + + /* There was a handler installed. Now remove it. */ + if (stage == 2) { + ++stage; + + memset(&action, 0, sizeof(action)); + + action.sa_handler = SIG_DFL; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + sigaction(SIGABRT, &action, NULL); + } + + /* Try again. */ + if (stage == 3) { + ++stage; + + kill(getpid(), SIGABRT); + } + + /* Now try to abort using the system specific command. */ + if (stage == 4) { + ++stage; + + __asm__ __volatile__("hlt"); + } + + /* If we can't signal ourselves and the abort instruction failed, exit. */ + if (stage == 5) { + ++stage; + + exit(127); + } + + // If even this fails try to use the provided instruction to crash + // or otherwise make sure we never return. +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" + while (1) __asm__ __volatile__("hlt"); +#pragma clang diagnostic pop +} diff --git a/libc/src/assert.c b/libc/src/assert.c new file mode 100644 index 0000000..5ceba4f --- /dev/null +++ b/libc/src/assert.c @@ -0,0 +1,19 @@ +/// MentOS, The Mentoring Operating system project +/// @file assert.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "assert.h" +#include "stdlib.h" +#include "stdio.h" + +void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) +{ + printf("FILE: %s\n" + "FUNC: %s\n" + "LINE: %d\n\n" + "Assertion `%s` failed.\n", + file, (function ? function : "NO_FUN"), line, assertion); + abort(); +} diff --git a/libc/src/crt0.S b/libc/src/crt0.S new file mode 100644 index 0000000..665b352 --- /dev/null +++ b/libc/src/crt0.S @@ -0,0 +1,15 @@ +extern main +extern __libc_start_main +global _start + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text +_start: ; _start is the entry point known to the linker + mov ebp, 0 ; Set ebp to 0 as x86 programs require + push main ; Push the pointer to `main` to the stack. + call __libc_start_main ; Call the libc initialization function. + mov ebx, eax ; Move `main` return value to ebx. + mov eax, 1 ; Call the `exit` function by using `int 80` (i.e., a system call) + int 0x80 diff --git a/libc/src/ctype.c b/libc/src/ctype.c new file mode 100644 index 0000000..88ce35f --- /dev/null +++ b/libc/src/ctype.c @@ -0,0 +1,63 @@ +/// MentOS, The Mentoring Operating system project +/// @file ctype.c +/// @brief Functions related to character handling. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ctype.h" + +/// Distance from a uppercase character to the correspondent lowercase in ASCII. +#define OFFSET 32 + +int isdigit(int c) +{ + return (c >= 48 && c <= 57); +} + +int isalpha(int c) +{ + return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)); +} + +int isalnum(int c) +{ + return (isalpha(c) || isdigit(c)); +} + +int isxdigit(int c) +{ + return (isdigit(c) || (c >= 65 && c <= 70)); +} + +int islower(int c) +{ + return (c >= 97 && c <= 122); +} + +int isupper(int c) +{ + return (c >= 65 && c <= 90); +} + +int tolower(int c) +{ + if (isalpha(c) == 0 || islower(c)) { + return c; + } + + return c + OFFSET; +} + +int toupper(int c) +{ + if (isalpha(c) == 0 || isupper(c)) { + return c; + } + + return c - OFFSET; +} + +int isspace(int c) +{ + return (c == ' '); +} diff --git a/libc/src/debug.c b/libc/src/debug.c new file mode 100644 index 0000000..84ab2f5 --- /dev/null +++ b/libc/src/debug.c @@ -0,0 +1,105 @@ +/// MentOS, The Mentoring Operating system project +/// @file debug.c +/// @brief Debugging primitives. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include + +#include +#include +#include +#include +#include + +/// Serial port for QEMU. +#define SERIAL_COM1 (0x03F8) + +void dbg_putchar(char c) +{ +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + outportb(SERIAL_COM1, (uint8_t)c); +#endif +} + +void dbg_puts(const char *s) +{ +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + while ((*s) != 0) + dbg_putchar(*s++); +#endif +} + +static inline void __debug_print_header(const char *file, const char *fun, int line) +{ + static char tmp_prefix[BUFSIZ], final_prefix[BUFSIZ]; + dbg_puts("[ LB |"); + sprintf(tmp_prefix, "%s:%d", file, line); + sprintf(final_prefix, " %-20s ", tmp_prefix); + dbg_puts(final_prefix); + dbg_putchar('|'); + sprintf(final_prefix, " %-25s ] ", fun); + dbg_puts(final_prefix); +} + +void dbg_printf(const char *file, const char *fun, int line, const char *format, ...) +{ + // Define a buffer for the formatted string. + static char formatted[BUFSIZ]; + static short new_line = 1; + + // Stage 1: FORMAT + if (strlen(format) >= BUFSIZ) + return; + + // Start variabile argument's list. + va_list ap; + va_start(ap, format); + // Format the message. + vsprintf(formatted, format, ap); + // End the list of arguments. + va_end(ap); + + // Stage 2: SEND + if (new_line) { + __debug_print_header(file, fun, line); + new_line = 0; + } + for (size_t it = 0; (formatted[it] != 0) && (it < BUFSIZ); ++it) { + dbg_putchar(formatted[it]); + if (formatted[it] != '\n') { + continue; + } + if ((it + 1) >= BUFSIZ) { + continue; + } + if (formatted[it + 1] == 0) { + new_line = 1; + } else { + __debug_print_header(file, fun, line); + } + } +} + +const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) + dblBytes = bytes / 1024.0; + } + sprintf(output, "%.03lf %2s", dblBytes, suffix[i]); + return output; +} + +const char *dec_to_binary(unsigned long value) +{ + static char buffer[33]; + for (int i = 0; i < 32; ++i) + buffer[i] = bit_check(value, 31 - i) ? '1' : '0'; + return buffer; +} \ No newline at end of file diff --git a/libc/src/fcvt.c b/libc/src/fcvt.c new file mode 100644 index 0000000..750f564 --- /dev/null +++ b/libc/src/fcvt.c @@ -0,0 +1,104 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcvt.c +/// @brief Define the functions required to turn double values into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fcvt.h" +#include "math.h" + +static void cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, unsigned buf_size, int eflag) +{ + int r2; + double fi, fj; + char *p, *p1; + + char *buf_end = (buf + buf_size); + + if (ndigits < 0) { + ndigits = 0; + } + + if (ndigits >= buf_size - 1) { + ndigits = buf_size - 2; + } + + r2 = 0; + *sign = 0; + p = &buf[0]; + + if (arg < 0) { + *sign = 1; + arg = -arg; + } + + arg = modf(arg, &fi); + p1 = buf_end; + + if (fi != 0) { + p1 = buf_end; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int)((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < buf_end) { + *p++ = *p1++; + } + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + + p1 = &buf[ndigits]; + if (eflag == 0) { + p1 += r2; + } + + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = '\0'; + return; + } + + while (p <= p1 && p < buf_end) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int)fj + '0'; + } + + if (p1 >= buf_end) { + buf[buf_size - 1] = '\0'; + return; + } + + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) { + ++*--p1; + } else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > buf) + *p = '0'; + p++; + } + } + } + *p = '\0'; +} + +void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, chars, decpt, sign, buf, buf_size, 1); +} + +void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, decimals, decpt, sign, buf, buf_size, 0); +} diff --git a/libc/src/grp.c b/libc/src/grp.c new file mode 100644 index 0000000..41c97f0 --- /dev/null +++ b/libc/src/grp.c @@ -0,0 +1,238 @@ +/// MentOS, The Mentoring Operating system project +/// @file grp.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "grp.h" +#include "sys/unistd.h" +#include "sys/errno.h" +#include "assert.h" +#include "string.h" +#include "stdio.h" +#include "debug.h" +#include "fcntl.h" + + +static inline void __parse_line(struct group* grp, char *buf) +{ + assert(grp && "Received null grp!"); + char *token; + // Parse the group name. + if ((token = strtok(buf, ":")) != NULL) + grp->gr_name = token; + // Parse the group passwd. + if ((token = strtok(NULL, ":")) != NULL) + grp->gr_passwd = token; + // Parse the group id. + if ((token = strtok(NULL, ":")) != NULL) + grp->gr_gid = atoi(token); + + size_t found_users = 0; + while((token = strtok(NULL, ",\n\0")) != NULL && found_users < MAX_MEMBERS_PER_GROUP) { + grp->gr_mem[found_users] = token; + found_users += 1; + } + + // Null terminate array + grp->gr_mem[found_users] = "\0"; +} + +static inline char *__search_entry(int fd, char *buf, int buflen, const char *name, gid_t gid) +{ + int ret; + char c; + int pos = 0; + while ((ret = read(fd, &c, 1U))) { + // Skip carriage return. + if (c == '\r') + continue; + if (pos >= buflen) { + errno = ERANGE; + return NULL; + } + // If we have found a newline or the EOF, parse the entry. + if ((c == '\n') || (ret == EOF)) { + // Close the buffer. + buf[pos] = 0; + // Check the entry. + if (name) { + if (strncmp(buf, name, strlen(name)) == 0) + return buf; + } else { + int gid_start = -1, col_count = 0; + for (int i = 0; i < pos; ++i) { + if (buf[i] == ':') { + if (++col_count == 2) { + gid_start = i + 1; + break; + } + } + } + if ((gid_start != -1) && (gid_start < pos)) { + // Parse the gid. + int found_gid = atoi(&buf[gid_start]); + // Check the gid. + if (found_gid == gid) + return buf; + } + } + // Reset the index. + pos = 0; + // If we have reached the EOF stop. + if (ret == EOF) + break; + } else { + buf[pos++] = c; + } + } + errno = ENOENT; + return NULL; +} + +struct group* getgrgid(gid_t gid) { + + static group grp; + static char buffer[BUFSIZ]; + + group *result; + if (!getgrgid_r(gid, &grp, buffer, BUFSIZ, &result)) + return NULL; + + return &grp; +} + +struct group *getgrnam(const char* name) { + + if (name == NULL) + return NULL; + + static group grp; + static char buffer[BUFSIZ]; + + group *result; + if (!getgrnam_r(name, &grp, buffer, BUFSIZ, &result)) + return NULL; + + return &grp; +} + +int getgrgid_r(gid_t gid, struct group* group, char* buf, size_t buflen, struct group ** result) { + + int fd = open("/etc/group", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + + char *entry = __search_entry(fd, buf, buflen, NULL, gid); + if (entry != NULL) { + + // Close the file. + close(fd); + // Parse the line. + __parse_line(group, entry); + // Return success. + return 1; + } + + // Close the file. + close(fd); + // Return fail. + return 0; +} + +int getgrnam_r(const char* name, struct group* group, char* buf, size_t buflen, struct group** result) { + + int fd = open("/etc/group", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + + char *entry = __search_entry(fd, buf, buflen, name, 0); + if (entry != NULL) { + + // Close the file. + close(fd); + // Parse the line. + __parse_line(group, entry); + // Return success. + return 1; + } + + // Close the file. + close(fd); + // Return fail. + return 0; +} + +static int fd = -1; +struct group* getgrent(void) { + + static group result; + + if (fd == -1) { + //pr_debug("Opening group file\n"); + fd = open("/etc/group", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + return 0; + } + } + + int ret; + char c; + int pos = 0; + + static char buffer[BUFSIZ]; + while ((ret = read(fd, &c, 1U))) { + + // Skip carriage return. + if (c == '\r') + continue; + + if (pos >= BUFSIZ) { + errno = ERANGE; + return NULL; + } + + // If we have found a newline or the EOF, parse the entry. + if ((c == '\n') || (ret == EOF)) { + // Close the buffer. + buffer[pos] = 0; + + // Check the entry. + if (strlen(buffer) != 0) { + //pr_debug("Found entry in group file: %s\n", buffer); + __parse_line(&result, buffer); + return &result; + } + + // If we have reached the EOF stop. + if (ret == EOF) + break; + + } else { + buffer[pos++] = c; + } + } + + errno = ENOENT; + return NULL; +} + +void endgrent(void) { + + //pr_debug("Closing group file\n"); + close(fd); + fd = -1; +} + +void setgrent(void) { + + //pr_debug("Resetting pointer to beginning of group file\n"); + lseek(fd, 0, SEEK_SET); +} \ No newline at end of file diff --git a/libc/src/io/mm_io.c b/libc/src/io/mm_io.c new file mode 100644 index 0000000..5bcdaa2 --- /dev/null +++ b/libc/src/io/mm_io.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file mm_io.c +/// @brief Memory Mapped IO functions implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "io/mm_io.h" + +uint8_t in_memb(uint32_t addr) +{ + return *((uint8_t *) (addr)); +} + +uint16_t in_mems(uint32_t addr) +{ + return *((uint16_t *) (addr)); +} + +uint32_t in_meml(uint32_t addr) +{ + return *((uint32_t *) (addr)); +} + +void out_memb(uint32_t addr, uint8_t value) +{ + (*((uint8_t *) (addr))) = (value); +} + +void out_mems(uint32_t addr, uint16_t value) +{ + (*((uint16_t *) (addr))) = (value); +} + +void out_meml(uint32_t addr, uint32_t value) +{ + (*((uint32_t *) (addr))) = (value); +} diff --git a/libc/src/io/port_io.c b/libc/src/io/port_io.c new file mode 100644 index 0000000..ebe5072 --- /dev/null +++ b/libc/src/io/port_io.c @@ -0,0 +1,59 @@ +/// MentOS, The Mentoring Operating system project +/// @file port_io.c +/// @brief Byte I/O on ports prototypes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "io/port_io.h" + +inline uint8_t inportb(uint16_t port) +{ + unsigned char data = 0; + __asm__ __volatile__("inb %%dx, %%al" : "=a"(data) : "d"(port)); + + return data; +} + +inline uint16_t inports(uint16_t port) +{ + uint16_t rv; + __asm__ __volatile__("inw %1, %0" : "=a"(rv) : "dN"(port)); + + return rv; +} + +void inportsm(uint16_t port, uint8_t *data, unsigned long size) +{ + __asm__ __volatile__("rep insw" + : "+D"(data), "+c"(size) + : "d"(port) + : "memory"); +} + +inline uint32_t inportl(uint16_t port) +{ + uint32_t rv; + __asm__ __volatile__("inl %%dx, %%eax" : "=a"(rv) : "dN"(port)); + + return rv; +} + +inline void outportb(uint16_t port, uint8_t data) +{ + __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); +} + +inline void outports(uint16_t port, uint16_t data) +{ + __asm__ __volatile__("outw %1, %0" : : "dN"(port), "a"(data)); +} + +void outportsm(uint16_t port, uint8_t *data, uint16_t size) +{ + asm volatile("rep outsw" : "+S"(data), "+c"(size) : "d"(port)); +} + +inline void outportl(uint16_t port, uint32_t data) +{ + __asm__ __volatile__("outl %%eax, %%dx" : : "dN"(port), "a"(data)); +} diff --git a/libc/src/ipc/ipc.c b/libc/src/ipc/ipc.c new file mode 100644 index 0000000..eb8f082 --- /dev/null +++ b/libc/src/ipc/ipc.c @@ -0,0 +1,36 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipc.c +/// @brief Inter-Process Communication (IPC) system call implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "stddef.h" +#include "ipc/sem.h" +#include "ipc/shm.h" +#include "ipc/msg.h" + +_syscall3(long, shmat, int, shmid, char *, shmaddr, int, shmflg) + +_syscall3(long, shmget, key_t, key, size_t, size, int, flag) + +_syscall1(long, shmdt, char *, shmaddr) + +_syscall3(long, shmctl, int, shmid, int, cmd, struct shmid_ds *, buf) + +_syscall3(long, semget, key_t, key, int, nsems, int, semflg) + +_syscall3(long, semop, int, semid, struct sembuf *, sops, unsigned, nsops) + +_syscall4(long, semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) + +_syscall2(long, msgget, key_t, key, int, msgflg) + +_syscall4(long, msgsnd, int, msqid, struct msgbuf *, msgp, size_t, msgsz, int, msgflg) + +_syscall5(long, msgrcv, int, msqid, struct msgbuf *, msgp, size_t, msgsz, long, msgtyp, int, msgflg) + +_syscall3(long, msgctl, int, msqid, int, cmd, struct msqid_ds *, buf) + diff --git a/libc/src/libc_start.c b/libc/src/libc_start.c new file mode 100644 index 0000000..87ddc00 --- /dev/null +++ b/libc/src/libc_start.c @@ -0,0 +1,40 @@ +/// @file libc_start.c +/// @brief Contains the programs initialization procedure. + +#include "sys/unistd.h" +#include "stdlib.h" +#include "string.h" +#include "assert.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +/// @brief Reference to the `environ` variable in `setenv.c`. +extern char **environ; + +/// @brief The entry point to every program. +/// @param main Pointer to the main function. +/// @param argc The number of arguments. +/// @param argv The pointer to the arguments. +/// @param envp The pointer to the environmental variables. +/// @return The return of the `main` function. +int __libc_start_main(int (*main)(int, char **, char **), int argc, char *argv[], char *envp[]) +{ + //dbg_print("== START %-30s =======================================\n", argv[0]); + //dbg_print("__libc_start_main(%p, %d, %p, %p)\n", main, argc, argv, envp); + assert(main && "There is no `main` function."); + assert(argv && "There is no `argv` array."); + assert(envp && "There is no `envp` array."); + //dbg_print("environ : %p\n", environ); + // Copy the environ. + environ = envp; + // Call the main function. + int result = main(argc, argv, envp); + // Free the environ. + //dbg_print("== END %-30s =======================================\n", argv[0]); + return result; +} + +// WARNING: This declaration must be here, because libc_start is compiled +// with all the programs, and all the programs NEED to have the `sigreturn` +// symbol. It must be this way, period. +_syscall0(int, sigreturn) diff --git a/libc/src/libgen.c b/libc/src/libgen.c new file mode 100644 index 0000000..50d03b5 --- /dev/null +++ b/libc/src/libgen.c @@ -0,0 +1,141 @@ +/// MentOS, The Mentoring Operating system project +/// @file libgen.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include "libgen.h" +#include "string.h" +#include "limits.h" + +int parse_path(char *out, char **cur, char sep, size_t max) +{ + if (**cur == '\0') { + return 0; + } + + *out++ = **cur; + ++*cur; + --max; + + while ((max > 0) && (**cur != '\0') && (**cur != sep)) { + *out++ = **cur; + ++*cur; + --max; + } + + *out = '\0'; + + return 1; +} + +char *dirname(const char *path) +{ + static char s[PATH_MAX]; + + static char dot[2] = "."; + + // Check the input path. + if (path == NULL) { + return dot; + } + + // Copy the path to the support string. + strcpy(s, path); + + // Get the last occurrence of '/'. + char *last_slash = strrchr(s, '/'); + + if (last_slash == s) { + // If the slash is acutally the first character of the string, move the + // pointer to the last slash after it. + ++last_slash; + } else if ((last_slash != NULL) && (last_slash[1] == '\0')) { + // If the slash is the last character, we need to search before it. + last_slash = memchr(s, '/', last_slash - s); + } + + if (last_slash != NULL) { + // If we have found it, close the string. + last_slash[0] = '\0'; + } else { + // Otherwise, return '.'. + return dot; + } + + return s; +} + +char *basename(const char *path) +{ + char *p = strrchr(path, '/'); + + return p ? p + 1 : (char *)path; +} + +char *realpath(const char *path, char *resolved) +{ + assert(path && "Provided null path."); + if (resolved == NULL) + resolved = malloc(sizeof(char) * PATH_MAX); + char abspath[PATH_MAX]; + // Initialize the absolute path. + memset(abspath, '\0', PATH_MAX); + int remaining; + if (path[0] != '/') { + // Get the current task. + getcwd(abspath, PATH_MAX); + // Check the current task. + assert((strlen(abspath) > 0) && "There is no current task."); + // Check that the current working directory is an absolute path. + assert((abspath[0] == '/') && "Current working directory is not an absolute path."); + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + // Set the number of characters that should be copied, + // based on the current absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Append the path. + strncat(abspath, path, remaining); + } else { + // Copy the path into the absolute path. + strncpy(abspath, path, PATH_MAX - 1); + } + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + + int absidx = 0, pathidx = 0; + + while (abspath[absidx]) { + // Skip multiple consecutive / characters + if (!strncmp("//", abspath + absidx, 2)) { + absidx++; + } + // Go to previous directory if /../ is found + else if (!strncmp("/../", abspath + absidx, 4)) { + // Go to a valid path character (pathidx points to the next one) + if (pathidx) + pathidx--; + while (pathidx && resolved[pathidx] != '/') { + pathidx--; + } + absidx += 3; + } else if (!strncmp("/./", abspath + absidx, 3)) { + absidx += 2; + } else { + resolved[pathidx++] = abspath[absidx++]; + } + } + // Remove the last / + if (pathidx > 1) + resolved[pathidx - 1] = '\0'; + else + resolved[1] = '\0'; + return resolved; +} \ No newline at end of file diff --git a/libc/src/math.c b/libc/src/math.c new file mode 100644 index 0000000..35ffffd --- /dev/null +++ b/libc/src/math.c @@ -0,0 +1,180 @@ +/// MentOS, The Mentoring Operating system project +/// @file math.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "math.h" +#include "stdint.h" + +double round(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; frndint" + : "=t"(out) + : "m"(x)); + return out; +} + +double floor(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x >= 0) + return 0.0; + return -1.0; + } + int i = (int)x; + if (x < 0) + return (double)(i - 1); + return (double)i; +} + +double ceil(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x <= 0) + return 0.0; + return 1.0; + } + int i = (int)x; + if (x > 0) + return (double)(i + 1); + return (double)i; +} + +double pow(double base, double exponent) +{ + double out; + __asm__ __volatile__("fyl2x;" + "fld %%st;" + "frndint;" + "fsub %%st,%%st(1);" + "fxch;" + "fchs;" + "f2xm1;" + "fld1;" + "faddp;" + "fxch;" + "fld1;" + "fscale;" + "fstp %%st(1);" + "fmulp;" + : "=t"(out) + : "0"(base), "u"(exponent) + : "st(1)"); + return out; +} + +double exp(double x) +{ + return pow(M_E, x); +} + +double fabs(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +float fabsf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +double sqrt(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +float sqrtf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +int isinf(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U && + ((unsigned)ieee754.u == 0); +} + +int isnan(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + + ((unsigned)ieee754.u != 0) > + 0x7ff00000; +} + +double log10(double x) +{ + return ln(x) / ln(10); +} + +double ln(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fyl2x" + : "=t"(out) + : "m"(x)); + return out; +} + +double logx(double x, double y) +{ + // Base may not equal 1 or be negative. + if (y == 1.f || y < 0.f || ln(y) == 0.f) + return 0.f; + return ln(x) / ln(y); +} + +/// Max power for forward and reverse projections. +#define MAXPOWTWO 4.503599627370496000E+15 + +double modf(double x, double *intpart) +{ + register double absvalue; + if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { + // It must be an integer. + (*intpart) = x; + } else { + // Shift fraction off right. + (*intpart) = absvalue + MAXPOWTWO; + // Shift back without fraction. + (*intpart) -= MAXPOWTWO; + + // Above arithmetic might round. + while ((*intpart) > absvalue) { + // Test again just to be sure. + (*intpart) -= 1.0; + } + if (x < 0.0) { + (*intpart) = -(*intpart); + } + } + // Signed fractional part. + return (x - (*intpart)); +} diff --git a/libc/src/pwd.c b/libc/src/pwd.c new file mode 100644 index 0000000..eb6684d --- /dev/null +++ b/libc/src/pwd.c @@ -0,0 +1,162 @@ +/// MentOS, The Mentoring Operating system project +/// @file pwd.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "pwd.h" +#include "sys/unistd.h" +#include "sys/errno.h" +#include "assert.h" +#include "string.h" +#include "stdio.h" +#include "fcntl.h" + +static inline void __parse_line(passwd_t *pwd, char *buf) +{ + assert(pwd && "Received null pwd!"); + char *token; + // Parse the username. + if ((token = strtok(buf, ":")) != NULL) + pwd->pw_name = token; + // Parse the password. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_passwd = token; + // Parse the user ID. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_uid = atoi(token); + // Parse the group ID. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_gid = atoi(token); + // Parse the user information. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_gecos = token; + // Parse the dir. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_dir = token; + // Parse the shell. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_shell = token; +} + +static inline char *__search_entry(int fd, char *buf, int buflen, const char *name, uid_t uid) +{ + int ret; + char c; + int pos = 0; + while ((ret = read(fd, &c, 1U))) { + // Skip carriage return. + if (c == '\r') + continue; + if (pos >= buflen) { + errno = ERANGE; + return NULL; + } + // If we have found a newline or the EOF, parse the entry. + if ((c == '\n') || (ret == EOF)) { + // Close the buffer. + buf[pos] = 0; + // Check the entry. + if (name) { + if (strncmp(buf, name, strlen(name)) == 0) + return buf; + } else { + int uid_start = -1, col_count = 0; + for (int i = 0; i < pos; ++i) { + if (buf[i] == ':') { + if (++col_count == 2) { + uid_start = i + 1; + break; + } + } + } + if ((uid_start != -1) && (uid_start < pos)) { + // Parse the uid. + int found_uid = atoi(&buf[uid_start]); + // Check the uid. + if (found_uid == uid) + return buf; + } + } + // Reset the index. + pos = 0; + // If we have reached the EOF stop. + if (ret == EOF) + break; + } else { + buf[pos++] = c; + } + } + errno = ENOENT; + return NULL; +} + +passwd_t *getpwnam(const char *name) +{ + if (name == NULL) + return NULL; + static passwd_t pwd; + static char buffer[BUFSIZ]; + passwd_t *result; + if (!getpwnam_r(name, &pwd, buffer, BUFSIZ, &result)) + return NULL; + return &pwd; +} + +passwd_t *getpwuid(uid_t uid) +{ + static passwd_t pwd; + static char buffer[BUFSIZ]; + passwd_t *result; + if (!getpwuid_r(uid, &pwd, buffer, BUFSIZ, &result)) + return NULL; + return &pwd; +} + +int getpwnam_r(const char *name, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result) +{ + if (name == NULL) + return 0; + int fd = open("/etc/passwd", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + char *entry = __search_entry(fd, buf, buflen, name, 0); + if (entry != NULL) { + // Close the file. + close(fd); + // Parse the line. + __parse_line(pwd, entry); + // Return success. + return 1; + } + // Close the file. + close(fd); + // Return fail. + return 0; +} + +int getpwuid_r(uid_t uid, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result) +{ + int fd = open("/etc/passwd", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + char *entry = __search_entry(fd, buf, buflen, NULL, uid); + if (entry != NULL) { + // Close the file. + close(fd); + // Parse the line. + __parse_line(pwd, entry); + // Return success. + return 1; + } + // Close the file. + close(fd); + // Return fail. + return 0; +} diff --git a/libc/src/sched.c b/libc/src/sched.c new file mode 100644 index 0000000..daddafa --- /dev/null +++ b/libc/src/sched.c @@ -0,0 +1,13 @@ +// +// Created by enryf on 24/09/2020. +// + +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "sched.h" + +_syscall2(int, sched_setparam, pid_t, pid, const sched_param_t *, param) + +_syscall2(int, sched_getparam, pid_t, pid, sched_param_t *, param) + +_syscall0(int, waitperiod) diff --git a/libc/src/setenv.c b/libc/src/setenv.c new file mode 100644 index 0000000..08fbbba --- /dev/null +++ b/libc/src/setenv.c @@ -0,0 +1,146 @@ +/// @file setenv.c +/// @brief Defines the functions used to manipulate the environmental variables. + +#include +#include "sys/errno.h" +#include "string.h" +#include "stdlib.h" + +char **environ; + +static char **__environ = NULL; +static size_t __environ_size = 0; + +static inline int __find_entry(const char *name, const size_t name_len) +{ + if (environ) { + int index = 0; + for (char **ptr = environ; *ptr; ++ptr, ++index) + if (!strncmp((*ptr), name, name_len) && (*ptr)[name_len] == '=') + return index; + } + return -1; +} + +static void __clone_environ() +{ + if (environ) { + // Count the number of variables. + __environ_size = 0; + for (char **ptr = environ; *ptr; ++ptr) { ++__environ_size; } + // Allocate the space. + __environ = malloc(sizeof(char *) * (__environ_size + 2)); + for (int i = 0; i < __environ_size; ++i) { + size_t entry_len = strlen(environ[i]) + 1; + __environ[i] = malloc(sizeof(char) * entry_len); + memcpy(__environ[i], environ[i], entry_len); + } + __environ[__environ_size] = (char *)NULL; + __environ[__environ_size + 1] = (char *)NULL; + __environ_size += 2; + // Set the new environ. + environ = __environ; + } +} + +int setenv(const char *name, const char *value, int replace) +{ + // There must be always an environ variable set. + assert(environ && "There is no `environ` set."); + // Check the name. + if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + errno = EINVAL; + return -1; + } + if (__environ == NULL) { + __clone_environ(); + environ = __environ; + } + const size_t name_len = strlen(name); + const size_t value_len = strlen(value) + 1; + const size_t total_len = name_len + value_len + 1; + //LOCK; + if (environ == NULL) { + return -1; + } + // Find the entry. + int index = __find_entry(name, name_len); + if (index >= 0) { + if (!replace) { + //UNLOCK; + return -1; + } + } else { + // Get the size of environ; + index = __environ_size - 2; + // Extend the environ dynamically allocated memory. + char **new_environ = (char **)realloc(environ, sizeof(char *) * (__environ_size + 1)); + // Check the pointer. + if (new_environ == NULL) { + //UNLOCK; + return -1; + } + // Increment the size. + __environ_size += 1; + // Close the environment. + new_environ[__environ_size - 2] = NULL; + new_environ[__environ_size - 1] = NULL; + // Update all the variables. + environ = __environ = new_environ; + } + // Free the previous entry. + if (environ[index]) + free(environ[index]); + // Allocate the new entry. + environ[index] = malloc(total_len); + // Memcopy because we do not want the null terminating character. + memcpy(environ[index], name, name_len); + // Set the equal. + environ[index][name_len] = '='; + // Add the value. + memcpy(&environ[index][name_len + 1], value, value_len); + //UNLOCK; + return 0; +} + +int unsetenv(const char *name) +{ + if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + errno = EINVAL; + return -1; + } + if (__environ == NULL) { + __clone_environ(); + environ = __environ; + } + size_t len = strlen(name); + //LOCK; + char **ep = environ; + while (*ep != NULL) { + if (!strncmp(*ep, name, len) && (*ep)[len] == '=') { + /* Found it. Remove this pointer by moving later ones back. */ + char **dp = ep; + do dp[0] = dp[1]; + while (*dp++); + /* Continue the loop in case NAME appears again. */ + } else { + ++ep; + } + } + //UNLOCK; + return 0; +} + +char *getenv(const char *name) +{ + size_t name_len = strlen(name); + int index = __find_entry(name, name_len); + if (index < 0) { + return NULL; + } + size_t env_len = strlen(environ[index]); + if ((name_len + 1) < env_len) { + return &environ[index][name_len + 1]; + } + return NULL; +} diff --git a/libc/src/stdio.c b/libc/src/stdio.c new file mode 100644 index 0000000..02f74c2 --- /dev/null +++ b/libc/src/stdio.c @@ -0,0 +1,207 @@ +/// @file stdio.c +/// @brief Standard I/0 functions. +/// @date Apr 2019 + +#include +#include "stdio.h" +#include "ctype.h" +#include "string.h" +#include "stdbool.h" +#include "sys/unistd.h" + +void putchar(int character) +{ + write(STDOUT_FILENO, &character, 1); +} + +void puts(char *str) +{ + write(STDOUT_FILENO, str, strlen(str)); +} + +int getchar(void) +{ + char c; + while (read(STDIN_FILENO, &c, 1) == 0) + continue; + + return c; +} + +char *gets(char *str) +{ + // Check the input string. + if (str == NULL) + return NULL; + // Buffer for reading input. + char buffer[GETS_BUFFERSIZE]; + memset(buffer, '\0', GETS_BUFFERSIZE); + // Char pointer to the buffer. + char *cptr = buffer; + // Character storage and counter to prevent overflow. + int ch, counter = 0; + // Read until we find a newline or we exceed the buffer size. + while (((ch = getchar()) != '\n') && (counter++ < GETS_BUFFERSIZE)) { + // If we encounter EOF, stop. + if (ch == EOF) { + // EOF at start of line return NULL. + if (cptr == str) { + return NULL; + } + break; + } + // Backspace key + if (ch == '\b') { + if (counter > 0) { + counter--; + --cptr; + putchar('\b'); + } + } else { + // The character is stored at address, and the pointer is incremented. + *cptr++ = ch; + } + } + // Add the null-terminating character. + *cptr = '\0'; + // Copy the string we have read. + strcpy(str, buffer); + // Return a pointer to the original string. + return str; +} + +int atoi(const char *str) +{ + // Check the input string. + if (str == NULL) + return 0; + // Initialize sign, the result variable, and two indices. + int sign = (str[0] == '-') ? -1 : +1, result = 0, i; + // Find where the number ends. + for (i = (sign == -1) ? 1 : 0; (str[i] != '\0') && isdigit(str[i]); ++i) + result = (result * 10) + str[i] - '0'; + return sign * result; +} + +long strtol(const char *str, char **endptr, int base) +{ + const char *s; + long acc, cutoff; + int c; + int neg, any, cutlim; + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = str; + do { + c = (unsigned char)*s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? LONG_MIN : LONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char)*s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MIN; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc -= c; + } + } else { + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + *endptr = (char *)(any ? s - 1 : str); + return (acc); +} + +int fgetc(int fd) +{ + unsigned char c; + if (read(fd, &c, 1) <= 0) + return EOF; + return c; +} + +char *fgets(char *buf, int n, int fd) +{ + int c; + char *p; + + /* get max bytes or upto a newline */ + for (p = buf, n--; n > 0; n--) { + if ((c = fgetc(fd)) == EOF) + break; + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + if (p == buf || c == EOF) + return NULL; + return (p); +} \ No newline at end of file diff --git a/libc/src/stdlib.c b/libc/src/stdlib.c new file mode 100644 index 0000000..5a6524a --- /dev/null +++ b/libc/src/stdlib.c @@ -0,0 +1,91 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdlib.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "system/syscall_types.h" +#include "stdlib.h" +#include "string.h" +#include "assert.h" + +/// @brief Number which identifies a memory area allocated through a call to +/// malloc(), calloc() or realloc(). +#define MALLOC_MAGIC_NUMBER 0x600DC0DE + +static inline int __malloc_is_valid_ptr(void *ptr) +{ + return (ptr && (((size_t *)ptr)[-1] == MALLOC_MAGIC_NUMBER)); +} + +size_t malloc_usable_size(void *ptr) +{ + if (__malloc_is_valid_ptr(ptr)) + return ((size_t *)ptr)[-2]; + return 0; +} + +void *malloc(unsigned int size) +{ + size_t *_res, _size = 2 * sizeof(size_t) + size; + __inline_syscall1(_res, brk, _size); + _res[0] = size; + _res[1] = MALLOC_MAGIC_NUMBER; + return &_res[2]; +} + +void *calloc(size_t num, size_t size) +{ + void *ptr = malloc(num * size); + if (ptr) { + memset(ptr, 0, num * size); + } + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ + // C standard implementation: When NULL is passed to realloc, + // simply malloc the requested size and return a pointer to that. + if (__builtin_expect(ptr == NULL, 0)) { + return malloc(size); + } + // C standard implementation: For a size of zero, free the + // pointer and return NULL, allocating no new memory. + if (__builtin_expect(size == 0, 0)) { + free(ptr); + return NULL; + } + // Get the old size. + size_t old_size = malloc_usable_size(ptr); + // Create the new pointer. + void *newp = malloc(size); + memset(newp, 0, size); + memcpy(newp, ptr, old_size); + free(ptr); + return newp; +} + +void free(void *ptr) +{ + int _res; + if (__malloc_is_valid_ptr(ptr)) { + size_t *_ptr = (size_t *)ptr - 2; + __inline_syscall1(_res, brk, _ptr); + } else { + __inline_syscall1(_res, brk, ptr); + } +} + +/// Seed used to generate random numbers. +static int rseed = 0; + +void srand(int x) +{ + rseed = x; +} + +int rand() +{ + return rseed = (rseed * 1103515245U + 12345U) & RAND_MAX; +} diff --git a/libc/src/strerror.c b/libc/src/strerror.c new file mode 100644 index 0000000..a67b78c --- /dev/null +++ b/libc/src/strerror.c @@ -0,0 +1,480 @@ +/// MentOS, The Mentoring Operating system project +/// @file strerror.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "strerror.h" +#include "string.h" + +char *strerror(int errnum) +{ + static char error[1024]; + + switch (errnum) { + case 0: + strcpy(error, "Success"); + break; +#ifdef ENOENT + case ENOENT: + strcpy(error, "No such file or directory"); + break; +#endif +#ifdef ESRCH + case ESRCH: + strcpy(error, "No such process"); + break; +#endif +#ifdef EINTR + case EINTR: + strcpy(error, "Interrupted system call"); + break; +#endif +#ifdef EIO + case EIO: + strcpy(error, "I/O error"); + break; +#endif +#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) + case ENXIO: + strcpy(error, "No such device or address"); + break; +#endif +#ifdef E2BIG + case E2BIG: + strcpy(error, "Arg list too long"); + break; +#endif +#ifdef ENOEXEC + case ENOEXEC: + strcpy(error, "Exec format error"); + break; +#endif +#ifdef EALREADY + case EALREADY: + strcpy(error, "Socket already connected"); + break; +#endif +#ifdef EBADF + case EBADF: + strcpy(error, "Bad file number"); + break; +#endif +#ifdef ECHILD + case ECHILD: + strcpy(error, "No children"); + break; +#endif +#ifdef EDESTADDRREQ + case EDESTADDRREQ: + strcpy(error, "Destination address required"); + break; +#endif +#ifdef EAGAIN + case EAGAIN: + strcpy(error, "No more processes"); + break; +#endif +#ifdef ENOMEM + case ENOMEM: + strcpy(error, "Not enough space"); + break; +#endif +#ifdef EACCES + case EACCES: + strcpy(error, "Permission denied"); + break; +#endif +#ifdef EFAULT + case EFAULT: + strcpy(error, "Bad address"); + break; +#endif +#ifdef ENOTBLK + case ENOTBLK: + strcpy(error, "Block device required"); + break; +#endif +#ifdef EBUSY + case EBUSY: + strcpy(error, "Device or resource busy"); + break; +#endif +#ifdef EEXIST + case EEXIST: + strcpy(error, "File exists"); + break; +#endif +#ifdef EXDEV + case EXDEV: + strcpy(error, "Cross-device link"); + break; +#endif +#ifdef ENODEV + case ENODEV: + strcpy(error, "No such device"); + break; +#endif +#ifdef ENOTDIR + case ENOTDIR: + strcpy(error, "Not a directory"); + break; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: + strcpy(error, "Host is down"); + break; +#endif +#ifdef EINPROGRESS + case EINPROGRESS: + strcpy(error, "Connection already in progress"); + break; +#endif +#ifdef EISDIR + case EISDIR: + strcpy(error, "Is a directory"); + break; +#endif +#ifdef EINVAL + case EINVAL: + strcpy(error, "Invalid argument"); + break; +#endif +#ifdef EISNAM + case EISNAM: + strcpy(error, "Is a named type file"); + break; +#endif +#ifdef ENETDOWN + case ENETDOWN: + strcpy(error, "Network interface is not configured"); + break; +#endif +#ifdef ENFILE + case ENFILE: + strcpy(error, "Too many open files in system"); + break; +#endif +#ifdef EMFILE + case EMFILE: + strcpy(error, "Too many open files"); + break; +#endif +#ifdef ENOTTY + case ENOTTY: + strcpy(error, "Not a character device"); + break; +#endif +#ifdef ETXTBSY + case ETXTBSY: + strcpy(error, "Text file busy"); + break; +#endif +#ifdef EFBIG + case EFBIG: + strcpy(error, "File too large"); + break; +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: + strcpy(error, "Host is unreachable"); + break; +#endif +#ifdef ENOSPC + case ENOSPC: + strcpy(error, "No space left on device"); + break; +#endif +#ifdef ENOTSUP + case ENOTSUP: + strcpy(error, "Not supported"); + break; +#endif +#ifdef ESPIPE + case ESPIPE: + strcpy(error, "Illegal seek"); + break; +#endif +#ifdef EROFS + case EROFS: + strcpy(error, "Read-only file system"); + break; +#endif +#ifdef EMLINK + case EMLINK: + strcpy(error, "Too many links"); + break; +#endif +#ifdef EPIPE + case EPIPE: + strcpy(error, "Broken pipe"); + break; +#endif +#ifdef EDOM + case EDOM: + strcpy(error, "Math argument"); + break; +#endif +#ifdef ERANGE + case ERANGE: + strcpy(error, "Result too large"); + break; +#endif +#ifdef ENOMSG + case ENOMSG: + strcpy(error, "No message of desired type"); + break; +#endif +#ifdef EIDRM + case EIDRM: + strcpy(error, "Identifier removed"); + break; +#endif +#ifdef EDEADLK + case EDEADLK: + strcpy(error, "Deadlock"); + break; +#endif +#ifdef ENETUNREACH + case ENETUNREACH: + strcpy(error, "Network is unreachable"); + break; +#endif +#ifdef ENOLCK + case ENOLCK: + strcpy(error, "No lock"); + break; +#endif +#ifdef ENOSTR + case ENOSTR: + strcpy(error, "Not a stream"); + break; +#endif +#ifdef ETIME + case ETIME: + strcpy(error, "Stream ioctl timeout"); + break; +#endif +#ifdef ENOSR + case ENOSR: + strcpy(error, "No stream resources"); + break; +#endif +#ifdef ENONET + case ENONET: + strcpy(error, "Machine is not on the network"); + break; +#endif +#ifdef ENOPKG + case ENOPKG: + strcpy(error, "No package"); + break; +#endif +#ifdef EREMOTE + case EREMOTE: + strcpy(error, "Resource is remote"); + break; +#endif +#ifdef ENOLINK + case ENOLINK: + strcpy(error, "Virtual circuit is gone"); + break; +#endif +#ifdef EADV + case EADV: + strcpy(error, "Advertise error"); + break; +#endif +#ifdef ESRMNT + case ESRMNT: + strcpy(error, "Srmount error"); + break; +#endif +#ifdef ECOMM + case ECOMM: + strcpy(error, "Communication error"); + break; +#endif +#ifdef EPROTO + case EPROTO: + strcpy(error, "Protocol error"); + break; +#endif +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + strcpy(error, "Unknown protocol"); + break; +#endif +#ifdef EMULTIHOP + case EMULTIHOP: + strcpy(error, "Multihop attempted"); + break; +#endif +#ifdef EBADMSG + case EBADMSG: + strcpy(error, "Bad message"); + break; +#endif +#ifdef ELIBACC + case ELIBACC: + strcpy(error, "Cannot access a needed shared library"); + break; +#endif +#ifdef ELIBBAD + case ELIBBAD: + strcpy(error, "Accessing a corrupted shared library"); + break; +#endif +#ifdef ELIBSCN + case ELIBSCN: + strcpy(error, ".lib section in a.out corrupted"); + break; +#endif +#ifdef ELIBMAX + case ELIBMAX: + strcpy(error, + "Attempting to link in more shared libraries than system limit"); + break; +#endif +#ifdef ELIBEXEC + case ELIBEXEC: + strcpy(error, "Cannot exec a shared library directly"); + break; +#endif +#ifdef ENOSYS + case ENOSYS: + strcpy(error, "Function not implemented"); + break; +#endif +#ifdef ENMFILE + case ENMFILE: + strcpy(error, "No more files"); + break; +#endif +#ifdef ENOTEMPTY + case ENOTEMPTY: + strcpy(error, "Directory not empty"); + break; +#endif +#ifdef ENAMETOOLONG + case ENAMETOOLONG: + strcpy(error, "File or path name too long"); + break; +#endif +#ifdef ELOOP + case ELOOP: + strcpy(error, "Too many symbolic links"); + break; +#endif +#ifdef ENOBUFS + case ENOBUFS: + strcpy(error, "No buffer space available"); + break; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + strcpy(error, "Address family not supported by protocol family"); + break; +#endif +#ifdef EPROTOTYPE + case EPROTOTYPE: + strcpy(error, "Protocol wrong type for socket"); + break; +#endif +#ifdef ENOTSOCK + case ENOTSOCK: + strcpy(error, "Socket operation on non-socket"); + break; +#endif +#ifdef ENOPROTOOPT + case ENOPROTOOPT: + strcpy(error, "Protocol not available"); + break; +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: + strcpy(error, "Can't send after socket shutdown"); + break; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: + strcpy(error, "Connection refused"); + break; +#endif +#ifdef EADDRINUSE + case EADDRINUSE: + strcpy(error, "Address already in use"); + break; +#endif +#ifdef ECONNABORTED + case ECONNABORTED: + strcpy(error, "Software caused connection abort"); + break; +#endif +#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) + case EWOULDBLOCK: + strcpy(error, "Operation would block"); + break; +#endif +#ifdef ENOTCONN + case ENOTCONN: + strcpy(error, "Socket is not connected"); + break; +#endif +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + strcpy(error, "Socket type not supported"); + break; +#endif +#ifdef EISCONN + case EISCONN: + strcpy(error, "Socket is already connected"); + break; +#endif +#ifdef ECANCELED + case ECANCELED: + strcpy(error, "Operation canceled"); + break; +#endif +#ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: + strcpy(error, "State not recoverable"); + break; +#endif +#ifdef EOWNERDEAD + case EOWNERDEAD: + strcpy(error, "Previous owner died"); + break; +#endif +#ifdef ESTRPIPE + case ESTRPIPE: + strcpy(error, "Streams pipe error"); + break; +#endif +#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) + case EOPNOTSUPP: + strcpy(error, "Operation not supported on socket"); + break; +#endif +#ifdef EMSGSIZE + case EMSGSIZE: + strcpy(error, "Message too long"); + break; +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: + strcpy(error, "Connection timed out"); + break; +#endif +#ifdef ENOTSCHEDULABLE + case ENOTSCHEDULABLE: + strcpy(error, "The process cannot be scheduled"); + break; +#endif + default: + strcpy(error, "Unknown error"); + break; + } + + return error; +} diff --git a/libc/src/string.c b/libc/src/string.c new file mode 100644 index 0000000..37c66e6 --- /dev/null +++ b/libc/src/string.c @@ -0,0 +1,702 @@ +/// MentOS, The Mentoring Operating system project +/// @file string.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include "string.h" +#include "ctype.h" +#include "stdio.h" +#include "stdlib.h" + +char *strncpy(char *destination, const char *source, size_t num) +{ + char *start = destination; + while (num && (*destination++ = *source++)) { + num--; + } + if (num) { + while (--num) { + *destination++ = '\0'; + } + } + + return start; +} + +int strncmp(const char *s1, const char *s2, size_t n) +{ + if (!n) + return 0; + while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) { + s1++; + s2++; + } + + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +int stricmp(const char *s1, const char *s2) +{ + while (*s2 != 0 && toupper(*s1) == toupper(*s2)) { + s1++, s2++; + } + + return (toupper(*s1) - toupper(*s2)); +} + +int strnicmp(const char *s1, const char *s2, size_t n) +{ + int f, l; + + do { + if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) { + f -= 'A' - 'a'; + } + if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) { + l -= 'A' - 'a'; + } + } while (--n && f && (f == l)); + + return f - l; +} + +char *strchr(const char *s, int ch) +{ + while (*s && *s != (char)ch) { + s++; + } + if (*s == (char)ch) + return (char *)s; + { + return NULL; + } +} + +char *strrchr(const char *s, int ch) +{ + char *start = (char *)s; + + while (*s++) {} + + while (--s != start && *s != (char)ch) {} + + if (*s == (char)ch) { + return (char *)s; + } + + return NULL; +} + +char *strstr(const char *str1, const char *str2) +{ + char *cp = (char *)str1; + char *s1, *s2; + + if (!*str2) { + return (char *)str1; + } + + while (*cp) { + s1 = cp; + s2 = (char *)str2; + + while (*s1 && *s2 && !(*s1 - *s2)) { + s1++, s2++; + } + if (!*s2) { + return cp; + } + cp++; + } + + return NULL; +} + +size_t strspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char NOT in control map stops search. + if (*str) { + n = 0; + while (map[*str >> 3] & (1 << (*str & 7))) { + n++; + str++; + } + + return n; + } + + return 0; +} + +size_t strcspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + n = 0; + map[0] |= 1; + while (!(map[*str >> 3] & (1 << (*str & 7)))) { + n++; + str++; + } + + return n; +} + +char *strpbrk(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + int n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + while (*str) { + if (map[*str >> 3] & (1 << (*str & 7))) { + return (char *)str; + } + str++; + } + + return NULL; +} + +void *memmove(void *dst, const void *src, size_t n) +{ + void *ret = dst; + + if (dst <= src || (char *)dst >= ((char *)src + n)) { + /* Non-overlapping buffers; copy from lower addresses to higher + * addresses. + */ + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst + 1; + src = (char *)src + 1; + } + } else { + // Overlapping buffers; copy from higher addresses to lower addresses. + dst = (char *)dst + n - 1; + src = (char *)src + n - 1; + + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst - 1; + src = (char *)src - 1; + } + } + + return ret; +} + +void *memchr(const void *ptr, int ch, size_t n) +{ + while (n && (*(unsigned char *)ptr != (unsigned char)ch)) { + ptr = (unsigned char *)ptr + 1; + n--; + } + + return (n ? (void *)ptr : NULL); +} + +char *strlwr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)tolower(*p); + p++; + } + + return s; +} + +char *strupr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)toupper(*p); + p++; + } + + return s; +} + +char *strncat(char *s1, const char *s2, size_t n) +{ + char *start = s1; + + while (*s1++) {} + + s1--; + + while (n--) { + if (!(*s1++ = *s2++)) + return start; + } + + *s1 = '\0'; + + return start; +} + +char *strnset(char *s, int c, size_t n) +{ + while (n-- && *s) { + *s++ = (char)c; + } + + return s; +} + +char *strrev(char *s) +{ + char *start = s; + char *left = s; + char ch; + + while (*s++) {} + s -= 2; + + while (left < s) { + ch = *left; + *left++ = *s; + *s-- = ch; + } + + return start; +} + +char *strtok_r(char *str, const char *delim, char **saveptr) +{ + char *s; + const char *ctrl = delim; + + char map[32]; + int n; + + // Clear delim map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in delimiter table. + do { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + } while (*ctrl++); + + /* Initialize s. If str is NULL, set s to the saved + * pointer (i.e., continue breaking tokens out of the str + * from the last strtok call). + */ + if (str) { + s = str; + } else { + s = *saveptr; + } + + /* Find beginning of token (skip over leading delimiters). Note that + * there is no token iff this loop sets s to point to the terminal + * null (*s == '\0'). + */ + while ((map[*s >> 3] & (1 << (*s & 7))) && *s) { + s++; + } + + str = s; + + /* Find the end of the token. If it is not the end of the str, + * put a null there. + */ + for (; *s; s++) { + if (map[*s >> 3] & (1 << (*s & 7))) { + *s++ = '\0'; + + break; + } + } + + // Update nexttoken. + *saveptr = s; + + // Determine if a token has been found. + if (str == (char *)s) { + return NULL; + } else { + return str; + } +} + +// Intrinsic functions. + +/* + * #pragma function(memset) + * #pragma function(memcmp) + * #pragma function(memcpy) + * #pragma function(strcpy) + * #pragma function(strlen) + * #pragma function(strcat) + * #pragma function(strcmp) + * #pragma function(strset) + */ + +void *memset(void *ptr, int value, size_t num) +{ + // Truncate c to 8 bits. + value = (value & 0xFF); + + char *dst = (char *)ptr; + + // Initialize the rest of the size. + while (num--) { + *dst++ = (char)value; + } + + return ptr; +} + +int memcmp(const void *ptr1, const void *ptr2, size_t n) +{ + if (!n) { + return 0; + } + + while (--n && *(char *)ptr1 == *(char *)ptr2) { + ptr1 = (char *)ptr1 + 1; + ptr2 = (char *)ptr2 + 1; + } + + return *((unsigned char *)ptr1) - *((unsigned char *)ptr2); +} + +void *memcpy(void *ptr1, const void *ptr2, size_t num) +{ + char *_dst = ptr1; + const char *_src = ptr2; + + while (num--) { + *_dst++ = *_src++; + } + + return ptr1; +} + +void *memccpy(void *ptr1, const void *ptr2, int c, size_t n) +{ + while (n && (*((char *)(ptr1 = (char *)ptr1 + 1) - 1) = + *((char *)(ptr2 = (char *)ptr2 + 1) - 1)) != (char)c) { + n--; + } + + return n ? ptr1 : NULL; +} + +char *strcpy(char *dst, const char *src) +{ + char *save = dst; + + while ((*dst++ = *src++) != '\0') {} + + return save; +} + +size_t strlen(const char *s) +{ + const char *eos; + + for (eos = s; *eos != 0; ++eos) {} + + long len = eos - s; + + return (len < 0) ? 0 : (size_t)len; +} + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; *sc != '\0' && count--; ++sc) {} + + long len = sc - s; + + return (len < 0) ? 0 : (size_t)len; +} + +int strcmp(const char *s1, const char *s2) +{ + int ret = 0; + const char *s1t = s1, *s2t = s2; + + for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) {} + + return (ret < 0) ? -1 : (ret > 0) ? 1 : + 0; +} + +char *strcat(char *dst, const char *src) +{ + char *cp = dst; + + while (*cp) { + cp++; + } + + while ((*cp++ = *src++) != '\0') {} + + return dst; +} + +char *strset(char *s, int c) +{ + char *start = s; + + while (*s) { + *s++ = (char)c; + } + + return start; +} + +char *strtok(char *str, const char *delim) +{ + const char *spanp; + int c, sc; + char *tok; + static char *last; + + if (str == NULL && (str = last) == NULL) { + return (NULL); + } + +cont: + c = *str++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) { + goto cont; + } + } + + if (c == 0) { + last = NULL; + + return (NULL); + } + tok = str - 1; + + for (;;) { + c = *str++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + str = NULL; + } else { + str[-1] = 0; + } + last = str; + + return (tok); + } + } while (sc != 0); + } +} + +char *trim(char *str) +{ + size_t len = 0; + char *frontp = str; + char *endp = NULL; + + if (str == NULL) { + return NULL; + } + if (str[0] == '\0') { + return str; + } + + len = strlen(str); + endp = str + len; + + /* Move the front and back pointers to address the first non-whitespace + * characters from each end. + */ + while (isspace((unsigned char)*frontp)) { + ++frontp; + } + if (endp != frontp) { + while (isspace((unsigned char)*(--endp)) && endp != frontp) {} + } + if (str + len - 1 != endp) { + *(endp + 1) = '\0'; + } else if (frontp != str && endp == frontp) { + *str = '\0'; + } + /* Shift the string so that it starts at str so that if it's dynamically + * allocated, we can still free it on the returned pointer. Note the reuse + * of endp to mean the front of the string buffer now. + */ + endp = str; + if (frontp != str) { + while (*frontp) { + *endp++ = *frontp++; + } + *endp = '\0'; + } + + return str; +} + +char *strdup(const char *s) +{ + size_t len = strlen(s) + 1; + char *new = malloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = malloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + if ((s = *stringp) == NULL) { + return (NULL); + } + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + s = NULL; + } else { + s[-1] = 0; + } + *stringp = s; + + return (tok); + } + } while (sc != 0); + } +} + +char *itoa(char *buffer, unsigned int num, unsigned int base) +{ + // int numval; + char *p, *pbase; + + p = pbase = buffer; + + if (base == 16) { + sprintf(buffer, "%0x", num); + } else { + if (num == 0) { + *p++ = '0'; + } + while (num != 0) { + *p++ = (char)('0' + (num % base)); + num = num / base; + } + *p-- = 0; + + while (p > pbase) { + char tmp; + tmp = *p; + *p = *pbase; + *pbase = tmp; + + p--; + pbase++; + } + } + return buffer; +} + +char *replace_char(char *str, char find, char replace) +{ + char *current_pos = strchr(str, find); + + while (current_pos) { + *current_pos = replace; + current_pos = strchr(current_pos, find); + } + + return str; +} + +void strmode(mode_t mode, char *p) +{ + // Usr. + *p++ = mode & S_IRUSR ? 'r' : '-'; + *p++ = mode & S_IWUSR ? 'w' : '-'; + *p++ = mode & S_IXUSR ? 'x' : '-'; + // Group. + *p++ = mode & S_IRGRP ? 'r' : '-'; + *p++ = mode & S_IWGRP ? 'w' : '-'; + *p++ = mode & S_IXGRP ? 'x' : '-'; + // Other. + *p++ = mode & S_IROTH ? 'r' : '-'; + *p++ = mode & S_IWOTH ? 'w' : '-'; + *p++ = mode & S_IXOTH ? 'x' : '-'; + // Will be a '+' if ACL's implemented. + *p++ = ' '; + *p = '\0'; +} diff --git a/libc/src/sys/errno.c b/libc/src/sys/errno.c new file mode 100644 index 0000000..32e7e5f --- /dev/null +++ b/libc/src/sys/errno.c @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file errno.c +/// @brief Stores the error number. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/errno.h" + +/// @brief Returns the error number for the current process. +/// @return Pointer to the error number. +int *__geterrno() +{ + static int _errno = 0; + return &_errno; +} diff --git a/libc/src/sys/ioctl.c b/libc/src/sys/ioctl.c new file mode 100644 index 0000000..3e4a461 --- /dev/null +++ b/libc/src/sys/ioctl.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.c +/// @brief Input/Output ConTroL (IOCTL) functions implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/ioctl.h" +#include "sys/errno.h" +#include "system/syscall_types.h" + +_syscall3(int, ioctl, int, fd, unsigned long int, request, void *, data) diff --git a/libc/src/sys/unistd.c b/libc/src/sys/unistd.c new file mode 100644 index 0000000..81b1185 --- /dev/null +++ b/libc/src/sys/unistd.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file unistd.c +/// @brief Functions used to manage files. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "string.h" +#include "limits.h" +#include "stdio.h" + +#if 0 +#include "vfs.h" + +int rmdir(const char *path) +{ + char absolute_path[PATH_MAX]; + + get_absolute_normalized_path(path, absolute_path); + + int32_t mp_id = get_mountpoint_id(absolute_path); + if (mp_id < 0) { + printf("rmdir: failed to remove '%s':" + "Cannot find mount-point\n", + path); + + return -1; + } + + if (mountpoint_list[mp_id].dir_op.rmdir_f == NULL) { + return -1; + } + + return mountpoint_list[mp_id].dir_op.rmdir_f(absolute_path); +} + +#endif \ No newline at end of file diff --git a/libc/src/sys/utsname.c b/libc/src/sys/utsname.c new file mode 100644 index 0000000..8398d9b --- /dev/null +++ b/libc/src/sys/utsname.c @@ -0,0 +1,12 @@ +/// MentOS, The Mentoring Operating system project +/// @file utsname.c +/// @brief Functions used to provide information about the machine & OS. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/utsname.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "stddef.h" + +_syscall1(int, uname, utsname_t *, buf) diff --git a/libc/src/termios.c b/libc/src/termios.c new file mode 100644 index 0000000..074872f --- /dev/null +++ b/libc/src/termios.c @@ -0,0 +1,38 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "termios.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "bits/ioctls.h" + +int tcgetattr(int fd, termios *termios_p) +{ + int retval; + __asm__ volatile("int $0x80" + : "=a"(retval) + : "0"(__NR_ioctl), "b"((long)(fd)), "c"((long)(TCGETS)), + "d"((long)(termios_p))); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} + +int tcsetattr(int fd, int optional_actions, const termios *termios_p) +{ + int retval; + __asm__ volatile("int $0x80" + : "=a"(retval) + : "0"(__NR_ioctl), "b"((long)(fd)), "c"((long)(TCSETS)), + "d"((long)(termios_p))); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} \ No newline at end of file diff --git a/libc/src/time.c b/libc/src/time.c new file mode 100644 index 0000000..ce78f5b --- /dev/null +++ b/libc/src/time.c @@ -0,0 +1,399 @@ +/// MentOS, The Mentoring Operating system project +/// @file time.c +/// @brief Clock functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "time.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "string.h" +#include "stdio.h" + +static const char *weekdays[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static const char *months[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +_syscall1(time_t, time, time_t *, t) + +time_t difftime(time_t time1, time_t time2) +{ + return time1 - time2; +} + +/// @brief Computes day of week +/// @param y Year +/// @param m Month of year (in range 1 to 12) +/// @param d Day of month (in range 1 to 31) +/// @return Day of week (in range 1 to 7) +static inline int day_of_week(unsigned int y, unsigned int m, unsigned int d) +{ + int h, j, k; + // January and February are counted as months 13 and 14 of the previous year + if (m <= 2) { + m += 12; + y -= 1; + } + // J is the century + j = (int)(y / 100); + // K the year of the century + k = (int)(y % 100); + // Compute H using Zeller's congruence + h = (int)(d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4)); + // Return the day of the week + return ((h + 5) % 7) + 1; +} + +tm_t *localtime(const time_t *time) +{ + static tm_t date; + unsigned int a, b, c, d, e, f; + time_t t = *time; + // Negative Unix time values are not supported + if (t < 1) { + t = 0; + } + //Retrieve hours, minutes and seconds + date.tm_sec = (int)(t % 60); + t /= 60; + date.tm_min = (int)(t % 60); + t /= 60; + date.tm_hour = (int)(t % 24); + t /= 24; + // Convert Unix time to date + a = (unsigned int)((4 * t + 102032) / 146097 + 15); + b = (unsigned int)(t + 2442113 + a - (a / 4)); + c = (20 * b - 2442) / 7305; + d = b - 365 * c - (c / 4); + e = d * 1000 / 30601; + f = d - e * 30 - e * 601 / 1000; + // January and February are counted as months 13 and 14 of the previous year + if (e <= 13) { + c -= 4716; + e -= 1; + } else { + c -= 4715; + e -= 13; + } + //Retrieve year, month and day + date.tm_year = (int)c; + date.tm_mon = (int)e; + date.tm_mday = (int)f; + // Calculate day of week. + date.tm_wday = day_of_week(date.tm_year, date.tm_mon, date.tm_mday); + return &date; +} + +size_t strftime(char *str, size_t maxsize, const char *format, const tm_t *timeptr) +{ + if (format == NULL || str == NULL || maxsize <= 0) { + return 0; + } + char *f = (char *)format; + char *s = str; + int value; + size_t ret = 0; + while ((*f) != '\0') { + if ((*f) != '%') { + (*s++) = (*f); + } else { + ++f; + switch (*f) { +#if 0 + case 'a': /* locale's abbreviated weekday name */ + PUT_STRN(weekdays[timeptr->tm_wday], 3); + break; + + case 'A': /* locale's full weekday name */ + PUT_STR(weekdays[timeptr->tm_wday]); + break; +#endif + case 'b': // Abbreviated month name + { + strncat(s, months[timeptr->tm_mon], 3); + break; + } + case 'B': // Full month name + { + unsigned int i = (unsigned int)timeptr->tm_mon; + strcat(s, months[i]); + break; + } + case 'd': /* day of month as decimal number (01-31) */ + { + value = timeptr->tm_mday; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } + case 'H': /* hour (24-hour clock) as decimal (00-23) */ + { + value = timeptr->tm_hour; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } +#if 0 + case 'I': /* hour (12-hour clock) as decimal (01-12) */ + SPRINTF("%02d", ((timeptr->tm_hour + 11) % 12) + 1); + break; +#endif + case 'j': // Day of year as decimal number (001-366) + { + value = timeptr->tm_yday; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } + case 'm': // Month as decimal number (01-12) + { + value = timeptr->tm_mon; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } +#if 0 + case 'M': /* month as decimal number (00-59) */ + SPRINTF("%02d", timeptr->tm_min); + break; + + case 'p': /* AM/PM designations (12-hour clock) */ + PUT_STR(timeptr->tm_hour < 12 ? "AM" : "PM"); + /* s.b. locale-specific */ + break; + + case 'S': /* second as decimal number (00-61) */ + SPRINTF("%02d", timeptr->tm_sec); + break; + + case 'U': /* week of year (first Sunday = 1) (00-53) */ + case 'W': /* week of year (first Monday = 1) (00-53) */ + { + int fudge = timeptr->tm_wday - timeptr->tm_yday % 7; + if (*fp == 'W') { + fudge--; + } + fudge = (fudge + 7) % 7; /* +7 so not negative */ + SPRINTF("%02d", (timeptr->tm_yday + fudge) / 7); + break; + } + + case 'w': /* weekday (0-6), Sunday is 0 */ + SPRINTF("%02d", timeptr->tm_wday); + break; + + case 'x': /* appropriate date representation */ + RECUR("%B %d, %Y"); /* s.b. locale-specific */ + break; + + case 'X': /* appropriate time representation */ + RECUR("%H:%M:%S"); /* s.b. locale-specific */ + break; + + case 'y': /* year without century as decimal (00-99) */ + SPRINTF("%02d", timeptr->tm_year % 100); + break; + + case 'Y': /* year with century as decimal */ + SPRINTF("%d", timeptr->tm_year + 1900); + break; + + case 'Z': /* time zone name or abbreviation */ + /* XXX %%% */ + break; + + case '%': + PUT_CH('%'); + break; +#endif + default: + (*s++) = '%'; + (*s++) = (*f); + } + } + ++f; + } + +#if 0 +#define PUT_CH(c) (ret < maxsize ? (*dp++ = (c), ret++) : 0) +#define PUT_STR(str) \ + p = (char *)(str); \ + goto dostr; +#define PUT_STRN(str, n) \ + p = (char *)(str); \ + len = (n); \ + goto dostrn; +#define SPRINTF(fmt, arg) \ + sprintf(tmpbuf, fmt, arg); \ + p = tmpbuf; \ + goto dostr; +#define RECUR(fmt) \ + { \ + size_t r = strftime(dp, maxsize - ret, fmt, timeptr); \ + if (r <= 0) \ + return r; \ + dp += r; \ + ret += r; \ + } + + char* fp = (char*) format; + char* dp = s; + size_t ret = 0; + + if (format == NULL || s == NULL || maxsize <= 0) { + return 0; + } + + while (*fp != '\0') { + char* p; + int len; + char tmpbuf[10]; + + if (*fp != '%') { + PUT_CH(*fp++); + continue; + } + + fp++; + + switch (*fp) { + + case 'a': /* locale's abbreviated weekday name */ + PUT_STRN(weekdays[timeptr->tm_wday], 3); + break; + + case 'A': /* locale's full weekday name */ + PUT_STR(weekdays[timeptr->tm_wday]); + break; + + case 'b': /* locale's abbreviated month name */ + PUT_STRN(months[timeptr->tm_mon], 3); + break; + + case 'B': /* locale's full month name */ + PUT_STR(months[timeptr->tm_mon]); + break; + + case 'c': /* appropriate date and time representation */ + RECUR("%X %x"); /* s.b. locale-specific */ + break; + + case 'd': /* day of month as decimal number (01-31) */ + SPRINTF("%02d", timeptr->tm_mday); + break; + + case 'H': /* hour (24-hour clock) as decimal (00-23) */ + SPRINTF("%02d", timeptr->tm_hour); + break; + + case 'I': /* hour (12-hour clock) as decimal (01-12) */ + SPRINTF("%02d", ((timeptr->tm_hour + 11) % 12) + 1); + break; + + case 'j': /* day of year as decimal number (001-366) */ + SPRINTF("%03d", timeptr->tm_yday + 1); + break; + + case 'm': /* month as decimal number (01-12) */ + SPRINTF("%02d", timeptr->tm_mon + 1); + break; + + case 'M': /* month as decimal number (00-59) */ + SPRINTF("%02d", timeptr->tm_min); + break; + + case 'p': /* AM/PM designations (12-hour clock) */ + PUT_STR(timeptr->tm_hour < 12 ? "AM" : "PM"); + /* s.b. locale-specific */ + break; + + case 'S': /* second as decimal number (00-61) */ + SPRINTF("%02d", timeptr->tm_sec); + break; + + case 'U': /* week of year (first Sunday = 1) (00-53) */ + case 'W': /* week of year (first Monday = 1) (00-53) */ + { + int fudge = timeptr->tm_wday - timeptr->tm_yday % 7; + if (*fp == 'W') { + fudge--; + } + fudge = (fudge + 7) % 7; /* +7 so not negative */ + SPRINTF("%02d", (timeptr->tm_yday + fudge) / 7); + break; + } + + case 'w': /* weekday (0-6), Sunday is 0 */ + SPRINTF("%02d", timeptr->tm_wday); + break; + + case 'x': /* appropriate date representation */ + RECUR("%B %d, %Y"); /* s.b. locale-specific */ + break; + + case 'X': /* appropriate time representation */ + RECUR("%H:%M:%S"); /* s.b. locale-specific */ + break; + + case 'y': /* year without century as decimal (00-99) */ + SPRINTF("%02d", timeptr->tm_year % 100); + break; + + case 'Y': /* year with century as decimal */ + SPRINTF("%d", timeptr->tm_year + 1900); + break; + + case 'Z': /* time zone name or abbreviation */ + /* XXX %%% */ + break; + + case '%': + PUT_CH('%'); + break; + + dostr: + while (*p != '\0') + PUT_CH(*p++); + break; + + dostrn: + while (len-- > 0) + PUT_CH(*p++); + break; + } + + ++fp; + } + if (ret >= maxsize) { + s[maxsize - 1] = '\0'; + return 0; + } + *dp = '\0'; +#endif + return ret; +} + +_syscall2(int, nanosleep, const timespec *, req, timespec *, rem) + +unsigned int sleep(unsigned int seconds) +{ + timespec req, rem; + req.tv_sec = seconds; + // Call the nanosleep. + int __ret = nanosleep(&req, &rem); + // If the call to the nanosleep is interrupted by a signal handler, + // then it returns -1 with errno set to EINTR. + if ((__ret == -1) && (errno == EINTR)) { + return rem.tv_sec; + } + return 0; +} + +_syscall2(int, getitimer, int, which, itimerval *, curr_value) + +_syscall3(int, setitimer, int, which, const itimerval *, new_value, itimerval *, old_value) \ No newline at end of file diff --git a/libc/src/unistd/chdir.c b/libc/src/unistd/chdir.c new file mode 100644 index 0000000..d93eb58 --- /dev/null +++ b/libc/src/unistd/chdir.c @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file chdir.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, chdir, const char *, path) + +_syscall1(int, fchdir, int, fd) diff --git a/libc/src/unistd/close.c b/libc/src/unistd/close.c new file mode 100644 index 0000000..51013b6 --- /dev/null +++ b/libc/src/unistd/close.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file close.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, close, int, fd) diff --git a/libc/src/unistd/exec.c b/libc/src/unistd/exec.c new file mode 100644 index 0000000..3580d54 --- /dev/null +++ b/libc/src/unistd/exec.c @@ -0,0 +1,243 @@ +/// MentOS, The Mentoring Operating system project +/// @file exec.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +#include "sys/stat.h" +#include "stdlib.h" +#include "string.h" +#include "stdarg.h" +#include "fcntl.h" + +extern char **environ; + +/// @brief Default `PATH`. +#define DEFAULT_PATH "/bin:/usr/bin" + +/// @brief Finds an executable inside the PATH entries. +/// @param file The file to search. +/// @param buf The buffer where we will store the absolute path. +/// @param buf_len The length of the buffer. +/// @return 0 if we have found the file inside the entries of PATH, +/// -1 otherwise. +static inline int __find_in_path(const char *file, char *buf, size_t buf_len) +{ + // Determine the search path. + char *PATH_VAR = getenv("PATH"); + if (PATH_VAR == NULL) { + PATH_VAR = DEFAULT_PATH; + } + // Prepare a stat object for later. + stat_t stat_buf; + // Copy the path. + char *path = strdup(PATH_VAR); + // Iterate through the path entries. + char *token = strtok(path, ":"); + while (token != NULL) { + strcpy(buf, token); + strcat(buf, "/"); + strcat(buf, file); + if (stat(buf, &stat_buf) == 0) { + if (stat_buf.st_mode & S_IXUSR) { + // TODO: Check why `init` has problems with this free. + // To reproduce the problem use this in init.c: + // execvp("login", _argv); + free(path); + return 0; + } + } + token = strtok(NULL, ":"); + } + free(path); + // We did not find the file inside PATH. + errno = ENOENT; + return -1; +} + +_syscall3(int, execve, const char *, path, char *const *, argv, char *const *, envp) + +int execv(const char *path, char *const argv[]) +{ + return execve(path, argv, environ); +} + +int execvp(const char *file, char *const argv[]) +{ + if (!file || !argv || !environ) { + errno = ENOENT; + return -1; + } + if (file[0] == '/') { + return execve(file, argv, environ); + } + // Prepare a buffer for the absolute path. + char absolute_path[PATH_MAX] = { 0 }; + // Find the file inside the entries of the PATH variable. + if (__find_in_path(file, absolute_path, PATH_MAX) == 0) { + return execve(absolute_path, argv, environ); + } + errno = ENOENT; + return -1; +} + +int execvpe(const char *file, char *const argv[], char *const envp[]) +{ + if (!file || !argv || !envp) { + errno = ENOENT; + return -1; + } + if (file[0] == '/') { + return execve(file, argv, envp); + } + // Prepare a buffer for the absolute path. + char absolute_path[PATH_MAX]; + // Find the file inside the entries of the PATH variable. + if (__find_in_path(file, absolute_path, PATH_MAX) == 0) { + return execve(absolute_path, argv, envp); + } + errno = ENOENT; + return -1; +} + +int execl(const char *path, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i <= argc; ++i) + argv[i] = va_arg(ap, char *); + va_end(ap); + + // Now, we can call `execve` plain and simple. + return execve(path, argv, environ); +} + +int execlp(const char *file, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i < argc; ++i) + argv[i] = va_arg(ap, char *); + va_end(ap); + + // Close argv. + argv[argc] = NULL; + + // Now, we can call `execve` plain and simple. + return execvpe(file, argv, environ); +} + +int execle(const char *path, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + argc -= 1; + if (argc < 0) { + errno = EINVAL; + return -1; + } + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i < argc; ++i) + argv[i] = va_arg(ap, char *); + // Close argv. + argv[argc] = NULL; + + // Phase 3: Store the pointer to the environ. + char **envp = va_arg(ap, char **); + va_end(ap); + + // Now, we can call `execve` plain and simple. + return execve(path, argv, envp); +} + +int execlpe(const char *file, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + // We don't need to count `envp` among the arguments. + if ((argc -= 1) < 0) { + errno = EINVAL; + return -1; + } + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i < argc; ++i) + argv[i] = va_arg(ap, char *); + // Close argv. + argv[argc] = NULL; + + // Phase 3: Store the pointer to the environ. + char **envp = va_arg(ap, char **); + va_end(ap); + + // Now, we can call `execve` plain and simple. + return execvpe(file, argv, envp); +} diff --git a/libc/src/unistd/exit.c b/libc/src/unistd/exit.c new file mode 100644 index 0000000..0432e08 --- /dev/null +++ b/libc/src/unistd/exit.c @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file exit.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" + +void exit(int status) +{ + long __res; + __inline_syscall1(__res, exit, status); + // The process never returns from this system call! +} diff --git a/libc/src/unistd/fork.c b/libc/src/unistd/fork.c new file mode 100644 index 0000000..a649f36 --- /dev/null +++ b/libc/src/unistd/fork.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file fork.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, fork) diff --git a/libc/src/unistd/getcwd.c b/libc/src/unistd/getcwd.c new file mode 100644 index 0000000..af08b71 --- /dev/null +++ b/libc/src/unistd/getcwd.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getcwd.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(char *, getcwd, char *, buf, size_t, size) diff --git a/libc/src/unistd/getdents.c b/libc/src/unistd/getdents.c new file mode 100644 index 0000000..29a2865 --- /dev/null +++ b/libc/src/unistd/getdents.c @@ -0,0 +1,12 @@ +/// MentOS, The Mentoring Operating system project +/// @file getdents.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + + +_syscall3(int, getdents, int, fd, dirent_t *, dirp, unsigned int, count) diff --git a/libc/src/unistd/getgid.c b/libc/src/unistd/getgid.c new file mode 100644 index 0000000..c27ee57 --- /dev/null +++ b/libc/src/unistd/getgid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getgid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, getgid) diff --git a/libc/src/unistd/getpid.c b/libc/src/unistd/getpid.c new file mode 100644 index 0000000..d9851e9 --- /dev/null +++ b/libc/src/unistd/getpid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getpid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, getpid) diff --git a/libc/src/unistd/getppid.c b/libc/src/unistd/getppid.c new file mode 100644 index 0000000..3cc612e --- /dev/null +++ b/libc/src/unistd/getppid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getppid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, getppid) diff --git a/libc/src/unistd/getsid.c b/libc/src/unistd/getsid.c new file mode 100644 index 0000000..7e8daf8 --- /dev/null +++ b/libc/src/unistd/getsid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getsid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(pid_t, getsid, pid_t, pid) diff --git a/libc/src/unistd/interval.c b/libc/src/unistd/interval.c new file mode 100644 index 0000000..fc7ad63 --- /dev/null +++ b/libc/src/unistd/interval.c @@ -0,0 +1,6 @@ + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, alarm, int, seconds) diff --git a/libc/src/unistd/kill.c b/libc/src/unistd/kill.c new file mode 100644 index 0000000..14e7a95 --- /dev/null +++ b/libc/src/unistd/kill.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file kill.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(int, kill, pid_t, pid, int, sig) diff --git a/libc/src/unistd/lseek.c b/libc/src/unistd/lseek.c new file mode 100644 index 0000000..b7b4fe4 --- /dev/null +++ b/libc/src/unistd/lseek.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(off_t, lseek, int, fd, off_t, offset, int, whence) diff --git a/libc/src/unistd/mkdir.c b/libc/src/unistd/mkdir.c new file mode 100644 index 0000000..e05c430 --- /dev/null +++ b/libc/src/unistd/mkdir.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file mkdir.c +/// @brief Make directory functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(int, mkdir, const char *, path, mode_t, mode) diff --git a/libc/src/unistd/nice.c b/libc/src/unistd/nice.c new file mode 100644 index 0000000..d72815b --- /dev/null +++ b/libc/src/unistd/nice.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file nice.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, nice, int, inc) diff --git a/libc/src/unistd/open.c b/libc/src/unistd/open.c new file mode 100644 index 0000000..dfe1d8f --- /dev/null +++ b/libc/src/unistd/open.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(int, open, const char *, pathname, int, flags, mode_t, mode) diff --git a/libc/src/unistd/read.c b/libc/src/unistd/read.c new file mode 100644 index 0000000..211166f --- /dev/null +++ b/libc/src/unistd/read.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file read.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes) diff --git a/libc/src/unistd/reboot.c b/libc/src/unistd/reboot.c new file mode 100644 index 0000000..d8a3a8c --- /dev/null +++ b/libc/src/unistd/reboot.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file reboot.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, void *, arg) diff --git a/libc/src/unistd/rmdir.c b/libc/src/unistd/rmdir.c new file mode 100644 index 0000000..3c5115e --- /dev/null +++ b/libc/src/unistd/rmdir.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file rmdir.c +/// @brief Make directory functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, rmdir, const char *, path) diff --git a/libc/src/unistd/setgid.c b/libc/src/unistd/setgid.c new file mode 100644 index 0000000..7ec415a --- /dev/null +++ b/libc/src/unistd/setgid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file setgid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, setgid, pid_t, pid) diff --git a/libc/src/unistd/setsid.c b/libc/src/unistd/setsid.c new file mode 100644 index 0000000..e988438 --- /dev/null +++ b/libc/src/unistd/setsid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file setsid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, setsid) diff --git a/libc/src/unistd/signal.c b/libc/src/unistd/signal.c new file mode 100644 index 0000000..f00d5e5 --- /dev/null +++ b/libc/src/unistd/signal.c @@ -0,0 +1,103 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +#include "signal.h" +#include "sys/bitops.h" + +static const char *sys_siglist[] = { + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "USR1", + "USR2", + "CHLD", + "PWR", + "WINCH", + "URG", + "POLL", + "STOP", + "TSTP", + "CONT", + "TTIN", + "TTOU", + "VTALRM", + "PROF", + "XCPU", + "XFSZ", + NULL, +}; + +_syscall2(sighandler_t, signal, int, signum, sighandler_t, handler) + +_syscall3(int, sigaction, int, signum, const sigaction_t *, act, sigaction_t *, oldact) + +_syscall3(int, sigprocmask, int, how, const sigset_t *, set, sigset_t *, oldset) + +const char *strsignal(int sig) +{ + if ((sig >= SIGHUP) && (sig < NSIG)) + return sys_siglist[sig - 1]; + return NULL; +} + +int sigemptyset(sigset_t *set) +{ + if (set) { + set->sig[0] = 0; + return 0; + } + return -1; +} + +int sigfillset(sigset_t *set) +{ + if (set) { + set->sig[0] = ~0UL; + return 0; + } + return -1; +} + +int sigaddset(sigset_t *set, int signum) +{ + if (set && ((signum))) { + bit_set_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigdelset(sigset_t *set, int signum) +{ + if (set) { + bit_clear_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigismember(sigset_t *set, int signum) +{ + if (set) + return bit_check(set->sig[(signum - 1) / 32], (signum - 1) % 32); + return -1; +} \ No newline at end of file diff --git a/libc/src/unistd/stat.c b/libc/src/unistd/stat.c new file mode 100644 index 0000000..da0ae8c --- /dev/null +++ b/libc/src/unistd/stat.c @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.c +/// @brief Stat functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +#include "sys/stat.h" + +_syscall2(int, stat, const char *, path, stat_t *, buf) + +_syscall2(int, fstat, int, fd, stat_t *, buf) diff --git a/libc/src/unistd/unlink.c b/libc/src/unistd/unlink.c new file mode 100644 index 0000000..4508ec7 --- /dev/null +++ b/libc/src/unistd/unlink.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file unlink.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, unlink, const char *, path) diff --git a/libc/src/unistd/waitpid.c b/libc/src/unistd/waitpid.c new file mode 100644 index 0000000..ee7d233 --- /dev/null +++ b/libc/src/unistd/waitpid.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file waitpid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "sys/wait.h" +#include "sys/errno.h" +#include "system/syscall_types.h" + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +pid_t waitpid(pid_t pid, int *status, int options) +{ + pid_t __res; + int __status = 0; + do { + __inline_syscall3(__res, waitpid, pid, &__status, options); + if (__res < 0) + break; + if (__status == EXIT_ZOMBIE) + break; + if (options && WNOHANG) + break; + } while (1); + if (status) + *status = __status; + __syscall_return(pid_t, __res); +} + +pid_t wait(int *status) +{ + return waitpid(-1, status, 0); +} diff --git a/libc/src/unistd/write.c b/libc/src/unistd/write.c new file mode 100644 index 0000000..5f7e547 --- /dev/null +++ b/libc/src/unistd/write.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file write.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes) diff --git a/libc/src/vscanf.c b/libc/src/vscanf.c new file mode 100644 index 0000000..5082962 --- /dev/null +++ b/libc/src/vscanf.c @@ -0,0 +1,138 @@ +/// MentOS, The Mentoring Operating system project +/// @file vscanf.c +/// @brief Reading formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +static int vsscanf(const char *buf, const char *s, va_list ap) +{ + int count = 0, noassign = 0, width = 0, base = 0; + const char *tc; + char tmp[BUFSIZ]; + + while (*s && *buf) { + while (isspace(*s)) + ++s; + if (*s == '%') { + ++s; + for (; *s; ++s) { + if (strchr("dibouxcsefg%", *s)) + break; + if (*s == '*') + noassign = 1; + else if (isdigit(*s)) { + for (tc = s; isdigit(*s); ++s) + {} + strncpy(tmp, tc, s - tc); + tmp[s - tc] = '\0'; + width = strtol(tmp, NULL, 10); + --s; + } + } + if (*s == 's') { + while (isspace(*buf)) + ++buf; + if (!width) + width = strcspn(buf, " \t\n\r\f\v"); + if (!noassign) { + char *string = va_arg(ap, char *); + strncpy(string, buf, width); + string[width] = '\0'; + } + buf += width; + } else if (*s == 'c') { + while (isspace(*buf)) + ++buf; + if (!width) + width = 1; + if (!noassign) { + strncpy(va_arg(ap, char *), buf, width); + } + buf += width; + } else if (strchr("duxob", *s)) { + while (isspace(*buf)) + ++buf; + if (*s == 'd' || *s == 'u') + base = 10; + else if (*s == 'x') + base = 16; + else if (*s == 'o') + base = 8; + else if (*s == 'b') + base = 2; + if (!width) { + if (isspace(*(s + 1)) || *(s + 1) == 0) + width = strcspn(buf, " \t\n\r\f\v"); + else + width = strchr(buf, *(s + 1)) - buf; + } + strncpy(tmp, buf, width); + tmp[width] = '\0'; + buf += width; + if (!noassign) + *va_arg(ap, unsigned int *) = strtol(tmp, NULL, base); + } + if (!noassign) + ++count; + width = noassign = 0; + ++s; + } else { + while (isspace(*buf)) + ++buf; + if (*s != *buf) + break; + else + ++s, ++buf; + } + } + return (count); +} + +static int vfscanf(int fd, const char *fmt, va_list ap) +{ + int count; + char buf[BUFSIZ + 1]; + + if (fgets(buf, BUFSIZ, fd) == 0) + return (-1); + count = vsscanf(buf, fmt, ap); + return (count); +} + +int scanf(const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfscanf(STDIN_FILENO, fmt, ap); + va_end(ap); + return (count); +} + +int fscanf(int fd, const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfscanf(fd, fmt, ap); + va_end(ap); + return (count); +} + +int sscanf(const char *buf, const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vsscanf(buf, fmt, ap); + va_end(ap); + return (count); +} diff --git a/libc/src/vsprintf.c b/libc/src/vsprintf.c new file mode 100644 index 0000000..7418095 --- /dev/null +++ b/libc/src/vsprintf.c @@ -0,0 +1,693 @@ +/// MentOS, The Mentoring Operating system project +/// @file vsprintf.c +/// @brief Print formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include "fcvt.h" +#include "ctype.h" +#include "string.h" +#include "stdarg.h" +#include "stdint.h" +#include "stdio.h" +#include "sys/unistd.h" + +/// Size of the buffer used to call cvt functions. +#define CVTBUFSIZE 500 + +#define FLAGS_ZEROPAD (1U << 0U) ///< Fill zeros before the number. +#define FLAGS_LEFT (1U << 1U) ///< Left align the value. +#define FLAGS_PLUS (1U << 2U) ///< Print the plus sign. +#define FLAGS_SPACE (1U << 3U) ///< If positive add a space instead of the plus sign. +#define FLAGS_HASH (1U << 4U) ///< Preceed with 0x or 0X, %x or %X respectively. +#define FLAGS_UPPERCASE (1U << 5U) ///< Print uppercase. +#define FLAGS_SIGN (1U << 6U) ///< Print the sign. + +/// The list of digits. +static char *_digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/// The list of uppercase digits. +static char *_upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/// @brief Returns the index of the first non-integer character. +static inline int skip_atoi(const char **s) +{ + int i = 0; + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags) +{ + char c, tmp[66] = { 0 }; + char *dig = _digits; + + if (bitmask_check(flags, FLAGS_UPPERCASE)) { + dig = _upper_digits; + } + if (bitmask_check(flags, FLAGS_LEFT)) { + bitmask_clear_assign(flags, FLAGS_ZEROPAD); + } + if (base < 2 || base > 36) { + return 0; + } + + c = bitmask_check(flags, FLAGS_ZEROPAD) ? '0' : ' '; + + // -------------------------------- + // Set the sign. + // -------------------------------- + char sign = 0; + if (bitmask_check(flags, FLAGS_SIGN)) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (bitmask_check(flags, FLAGS_PLUS)) { + sign = '+'; + size--; + } else if (bitmask_check(flags, FLAGS_SPACE)) { + sign = ' '; + size--; + } + } + // Sice I've removed the sign (if negative), i can transform it to unsigned. + uint32_t uns_num = (uint32_t)num; + if (bitmask_check(flags, FLAGS_HASH)) { + if (base == 16) { + size -= 2; + } else if (base == 8) { + size--; + } + } + + int32_t i = 0; + if (uns_num == 0) { + tmp[i++] = '0'; + } else { + while (uns_num != 0) { + tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; + uns_num = ((unsigned long)uns_num) / (unsigned)base; + } + } + if (i > precision) { + precision = i; + } + size -= precision; + if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { + while (size-- > 0) + *str++ = ' '; + } + if (sign) { + *str++ = sign; + } + if (bitmask_check(flags, FLAGS_HASH)) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = _digits[33]; + } + } + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + while (i < precision--) { + *str++ = '0'; + } + while (i-- > 0) { + *str++ = tmp[i]; + } + while (size-- > 0) { + *str++ = ' '; + } + return str; +} + +static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + char *dig = _digits; + int i, len; + + if (bitmask_check(flags, FLAGS_UPPERCASE)) { + dig = _upper_digits; + } + + len = 0; + for (i = 0; i < 6; i++) { + if (i != 0) { + tmp[len++] = ':'; + } + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) { + if (i != 0) { + tmp[len++] = '.'; + } + n = addr[i]; + + if (n == 0) { + tmp[len++] = _digits[0]; + } else { + if (n >= 100) { + tmp[len++] = _digits[n / 100]; + n = n % 100; + tmp[len++] = _digits[n / 10]; + n = n % 10; + } else if (n >= 10) { + tmp[len++] = _digits[n / 10]; + n = n % 10; + } + + tmp[len++] = _digits[n]; + } + } + + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static void cfltcvt(double value, char *buffer, char fmt, int precision) +{ + int decpt, sign, exp, pos; + char cvtbuf[CVTBUFSIZE]; + char *digits = cvtbuf; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') { + ecvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) { + fmt = 'e'; + precision -= 1; + } else { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') { + ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE); + + if (sign) { + *buffer++ = '-'; + } + *buffer++ = *digits; + if (precision > 0) { + *buffer++ = '.'; + } + memcpy(buffer, digits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) { + if (value == 0.0) { + exp = 0; + } else { + exp = -1; + } + } else { + exp = decpt - 1; + } + + if (exp < 0) { + *buffer++ = '-'; + exp = -exp; + } else { + *buffer++ = '+'; + } + + buffer[2] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[1] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[0] = (char)((exp % 10) + '0'); + buffer += 3; + } else if (fmt == 'f') { + fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + if (sign) { + *buffer++ = '-'; + } + if (*digits) { + if (decpt <= 0) { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) { + *buffer++ = '0'; + } + while (*digits) { + *buffer++ = *digits++; + } + } else { + pos = 0; + while (*digits) { + if (pos++ == decpt) { + *buffer++ = '.'; + } + *buffer++ = *digits++; + } + } + } else { + *buffer++ = '0'; + if (precision > 0) { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) { + *buffer++ = '0'; + } + } + } + } + + *buffer = '\0'; +} + +static void forcdecpt(char *buffer) +{ + while (*buffer) { + if (*buffer == '.') { + return; + } + if (*buffer == 'e' || *buffer == 'E') { + break; + } + + buffer++; + } + + if (*buffer) { + long n = (long)strlen(buffer); + while (n > 0) { + buffer[n + 1] = buffer[n]; + n--; + } + *buffer = '.'; + } else { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) +{ + char *stop; + + while (*buffer && *buffer != '.') { + buffer++; + } + + if (*buffer++) { + while (*buffer && *buffer != 'e' && *buffer != 'E') { + buffer++; + } + stop = buffer--; + while (*buffer == '0') { + buffer--; + } + if (*buffer == '.') { + buffer--; + } + while ((*++buffer = *stop++)) {} + } +} + +static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags) +{ + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding. + if (bitmask_check(flags, FLAGS_LEFT)) + bitmask_clear_assign(flags, FLAGS_ZEROPAD); + + // Determine padding and sign char. + c = bitmask_check(flags, FLAGS_ZEROPAD) ? '0' : ' '; + sign = 0; + if (bitmask_check(flags, FLAGS_SIGN)) { + if (num < 0.0) { + sign = '-'; + num = -num; + size--; + } else if (bitmask_check(flags, FLAGS_PLUS)) { + sign = '+'; + size--; + } else if (bitmask_check(flags, FLAGS_SPACE)) { + sign = ' '; + size--; + } + } + + // Compute the precision value. + if (precision < 0) { + // Default precision: 6. + precision = 6; + } else if (precision == 0 && fmt == 'g') { + // ANSI specified. + precision = 1; + } + + // Convert floating point number to text. + cfltcvt(num, tmp, fmt, precision); + + // '#' and precision == 0 means force a decimal point. + if (bitmask_check(flags, FLAGS_HASH) && (precision == 0)) { + forcdecpt(tmp); + } + + // 'g' format means crop zero unless '#' given. + if ((fmt == 'g') && !bitmask_check(flags, FLAGS_HASH)) { + cropzeros(tmp); + } + + n = strlen(tmp); + + // Output number with alignment and padding. + size -= n; + if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = ' '; + } + } + + if (sign) { + *str++ = sign; + } + + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + + for (i = 0; i < n; i++) { + *str++ = tmp[i]; + } + + while (size-- > 0) { + *str++ = ' '; + } + + return str; +} + +int vsprintf(char *str, const char *fmt, va_list args) +{ + int base; + char *tmp; + char *s; + + // Flags to number(). + unsigned flags; + + // 'h', 'l', or 'L' for integer fields. + char qualifier; + + for (tmp = str; *fmt; fmt++) { + if (*fmt != '%') { + *tmp++ = *fmt; + + continue; + } + + // Process flags- + flags = 0; + repeat: + // This also skips first '%'. + fmt++; + switch (*fmt) { + case '-': + bitmask_set_assign(flags, FLAGS_LEFT); + goto repeat; + case '+': + bitmask_set_assign(flags, FLAGS_PLUS); + goto repeat; + case ' ': + bitmask_set_assign(flags, FLAGS_SPACE); + goto repeat; + case '#': + bitmask_set_assign(flags, FLAGS_HASH); + goto repeat; + case '0': + bitmask_set_assign(flags, FLAGS_ZEROPAD); + goto repeat; + } + + // Get the width of the output field. + int32_t field_width; + field_width = -1; + + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int32_t); + if (field_width < 0) { + field_width = -field_width; + bitmask_set_assign(flags, FLAGS_LEFT); + } + } + + /* Get the precision, thus the minimum number of digits for + * integers; max number of chars for from string. + */ + int32_t precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + // Get the conversion qualifier. + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base. + base = 10; + + switch (*fmt) { + case 'c': + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (--field_width > 0) { + *tmp++ = ' '; + } + } + *tmp++ = va_arg(args, char); + while (--field_width > 0) { + *tmp++ = ' '; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) { + s = ""; + } + + int32_t len = (int32_t)strnlen(s, (uint32_t)precision); + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < field_width--) { + *tmp++ = ' '; + } + } + + int32_t it; + for (it = 0; it < len; ++it) { + *tmp++ = *s++; + } + while (len < field_width--) { + *tmp++ = ' '; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + bitmask_set_assign(flags, FLAGS_ZEROPAD); + } + tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + continue; + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (tmp - str); + } else { + int *ip = va_arg(args, int *); + *ip = (tmp - str); + } + continue; + case 'A': + bitmask_set_assign(flags, FLAGS_UPPERCASE); + break; + case 'a': + if (qualifier == 'l') + tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + else + tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + // Integer number formats - set up the flags and "break". + case 'o': + base = 8; + break; + + case 'X': + bitmask_set_assign(flags, FLAGS_UPPERCASE); + break; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + bitmask_set_assign(flags, FLAGS_SIGN); + + case 'u': + break; + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); + continue; + default: + if (*fmt != '%') + *tmp++ = '%'; + if (*fmt) + *tmp++ = *fmt; + else + --fmt; + continue; + } + + if (bitmask_check(flags, FLAGS_SIGN)) { + long num; + if (qualifier == 'l') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = va_arg(args, short); + } else { + num = va_arg(args, int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } else { + unsigned long num; + if (qualifier == 'l') { + num = va_arg(args, unsigned long); + } else if (qualifier == 'h') { + num = va_arg(args, unsigned short); + } else { + num = va_arg(args, unsigned int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } + } + + *tmp = '\0'; + return tmp - str; +} + +int printf(const char *fmt, ...) +{ + char buffer[4096]; + va_list ap; + + // Start variabile argument's list. + va_start(ap, fmt); + int len = vsprintf(buffer, fmt, ap); + va_end(ap); + + // Write the contento to standard output. + puts(buffer); + + return len; +} + +int sprintf(char *str, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + int len = vsprintf(str, fmt, ap); + va_end(ap); + + return len; +} + +int fprintf(int fd, const char *fmt, ...) +{ + char buffer[4096]; + va_list ap; + + va_start(ap, fmt); + int len = vsprintf(buffer, fmt, ap); + va_end(ap); + + if (len > 0) { + if (write(fd, buffer, len) <= 0) + return EOF; + return len; + } + return -1; +} \ No newline at end of file diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index a3657dd..78e3ec6 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -1,336 +1,349 @@ -# ------------------------------------------------------------------------------ +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) # Initialize the project. -project(MentOs C ASM) +project(mentos C ASM) -# ------------------------------------------------------------------------------ -# Set the project name. -set(PROJECT_NAME MentOs) +# ============================================================================= +# Set the main file names. +set(KERNEL_NAME ${PROJECT_NAME}_kernel) +set(BOOTLOADER_NAME ${PROJECT_NAME}_bootloader) +set(BUDDY_SYSTEM_NAME ${PROJECT_NAME}_memory) +set(BUDDY_SYSTEM_FILE ${PROJECT_SOURCE_DIR}/lib${PROJECT_NAME}_memory.a) -# ------------------------------------------------------------------------------ -# Find doxygen -find_package(Doxygen) +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() -# Add option to generate documentation. -option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) +# ============================================================================= +# Define that this code is kernel code. +add_definitions(-D__KERNEL__) -if (BUILD_DOCUMENTATION) - if (NOT DOXYGEN_FOUND) - message(FATAL_ERROR "Doxygen is needed to build the documentation.") - endif (NOT DOXYGEN_FOUND) +# ============================================================================= +# Add the memory option. +option(USE_BUDDY_SYSTEM "Build using the buddysystem written by the user." OFF) - set(doxyfile_in ${CMAKE_SOURCE_DIR}/doc/dreamos.doxyfile) - set(doxyfile ${CMAKE_BINARY_DIR}/doxyfile) - - configure_file(${doxyfile_in} ${doxyfile} @ONLY) - - add_custom_target( - doc - COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - VERBATIM - ) - install(DIRECTORY ${CMAKE_BINARY_DIR}/html DESTINATION share/doc) -endif (BUILD_DOCUMENTATION) - -# ------------------------------------------------------------------------------ +# ============================================================================= # Add the scheduling option. set(SCHEDULER_TYPE "SCHEDULER_RR" - CACHE STRING - "Chose the type of scheduler: SCHEDULER_RR SCHEDULER_PRIORITY SCHEDULER_CFS") -set_property( - CACHE SCHEDULER_TYPE PROPERTY STRINGS - SCHEDULER_RR - SCHEDULER_PRIORITY - SCHEDULER_CFS) -if ("${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_RR" OR - "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_PRIORITY" OR - "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_CFS") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${SCHEDULER_TYPE}") - message(STATUS "Setting scheduler type to ${SCHEDULER_TYPE}.") -else () - message(FATAL_ERROR "Scheduler type ${SCHEDULER_TYPE} is not valid.") -endif () + CACHE STRING "Chose the type of scheduler: SCHEDULER_RR SCHEDULER_PRIORITY SCHEDULER_CFS SCHEDULER_EDF SCHEDULER_RM SCHEDULER_AEDF") +# List of schedulers. +set_property(CACHE SCHEDULER_TYPE PROPERTY STRINGS SCHEDULER_RR SCHEDULER_PRIORITY SCHEDULER_CFS SCHEDULER_EDF SCHEDULER_RM) +# Check which scheduler is currently active and export the related macro. +if("${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_RR" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_PRIORITY" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_CFS" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_EDF" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_RM" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_AEDF") + # Add the define stating which scheduler is currently active. + add_definitions(-D${SCHEDULER_TYPE}) + # Notify the type of scheduler. + message(STATUS "Setting scheduler type to ${SCHEDULER_TYPE}.") +else() + message(FATAL_ERROR "Scheduler type ${SCHEDULER_TYPE} is not valid.") +endif() -# ------------------------------------------------------------------------------ -# Add the option to enable buddy system. -option(ENABLE_BUDDY_SYSTEM "Enables the buddy system" OFF) -if (ENABLE_BUDDY_SYSTEM) - message(STATUS "Enabling buddy system.") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_BUDDYSYSTEM") -else (ENABLE_BUDDY_SYSTEM) - message(STATUS "Buddy system disabled.") -endif (ENABLE_BUDDY_SYSTEM) +# ============================================================================= +# Add the video option. +set(VIDEO_TYPE + "VGA_TEXT_MODE" + CACHE STRING "Chose the type of video: VGA_TEXT_MODE VGA_MODE_320_200_256 VGA_MODE_640_480_16 VGA_MODE_720_480_16") +# List of video tpes. +set_property(CACHE VIDEO_TYPE PROPERTY STRINGS VGA_TEXT_MODE VGA_MODE_320_200_256 VGA_MODE_640_480_16 VGA_MODE_720_480_16) +# Check which VIDEO is currently active and export the related macro. +if("${VIDEO_TYPE}" STREQUAL "VGA_TEXT_MODE" + OR "${VIDEO_TYPE}" STREQUAL "VGA_MODE_320_200_256" + OR "${VIDEO_TYPE}" STREQUAL "VGA_MODE_640_480_16" + OR "${VIDEO_TYPE}" STREQUAL "VGA_MODE_720_480_16") + add_definitions(-D${VIDEO_TYPE}) + message(STATUS "Setting video type to ${VIDEO_TYPE}.") +else() + message(FATAL_ERROR "Video type ${VIDEO_TYPE} is not valid.") +endif() -# ------------------------------------------------------------------------------ -# Set the compiler options (original). +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the compiler options. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-pic") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-stack-protector") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcommon") +endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") -# ------------------------------------------------------------------------------ -# Set the compiler options (extra). -set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Wall") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Wpedantic") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -pedantic-errors") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Wextra") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Werror") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wdisabled-optimization") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wfloat-equal") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wfloat-conversion") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-nonliteral") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-security") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-y2k") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wimport") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winit-self") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winline") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winvalid-pch") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-field-initializers") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-include-dirs") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpacked") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpointer-arith") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstack-protector") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-aliasing=2") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-default") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-enum") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunreachable-code") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-function") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-label") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-parameter") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-value") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-variable") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wvariadic-macros") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-compare") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-conversion") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wuninitialized") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") -# ------------------------------------------------------------------------------ + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0") + add_definitions(-DDEBUG) -if (CMAKE_BUILD_TYPE MATCHES "Release") -elseif (CMAKE_BUILD_TYPE MATCHES "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG") -endif (CMAKE_BUILD_TYPE MATCHES "Release") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") -# ------------------------------------------------------------------------------ -# Set the assembly flags. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + add_definitions(-DRELEASE) + +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +# ============================================================================= +# Enable specific compilers for ASM. enable_language(ASM) -set(CMAKE_ASM_COMPILER ${CMAKE_SOURCE_DIR}/third_party/nasm/bin/nasm) -SET(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") -SET(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O0") -SET(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -f elf") -SET(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -F dwarf") +# Find the NASM compiler. +find_program( + ASM_COMPILER + NAMES nasm + HINTS /usr/bin/ ${CMAKE_SOURCE_DIR}/third_party/nasm/bin) +# Check that we have found the compiler. +if(NOT ASM_COMPILER) + message(FATAL_ERROR "ASM compiler not found!") +endif(NOT ASM_COMPILER) +# Set the asm compiler. +set(CMAKE_ASM_COMPILER ${ASM_COMPILER}) +# Set the assembly compiler flags. +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -f elf") set(CMAKE_ASM_COMPILE_OBJECT " -o ") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O0") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -F dwarf") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") -# ------------------------------------------------------------------------------ +# ============================================================================= # Add the includes. include_directories( - inc - inc/descriptor_tables - inc/devices - inc/drivers - inc/drivers/keyboard - inc/fs - inc/hardware - inc/io - inc/kernel - inc/libc - inc/lng - inc/mem - inc/misc - inc/sys - inc/system - inc/process - inc/elf - inc/ui - inc/ui/shell - inc/ui/command - inc/ui/init -) - -# ------------------------------------------------------------------------------ -# Add the source files. -set(SOURCE_FILES - boot.asm + inc + inc/bits + inc/descriptor_tables + inc/devices + inc/drivers + inc/drivers/keyboard + inc/fs + inc/hardware + inc/io + inc/io/vga + inc/ipc + inc/kernel + inc/klib + inc/lng + inc/mem + inc/misc + inc/sys + inc/system + inc/process + inc/elf + inc/ui + inc/ui/shell + inc/ui/command + inc/ui/init + ../libc/inc) +# ============================================================================= +# Add kernel library. +if(USE_BUDDY_SYSTEM) + add_library( + ${KERNEL_NAME} src/kernel.c src/multiboot.c - src/devices/pci.c src/devices/fpu.c - src/drivers/ata.c + src/drivers/rtc.c src/drivers/fdc.c src/drivers/mouse.c src/drivers/keyboard/keyboard.c src/drivers/keyboard/keymap.c - - src/fs/fcntl.c src/fs/initrd.c src/fs/vfs.c src/fs/read_write.c src/fs/open.c src/fs/stat.c src/fs/readdir.c - - src/sys/module.c - src/sys/unistd.c + src/fs/procfs.c + src/fs/ioctl.c + src/fs/namei.c src/hardware/timer.c src/hardware/cpuid.c src/hardware/pic8259.c src/io/port_io.c src/io/mm_io.c src/io/video.c - + src/io/stdio.c + src/io/proc_video.c + src/io/proc_running.c + src/io/proc_system.c + src/io/vga/vga.c + src/ipc/msg.c + src/ipc/sem.c + src/ipc/shm.c src/kernel/sys.c - - src/libc/assert.c - src/libc/ctype.c - src/libc/stdio.c - src/libc/string.c - src/libc/vsprintf.c - src/libc/queue.c - src/libc/stdlib.c - src/libc/spinlock.c - src/libc/list.c - src/libc/ordered_array.c - src/libc/mutex.c - src/libc/libgen.c - src/libc/strerror.c - src/libc/tree.c - src/libc/bitset.c - src/libc/fcvt.c - src/libc/rbtree.c - src/libc/math.c - src/libc/hashmap.c - - src/libc/unistd/getppid.c - src/libc/unistd/getpid.c - src/libc/unistd/exit.c - src/libc/unistd/vfork.c - src/libc/unistd/read.c - src/libc/unistd/write.c - src/libc/unistd/execve.c - src/libc/unistd/nice.c - src/libc/unistd/open.c - src/libc/unistd/reboot.c - src/libc/unistd/waitpid.c - src/libc/unistd/chdir.c - src/libc/unistd/getcwd.c - src/libc/unistd/close.c - src/libc/unistd/stat.c - src/libc/unistd/mkdir.c - + src/klib/assert.c + src/klib/ctype.c + src/klib/mutex.c + src/klib/string.c + src/klib/vsprintf.c + src/klib/vscanf.c + src/klib/time.c + src/klib/libgen.c + src/klib/strerror.c + src/klib/math.c + src/klib/fcvt.c + src/klib/spinlock.c + src/klib/rbtree.c + src/klib/ndtree.c + src/klib/hashmap.c + src/klib/list.c src/mem/kheap.c src/mem/paging.c + src/mem/slab.c + src/mem/vmem_map.c src/mem/zone_allocator.c - - src/misc/bitops.c - src/misc/clock.c + src/mem/buddysystem.c src/misc/debug.c - src/sys/dirent.c - src/sys/utsname.c src/elf/elf.c - src/descriptor_tables/gdt.c - src/descriptor_tables/gdt.asm + src/descriptor_tables/gdt.S src/descriptor_tables/interrupt.c src/descriptor_tables/exception.c - src/descriptor_tables/interrupt.asm - src/descriptor_tables/exception.asm + src/descriptor_tables/interrupt.S + src/descriptor_tables/exception.S src/descriptor_tables/idt.c - src/descriptor_tables/idt.asm + src/descriptor_tables/idt.S src/descriptor_tables/tss.c - src/descriptor_tables/tss.asm - - src/process/scheduler.c + src/descriptor_tables/tss.S src/process/scheduler_algorithm.c + src/process/scheduler.c src/process/process.c - src/process/user.asm - + src/process/wait.c + src/process/user.S + src/sys/utsname.c + src/sys/module.c src/system/errno.c src/system/panic.c - src/system/syscall.c src/system/printk.c + src/system/signal.c + src/system/syscall.c) +else(USE_BUDDY_SYSTEM) + add_library( + ${KERNEL_NAME} + src/kernel.c + src/multiboot.c + src/devices/pci.c + src/devices/fpu.c + src/drivers/ata.c + src/drivers/rtc.c + src/drivers/fdc.c + src/drivers/mouse.c + src/drivers/keyboard/keyboard.c + src/drivers/keyboard/keymap.c + src/fs/initrd.c + src/fs/vfs.c + src/fs/read_write.c + src/fs/open.c + src/fs/stat.c + src/fs/readdir.c + src/fs/procfs.c + src/fs/ioctl.c + src/fs/namei.c + src/hardware/timer.c + src/hardware/cpuid.c + src/hardware/pic8259.c + src/io/port_io.c + src/io/mm_io.c + src/io/video.c + src/io/stdio.c + src/io/proc_video.c + src/io/proc_running.c + src/io/proc_system.c + src/io/vga/vga.c + src/ipc/msg.c + src/ipc/sem.c + src/ipc/shm.c + src/kernel/sys.c + src/klib/assert.c + src/klib/ctype.c + src/klib/mutex.c + src/klib/string.c + src/klib/vsprintf.c + src/klib/vscanf.c + src/klib/time.c + src/klib/libgen.c + src/klib/strerror.c + src/klib/math.c + src/klib/fcvt.c + src/klib/spinlock.c + src/klib/rbtree.c + src/klib/ndtree.c + src/klib/hashmap.c + src/klib/list.c + src/mem/kheap.c + src/mem/paging.c + src/mem/slab.c + src/mem/vmem_map.c + src/mem/zone_allocator.c + src/misc/debug.c + src/elf/elf.c + src/descriptor_tables/gdt.c + src/descriptor_tables/gdt.S + src/descriptor_tables/interrupt.c + src/descriptor_tables/exception.c + src/descriptor_tables/interrupt.S + src/descriptor_tables/exception.S + src/descriptor_tables/idt.c + src/descriptor_tables/idt.S + src/descriptor_tables/tss.c + src/descriptor_tables/tss.S + src/process/scheduler_algorithm.c + src/process/scheduler.c + src/process/process.c + src/process/wait.c + src/process/user.S + src/sys/utsname.c + src/sys/module.c + src/system/errno.c + src/system/panic.c + src/system/printk.c + src/system/signal.c + src/system/syscall.c) + # Link the exercise libraries. + target_link_libraries(${KERNEL_NAME} ${BUDDY_SYSTEM_FILE}) +endif(USE_BUDDY_SYSTEM) +# ============================================================================= +# Add bootloader library. +add_library(${BOOTLOADER_NAME} src/boot.c boot.S) - src/ui/command/cmd_cd.c - src/ui/command/cmd_clear.c - src/ui/command/cmd_cpuid.c - src/ui/command/cmd_credits.c - src/ui/command/cmd_date.c - src/ui/command/cmd_drv_load.c - src/ui/command/cmd_echo.c - src/ui/command/cmd_logo.c - src/ui/command/cmd_ls.c - src/ui/command/cmd_mkdir.c - src/ui/command/cmd_more.c - src/ui/command/cmd_newfile.c - src/ui/command/cmd_poweroff.c - src/ui/command/cmd_ps.c - src/ui/command/cmd_pwd.c - src/ui/command/cmd_rm.c - src/ui/command/cmd_rmdir.c - src/ui/command/cmd_showpid.c - src/ui/command/cmd_sleep.c - #src/ui/command/cmd_tester.c - src/ui/command/cmd_touch.c - src/ui/command/cmd_uname.c - src/ui/command/cmd_whoami.c - src/ui/command/cmd_ipcs.c - src/ui/command/cmd_ipcrm.c - src/ui/command/cmd_nice.c - - src/ui/init/init.c - - src/ui/shell/shell.c - src/ui/shell/shell_login.c - ) - -if (ENABLE_BUDDY_SYSTEM MATCHES "ON") - set(SOURCE_FILES - ${SOURCE_FILES} - src/mem/buddysystem.c) -endif (ENABLE_BUDDY_SYSTEM MATCHES "ON") - -# ------------------------------------------------------------------------------ -# Add the executable. -add_library(${PROJECT_NAME} ${SOURCE_FILES}) - -# ------------------------------------------------------------------------------ +# ============================================================================= # Build the kernel. -add_custom_command( - TARGET MentOs - POST_BUILD - COMMAND $(LD) - -melf_i386 - -static - --oformat elf32-i386 - -Ttext 0x100000 - --output=${CMAKE_CURRENT_BINARY_DIR}/kernel.bin - --script=${CMAKE_CURRENT_SOURCE_DIR}/kernel.lds - ${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.a - -Map ${CMAKE_CURRENT_BINARY_DIR}/kernel.map -) +add_custom_target( + kernel-bootloader.bin ALL + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/../debug_sym + COMMAND ${CMAKE_LINKER} -melf_i386 -static --oformat elf32-i386 --output kernel.bin --script ${CMAKE_CURRENT_SOURCE_DIR}/kernel.lds -Map kernel.map -u kmain + $ ${BUDDY_SYSTEM_FILE} + COMMAND objcopy -I binary -O elf32-i386 -B i386 kernel.bin kernel.bin.o + COMMAND ${CMAKE_LINKER} -melf_i386 -static --oformat elf32-i386 --output kernel-bootloader.bin --script ${CMAKE_CURRENT_SOURCE_DIR}/boot.lds -Map kernel-bootloader.map + kernel.bin.o $ + DEPENDS ${KERNEL_NAME} + DEPENDS ${BOOTLOADER_NAME}) diff --git a/mentos/boot.asm b/mentos/boot.S similarity index 76% rename from mentos/boot.asm rename to mentos/boot.S index 0c26b1f..fffbe63 100644 --- a/mentos/boot.asm +++ b/mentos/boot.S @@ -1,11 +1,11 @@ ; MentOS, The Mentoring Operating system project ; @file boot.asm ; @brief Kernel start location, multiboot header -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. -[BITS 32] ; All instructions should be 32-bit. -[EXTERN kmain] ; The start point of our C code +bits 32 ; All instructions should be 32-bit. +extern boot_main ; The start point of our C code ; The magic field should contain this. MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 @@ -49,24 +49,19 @@ multiboot_header: ; ----------------------------------------------------------------------------- ; SECTION (data) ; ----------------------------------------------------------------------------- -section .data, nobits -align 4096 -[GLOBAL boot_page_dir] -boot_page_dir: - resb 0x1000 -boot_page_tabl: - resb 0x1000 +; section .data nobits +; align 4096 ; ----------------------------------------------------------------------------- ; SECTION (text) ; ----------------------------------------------------------------------------- section .text -[GLOBAL kernel_entry] -kernel_entry: +global boot_entry +boot_entry: ; Clear interrupt flag [IF = 0]; 0xFA cli - ; To set up a stack, we simply set the esp register to point to the top of - ; our stack (as it grows downwards). + ; To set up a stack, we simply set the esp register to point to the top of + ; our stack (as it grows downwards). mov esp, stack_top ; pass the initial ESP push esp @@ -74,24 +69,35 @@ kernel_entry: push ebx ; pass Multiboot magic number push eax - ; Call the kmain() function inside kernel.c - call kmain + ; Call the boot_main() function inside boot.c + call boot_main ; Set interrupt flag [IF = 1]; 0xFA - ; Clear interrupts and hang if we return from kmain + ; Clear interrupts and hang if we return from boot_main cli hang: hlt jmp hang +global boot_kernel +boot_kernel: + mov edx, [esp + 4] ; stack_pointer + mov ebx, [esp + 8] ; entry + mov eax, [esp + 12] ; boot info + mov esp, edx ; set stack pointer + push eax ; push the boot info + call ebx ; call the kernel main + ; ----------------------------------------------------------------------------- ; SECTION (bss) ; ----------------------------------------------------------------------------- section .bss -[GLOBAL stack_bottom] -[GLOBAL stack_top] align 16 + +global stack_bottom stack_bottom: resb KERNEL_STACK_SIZE + +global stack_top stack_top: ; the top of the stack is the bottom because the stack counts down diff --git a/mentos/boot.lds b/mentos/boot.lds new file mode 100644 index 0000000..c99c023 --- /dev/null +++ b/mentos/boot.lds @@ -0,0 +1,59 @@ +OUTPUT_FORMAT("elf") +OUTPUT_ARCH(i386) + +ENTRY(boot_entry) + +BOOTLOADER_PHYSICAL_ADDRESS = 0x00100000; + +MEMORY { + BOOTLOADER_MEM : ORIGIN = 0x00000000, LENGTH = 128M +} + +SECTIONS +{ + . = BOOTLOADER_PHYSICAL_ADDRESS; + _bootloader_start = .; + .multiboot . : AT(ADDR(.multiboot)) + { + . = ALIGN(4); + _multiboot_header_start = .; + *(.multiboot_header) + _multiboot_header_end = .; + } > BOOTLOADER_MEM + + /* Put the .text section. */ + .text . : AT(ADDR(.text)) + { + _text_start = .; + *(.text) + _text_end = .; + } > BOOTLOADER_MEM + + /* Read-only data. */ + .rodata ALIGN(4K) : AT(ADDR(.rodata)) + { + _rodata_start = .; + *(.rodata) + _rodata_end = .; + } > BOOTLOADER_MEM + + /* Read-write data (initialized) */ + .data ALIGN(4K) : AT(ADDR(.data)) + { + _data_start = .; + *(.data) + _data_end = .; + } > BOOTLOADER_MEM + + /* Read-write data (uninitialized) and stack */ + .bss ALIGN(4K) : AT(ADDR(.bss)) + { + _bss_start = .; + *(.bss*) + _bss_end = .; + } > BOOTLOADER_MEM + + /* Put a symbol end here, it tells us where all the kernel code/data ends, + it means everything after 'end' can be used for something else. */ + _bootloader_end = .; +} diff --git a/mentos/inc/bits/ioctls.h b/mentos/inc/bits/ioctls.h new file mode 100644 index 0000000..c872a26 --- /dev/null +++ b/mentos/inc/bits/ioctls.h @@ -0,0 +1,9 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctls.h +/// @brief Definitions of tty ioctl numbers. 0x54 is just a magic number to make these +/// relatively unique ('T') + +#pragma once + +#define TCGETS 0x5401U ///< Get the current serial port settings. +#define TCSETS 0x5402U ///< Set the current serial port settings. diff --git a/mentos/inc/bits/termios-struct.h b/mentos/inc/bits/termios-struct.h new file mode 100644 index 0000000..a54adbc --- /dev/null +++ b/mentos/inc/bits/termios-struct.h @@ -0,0 +1,45 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios-struct.h +/// @brief +/// Copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Type for control characters. +typedef unsigned char cc_t; +/// Type for speed. +typedef unsigned int speed_t; +/// Type for flags. +typedef unsigned int tcflag_t; +/// The number of control characters. +#define NCCS 32 + +/// @brief Stores information about a terminal IOs. +typedef struct termios { + tcflag_t c_iflag; ///< input mode flags + tcflag_t c_oflag; ///< output mode flags + tcflag_t c_cflag; ///< control mode flags + tcflag_t c_lflag; ///< local mode flags + cc_t c_line; ///< line discipline + cc_t c_cc[NCCS]; ///< control characters + speed_t c_ispeed; ///< input speed + speed_t c_ospeed; ///< output speed +} termios; + +/// @brief These flags generally control higher-level aspects of input processing than the input +/// modes flags described in Input Modes, such as echoing, signals, and the choice of canonical +/// or noncanonical input. +enum { + ISIG = 000001U, ///< Controls whether the INTR, QUIT, and SUSP characters are recognized. + ICANON = 000002U, ///< Enables canonical input processing mode. + ECHO = 000010U, ///< Echo input characters. + ECHOE = 000020U, ///< If ICANON is set, the ERASE character erases the preceding character. + ECHOK = 000040U, ///< If ICANON is set, the KILL character erases the current line. + ECHONL = 000100U, ///< If ICANON is set, echo the NL character even if ECHO is not set. + NOFLSH = 000200U, ///< Do not clear in/out queues when receiving INTR, QUIT, and SUSP. + TOSTOP = 000400U, ///< Allows SIGTTOU signals generated by background processes. + ECHOCTL = 001000U, ///< If this and ECHO are set, control characters with ‘^’ are echoed. + ECHOKE = 004000U, ///< If ICANON is set, KILL is echoed by erasing each character on the line. + IEXTEN = 100000U, ///< Enables implementation-defined input processing. +}; \ No newline at end of file diff --git a/mentos/inc/boot.h b/mentos/inc/boot.h new file mode 100644 index 0000000..cce5be3 --- /dev/null +++ b/mentos/inc/boot.h @@ -0,0 +1,85 @@ +/// MentOS, The Mentoring Operating system project +/// @file boot.h +/// @brief Bootloader structures +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#ifndef MENTOS_BOOT_H +#define MENTOS_BOOT_H + +#include "multiboot.h" + +/// @brief Mentos structure to communicate bootloader info to the kernel +typedef struct boot_info_t { + /// Boot magic number. + uint32_t magic; + + /* + * Bootloader physical range + * it can be overwritten to do other things + * */ + + /// bootloader code start + uint32_t bootloader_phy_start; + /// bootloader code end + uint32_t bootloader_phy_end; + + /* + * Kernel virtual and physical range + * must not be overwritten + * it's assumed to be contiguous, so section holes are just wasted space + * */ + + /// kernel physical code start + uint32_t kernel_phy_start; + /// kernel physical code end + uint32_t kernel_phy_end; + + /// kernel code start + uint32_t kernel_start; + /// kernel code end + uint32_t kernel_end; + + /// kernel size. + uint32_t kernel_size; + + /// Address after the modules. + uint32_t module_end; + + /* + * Range of addressable lowmemory, is memory that is available + * (not used by the kernel executable nor by the bootloader) + * and can be accessed by the kernel at any time + * */ + + /// lowmem physical addressable start + uint32_t lowmem_phy_start; + /// lowmem physical addressable end + uint32_t lowmem_phy_end; + + /// lowmem addressable start + uint32_t lowmem_start; + /// lowmem addressable end + uint32_t lowmem_end; + + /// stack end (comes after lowmem_end, and is the end of the low mapped memory) + uint32_t stack_end; + + /* + * Range of non-addressable highmemory, is memory that can be + * accessed by the kernel only if previously mapped + * */ + + /// highmem addressable start + uint32_t highmem_phy_start; + /// highmem addressable end + uint32_t highmem_phy_end; + + /// multiboot info + multiboot_info_t *multiboot_header; + + /// stack suggested start address (also set by the bootloader) + uint32_t stack_base; +} boot_info_t; + +#endif //MENTOS_BOOT_H diff --git a/mentos/inc/descriptor_tables/gdt.h b/mentos/inc/descriptor_tables/gdt.h index a7728e4..9cb385d 100644 --- a/mentos/inc/descriptor_tables/gdt.h +++ b/mentos/inc/descriptor_tables/gdt.h @@ -1,40 +1,122 @@ /// MentOS, The Mentoring Operating system project -/// @file gdt.h +/// @file gdt.h /// @brief Data structures concerning the Global Descriptor Table (GDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" -/// @brief Access flags, determines what ring this segment can be used in. -typedef enum gdt_access_option_t -{ - /// Identifies a kernel segment. - KERNEL = 0x00, - /// Identifies a user segment. - USER = 0x03, - /// Identifies a code segment. - CODE = 0x10, - /// Identifies a data segment. - DATA = 0x10, - /// Segment is present. - PRESENT = 0x80, -} __attribute__ ((__packed__)) gdt_access_option_t; +/// @defgroup gdt_bits List of GDT Bits. +/// @brief Bitmasks used to access to specific bits of the GDT. +/// @details +/// @image html gdt_bits.png +/// #### PR (Present bit) +/// This must be 1 for all valid selectors. +/// +/// #### PRIV (Privilege bits) +/// Contains the ring level, specifically: +/// - `00 (0)` = highest (kernel), +/// - `11 (3)` = lowest (user applications). +/// +/// #### S (Descriptor type) +/// This bit should be set for code or data segments and should be cleared +/// for system segments (eg. a Task State Segment). +/// +/// #### EX (Executable bit) +/// - If 1 code in this segment can be executed, ie. a code selector. +/// - If 0 it is a data selector. +/// +/// #### DC (Direction bit/Conforming bit) +/// Direction bit for data selectors: Tells the direction. 0 the segment grows up. +/// 1 the segment grows down, ie., the offset has to be greater than the limit. +/// +/// Conforming bit for code selectors: +/// - If 1 code in this segment can be executed from an equal or lower privilege +/// level. For example, code in ring 3 can far-jump to conforming code in a ring 2 segment. +/// The privl-bits represent the highest privilege level that is allowed to execute +/// the segment. For example, code in ring 0 cannot far-jump to a conforming code segment +/// with privl==0x2, while code in ring 2 and 3 can. Note that the privilege level remains +/// the same, ie. a far-jump form ring 3 to a privl==2-segment remains in ring 3 +/// after the jump. +/// - If 0 code in this segment can only be executed from the ring set in privl. +/// +/// #### RW (Readable bit/Writable bit) +/// - Readable bit for code selectors: +/// - Whether read access for this segment is allowed. +/// - Write access is never allowed for code segments. +/// - Writable bit for data selectors: +/// - Whether write access for this segment is allowed. +/// - Read access is always allowed for data segments. +/// +/// #### AC (Accessed bit) +/// Just set to 0. The CPU sets this to 1 when the segment is accessed. +/// +/// #### GR (Granularity bit) +/// - If 0 the limit is in 1 B blocks (byte granularity); +/// - if 1 the limit is in 4 KiB blocks (page granularity). +/// +/// #### SZ (Size bit) +/// - If 0 the selector defines 16 bit protected mode; +/// - if 1 it defines 32 bit protected mode. +/// +/// You can have both 16 bit and 32 bit selectors at once. +/// +/// @{ -/// @brief Options for the second option. -typedef enum gdt_granularity_option_t -{ - /// Granularity. - GRANULARITY = 0x80, - /// Szbits. - SZBITS = 0x40 -} __attribute__ ((__packed__)) gdt_granularity_option_t; +/// @brief Present bit. +/// This must be 1 for all valid selectors. +#define GDT_PRESENT 128U // 0b10000000U + +/// @brief Sets the 2 privilege bits (ring level) to 0 = highest (kernel). +#define GDT_KERNEL 0U // 0b00000000U + +/// @brief Sets the 2 privilege bits (ring level) to 3 = lowest (user applications). +#define GDT_USER 96U // 0b01100000U + +/// @brief Descriptor type. +/// This bit should be set for code or data segments and should be cleared for system segments (eg. a Task State Segment) +#define GDT_S 16U // 0b00010000U + +/// @brief Executable bit. +/// If 1 code in this segment can be executed, ie. a code selector. If 0 it is a data selector. +#define GDT_EX 8U // 0b00001000U + +/// @brief Direction bit/Conforming bit. +#define GDT_DC 4U // 0b00000100U + +/// @brief Readable bit/Writable bit. +#define GDT_RW 2U // 0b00000010U + +/// @brief Accessed bit. +/// Just set to 0. The CPU sets this to 1 when the segment is accessed. +#define GDT_AC 1U // 0b00000001U + +/// @brief Identifies an executable code segment. +#define GDT_CODE (GDT_S | GDT_EX) + +/// @brief Identifies a writable data segment. +#define GDT_DATA (GDT_S | GDT_RW) + +/// @brief Granularity bit. +/// If 0 the limit is in 1 B blocks (byte granularity), +/// if 1 the limit is in 4 KiB blocks (page granularity). +#define GDT_GRANULARITY 128U // 0b10000000U + +/// @brief Size bit. +/// If 0 the selector defines 16 bit protected mode. +/// If 1 it defines 32 bit protected mode. +/// You can have both 16 bit and 32 bit selectors at once. +#define GDT_OPERAND_SIZE 64U // 0b01000000U + +/// @} + +/// @brief Used in IDT for padding. +#define IDT_PADDING 14U // 0b00001110U /// @brief Data structure representing a GDT descriptor. -typedef struct gdt_descriptor_t -{ +typedef struct gdt_descriptor_t { /// The lower 16 bits of the limit. uint16_t limit_low; /// The lower 16 bits of the base. @@ -50,8 +132,7 @@ typedef struct gdt_descriptor_t } __attribute__((packed)) gdt_descriptor_t; /// @brief Data structure used to load the GDT into the GDTR. -typedef struct gdt_pointer_t -{ +typedef struct gdt_pointer_t { /// The size of the GDT (entry number). uint16_t limit; /// The starting address of the GDT. @@ -70,10 +151,6 @@ void init_gdt(); /// @param index The index inside the GDT. /// @param base Memory address where the segment we are defining starts. /// @param limit The memory address which determines the end of the segnment. -/// @param _access Type (4bit) - S (1) bit -DPL (2 bit) - P(1 bit). -/// @param _granul SegLimit_hi(4 bit) AVL(1 bit) L(1 bit) D/B(1 bit) G(1bit). -void gdt_set_gate(uint8_t index, - uint32_t base, - uint32_t limit, - uint8_t _access, - uint8_t _granul); +/// @param access Type (4bit) - S (1) bit -DPL (2 bit) - P(1 bit). +/// @param granul SegLimit_hi(4 bit) AVL(1 bit) L(1 bit) D/B(1 bit) G(1bit). +void gdt_set_gate(uint8_t index, uint32_t base, uint32_t limit, uint8_t access, uint8_t granul); diff --git a/mentos/inc/descriptor_tables/idt.h b/mentos/inc/descriptor_tables/idt.h index ddb388b..18185d4 100644 --- a/mentos/inc/descriptor_tables/idt.h +++ b/mentos/inc/descriptor_tables/idt.h @@ -1,30 +1,23 @@ /// MentOS, The Mentoring Operating system project /// @file idt.h /// @brief Data structures concerning the Interrupt Descriptor Table (IDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "isr.h" #include "stdint.h" -#include /// The maximum dimension of the IDT. #define IDT_SIZE 256 - /// When an exception occurs whose entry is a Task Gate, a task switch results. #define TASK_GATE 0x5 - /// Used to specify an interrupt service routine (16-bit). #define INT16_GATE 0x6 - /// @brief Similar to an Interrupt gate (16-bit). #define TRAP16_GATE 0x7 - /// Used to specify an interrupt service routine (32-bit). #define INT32_GATE 0xE - /// @brief Similar to an Interrupt gate (32-bit). #define TRAP32_GATE 0xF @@ -37,36 +30,34 @@ /// @brief This structure describes one interrupt gate. typedef struct idt_descriptor_t { - unsigned short offset_low; - unsigned short seg_selector; - // This will ALWAYS be set to 0. - unsigned char null_par; - // |P|DPL|01110|. - // P present, DPL required Ring (2bits). - unsigned char options; - unsigned short offset_high; + /// TODO: Comment. + unsigned short offset_low; + /// Segment selector. + unsigned short seg_selector; + /// This will ALWAYS be set to 0. + unsigned char null_par; + /// @brief IDT descriptor options: + /// |P|DPL|01110|. + /// P present, DPL required Ring (2bits). + unsigned char options; + /// TODO: Comment. + unsigned short offset_high; } __attribute__((packed)) idt_descriptor_t; /// @brief A pointer structure used for informing the CPU about our IDT. typedef struct idt_pointer_t { - /// The size of the IDT (entry number). - unsigned short int limit; - /// The start address of the IDT. - unsigned int base; + /// The size of the IDT (entry number). + unsigned short int limit; + /// The start address of the IDT. + unsigned int base; } __attribute__((packed)) idt_pointer_t; /// @brief Initialise the interrupt descriptor table. void init_idt(); -/// @brief Use this function to set an entry in the IDT. -/// @param index Indice della IDT. -/// @param handler Puntatore alla funzione che gestira' l'interrupt/Eccezione -/// @param options Le opzioni del descrittore (PRESENT,NOTPRESENT,KERNEL,USER) -/// @param seg_sel Il selettore del segmento della GDT. -void idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, - uint8_t seg_sel); +// == List of exceptions generated internally by the CPU ====================== +//! @cond Doxygen_Suppress -//==== List of exceptions generated internally by the CPU ====================== extern void INT_0(); extern void INT_1(); @@ -132,9 +123,8 @@ extern void INT_30(); extern void INT_31(); extern void INT_80(); -//============================================================================== -//==== List of interrupts generated by PIC ===================================== +// == List of interrupts generated by PIC ===================================== extern void IRQ_0(); extern void IRQ_1(); @@ -166,4 +156,6 @@ extern void IRQ_13(); extern void IRQ_14(); extern void IRQ_15(); -//============================================================================== + +//! @endcond +// ============================================================================ diff --git a/mentos/inc/descriptor_tables/isr.h b/mentos/inc/descriptor_tables/isr.h index c8c5217..9c36db0 100644 --- a/mentos/inc/descriptor_tables/isr.h +++ b/mentos/inc/descriptor_tables/isr.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file isr.h /// @brief Data structures concerning the Interrupt Service Routines (ISRs). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -19,9 +19,9 @@ typedef void (*interrupt_handler_t)(pt_regs *f); /// prints the rose exceptions and stops kernel execution. void isrs_init(); -/// @brief For each interrupt irqs_init sets a default handler which +/// @brief For each interrupt irq_init sets a default handler which /// prints the rose IRQ line and stops kernel execution. -void irqs_init(); +void irq_init(); /* Even if an interrupt service routine is called for exceptions and * interrupts, we use two distinct methods to logically setup an ISR to @@ -31,99 +31,52 @@ void irqs_init(); * for a same IRQ. */ -/// @brief Installs an ISR to handle an exception. -/// @param i Exception identifier. -/// @param handler Exception handler. -/// @return 0 On success, -1 otherwise. -int isr_install_handler(uint32_t i, interrupt_handler_t handler, - char *description); +/// @brief Installs an ISR to handle an exception. +/// @param i Exception identifier. +/// @param handler Exception handler. +/// @param description Exception description. +/// @return 0 on success, -1 otherwise. +int isr_install_handler(uint32_t i, interrupt_handler_t handler, char *description); -/// @brief Installs an ISR to handle an interrupt. -/// @param i Interrupt identifier. -/// @param handler Interrupt handler. -/// @return 0 On success, -1 otherwise. -int irq_install_handler(uint32_t i, interrupt_handler_t handler, - char *description); +/// @brief Installs an ISR to handle an interrupt. +/// @param i Interrupt identifier. +/// @param handler Interrupt handler. +/// @param description Interrupt description. +/// @return 0 on success, -1 otherwise. +int irq_install_handler(unsigned i, interrupt_handler_t handler, char *description); -/// @brief Method called by CPU to handle interrupts. -/// @param f The interrupt stack frame. +/// @brief Method called by CPU to handle interrupts. +/// @param f The interrupt stack frame. extern void irq_handler(pt_regs *f); -/// @brief Method called by CPU to handle exceptions. -/// @param f The interrupt stack frame. +/// @brief Method called by CPU to handle exceptions. +/// @param f The interrupt stack frame. extern void isq_handler(pt_regs *f); //==== List of exceptions generated internally by the CPU ====================== -/// @brief DE Divide Error. -#define DIVIDE_ERROR 0 - -/// @brief DB Debug. -#define DEBUG_EXC 1 - -/// @brief Non Mascable Interrupt. -#define NMI_INTERRUPT 2 - -/// @brief BP Breakpoint. -#define BREAKPOINT 3 - -/// @brief OF Overflow. -#define OVERFLOW 4 - -/// @brief BR Bound Range Exception. -#define BOUND_RANGE_EXCEED 5 - -/// @brief UD Invalid OpCode Exception. -#define INVALID_OPCODE 6 - -/// @brief NM Device Not Available. -#define DEV_NOT_AVL 7 - -/// @brief DF Double Fault. -#define DOUBLE_FAULT 8 - -/// @brief Coprocessor Segment Overrun. -#define COPROC_SEG_OVERRUN 9 - -/// @brief TS Invalid TSS. -#define INVALID_TSS 10 - -/// @brief NP Segment Not Present. -#define SEGMENT_NOT_PRESENT 11 - -/// @brief SS Stack Segment Fault. -#define STACK_SEGMENT_FAULT 12 - -/// @brief GP General Protection. -#define GENERAL_PROTECTION 13 - -/// @brief PF Page Fault. -#define PAGE_FAULT 14 - -/// @brief XX Reserverd. -#define INT_RSV 15 - -/// @brief MF Floating Point. -#define FLOATING_POINT_ERR 16 - -/// @brief AC Alignment Check. -#define ALIGNMENT_CHECK 17 - -/// @brief MC Machine Check. -#define MACHINE_CHECK 18 - -/// @brief XF Streaming SIMD Exception. -#define SIMD_FP_EXC 19 - -/// @brief Virtualization Exception. -#define VIRT_EXC 20 - -/// @brief Reserved [21-29]. -/// @brief Security Exception. -#define SECURITY_EXC 30 - -/// @brief Triple Fault -#define TRIPLE_FAULT 31 - -/// @brief System call interrupt. -#define SYSTEM_CALL 80 +#define DIVIDE_ERROR 0 ///< DE Divide Error. +#define DEBUG_EXC 1 ///< DB Debug. +#define NMI_INTERRUPT 2 ///< Non Mascable Interrupt. +#define BREAKPOINT 3 ///< BP Breakpoint. +#define OVERFLOW 4 ///< OF Overflow. +#define BOUND_RANGE_EXCEED 5 ///< BR Bound Range Exception. +#define INVALID_OPCODE 6 ///< UD Invalid OpCode Exception. +#define DEV_NOT_AVL 7 ///< NM Device Not Available. +#define DOUBLE_FAULT 8 ///< DF Double Fault. +#define COPROC_SEG_OVERRUN 9 ///< Coprocessor Segment Overrun. +#define INVALID_TSS 10 ///< TS Invalid TSS. +#define SEGMENT_NOT_PRESENT 11 ///< NP Segment Not Present. +#define STACK_SEGMENT_FAULT 12 ///< SS Stack Segment Fault. +#define GENERAL_PROTECTION 13 ///< GP General Protection. +#define PAGE_FAULT 14 ///< PF Page Fault. +#define INT_RSV 15 ///< XX Reserverd. +#define FLOATING_POINT_ERR 16 ///< MF Floating Point. +#define ALIGNMENT_CHECK 17 ///< AC Alignment Check. +#define MACHINE_CHECK 18 ///< MC Machine Check. +#define SIMD_FP_EXC 19 ///< XF Streaming SIMD Exception. +#define VIRT_EXC 20 ///< Virtualization Exception. +// Reserved [21-29]. +#define SECURITY_EXC 30 ///< Security Exception. +#define TRIPLE_FAULT 31 ///< Triple Fault +#define SYSTEM_CALL 80 ///< System call interrupt. //============================================================================== diff --git a/mentos/inc/descriptor_tables/tss.h b/mentos/inc/descriptor_tables/tss.h index ea5acdb..cb265ff 100644 --- a/mentos/inc/descriptor_tables/tss.h +++ b/mentos/inc/descriptor_tables/tss.h @@ -1,61 +1,55 @@ /// MentOS, The Mentoring Operating system project /// @file tss.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "gdt.h" -#include "stdio.h" -#include "kernel.h" +#include "stdint.h" -// TODO: doxygen comment. +/// @brief Task state segment entry. typedef struct tss_entry { - uint32_t prevTss; - uint32_t esp0; - uint32_t ss0; - uint32_t esp1; - uint32_t ss1; - uint32_t esp2; - uint32_t ss2; - uint32_t cr3; - uint32_t eip; - uint32_t eflags; - uint32_t eax; - uint32_t ecx; - uint32_t edx; - uint32_t ebx; - uint32_t esp; - uint32_t ebp; - uint32_t esi; - uint32_t edi; - uint32_t es; - uint32_t cs; - uint32_t ss; - uint32_t ds; - uint32_t fs; - uint32_t gs; - uint32_t ldt; - uint16_t trap; - uint16_t iomap; + uint32_t prev_tss; ///< If we used hardware task switching this would form a linked list. + uint32_t esp0; ///< The stack pointer to load when we change to kernel mode. + uint32_t ss0; ///< The stack segment to load when we change to kernel mode. + uint32_t esp1; ///< everything below here is unusued now. + uint32_t ss1; ///< TODO: Comment. + uint32_t esp2; ///< TODO: Comment. + uint32_t ss2; ///< TODO: Comment. + uint32_t cr3; ///< TODO: Comment. + uint32_t eip; ///< TODO: Comment. + uint32_t eflags; ///< TODO: Comment. + uint32_t eax; ///< TODO: Comment. + uint32_t ecx; ///< TODO: Comment. + uint32_t edx; ///< TODO: Comment. + uint32_t ebx; ///< TODO: Comment. + uint32_t esp; ///< TODO: Comment. + uint32_t ebp; ///< TODO: Comment. + uint32_t esi; ///< TODO: Comment. + uint32_t edi; ///< TODO: Comment. + uint32_t es; ///< TODO: Comment. + uint32_t cs; ///< TODO: Comment. + uint32_t ss; ///< TODO: Comment. + uint32_t ds; ///< TODO: Comment. + uint32_t fs; ///< TODO: Comment. + uint32_t gs; ///< TODO: Comment. + uint32_t ldt; ///< TODO: Comment. + uint16_t trap; ///< TODO: Comment. + uint16_t iomap; ///< TODO: Comment. } tss_entry_t; +/// @brief Flushes the Task State Segment. extern void tss_flush(); /// @brief We don't need tss to assist task switching, but it's required to /// have one tss for switching back to kernel mode(system call for /// example). -/// @param idx -/// @param kss -/// @param kesp -void tss_init(uint8_t idx, uint32_t kss, uint32_t kesp); +/// @param idx Index. +/// @param ss0 Kernel data segment. +void tss_init(uint8_t idx, uint32_t ss0); -// esp the kernel should be using. -/// @brief This function is used to set the tss's esp, so that CPU knows what. -/// @param kss -/// @param kesp +/// @brief This function is used to set the esp the kernel should be using. +/// @param kss Kernel data segment. +/// @param kesp Kernel stack address. void tss_set_stack(uint32_t kss, uint32_t kesp); - -// TODO: doxygen comment. -void print_tss(); diff --git a/mentos/inc/devices/fpu.h b/mentos/inc/devices/fpu.h index 5edd9c5..06bee5e 100644 --- a/mentos/inc/devices/fpu.h +++ b/mentos/inc/devices/fpu.h @@ -1,21 +1,126 @@ /// MentOS, The Mentoring Operating system project /// @file fpu.h /// @brief Floating Point Unit (FPU). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "stdint.h" -#include "stdbool.h" +#pragma once -// TODO: doxygen comment. -/// @brief +#include "stdint.h" + +/// @brief Environment information of floating point unit. +typedef struct env87 { + /// Control word (16bits). + long en_cw; + /// Status word (16bits). + long en_sw; + /// Tag word (16bits). + long en_tw; + /// Floating point instruction pointer. + long en_fip; + /// Floating code segment selector. + unsigned short en_fcs; + /// Opcode last executed (11 bits). + unsigned short en_opcode; + /// Floating operand offset. + long en_foo; + /// Floating operand segment selector. + long en_fos; +} env87; + +/// @brief Contents of each floating point accumulator. +typedef struct fpacc87 { + /// Easy to access bytes. + unsigned char fp_bytes[10]; +} fpacc87; + +/// @brief Floating point context. +typedef struct save87 { + /// Floating point control/status. + env87 sv_env; + /// Accumulator contents, 0-7. + fpacc87 sv_ac[8]; + /// Padding for (now unused) saved status word. + unsigned char sv_pad0[4]; + /* + * Bogus padding for emulators. Emulators should use their own + * struct and arrange to store into this struct (ending here) + * before it is inspected for ptracing or for core dumps. Some + * emulators overwrite the whole struct. We have no good way of + * knowing how much padding to leave. Leave just enough for the + * GPL emulator's i387_union (176 bytes total). + */ + /// Padding used by emulators. + unsigned char sv_pad[64]; +} save87; + +/// @brief Stores the XMM environment. +typedef struct envxmm { + /// Control word (16bits). + uint16_t en_cw; + /// Status word (16bits). + uint16_t en_sw; + /// Tag word (16bits). + uint16_t en_tw; + /// Opcode last executed (11 bits). + uint16_t en_opcode; + /// Floating point instruction pointer. + uint32_t en_fip; + /// Floating code segment selector. + uint16_t en_fcs; + /// Padding. + uint16_t en_pad0; + /// Floating operand offset. + uint32_t en_foo; + /// Floating operand segment selector. + uint16_t en_fos; + /// Padding. + uint16_t en_pad1; + /// SSE sontorol/status register. + uint32_t en_mxcsr; + /// Valid bits in mxcsr. + uint32_t en_mxcsr_mask; +} envxmm; + +/// @brief Contents of each SSE extended accumulator. +typedef struct xmmacc { + /// TODO: Comment. + unsigned char xmm_bytes[16]; +} xmmacc; + +/// @brief Stores the XMM context. +typedef struct savexmm { + /// TODO: Comment. + envxmm sv_env; + /// TODO: Comment. + struct { + /// TODO: Comment. + fpacc87 fp_acc; + /// Padding. + unsigned char fp_pad[6]; + } sv_fp[8]; + /// TODO: Comment. + xmmacc sv_xmm[8]; + /// Padding. + unsigned char sv_pad[224]; +} __attribute__((__aligned__(16))) savexmm; + +/// @brief Stores FPU registers details. +typedef union savefpu { + /// Stores the floating point context. + save87 sv_87; + /// Stores the XMM context. + savexmm sv_xmm; +} savefpu; + +/// @brief Called during a context switch to save the FPU registers status +/// of the currently running thread. void switch_fpu(); -// TODO: doxygen comment. -/// @brief +/// @brief Called during a context switch to load the FPU registers status +/// of the currently running thread inside the FPU. void unswitch_fpu(); -// TODO: doxygen comment. -/// @brief -/// @return -bool_t fpu_install(); +/// @brief Enable the FPU context handling. +/// @return 0 if fails, 1 if succeed. +int fpu_install(); diff --git a/mentos/inc/devices/pci.h b/mentos/inc/devices/pci.h index f517fd4..404cfe1 100644 --- a/mentos/inc/devices/pci.h +++ b/mentos/inc/devices/pci.h @@ -1,8 +1,9 @@ /// MentOS, The Mentoring Operating system project /// @file pci.h /// @brief Routines for PCI initialization. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #pragma once @@ -82,116 +83,95 @@ /// register, and reading it back. Only 1 bits are decoded. /// @{ -#define PCI_BASE_ADDRESS_0 0x10 - -#define PCI_BASE_ADDRESS_1 0x14 - -#define PCI_BASE_ADDRESS_2 0x18 - -#define PCI_BASE_ADDRESS_3 0x1c - -#define PCI_BASE_ADDRESS_4 0x20 - -#define PCI_BASE_ADDRESS_5 0x24 +#define PCI_BASE_ADDRESS_0 0x10 ///< Location of base address 0. +#define PCI_BASE_ADDRESS_1 0x14 ///< Location of base address 1. +#define PCI_BASE_ADDRESS_2 0x18 ///< Location of base address 2. +#define PCI_BASE_ADDRESS_3 0x1c ///< Location of base address 3. +#define PCI_BASE_ADDRESS_4 0x20 ///< Location of base address 4. +#define PCI_BASE_ADDRESS_5 0x24 ///< Location of base address 5. /// @} pci_base_addresses /// Points to the Card Information Structure and is used by devices that /// share silicon between CardBus and PCI. #define PCI_CARDBUS_CIS 0x28 - +/// Points to the Subsystem Vendor ID #define PCI_SUBSYSTEM_VENDOR_ID 0x2c - +/// Points to the Subsystem Device ID #define PCI_SUBSYSTEM_ID 0x2e - +/// Bits 31..11 are address, 10..1 reserved #define PCI_ROM_ADDRESS 0x30 - /// Points to a linked list of new capabilities implemented by the device. /// Used if bit 4 of the status register (Capabilities List bit) is set to 1. /// The bottom two bits are reserved and should be masked before the Pointer /// is used to access the Configuration Space. #define PCI_CAPABILITY_LIST 0x34 - /// Specifies which input of the system interrupt controllers the device's /// interrupt pin is connected to and is implemented by any device that makes /// use of an interrupt pin. For the x86 architecture this register /// corresponds to the PIC IRQ numbers 0-15 (and not I/O APIC IRQ numbers) and /// a value of 0xFF defines no connection. #define PCI_INTERRUPT_LINE 0x3c - /// Specifies which interrupt pin the device uses. Where a value of 0x01 is /// INTA#, 0x02 is INTB#, 0x03 is INTC#, 0x04 is INTD#, and 0x00 means the /// device does not use an interrupt pin. #define PCI_INTERRUPT_PIN 0x3d - /// A read-only register that specifies the burst period length, in 1/4 /// microsecond units, that the device needs (assuming a 33 MHz clock rate). #define PCI_MIN_GNT 0x3e - /// A read-only register that specifies how often the device needs access to /// the PCI bus (in 1/4 microsecond units). #define PCI_MAX_LAT 0x3f /// @} pci_configuration_space -#define PCI_SECONDARY_BUS 0x19 +#define PCI_PRIMARY_BUS 0x18 ///< Primary bus number. +#define PCI_SECONDARY_BUS 0x19 ///< Secondary bus number. -#define PCI_HEADER_TYPE_DEVICE 0 +#define PCI_HEADER_TYPE_NORMAL 0 ///< TODO: Document. +#define PCI_HEADER_TYPE_BRIDGE 1 ///< TODO: Document. +#define PCI_HEADER_TYPE_CARDBUS 2 ///< TODO: Document. -#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_TYPE_BRIDGE 0x060400 ///< TODO: Document. +#define PCI_TYPE_SATA 0x010600 ///< TODO: Document. -#define PCI_HEADER_TYPE_CARDBUS 2 +#define PCI_ADDRESS_PORT 0xCF8 ///< TODO: Document. +#define PCI_VALUE_PORT 0xCFC ///< TODO: Document. +#define PCI_NONE 0xFFFF ///< TODO: Document. -#define PCI_TYPE_BRIDGE 0x060400 +/// @brief PIC scan function. +typedef void (*pci_scan_func_t)(uint32_t device, uint16_t vendor_id, uint16_t device_id, void *extra); -#define PCI_TYPE_SATA 0x010600 - -#define PCI_ADDRESS_PORT 0xCF8 - -#define PCI_VALUE_PORT 0xCFC - -#define PCI_NONE 0xFFFF - -// TODO: doxygen comment. -/// @brief -typedef void (*pci_func_t)(uint32_t device, uint16_t vendor_id, - uint16_t device_id, void *extra); - -// TODO: doxygen comment. -/// @brief +/// @brief Extract the `bus` from the device. static inline int pci_extract_bus(uint32_t device) { - return (uint8_t)((device >> 16)); + return (uint8_t)((device >> 16)); } -// TODO: doxygen comment. -/// @brief +/// @brief Extract the `slot` from the device. static inline int pci_extract_slot(uint32_t device) { - return (uint8_t)((device >> 8)); + return (uint8_t)((device >> 8)); } -// TODO: doxygen comment. -/// @brief +/// @brief Extract the `func` from the device. static inline int pci_extract_func(uint32_t device) { - return (uint8_t)(device); + return (uint8_t)(device); } -// TODO: doxygen comment. -/// @brief +/// @brief TODO: doxygen comment. static inline uint32_t pci_get_addr(uint32_t device, int field) { - return 0x80000000 | (pci_extract_bus(device) << 16) | - (pci_extract_slot(device) << 11) | (pci_extract_func(device) << 8) | - ((field)&0xFC); + return 0x80000000 | (pci_extract_bus(device) << 16) | + (pci_extract_slot(device) << 11) | (pci_extract_func(device) << 8) | + ((field)&0xFC); } -// TODO: doxygen comment. -/// @brief +/// @brief TODO: doxygen comment. static inline uint32_t pci_box_device(int bus, int slot, int func) { - return (uint32_t)((bus << 16) | (slot << 8) | func); + return (uint32_t)((bus << 16) | (slot << 8) | func); } /// @brief Reads a field from the given PCI device. @@ -212,27 +192,26 @@ const char *pci_vendor_lookup(unsigned short vendor_id); // TODO: doxygen comment. /// @brief const char *pci_device_lookup(unsigned short vendor_id, - unsigned short device_id); + unsigned short device_id); // TODO: doxygen comment. /// @brief -void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra); +void pci_scan_hit(pci_scan_func_t f, uint32_t dev, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, - void *extra); +void pci_scan_func(pci_scan_func_t f, int type, int bus, int slot, int func, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void *extra); +void pci_scan_slot(pci_scan_func_t f, int type, int bus, int slot, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan_bus(pci_func_t f, int type, int bus, void *extra); +void pci_scan_bus(pci_scan_func_t f, int type, int bus, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan(pci_func_t f, int type, void *extra); +void pci_scan(pci_scan_func_t f, int type, void *extra); // TODO: doxygen comment. /// @brief @@ -245,3 +224,5 @@ int pci_get_interrupt(uint32_t device); // TODO: doxygen comment. /// @brief void pci_debug_scan(); + +///! @endcond \ No newline at end of file diff --git a/mentos/inc/drivers/ata.h b/mentos/inc/drivers/ata.h index 6920636..a8241b5 100644 --- a/mentos/inc/drivers/ata.h +++ b/mentos/inc/drivers/ata.h @@ -1,8 +1,16 @@ /// MentOS, The Mentoring Operating system project -// @file ata.h +/// @file ata.h /// @brief ATA values and data structures. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @details +/// IDE is a keyword which refers to the electrical specification of the cables +/// which connect ATA drives (like hard drives) to another device. The drives +/// use the ATA (Advanced Technology Attachment) interface. An IDE cable also +/// can terminate at an IDE card connected to PCI. +/// ATAPI is an extension to ATA (recently renamed to PATA) which adds support +/// for the SCSI command set. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #pragma once @@ -11,189 +19,187 @@ /// @defgroup ata_defines The ATA configuration registers. /// @{ -/// @defgroup status_register Status register bits +/// @defgroup status_register Status register bits. /// @{ -/// Device Busy -#define ATA_STAT_BUSY 0x80 -/// Device Ready -#define ATA_STAT_READY 0x40 -/// Device Fault -#define ATA_STAT_FAULT 0x20 -/// Device Seek Complete -#define ATA_STAT_SEEK 0x10 -/// Data Request (ready) -#define ATA_STAT_DRQ 0x08 -/// Corrected Data Error -#define ATA_STAT_CORR 0x04 -/// Vendor specific -#define ATA_STAT_INDEX 0x02 -/// Error -#define ATA_STAT_ERR 0x01 +#define ATA_STAT_BUSY 0x80 /// Device Busy +#define ATA_STAT_READY 0x40 ///< Device Ready +#define ATA_STAT_FAULT 0x20 ///< Device Fault +#define ATA_STAT_SEEK 0x10 ///< Device Seek Complete +#define ATA_STAT_DRQ 0x08 ///< Data Request (ready) +#define ATA_STAT_CORR 0x04 ///< Corrected Data Error +#define ATA_STAT_INDEX 0x02 ///< Vendor specific +#define ATA_STAT_ERR 0x01 ///< Error -/// @} - -/// @defgroup device_registers Device / Head Register Bits -/// @{ - -#define ATA_DEVICE(x) ((x & 1) << 4) -#define ATA_LBA 0xE0 - -/// @} +///< @} /// @defgroup ata_commands ATA Commands /// @{ -/// Read Sectors (with retries) -#define ATA_CMD_READ 0x20 -/// Read Sectors (no retries) -#define ATA_CMD_READN 0x21 -/// Write Sectores (with retries) -#define ATA_CMD_WRITE 0x30 -/// Write Sectors (no retries) -#define ATA_CMD_WRITEN 0x31 -/// Read Verify (with retries) -#define ATA_CMD_VRFY 0x40 -/// Read verify (no retries) -#define ATA_CMD_VRFYN 0x41 -/// Seek -#define ATA_CMD_SEEK 0x70 -/// Execute Device Diagnostic -#define ATA_CMD_DIAG 0x90 -/// Initialize Device Parameters -#define ATA_CMD_INIT 0x91 -/// Read Multiple -#define ATA_CMD_RD_MULT 0xC4 -/// Write Multiple -#define ATA_CMD_WR_MULT 0xC5 -/// Set Multiple Mode -#define ATA_CMD_SETMULT 0xC6 -/// Read DMA (with retries) -#define ATA_CMD_RD_DMA 0xC8 -/// Read DMS (no retries) -#define ATA_CMD_RD_DMAN 0xC9 -/// Write DMA (with retries) -#define ATA_CMD_WR_DMA 0xCA -/// Write DMA (no retires) -#define ATA_CMD_WR_DMAN 0xCB -/// Identify Device -#define ATA_CMD_IDENT 0xEC -#define ATA_CMD_CH_FLSH 0xE7 -/// Set Features -#define ATA_CMD_SETF 0xEF -/// Check Power Mode -#define ATA_CMD_CHK_PWR 0xE5 +#define ATA_CMD_READ 0x20 ///< Read Sectors (with retries) +#define ATA_CMD_READN 0x21 ///< Read Sectors (no retries) +#define ATA_CMD_WRITE 0x30 ///< Write Sectores (with retries) +#define ATA_CMD_WRITEN 0x31 ///< Write Sectors (no retries) +#define ATA_CMD_VRFY 0x40 ///< Read Verify (with retries) +#define ATA_CMD_VRFYN 0x41 ///< Read verify (no retries) +#define ATA_CMD_SEEK 0x70 ///< Seek +#define ATA_CMD_DIAG 0x90 ///< Execute Device Diagnostic +#define ATA_CMD_INIT 0x91 ///< Initialize Device Parameters +#define ATA_CMD_RD_MULT 0xC4 ///< Read Multiple +#define ATA_CMD_WR_MULT 0xC5 ///< Write Multiple +#define ATA_CMD_SETMULT 0xC6 ///< Set Multiple Mode +#define ATA_CMD_RD_DMA 0xC8 ///< Read DMA (with retries) +#define ATA_CMD_RD_DMAN 0xC9 ///< Read DMS (no retries) +#define ATA_CMD_WR_DMA 0xCA ///< Write DMA (with retries) +#define ATA_CMD_WR_DMAN 0xCB ///< Write DMA (no retires) +#define ATA_CMD_IDENT 0xEC ///< Identify Device +#define ATA_CMD_CH_FLSH 0xE7 ///< Cache flush. +#define ATA_CMD_SETF 0xEF ///< Set Features +#define ATA_CMD_CHK_PWR 0xE5 ///< Check Power Mode -/// @} +///< @} /// @defgroup atapi_commands ATAPI Commands /// @{ -#define ATAPI_CMD_PACKET 0xA0 +#define ATAPI_CMD_PACKET 0xA0 #define ATAPI_CMD_ID_PCKT 0xA1 /// @} -#define ATA_IDENT_DEVICETYPE 0 -#define ATA_IDENT_CYLINDERS 2 -#define ATA_IDENT_HEADS 6 -#define ATA_IDENT_SECTORS 12 -#define ATA_IDENT_SERIAL 20 -#define ATA_IDENT_MODEL 54 +/// @defgroup ata_ident_brits Identification Space Bits +/// @brief Definitions used to read information from the identification space. +/// @{ + +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 #define ATA_IDENT_CAPABILITIES 98 -#define ATA_IDENT_FIELDVALID 106 -#define ATA_IDENT_MAX_LBA 120 -#define ATA_IDENT_COMMANDSETS 164 -#define ATA_IDENT_MAX_LBA_EXT 200 - -#define IDE_ATA 0x00 -#define IDE_ATAPI 0x01 - -#define ATA_MASTER 0x00 -#define ATA_SLAVE 0x01 - -#define ATA_REG_DATA 0x00 -#define ATA_REG_ERROR 0x01 -#define ATA_REG_FEATURES 0x01 -#define ATA_REG_SECCOUNT0 0x02 -#define ATA_REG_LBA0 0x03 -#define ATA_REG_LBA1 0x04 -#define ATA_REG_LBA2 0x05 -#define ATA_REG_HDDEVSEL 0x06 -#define ATA_REG_COMMAND 0x07 -#define ATA_REG_STATUS 0x07 -#define ATA_REG_SECCOUNT1 0x08 -#define ATA_REG_LBA3 0x09 -#define ATA_REG_LBA4 0x0A -#define ATA_REG_LBA5 0x0B -#define ATA_REG_CONTROL 0x0C -#define ATA_REG_ALTSTATUS 0x0C -#define ATA_REG_DEVADDRESS 0x0D - -// Channels: -#define ATA_PRIMARY 0x00 -#define ATA_SECONDARY 0x01 - -// Directions: -#define ATA_READ 0x00 -#define ATA_WRITE 0x01 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 /// @} -typedef struct { - uint16_t base; - uint16_t ctrl; - uint16_t bmide; - uint16_t nien; +/// @defgroup ata_interface Interface Type +/// @{ + +#define IDE_ATA 0x00 ///< ATA (Advanced Technology Attachment) interface. +#define IDE_ATAPI 0x01 ///< Extended ATA with support for SCSI command set. + +/// @} + +/// @defgroup ata_priority Interface Priority +/// @{ + +#define ATA_DEVICE_0 0x00 +#define ATA_DEVICE_1 0x01 + +/// @} + +/// @defgroup ata_command_block_registers Command block registers +/// @{ + +#define ATA_REG_DATA 0x00 ///< Read/Write PIO data bytes. +#define ATA_REG_ERROR 0x01 ///< Error generated by the last ATA command. +#define ATA_REG_FEATURES 0x01 ///< Control command specific interface features. +#define ATA_REG_SECCOUNT0 0x02 ///< Number of sectors to read/write (0 is a special value). +#define ATA_REG_LBA0 0x03 ///< Sector Number Register. +#define ATA_REG_LBA1 0x04 ///< Cylinder Low Register. +#define ATA_REG_LBA2 0x05 ///< Cylinder High Register. +#define ATA_REG_HDDEVSEL 0x06 ///< Used to select a drive and/or head. +#define ATA_REG_COMMAND 0x07 ///< Used to send ATA commands to the device. +#define ATA_REG_STATUS 0x07 ///< Used to read the current status. +#define ATA_REG_SECCOUNT1 0x08 ///< +#define ATA_REG_LBA3 0x09 ///< +#define ATA_REG_LBA4 0x0A ///< +#define ATA_REG_LBA5 0x0B ///< +#define ATA_REG_CONTROL 0x0C ///< +#define ATA_REG_ALTSTATUS 0x0C ///< +#define ATA_REG_DEVADDRESS 0x0D ///< + +/// @} + +/// @defgroup ata_channels Channels +/// @{ + +#define ATA_PRIMARY 0x00 ///< Primary channel. +#define ATA_SECONDARY 0x01 ///< Secondary channel. +/// @} + +/// @defgroup ata_directions Directions +/// @{ + +#define ATA_READ 0x00 ///< Read direction. +#define ATA_WRITE 0x01 ///< Write direction. + +/// @} +/// @} + +/// @brief Stores information of a channel. +typedef struct ide_channel_regs_t{ + uint16_t base; ///< I/O Base. + uint16_t ctrl; ///< Control Base + uint16_t bmide; ///< Bus Master IDE + uint16_t nien; ///< nIEN (No Interrupt); } ide_channel_regs_t; -typedef struct { - uint8_t reserved; - uint8_t channel; - uint8_t drive; - uint16_t type; - uint16_t signature; - uint16_t capabilities; - uint32_t command_sets; - uint32_t size; - uint8_t model[41]; +/// @brief Stores information of a device. +typedef struct ide_device_t{ + uint8_t reserved; ///< 0 (Empty) or 1 (This Drive really exists). + uint8_t channel; ///< 0 (Primary Channel) or 1 (Secondary Channel). + uint8_t drive; ///< 0 (Drive 0) or 1 (Drive 1). + uint16_t type; ///< 0: ATA, 1:ATAPI. + uint16_t signature; ///< Drive Signature. + uint16_t capabilities; ///< Features. + uint32_t command_sets; ///< Command Sets Supported. + uint32_t size; ///< Size in Sectors. + uint8_t model[41]; ///< Model in string. } ide_device_t; -typedef struct { - uint8_t status; - uint8_t chs_first_sector[3]; - uint8_t type; - uint8_t chs_last_sector[3]; - uint32_t lba_first_sector; - uint32_t sector_count; +/// @brief +typedef struct partition_t{ + uint8_t status; ///< + uint8_t chs_first_sector[3]; ///< + uint8_t type; ///< + uint8_t chs_last_sector[3]; ///< + uint32_t lba_first_sector; ///< + uint32_t sector_count; ///< } partition_t; -typedef struct { - uint16_t flags; - uint16_t unused1[9]; - char serial[20]; - uint16_t unused2[3]; - char firmware[8]; - char model[40]; - uint16_t sectors_per_int; - uint16_t unused3; - uint16_t capabilities[2]; - uint16_t unused4[2]; - uint16_t valid_ext_data; - uint16_t unused5[5]; - uint16_t size_of_rw_mult; - uint32_t sectors_28; - uint16_t unused6[38]; - uint64_t sectors_48; - uint16_t unused7[152]; +typedef struct ata_identify_t{ + uint16_t flags; + uint16_t unused1[9]; + char serial[20]; + uint16_t unused2[3]; + char firmware[8]; + char model[40]; + uint16_t sectors_per_int; + uint16_t unused3; + uint16_t capabilities[2]; + uint16_t unused4[2]; + uint16_t valid_ext_data; + uint16_t unused5[5]; + uint16_t size_of_rw_mult; + uint32_t sectors_28; + uint16_t unused6[38]; + uint64_t sectors_48; + uint16_t unused7[152]; } __attribute__((packed)) ata_identify_t; -typedef struct { - uint8_t boostrap[446]; - partition_t partitions[4]; - uint8_t signature[2]; +/// @brief Master Boot Record. +typedef struct mbr_t{ + uint8_t boostrap[446]; + partition_t partitions[4]; + uint8_t signature[2]; } __attribute__((packed)) mbr_t; int ata_initialize(); int ata_finalize(); + +///! @endcond \ No newline at end of file diff --git a/mentos/inc/drivers/fdc.h b/mentos/inc/drivers/fdc.h index 608ecec..c037c25 100644 --- a/mentos/inc/drivers/fdc.h +++ b/mentos/inc/drivers/fdc.h @@ -1,38 +1,37 @@ /// MentOS, The Mentoring Operating system project /// @file fdc.h /// @brief Definitions about the floppy. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once /// @brief Floppy Disk Controller (FDC) registers. -typedef enum fdc_registers_t -{ +typedef enum fdc_registers_t { STATUS_REGISTER_A = 0x3F0, ///< This register is read-only and monitors the state of the ///< interrupt pin and several disk interface pins. - STATUS_REGISTER_B = 0x3F1, + STATUS_REGISTER_B = 0x3F1, ///< This register is read-only and monitors the state of several ///< isk interface pins. - DOR = 0x3F2, + DOR = 0x3F2, ///< The Digital Output Register contains the drive select and ///< motor enable bits, a reset bit and a DMA GATE bit. - TAPE_DRIVE_REGISTER = 0x3F3, + TAPE_DRIVE_REGISTER = 0x3F3, ///< This register allows the user to assign tape support to a ///< particular drive during initialization. - MAIN_STATUS_REGISTER = 0x3F4, + MAIN_STATUS_REGISTER = 0x3F4, ///< The Main Status Register is a read-only register and is used ///< for controlling command input and result output for all commands. - DATARATE_SELECT_REGISTER = 0x3F4, + DATARATE_SELECT_REGISTER = 0x3F4, ///< This register is included for compatibility with the 82072 ///< floppy controller and is write-only. - DATA_FIFO = 0x3F5, + DATA_FIFO = 0x3F5, ///< All command parameter information and disk data transfers go ///< through the FIFO. - DIGITAL_INPUT_REGISTER = 0x3F7, + DIGITAL_INPUT_REGISTER = 0x3F7, ///< This register is read only in all modes. - CONFIGURATION_CONTROL_REGISTER = 0x3F7 + CONFIGURATION_CONTROL_REGISTER = 0x3F7 ///< This register sets the datarate and is write only. } fdc_registers_t; diff --git a/mentos/inc/drivers/keyboard/keyboard.h b/mentos/inc/drivers/keyboard/keyboard.h index 6436794..c3a84f5 100644 --- a/mentos/inc/drivers/keyboard/keyboard.h +++ b/mentos/inc/drivers/keyboard/keyboard.h @@ -1,14 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file keyboard.h /// @brief Definitions about the keyboard. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "kernel.h" -#include "stdint.h" -#include "stdbool.h" /// @brief The dimension of the circular buffer used to store video history. #define BUFSIZE 256 @@ -16,8 +14,9 @@ /// @brief Function used to install the keyboard. void keyboard_install(); -/// @brief The keyboard handler. -void keyboard_isr(pt_regs *r); +/// @brief The interrupt service routine of the keyboard. +/// @param f The interrupt stack frame. +void keyboard_isr(pt_regs *f); /// @brief Enable the keyboard. void keyboard_enable(); @@ -32,20 +31,3 @@ void keyboard_update_leds(); /// @details It loops until there is something new to read. /// @return The read character. int keyboard_getc(); - -/// @brief Set Keyboard echo Shadow. -/// @param value 1 if you want enable shadow, 0 otherwise. -void keyboard_set_shadow(const bool_t value); - -/// @brief Set Keyboard echo Shadow character. -/// @param value 1 if you want enable shadow, 0 otherwise. -void keyboard_set_shadow_character(const char _shadow_character); - -/// @brief Get Keyboard Shadow information. -bool_t keyboard_get_shadow(); - -/// @brief Get ctrl status. -bool_t keyboard_is_ctrl_pressed(); - -/// @brief Get shift status. -bool_t keyboard_is_shifted(); diff --git a/mentos/inc/drivers/keyboard/keymap.h b/mentos/inc/drivers/keyboard/keymap.h index 9ce71c7..c55fefb 100644 --- a/mentos/inc/drivers/keyboard/keymap.h +++ b/mentos/inc/drivers/keyboard/keymap.h @@ -1,242 +1,151 @@ /// MentOS, The Mentoring Operating system project /// @file keymap.h /// @brief Keymap for keyboard. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" +/// @brief The list of supported keyboard layouts. +typedef enum { + KEYMAP_IT, ///< Identifies the IT keyboard mapping. + KEYMAP_US, ///< Identifies the US keyboard mapping. + KEYMAP_TYPE_MAX ///< The delimiter for the keyboard types. +} keymap_type_t; + +/// @brief Defines a set of arrays used to map key to characters. +typedef struct keymap_t { + /// The basic mapping. + int32_t base[65536]; + /// The mapping when shifted. + int32_t shift[65536]; + /// The mapping when numlock is active. + uint32_t numlock[65536]; +} keymap_t; + +/// @brief Returns the current keymap type. +/// @return The current keymap type. +keymap_type_t get_keymap_type(); + +/// @brief Changes the current keymap type. +/// @param type The type to set. +void set_keymap_type(keymap_type_t type); + +/// @brief Returns the current keymap. +/// @return Pointer to the current keymap. +const keymap_t *get_keymap(); + +/// @brief Initializes the supported keymaps. +void init_keymaps(); + /// @defgroup keyboardcodes Keyboard Codes /// @brief This is the list of keyboard codes. /// @{ -/// Escape character. -#define KEY_ESCAPE 0x01 -/// 1. -#define KEY_ONE 0x02 -/// 2. -#define KEY_TWO 0x03 -/// 3. -#define KEY_THREE 0x04 -/// 4. -#define KEY_FOUR 0x05 -/// 5. -#define KEY_FIVE 0x06 -/// 6. -#define KEY_SIX 0x07 -/// 7. -#define KEY_SEVEN 0x08 -/// 8. -#define KEY_EIGHT 0x09 -/// 9. -#define KEY_NINE 0x0A -/// 0. -#define KEY_ZERO 0x0B -/// '. -#define KEY_APOSTROPHE 0x0C -/// i'. -#define KEY_I_ACC 0x0D -/// Backspace. -#define KEY_BACKSPACE 0x0E -/// Tabular. -#define KEY_TAB 0x0F -/// q. -#define KEY_Q 0x10 -/// w. -#define KEY_W 0x11 -/// e. -#define KEY_E 0x12 -/// r. -#define KEY_R 0x13 -/// t. -#define KEY_T 0x14 -/// y. -#define KEY_Y 0x15 -/// u. -#define KEY_U 0x16 -/// i. -#define KEY_I 0x17 -/// o. -#define KEY_O 0x18 -/// p. -#define KEY_P 0x19 -/// (. -#define KEY_LEFT_BRAKET 0x1A -/// ). -#define KEY_RIGHT_BRAKET 0x1B -/// Enter. -#define KEY_ENTER 0x1C -/// Left-ctrl. -#define KEY_LEFT_CONTROL 0x1D -/// a. -#define KEY_A 0x1E -/// s. -#define KEY_S 0x1F -/// d. -#define KEY_D 0x20 -/// f. -#define KEY_F 0x21 -/// g. -#define KEY_G 0x22 -/// h. -#define KEY_H 0x23 -/// j. -#define KEY_J 0x24 -/// k. -#define KEY_K 0x25 -/// l. -#define KEY_L 0x26 -/// '. -#define KEY_SEMICOLON 0x27 -/// ". -#define KEY_DOUBLE_QUOTES 0x28 -///` or ~. -#define KEY_GRAVE 0x29 -/// LShift. -#define KEY_LEFT_SHIFT 0x2A -/// \ or |. -#define KEY_BACKSLASH 0x2B -/// Z. -#define KEY_Z 0x2c -/// X. -#define KEY_X 0x2d -/// C. -#define KEY_C 0x2e -/// V. -#define KEY_V 0x2f -/// B. -#define KEY_B 0x30 -/// N. -#define KEY_N 0x31 -/// M. -#define KEY_M 0x32 -/// , or <. -#define KEY_COMMA 0x33 -/// . or >. -#define KEY_PERIOD 0x34 -/// - or _. -#define KEY_MINUS 0x35 -/// RShift. -#define KEY_RIGHT_SHIFT 0x36 -/// NP - *. -#define KEY_KP_MUL 0x37 -/// LAlt. -#define KEY_LEFT_ALT 0x38 -/// Space. -#define KEY_SPACE 0x39 -/// Caps Lock. -#define KEY_CAPS_LOCK 0x3a -/// Function 1. -#define KEY_F1 0x3B -/// Function 2. -#define KEY_F2 0x3C -/// Function 3. -#define KEY_F3 0x3D -/// Function 4. -#define KEY_F4 0x3E -/// Function 5. -#define KEY_F5 0x3F -/// Function 6. -#define KEY_F6 0x40 -/// Function 7. -#define KEY_F7 0x41 -/// Function 8. -#define KEY_F8 0x42 -/// Function 9. -#define KEY_F9 0x43 -/// Function 10. -#define KEY_F10 0x44 -/// Num Lock. -#define KEY_NUM_LOCK 0x45 -/// Scroll Lock. -#define KEY_SCROLL_LOCK 0x46 -/// NP - Home. -#define KEY_KP7 0x47 -/// NP - UpArrow. -#define KEY_KP8 0x48 -/// NP - Pgup. -#define KEY_KP9 0x49 -/// NP - Grey. -#define KEY_KP_SUB 0x4A -/// NP - LArrow. -#define KEY_KP4 0x4B -/// NP - Center. -#define KEY_KP5 0x4C -/// NP - RArrow. -#define KEY_KP6 0x4D -/// NP - Grey +. -#define KEY_KP_ADD 0x4E -/// NP - End. -#define KEY_KP1 0x4F -/// NP - DArrow. -#define KEY_KP2 0x50 -/// NP - Pgdn. -#define KEY_KP3 0x51 -/// NP - Ins. -#define KEY_KP0 0x52 -/// NP - Del. -#define KEY_KP_DEC 0x53 -/// NP - Del. -#define KEY_KP_LESS 0x56 -/// NP - Enter 57372. -#define KEY_KP_RETURN 0xe01c -/// Right Ctrl 57373. -#define KEY_RIGHT_CONTROL 0xE01D -/// Divide 57397. -#define KEY_KP_DIV 0xE035 -/// Right Alt 57400. -#define KEY_RIGHT_ALT 0xe038 -/// F11 57431. -#define KEY_F11 0xe057 -/// F12 57432. -#define KEY_F12 0xe058 -/// Left Winkey 57435. -#define KEY_LEFT_WIN 0xe05b -/// Right Winkey 57436. -#define KEY_RIGHT_WIN 0xe05c -/// Ins 57426. -#define KEY_INSERT 0xe052 -/// Home 57415. -#define KEY_HOME 0xe047 -/// Up Arrow 57416. -#define KEY_UP_ARROW 0xe048 -/// Pgup 57417. -#define KEY_PAGE_UP 0xe049 -/// Left Arrow 57419. -#define KEY_LEFT_ARROW 0xe04b -/// Del 57427. -#define KEY_DELETE 0xe053 -/// End 57423. -#define KEY_END 0xe04f -/// Pgdn 57425. -#define KEY_PAGE_DOWN 0xe051 -/// Right Arrow 57421. -#define KEY_RIGHT_ARROW 0xe04d -/// Down Arrow 57424. -#define KEY_DOWN_ARROW 0xe050 -///< Code break code. -#define CODE_BREAK 0x80 +#define KEY_ESCAPE 0x0001U ///< Escape character +#define KEY_ONE 0x0002U ///< 1 +#define KEY_TWO 0x0003U ///< 2 +#define KEY_THREE 0x0004U ///< 3 +#define KEY_FOUR 0x0005U ///< 4 +#define KEY_FIVE 0x0006U ///< 5 +#define KEY_SIX 0x0007U ///< 6 +#define KEY_SEVEN 0x0008U ///< 7 +#define KEY_EIGHT 0x0009U ///< 8 +#define KEY_NINE 0x000AU ///< 9 +#define KEY_ZERO 0x000BU ///< 0 +#define KEY_APOSTROPHE 0x000CU ///< ' +#define KEY_I_ACC 0x000DU ///< i' +#define KEY_BACKSPACE 0x000EU ///< Backspace +#define KEY_TAB 0x000FU ///< Tabular +#define KEY_Q 0x0010U ///< q +#define KEY_W 0x0011U ///< w +#define KEY_E 0x0012U ///< e +#define KEY_R 0x0013U ///< r +#define KEY_T 0x0014U ///< t +#define KEY_Y 0x0015U ///< y +#define KEY_U 0x0016U ///< u +#define KEY_I 0x0017U ///< i +#define KEY_O 0x0018U ///< o +#define KEY_P 0x0019U ///< p +#define KEY_LEFT_BRAKET 0x001AU ///< ( +#define KEY_RIGHT_BRAKET 0x001BU ///< ) +#define KEY_ENTER 0x001CU ///< Enter +#define KEY_LEFT_CONTROL 0x001DU ///< Left-ctrl +#define KEY_A 0x001EU ///< a +#define KEY_S 0x001FU ///< s +#define KEY_D 0x0020U ///< d +#define KEY_F 0x0021U ///< f +#define KEY_G 0x0022U ///< g +#define KEY_H 0x0023U ///< h +#define KEY_J 0x0024U ///< j +#define KEY_K 0x0025U ///< k +#define KEY_L 0x0026U ///< l +#define KEY_SEMICOLON 0x0027U ///< ' +#define KEY_DOUBLE_QUOTES 0x0028U ///< " +#define KEY_GRAVE 0x0029U ///< Either ` or ~ +#define KEY_LEFT_SHIFT 0x002AU ///< LShift +#define KEY_BACKSLASH 0x002BU ///< Either \ or | +#define KEY_Z 0x002cU ///< Z +#define KEY_X 0x002dU ///< X +#define KEY_C 0x002eU ///< C +#define KEY_V 0x002fU ///< V +#define KEY_B 0x0030U ///< B +#define KEY_N 0x0031U ///< N +#define KEY_M 0x0032U ///< M +#define KEY_COMMA 0x0033U ///< Either , or < +#define KEY_PERIOD 0x0034U ///< Either . or > +#define KEY_MINUS 0x0035U ///< Either - and _ +#define KEY_RIGHT_SHIFT 0x0036U ///< RShift +#define KEY_KP_MUL 0x0037U ///< NP - * +#define KEY_LEFT_ALT 0x0038U ///< LAlt +#define KEY_SPACE 0x0039U ///< Space +#define KEY_CAPS_LOCK 0x003aU ///< Caps Lock +#define KEY_F1 0x003BU ///< Function 1 +#define KEY_F2 0x003CU ///< Function 2 +#define KEY_F3 0x003DU ///< Function 3 +#define KEY_F4 0x003EU ///< Function 4 +#define KEY_F5 0x003FU ///< Function 5 +#define KEY_F6 0x0040U ///< Function 6 +#define KEY_F7 0x0041U ///< Function 7 +#define KEY_F8 0x0042U ///< Function 8 +#define KEY_F9 0x0043U ///< Function 9 +#define KEY_F10 0x0044U ///< Function 10 +#define KEY_NUM_LOCK 0x0045U ///< Num Lock +#define KEY_SCROLL_LOCK 0x0046U ///< Scroll Lock +#define KEY_KP7 0x0047U ///< NP - Home +#define KEY_KP8 0x0048U ///< NP - UpArrow +#define KEY_KP9 0x0049U ///< NP - Pgup +#define KEY_KP_SUB 0x004AU ///< NP - Grey +#define KEY_KP4 0x004BU ///< NP - LArrow +#define KEY_KP5 0x004CU ///< NP - Center +#define KEY_KP6 0x004DU ///< NP - RArrow +#define KEY_KP_ADD 0x004EU ///< NP - Grey + +#define KEY_KP1 0x004FU ///< NP - End +#define KEY_KP2 0x0050U ///< NP - DArrow +#define KEY_KP3 0x0051U ///< NP - Pgdn +#define KEY_KP0 0x0052U ///< NP - Ins +#define KEY_KP_DEC 0x0053U ///< NP - Del +#define KEY_KP_LESS 0x0056U ///< NP - Del +#define KEY_KP_RETURN 0xe01cU ///< NP - Enter 57372 +#define KEY_RIGHT_CONTROL 0xE01DU ///< Right Ctrl 57373 +#define KEY_KP_DIV 0xE035U ///< Divide 57397 +#define KEY_RIGHT_ALT 0xe038U ///< Right Alt 57400 +#define KEY_F11 0xe057U ///< F11 57431 +#define KEY_F12 0xe058U ///< F12 57432 +#define KEY_LEFT_WIN 0xe05bU ///< Left Winkey 57435 +#define KEY_RIGHT_WIN 0xe05cU ///< Right Winkey 57436 +#define KEY_INSERT 0xe052U ///< Ins 57426 +#define KEY_HOME 0xe047U ///< Home 57415 +#define KEY_UP_ARROW 0xe048U ///< Up Arrow 57416 +#define KEY_PAGE_UP 0xe049U ///< Pgup 57417 +#define KEY_LEFT_ARROW 0xe04bU ///< Left Arrow 57419 +#define KEY_DELETE 0xe053U ///< Del 57427 +#define KEY_END 0xe04fU ///< End 57423 +#define KEY_PAGE_DOWN 0xe051U ///< Pgdn 57425 +#define KEY_RIGHT_ARROW 0xe04dU ///< Right Arrow 57421 +#define KEY_DOWN_ARROW 0xe050U ///< Down Arrow 57424 +#define CODE_BREAK 0x0080U ///< Code break code /// @} - -/// Num Pad Led. -#define NUM_LED 0x45 -/// Scroll Lock Led. -#define SCROLL_LED 0x46 -/// Caps Lock Led. -#define CAPS_LED 0x3a - -/// @brief Defines a set of arrays used to map key to characters. -typedef struct keymap_t { - /// The basic mapping. - int32_t base[65536]; - /// The mapping when shifted. - int32_t shift[65536]; - /// The mapping when numlock is active. - uint32_t numlock[65536]; -} keymap_t; - -/// @brief Italian keymap. -extern const keymap_t keymap_it; diff --git a/mentos/inc/drivers/mouse.h b/mentos/inc/drivers/mouse.h index 0f20a34..8ed7154 100644 --- a/mentos/inc/drivers/mouse.h +++ b/mentos/inc/drivers/mouse.h @@ -1,24 +1,25 @@ /// MentOS, The Mentoring Operating system project /// @file mouse.h /// @brief Driver for *PS2* Mouses. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #pragma once /* The mouse starts sending automatic packets when the mouse moves or is * clicked. */ -#include +#include "kernel.h" -#define MOUSE_ENABLE_PACKET 0xF4 +#define MOUSE_ENABLE_PACKET 0xF4 /// The mouse stops sending automatic packets. -#define MOUSE_DISABLE_PACKET 0xF5 +#define MOUSE_DISABLE_PACKET 0xF5 /// Disables streaming, sets the packet rate to 100 per second, and /// resolution to 4 pixels per mm. -#define MOUSE_USE_DEFAULT_SETTINGS 0xF6 +#define MOUSE_USE_DEFAULT_SETTINGS 0xF6 /// @brief Sets up the mouse by installing the mouse handler into IRQ12. void mouse_install(); @@ -41,5 +42,8 @@ void mouse_write(unsigned char data); /// @return The data received from mouse. unsigned char mouse_read(); -/// @brief The mouse handler. -void mouse_isr(register_t *r); +/// @brief The interrupt service routine of the mouse. +/// @param f The interrupt stack frame. +void mouse_isr(pt_regs *f); + +///! @endcond \ No newline at end of file diff --git a/mentos/inc/drivers/rtc.h b/mentos/inc/drivers/rtc.h new file mode 100644 index 0000000..d0dd81f --- /dev/null +++ b/mentos/inc/drivers/rtc.h @@ -0,0 +1,16 @@ +/// MentOS, The Mentoring Operating system project +/// @file rtc.h +/// @brief Real Time Clock (RTC) driver. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "time.h" + +/// @brief Copies the global time inside the provided variable. +/// @param time Pointer where we store the global time. +extern void gettime(tm_t *time); + +/// @brief Installs the Real Time Clock. +extern void rtc_install(void); \ No newline at end of file diff --git a/mentos/inc/elf/elf.h b/mentos/inc/elf/elf.h index b3bad2e..44ba0b3 100644 --- a/mentos/inc/elf/elf.h +++ b/mentos/inc/elf/elf.h @@ -1,60 +1,292 @@ /// MentOS, The Mentoring Operating system project /// @file elf.h /// @brief Function for multiboot support. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once +#include "process.h" #include "stdint.h" -#include "multiboot.h" -// Used to get the symbol type. -#define ELF32_ST_TYPE(i) ((i)&0xf) +/// @defgroup header_segment_types Program Header Segment Types +/// @brief List of numeric defines which identify segment entries types. +/// @{ -// List of symbol types. -#define ELF32_TYPE_FUNCTION (0x02) +/// @brief Unused. +#define PT_NULL 0 +/// @brief Specifies a loadable segment, described by p_filesz and p_memsz. +/// The bytes from the file are mapped to the beginning of the memory segment. +/// If the segment's memory size (p_memsz) is larger than the file +/// size (p_filesz), the extra bytes are defined to hold the value 0 +/// and to follow the segment's initialized area. The file size can not +/// be larger than the memory size. Loadable segment entries in the program +/// header table appear in ascending order, sorted on the p_vaddr member. +#define PT_LOAD 1 +/// @brief Specifies dynamic linking information. +#define PT_DYNAMIC 2 +/// @brief Specifies the location and size of a null-terminated path name +/// to invoke as an interpreter. This segment type is mandatory for dynamic +/// executable files and can occur in shared objects. It cannot occur more +/// than once in a file. This type, if present, it must precede any loadable +/// segment entry. +#define PT_INTERP 3 +/// @brief Specifies the location and size of auxiliary information. +#define PT_NOTE 4 +/// @brief Reserved but has unspecified semantics. +#define PT_SHLIB 5 +/// @brief Specifies the location and size of the program header table +/// itself, both in the file and in the memory image of the program. +/// This segment type cannot occur more than once in a file. Moreover, +/// it can occur only if the program header table is part of the memory +/// image of the program. This type, if present, must precede any loadable +/// segment entry. +#define PT_PHDR 6 +/// @brief Section for supporting exception handling routines. +/// @details +/// The .eh_frame section has the same structure with .debug_frame, +/// which follows DWARF format. It represents the table that describes +/// how to set registers to restore the previous call frame at runtime. +#define PT_EH_FRAME 0x6474E550 +/// @brief Is a program header which tells the system how to control +/// the stack when the ELF is loaded into memory. +#define PT_GNU_STACK 0x6474E551 +/// @brief This segment indicates the memory region which should be made +/// Read-Only after relocation is done. This segment usually appears in a +/// dynamic link library and it contains .ctors, .dtors, .dynamic, .got s +/// ections. See paragraph below. +#define PT_GNU_RELRO 0x6474E552 +/// @brief TODO: Document. +#define PT_LOPROC 0x70000000 +/// @brief TODO: Document. +#define PT_HIPROC 0x7FFFFFFF -/// @brief A section header with all kinds of useful information. -typedef struct elf_section_header -{ - uint32_t name_offset_in_shstrtab; - uint32_t type; +/// @} + +/// Elf header ident size. +#define EI_NIDENT 16 + +/// @brief The elf starting section. +typedef struct elf_header { + /// Elf header identity bits. + uint8_t ident[EI_NIDENT]; + /// Identifies object file type. + uint16_t type; + /// Specifies target instruction set architecture. + uint16_t machine; + /// Set to 1 for the original version of ELF. + uint32_t version; + /// This is the memory address of the entry point from where the process starts executing. + uint32_t entry; + /// Points to the start of the program header table. + uint32_t phoff; + /// TODO: Comment. + uint32_t shoff; + /// TODO: Comment. uint32_t flags; - uint32_t addr; + /// TODO: Comment. + uint16_t ehsize; + /// TODO: Comment. + uint16_t phentsize; + /// TODO: Comment. + uint16_t phnum; + /// TODO: Comment. + uint16_t shentsize; + /// TODO: Comment. + uint16_t shnum; + /// TODO: Comment. + uint16_t shstrndx; +} elf_header_t; + +/// @brief The elf program header, holding program layout information +typedef struct elf_program_header { + /// Identifies the type of the segment. + uint32_t type; + /// Offset of the segment in the file image. uint32_t offset; + /// Virtual address of the segment in memory. + uint32_t vaddr; + /// On systems where physical address is relevant, reserved for + /// segment's physical address. + uint32_t paddr; + /// Size in bytes of the segment in the file image. May be 0. + uint32_t filesz; + /// Size in bytes of the segment in memory. May be 0. + uint32_t memsz; + /// Segment-dependent flags. + uint32_t flags; + /// Values of 0 or 1 specify no alignment. Otherwise should be a positive, + /// integral power of 2, with p_vaddr equating p_offset modulus p_align. + uint32_t align; +} elf_program_header_t; + +/// @brief A section header with all kinds of useful information +typedef struct elf_section_header { + /// TODO: Comment. + uint32_t name; + /// TODO: Comment. + uint32_t type; + /// TODO: Comment. + uint32_t flags; + /// TODO: Comment. + uint32_t addr; + /// TODO: Comment. + uint32_t offset; + /// TODO: Comment. uint32_t size; + /// TODO: Comment. uint32_t link; + /// TODO: Comment. uint32_t info; + /// TODO: Comment. uint32_t addralign; + /// TODO: Comment. uint32_t entsize; -} __attribute__((packed)) elf_section_header_t; +} elf_section_header_t; /// @brief A symbol itself. -typedef struct elf_symbol -{ - uint32_t name_offset_in_strtab; +typedef struct elf_symbol { + /// TODO: Comment. + uint32_t name; + /// TODO: Comment. uint32_t value; + /// TODO: Comment. uint32_t size; - uint8_t info; - uint8_t other; - uint16_t shndx; -} __attribute__((packed)) elf_symbol_t; + /// TODO: Comment. + uint8_t info; + /// TODO: Comment. + uint8_t other; + /// TODO: Comment. + uint16_t ndx; +} elf_symbol_t; -// Will hold the array of symbols and their names for us. -typedef struct elf_symbols -{ - elf_symbol_t *symtab; - uint32_t symtab_size; - const char *strtab; - uint32_t strtab_size; -} elf_symbols_t; +/// @brief Holds information about relocation object (that do not need an addend). +typedef struct elf_rel_t { + /// TODO: Comment. + uint32_t r_offset; + /// TODO: Comment. + uint32_t r_info; +} elf_rel_t; +/// @brief Holds information about relocation object (that need an addend). +typedef struct elf_rela_t { + /// TODO: Comment. + uint32_t r_offset; + /// TODO: Comment. + uint32_t r_info; + /// TODO: Comment. + int32_t r_addend; +} elf_rela_t; -/// @brief Builds as the set of elf symbols from a multiboot scructure. -void build_elf_symbols_from_multiboot (multiboot_info_t* mb); +/// @brief Fields index of ELF_IDENT. +enum Elf_Ident { + EI_MAG0 = 0, ///< 0x7F + EI_MAG1 = 1, ///< 'E' + EI_MAG2 = 2, ///< 'L' + EI_MAG3 = 3, ///< 'F' + EI_CLASS = 4, ///< Architecture (32/64) + EI_DATA = 5, ///< Set to either 1 or 2 to signify little or big endianness. + EI_VERSION = 6, ///< ELF Version + EI_OSABI = 7, ///< OS Specific + EI_ABIVERSION = 8, ///< OS Specific + EI_PAD = 9 ///< Padding +}; -/* Locate a symbol (only functions) in the following elf symbols - * Note that, for now, we'll only use this for the kernel (no other elf things..) - */ -const char *elf_lookup_symbol (uint32_t addr, elf_symbols_t *elf); +#define ELFMAG0 0x7F ///< e_ident[EI_MAG0] +#define ELFMAG1 'E' ///< e_ident[EI_MAG1] +#define ELFMAG2 'L' ///< e_ident[EI_MAG2] +#define ELFMAG3 'F' ///< e_ident[EI_MAG3] + +#define ELFDATA2LSB 1 ///< Little Endian +#define ELFCLASS32 1 ///< 32-bit Architecture + +/// @brief Type of ELF files. +typedef enum Elf_Type { + ET_NONE = 0, ///< Unkown Type + ET_REL = 1, ///< Relocatable File + ET_EXEC = 2 ///< Executable File +} Elf_Type; + +#define EM_386 3 ///< x86 Machine Type. +#define EV_CURRENT 1 ///< ELF Current Version. + +/// @brief Defines a number of different types of sections, which correspond +/// to values stored in the field sh_type in the section header +typedef enum ShT_Types { + SHT_NULL = 0, ///< Null section + SHT_PROGBITS = 1, ///< Program information + SHT_SYMTAB = 2, ///< Symbol table + SHT_STRTAB = 3, ///< String table + SHT_RELA = 4, ///< Relocation (w/ addend) + SHT_NOBITS = 8, ///< Not present in file + SHT_REL = 9, ///< Relocation (no addend) +} ShT_Types; + +/// @brief ShT_Attributes corresponds to the field sh_flags, but are bit +/// flags rather than stand-alone values. +enum ShT_Attributes { + SHF_WRITE = 0x01, ///< Writable section + SHF_ALLOC = 0x02 ///< Exists in memory +}; + +/// @brief Provide access to teh symbol biding. +#define ELF32_ST_BIND(INFO) ((INFO) >> 4) +/// @brief Provide access to teh symbol type. +#define ELF32_ST_TYPE(INFO) ((INFO)&0x0F) + +/// @brief Provides possible symbol bindings. +enum StT_Bindings { + STB_LOCAL = 0, ///< Local scope + STB_GLOBAL = 1, ///< Global scope + STB_WEAK = 2 ///< Weak, (ie. __attribute__((weak))) +}; + +/// @brief Provides a number of possible symbol types. +enum StT_Types { + STT_NOTYPE = 0, ///< No type + STT_OBJECT = 1, ///< Variables, arrays, etc. + STT_FUNC = 2 ///< Methods or functions +}; + +/// @brief Loads an ELF file into the memory of task. +/// @param task The task for which we load the ELF. +/// @param file The ELF file. +/// @param entry The ELF binary entry. +/// @return 0 if fails, 1 if succeed. +int elf_load_file(task_struct *task, vfs_file_t *file, uint32_t *entry); + +/// @brief Checks if the file is a valid ELF. +/// @param file The file to check. +/// @param type The type of ELF file we expect. +/// @return 0 if fails, 1 if succeed. +int elf_check_file_type(vfs_file_t *file, Elf_Type type); + +/// @brief Checks the correctness of the ELF header. +/// @param hdr The header to check. +/// @return 0 if fails, 1 if succeed. +int elf_check_file_header(elf_header_t *hdr); + +/// @brief Checks the correctness of the ELF header magic number. +/// @param hdr The header to check. +/// @return 0 if fails, 1 if succeed. +int elf_check_magic_number(elf_header_t *hdr); + +/// @brief Transforms the passed ELF type to string. +/// @param type The integer representing the ELF type. +/// @return The string representing the ELF type. +const char *elf_type_to_string(int type); + +/// @brief Transforms the passed ELF section header type to string. +/// @param type The integer representing the ELF section header type. +/// @return The string representing the ELF section header type. +const char *elf_section_header_type_to_string(int type); + +/// @brief Transforms the passed ELF symbol type to string. +/// @param type The integer representing the ELF symbol type. +/// @return The string representing the ELF symbol type. +const char *elf_symbol_type_to_string(int type); + +/// @brief Transforms the passed ELF symbol bind to string. +/// @param bind The integer representing the ELF symbol bind. +/// @return The string representing the ELF symbol bind. +const char *elf_symbol_bind_to_string(int bind); \ No newline at end of file diff --git a/mentos/inc/fs/fcntl.h b/mentos/inc/fs/fcntl.h deleted file mode 100644 index 7c8a835..0000000 --- a/mentos/inc/fs/fcntl.h +++ /dev/null @@ -1,26 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcntl.h -/// @brief Headers of functions fcntl() and open(). -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// Open for reading only. -#define O_RDONLY 0x00 -/// Open for writing only. -#define O_WRONLY 0x01 -/// Open for reading and writing. -#define O_RDWR 0x02 -/// If the file exists, this flag has no effect. Otherwise, the file is created. -#define O_CREAT 0x40 -/// The file offset will be set to the end of the file prior to each write. -#define O_APPEND 0x400 - -/// @brief Deletes the file whose name is specified in filename. -/// @param pathname A path to a file. -/// @return On success, zero is returned. On error, -1 is returned, -/// and errno is set appropriately. -int remove(const char *pathname); diff --git a/mentos/inc/fs/initrd.h b/mentos/inc/fs/initrd.h index e5111d3..d95cac4 100644 --- a/mentos/inc/fs/initrd.h +++ b/mentos/inc/fs/initrd.h @@ -1,131 +1,15 @@ /// MentOS, The Mentoring Operating system project /// @file initrd.h /// @brief Headers of functions for initrd filesystem. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "stat.h" -#include "dirent.h" -#include "unistd.h" -#include "stdint.h" -#include "kernel.h" +/// @brief Initialize the initrd filesystem. +/// @return 0 if fails, 1 if succeed. +int initrd_init_module(void); -/// The maximum number of files. -#define MAX_FILES 32 - -/// The maximum number of file descriptors. -#define MAX_INITRD_DESCRIPTORS MAX_OPEN_FD - -/// @brief Contains the number of files inside the initrd filesystem. -typedef struct initrd_t { - /// Number of files. - uint32_t nfiles; -} initrd_t; - -/// @brief Information concerning a file. -typedef struct initrd_file_t { - /// Number used as delimiter, it must be set to 0xBF. - int magic; - /// The name of the file. - char fileName[NAME_MAX]; - /// The type of the file. - short int file_type; - /// The uid of the owner. - int uid; - /// Offset of the starting address. - unsigned int offset; - /// Dimension of the file. - unsigned int length; -} initrd_file_t; - -/// @brief Descriptor linked to open files. -typedef struct initrd_fd { - /// Id of the open file inside the file system. More precisely, its index - /// inside the vector of files. - int file_descriptor; - /// The current position inside the file. Used for writing/reading - /// opeations. - int cur_pos; -} initrd_fd; - -// TODO: doxygen comment. -/// @brief -extern initrd_t *fs_specs; -/// @brief File system headers. -extern initrd_file_t *fs_headers; -// TODO: doxygen comment. -/// @brief -extern unsigned int fs_end; - -/// Initializes the initrd file system. -uint32_t initfs_init(); - -/// @brief Opens a directory at the given path. -/// @param path The path where the directory resides. -/// @return Structure used to access the directory. -DIR *initfs_opendir(const char *path); - -/// @brief Closes the directory stream associated with dirp. -/// @param dirp The directory handler. -/// @return 0 on success. On error, -1 is returned, and errno is set. -int initfs_closedir(DIR *dirp); - -/// @brief Moves the position of the currently readed entry inside the -/// directory. -/// @param dirp The directory handler. -/// @return A pointer to the next entry inside the directory. -dirent_t *initrd_readdir(DIR *dirp); - -/// @brief Creates a new directory. -/// @param path The path to the new directory. -/// @param mode The file mode. -/// @return 0 if success. -int initrd_mkdir(const char *path, mode_t mode); - -/// @brief Removes a directory. -/// @param path The path to the directory. -/// @return 0 if success. -int initrd_rmdir(const char *path); - -/// @brief Open the file at the given path and returns its file descriptor. -/// @param path The path to the file. -/// @param flags The flags used to determine the behavior of the function. -/// @return The file descriptor of the opened file, otherwise returns -1. -int initfs_open(const char *path, int flags, ...); - -/// @brief Deletes the file at the given path. -/// @param path The path to the file. -/// @return On success, zero is returned. On error, -1 is returned. -int initfs_remove(const char *path); - -/// @brief Reads from the file identified by the file descriptor. -/// @param fildes The file descriptor of the file. -/// @param buf Buffer where the read content must be placed. -/// @param nbyte The number of bytes to read. -/// @return T The number of red bytes. -ssize_t initfs_read(int fildes, char *buf, size_t nbyte); - -/// @brief Retrieves information concerning the file at the given position. -/// @param path The path where the file resides. -/// @param stat The structure where the information are stored. -/// @return 0 if success. -int initrd_stat(const char *path, stat_t *stat); - -/// @brief Writes the given content inside the file. -/// @param fildes The file descriptor of the file. -/// @param buf The content to write. -/// @param nbyte The number of bytes to write. -/// @return T The number of written bytes. -ssize_t initrd_write(int fildes, const void *buf, size_t nbyte); - -/// @brief Closes the given file. -/// @param fildes The file descriptor of the file. -int initrd_close(int fildes); - -// TODO: doxygen comment. -size_t initrd_nfiles(); - -// TODO: doxygen comment. -void dump_initrd_fs(); +/// @brief Clean up the initrd filesystem. +/// @return 0 if fails, 1 if succeed. +int initrd_cleanup_module(void); diff --git a/mentos/inc/fs/ioctl.h b/mentos/inc/fs/ioctl.h new file mode 100644 index 0000000..cab5e05 --- /dev/null +++ b/mentos/inc/fs/ioctl.h @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.h +/// @brief Declares device controlling operations. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// @brief Manipulates the underlying device parameters of special files, or operating +/// characteristics of character special files (e.g., terminals). +/// @param fd Must be an open file descriptor. +/// @param request The device-dependent request code +/// @param data An untyped pointer to memory. +/// @return On success zero is returned. +int sys_ioctl(int fd, int request, void *data); \ No newline at end of file diff --git a/mentos/inc/fs/procfs.h b/mentos/inc/fs/procfs.h new file mode 100644 index 0000000..68afcab --- /dev/null +++ b/mentos/inc/fs/procfs.h @@ -0,0 +1,69 @@ +/// MentOS, The Mentoring Operating system project +/// @file procfs.h +/// @brief Proc file system public functions and structures. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "process.h" + +/// @brief Stores information about a procfs directory entry. +typedef struct proc_dir_entry_t { + /// Generic system operations. + vfs_sys_operations_t *sys_operations; + /// Files operations. + vfs_file_operations_t *fs_operations; + /// Data associated with the dir_entry. + void *data; + /// Name of the entry. + const char *name; +} proc_dir_entry_t; + +/// @brief Initialize the procfs filesystem. +/// @return 0 if fails, 1 if succeed. +int procfs_module_init(); + +/// @brief Clean up the procfs filesystem. +/// @return 0 if fails, 1 if succeed. +int procfs_cleanup_module(); + +/// @brief Finds the direntry inside `/proc` or under the given `parent`. +/// @param name The name of the entry we are searching. +/// @param parent The parent (optional). +/// @return A pointer to the entry, or NULL. +proc_dir_entry_t *proc_dir_entry_get(const char *name, proc_dir_entry_t *parent); + +/// @brief Creates a new directory inside the procfs filesystem. +/// @param name The name of the entry we are creating. +/// @param parent The parent (optional). +/// @return A pointer to the entry, or NULL if fails. +proc_dir_entry_t *proc_mkdir(const char *name, proc_dir_entry_t *parent); + +/// @brief Removes a directory from the procfs filesystem. +/// @param name The name of the entry we are removing. +/// @param parent The parent (optional). +/// @return 0 if succeed, or -errno in case of error. +int proc_rmdir(const char *name, proc_dir_entry_t *parent); + +/// @brief Creates a new entry inside the procfs filesystem. +/// @param name The name of the entry we are creating. +/// @param parent The parent (optional). +/// @return A pointer to the entry, or NULL if fails. +proc_dir_entry_t *proc_create_entry(const char *name, proc_dir_entry_t *parent); + +/// @brief Removes an entry from the procfs filesystem. +/// @param name The name of the entry we are removing. +/// @param parent The parent (optional). +/// @return 0 if succeed, or -errno in case of error. +int proc_destroy_entry(const char *name, proc_dir_entry_t *parent); + +/// @brief Create the entire procfs entry tree for the give process. +/// @param entry Pointer to the task_struct of the process. +/// @return 0 if succeed, or -errno in case of error. +int proc_create_entry_pid(task_struct *entry); + +/// @brief Destroy the entire procfs entry tree for the give process. +/// @param entry Pointer to the task_struct of the process. +/// @return 0 if succeed, or -errno in case of error. +int proc_destroy_entry_pid(task_struct *entry); \ No newline at end of file diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index 360ebec..c5ef6a1 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -1,52 +1,169 @@ /// MentOS, The Mentoring Operating system project /// @file vfs.h /// @brief Headers for Virtual File System (VFS). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "unistd.h" #include "vfs_types.h" +#include "slab.h" -/// The maximum number of mount points. -#define MAX_MOUNTPOINT 10 +#define MAX_OPEN_FD 16 ///< Maximum number of opened file. -/// The currently opened file descriptor. -extern int current_fd; +#define STDIN_FILENO 0 ///< Standard input. +#define STDOUT_FILENO 1 ///< Standard output. +#define STDERR_FILENO 2 ///< Standard error output. -/// The list of file descriptors. -extern file_descriptor_t fd_list[MAX_OPEN_FD]; +extern kmem_cache_t *vfs_file_cache; -/// The list of mount points. -extern mountpoint_t mountpoint_list[MAX_MOUNTPOINT]; +/// @brief Forward declaration of task_struct. +struct task_struct; + +/// @brief Searches for the mountpoint of the given path. +/// @param absolute_path Path for which we want to search the mountpoint. +/// @return Pointer to the vfs_file of the mountpoint. +super_block_t *vfs_get_superblock(const char *absolute_path); /// @brief Initialize the Virtual File System (VFS). void vfs_init(); -/// @brief Retrieves the id of the mount point where the path resides. -/// @param path The path to the mountpoint. -/// @return The id of the mountpoint. -int32_t get_mountpoint_id(const char *path); +/// @brief Register a new filesystem. +/// @param fs A pointer to the information concerning the new filesystem. +/// @return The outcome of the operation, 0 if fails. +int vfs_register_filesystem(file_system_type *fs); -// TODO: doxigen comment. -mountpoint_t * get_mountpoint(const char *path); +/// @brief Unregister a new filesystem. +/// @param fs A pointer to the information concerning the filesystem. +/// @return The outcome of the operation, 0 if fails. +int vfs_unregister_filesystem(file_system_type *fs); -// TODO: doxigen comment. -mountpoint_t * get_mountpoint_from_id(int32_t mp_id); +/// @brief Given a pathname for a file, kopen() returns a file struct, used to access the file. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file is created. +/// @return Returns a file struct, or NULL. +vfs_file_t *vfs_open(const char *pathname, int flags, mode_t mode); -/// @brief A path is extracted from the relative path, excluding the -/// mountpoint. -/// @param mp_id Id of the mountpoint point of the file. -/// @param path Path to the file to be opened. -/// @return Path without the mountpoint part. -int get_relative_path(uint32_t mp_id, char *path); +/// @brief Decreases the number of references to a given file, if the +/// references number reaches 0, close the file. +/// @param file A pointer to the file structure. +/// @return The result of the call. +int vfs_close(vfs_file_t *file); -/// @brief Given a path, it extracts its absolute path (starting from -/// the current one). -/// @param path Path to the file to be opened. -/// @return Error code. -int get_absolute_path(char *path); +/// @brief Read data from a file. +/// @param file The file structure used to reference a file. +/// @param buf The buffer. +/// @param offset The offset from which the function starts to read. +/// @param nbytes The number of bytes to read. +/// @return The number of read characters. +ssize_t vfs_read(vfs_file_t *file, void *buf, size_t offset, size_t nbytes); -/// @brief Dumps the list of file descriptors. -void vfs_dump(); +/// @brief Write data to a file. +/// @param file The file structure used to reference a file. +/// @param buf The buffer. +/// @param offset The offset from which the function starts to write. +/// @param nbytes The number of bytes to write. +/// @return The number of written characters. +ssize_t vfs_write(vfs_file_t *file, void *buf, size_t offset, size_t nbytes); + +/// @brief Repositions the file offset inside a file. +/// @param file The file for which we reposition the offest. +/// @param offset The offest to use for the operation. +/// @param whence The type of operation. +/// @return Upon successful completion, returns the resulting offset +/// location as measured in bytes from the beginning of the file. On +/// error, the value (off_t) -1 is returned and errno is set to +/// indicate the error. +off_t vfs_lseek(vfs_file_t *file, off_t offset, int whence); + +/// Provide access to the directory entries. +/// @param file The directory for which we accessing the entries. +/// @param dirp The buffer where de data should be placed. +/// @param off The offset from which we start reading the entries. +/// @param count The size of the buffer. +/// @return On success, the number of bytes read is returned. On end of +/// directory, 0 is returned. On error, -1 is returned, and errno is set +/// appropriately. +int vfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t off, size_t count); + +/// @brief Perform the I/O control operation specified by REQUEST on FD. +/// One argument may follow; its presence and type depend on REQUEST. +/// @param file The file for which we are executing the operations. +/// @param request The device-dependent request code +/// @param data An untyped pointer to memory. +/// @return Return value depends on REQUEST. Usually -1 indicates error. +int vfs_ioctl(vfs_file_t *file, int request, void *data); + +/// @brief Delete a name and possibly the file it refers to. +/// @param path The path to the file. +/// @return On success, zero is returned. On error, -1 is returned, and +/// errno is set appropriately. +int vfs_unlink(const char *path); + +/// @brief Creates a new directory at the given path. +/// @param path The path of the new directory. +/// @param mode The permission of the new directory. +/// @return Returns a negative value on failure. +int vfs_mkdir(const char *path, mode_t mode); + +/// @brief Removes the given directory. +/// @param path The path to the directory to remove. +/// @return Returns a negative value on failure. +int vfs_rmdir(const char *path); + +/// @brief Stat the file at the given path. +/// @param path Path to the file for which we are retrieving the statistics. +/// @param buf Buffer where we are storing the statistics. +/// @return 0 on success, a negative number if fails and errno is set. +int vfs_stat(const char *path, stat_t *buf); + +/// @brief Stat the given file. +/// @param file Pointer to the file for which we are retrieving the statistics. +/// @param buf Buffer where we are storing the statistics. +/// @return 0 on success, a negative number if fails and errno is set. +int vfs_fstat(vfs_file_t *file, stat_t *buf); + +/// @brief Mount a file system to the specified path. +/// @param path Path where we want to map the filesystem. +/// @param fs_root Root node of the filesystem. +/// @return 1 on success, 0 on fail. +/// @details +/// For example, if we have an EXT2 filesystem with a root node +/// of ext2_root and we want to mount it to /, we would run +/// vfs_mount("/", ext2_root); - or, if we have a procfs node, +/// we could mount that to /dev/procfs. Individual files can also +/// be mounted. +int vfs_mount(const char *path, vfs_file_t *fs_root); + +/// @brief Mount the path as a filesystem of the given type. +/// @param type The type of filesystem +/// @param path The path to where it should be mounter. +/// @param args The arguments passed to the filesystem mount callback. +/// @return 0 on success, a negative number if fails and errno is set. +int do_mount(const char *type, const char *path, const char *args); + +/// @brief Locks the access to the given file. +/// @param file The file to lock. +void vfs_lock(vfs_file_t *file); + +/// @brief Extends the file descriptor list for the given task. +/// @param task The task for which we extend the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_extend_task_fd_list(struct task_struct *task); + +/// @brief Initialize the file descriptor list for the given task. +/// @param task The task for which we initialize the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_init_task(struct task_struct *task); + +/// @brief Duplicate the file descriptor list of old_task into new_task. +/// @param new_task The task where we clone the file descriptor list. +/// @param old_task The task from which we clone the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_dup_task(struct task_struct *new_task, struct task_struct *old_task); + +/// @brief Destroy the file descriptor list for the given task. +/// @param task The task for which we destroy the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_destroy_task(struct task_struct *task); diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index dbbb06e..ba47810 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -1,136 +1,151 @@ /// MentOS, The Mentoring Operating system project /// @file vfs_types.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "stat.h" -#include "kernel.h" -#include "dirent.h" +#include "list_head.h" +#include "sys/dirent.h" +#include "bits/stat.h" +#include "stdint.h" -#define PATH_SEPARATOR '/' -#define PATH_SEPARATOR_STRING "/" -#define PATH_UP ".." -#define PATH_DOT "." +#define PATH_SEPARATOR '/' ///< The character used as path separator. +#define PATH_SEPARATOR_STRING "/" ///< The string used as path separator. +#define PATH_UP ".." ///< The path to the parent. +#define PATH_DOT "." ///< The path to the current directory. -/// Identifies a file. -#define FS_FILE 0x01U -/// Identifies a directory. -#define FS_DIRECTORY 0x02U -/// Identifies a character devies. -#define FS_CHARDEVICE 0x04U -/// Identifies a block devies. -#define FS_BLOCKDEVICE 0x08U -/// Identifies a pipe. -#define FS_PIPE 0x10U -/// Identifies a symbolic link. -#define FS_SYMLINK 0x20U -/// Identifies a mount-point. -#define FS_MOUNTPOINT 0x40U - -/// Function used to open a directory. -typedef DIR *(*opendir_callback)(const char *); - -/// Function used to close a directory. -typedef int (*closedir_callback)(DIR *); +/// Forward declaration of the VFS file. +typedef struct vfs_file_t vfs_file_t; /// Function used to create a directory. -typedef int (*mkdir_callback)(const char *, mode_t); - +typedef int (*vfs_mkdir_callback)(const char *, mode_t); /// Function used to remove a directory. -typedef int (*rmdir_callback)(const char *); - -/// Function used to read the next entry of a directory. -typedef dirent_t *(*readdir_callback)(DIR *); - -/// Function used to open a file. -typedef int (*open_callback)(const char *, int, ...); - +typedef int (*vfs_rmdir_callback)(const char *); +/// Function used to read the entries of a directory. +typedef int (*vfs_getdents_callback)(vfs_file_t *, dirent_t *, off_t, size_t); +/// Function used to open a file (or directory). +typedef vfs_file_t *(*vfs_open_callback)(const char *, int, mode_t); /// Function used to remove a file. -typedef int (*remove_callback)(const char *); - +typedef int (*vfs_unlink_callback)(const char *); /// Function used to close a file. -typedef int (*close_callback)(int); - +typedef int (*vfs_close_callback)(vfs_file_t *); /// Function used to read from a file. -typedef ssize_t (*read_callback)(int, char *, size_t); - +typedef ssize_t (*vfs_read_callback)(vfs_file_t *, char *, off_t, size_t); /// Function used to write inside a file. -typedef ssize_t (*write_callback)(int, const void *, size_t); - +typedef ssize_t (*vfs_write_callback)(vfs_file_t *, const void *, off_t, size_t); +/// Function used to reposition the file offset inside a file. +typedef off_t (*vfs_lseek_callback)(vfs_file_t *, off_t, int); /// Function used to stat fs entries. -typedef int (*stat_callback)(const char *, stat_t *); +typedef int (*vfs_stat_callback)(const char *, stat_t *); +/// Function used to stat files. +typedef int (*vfs_fstat_callback)(vfs_file_t *, stat_t *); +/// Function used to perform ioctl on files. +typedef int (*vfs_ioctl_callback)(vfs_file_t *, int, void *); -/// @brief Set of functions used to perform operations on directories. -typedef struct directory_operations_t { - /// Identifies a mount-point. - opendir_callback opendir_f; - /// Closes a directory. - closedir_callback closedir_f; - /// Creates a directory. - mkdir_callback mkdir_f; - /// Removes a directory. - rmdir_callback rmdir_f; - /// Read next entry inside the directory. - readdir_callback readdir_f; -} directory_operations_t; +/// @brief Filesystem information. +typedef struct file_system_type { + /// Name of the filesystem. + const char *name; + /// Flags of the filesystem. + int fs_flags; + /// Mount function. + vfs_file_t *(*mount)(const char *, const char *); +} file_system_type; + +/// @brief Set of functions used to perform operations on filesystem. +typedef struct vfs_sys_operations_t { + /// Creates a directory. + vfs_mkdir_callback mkdir_f; + /// Removes a directory. + vfs_rmdir_callback rmdir_f; + /// Stat function. + vfs_stat_callback stat_f; +} vfs_sys_operations_t; /// @brief Set of functions used to perform operations on files. -typedef struct super_node_operations_t { - /// Open a file. - open_callback open_f; - /// Remove a file. - remove_callback remove_f; - /// Close a file. - close_callback close_f; - /// Read from a file. - read_callback read_f; - /// Write inside a file. - write_callback write_f; -} super_node_operations_t; - -/// @brief Stat operations. -typedef struct stat_operations_t { - /// Stat function. - stat_callback stat_f; -} stat_operations_t; +typedef struct vfs_file_operations_t { + /// Open a file. + vfs_open_callback open_f; + /// Remove a file. + vfs_unlink_callback unlink_f; + /// Close a file. + vfs_close_callback close_f; + /// Read from a file. + vfs_read_callback read_f; + /// Write inside a file. + vfs_write_callback write_f; + /// Reposition the file offset inside a file. + vfs_lseek_callback lseek_f; + /// Stat the file. + vfs_fstat_callback stat_f; + /// Perform ioctl on file. + vfs_ioctl_callback ioctl_f; + /// Read entries inside the directory. + vfs_getdents_callback getdents_f; +} vfs_file_operations_t; /// @brief Data structure that contains information about the mounted filesystems. -typedef struct mountpoint_t { - /// The id of the mountpoint. - int32_t mp_id; - /// Name of the mountpoint. - char mountpoint[NAME_MAX]; - /// Maschera dei permessi. - unsigned int pmask; - /// User ID. - unsigned int uid; - /// Group ID. - unsigned int gid; - /// Starting address of the FileSystem. - unsigned int start_address; - /// Ending address of the FileSystem. - unsigned int end_address; - /// Device ID. - int dev_id; - /// Operations on files. - super_node_operations_t operations; - /// Operations on directories. - directory_operations_t dir_op; - /// Stat operations. - stat_operations_t stat_op; -} mountpoint_t; +struct vfs_file_t { + /// The filename. + char name[NAME_MAX]; + /// Device object (optional). + void *device; + /// The permissions mask. + uint32_t mask; + /// The owning user. + uint32_t uid; + /// The owning group. + uint32_t gid; + /// Flags (node type, etc). + uint32_t flags; + /// Inode number. + uint32_t ino; + /// Size of the file, in byte. + uint32_t length; + /// Used to keep track which fs it belongs to. + uint32_t impl; + /// Flags passed to open (read/write/append, etc.) + uint32_t open_flags; + /// Number of file descriptors associated with this file + int count; + /// Accessed (time). + uint32_t atime; + /// Modified (time). + uint32_t mtime; + /// Created (time). + uint32_t ctime; + /// Generic system operations. + vfs_sys_operations_t *sys_operations; + /// Files operations. + vfs_file_operations_t *fs_operations; + /// Offset for read operations. + size_t f_pos; + /// The number of links. + uint32_t nlink; + /// List to hold all active files associated with a specific entry in a filesystem. + list_head siblings; + /// TODO: Comment. + int32_t refcount; +}; + +/// @brief A structure that represents an instance of a filesystem, i.e., a mounted filesystem. +typedef struct super_block_t { + /// Name of the superblock. + char name[NAME_MAX]; + /// Pointer to the root file of the given filesystem. + vfs_file_t *root; + /// Pointer to the information regarding the filesystem. + file_system_type *type; + /// List to hold all active mounting points. + list_head mounts; +} super_block_t; /// @brief Data structure containing information about an open file. -typedef struct file_descriptor_t { - /// The descriptor of the internal file of the FileSystem. - int fs_spec_id; - /// The id of the mountpoint where the. - int mountpoint_id; - /// Offset for file reading, for the next read. - int offset; - /// Flags for file opening modes. - int flags_mask; -} file_descriptor_t; +typedef struct vfs_file_descriptor_t { + /// the underlying file structure + vfs_file_t *file_struct; + /// Flags for file opening modes. + int flags_mask; +} vfs_file_descriptor_t; diff --git a/mentos/inc/hardware/cmd_cpuid.h b/mentos/inc/hardware/cmd_cpuid.h deleted file mode 100644 index 742a698..0000000 --- a/mentos/inc/hardware/cmd_cpuid.h +++ /dev/null @@ -1,86 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cpuid.h -/// @brief CPUID definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdint.h" -#include "kernel.h" - -/// Dimension of the exc flags. -#define ECX_FLAGS_SIZE 24 - -/// Dimension of the edx flags. -#define EDX_FLAGS_SIZE 32 - -/// @brief Contains the information concerning the CPU. -typedef struct cpuinfo_t { - /// The name of the vendor. - char cpu_vendor[13]; - /// The type of the CPU. - char *cpu_type; - /// The family of the CPU. - uint32_t cpu_family; - /// The model of the CPU. - uint32_t cpu_model; - /// Identifier for individual cores when the CPU is interrogated by the - /// CPUID instruction. - uint32_t apic_id; - /// Ecx flags. - uint32_t cpuid_ecx_flags[ECX_FLAGS_SIZE]; - /// Edx flags. - uint32_t cpuid_edx_flags[EDX_FLAGS_SIZE]; - // TODO: doxygen comment. - int is_brand_string; - // TODO: doxygen comment. - char *brand_string; -} cpuinfo_t; - -/// This will be populated with the information concerning the CPU. -cpuinfo_t sinfo; - -/// @brief Main CPUID procedure. -/// @param cpuinfo Structure to fill with CPUID information. -void get_cpuid(cpuinfo_t *cpuinfo); - -/// @brief Actual CPUID call. -/// @param registers The registers to fill with the result of the call. -void call_cpuid(register_t *registers); - -/// @brief Extract vendor string. -/// @param cpuinfo The struct containing the CPUID infos. -/// @param registers The registers. -void cpuid_write_vendor(cpuinfo_t *cpuinfo, register_t *registers); - -// TODO: doxygen documentation. -/// @brief CPUID is called with EAX=1 -/// EAX contains Type, Family, Model and Stepping ID -/// EBX contains the Brand Index if supported, and the APIC ID -/// ECX/EDX contains feature information -/// @param cpuinfo -/// @param registers -void cpuid_write_proctype(cpuinfo_t *cpuinfo, register_t *registers); - -/// @brief EAX=1, ECX contains a list of supported features. -void cpuid_feature_ecx(cpuinfo_t *, uint32_t); - -/// @brief EAX=1, EDX contains a list of supported features. -void cpuid_feature_edx(cpuinfo_t *, uint32_t); - -// TODO: doxygen documentation. -/// @brief Extract single byte from a register. -/// @param reg -/// @param position -/// @param value -/// @return -uint32_t cpuid_get_byte(const uint32_t reg, const uint32_t position, - const uint32_t value); - -/// @brief Index of brand strings. -char *cpuid_brand_index(register_t *); - -/// @brief Brand string is contained in EAX, EBX, ECX and EDX. -char *cpuid_brand_string(register_t *); diff --git a/mentos/inc/hardware/cpuid.h b/mentos/inc/hardware/cpuid.h new file mode 100644 index 0000000..65313ff --- /dev/null +++ b/mentos/inc/hardware/cpuid.h @@ -0,0 +1,96 @@ +/// MentOS, The Mentoring Operating system project +/// @file cpuid.h +/// @brief CPUID definitions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" +#include "kernel.h" + +/// Dimension of the exc flags. +#define ECX_FLAGS_SIZE 24 + +/// Dimension of the edx flags. +#define EDX_FLAGS_SIZE 32 + +/// @brief Contains the information concerning the CPU. +typedef struct cpuinfo_t { + /// The name of the vendor. + char cpu_vendor[13]; + /// The type of the CPU. + char *cpu_type; + /// The family of the CPU. + uint32_t cpu_family; + /// The model of the CPU. + uint32_t cpu_model; + /// Identifier for individual cores when the CPU is interrogated by the + /// CPUID instruction. + uint32_t apic_id; + /// Ecx flags. + uint32_t cpuid_ecx_flags[ECX_FLAGS_SIZE]; + /// Edx flags. + uint32_t cpuid_edx_flags[EDX_FLAGS_SIZE]; + /// TODO: Comment. + int is_brand_string; + /// TODO: Comment. + char *brand_string; +} cpuinfo_t; + +/// This will be populated with the information concerning the CPU. +cpuinfo_t sinfo; + +/// @brief Main CPUID procedure. +/// @param cpuinfo Structure to fill with CPUID information. +void get_cpuid(cpuinfo_t *cpuinfo); + +/// @brief Actual CPUID call. +/// @param registers The registers to fill with the result of the call. +void call_cpuid(pt_regs *registers); + +/// @brief Extract vendor string. +/// @param cpuinfo The struct containing the CPUID infos. +/// @param registers The registers. +void cpuid_write_vendor(cpuinfo_t *cpuinfo, pt_regs *registers); + +// TODO: doxygen documentation. +/// @brief +/// @param cpuinfo +/// @param registers +/// @details +/// CPUID is called with EAX=1 +/// EAX contains Type, Family, Model and Stepping ID +/// EBX contains the Brand Index if supported, and the APIC ID +/// ECX/EDX contains feature information +void cpuid_write_proctype(cpuinfo_t *cpuinfo, pt_regs *registers); + +// TODO: doxygen documentation. +/// @brief EAX=1, ECX contains a list of supported features. +/// @param cpuinfo +/// @param ecx +void cpuid_feature_ecx(cpuinfo_t *cpuinfo, uint32_t ecx); + +// TODO: doxygen documentation. +/// @brief EAX=1, EDX contains a list of supported features. +/// @param cpuinfo +/// @param edx +void cpuid_feature_edx(cpuinfo_t *cpuinfo, uint32_t edx); + +// TODO: doxygen documentation. +/// @brief Extract single byte from a register. +/// @param reg +/// @param position +/// @param value +/// @return +uint32_t cpuid_get_byte(uint32_t reg, uint32_t position, uint32_t value); + +/// @brief Index of brand strings. TODO: Document +/// @param f Stack frame. +/// @return The brand string. +char *cpuid_brand_index(pt_regs *f); + +/// @brief Brand string is contained in EAX, EBX, ECX and EDX. +/// @param f Stack frame. +/// @return The brand string. +char *cpuid_brand_string(pt_regs *f); diff --git a/mentos/inc/hardware/pic8259.h b/mentos/inc/hardware/pic8259.h index 7d409c8..068c902 100644 --- a/mentos/inc/hardware/pic8259.h +++ b/mentos/inc/hardware/pic8259.h @@ -1,20 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file pic8259.h /// @brief Pic8259 definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "idt.h" -#include "kheap.h" -#include "debug.h" -#include "stddef.h" -#include "kernel.h" -#include "bitops.h" -#include "port_io.h" -#include "irqflags.h" -#include "scheduler.h" +#include "stdint.h" /// The total number of IRQs. #define IRQ_NUM 16 diff --git a/mentos/inc/hardware/timer.h b/mentos/inc/hardware/timer.h index a3a1860..459fe61 100644 --- a/mentos/inc/hardware/timer.h +++ b/mentos/inc/hardware/timer.h @@ -1,85 +1,211 @@ /// MentOS, The Mentoring Operating system project /// @file timer.h /// @brief Programmable Interval Timer (PIT) definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "kernel.h" #include "stdint.h" +#include "list_head.h" +#include "spinlock.h" +#include "process.h" +#include "time.h" -/// @defgroup picregs Programmable Interval Timer Registers -/// @brief The list of registers used to set the PIT. -/// @{ +/// This enables the dynamic timer system use an hierarchical timing wheel, +/// an optimized data structure that allows O(1) insertion, O(1) deletion and +/// ammortized O(1) tick update. +#define ENABLE_REAL_TIMER_SYSTEM -/// Channel 0 data port (read/write). -#define PIT_DATAREG0 0x40 +/// This enables the system dump tvec_base timer vectors content on +/// the console. +#define ENABLE_REAL_TIMER_SYSTEM_DUMP -/// Channel 1 data port (read/write). -#define PIT_DATAREG1 0x41 - -/// Channel 2 data port (read/write). -#define PIT_DATAREG2 0x42 - -/// Mode/Command register (write only, a read is ignored). -#define PIT_COMREG 0x43 - -/// @} - -/// @brief Frequency divider value (1.193182 MHz). -#define PIT_DIVISOR 1193180 - -/// @brief Command used to configure the PIT. -/// @details -/// 0x36 = 00110110B. -/// Channel | 00 | Select Channel 0. -/// Access mode | 11 | lobyte/hibyte. -/// Operating mode | 011 | Mode 3 (square wave generator). -/// BCD/Binary mode | 0 | 16-bit binary. -#define PIT_CONFIGURATION 0x36 - -#define PIT_MASK 0xFF - -/// @brief Pointer to a functionality to wake up. -typedef void (*wakeup_callback_t)(); - -/// @brief Holds the information about a wake-up functionality. -typedef struct wakeup_info { - /// Pointer to the functionality. - wakeup_callback_t func; - /// The tick, in the future, when the functionality must be triggered. - __volatile__ uint32_t wakeup_at_jiffy; - /// The period in seconds. - uint32_t period; -} wakeup_info_t; +/// Counts down in real (i.e., wall clock) time. +#define ITIMER_REAL 0 +/// Counts down against the user-mode CPU time consumed by the process. +#define ITIMER_VIRTUAL 1 +/// This timer counts down against the total (i.e., both user and system) CPU +/// time consumed by the process. +#define ITIMER_PROF 2 /// @brief Handles the timer. -/// @details In this case, it's very simple: We -/// increment the 'timer_ticks' variable every time the -/// timer fires. By default, the timer fires 18.222 times -/// per second. Why 18.222Hz? Some engineer at IBM must've -/// been smoking something funky -void timer_handler(pt_regs *reg); +/// @param f The interrupt stack frame. +/// @details +/// In this case, it's very simple: We increment the 'timer_ticks' variable +/// every time the timer fires. By default, the timer fires 18.222 times +/// per second. Why 18.222Hz? Some engineer at IBM must've been smoking +/// something funky. +void timer_handler(pt_regs *f); /// @brief Sets up the system clock by installing the timer handler into IRQ0. void timer_install(); -/// @brief Returns the number of ticks since the system is running. -uint64_t timer_get_ticks(); +/// @brief Returns the number of seconds since the system started its execution. +/// @return Value in seconds. +uint64_t timer_get_seconds(); -/// @brief Returns the number of ticks since the system is running. -uint64_t timer_get_subticks(); - -/// @brief Registers a function which will be waken up at each tick. -/// @param func The functionality which must be triggered. -/// @param period The period in second. -void timer_register_wakeup_call(wakeup_callback_t func, uint32_t period); - -/// @brief Makes the process sleep for the given ammount of time. -/// @param seconds The ammount of seconds. -void sleep(unsigned int seconds); +/// @brief Returns the number of ticks since the system started its execution. +/// @return Value in ticks. +unsigned long timer_get_ticks(); /// @brief Allows to set the timer phase to the given frequency. /// @param hz The frequency to set. void timer_phase(const uint32_t hz); + +// =============================================================================== +// Per-CPU timer vectors + +#ifdef ENABLE_REAL_TIMER_SYSTEM + +/// Number of bits for the normal timer vector. +#define TVN_BITS 6 +/// Number of bits for the root timer vector. +#define TVR_BITS 8 +/// Number of headers in a normal timer vector. +#define TVN_SIZE (1 << TVN_BITS) +/// Number of headers in a root timer vector. +#define TVR_SIZE (1 << TVR_BITS) +/// A mask with all 1s for the normal timer vector (0b00111111) +#define TVN_MASK (TVN_SIZE - 1) +/// A mask with all 1s for the root timer vector (0b11111111) +#define TVR_MASK (TVR_SIZE - 1) +/// A shift used to calculate a timer position inside the tvec_base structure +#define TIMER_TICKS_BITS(tv) (TVR_BITS + TVN_BITS * (tv)) +/// Expiration ticks of timer based on position inside tvec_base structure +#define TIMER_TICKS(tv) (1 << TIMER_TICKS_BITS(tv)) + +/// @brief Root timer vector. +typedef struct timer_vec_root { + /// Array of lists of timers + list_head vec[TVR_SIZE]; +} timer_vec_root; + +/// @brief Normal timer vector. +typedef struct timer_vec { + /// Array of lists of timers + list_head vec[TVN_SIZE]; +} timer_vec; + +#endif + +/// @brief Contains all the timers of a single CPU +typedef struct tvec_base_s { + /// Lock for the timer data structure + spinlock_t lock; + /// Points to the dynamic timer that is currently handled by the CPU. + struct timer_list *running_timer; +#ifdef ENABLE_REAL_TIMER_SYSTEM + /// The earliest expiration time of the dynamic timers yet to be checked + unsigned long timer_ticks; + + /// Lists of timers that will expires in the next 255 ticks + struct timer_vec_root tv1; + /// Lists of timers that will expires in the next 2^14 - 1 ticks + struct timer_vec tv2; + /// Lists of timers that will expires in the next 2^20 - 1 ticks + struct timer_vec tv3; + /// Lists of timers that will expires in the next 2^26 - 1 ticks + struct timer_vec tv4; + /// Lists of timers with extremely large expires fields (2^32 - 1 ticks) + struct timer_vec tv5; + +#else + /// List of all the timers + struct list_head list; +#endif + +} tvec_base_t; + +/// @brief Represents the request to execute a function in the future, also +/// known as timer. +struct timer_list { + /// Protects the access to the timer. + spinlock_t lock; + /// Lists of timers are mantained using the list_head. + struct list_head entry; + /// Ticks value when the timer has to expire + unsigned long expires; + /// Functions to be executed when the timer expires + void (*function)(unsigned long); + /// Custom data to be passed to the timer function + unsigned long data; + /// Pointer to the structure containing all the other related timers. + tvec_base_t *base; +}; + +/// @brief Initialize dynamic timer system +void dynamic_timers_install(); + +/// @brief Initializes a new timer struct. +/// @param timer The timer to initialize. +void init_timer(struct timer_list *timer); + +/// @brief Updates the timer data structures +void run_timer_softirq(); + +/// @brief Add a new timer to the current CPU. +/// @param timer The timer to add. +void add_timer(struct timer_list *timer); + +/// @brief Removes a timer from the current CPU. +/// @param timer The timer to remove. +void del_timer(struct timer_list *timer); + +/// @brief Updates and executes dynamics timers +void run_timer_softirq(); + +/// @brief Suspends the execution of the calling thread. +/// @param req The amount of time we want to sleep. +/// @param rem The remaining time we did not sleep. +/// @return If the call is interrupted by a signal handler, nanosleep() +/// returns -1, sets errno to EINTR, and writes the remaining time +/// into the structure pointed to by rem unless rem is NULL. +/// @details +/// The execution is suspended until either at least the time specified +/// in *req has elapsed, or the delivery of a signal that triggers the +/// invocation of a handler in the calling thread or that terminates +/// the process. +int sys_nanosleep(const timespec *req, timespec *rem); + +/// @brief Send signal to calling thread after desired seconds. +/// @param seconds The number of seconds in the interval +/// @return the number of seconds remaining until any previously scheduled +/// alarm was due to be delivered, or zero if there was no previously +/// scheduled alarm. +int sys_alarm(int seconds); + +/// @brief Rappresents a time value. +struct timeval { + time_t tv_sec; ///< Seconds. + time_t tv_usec; ///< Microseconds. +}; + +/// @brief Rappresents a time interval. +struct itimerval { + struct timeval it_interval; ///< Next value. + struct timeval it_value; ///< Current value. +}; + +/// @brief Fills the structure pointed to by curr_value with the current setting for the timer specified by which. +/// @param which The time domain (i.e., ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF) +/// @param curr_value There the values must be stored. +/// @return Zero on success, or a negative value indicating the error. +int sys_getitimer(int which, struct itimerval *curr_value); + +/// @brief Arms or disarms the timer specified by which, by setting the timer +/// to the value specified by new_value. +/// @details +/// The system provides each process with three interval timers, each +/// decrementing in a distinct time domain. When any timer expires, +/// a signal is sent to the process, and the timer (potentially) restarts. +/// @param which The time domain (i.e., ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF) +/// @param new_value The new value to set. +/// @param old_value Where the old value must be stored. +/// @return Zero on success, or a negative value indicating the error. +int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); + +/// @brief Update the profiling timer and generate SIGPROF if it has expired. +/// @param proc The process for which we must update the profiling. +void update_process_profiling_timer(task_struct *proc); \ No newline at end of file diff --git a/mentos/inc/io/mm_io.h b/mentos/inc/io/mm_io.h index 6f3bc3a..152886e 100644 --- a/mentos/inc/io/mm_io.h +++ b/mentos/inc/io/mm_io.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file mm_io.h /// @brief Memory Mapped IO functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -9,19 +9,31 @@ #include "stdint.h" /// @brief Reads a 8-bit value from the given address. +/// @param addr The address from which we read. +/// @return The content read from the address. uint8_t in_memb(uint32_t addr); /// @brief Reads a 16-bit value from the given address. +/// @param addr The address from which we read. +/// @return The content read from the address. uint16_t in_mems(uint32_t addr); /// @brief Reads a 32-bit value from the given address. +/// @param addr The address from which we read. +/// @return The content read from the address. uint32_t in_meml(uint32_t addr); /// @brief Writes a 8-bit value at the given address. +/// @param addr The address where we want to write. +/// @param value The value we want to write. void out_memb(uint32_t addr, uint8_t value); /// @brief Writes a 16-bit value at the given address. +/// @param addr The address where we want to write. +/// @param value The value we want to write. void out_mems(uint32_t addr, uint16_t value); /// @brief Writes a 32-bit value at the given address. +/// @param addr The address where we want to write. +/// @param value The value we want to write. void out_meml(uint32_t addr, uint32_t value); diff --git a/mentos/inc/io/port_io.h b/mentos/inc/io/port_io.h index 270ad71..1ec56dd 100644 --- a/mentos/inc/io/port_io.h +++ b/mentos/inc/io/port_io.h @@ -1,43 +1,51 @@ /// MentOS, The Mentoring Operating system project /// @file port_io.h /// @brief Byte I/O on ports prototypes. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" -/// @brief Used for reading from the I/O ports. +/// @brief Used for reading from the I/O ports. /// @param port The input port. -/// @return The read value. +/// @return The read value. uint8_t inportb(uint16_t port); -/// @brief Used for reading 2 bytes from the I/O ports. +/// @brief Used for reading 2 bytes from the I/O ports. /// @param port The input port. -/// @return The read value. +/// @return The read value. uint16_t inports(uint16_t port); +/// @brief Read from the given port and store what we read in `data`. +/// @param port The input port. +/// @param data The buffer where we store what we read. +/// @param size The dimension of the buffer. void inportsm(uint16_t port, uint8_t *data, unsigned long size); -/// @brief Used for reading 4 bytes from the I/O ports. +/// @brief Used for reading 4 bytes from the I/O ports. /// @param port The input port. -/// @return The read value. +/// @return The read value. uint32_t inportl(uint16_t port); -/// @brief Use this to write to I/O ports to send bytes to devices. +/// @brief Use this to write to I/O ports to send bytes to devices. /// @param port The output port. /// @param data The data to write. void outportb(uint16_t port, uint8_t data); -/// @brief Use this to write to I/O ports to send 2 bytes to devices. +/// @brief Use this to write to I/O ports to send 2 bytes to devices. /// @param port The output port. /// @param data The data to write. void outports(uint16_t port, uint16_t data); +/// @brief Write to the given port what it is stored inside `data`. +/// @param port The output port. +/// @param data The buffer where we have the data. +/// @param size The dimension of the buffer. void outportsm(uint16_t port, uint8_t *data, uint16_t size); -/// @brief Use this to write to I/O ports to send 4 bytes to devices. +/// @brief Use this to write to I/O ports to send 4 bytes to devices. /// @param port The output port. /// @param data The data to write. void outportl(uint16_t port, uint32_t data); diff --git a/mentos/inc/io/proc_modules.h b/mentos/inc/io/proc_modules.h new file mode 100644 index 0000000..5725d6c --- /dev/null +++ b/mentos/inc/io/proc_modules.h @@ -0,0 +1,17 @@ +/// MentOS, The Mentoring Operating system project +/// @file proc_modules.h +/// @brief Contains functions for managing procfs filesystems. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "vfs.h" + +/// @brief Initialize the procfs video files. +/// @return 0 on success, 1 on failure. +int procv_module_init(); + +/// @brief Initialize the procfs system files. +/// @return 0 on success, 1 on failure. +int procs_module_init(); diff --git a/mentos/inc/io/vga/vga.h b/mentos/inc/io/vga/vga.h new file mode 100644 index 0000000..db8dfd9 --- /dev/null +++ b/mentos/inc/io/vga/vga.h @@ -0,0 +1,24 @@ + +#pragma once + +void vga_initialize(); +void vga_finalize(); + +int vga_is_enabled(); + +int vga_width(); +int vga_height(); + +void vga_clear_screen(); + +void vga_draw_char(unsigned int x, unsigned int y, unsigned char c, unsigned char color); + +void vga_draw_string(int x, int y, char *str, unsigned char color); + +void vga_draw_line(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned char color); + +void vga_draw_rectangle(unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, unsigned char fill); + +void vga_draw_circle(unsigned int xc, unsigned int yc, unsigned int r, unsigned char col); + +void vga_draw_triangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int x3, unsigned int y3, unsigned char col); \ No newline at end of file diff --git a/mentos/inc/io/vga/vga_font.h b/mentos/inc/io/vga/vga_font.h new file mode 100644 index 0000000..3108658 --- /dev/null +++ b/mentos/inc/io/vga/vga_font.h @@ -0,0 +1,917 @@ +/// MentOS, The Mentoring Operating system project +/// @file vga_font.h +/// @brief VGA fonts. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +unsigned char arr_8x16_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, 0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0xFF, 0xDB, 0xFF, 0xFF, 0xC3, 0xE7, 0xFF, 0xFF, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0xE7, 0xE7, 0xE7, 0x99, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x1E, 0x0E, 0x1A, 0x32, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x30, 0x30, 0x70, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x63, 0x63, 0x67, 0xE7, 0xE6, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, 0xE7, 0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0xFE, 0x3E, 0x1E, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6C, 0xFE, 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7C, 0x7C, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x7C, 0x7C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18, 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xCE, 0xD6, 0xD6, 0xE6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x0E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE6, 0x66, 0x6C, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x6C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0x86, 0x0C, 0x18, 0x30, 0x60, 0xC2, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0xDC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, + 0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x62, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x0C, 0x06, 0x7C, 0x00, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x38, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x0C, 0x06, 0x3C, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3C, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC6, 0xC6, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6C, 0x38, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xFE, 0x66, 0x60, 0x7C, 0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x76, 0x36, 0x7E, 0xD8, 0xD8, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x6C, 0xCC, 0xCC, 0xFE, 0xCC, 0xCC, 0xCC, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0x78, 0x00, + 0x00, 0xC6, 0xC6, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC4, 0xCC, 0xDE, 0xCC, 0xCC, 0xCC, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0E, 0x1B, 0x18, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0C, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xDC, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xC0, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, 0x60, 0xCE, 0x93, 0x06, 0x0C, 0x1F, 0x00, 0x00, + 0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xCE, 0x9A, 0x3F, 0x06, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0xD8, 0xD8, 0xD8, 0xDC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC6, 0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFE, 0xC6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7E, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x6C, 0x6C, 0x6C, 0xEE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x30, 0x18, 0x0C, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0xDB, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7E, 0xCF, 0xDB, 0xF3, 0x7E, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x30, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x6C, 0x3C, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xD8, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x98, 0x30, 0x60, 0xC8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char arr_8x8_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, + 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x92, 0x10, 0x7C, + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, + 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, + 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, + 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, + 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, + 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, + 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, + 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00, + 0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0x86, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, + 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, + 0x18, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x18, 0x00, + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, + 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0x7C, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, + 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, + 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, + 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, + 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, + 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3A, 0x00, + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, + 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, + 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0x7C, 0x0E, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, + 0x7C, 0xC6, 0xE0, 0x78, 0x0E, 0xC6, 0x7C, 0x00, + 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, + 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xD6, 0x00, + 0x00, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, + 0x00, 0x00, 0xDC, 0x76, 0x62, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x7C, 0xC0, 0x70, 0x1C, 0xF8, 0x00, + 0x10, 0x30, 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, + 0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00, + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, + 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x0C, 0x06, 0x7C, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x1C, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x7E, 0x81, 0x3C, 0x06, 0x3E, 0x66, 0x3B, 0x00, + 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0x78, 0x0C, 0x38, + 0x7E, 0x81, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xE0, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xCC, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7C, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, + 0xE0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xC6, 0x10, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xCC, 0xFC, 0xCC, 0x00, + 0x1C, 0x00, 0xFC, 0x60, 0x78, 0x60, 0xFC, 0x00, + 0x00, 0x00, 0x7F, 0x0C, 0x7F, 0xCC, 0x7F, 0x00, + 0x3E, 0x6C, 0xCC, 0xFE, 0xCC, 0xCC, 0xCE, 0x00, + 0x78, 0x84, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0x84, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xE0, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00, + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x18, 0x18, 0x7E, 0xC0, 0xC0, 0x7E, 0x18, 0x18, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0xE6, 0xFC, 0x00, + 0xCC, 0xCC, 0x78, 0x30, 0xFC, 0x30, 0xFC, 0x30, + 0xF8, 0xCC, 0xCC, 0xFA, 0xC6, 0xCF, 0xC6, 0xC3, + 0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0xD8, 0x70, + 0x1C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xF8, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0x00, + 0xFC, 0x00, 0xCC, 0xEC, 0xFC, 0xDC, 0xCC, 0x00, + 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x00, 0x00, + 0xC6, 0xCC, 0xD8, 0x36, 0x6B, 0xC2, 0x84, 0x0F, + 0xC3, 0xC6, 0xCC, 0xDB, 0x37, 0x6D, 0xCF, 0x03, + 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0xDB, 0xF6, 0xDB, 0x6F, 0xDB, 0x7E, 0xD7, 0xED, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0xC8, 0xDC, 0x76, 0x00, + 0x00, 0x78, 0xCC, 0xF8, 0xCC, 0xF8, 0xC0, 0xC0, + 0x00, 0xFC, 0xCC, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, + 0xFC, 0xCC, 0x60, 0x30, 0x60, 0xCC, 0xFC, 0x00, + 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0xC0, + 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xFC, 0x30, 0x78, 0xCC, 0xCC, 0x78, 0x30, 0xFC, + 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x6C, 0xEE, 0x00, + 0x1C, 0x30, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x00, 0x00, + 0x06, 0x0C, 0x7E, 0xDB, 0xDB, 0x7E, 0x60, 0xC0, + 0x38, 0x60, 0xC0, 0xF8, 0xC0, 0x60, 0x38, 0x00, + 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, + 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xFC, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xFC, 0x00, + 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70, + 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0F, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, + 0x58, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, + 0x70, 0x98, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// Constant: font8x8_basic +// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) +unsigned char arr_8x8_basic_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0000 (nul) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0001 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0002 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0003 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0004 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0005 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0006 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0007 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0008 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0009 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0010 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0011 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0012 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0013 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0014 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0015 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0016 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0017 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0018 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0019 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0020 (space) + 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, // U+0021 (!) + 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0022 (") + 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, // U+0023 (#) + 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00, // U+0024 ($) + 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00, // U+0025 (%) + 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00, // U+0026 (&) + 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0027 (') + 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00, // U+0028 (() + 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00, // U+0029 ()) + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // U+002A (*) + 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00, // U+002B (+) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+002C (,) + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // U+002D (-) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+002E (.) + 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, // U+002F (/) + 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00, // U+0030 (0) + 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00, // U+0031 (1) + 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00, // U+0032 (2) + 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00, // U+0033 (3) + 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00, // U+0034 (4) + 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00, // U+0035 (5) + 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00, // U+0036 (6) + 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00, // U+0037 (7) + 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00, // U+0038 (8) + 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00, // U+0039 (9) + 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+003A (:) + 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+003B (;) + 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00, // U+003C (<) + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, // U+003D (=) + 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00, // U+003E (>) + 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00, // U+003F (?) + 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00, // U+0040 (@) + 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00, // U+0041 (A) + 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00, // U+0042 (B) + 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00, // U+0043 (C) + 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00, // U+0044 (D) + 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00, // U+0045 (E) + 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00, // U+0046 (F) + 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00, // U+0047 (G) + 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, // U+0048 (H) + 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0049 (I) + 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00, // U+004A (J) + 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00, // U+004B (K) + 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00, // U+004C (L) + 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00, // U+004D (M) + 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00, // U+004E (N) + 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, // U+004F (O) + 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00, // U+0050 (P) + 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00, // U+0051 (Q) + 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00, // U+0052 (R) + 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00, // U+0053 (S) + 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0054 (T) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00, // U+0055 (U) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0056 (V) + 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // U+0057 (W) + 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00, // U+0058 (X) + 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00, // U+0059 (Y) + 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00, // U+005A (Z) + 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00, // U+005B ([) + 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // U+005C (\) + 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00, // U+005D (]) + 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, // U+005E (^) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // U+005F (_) + 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0060 (`) + 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00, // U+0061 (a) + 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, // U+0062 (b) + 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00, // U+0063 (c) + 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00, // U+0064 (d) + 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00, // U+0065 (e) + 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00, // U+0066 (f) + 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0067 (g) + 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00, // U+0068 (h) + 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0069 (i) + 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, // U+006A (j) + 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00, // U+006B (k) + 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+006C (l) + 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // U+006D (m) + 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00, // U+006E (n) + 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00, // U+006F (o) + 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F, // U+0070 (p) + 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78, // U+0071 (q) + 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00, // U+0072 (r) + 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00, // U+0073 (s) + 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00, // U+0074 (t) + 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, // U+0075 (u) + 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0076 (v) + 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00, // U+0077 (w) + 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00, // U+0078 (x) + 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0079 (y) + 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00, // U+007A (z) + 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00, // U+007B ({) + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // U+007C (|) + 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00, // U+007D (}) + 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+007E (~) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // U+007F +}; + +unsigned char vga_font[256 * 16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; \ No newline at end of file diff --git a/mentos/inc/io/vga/vga_mode.h b/mentos/inc/io/vga/vga_mode.h new file mode 100644 index 0000000..8f7b54e --- /dev/null +++ b/mentos/inc/io/vga/vga_mode.h @@ -0,0 +1,421 @@ +/// @brief +typedef struct { + unsigned char misc; ///< 00h -- + /// @brief The Sequencer Registers. + struct { + unsigned char reset; ///< 00h -- + unsigned char clocking_mode; ///< 01h -- + unsigned char map_mask; ///< 02h -- + unsigned char character_map_select; ///< 03h -- + unsigned char sequencer_memory_mode; ///< 04h -- + } sc; + struct { + unsigned char horizontal_total; ///< 00h -- + unsigned char end_horizontal_display; ///< 01h -- + unsigned char start_horizontal_blanking; ///< 02h -- + unsigned char end_horizontal_blanking; ///< 03h -- + unsigned char start_horizontal_retrace; ///< 04h -- + unsigned char end_horizontal_retrace; ///< 05h -- + unsigned char vertical_total; ///< 06h -- + unsigned char overflow; ///< 07h -- + unsigned char preset_row_scan; ///< 08h -- + unsigned char maximum_scan_line; ///< 09h -- + unsigned char cursor_start; ///< 0Ah -- + unsigned char cursor_end; ///< 0Bh -- + unsigned char start_address_high; ///< 0Ch -- + unsigned char start_address_low; ///< 0Dh -- + unsigned char cursor_location_high; ///< 0Eh -- + unsigned char cursor_location_low; ///< 0Fh -- + unsigned char vertical_retrace_start; ///< 10h -- + unsigned char vertical_retrace_end; ///< 11h -- + unsigned char vertical_display_end; ///< 12h -- + unsigned char offset; ///< 13h -- + unsigned char underline_location; ///< 14h -- + unsigned char start_vertical_blanking; ///< 15h -- + unsigned char end_vertical_blanking; ///< 16h -- + unsigned char crtc_mode_control; ///< 17h -- + unsigned char line_compare; ///< 18h -- + } crtc; + /// @brief The Graphics Registers. + struct { + unsigned char set_reset; ///< 00h -- + unsigned char enable_set_reset; ///< 01h -- + unsigned char color_compare; ///< 02h -- + unsigned char data_rotate; ///< 03h -- + unsigned char read_map; ///< 04h -- + unsigned char graphics_mode; ///< 05h -- + unsigned char misc_graphics; ///< 06h -- + unsigned char color_dont_care; ///< 07h -- + unsigned char bit_mask; ///< 08h -- + } gc; + /// @brief The Attribute Controller Registers. + struct { + unsigned char internal_palette_registers[16]; ///< 00h-0Fh -- + unsigned char attribute_mode_control; ///< 10h -- + unsigned char overscan_color; ///< 11h -- + unsigned char color_plane_enable; ///< 12h -- + unsigned char horizontal_pixel_panning; ///< 13h -- + unsigned char color_select; ///< 14h -- + } ac; +} vga_mode_t; + +vga_mode_t _mode_80_25_text = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0x67, // 0110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x00, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x03, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x02, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x5F, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x4F, // + .start_horizontal_blanking = 0x50, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x55, + .end_horizontal_retrace = 0x81, + .vertical_total = 0xBF, + .overflow = 0x1F, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x4F, + .cursor_start = 0x0D, + .cursor_end = 0x0E, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x50, + .vertical_retrace_start = 0x9C, + .vertical_retrace_end = 0x0E, + .vertical_display_end = 0x8F, + .offset = 0x28, + .underline_location = 0x1F, + .start_vertical_blanking = 0x96, + .end_vertical_blanking = 0xB9, + .crtc_mode_control = 0xA3, // 0xA3 1010 0011 + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x00, + .graphics_mode = 0x10, + .misc_graphics = 0x0E, + .color_dont_care = 0x00, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x14, + 0x07, + 0x38, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + }, + .attribute_mode_control = 0x0C, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x08, + .color_select = 0x00, + } +}; + +vga_mode_t _mode_320_200_256 = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0x63, // 0110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x01, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x0F, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x06, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x5F, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x4F, // + .start_horizontal_blanking = 0x50, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x54, + .end_horizontal_retrace = 0x80, + .vertical_total = 0xBF, + .overflow = 0x1F, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x41, + .cursor_start = 0x00, + .cursor_end = 0x00, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x00, + .vertical_retrace_start = 0x9C, + .vertical_retrace_end = 0xE3, + .vertical_display_end = 0x8F, + .offset = 0x28, + .underline_location = 0x40, + .start_vertical_blanking = 0x96, + .end_vertical_blanking = 0xB9, + .crtc_mode_control = 0xA3, // 0xA3 1010 0011 + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x00, + .graphics_mode = 0x40, + .misc_graphics = 0x05, + .color_dont_care = 0x0F, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + }, + .attribute_mode_control = 0x41, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x00, + .color_select = 0x00, + } +}; + + +vga_mode_t _mode_640_480_16 = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0xE3, // 1110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x01, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x0F, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x06, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x5F, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x4F, // + .start_horizontal_blanking = 0x50, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x54, + .end_horizontal_retrace = 0x80, + .vertical_total = 0x0B, + .overflow = 0x3E, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x40, + .cursor_start = 0x00, + .cursor_end = 0x00, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x00, + .vertical_retrace_start = 0xEA, + .vertical_retrace_end = 0x0C, + .vertical_display_end = 0xDF, + .offset = 0x28, + .underline_location = 0x00, + .start_vertical_blanking = 0xE7, + .end_vertical_blanking = 0x04, + .crtc_mode_control = 0xE3, + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x03, + .graphics_mode = 0x00, + .misc_graphics = 0x05, + .color_dont_care = 0x0F, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x14, + 0x07, + 0x38, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + }, + .attribute_mode_control = 0x01, // 0b01000001, // 0x41, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x00, + .color_select = 0x00, + } +}; + +vga_mode_t _mode_720_480_16 = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0xE7, // 1110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x01, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x08, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x06, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x6B, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x59, // + .start_horizontal_blanking = 0x5A, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x60, + .end_horizontal_retrace = 0x8D, + .vertical_total = 0x0B, + .overflow = 0x3E, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x40, + .cursor_start = 0x06, + .cursor_end = 0x07, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x00, + .vertical_retrace_start = 0xEA, + .vertical_retrace_end = 0x0C, + .vertical_display_end = 0xDF, + .offset = 0x2D, + .underline_location = 0x08, + .start_vertical_blanking = 0xE8, + .end_vertical_blanking = 0x05, + .crtc_mode_control = 0xE3, + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x03, + .graphics_mode = 0x00, + .misc_graphics = 0x05, + .color_dont_care = 0x0F, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + }, + .attribute_mode_control = 0x01, // 0b01000001, // 0x41, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x00, + .color_select = 0x00, + } +}; diff --git a/mentos/inc/io/vga/vga_palette.h b/mentos/inc/io/vga/vga_palette.h new file mode 100644 index 0000000..79f804f --- /dev/null +++ b/mentos/inc/io/vga/vga_palette.h @@ -0,0 +1,291 @@ +/// MentOS, The Mentoring Operating system project +/// @file vga_palette.h +/// @brief VGA color palettes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +typedef struct { + unsigned char red; + unsigned char green; + unsigned char blue; +} palette_entry_t; + +palette_entry_t ansi_16_palette[17] = { + { 0x00, 0x00, 0x00 }, // Black + { 0xAA, 0x00, 0x00 }, // Red + { 0x00, 0xAA, 0x00 }, // Green + { 0xAA, 0xAA, 0x00 }, // Yellow + { 0x00, 0x00, 0xAA }, // Blue + { 0xAA, 0x00, 0xAA }, // Magenta + { 0x00, 0xAA, 0xAA }, // Cyan + { 0xAA, 0xAA, 0xAA }, // White + { 0x55, 0x55, 0x55 }, // Black + { 0xFF, 0x55, 0x55 }, // Red + { 0x55, 0xFF, 0x55 }, // Green + { 0xFF, 0xFF, 0x55 }, // Yellow + { 0x55, 0x55, 0xFF }, // Blue + { 0xFF, 0x55, 0xFF }, // Magenta + { 0x55, 0xFF, 0xFF }, // Cyan + { 0xFF, 0xFF, 0xFF }, // White +}; + +palette_entry_t ansi_256_palette[256] = { + { 0, 0, 0 }, // Black + { 128, 0, 0 }, // Maroon + { 0, 128, 0 }, // Green + { 128, 128, 0 }, // Olive + { 0, 0, 128 }, // Navy + { 128, 0, 128 }, // Purple + { 0, 128, 128 }, // Teal + { 192, 192, 192 }, // Silver + { 128, 128, 128 }, // Grey + { 255, 0, 0 }, // Red + { 0, 255, 0 }, // Lime + { 255, 255, 0 }, // Yellow + { 0, 0, 255 }, // Blue + { 255, 0, 255 }, // Fuchsia + { 0, 255, 255 }, // Aqua + { 255, 255, 255 }, // White + { 0, 0, 55 }, // ExtremelyDarkBlue + { 0, 0, 95 }, // NavyBlue + { 0, 0, 135 }, // DarkBlue + { 0, 0, 175 }, // Blue3 + { 0, 0, 215 }, // Blue3 + { 0, 0, 255 }, // Blue1 + { 0, 95, 0 }, // DarkGreen + { 0, 95, 95 }, // DeepSkyBlue4 + { 0, 95, 135 }, // DeepSkyBlue4 + { 0, 95, 175 }, // DeepSkyBlue4 + { 0, 95, 215 }, // DodgerBlue3 + { 0, 95, 255 }, // DodgerBlue2 + { 0, 135, 0 }, // Green4 + { 0, 135, 95 }, // SpringGreen4 + { 0, 135, 135 }, // Turquoise4 + { 0, 135, 175 }, // DeepSkyBlue3 + { 0, 135, 215 }, // DeepSkyBlue3 + { 0, 135, 255 }, // DodgerBlue1 + { 0, 175, 0 }, // Green3 + { 0, 175, 95 }, // SpringGreen3 + { 0, 175, 135 }, // DarkCyan + { 0, 175, 175 }, // LightSeaGreen + { 0, 175, 215 }, // DeepSkyBlue2 + { 0, 175, 255 }, // DeepSkyBlue1 + { 0, 215, 0 }, // Green3 + { 0, 215, 95 }, // SpringGreen3 + { 0, 215, 135 }, // SpringGreen2 + { 0, 215, 175 }, // Cyan3 + { 0, 215, 215 }, // DarkTurquoise + { 0, 215, 255 }, // Turquoise2 + { 0, 255, 0 }, // Green1 + { 0, 255, 95 }, // SpringGreen2 + { 0, 255, 135 }, // SpringGreen1 + { 0, 255, 175 }, // MediumSpringGreen + { 0, 255, 215 }, // Cyan2 + { 0, 255, 255 }, // Cyan1 + { 95, 0, 0 }, // DarkRed + { 95, 0, 95 }, // DeepPink4 + { 95, 0, 135 }, // Purple4 + { 95, 0, 175 }, // Purple4 + { 95, 0, 215 }, // Purple3 + { 95, 0, 255 }, // BlueViolet + { 95, 95, 0 }, // Orange4 + { 95, 95, 95 }, // Grey37 + { 95, 95, 135 }, // MediumPurple4 + { 95, 95, 175 }, // SlateBlue3 + { 95, 95, 215 }, // SlateBlue3 + { 95, 95, 255 }, // RoyalBlue1 + { 95, 135, 0 }, // Chartreuse4 + { 95, 135, 95 }, // DarkSeaGreen4 + { 95, 135, 135 }, // PaleTurquoise4 + { 95, 135, 175 }, // SteelBlue + { 95, 135, 215 }, // SteelBlue3 + { 95, 135, 255 }, // CornflowerBlue + { 95, 175, 0 }, // Chartreuse3 + { 95, 175, 95 }, // DarkSeaGreen4 + { 95, 175, 135 }, // CadetBlue + { 95, 175, 175 }, // CadetBlue + { 95, 175, 215 }, // SkyBlue3 + { 95, 175, 255 }, // SteelBlue1 + { 95, 215, 0 }, // Chartreuse3 + { 95, 215, 95 }, // PaleGreen3 + { 95, 215, 135 }, // SeaGreen3 + { 95, 215, 175 }, // Aquamarine3 + { 95, 215, 215 }, // MediumTurquoise + { 95, 215, 255 }, // SteelBlue1 + { 95, 255, 0 }, // Chartreuse2 + { 95, 255, 95 }, // SeaGreen2 + { 95, 255, 135 }, // SeaGreen1 + { 95, 255, 175 }, // SeaGreen1 + { 95, 255, 215 }, // Aquamarine1 + { 95, 255, 255 }, // DarkSlateGray2 + { 135, 0, 0 }, // DarkRed + { 135, 0, 95 }, // DeepPink4 + { 135, 0, 135 }, // DarkMagenta + { 135, 0, 175 }, // DarkMagenta + { 135, 0, 215 }, // DarkViolet + { 135, 0, 255 }, // Purple + { 135, 95, 0 }, // Orange4 + { 135, 95, 95 }, // LightPink4 + { 135, 95, 135 }, // Plum4 + { 135, 95, 175 }, // MediumPurple3 + { 135, 95, 215 }, // MediumPurple3 + { 135, 95, 255 }, // SlateBlue1 + { 135, 135, 0 }, // Yellow4 + { 135, 135, 95 }, // Wheat4 + { 135, 135, 135 }, // Grey53 + { 135, 135, 175 }, // LightSlateGrey + { 135, 135, 215 }, // MediumPurple + { 135, 135, 255 }, // LightSlateBlue + { 135, 175, 0 }, // Yellow4 + { 135, 175, 95 }, // DarkOliveGreen3 + { 135, 175, 135 }, // DarkSeaGreen + { 135, 175, 175 }, // LightSkyBlue3 + { 135, 175, 215 }, // LightSkyBlue3 + { 135, 175, 255 }, // SkyBlue2 + { 135, 215, 0 }, // Chartreuse2 + { 135, 215, 95 }, // DarkOliveGreen3 + { 135, 215, 135 }, // PaleGreen3 + { 135, 215, 175 }, // DarkSeaGreen3 + { 135, 215, 215 }, // DarkSlateGray3 + { 135, 215, 255 }, // SkyBlue1 + { 135, 255, 0 }, // Chartreuse1 + { 135, 255, 95 }, // LightGreen + { 135, 255, 135 }, // LightGreen + { 135, 255, 175 }, // PaleGreen1 + { 135, 255, 215 }, // Aquamarine1 + { 135, 255, 255 }, // DarkSlateGray1 + { 175, 0, 0 }, // Red3 + { 175, 0, 95 }, // DeepPink4 + { 175, 0, 135 }, // MediumVioletRed + { 175, 0, 175 }, // Magenta3 + { 175, 0, 215 }, // DarkViolet + { 175, 0, 255 }, // Purple + { 175, 95, 0 }, // DarkOrange3 + { 175, 95, 95 }, // IndianRed + { 175, 95, 135 }, // HotPink3 + { 175, 95, 175 }, // MediumOrchid3 + { 175, 95, 215 }, // MediumOrchid + { 175, 95, 255 }, // MediumPurple2 + { 175, 135, 0 }, // DarkGoldenrod + { 175, 135, 95 }, // LightSalmon3 + { 175, 135, 135 }, // RosyBrown + { 175, 135, 175 }, // Grey63 + { 175, 135, 215 }, // MediumPurple2 + { 175, 135, 255 }, // MediumPurple1 + { 175, 175, 0 }, // Gold3 + { 175, 175, 95 }, // DarkKhaki + { 175, 175, 135 }, // NavajoWhite3 + { 175, 175, 175 }, // Grey69 + { 175, 175, 215 }, // LightSteelBlue3 + { 175, 175, 255 }, // LightSteelBlue + { 175, 215, 0 }, // Yellow3 + { 175, 215, 95 }, // DarkOliveGreen3 + { 175, 215, 135 }, // DarkSeaGreen3 + { 175, 215, 175 }, // DarkSeaGreen2 + { 175, 215, 215 }, // LightCyan3 + { 175, 215, 255 }, // LightSkyBlue1 + { 175, 255, 0 }, // GreenYellow + { 175, 255, 95 }, // DarkOliveGreen2 + { 175, 255, 135 }, // PaleGreen1 + { 175, 255, 175 }, // DarkSeaGreen2 + { 175, 255, 215 }, // DarkSeaGreen1 + { 175, 255, 255 }, // PaleTurquoise1 + { 215, 0, 0 }, // Red3 + { 215, 0, 95 }, // DeepPink3 + { 215, 0, 135 }, // DeepPink3 + { 215, 0, 175 }, // Magenta3 + { 215, 0, 215 }, // Magenta3 + { 215, 0, 255 }, // Magenta2 + { 215, 95, 0 }, // DarkOrange3 + { 215, 95, 95 }, // IndianRed + { 215, 95, 135 }, // HotPink3 + { 215, 95, 175 }, // HotPink2 + { 215, 95, 215 }, // Orchid + { 215, 95, 255 }, // MediumOrchid1 + { 215, 135, 0 }, // Orange3 + { 215, 135, 95 }, // LightSalmon3 + { 215, 135, 135 }, // LightPink3 + { 215, 135, 175 }, // Pink3 + { 215, 135, 215 }, // Plum3 + { 215, 135, 255 }, // Violet + { 215, 175, 0 }, // Gold3 + { 215, 175, 95 }, // LightGoldenrod3 + { 215, 175, 135 }, // Tan + { 215, 175, 175 }, // MistyRose3 + { 215, 175, 215 }, // Thistle3 + { 215, 175, 255 }, // Plum2 + { 215, 215, 0 }, // Yellow3 + { 215, 215, 95 }, // Khaki3 + { 215, 215, 135 }, // LightGoldenrod2 + { 215, 215, 175 }, // LightYellow3 + { 215, 215, 215 }, // Grey84 + { 215, 215, 255 }, // LightSteelBlue1 + { 215, 255, 0 }, // Yellow2 + { 215, 255, 95 }, // DarkOliveGreen1 + { 215, 255, 135 }, // DarkOliveGreen1 + { 215, 255, 175 }, // DarkSeaGreen1 + { 215, 255, 215 }, // Honeydew2 + { 215, 255, 255 }, // LightCyan1 + { 255, 0, 0 }, // Red1 + { 255, 0, 95 }, // DeepPink2 + { 255, 0, 135 }, // DeepPink1 + { 255, 0, 175 }, // DeepPink1 + { 255, 0, 215 }, // Magenta2 + { 255, 0, 255 }, // Magenta1 + { 255, 95, 0 }, // OrangeRed1 + { 255, 95, 95 }, // IndianRed1 + { 255, 95, 135 }, // IndianRed1 + { 255, 95, 175 }, // HotPink + { 255, 95, 215 }, // HotPink + { 255, 95, 255 }, // MediumOrchid1 + { 255, 135, 0 }, // DarkOrange + { 255, 135, 95 }, // Salmon1 + { 255, 135, 135 }, // LightCoral + { 255, 135, 175 }, // PaleVioletRed1 + { 255, 135, 215 }, // Orchid2 + { 255, 135, 255 }, // Orchid1 + { 255, 175, 0 }, // Orange1 + { 255, 175, 95 }, // SandyBrown + { 255, 175, 135 }, // LightSalmon1 + { 255, 175, 175 }, // LightPink1 + { 255, 175, 215 }, // Pink1 + { 255, 175, 255 }, // Plum1 + { 255, 215, 0 }, // Gold1 + { 255, 215, 95 }, // LightGoldenrod2 + { 255, 215, 135 }, // LightGoldenrod2 + { 255, 215, 175 }, // NavajoWhite1 + { 255, 215, 215 }, // MistyRose1 + { 255, 215, 255 }, // Thistle1 + { 255, 255, 0 }, // Yellow1 + { 255, 255, 95 }, // LightGoldenrod1 + { 255, 255, 135 }, // Khaki1 + { 255, 255, 175 }, // Wheat1 + { 255, 255, 215 }, // Cornsilk1 + { 255, 255, 255 }, // Grey100 + { 8, 8, 8 }, // Grey3 + { 18, 18, 18 }, // Grey7 + { 28, 28, 28 }, // Grey11 + { 38, 38, 38 }, // Grey15 + { 48, 48, 48 }, // Grey19 + { 58, 58, 58 }, // Grey23 + { 68, 68, 68 }, // Grey27 + { 78, 78, 78 }, // Grey30 + { 88, 88, 88 }, // Grey35 + { 98, 98, 98 }, // Grey39 + { 108, 108, 108 }, // Grey42 + { 118, 118, 118 }, // Grey46 + { 128, 128, 128 }, // Grey50 + { 138, 138, 138 }, // Grey54 + { 148, 148, 148 }, // Grey58 + { 158, 158, 158 }, // Grey62 + { 168, 168, 168 }, // Grey66 + { 178, 178, 178 }, // Grey70 + { 188, 188, 188 }, // Grey74 + { 198, 198, 198 }, // Grey78 + { 208, 208, 208 }, // Grey82 + { 218, 218, 218 }, // Grey85 + { 228, 228, 228 }, // Grey89 + { 238, 238, 238 }, // Grey93 +}; \ No newline at end of file diff --git a/mentos/inc/io/video.h b/mentos/inc/io/video.h index b619214..ab03277 100644 --- a/mentos/inc/io/video.h +++ b/mentos/inc/io/video.h @@ -1,78 +1,87 @@ /// MentOS, The Mentoring Operating system project /// @file video.h /// @brief Video functions and costants. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" -/// @brief A set of colors. -typedef enum video_color_t { - ///0 : Black - BLACK, - /// 1 : Blue - BLUE, - /// 2 : Green - GREEN, - /// 3 : Cyan - CYAN, - /// 4 : Red - RED, - /// 5 : Magenta - MAGENTA, - /// 6 : Brown - BROWN, - /// 7 : Grey - GREY, - /// 8 : Dark Grey - DARK_GREY, - /// 9 : Bright Blue - BRIGHT_BLUE, - /// 10 : Bright Green - BRIGHT_GREEN, - /// 11 : Bright Cyan - BRIGHT_CYAN, - /// 12 : Bright Red - BRIGHT_RED, - /// 13 : Bright Magenta - BRIGHT_MAGENTA, - /// 14 : Yellow - YELLOW, - /// 15 : White - WHITE, -} video_color_t; +#define FG_RESET "\033[0m" ///< ANSI code for resetting. + +#define FG_BLACK "\033[30m" ///< ANSI code for setting a BLACK foreground. +#define FG_RED "\033[31m" ///< ANSI code for setting a RED foreground. +#define FG_GREEN "\033[32m" ///< ANSI code for setting a GREEN foreground. +#define FG_YELLOW "\033[33m" ///< ANSI code for setting a YELLOW foreground. +#define FG_BLUE "\033[34m" ///< ANSI code for setting a BLUE foreground. +#define FG_MAGENTA "\033[35m" ///< ANSI code for setting a MAGENTA foreground. +#define FG_CYAN "\033[36m" ///< ANSI code for setting a CYAN foreground. +#define FG_WHITE "\033[37m" ///< ANSI code for setting a WHITE foreground. + +#define FG_BLACK_BOLD "\033[1;30m" ///< ANSI code for setting a BLACK foreground. +#define FG_RED_BOLD "\033[1;31m" ///< ANSI code for setting a RED foreground. +#define FG_GREEN_BOLD "\033[1;32m" ///< ANSI code for setting a GREEN foreground. +#define FG_YELLOW_BOLD "\033[1;33m" ///< ANSI code for setting a YELLOW foreground. +#define FG_BLUE_BOLD "\033[1;34m" ///< ANSI code for setting a BLUE foreground. +#define FG_MAGENTA_BOLD "\033[1;35m" ///< ANSI code for setting a MAGENTA foreground. +#define FG_CYAN_BOLD "\033[1;36m" ///< ANSI code for setting a CYAN foreground. +#define FG_WHITE_BOLD "\033[1;37m" ///< ANSI code for setting a WHITE foreground. + +#define FG_BLACK_BRIGHT "\033[90m" ///< ANSI code for setting a BRIGHT_BLACK foreground. +#define FG_RED_BRIGHT "\033[91m" ///< ANSI code for setting a BRIGHT_RED foreground. +#define FG_GREEN_BRIGHT "\033[92m" ///< ANSI code for setting a BRIGHT_GREEN foreground. +#define FG_YELLOW_BRIGHT "\033[93m" ///< ANSI code for setting a BRIGHT_YELLOW foreground. +#define FG_BLUE_BRIGHT "\033[94m" ///< ANSI code for setting a BRIGHT_BLUE foreground. +#define FG_MAGENTA_BRIGHT "\033[95m" ///< ANSI code for setting a BRIGHT_MAGENTA foreground. +#define FG_CYAN_BRIGHT "\033[96m" ///< ANSI code for setting a BRIGHT_CYAN foreground. +#define FG_WHITE_BRIGHT "\033[97m" ///< ANSI code for setting a BRIGHT_WHITE foreground. + +#define FG_BLACK_BRIGHT_BOLD "\033[1;90m" ///< ANSI code for setting a BRIGHT_BLACK foreground. +#define FG_RED_BRIGHT_BOLD "\033[1;91m" ///< ANSI code for setting a BRIGHT_RED foreground. +#define FG_GREEN_BRIGHT_BOLD "\033[1;92m" ///< ANSI code for setting a BRIGHT_GREEN foreground. +#define FG_YELLOW_BRIGHT_BOLD "\033[1;93m" ///< ANSI code for setting a BRIGHT_YELLOW foreground. +#define FG_BLUE_BRIGHT_BOLD "\033[1;94m" ///< ANSI code for setting a BRIGHT_BLUE foreground. +#define FG_MAGENTA_BRIGHT_BOLD "\033[1;95m" ///< ANSI code for setting a BRIGHT_MAGENTA foreground. +#define FG_CYAN_BRIGHT_BOLD "\033[1;96m" ///< ANSI code for setting a BRIGHT_CYAN foreground. +#define FG_WHITE_BRIGHT_BOLD "\033[1;97m" ///< ANSI code for setting a BRIGHT_WHITE foreground. + +#define BG_BLACK "\033[40m" ///< ANSI code for setting a BLACK background. +#define BG_RED "\033[41m" ///< ANSI code for setting a RED background. +#define BG_GREEN "\033[42m" ///< ANSI code for setting a GREEN background. +#define BG_YELLOW "\033[43m" ///< ANSI code for setting a YELLOW background. +#define BG_BLUE "\033[44m" ///< ANSI code for setting a BLUE background. +#define BG_MAGENTA "\033[45m" ///< ANSI code for setting a MAGENTA background. +#define BG_CYAN "\033[46m" ///< ANSI code for setting a CYAN background. +#define BG_WHITE "\033[47m" ///< ANSI code for setting a WHITE background. + +#define BG_BRIGHT_BLACK "\033[100m" ///< ANSI code for setting a BRIGHT_BLACK background. +#define BG_BRIGHT_RED "\033[101m" ///< ANSI code for setting a BRIGHT_RED background. +#define BG_BRIGHT_GREEN "\033[102m" ///< ANSI code for setting a BRIGHT_GREEN background. +#define BG_BRIGHT_YELLOW "\033[103m" ///< ANSI code for setting a BRIGHT_YELLOW background. +#define BG_BRIGHT_BLUE "\033[104m" ///< ANSI code for setting a BRIGHT_BLUE background. +#define BG_BRIGHT_MAGENTA "\033[105m" ///< ANSI code for setting a BRIGHT_MAGENTA background. +#define BG_BRIGHT_CYAN "\033[106m" ///< ANSI code for setting a BRIGHT_CYAN background. +#define BG_BRIGHT_WHITE "\033[107m" ///< ANSI code for setting a BRIGHT_WHITE background. /// @brief Initialize the video. void video_init(); /// @brief Print the given character on the screen. -void video_putc(int); +/// @param c The character to print. +void video_putc(int c); /// @brief Prints the given string on the screen. +/// @param str The string to print. void video_puts(const char *str); -/// @brief Change foreground colour. -void video_set_color(const video_color_t foreground); - -/// @brief Change background colour. -void video_set_background(const video_color_t background); - -/// @brief Deletes the last inserted character. -void video_delete_last_character(); - -/// @brief Move the cursor to the given position. -void video_set_cursor(const unsigned int x, const unsigned int y); - /// @brief When something is written in another position, update the cursor. void video_set_cursor_auto(); /// @brief Move the cursor at the position x, y on the screen. -void video_move_cursor(int, int); - -/// @brief Prints a tab on the screen. -void video_put_tab(); +/// @param x The x coordinate. +/// @param y The y coordinate. +void video_move_cursor(unsigned int x, unsigned int y); /// @brief Clears the screen. void video_clear(); @@ -84,49 +93,19 @@ void video_new_line(); void video_cartridge_return(); /// @brief Get the current column number. -uint32_t video_get_column(); +/// @return The column number. +uint32_t video_get_x(); /// @brief Get the current row number. -uint32_t video_get_line(); +/// @return The row number. +uint32_t video_get_y(); /// @brief The whole screen is shifted up by one line. Used when the cursor /// reaches the last position of the screen. -void video_shift_one_line(); +void video_shift_one_line_up(); -/// @brief The scrolling buffer is updated to contain the screen up the -/// current one. The oldest line is lost to make space for the new one. -void video_rotate_scroll_buffer(); +/// @brief The whole screen is shifted up by one page. +void video_shift_one_page_up(); -/// @brief Called by the pression of the PAGEUP key. -/// The screen aboce the current one is printed and the current one is -/// saved in downbuffer, ready to be restored in future. -void video_scroll_up(); - -/// @brief Called by the pression of the PAGEDOWN key. -/// The content of downbuffer (that is, the screen present when you -/// pressed PAGEUP) is printed again. -void video_scroll_down(); - -/// Determines the lower-bound on the x axis for the video. -uint32_t lower_bound_x; - -/// Determines the lower-bound on the y axis for the video. -uint32_t lower_bound_y; - -/// Determines the current position of the shell cursor on the x axis. -uint32_t shell_current_x; - -/// Determines the current position of the shell cursor on the y axis. -uint32_t shell_current_y; - -/// Determines the lower-bound on the x axis for the shell. -uint32_t shell_lower_bound_x; - -/// Determines the lower-bound on the y axis for the shell. -uint32_t shell_lower_bound_y; - -/// @brief Prints [OK] at the current row and column 60. -void video_print_ok(); - -/// @brief Prints [FAIL] at the current row and column 60. -void video_print_fail(); +/// @brief The whole screen is shifted down by one page. +void video_shift_one_page_down(); diff --git a/mentos/inc/kernel.h b/mentos/inc/kernel.h index 38fe385..52238c6 100644 --- a/mentos/inc/kernel.h +++ b/mentos/inc/kernel.h @@ -1,278 +1,67 @@ /// MentOS, The Mentoring Operating system project /// @file kernel.h /// @brief Kernel generic data structure and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "elf.h" -#include "stddef.h" #include "stdint.h" -#include "stdbool.h" -#include "multiboot.h" -/// The maximum number of modules. -#define MAX_MODULES 10 - -/// Our kernel now loads at 0xC0000000, so what low memory address such as -/// 0xb800 you used to access, should be LOAD_MEMORY_ADDRESS + 0xb800 -#define LOAD_MEMORY_ADDRESS 0x00000000 - -// TODO: doxygen comment. +/// @brief The initial stack pointer. extern uintptr_t initial_esp; -/// @brief Halt. -inline static void halt() -{ - __asm__ __volatile__("hlt" ::: "memory"); -} - -/// @brief Pause. -inline static void pause() -{ - __asm__ __volatile__("pause" ::: "memory"); -} +/// @brief Push the `item` of a given `type` on the `stack`. +#define PUSH_ARG(stack, type, item) (*((type *)((stack) -= sizeof(type))) = (item)) +/// Kilobytes. #define K 1024 - +/// Megabytes. #define M (1024 * K) - +/// Gigabytes. #define G (1024 * M) -/// Pointer the beging of the module. -extern char *module_start[MAX_MODULES]; - -/// Address to the end of the module. -extern char *module_end[MAX_MODULES]; - -/// Elf symbols of the kernel. -extern elf_symbols_t kernel_elf; - -/// @brief Entry point of the kernel. -/// @param boot_informations Information concerning the boot. -/// @return The exit status of the kernel. -int kmain(uint32_t magic, multiboot_info_t *header, uintptr_t esp); - -/// @brief Register structs for interrupt/exception. -typedef struct register_t { - /// FS and GS have no hardware-assigned uses. - uint32_t gs; - /// FS and GS have no hardware-assigned uses. - uint32_t fs; - /// Extra Segment determined by the programmer. - uint32_t es; - /// Data Segment. - uint32_t ds; - /// 32-bit destination register. - uint32_t edi; - /// 32-bit source register. - uint32_t esi; - /// 32-bit base pointer register. - uint32_t ebp; - /// 32-bit stack pointer register. - uint32_t esp; - /// 32-bit base register. - uint32_t ebx; - /// 32-bit data register. - uint32_t edx; - /// 32-bit counter. - uint32_t ecx; - /// 32-bit accumulator register. - uint32_t eax; - /// Interrupt number. - uint32_t int_no; - /// Error code. - uint32_t err_code; - /// Instruction Pointer Register. - uint32_t eip; - /// Code Segment. - uint32_t cs; - /// 32-bit flag register. - uint32_t eflags; - // TODO: Check meaning! - uint32_t useresp; - /// Stack Segment. - uint32_t ss; -} register_t; - -/* - * /// @brief Register structs for bios service. - * typedef struct register16_t - * { - * /// Destination Index. - * uint16_t di; - * /// Source Index. - * uint16_t si; - * /// Base Pointer. - * uint16_t bp; - * /// Stack Pointer. - * uint16_t sp; - * /// Also known as the base register. - * uint16_t bx; - * /// Also known as the data register. - * uint16_t dx; - * /// Also known as the count register. - * uint16_t cx; - * /// Is the primary accumulator. - * uint16_t ax; - * /// Data Segment. - * uint16_t ds; - * /// Extra Segment determined by the programmer. - * uint16_t es; - * /// FS and GS have no hardware-assigned uses. - * uint16_t fs; - * /// FS and GS have no hardware-assigned uses. - * uint16_t gs; - * /// Stack Segment. - * uint16_t ss; - * /// 32-bit flag register. - * uint16_t eflags; - * } register16_t; - */ - -//==== Interrupt stack frame =================================================== -// Interrupt stack frame. When the CPU moves from Ring3 to Ring0 because of -// an interrupt, the following registes/values are moved into the kernel's stack -// TODO: doxygen comment. +/// @brief Interrupt stack frame. +/// @details +/// When the CPU moves from Ring3 to Ring0 because of an interrupt, +/// the following registes/values are moved into the kernel's stack typedef struct pt_regs { - /// FS and GS have no hardware-assigned uses. - uint32_t gs; - /// FS and GS have no hardware-assigned uses. - uint32_t fs; - /// Extra Segment determined by the programmer. - uint32_t es; - /// Data Segment. - uint32_t ds; - /// 32-bit destination register. - uint32_t edi; - /// 32-bit source register. - uint32_t esi; - /// 32-bit base pointer register. - uint32_t ebp; - /// 32-bit stack pointer register. - uint32_t esp; - /// 32-bit base register. - uint32_t ebx; - /// 32-bit data register. - uint32_t edx; - /// 32-bit counter. - uint32_t ecx; - /// 32-bit accumulator register. - uint32_t eax; - /// Interrupt number. - uint32_t int_no; - /// Error code. - uint32_t err_code; - /// Instruction Pointer Register. - uint32_t eip; - /// Code Segment. - uint32_t cs; - /// 32-bit flag register. - uint32_t eflags; - // TODO: Check meaning! - uint32_t useresp; - /// Stack Segment. - uint32_t ss; + /// FS and GS have no hardware-assigned uses. + uint32_t gs; + /// FS and GS have no hardware-assigned uses. + uint32_t fs; + /// Extra Segment determined by the programmer. + uint32_t es; + /// Data Segment. + uint32_t ds; + /// 32-bit destination register. + uint32_t edi; + /// 32-bit source register. + uint32_t esi; + /// 32-bit base pointer register. + uint32_t ebp; + /// 32-bit stack pointer register. + uint32_t esp; + /// 32-bit base register. + uint32_t ebx; + /// 32-bit data register. + uint32_t edx; + /// 32-bit counter. + uint32_t ecx; + /// 32-bit accumulator register. + uint32_t eax; + /// Interrupt number. + uint32_t int_no; + /// Error code. + uint32_t err_code; + /// Instruction Pointer Register. + uint32_t eip; + /// Code Segment. + uint32_t cs; + /// 32-bit flag register. + uint32_t eflags; + /// User application ESP. + uint32_t useresp; + /// Stack Segment. + uint32_t ss; } pt_regs; -//============================================================================== - -//==== Floating Point Unit (FPU) Register ====================================== -// Data structure used to save FPU registers. -/// @brief Environment information of floating point unit. -typedef struct { - /// Control word (16bits). - long en_cw; - /// Status word (16bits). - long en_sw; - /// Tag word (16bits). - long en_tw; - /// Floating point instruction pointer. - long en_fip; - /// Floating code segment selector. - unsigned short en_fcs; - /// Opcode last executed (11 bits). - unsigned short en_opcode; - /// Floating operand offset. - long en_foo; - /// Floating operand segment selector. - long en_fos; -} env87; - -/// @brief Contents of each floating point accumulator. -typedef struct { - unsigned char fp_bytes[10]; -} fpacc87; - -/// @brief Floating point context. -typedef struct { - /// Floating point control/status. - env87 sv_env; - /// Accumulator contents, 0-7. - fpacc87 sv_ac[8]; - /// Padding for (now unused) saved status word. - unsigned char sv_pad0[4]; - /* - * Bogus padding for emulators. Emulators should use their own - * struct and arrange to store into this struct (ending here) - * before it is inspected for ptracing or for core dumps. Some - * emulators overwrite the whole struct. We have no good way of - * knowing how much padding to leave. Leave just enough for the - * GPL emulator's i387_union (176 bytes total). - */ - unsigned char sv_pad[64]; // Padding; used by emulators -} save87; - -// TODO: doxygen comment. -typedef struct { - /// Control word (16bits). - uint16_t en_cw; - /// Status word (16bits). - uint16_t en_sw; - /// Tag word (16bits). - uint16_t en_tw; - /// Opcode last executed (11 bits). - uint16_t en_opcode; - /// Floating point instruction pointer. - uint32_t en_fip; - /// Floating code segment selector. - uint16_t en_fcs; - /// Padding. - uint16_t en_pad0; - /// Floating operand offset. - uint32_t en_foo; - /// Floating operand segment selector. - uint16_t en_fos; - /// Padding. - uint16_t en_pad1; - /// SSE sontorol/status register. - uint32_t en_mxcsr; - /// Valid bits in mxcsr. - uint32_t en_mxcsr_mask; -} envxmm; - -/// @brief Contents of each SSE extended accumulator. -typedef struct { - unsigned char xmm_bytes[16]; -} xmmacc; - -// TODO: doxygen comment. -typedef struct { - envxmm sv_env; - struct { - fpacc87 fp_acc; - /// Padding. - unsigned char fp_pad[6]; - } sv_fp[8]; - xmmacc sv_xmm[8]; - - /// Padding. - unsigned char sv_pad[224]; -} __attribute__((__aligned__(16))) savexmm; - -// TODO: doxygen comment. -typedef union { - save87 sv_87; - savexmm sv_xmm; -} savefpu; -//============================================================================== diff --git a/mentos/inc/libc/compiler.h b/mentos/inc/klib/compiler.h similarity index 90% rename from mentos/inc/libc/compiler.h rename to mentos/inc/klib/compiler.h index dd661bd..6fb9c27 100644 --- a/mentos/inc/libc/compiler.h +++ b/mentos/inc/klib/compiler.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file compiler.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once diff --git a/mentos/inc/klib/hashmap.h b/mentos/inc/klib/hashmap.h new file mode 100644 index 0000000..37e2c44 --- /dev/null +++ b/mentos/inc/klib/hashmap.h @@ -0,0 +1,136 @@ +/// MentOS, The Mentoring Operating system project +/// @file hashmap.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "list.h" + +// == OPAQUE TYPES ============================================================ +/// @brief Stores information of an entry of the hashmap. +typedef struct hashmap_entry_t hashmap_entry_t; +/// @brief Stores information of a hashmap. +typedef struct hashmap_t hashmap_t; + +// == HASHMAP FUNCTIONS ======================================================= +/// @brief Hashing function, used to generate hash keys. +typedef unsigned int (*hashmap_hash_t)(const void *key); +/// @brief Comparison function, used to compare hash keys. +typedef int (*hashmap_comp_t)(const void *a, const void *b); +/// @brief Key duplication function, used to duplicate hash keys. +typedef void *(*hashmap_dupe_t)(const void *); +/// @brief Key deallocation function, used to free the memory occupied by hash keys. +typedef void (*hashmap_free_t)(void *); + +// == HASHMAP KEY MANAGEMENT FUNCTIONS ======================================== +/// @brief Transforms an integer key into a hash key. +/// @param key The integer key. +/// @return The resulting hash key. +unsigned int hashmap_int_hash(const void *key); + +/// @brief Compares two integer hash keys. +/// @param a The first hash key. +/// @param b The second hash key. +/// @return Result of the comparison. +int hashmap_int_comp(const void *a, const void *b); + +/// @brief Transforms a string key into a hash key. +/// @param key The string key. +/// @return The resulting hash key. +unsigned int hashmap_str_hash(const void *key); + +/// @brief Compares two string hash keys. +/// @param a The first hash key. +/// @param b The second hash key. +/// @return Result of the comparison. +int hashmap_str_comp(const void *a, const void *b); + +/// @brief This function can be passed as hashmap_dupe_t, it does nothing. +/// @param value The value to duplicate. +/// @return The duplicated value. +void *hashmap_do_not_duplicate(const void *value); + +/// @brief This function can be passed as hashmap_free_t, it does nothing. +/// @param value The value to free. +void hashmap_do_not_free(void *value); + +// == HASHMAP CREATION AND DESTRUCTION ======================================== +/// @brief User-defined hashmap. +/// @param size Dimension of the hashmap. +/// @param hash_fun The hashing function. +/// @param comp_fun The hash compare function. +/// @param dupe_fun The key duplication function. +/// @param key_free_fun The function used to free memory of keys. +/// @return A pointer to the hashmap. +/// @details +/// (key_free_fun) : No free function. +/// (val_free_fun) : Standard `free` function. +hashmap_t *hashmap_create( + unsigned int size, + hashmap_hash_t hash_fun, + hashmap_comp_t comp_fun, + hashmap_dupe_t dupe_fun, + hashmap_free_t key_free_fun); + +/// @brief Standard hashmap with keys of type (char *). +/// @param size Dimension of the hashmap. +/// @return A pointer to the hashmap. +/// @details +/// (key_free_fun) : Standard `free` function. +/// (val_free_fun) : Standard `free` function. +hashmap_t *hashmap_create_str(unsigned int size); + +/// @brief Standard hashmap with keys of type (char *). +/// @param size Dimension of the hashmap. +/// @return A pointer to the hashmap. +/// @details +/// (key_free_fun) : No free function. +/// (val_free_fun) : Standard `free` function. +hashmap_t *hashmap_create_int(unsigned int size); + +/// @brief Frees the memory of the hashmap. +/// @param map A pointer to the hashmap. +void hashmap_free(hashmap_t *map); + +// == HASHMAP ACCESS FUNCTIONS ================================================ +/// @brief Sets the `value` for the given `key` in the hashmap `map`. +/// @param map The hashmap. +/// @param key The entry key. +/// @param value The entry value. +/// @return NULL on success, a pointer to an already existing entry if fails. +void *hashmap_set(hashmap_t *map, const void *key, void *value); + +/// @brief Access the value for the given key. +/// @param map The hashmap. +/// @param key The key of the entry we are searching. +/// @return The value on success, or NULL on failure. +void *hashmap_get(hashmap_t *map, const void *key); + +/// @brief Removes the entry with the given key. +/// @param map The hashmap. +/// @param key The key of the entry we are searching. +/// @return The value on success, or NULL on failure. +void *hashmap_remove(hashmap_t *map, const void *key); + +/// @brief Checks if the hashmap is empty. +/// @param map The hashmap. +/// @return 1 if empty, 0 otherwise. +int hashmap_is_empty(hashmap_t *map); + +/// @brief Checks if the hashmap contains an entry with the given key. +/// @param map The hashmap. +/// @param key The key of the entry we are searching. +/// @return 1 if the entry is present, 0 otherwise. +int hashmap_has(hashmap_t *map, const void *key); + +/// @brief Provides access to all the keys. +/// @param map The hashmap. +/// @return A list with all the keys, remember to destroy the list. +list_t *hashmap_keys(hashmap_t *map); + +/// @brief Provides access to all the values. +/// @param map The hashmap. +/// @return A list with all the values, remember to destroy the list. +list_t *hashmap_values(hashmap_t *map); diff --git a/mentos/inc/klib/irqflags.h b/mentos/inc/klib/irqflags.h new file mode 100644 index 0000000..23b3ad2 --- /dev/null +++ b/mentos/inc/klib/irqflags.h @@ -0,0 +1,52 @@ +/// MentOS, The Mentoring Operating system project +/// @file irqflags.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "proc_access.h" +#include "stddef.h" +#include "stdint.h" + +/// @brief Enable IRQs (nested). +/// @details If called after calling irq_nested_disable, this function will +/// not activate IRQs if they were not active before. +inline static void irq_nested_enable(uint8_t flags) +{ + if (flags) { + sti(); + } +} + +/// @brief Disable IRQs (nested). +/// @details Disable IRQs when unsure if IRQs were enabled at all. +/// This function together with irq_nested_enable can be used in +/// situations when interrupts shouldn't be activated if they were not +/// activated before calling this function. +inline static uint8_t irq_nested_disable() +{ + size_t flags; + __asm__ __volatile__("pushf; cli; pop %0" + : "=r"(flags) + : + : "memory"); + if (flags & (1 << 9)) + return 1; + return 0; +} + +/// @brief Determines, if the interrupt flags (IF) is set. +inline static uint8_t is_irq_enabled() +{ + size_t flags; + __asm__ __volatile__("pushf; pop %0" + : "=r"(flags) + : + : "memory"); + if (flags & (1 << 9)) { + return 1; + } + return 0; +} diff --git a/mentos/inc/klib/list.h b/mentos/inc/klib/list.h new file mode 100644 index 0000000..22dacff --- /dev/null +++ b/mentos/inc/klib/list.h @@ -0,0 +1,139 @@ +/// MentOS, The Mentoring Operating system project +/// @file list.h +/// @brief An implementation for generic list. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Represent the node of a list. +typedef struct listnode_t { + /// A pointer to the value. + void *value; + /// The previous node. + struct listnode_t *prev; + /// The next node. + struct listnode_t *next; +} listnode_t; + +/// @brief Represent the list. +typedef struct list_t { + /// The first element of the list. + listnode_t *head; + /// The last element of the list. + listnode_t *tail; + /// The size of the list. + unsigned int size; +} list_t; + +/// @brief Macro used to iterate through a list. +#define listnode_foreach(it, list) \ + for (listnode_t * (it) = (list)->head; (it) != NULL; (it) = (it)->next) + +/// @brief Create a list and set head, tail to NULL, and size to 0. +/// @return The newly created list. +list_t *list_create(); + +/// @brief Get list size. +/// @param list The list. +/// @return The size of the list. +unsigned int list_size(list_t *list); + +/// @brief Checks if the list is empty. +/// @param list The list. +/// @return 1 if empty, 0 otherwise. +int list_empty(list_t *list); + +/// @brief Insert a value at the front of list. +/// @param list The list. +/// @param value The value to insert. +/// @return The node associated with the inserted value. +listnode_t *list_insert_front(list_t *list, void *value); + +/// @brief Insert a value at the back of list. +/// @param list The list. +/// @param value The value to insert. +/// @return The node associated with the inserted value. +listnode_t *list_insert_back(list_t *list, void *value); + +/// @brief Given a listnode, remove it from list. +/// @param list The list. +/// @param node The node that has to be removed. +/// @return The value associated with the removed node. +void *list_remove_node(list_t *list, listnode_t *node); + +/// @brief Remove a value at the front of list. +/// @param list The list. +/// @return The value associated with the removed node. +void *list_remove_front(list_t *list); + +/// @brief Remove a value at the back of list. +/// @param list The list. +/// @return The value associated with the removed node. +void *list_remove_back(list_t *list); + +/// @brief Searches the node of the list which points at the given value. +/// @param list The list. +/// @param value The value that has to be searched. +/// @return The node associated with the value. +listnode_t *list_find(list_t *list, void *value); + +/// @brief Insert after tail of list(same as insert back). +/// @param list The list. +/// @param value The value to insert. +void list_push_back(list_t *list, void *value); + +/// @brief Remove and return the tail of the list. +/// @param list The list. +/// @return The node that has been removed. +/// @details User is responsible for freeing the returned node and the value. +listnode_t *list_pop_back(list_t *list); + +/// @brief Insert before head of list(same as insert front). +/// @param list The list. +/// @param value The value to insert. +void list_push_front(list_t *list, void *value); + +/// @brief Remove and return the head of the list. +/// @param list The list. +/// @return The node that has been removed. +/// @details User is responsible for freeing the returned node and the value. +listnode_t *list_pop_front(list_t *list); + +/// @brief Get the value of the first element but not remove it. +/// @param list The list. +/// @return The value associated with the first node. +void *list_peek_front(list_t *list); + +/// @brief Get the value of the last element but not remove it. +/// @param list The list. +/// @return The value associated with the first node. +void *list_peek_back(list_t *list); + +/// @brief Destroy a list. +/// @param list The list. +void list_destroy(list_t *list); + +/// @brief Checks if the given value is contained inside the list. +/// @param list The list. +/// @param value The value to search. +/// @return -1 if list element is not found, the index otherwise. +int list_get_index_of_value(list_t *list, void *value); + +/// @brief Returns the node at the given index. +/// @param list The list. +/// @param index The index of the desired node. +/// @return A pointer to the node, or NULL otherwise. +listnode_t *list_get_node_by_index(list_t *list, unsigned int index); + +/// @brief Removes a node from the list at the given index. +/// @param list The list. +/// @param index The index of the node we need to remove. +/// @return The value contained inside the node, NULL otherwise. +void *list_remove_by_index(list_t *list, unsigned int index); + +/// @brief Append source at the end of target. +/// @param target Where the element are added. +/// @param source Where the element are removed. +/// @details Beware, source is destroyed. +void list_merge(list_t *target, list_t *source); diff --git a/mentos/inc/klib/list_head.h b/mentos/inc/klib/list_head.h new file mode 100644 index 0000000..1620b33 --- /dev/null +++ b/mentos/inc/klib/list_head.h @@ -0,0 +1,173 @@ +/// MentOS, The Mentoring Operating system project +/// @file list_head.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Structure used to implement the list_head data structure. +typedef struct list_head { + /// @brief The previous element. + struct list_head *prev; + /// @brief The subsequent element. + struct list_head *next; +} list_head; + +/// @brief Get the struct for this entry. +/// @param ptr The &list_head pointer. +/// @param type The type of the struct this is embedded in. +/// @param member The name of the list_head within the struct. +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/// @brief Iterates over a list. +/// @param pos The &list_head to use as a loop cursor. +/// @param head The head for your list. +#define list_for_each(pos, head) \ + for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) + +/// @brief Iterates over a list backwards. +/// @param pos The &list_head to use as a loop cursor. +/// @param head The head for your list. +#define list_for_each_prev(pos, head) \ + for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) + +/// @brief Iterates over a list safe against removal of list entry. +/// @param pos The &list_head to use as a loop cursor. +/// @param store Another &list_head to use as temporary storage. +/// @param head The head for your list. +#define list_for_each_safe(pos, store, head) \ + for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); \ + (pos) = (store), (store) = (pos)->next) + +/// @brief Iterates over a list. +/// @param pos The &list_head to use as a loop cursor. +/// @param head The head for your list. +#define list_for_each_decl(pos, head) \ + for (list_head * (pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) + +/// @brief Initializes the list_head. +/// @param head The head for your list. +#define list_head_init(head) (head)->next = (head)->prev = (head) + +/// @brief Initializes the list_head. +/// @param head The head for your list. +#define list_head_size(head) \ + ({ \ + unsigned __list_head_size = 0; \ + list_for_each_decl(it, head) __list_head_size += 1; \ + __list_head_size; \ + }) + +/// @brief Insert element l2 after l1. +static inline void list_head_insert_after(list_head *l1, list_head *l2) +{ + // [La]->l1 La<-[l1]->Lb <-[l2]-> l1<-[Lb] + + list_head *l1_next = l1->next; + // [La]->l1 La<-[l1]->l2 <-[l2]-> l1<-[Lb] + l1->next = l2; + // [La]->l1 La<-[l1]->l2 l1<-[l2]-> l1<-[Lb] + l2->prev = l1; + // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l1<-[Lb] + l2->next = l1_next; + // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l2<-[Lb] + l1_next->prev = l2; +} + +/// @brief Insert element l2 before l1. +static inline void list_head_insert_before(list_head *l1, list_head *l2) +{ + // [La]->l1 [l2] La<-[l1]->Lb l1<-[Lb] + + list_head *l1_prev = l1->prev; + // [La]->l2 [l2] La<-[l1]->Lb l1<-[Lb] + l1_prev->next = l2; + // [La]->l2 La<-[l2] La<-[l1]->Lb l1<-[Lb] + l2->prev = l1_prev; + // [La]->l2 La<-[l2]->l1 La<-[l1]->Lb l1<-[Lb] + l2->next = l1; + // [La]->l2 La<-[l2]->l1 l2<-[l1]->Lb l1<-[Lb] + l1->prev = l2; +} + +/// @brief Remove l from the list. +/// @param l The element to remove. +static inline void list_head_del(list_head *l) +{ + // [La]->l La<-[l]->Lb l<-[Lb] + + // [La]->Lb La<-[l]->Lb l<-[Lb] + l->prev->next = l->next; + // [La]->Lb La<-[l]->Lb La<-[Lb] + l->next->prev = l->prev; + // [La]->Lb l<-[l]->l La<-[Lb] + l->next = l->prev = l; +} + +/// @brief Tests whether the given list is empty. +/// @param head The list to check. +/// @return 1 if empty, 0 otherwise. +static inline int list_head_empty(list_head const *head) +{ + return head->next == head; +} + +/// Insert a new entry between two known consecutive entries. +static inline void __list_add(list_head *new, list_head *prev, list_head *next) +{ + // [prev]-> <-[new]-> <-[next] + + // [prev]-> <-[new]-> new<-[next] + next->prev = new; + // [prev]-> <-[new]->next new<-[next] + new->next = next; + // [prev]-> prev<-[new]->next new<-[next] + new->prev = prev; + // [prev]->new prev<-[new]->next new<-[next] + prev->next = new; +} + +/// @brief Insert element l2 before l1. +static inline void list_head_add(list_head *new, list_head *head) +{ + __list_add(new, head, head->next); +} + +/// @brief Insert element l2 before l1. +static inline void list_head_add_tail(list_head *new, list_head *head) +{ + __list_add(new, head->prev, head); +} + +/// @brief Removes an element from the list pointer, it's used when we have a possibly +/// null list pointer and want to pop an element from it +static inline list_head *list_head_pop(list_head *listp) +{ + if (list_head_empty(listp)) + return NULL; + + list_head *value = listp->next; + list_head_del(listp->next); + + return value; +} + +static inline list_head *list_head_front(list_head *listp) +{ + return listp->next; +} + +/// Merges the elements of l2, into the elements of l1. +static inline void list_head_merge(list_head *l1, list_head *l2) +{ + l1->prev->next = l2->next; + l2->next->prev = l1->prev; + l2->prev->next = l1; + l1->prev = l2->prev; + // Initialize the second list. + list_head_init(l2); +} \ No newline at end of file diff --git a/mentos/inc/libc/mutex.h b/mentos/inc/klib/mutex.h similarity index 74% rename from mentos/inc/libc/mutex.h rename to mentos/inc/klib/mutex.h index a32927d..e6aafe1 100644 --- a/mentos/inc/libc/mutex.h +++ b/mentos/inc/klib/mutex.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file mutex.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,10 +10,10 @@ /// @brief Structure of a mutex. typedef struct mutex_t { - /// The state of the mutex. - uint8_t state; - /// The owner of the mutex. - uint32_t owner; + /// The state of the mutex. + uint8_t state; + /// The owner of the mutex. + uint32_t owner; } mutex_t; /// @brief Allows to lock a mutex. diff --git a/mentos/inc/klib/ndtree.h b/mentos/inc/klib/ndtree.h new file mode 100644 index 0000000..55a6536 --- /dev/null +++ b/mentos/inc/klib/ndtree.h @@ -0,0 +1,198 @@ +/// MentOS, The Mentoring Operating system project +/// @file ndtree.h +/// @brief N-Dimensional tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +// ============================================================================ +// Opaque types. + +/// @brief Node of the tree. +typedef struct ndtree_node_t ndtree_node_t; +/// @brief The tree itself. +typedef struct ndtree_t ndtree_t; +/// @brief Iterator for traversing the tree. +typedef struct ndtree_iter_t ndtree_iter_t; + +// ============================================================================ +// Comparison functions. + +/// @brief Function for comparing elements in the tree. +typedef int (*ndtree_tree_cmp_f)(ndtree_t *tree, void *lhs, void *rhs); +/// @brief Callback to call on elements of the tree. +typedef void (*ndtree_tree_node_f)(ndtree_t *tree, ndtree_node_t *node); + +// ============================================================================ +// Node management functions. + +/// @brief Allocate memory for a node. +/// @return Pointer to the allocated node. +ndtree_node_t *ndtree_node_alloc(); + +/// @brief Allocate memory for a node and sets its value. +/// @param value Value to associated node. +/// @return Pointer to the allocated node. +ndtree_node_t *ndtree_node_create(void *value); + +/// @brief Initializes the already allocated node. +/// @param node The node itself. +/// @param value The value associated to the node. +/// @return Pointer to the node itself. +ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value); + +/// @brief Sets the value of the given node. +/// @param node The node to manipulate. +/// @param value The value associated to the node. +void ndtree_node_set_value(ndtree_node_t *node, void *value); + +/// @brief Provides access to the value associated to a node. +/// @param node The node itself. +/// @return The value associated to the node. +void *ndtree_node_get_value(ndtree_node_t *node); + +/// @brief Sets the given node as root of the tree. +/// @param tree The tree. +/// @param node The node to set as root. +void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node); + +/// @brief Creates a new node and assigns it as root of the tree. +/// @param tree The tree. +/// @param value The value associated to the node. +/// @return The newly created node. +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value); + +/// @brief Provides access to the root of the tree. +/// @param tree The tree. +/// @return Pointer to the node. +ndtree_node_t *ndtree_get_root(ndtree_t *tree); + +/// @brief Adds the given `child` as child of `parent`. +/// @param tree The tree. +/// @param parent The `parent` node. +/// @param child The new `child` node. +void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child); + +/// @brief Creates a new node and sets it as child of `parent`. +/// @param tree The tree. +/// @param parent The `parent` node. +/// @param value Value associated with the new child. +/// @return Pointer to the newly created child node. +ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value); + +/// @brief Counts the number of children of the given node. +/// @param node The node of which we count the children. +/// @return The number of children. +unsigned int ndtree_node_count_children(ndtree_node_t *node); + +/// @brief Deallocate a node. +/// @param node The node to destroy. +void ndtree_node_dealloc(ndtree_node_t *node); + +// ============================================================================ +// Tree management functions. + +/// @brief Allocate memory for a tree. +/// @return Pointer to the allocated tree. +ndtree_t *ndtree_tree_alloc(); + +/// @brief Allocate memory for a tree and sets the function used to compare nodes. +/// @param cmp Function used to compare elements of the tree. +/// @return Pointer to the allocated tree. +ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp); + +/// @brief Deallocate a node. +/// @param tree The tree to destroy. +/// @param node_cb The function called on each element of the tree before destroying the tree. +void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb); + +/// @brief Initializes the tree. +/// @param tree The tree to initialize. +/// @param cmp The compare function to associate to the tree. +/// @return Pointer to tree itself. +ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f cmp); + +/// @brief Searches the node inside the tree with the given value. +/// @param tree The tree. +/// @param cmp The node compare function. +/// @param value The value to search. +/// @return Node associated with the value. +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value); + +/// @brief Searches the given value among the children of node. +/// @param tree The tree. +/// @param node The node under which we search. +/// @param cmp The node compare function. +/// @param value The value to search. +/// @return Node associated with the value. +ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value); + +/// @brief Returns the size of the tree. +/// @param tree The tree. +/// @return The size of the tree. +unsigned int ndtree_tree_size(ndtree_t *tree); + +/// @brief Removes the node from the given tree. +/// @param tree The tree. +/// @param node The node to remove. +/// @param node_cb The function called on the node before removing it. +/// @return Returns 1 if the node was removed, 0 otherwise. +/// @details +/// Optionally the node callback can be provided to dealloc node and/or +/// user data. Use ndtree_tree_node_dealloc default callback to deallocate +/// node created by ndtree_tree_insert(...). +int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb); + +/// @brief Removes the node from the given tree. +/// @param tree The tree. +/// @param value The value to search. +/// @param node_cb The function called on the node before removing it. +/// @return Returns 1 if the value was removed, 0 otherwise. +/// @details +/// Optionally the node callback can be provided to dealloc node and/or +/// user data. Use ndtree_tree_node_dealloc default callback to deallocate +/// node created by ndtree_tree_insert(...). +int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb); + +// ============================================================================ +// Iterators. + +/// @brief Allocate the memory for the iterator. +/// @return Pointer to the allocated iterator. +ndtree_iter_t *ndtree_iter_alloc(); + +/// @brief Deallocate the memory for the iterator. +/// @param iter Pointer to the allocated iterator. +void ndtree_iter_dealloc(ndtree_iter_t *iter); + +/// @brief Initializes the iterator the the first child of the node. +/// @param node The node of which we want to iterate the children. +/// @param iter The iterator we want to initialize. +/// @return Pointer to the first node of the list. +ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter); + +/// @brief Initializes the iterator the the last child of the node. +/// @param node The node of which we want to iterate the children. +/// @param iter The iterator we want to initialize. +/// @return Pointer to the last node of the list. +ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter); + +/// @brief Moves the iterator to the next element. +/// @param iter The iterator. +/// @return Pointer to the next element. +ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter); + +/// @brief Moves the iterator to the previous element. +/// @param iter The iterator. +/// @return Pointer to the previous element. +ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter); + +// ============================================================================ +// Tree visiting functions. + +/// @brief Run a visit of the tree (DFS). +/// @param tree The tree to visit. +/// @param enter_fun Function to call when entering a node. +/// @param exit_fun Function to call when exiting a node. +void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun); diff --git a/mentos/inc/klib/rbtree.h b/mentos/inc/klib/rbtree.h new file mode 100644 index 0000000..ae8732b --- /dev/null +++ b/mentos/inc/klib/rbtree.h @@ -0,0 +1,180 @@ +/// MentOS, The Mentoring Operating system project +/// @file rbtree.h +/// @brief Red/Black tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifndef RBTREE_ITER_MAX_HEIGHT +/// Tallest allowable tree to iterate. +#define RBTREE_ITER_MAX_HEIGHT 64 +#endif + +// ============================================================================ +// Opaque types. + +/// @brief Node of the tree. +typedef struct rbtree_node_t rbtree_node_t; +/// @brief The tree itself. +typedef struct rbtree_t rbtree_t; +/// @brief Iterator for traversing the tree. +typedef struct rbtree_iter_t rbtree_iter_t; + +// ============================================================================ +// Comparison functions. +/// @brief Function for comparing elements in the tree. +typedef int (*rbtree_tree_cmp_f)(rbtree_t *tree, rbtree_node_t *a, void *arg); +/// @brief Function for comparing elements in the tree. +typedef int (*rbtree_tree_node_cmp_f)(rbtree_t *tree, rbtree_node_t *a, rbtree_node_t *b); +/// @brief Callback to call on elements of the tree. +typedef void (*rbtree_tree_node_f)(rbtree_t *tree, rbtree_node_t *node); + +// ============================================================================ +// Node management functions. + +/// @brief Allocate memory for a node. +/// @return Pointer to the allocated node. +rbtree_node_t *rbtree_node_alloc(); + +/// @brief Allocate memory for a node and sets its value. +/// @param value Value to associated node. +/// @return Pointer to the allocated node. +rbtree_node_t *rbtree_node_create(void *value); + +/// @brief Initializes the already allocated node. +/// @param node The node itself. +/// @param value The value associated to the node. +/// @return Pointer to the node itself. +rbtree_node_t *rbtree_node_init(rbtree_node_t *node, void *value); + +/// @brief Provides access to the value associated to a node. +/// @param node The node itself. +/// @return The value associated to the node. +void *rbtree_node_get_value(rbtree_node_t *node); + +/// @brief Deallocate a node. +/// @param node The node to destroy. +void rbtree_node_dealloc(rbtree_node_t *node); + +// ============================================================================ +// Tree management functions. + +/// @brief Allocate memory for a tree. +/// @return Pointer to the allocated tree. +rbtree_t *rbtree_tree_alloc(); + +/// @brief Allocate memory for a tree and sets the function used to compare nodes. +/// @param cmp Function used to compare elements of the tree. +/// @return Pointer to the allocated tree. +rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f cmp); + +/// @brief Initializes the tree. +/// @param tree The tree to initialize. +/// @param cmp The compare function to associate to the tree. +/// @return Pointer to tree itself. +rbtree_t *rbtree_tree_init(rbtree_t *tree, rbtree_tree_node_cmp_f cmp); + +/// @brief Deallocate a node. +/// @param tree The tree to destroy. +/// @param node_cb The function called on each element of the tree before destroying the tree. +void rbtree_tree_dealloc(rbtree_t *tree, rbtree_tree_node_f node_cb); + +/// @brief Searches the node inside the tree with the given value. +/// @param tree The tree. +/// @param value The value to search. +/// @return Pointer to the value itself. +void *rbtree_tree_find(rbtree_t *tree, void *value); + +/// @brief Searches the node inside the tree with the given value. +/// @param tree The tree. +/// @param cmp_fun The node compare function. +/// @param value The value to search. +/// @return Pointer to the value itself. +void *rbtree_tree_find_by_value(rbtree_t *tree, rbtree_tree_cmp_f cmp_fun, void *value); + +/// @brief Interts the value inside the tree. +/// @param tree The tree. +/// @param value The value to insert. +/// @return 1 on success, 0 on failure. +int rbtree_tree_insert(rbtree_t *tree, void *value); + +/// @brief Removes the value from the given tree. +/// @param tree The tree. +/// @param value The value to search. +/// @return Returns 1 if the value was removed, 0 otherwise. +int rbtree_tree_remove(rbtree_t *tree, void *value); + +/// @brief Returns the size of the tree. +/// @param tree The tree. +/// @return The size of the tree. +unsigned int rbtree_tree_size(rbtree_t *tree); + +/// @brief Interts the value inside the tree. +/// @param tree The tree. +/// @param node The node to insert. +/// @return 1 on success, 0 on failure. +int rbtree_tree_insert_node(rbtree_t *tree, rbtree_node_t *node); + +/// @brief Removes the value from the tree. +/// @param tree The tree. +/// @param value The value to remove. +/// @param node_cb The callback to call on the node before removing the node. +/// @return 1 on success, 0 on failure. +int rbtree_tree_remove_with_cb(rbtree_t *tree, void *value, rbtree_tree_node_f node_cb); + +// ============================================================================ +// Iterators. + +/// @brief Allocate the memory for the iterator. +/// @return Pointer to the allocated iterator. +rbtree_iter_t *rbtree_iter_alloc(); + +/// @brief Deallocate the memory for the iterator. +/// @param iter Pointer to the allocated iterator. +/// @return Pointer to the iterator itself. +rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *iter); + +/// @brief Allocate the memory for the iterator and initializes it. +/// @return Pointer to the allocated iterator. +rbtree_iter_t *rbtree_iter_create(); + +/// @brief Deallocate the memory for the iterator. +/// @param iter Pointer to the allocated iterator. +void rbtree_iter_dealloc(rbtree_iter_t *iter); + +/// @brief Initializes the iterator the the first element of the tree. +/// @param iter The iterator we want to initialize. +/// @param tree The tree. +/// @return Pointer to the first value of the tree. +void *rbtree_iter_first(rbtree_iter_t *iter, rbtree_t *tree); + +/// @brief Initializes the iterator the the last element of the tree. +/// @param iter The iterator we want to initialize. +/// @param tree The tree. +/// @return Pointer to the last value of the tree. +void *rbtree_iter_last(rbtree_iter_t *iter, rbtree_t *tree); + +/// @brief Moves the iterator to the next element. +/// @param iter The iterator. +/// @return Pointer to the next element. +void *rbtree_iter_next(rbtree_iter_t *iter); + +/// @brief Moves the iterator to the previous element. +/// @param iter The iterator. +/// @return Pointer to the previous element. +void *rbtree_iter_prev(rbtree_iter_t *iter); + +// ============================================================================ +// Tree debugging functions. + +/// @brief Checks the tree. +/// @param tree The tree. +/// @param root The root of the tree. +/// @return 1 on failure, 0 on success. +int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root); + +/// @brief Prints the tree using the provided callback. +/// @param tree The tree. +/// @param fun The print callback. +void rbtree_tree_print(rbtree_t *tree, rbtree_tree_node_f fun); diff --git a/mentos/inc/libc/spinlock.h b/mentos/inc/klib/spinlock.h similarity index 56% rename from mentos/inc/libc/spinlock.h rename to mentos/inc/klib/spinlock.h index 5976c70..792661c 100644 --- a/mentos/inc/libc/spinlock.h +++ b/mentos/inc/klib/spinlock.h @@ -1,32 +1,34 @@ /// MentOS, The Mentoring Operating system project /// @file spinlock.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "debug.h" -#include "stddef.h" -#include "irqflags.h" #include "stdatomic.h" -#include "scheduler.h" +/// Determines if the spinlock is free. #define SPINLOCK_FREE 0 - +/// Determines if the spinlock is busy. #define SPINLOCK_BUSY 1 /// @brief Spinlock structure. typedef atomic_t spinlock_t; /// @brief Initialize the spinlock. +/// @param spinlock The spinlock we initialize. void spinlock_init(spinlock_t *spinlock); /// @brief Try to lock the spinlock. +/// @param spinlock The spinlock we lock. void spinlock_lock(spinlock_t *spinlock); /// @brief Try to unlock the spinlock. +/// @param spinlock The spinlock we unlock. void spinlock_unlock(spinlock_t *spinlock); /// @brief Try to unlock the spinlock. -bool_t spinlock_trylock(spinlock_t *spinlock); +/// @param spinlock The spinlock we try to block. +/// @return 1 if succeeded, 0 otherwise. +int spinlock_trylock(spinlock_t *spinlock); diff --git a/mentos/inc/klib/stdatomic.h b/mentos/inc/klib/stdatomic.h new file mode 100644 index 0000000..78fca83 --- /dev/null +++ b/mentos/inc/klib/stdatomic.h @@ -0,0 +1,166 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdatomic.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" +#include "stdbool.h" +#include "compiler.h" + +/// @brief Standard structure for atomic operations (see below +/// for volatile explanation). +typedef volatile unsigned atomic_t; + +/// @brief The prefix used to lock. +#define LOCK_PREFIX "\n\tlock; " + +/// @brief Compile read-write barrier. +#define barrier() __asm__ __volatile__("" \ + : \ + : \ + : "memory") + +/// @brief Pause instruction to prevent excess processor bus usage. +#define cpu_relax() __asm__ __volatile__("pause\n" \ + : \ + : \ + : "memory") + +/// @brief Atomically sets the value of ptr with the value of x. +/// +/// @param ptr Pointer to the atomic variable to set. +/// @param x The value to set. +/// @return The final value of the atomic variable. +inline static int32_t atomic_set_and_test(atomic_t *ptr, int32_t x) +{ + __asm__ __volatile__("xchgl %0,%1" // instruction + : "=r"(x) // outputs + : "m"(*ptr), "0"(x) // inputs + : "memory"); // side effects + return x; +} + +/// @brief Atomically set ptr equal to i. +inline static void atomic_set(atomic_t *ptr, int32_t i) +{ + atomic_set_and_test(ptr, i); +} + +/// @brief Atomically read the integer value of ptr. +inline static int32_t atomic_read(const atomic_t *ptr) +{ + return READ_ONCE(*ptr); +} + +/// @brief Atomically add i to ptr. +inline static int32_t atomic_add(atomic_t const *ptr, int32_t i) +{ + int result = i; + __asm__ __volatile__(LOCK_PREFIX "xaddl %0, %1" + : "=r"(i) + : "m"(ptr), "0"(i) + : "memory", "cc"); + return result + i; +} + +/// @brief Atomically subtract i from ptr. +inline static int32_t atomic_sub(atomic_t *ptr, int32_t i) +{ + return atomic_add(ptr, -i); +} + +/// @brief Atomically add one to ptr. +inline static int32_t atomic_inc(atomic_t *ptr) +{ + return atomic_add(ptr, 1); +} + +/// @brief Atomically subtract one from ptr. +inline static int32_t atomic_dec(atomic_t *ptr) +{ + return atomic_add(ptr, -1); +} + +/// @brief Atomically add i to ptr and return true if the result is negative; +/// otherwise false. +inline static bool_t atomic_add_negative(atomic_t *ptr, int32_t i) +{ + return (bool_t)(atomic_add(ptr, i) < 0); +} + +/// @brief Atomically subtract i from ptr and return true if the result is +/// zero; otherwise false. +inline static bool_t atomic_sub_and_test(atomic_t *ptr, int32_t i) +{ + return (bool_t)(atomic_sub(ptr, i) == 0); +} + +/// @brief Atomically increment ptr by one and return true if the result is +/// zero; false otherwise. +inline static int32_t atomic_inc_and_test(atomic_t *ptr) +{ + return (bool_t)(atomic_inc(ptr) == 0); +} + +/// @brief Atomically decrement ptr by one and return true if zero; false +/// otherwise. +inline static int32_t atomic_dec_and_test(atomic_t *ptr) +{ + return (bool_t)(atomic_dec(ptr) == 0); +} + +/// @brief Atomically sets a bit in memory, using Bit Test And Set (bts). +/// @param offset The offset to the bit. +/// @param base The base address. +static inline void set_bit(int offset, volatile unsigned long *base) +{ + __asm__ __volatile__("btsl %[offset],%[base]" + : [base] "=m"(*(volatile long *)base) + : [offset] "Ir"(offset)); +} + +/// @brief Atomically clears a bit in memory. +/// @param offset The offset to the bit. +/// @param base The base address. +static inline void clear_bit(int offset, volatile unsigned long *base) +{ + __asm__ __volatile__("btrl %[offset],%[base]" + : [base] "=m"(*(volatile long *)base) + : [offset] "Ir"(offset)); +} + +/// @brief Atomically tests a bit in memory. +/// @param offset The offset to the bit. +/// @param base The base address. +/// @return 1 if the bit is set, 0 otherwise. +static inline int test_bit(int offset, volatile unsigned long *base) +{ + int old = 0; + __asm__ __volatile__("btl %[offset],%[base]\n" // Bit Test + "sbbl %[old],%[old]\n" // Return the previous value. + : [old] "=r"(old) + : [base] "m"(*(volatile long *)base), + [offset] "Ir"(offset)); + return old; +} + +// == Volatile Variable ======================================================= +// In C, and consequently C++, the volatile keyword was intended to: +// - allow access to memory-mapped I/O devices +// - allow uses of variables between setjmp and longjmp +// - allow uses of sig_atomic_t variables in signal handlers. +// +// Operations on volatile variables are not atomic, nor do they establish +// a proper happens-before relationship for threading like with the +// `__asm__` inline blocks. +// This is specified in the relevant standards (C, C++, POSIX, WIN32), and +// volatile variables are not thread-safe in the vast majority of current +// implementations. +// Thus, the usage of volatile keyword as a portable synchronization mechanism +// is discouraged by many C/C++ groups. + +// == xchg/xchgl ============================================================== +// \ No newline at end of file diff --git a/mentos/inc/libc/assert.h b/mentos/inc/libc/assert.h deleted file mode 100644 index 0148391..0000000 --- a/mentos/inc/libc/assert.h +++ /dev/null @@ -1,24 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file assert.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdio.h" -#include "panic.h" -#include "stdarg.h" - -/// @brief Function used to log the information of a failed assertion. -/// @param assertion The failed assertion. -/// @param file The file where the assertion is located. -/// @param line The line inside the file. -/// @param function The function where the assertion is. -void __assert_fail(const char *assertion, const char *file, unsigned int line, - const char *function); - -/// @brief Assert function. -#define assert(expression) \ - ((expression) ? (void)0 : \ - __assert_fail(#expression, __FILE__, __LINE__, __func__)) diff --git a/mentos/inc/libc/bitset.h b/mentos/inc/libc/bitset.h deleted file mode 100644 index 094150f..0000000 --- a/mentos/inc/libc/bitset.h +++ /dev/null @@ -1,42 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitset.h -/// @brief Bitset data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdbool.h" - -/// @brief Bitset structure. -typedef struct { - /// The internal data. - unsigned char *data; - /// The size of the bitset. - size_t size; -} bitset_t; - -// TODO: doxygen comment. -/// @brief -void bitset_init(bitset_t *set, size_t size); - -// TODO: doxygen comment. -/// @brief -void bitset_free(bitset_t *set); - -// TODO: doxygen comment. -/// @brief -void bitset_set(bitset_t *set, size_t bit); - -// TODO: doxygen comment. -/// @brief -void bitset_clear(bitset_t *set, size_t bit); - -// TODO: doxygen comment. -/// @brief -bool_t bitset_test(bitset_t *set, size_t bit); - -// TODO: doxygen comment. -/// @brief -signed long bitset_find_first_unset_bit(bitset_t *set); diff --git a/mentos/inc/libc/fcvt.h b/mentos/inc/libc/fcvt.h deleted file mode 100644 index a645dfc..0000000 --- a/mentos/inc/libc/fcvt.h +++ /dev/null @@ -1,27 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcvt.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// TODO: doxygen comment. -/// @brief -/// @param arg -/// @param ndigits -/// @param decpt -/// @param sign -/// @param buf -/// @result -char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); - -// TODO: doxygen comment. -/// @brief -/// @param arg -/// @param ndigits -/// @param decpt -/// @param sign -/// @param buf -/// @result -char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); diff --git a/mentos/inc/libc/hashmap.h b/mentos/inc/libc/hashmap.h deleted file mode 100644 index 32b94cf..0000000 --- a/mentos/inc/libc/hashmap.h +++ /dev/null @@ -1,80 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file hashmap.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "list.h" -#include "stdbool.h" - -//============================================================================== -// Opaque types. -typedef struct hashmap_entry_t hashmap_entry_t; -typedef struct hashmap_t hashmap_t; - -//============================================================================= -// Hashmap functions. - -// TODO: doxygen comment. -typedef size_t (*hashmap_hash_t)(void *key); - -// TODO: doxygen comment. -typedef bool_t (*hashmap_comp_t)(void *a, void *b); - -// TODO: doxygen comment. -typedef void (*hashmap_free_t)(void *); - -// TODO: doxygen comment. -typedef void *(*hashmap_dupe_t)(void *); - -//============================================================================== -// Hashmap creation and destruction. - -// TODO: doxygen comment. -extern hashmap_t *hashmap_create(size_t size); - -// TODO: doxygen comment. -extern hashmap_t *hashmap_create_int(size_t size); - -// TODO: doxygen comment. -extern void hashmap_free(hashmap_t *map); - -//============================================================================== -// Hashmap management. - -// TODO: doxygen comment. -extern void *hashmap_set(hashmap_t *map, void *key, void *value); - -// TODO: doxygen comment. -extern void *hashmap_get(hashmap_t *map, void *key); - -// TODO: doxygen comment. -extern void *hashmap_remove(hashmap_t *map, void *key); - -//============================================================================== -// Hashmap search. - -// TODO: doxygen comment. -extern bool_t hashmap_is_empty(hashmap_t *map); - -// TODO: doxygen comment. -extern bool_t hashmap_has(hashmap_t *map, void *key); - -// TODO: doxygen comment. -extern list_t *hashmap_keys(hashmap_t *map); - -// TODO: doxygen comment. -extern list_t *hashmap_values(hashmap_t *map); - -// TODO: doxygen comment. -extern size_t hashmap_string_hash(void *key); - -// TODO: doxygen comment. -extern bool_t hashmap_string_comp(void *a, void *b); - -// TODO: doxygen comment. -extern void *hashmap_string_dupe(void *key); - -//============================================================================== diff --git a/mentos/inc/libc/irqflags.h b/mentos/inc/libc/irqflags.h deleted file mode 100644 index b7145db..0000000 --- a/mentos/inc/libc/irqflags.h +++ /dev/null @@ -1,72 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file irqflags.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdint.h" - -/// @brief Enable IRQs. -inline static void irq_enable() -{ - __asm__ __volatile__("sti" ::: "memory"); -} - -// TODO: doxygen comment. -inline unsigned long get_eflags() -{ - unsigned long eflags; - /* "=rm" is safe here, because "pop" adjusts the stack before - * it evaluates its effective address -- this is part of the - * documented behavior of the "pop" instruction. - */ - __asm__ __volatile__("pushf ; pop %0" - : "=rm"(eflags) - : /* no input */ - : "memory"); - return eflags; -} - -/// @brief Enable IRQs (nested). -/// @details If called after calling irq_nested_disable, this function will -/// not activate IRQs if they were not active before. -inline static void irq_nested_enable(uint8_t flags) -{ - if (flags) { - irq_enable(); - } -} - -/// @brief Disable IRQs. -inline static void irq_disable() -{ - __asm__ __volatile__("cli" ::: "memory"); -} - -/// @brief Disable IRQs (nested). -/// @details Disable IRQs when unsure if IRQs were enabled at all. -/// This function together with irq_nested_enable can be used in -/// situations when interrupts shouldn't be activated if they were not -/// activated before calling this function. -inline static uint8_t irq_nested_disable() -{ - size_t flags; - __asm__ __volatile__("pushf; cli; pop %0" : "=r"(flags) : : "memory"); - if (flags & (1 << 9)) - return 1; - return 0; -} - -/// @brief Determines, if the interrupt flags (IF) is set. -inline static uint8_t is_irq_enabled() -{ - size_t flags; - __asm__ __volatile__("pushf; pop %0" : "=r"(flags) : : "memory"); - if (flags & (1 << 9)) { - return 1; - } - return 0; -} diff --git a/mentos/inc/libc/libgen.h b/mentos/inc/libc/libgen.h deleted file mode 100644 index 5f9bc61..0000000 --- a/mentos/inc/libc/libgen.h +++ /dev/null @@ -1,28 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file libgen.h -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "stddef.h" - -// TODO: doxygen comment. -/// @brief -/// @param out -/// @param cur -/// @param sep -/// @param max -/// @result -int parse_path(char *out, char **cur, char sep, size_t max); - -// TODO: doxygen comment. -/// @brief -/// @param path -/// @result -char *dirname(const char *path); - -// TODO: doxygen comment. -/// @brief -/// @param path -/// @result -char *basename(const char *path); diff --git a/mentos/inc/libc/limits.h b/mentos/inc/libc/limits.h deleted file mode 100644 index bd5c67f..0000000 --- a/mentos/inc/libc/limits.h +++ /dev/null @@ -1,47 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file limits.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// Number of bits in a `char'. -#define CHAR_BIT 8 - -/// Minimum value a `signed char' can hold. - -#define SCHAR_MIN (-128) - -/// Maximum value a `signed char' can hold. -#define SCHAR_MAX 127 - -/// Maximum value a `char' can hold. (Minimum is 0.) -#define CHAR_MAX 255 - -/// Minimum value a `signed short int' can hold. -#define SHRT_MIN (-32768) - -/// Maximum value a `signed short int' can hold. -#define SHRT_MAX 32767 - -/// Minimum value a `signed int' can hold. -#define INT_MIN (-INT_MAX - 1) - -/// Maximum values a `signed int' can hold. -#define INT_MAX 2147483647 - -/// Maximum value an `unsigned int' can hold. (Minimum is 0.) -#define UINT_MAX 4294967295U - -/// Minimum value a `signed long int' can hold. -#define LONG_MAX 2147483647L - -/// Maximum value a `signed long int' can hold. -#define LONG_MIN (-LONG_MAX - 1L) - -/// Maximum number of characters in a file name. -#define NAME_MAX 255 - -/// Maximum number of characters in a path name. -#define PATH_MAX 4096 \ No newline at end of file diff --git a/mentos/inc/libc/list.h b/mentos/inc/libc/list.h deleted file mode 100644 index c8852ab..0000000 --- a/mentos/inc/libc/list.h +++ /dev/null @@ -1,107 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file list.h -/// @brief An implementation for generic list. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "stddef.h" -#include "stdbool.h" - -/// @brief Represent the node of a list. -typedef struct listnode_t { - /// A pointer to the value. - void *value; - /// The previous node. - struct listnode_t *prev; - /// The next node. - struct listnode_t *next; -} listnode_t; - -/// @brief Represent the list. -typedef struct list_t { - /// The first element of the list. - listnode_t *head; - /// The last element of the list. - listnode_t *tail; - /// The size of the list. - size_t size; -} list_t; - -/// @brief Macro used to iterate through a list. -#define listnode_foreach(it, list) \ - for (listnode_t * (it) = (list)->head; (it) != NULL; (it) = (it)->next) - -/// @brief Create a list and set head, tail to NULL, and size to 0. -list_t *list_create(); - -/// @brief Get list size. -size_t list_size(list_t *list); - -/// @brief Checks if the list is empty. -bool_t list_empty(list_t *list); - -/// @brief Insert a value at the front of list. -listnode_t *list_insert_front(list_t *list, void *value); - -/// @brief Insert a value at the back of list. -listnode_t *list_insert_back(list_t *list, void *value); - -/// @brief Insert a value at the back of list. -void list_insert_node_back(list_t *list, listnode_t *item); - -/// @brief Given a listnode, remove it from lis. -void *list_remove_node(list_t *list, listnode_t *node); - -/// @brief Remove a value at the front of list. -void *list_remove_front(list_t *list); - -/// @brief Remove a value at the back of list. -void *list_remove_back(list_t *list); - -/// @brief Searches the node of the list which points at the given value. -listnode_t *list_find(list_t *list, void *value); - -/// @brief Insert after tail of list(same as insert back). -void list_push(list_t *list, void *value); - -/// @brief Remove and return the tail of the list. -/// @details User is responsible for freeing the returned node and the value. -listnode_t *list_pop_back(list_t *list); - -/// @brief Remove and return the head of the list. -/// @details User is responsible for freeing the returned node and the value. -listnode_t *list_pop_front(list_t *list); - -/// @brief Insert before head of list(same as insert front). -void list_enqueue(list_t *list, void *value); - -/// @brief Remove and return tail of list(same as list_pop). -listnode_t *list_dequeue(list_t *list); - -/// @brief Get the value of the first element but not remove it. -void *list_peek_front(list_t *list); - -/// @brief Get the value of the last element but not remove it. -void *list_peek_back(list_t *list); - -/// @brief Destory a list. -void list_destroy(list_t *list); - -/// @brief Destroy a node of the list. -void listnode_destroy(listnode_t *node); - -/// @brief Does the list contain a value (Return -1 if list element is not -/// found). -int list_contain(list_t *list, void *value); - -/// @brief Returns the node at the given index. -listnode_t *list_get_node_by_index(list_t *list, size_t index); - -/// @brief Removes a node from the list at the given index. -void *list_remove_by_index(list_t *list, size_t index); - -/// @brief Append source at the end of target and DESTROY source. -void list_merge(list_t *target, list_t *source); diff --git a/mentos/inc/libc/list_head.h b/mentos/inc/libc/list_head.h deleted file mode 100644 index 2af28ec..0000000 --- a/mentos/inc/libc/list_head.h +++ /dev/null @@ -1,129 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file list_head.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// @brief Structure used to implement the list_head data structure. -typedef struct list_head { - /// @brief The previous element. - struct list_head *prev; - /// @brief The subsequent element. - struct list_head *next; -} list_head; - -/// @brief Get the struct for this entry. -/// @param ptr The &struct list_head pointer. -/// @param type The type of the struct this is embedded in. -/// @param member The name of the list_head within the struct. -#define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) - -/// @brief Iterates over a list. -/// @param pos The &struct list_head to use as a loop cursor. -/// @param head The head for your list. -#define list_for_each(pos, head) \ - for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) - -/// @brief Iterates over a list backwards. -/// @param pos The &struct list_head to use as a loop cursor. -/// @param head The head for your list. -#define list_for_each_prev(pos, head) \ - for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) - -/// @brief Iterates over a list safe against removal of list entry. -/// @param pos The &struct list_head to use as a loop cursor. -/// @param store Another &struct list_head to use as temporary storage. -/// @param head The head for your list. -#define list_for_each_safe(pos, store, head) \ - for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); \ - (pos) = (store), (store) = (pos)->next) - -/// @brief Initializes the list_head. -/// @param head The head for your list. -#define list_head_init(head) (head)->next = (head)->prev = (head) - -/// @brief Insert element l2 after l1. -static inline void list_head_insert_after(list_head *l1, list_head *l2) -{ - // [La]->l1 La<-[l1]->Lb <-[l2]-> l1<-[Lb] - - list_head *l1_next = l1->next; - // [La]->l1 La<-[l1]->l2 <-[l2]-> l1<-[Lb] - l1->next = l2; - // [La]->l1 La<-[l1]->l2 l1<-[l2]-> l1<-[Lb] - l2->prev = l1; - // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l1<-[Lb] - l2->next = l1_next; - // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l2<-[Lb] - l1_next->prev = l2; -} - -/// @brief Insert element l2 before l1. -static inline void list_head_insert_before(list_head *l1, list_head *l2) -{ - // [La]->l1 [l2] La<-[l1]->Lb l1<-[Lb] - - list_head *l1_prev = l1->prev; - // [La]->l2 [l2] La<-[l1]->Lb l1<-[Lb] - l1_prev->next = l2; - // [La]->l2 La<-[l2] La<-[l1]->Lb l1<-[Lb] - l2->prev = l1_prev; - // [La]->l2 La<-[l2]->l1 La<-[l1]->Lb l1<-[Lb] - l2->next = l1; - // [La]->l2 La<-[l2]->l1 l2<-[l1]->Lb l1<-[Lb] - l1->prev = l2; -} - -/// @brief Remove l from the list. -/// @param l The element to remove. -static inline void list_head_del(list_head *l) -{ - // [La]->l La<-[l]->Lb l<-[Lb] - - // [La]->Lb La<-[l]->Lb l<-[Lb] - l->prev->next = l->next; - // [La]->Lb La<-[l]->Lb La<-[Lb] - l->next->prev = l->prev; - // [La]->Lb l<-[l]->l La<-[Lb] - l->next = l->prev = l; -} - -/// @brief Tests whether the given list is empty. -/// @param head The list to check. -/// @return 1 if empty, 0 otherwise. -static inline int list_head_empty(list_head const *head) -{ - return head->next == head; -} - -/// Insert a new entry between two known consecutive entries. -static inline void __list_add(list_head *new, list_head *prev, list_head *next) -{ - // [prev]-> <-[new]-> <-[next] - - // [prev]-> <-[new]-> new<-[next] - next->prev = new; - // [prev]-> <-[new]->next new<-[next] - new->next = next; - // [prev]-> prev<-[new]->next new<-[next] - new->prev = prev; - // [prev]->new prev<-[new]->next new<-[next] - prev->next = new; -} - -/// @brief Insert element l2 before l1. -static inline void list_head_add(list_head *new, list_head *head) -{ - __list_add(new, head, head->next); -} - -/// @brief Insert element l2 before l1. -static inline void list_head_add_tail(list_head *new, list_head *head) -{ - __list_add(new, head->prev, head); -} diff --git a/mentos/inc/libc/math.h b/mentos/inc/libc/math.h deleted file mode 100644 index e41a9cc..0000000 --- a/mentos/inc/libc/math.h +++ /dev/null @@ -1,131 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file math.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// @brief The absolute value. -#define abs(a) (((a) < 0) ? -(a) : (a)) - -/// @brief The max of the two values. -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -/// @brief The min of the two values. -#define min(a, b) (((a) < (b)) ? (a) : (b)) - -/// @brief The sign the the passed value. -#define sign(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0)) - -/// @brief Returns a rounded up, away from zero, to the nearest multiple of b. -#define ceil(NUMBER, BASE) (((NUMBER) + (BASE)-1) & ~((BASE)-1)) - -/// @brief e -#define M_E 2.7182818284590452354 - -/// @brief log_2 e -#define M_LOG2E 1.4426950408889634074 - -/// @brief log_10 e -#define M_LOG10E 0.43429448190325182765 - -/// @brief log_e 2 -#define M_LN2 0.69314718055994530942 - -/// @brief log_e 10 -#define M_LN10 2.30258509299404568402 - -/// @brief pi -#define M_PI 3.14159265358979323846 - -/// @brief pi / 2 -#define M_PI_2 1.57079632679489661923 - -/// @brief pi / 4 -#define M_PI_4 0.78539816339744830962 - -/// @brief 1 / pi -#define M_1_PI 0.31830988618379067154 - -/// @brief 2 / pi -#define M_2_PI 0.63661977236758134308 - -/// @brief 2 / sqrt(pi) -#define M_2_SQRTPI 1.12837916709551257390 - -/// @brief sqrt(2) -#define M_SQRT2 1.41421356237309504880 - -/// @brief 1 / sqrt(2) -#define M_SQRT1_2 0.70710678118654752440 - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -double round(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -double floor(double x); - -/// @brief Power function. -/// @param x First number. -/// @param y Second number. -/// @result Power between number x and y. -double pow(double x, double y); - -// TODO: doxygen comment. -/// @brief -/// @param base -/// @param value -/// @result -long find_nearest_pow_greater(double base, double value); - -/// @brief Exponential function. -/// @param x Value of the exponent. -double exp(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -double fabs(double x); - -/// @brief Square root function. -/// @param x Topic of the square root. -double sqrt(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -int isinf(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -int isnan(double x); - -/// @brief Logarithm function in base 10. -/// @param x Topic of the logarithm function. -double log10(double x); - -/// @brief Natural logarithm function. -/// @param x Topic of the logarithm function. -double ln(double x); - -/// @brief Logarithm function in base x. -/// @brief x Base of the logarithm. -/// @param y Topic of the logarithm function. -double logx(double x, double y); - -/// @brief Breaks x into an integral and a fractional part. -/// The integer part is stored in the object pointed by intpart, and the -/// fractional part is returned by the function. Both parts have the same -/// sign as x. -double modf(double x, double *intpart); diff --git a/mentos/inc/libc/ordered_array.h b/mentos/inc/libc/ordered_array.h deleted file mode 100644 index b4cf225..0000000 --- a/mentos/inc/libc/ordered_array.h +++ /dev/null @@ -1,53 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ordered_array.h -/// @brief Interface for creating, inserting and deleting from ordered arrays. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" - -/// @brief This array is insertion sorted - it always remains in a sorted -/// state (between calls). It can store anything that can be cast to a -/// void* -- so a uint32_t, or any pointer. -typedef void *array_type_t; - -/// @brief A predicate should return nonzero if the first argument is less -/// than the second. Else it should return zero. -typedef int8_t (*lessthan_predicate_t)(array_type_t, array_type_t); - -/// @brief Structure which holds information concerning an ordered array. -typedef struct ordered_array_t { - /// Pointer to the array. - array_type_t *array; - /// The size of the array. - uint32_t size; - /// The maximum size of the array. - uint32_t max_size; - /// Ordering fucntion. - lessthan_predicate_t less_than; -} ordered_array_t; - -/// @brief A standard less than predicate. -int8_t standard_lessthan_predicate(array_type_t a, array_type_t b); - -/// @brief Create an ordered array. -ordered_array_t create_ordered_array(uint32_t max_size, - lessthan_predicate_t less_than); - -/// @brief Set the ordered array. -ordered_array_t place_ordered_array(void *addr, uint32_t max_size, - lessthan_predicate_t less_than); - -/// @brief Destroy an ordered array. -void destroy_ordered_array(ordered_array_t *array); - -/// @brief Add an item into the array. -void insert_ordered_array(array_type_t item, ordered_array_t *array); - -/// @brief Lookup the item at index i. -array_type_t lookup_ordered_array(uint32_t i, ordered_array_t *array); - -/// @brief Deletes the item at location i from the array. -void remove_ordered_array(uint32_t i, ordered_array_t *array); diff --git a/mentos/inc/libc/queue.h b/mentos/inc/libc/queue.h deleted file mode 100644 index 599d507..0000000 --- a/mentos/inc/libc/queue.h +++ /dev/null @@ -1,76 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file queue.h -/// @brief Implementation of queue data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdbool.h" - -/// @brief A node of the queue. -typedef struct queue_node_t { - /// The wrapped data. - void *data; - /// The next node of the queue. - struct queue_node_t *next; -} queue_node_t; - -/// @brief The queue. -typedef struct queue_t { - /// The front of the queue. - queue_node_t *front; - /// The back of the queue. - queue_node_t *back; - /// The size of the data contained inside the queue. - size_t data_size; -} * queue_t; - -/// @brief Creates a queue. -/// @param data_size The size of the stored elements. -/// @return The created queue. -queue_t queue_create(size_t data_size); - -/// @brief Destroys the queue. -/// @param queue The queue to be destroyed. -/// @return If the queue is destroyed. -bool_t queue_destroy(queue_t queue); - -/// @brief Returns if the queue is empty. -/// @param queue The queue. -bool_t queue_is_empty(queue_t queue); - -/// @brief Allows to add data to the queue. -/// @param queue The queue. -/// @param data The data to add. -/// @return If the data has been pushed inside the queue. -bool_t queue_enqueue(queue_t queue, void *data); - -/// @brief Removes the first element of the queue. -/// @param queue The queue. -/// @return If the data has been removed. -bool_t queue_dequeue(queue_t queue); - -/// @brief Returns the first element of the queue. -/// @param queue The queue. -/// @param data The data. -/// @return If the data has been correctly retrieved. -bool_t queue_front(queue_t queue, void *data); - -/// @brief Returns the last element of the queue. -/// @param queue The queue. -/// @param data The data. -/// @return If the data has been correctly retrieved. -bool_t queue_back(queue_t queue, void *data); - -/// @brief Returns the first element of the queue and removes it. -/// @param queue The queue. -/// @param data The data. -/// @return If the data has been correctly retrieved. -bool_t queue_front_and_dequeue(queue_t queue, void *data); - -/// @brief Deletes all the elements inside the queue. -/// @param queue The queue. -/// @return If the queue has been cleared. -bool_t queue_clear(queue_t queue); diff --git a/mentos/inc/libc/rbtree.h b/mentos/inc/libc/rbtree.h deleted file mode 100644 index b2c9806..0000000 --- a/mentos/inc/libc/rbtree.h +++ /dev/null @@ -1,134 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file rbtree.h -/// @brief Implementation of red black tree data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -#ifndef RBTREE_ITER_MAX_HEIGHT - -/// Tallest allowable tree to iterate. -#define RBTREE_ITER_MAX_HEIGHT 64 -#endif - -//============================================================================== -// Opaque types. - -// TODO: doxygen comment. -typedef struct rbtree_t rbtree_t; - -// TODO: doxygen comment. -typedef struct rbtree_node_t rbtree_node_t; - -// TODO: doxygen comment. -typedef struct rbtree_iter_t rbtree_iter_t; - -//============================================================================== -// Comparison functions. - -// TODO: doxygen comment. -typedef int (*rbtree_tree_cmp_f)(rbtree_t *self, rbtree_node_t *a, void *arg); - -// TODO: doxygen comment. -typedef int (*rbtree_tree_node_cmp_f)(rbtree_t *self, rbtree_node_t *a, - rbtree_node_t *b); - -// TODO: doxygen comment. -typedef void (*rbtree_tree_node_f)(rbtree_t *self, rbtree_node_t *node); - -//============================================================================== -// Node management functions. - -// TODO: doxygen comment. -rbtree_node_t *rbtree_node_alloc(); - -// TODO: doxygen comment. -rbtree_node_t *rbtree_node_create(void *value); - -// TODO: doxygen comment. -rbtree_node_t *rbtree_node_init(rbtree_node_t *self, void *value); - -// TODO: doxygen comment. -void *rbtree_node_get_value(rbtree_node_t *self); - -// TODO: doxygen comment. -void rbtree_node_dealloc(rbtree_node_t *self); - -//============================================================================== -// Tree management functions. - -// TODO: doxygen comment. -rbtree_t *rbtree_tree_alloc(); - -// TODO: doxygen comment. -rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f cmp); - -// TODO: doxygen comment. -rbtree_t *rbtree_tree_init(rbtree_t *self, rbtree_tree_node_cmp_f cmp); - -// TODO: doxygen comment. -void rbtree_tree_dealloc(rbtree_t *self, rbtree_tree_node_f node_cb); - -// TODO: doxygen comment. -void *rbtree_tree_find(rbtree_t *self, void *value); - -// TODO: doxygen comment. -void *rbtree_tree_find_by_value(rbtree_t *self, rbtree_tree_cmp_f cmp_fun, - void *value); - -// TODO: doxygen comment. -int rbtree_tree_insert(rbtree_t *self, void *value); - -// TODO: doxygen comment. -int rbtree_tree_remove(rbtree_t *self, void *value); - -// TODO: doxygen comment. -size_t rbtree_tree_size(rbtree_t *self); - -// TODO: doxygen comment. -int rbtree_tree_insert_node(rbtree_t *self, rbtree_node_t *node); - -// TODO: doxygen comment. -int rbtree_tree_remove_with_cb(rbtree_t *self, void *value, - rbtree_tree_node_f node_cb); - -//============================================================================== -// Iterators. - -// TODO: doxygen comment. -rbtree_iter_t *rbtree_iter_alloc(); - -// TODO: doxygen comment. -rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *self); - -// TODO: doxygen comment. -rbtree_iter_t *rbtree_iter_create(); - -// TODO: doxygen comment. -void rbtree_iter_dealloc(rbtree_iter_t *self); - -// TODO: doxygen comment. -void *rbtree_iter_first(rbtree_iter_t *self, rbtree_t *tree); - -// TODO: doxygen comment. -void *rbtree_iter_last(rbtree_iter_t *self, rbtree_t *tree); - -// TODO: doxygen comment. -void *rbtree_iter_next(rbtree_iter_t *self); - -// TODO: doxygen comment. -void *rbtree_iter_prev(rbtree_iter_t *self); - -//============================================================================== -// Tree debugging functions. - -// TODO: doxygen comment. -int rbtree_tree_test(rbtree_t *self, rbtree_node_t *root); - -// TODO: doxygen comment. -void rbtree_tree_print(rbtree_t *self, rbtree_tree_node_f fun); - -//============================================================================== diff --git a/mentos/inc/libc/stdatomic.h b/mentos/inc/libc/stdatomic.h deleted file mode 100644 index 8ec33ff..0000000 --- a/mentos/inc/libc/stdatomic.h +++ /dev/null @@ -1,109 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdatomic.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "stdbool.h" -#include "compiler.h" - -/// @brief Standard structure for atomic operations. -typedef volatile unsigned atomic_t; - -/// @brief At declaration, initialize an atomic_t to i. -#define ATOMIC_INIT(i) \ - { \ - (i) \ - } - -/// @brief The prefix used to lock. -#define LOCK_PREFIX "\n\tlock; " - -/// @brief Compile read-write barrier. -#define barrier() asm volatile("" : : : "memory") - -/// @brief Pause instruction to prevent excess processor bus usage. -#define cpu_relax() asm volatile("pause\n" : : : "memory") - -// TODO: doxygen comment. -inline static int32_t atomic_set_and_test(atomic_t const *ptr, int32_t i) -{ - __asm__ __volatile__("xchgl %0,%1" - : "=r"(i) - : "m"(*(volatile unsigned *)ptr), "0"(i) - : "memory"); - // __asm__ __volatile__("xchgl %0, %1" : "=r"(i) : "m"(ptr), "0"(i) : "memory"); - return i; -} - -/// @brief Atomically set ptr equal to i. -inline static void atomic_set(atomic_t *ptr, int32_t i) -{ - atomic_set_and_test(ptr, i); -} - -/// @brief Atomically read the integer value of ptr. -inline static int32_t atomic_read(const atomic_t *ptr) -{ - return READ_ONCE(*ptr); -} - -/// @brief Atomically add i to ptr. -inline static int32_t atomic_add(atomic_t const *ptr, int32_t i) -{ - int result = i; - asm volatile(LOCK_PREFIX "xaddl %0, %1" - : "=r"(i) - : "m"(ptr), "0"(i) - : "memory", "cc"); - return result + i; -} - -/// @brief Atomically subtract i from ptr. -inline static int32_t atomic_sub(atomic_t *ptr, int32_t i) -{ - return atomic_add(ptr, -i); -} - -/// @brief Atomically add one to ptr. -inline static int32_t atomic_inc(atomic_t *ptr) -{ - return atomic_add(ptr, 1); -} - -/// @brief Atomically subtract one from ptr. -inline static int32_t atomic_dec(atomic_t *ptr) -{ - return atomic_add(ptr, -1); -} - -/// @brief Atomically add i to ptr and return true if the result is negative; -/// otherwise false. -inline static bool_t atomic_add_negative(atomic_t *ptr, int32_t i) -{ - return (bool_t)(atomic_add(ptr, i) < 0); -} - -/// @brief Atomically subtract i from ptr and return true if the result is -/// zero; otherwise false. -inline static bool_t atomic_sub_and_test(atomic_t *ptr, int32_t i) -{ - return (bool_t)(atomic_sub(ptr, i) == 0); -} - -/// @brief Atomically increment ptr by one and return true if the result is -/// zero; false otherwise. -inline static int32_t atomic_inc_and_test(atomic_t *ptr) -{ - return (bool_t)(atomic_inc(ptr) == 0); -} - -/// @brief Atomically decrement ptr by one and return true if zero; false -/// otherwise. -inline static int32_t atomic_dec_and_test(atomic_t *ptr) -{ - return (bool_t)(atomic_dec(ptr) == 0); -} diff --git a/mentos/inc/libc/stdbool.h b/mentos/inc/libc/stdbool.h deleted file mode 100644 index 9a52976..0000000 --- a/mentos/inc/libc/stdbool.h +++ /dev/null @@ -1,16 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdbool.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// @brief Define boolean value. -typedef enum bool_t -{ - /// [0] False. - false, - /// [1] True. - true -} __attribute__ ((__packed__)) bool_t; diff --git a/mentos/inc/libc/stddef.h b/mentos/inc/libc/stddef.h deleted file mode 100644 index 9098592..0000000 --- a/mentos/inc/libc/stddef.h +++ /dev/null @@ -1,87 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stddef.h -/// @brief Define basic data types. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#ifndef NULL - -/// @brief Define NULL. -#define NULL ((void *)0) - -#endif - -#ifndef EOF - -/// @brief Define End-Of-File. -#define EOF (-1) - -#endif - -/// @brief Define the size of a buffer. -#define BUFSIZ 512 - -/// @brief Define the size of the kernel. -#define KERNEL_SIZE 0x200 - -#ifndef TRUE - -/// @brief Define the value of true. -#define TRUE 1 - -#endif - -#ifndef FALSE - -/// @brief Define the value of false. -#define FALSE 0 - -#endif - -/// @brief Define the byte type. -typedef unsigned char byte_t; - -/// @brief Define the generic size type. -typedef unsigned long size_t; - -/// @brief Define the generic signed size type. -typedef long ssize_t; - -/// @brief Define the type of an inode. -typedef unsigned int ino_t; - -// TODO: doxygen comment. -typedef unsigned int dev_t; - -/// @brief The type of user-id. -typedef unsigned int uid_t; - -/// @brief The type of group-id. -typedef unsigned int gid_t; - -/// @brief The type of offset. -typedef unsigned int off_t; - -/// @brief The type of mode. -typedef unsigned int mode_t; - -// TODO: doxygen comment. -typedef mode_t pgprot_t; - -// TODO: doxygen comment. -typedef unsigned long int ulong; - -// TODO: doxygen comment. -typedef unsigned short int ushort; - -// TODO: doxygen comment. -typedef unsigned int uint; - -// TODO: doxygen comment. -typedef int key_t; - -// TODO: doxygen comment. -// Check with clock.h -typedef long __time_t; diff --git a/mentos/inc/libc/stdio.h b/mentos/inc/libc/stdio.h deleted file mode 100644 index 47be959..0000000 --- a/mentos/inc/libc/stdio.h +++ /dev/null @@ -1,50 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdio.h -/// @brief Standard I/0 functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdarg.h" -#include "stddef.h" - -/// @brief The maximum number of digits of an integer. -#define MAX_DIGITS_IN_INTEGER 11 -/// @brief The size of 'gets' buffer. -#define GETS_BUFFERSIZE 255 - -#ifndef EOF -/// @brief Define the End-Of-File. -#define EOF (-1) -#endif - -/// @brief Writes the given character to the standard output (stdout). -void putchar(int character); - -/// @brief Writes the string pointed by str to the standard output (stdout) -/// and appends a newline character ('\n'). -void puts(char *str); - -/// @brief Returns the next character from the standard input (stdin). -int getchar(void); - -/// @brief Reads characters from the standard input (stdin) and stores them -/// as a C string into str until a newline character or the end-of-file is -/// reached. -char *gets(char *str); - -/// @brief Convert the given string to an integer. -int atoi(const char *str); - -/// @brief Write formatted output to stdout. -int printf(const char *, ...); - -/// @brief Read formatted input from stdin. -size_t scanf(const char *, ...); - -/// @brief Write formatted output to the string str from argument list ARG. -int vsprintf(char *str, const char *fmt, va_list args); - -/// @brief Write formatted output to the string str. -int sprintf(char *str, const char *fmt, ...); diff --git a/mentos/inc/libc/stdlib.h b/mentos/inc/libc/stdlib.h deleted file mode 100644 index e508a61..0000000 --- a/mentos/inc/libc/stdlib.h +++ /dev/null @@ -1,25 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdlib.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// @brief Malloc based on the number of elements. -void *calloc(size_t element_number, size_t element_size); - -void *malloc(unsigned int size); - -void free(void * p); - -/// The maximum value returned by the rand function. -#define RAND_MAX ((1U << 31U) - 1U) - -/// @brief Allows to set the seed of the random value generator. -void srand(int x); - -/// @brief Generates a random value. -int rand(); diff --git a/mentos/inc/libc/strerror.h b/mentos/inc/libc/strerror.h deleted file mode 100644 index 631db12..0000000 --- a/mentos/inc/libc/strerror.h +++ /dev/null @@ -1,12 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file strerror.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "errno.h" - -// TODO: doxygen comment. -char *strerror(int errnum); diff --git a/mentos/inc/libc/string.h b/mentos/inc/libc/string.h deleted file mode 100644 index c3a4fdf..0000000 --- a/mentos/inc/libc/string.h +++ /dev/null @@ -1,159 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file string.h -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "list.h" -#include "stddef.h" - -/// @brief Copies the first num characters of source to destination. -char *strncpy(char *destination, const char *source, size_t num); - -/// @brief Compares up to n characters of s1 to those of s2. -int strncmp(const char *s1, const char *s2, size_t n); - -/// @brief Case insensitive string compare. -int stricmp(const char *s1, const char *s2); - -/// @brief Case-insensitively compare up to n characters of s1 to those of s2. -int strnicmp(const char *s1, const char *s2, size_t n); - -/// @brief Returns a pointer to the first occurrence of ch in str. -char *strchr(const char *s, int ch); - -/// @brief Returns a pointer to the last occurrence of ch in str. -char *strrchr(const char *s, int ch); - -/// @brief Returns a pointer to the first occurrence of s2 in s1, or NULL if -/// s2 is not part of s1. -char *strstr(const char *s1, const char *s2); - -/// @brief Returns the length of the initial portion of string which consists -/// only of characters that are part of control. -size_t strspn(const char *string, const char *control); - -/// @brief Calculates the length of the initial segment of string which -/// consists entirely of characters not in control. -size_t strcspn(const char *string, const char *control); - -/// @brief Finds the first character in the string string that matches any -/// character specified in control. -char *strpbrk(const char *string, const char *control); - -/// @brief Make a copy of the given string. -char *strdup(const char *s); - -/// @brief Make a copy of the given string. -char *kstrdup(const char *s); - -/// @brief Appends the string pointed to, by s2 to the end of the string -/// pointed to, by s1 up to n characters long. -char *strncat(char *s1, const char *s2, size_t n); - -/// @brief Fill the string s with the character c, to the given length n. -char *strnset(char *s, int c, size_t n); - -/// @brief Fill the string s with the character c. -char *strset(char *s, int c); - -/// @brief Reverse the string s. -char *strrev(char *s); - -// TODO: Check behaviour & doxygen comment. -char *strtok(char *str, const char *delim); - -// TODO: Check behaviour & doxygen comment. -char *strtok_r(char *string, const char *control, char **lasts); - -/// @brief Another function to copy n characters from str2 to str1. -void *memmove(void *dst, const void *src, size_t n); - -/// @brief Searches for the first occurrence of the character c (an unsigned -/// char) in the first n bytes of the string pointed to, by the -/// argument str. -void *memchr(const void *str, int c, size_t n); - -char *strlwr(char *s); - -char *strupr(char *s); - -/// @brief Copies the first n bytes from memory area src to memory area dest, -/// stopping when the character c is found. -void *memccpy(void *dst, const void *src, int c, size_t n); - -/// @brief Copy a block of memory, handling overlap. -/// @param _dst Pointer to the destination. -/// @param _src Pointer to the source. -/// @param num Number of bytes to be copied. -/// @return Pointer to the destination. -void *memcpy(void *_dst, const void *_src, size_t num); - -/// @brief Compares the first n bytes of str1 and str2. -int memcmp(const void *str1, const void *str2, size_t n); - -/// @brief Sets the first num bytes of the block of memory pointed by ptr -/// to the specified value. -/// @param ptr Pointer to the block of memory to set. -/// @param value Value to be set. -/// @param num Number of bytes to be set to the given value. -/// @return The same ptr. -void *memset(void *ptr, int value, size_t num); - -/// @brief Copy the string src into the array dst. -char *strcpy(char *dst, const char *src); - -/// @brief Appends a copy of the string src to the string dst. -char *strcat(char *dst, const char *src); - -/// @brief Checks if the two strings are equal. -int strcmp(const char *s1, const char *s2); - -/// @brief Returns the lenght of the string s. -size_t strlen(const char *s); - -/// @brief Returns the number of characters inside s, excluding the -/// terminating null byte ('\0'), but at most count. -size_t strnlen(const char *s, size_t count); - -/// @brief Separate a string in token according to the delimiter. -/// If str is NULL, the scanning will continue for the previous string. -/// It can be bettered. -char *strtok(char *str, const char *delim); - -/// @brief Compare num characters of s2 and s1. -int _kstrncmp(const char *s1, const char *s2, size_t num); - -/// @brief Removes the whitespaces in from of str. -char *trim(char *str); - -/// @brief Create a copy of str. -char *strdup(const char *src); - -/// @brief Separate stringp based on delim. -char *strsep(char **stringp, const char *delim); - -/// @brief Split a string into list of strings. -list_t *str_split(const char *str, const char *delim, unsigned int *num); - -/// @brief Reconstruct a string from list using delim as delimiter. -char *list2str(list_t *list, const char *delim); - -/// @brief Move the number "num" into a string. -/// @param buffer The string containing the number. -/// @param num The number to convert. -/// @param base The base used to convert. -void int_to_str(char *buffer, unsigned int num, unsigned int base); - -/// @brief Transforms num into a string. -void _knntos(char *buffer, int num, int base); - -/// @brief Replaces the occurences of find with replace inside str. -char *replace_char(char *str, char find, char replace); - -/// @brief Converts a file mode (the type and permission information associated -/// with an inode) into a symbolic string which is stored in the location -/// referenced by p. -void strmode(mode_t mode, char *p); diff --git a/mentos/inc/libc/tree.h b/mentos/inc/libc/tree.h deleted file mode 100644 index 097a36a..0000000 --- a/mentos/inc/libc/tree.h +++ /dev/null @@ -1,77 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file tree.h -/// @brief General-purpose tree implementation. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "list.h" - -/// @brief A node of the tree. -typedef struct tree_node_t { - /// The value stored by the node. - void *value; - /// Pointer to the list of childrens. - list_t *children; - /// Pointer to the parent. - struct tree_node_t *parent; -} tree_node_t; - -/// @brief Represents the tree. -typedef struct tree_t { - /// Number of nodes inside the tree. - size_t nodes; - /// Pointer to the root of the tree. - tree_node_t *root; -} tree_t; - -typedef uint8_t (*tree_comparator_t)(void *, void *); - -/// @brief Creates a new tree. -tree_t *tree_create(); - -/// @brief Sets the root node for a new tree. -tree_node_t *tree_set_root(tree_t *tree, void *value); - -/// @brief Free the contents of a node and its children, -/// but not the nodes themselves. -void tree_node_destroy(tree_node_t *node); - -/// @brief Free the contents of a tree, but not the nodes. -void tree_destroy(tree_t *tree); - -/// @brief Free all of the nodes in a tree, but not their contents. -void tree_free(tree_t *tree); - -/// @brief Create a new tree node pointing to the given value. -tree_node_t *tree_node_create(void *value); - -/// @brief Insert a node as a child of parent. -void tree_node_insert_child_node(tree_t *tree, tree_node_t *parent, - tree_node_t *node); - -/// @brief Insert a (fresh) node as a child of parent. -tree_node_t *tree_node_insert_child(tree_t *tree, tree_node_t *parent, - void *value); - -/// @brief Recursive node part of tree_find_parent. -tree_node_t *tree_node_find_parent(tree_node_t *haystack, tree_node_t *needle); - -/// @brief Remove a node when we know its parent; update node counts for the -/// tree. -void tree_node_parent_remove(tree_t *tree, tree_node_t *parent, - tree_node_t *node); - -/// @brief Remove an entire branch given its root. -void tree_node_remove(tree_t *tree, tree_node_t *node); - -/// @brief Remove this node and move its children into its parent's -/// list of children. -void tree_remove(tree_t *tree, tree_node_t *node); - -// TODO: doxygen comment. -void tree_break_off(tree_t *tree, tree_node_t *node); - -/// @brief Searches the given value inside the tree. -tree_node_t *tree_find(tree_t *tree, void *value, tree_comparator_t comparator); diff --git a/mentos/inc/link_access.h b/mentos/inc/link_access.h new file mode 100644 index 0000000..76df631 --- /dev/null +++ b/mentos/inc/link_access.h @@ -0,0 +1,45 @@ +/// MentOS, The Mentoring Operating system project +/// @file link_access.h +/// @brief Set of macros that provide access to linking symbols. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifdef __APPLE__ +#include + +/// @brief Define variables pointing to external symbols, specifically +/// to the .data section of a linked object file. +#define EXTLD(NAME) extern const unsigned char _section$__DATA__##NAME[]; +/// Provide access to the .data of the linked object. +#define LDVAR(NAME) _section$__DATA__##NAME +/// Provides access to the length of the .data section of the linked object. +#define LDLEN(NAME) (getsectbyname("__DATA", "__" #NAME)->size) + +#elif (defined __WIN32__) // mingw + +/// @brief Define variables pointing to external symbols, specifically +/// to the .data section of a linked object file. +#define EXTLD(NAME) \ + extern const unsigned char binary_##NAME##_start[]; \ + extern const unsigned char binary_##NAME##_end[]; \ + extern const unsigned char binary_##NAME##_size[]; +/// Provide access to the .data of the linked object. +#define LDVAR(NAME) binary_##NAME##_start +/// Provides access to the length of the .data section of the linked object. +#define LDLEN(NAME) binary_##NAME##_size + +#else // gnu/linux ld + +/// @brief Define variables pointing to external symbols, specifically +/// to the .data section of a linked object file. +#define EXTLD(NAME) \ + extern const unsigned char _binary_##NAME##_start[]; \ + extern const unsigned char _binary_##NAME##_end[]; \ + extern const unsigned char _binary_##NAME##_size[]; +/// Provides access to the .data of the linked object. +#define LDVAR(NAME) _binary_##NAME##_start +/// Provides access to the length of the .data section of the linked object. +#define LDLEN(NAME) _binary_##NAME##_size +#endif diff --git a/mentos/inc/mem/buddysystem.h b/mentos/inc/mem/buddysystem.h index 160d0d1..2b311d1 100644 --- a/mentos/inc/mem/buddysystem.h +++ b/mentos/inc/mem/buddysystem.h @@ -1,25 +1,124 @@ /// MentOS, The Mentoring Operating system project /// @file buddysystem.h /// @brief Buddy System. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "zone_allocator.h" +#include "klib/list_head.h" +#include "klib/stdatomic.h" +#include "stdint.h" -/// @brief Allocate a block of page frames in a zone of size 2^{order. -/// @param zone A memory zone. -/// @param order The logarithm of the size of the block. -/// @return The address of the first page descriptor of the block, or NULL. -page_t *bb_alloc_pages(zone_t *zone, unsigned int order); +/// @brief Max gfp pages order of buddysystem blocks. +#define MAX_BUDDYSYSTEM_GFP_ORDER 14 -/// @brief Free a block of page frames in a zone of size 2^{order. -/// @param zone A memory zone. -/// @param page The address of the first page descriptor of the block. -/// @param order The logarithm of the size of the block. -void bb_free_pages(zone_t *zone, page_t *page, unsigned int order); +/// @brief Provide the offset of the element inside the given type of page. +#define BBSTRUCT_OFFSET(page, element) \ + ((uint32_t) & (((page *)NULL)->element)) -/// @brief Print the size of free_list of each free_area. -/// @param zone A memory zone. -void buddy_system_dump(zone_t *zone); +/// @brief Returns the address of the given element of a given type of page, +/// based on the provided bbstruct. +#define PG_FROM_BBSTRUCT(bbstruct, page, element) \ + ((page *)(((uint32_t)(bbstruct)) - BBSTRUCT_OFFSET(page, element))) + +/// The base structure representing a bb page +typedef struct bb_page_t { + /// The flags of the page. + volatile unsigned long flags; + /// The current page order. + uint32_t order; + /// Keep track of where the page is located. + union { + /// The page siblings when not allocated. + list_head siblings; + /// The cache list pointer when allocated but on cache. + list_head cache; + } location; +} bb_page_t; + +/// @brief Buddy system descriptor: collection of free page blocks. +/// Each block represents 2^k free contiguous page. +typedef struct bb_free_area_t { + /// free_list collectes the first page descriptors of a blocks of 2^k frames + list_head free_list; + /// nr_free specifies the number of blocks of free pages. + int nr_free; +} bb_free_area_t; + +/// @brief Buddy system instance, +/// that represents a memory area managed by the buddy system +typedef struct bb_instance_t { + /// Name of this bb instance + const char *name; + /// List of buddy system pages grouped by level. + bb_free_area_t free_area[MAX_BUDDYSYSTEM_GFP_ORDER]; + /// Pointer to start of free pages cache + list_head free_pages_cache_list; + /// Size of the current cache + unsigned long free_pages_cache_size; + /// Buddysystem instance size in number of pages. + unsigned long size; + /// Address of the first managed page + bb_page_t *base_page; + /// Size of the (padded) wrapper page structure + unsigned long pgs_size; + /// Offset of the bb_page_t struct from the start of the whole structure + unsigned long bbpg_offset; +} bb_instance_t; + +/// @brief Allocate a block of page frames of size 2^order. +/// @param instance A buddy system instance. +/// @param order The logarithm of the size of the block. +/// @return The address of the first page descriptor of the block, or NULL. +bb_page_t *bb_alloc_pages(bb_instance_t *instance, unsigned int order); + +/// @brief Free a block of page frames of size 2^order. +/// @param instance A buddy system instance. +/// @param page The address of the first page descriptor of the block. +void bb_free_pages(bb_instance_t *instance, bb_page_t *page); + +/// @brief Alloc a page using bb cache. +/// @param instance Buddy system instance. +/// @return An allocated page. +bb_page_t *bb_alloc_page_cached(bb_instance_t *instance); + +/// @brief Free a page allocated with bb_alloc_page_cached. +/// @param instance Buddy system instance. +/// @param page The address of the first page descriptor of the block. +void bb_free_page_cached(bb_instance_t *instance, bb_page_t *page); + +/// @brief Initialize Buddy System. +/// @param instance A buddysystem instance. +/// @param name The name of the current instance (for debug purposes) +/// @param pages_start The start address of the page structures +/// @param bbpage_offset The offset from the start of the whole page of the +/// bb_page_t struct. +/// @param pages_stride The (padded) size of the whole page structure +/// @param pages_count The number of pages in this region +void buddy_system_init( + bb_instance_t *instance, + const char *name, + void *pages_start, + uint32_t bbpage_offset, + uint32_t pages_stride, + uint32_t pages_count); + +/// @brief Print the size of free_list of each free_area. +/// @param instance A buddy system instance. +void buddy_system_dump(bb_instance_t *instance); + +/// @brief Returns the total space for the given instance. +/// @param instance A buddy system instance. +/// @return The requested total sapce. +unsigned long buddy_system_get_total_space(bb_instance_t *instance); + +/// @brief Returns the free space for the given instance. +/// @param instance A buddy system instance. +/// @return The requested total sapce. +unsigned long buddy_system_get_free_space(bb_instance_t *instance); + +/// @brief Returns the cached space for the given instance. +/// @param instance A buddy system instance. +/// @return The requested total sapce. +unsigned long buddy_system_get_cached_space(bb_instance_t *instance); diff --git a/mentos/inc/mem/gfp.h b/mentos/inc/mem/gfp.h index 602745e..c1785fe 100644 --- a/mentos/inc/mem/gfp.h +++ b/mentos/inc/mem/gfp.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file gfp.h -/// @brief List of GFP Flags. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief List of Get Free Pages (GFP) Flags. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -9,159 +9,145 @@ /// Type used for GFP_FLAGS. typedef unsigned int gfp_t; -// Plain integer GFP bitmasks. Do not use this directly. -#define ___GFP_DMA 0x01u +/// @defgroup GFP The Get Free Pages (GFP) flags +/// @brief Sets of GFP defines. +/// @{ -#define ___GFP_HIGHMEM 0x02u +/// @defgroup gfp_bitmasks Bitmasks +/// @brief Plain integer GFP bitmasks. Do not use this directly. +/// @{ -#define ___GFP_DMA32 0x04u +#define ___GFP_DMA 0x001U ///< DMA +#define ___GFP_HIGHMEM 0x002U ///< HIGHMEM +#define ___GFP_DMA32 0x004U ///< DMA32 +#define ___GFP_RECLAIMABLE 0x010U ///< RECLAIMABLE +#define ___GFP_HIGH 0x020U ///< HIGH +#define ___GFP_IO 0x040U ///< IO +#define ___GFP_FS 0x080U ///< FS +#define ___GFP_ZERO 0x100U ///< ZERO +#define ___GFP_ATOMIC 0x200U ///< ATOMIC +#define ___GFP_DIRECT_RECLAIM 0x400U ///< DIRECT_RECLAIM +#define ___GFP_KSWAPD_RECLAIM 0x800U ///< KSWAPD_RECLAIM -#define ___GFP_RECLAIMABLE 0x10u +/// @} -#define ___GFP_HIGH 0x20u - -#define ___GFP_IO 0x40u - -#define ___GFP_FS 0x80u - -#define ___GFP_ZERO 0x100u - -#define ___GFP_ATOMIC 0x200u - -#define ___GFP_DIRECT_RECLAIM 0x400u - -#define ___GFP_KSWAPD_RECLAIM 0x800u - -/* - * Physical address zone modifiers (see linux/mmzone.h - low four bits) - * - * Do not put any conditional on these. If necessary modify the definitions - * without the underscores and use them consistently. The definitions here may - * be used in bit comparisons. - */ -#define __GFP_DMA ___GFP_DMA - -#define __GFP_HIGHMEM ___GFP_HIGHMEM - -#define __GFP_DMA32 ___GFP_DMA32 +/// @defgroup zone_modifiers Zone Modifiers +/// @brief Physical address zone modifiers (see linux/mmzone.h - low four bits) +/// @details +/// Do not put any conditional on these. If necessary modify the definitions +/// without the underscores and use them consistently. The definitions here may +/// be used in bit comparisons. +/// @{ +#define __GFP_DMA ___GFP_DMA ///< DMA +#define __GFP_HIGHMEM ___GFP_HIGHMEM ///< HIGHMEM +#define __GFP_DMA32 ___GFP_DMA32 ///< DMA32 +/// All of the above. #define GFP_ZONEMASK (__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32) -/** - * DOC: Watermark modifiers - * - * Watermark modifiers -- controls access to emergency reserves - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * %__GFP_HIGH indicates that the caller is high-priority and that granting - * the request is necessary before the system can make forward progress. - * For example, creating an IO context to clean pages. - * - * %__GFP_ATOMIC indicates that the caller cannot reclaim or sleep and is - * high priority. Users are typically interrupt handlers. This may be - * used in conjunction with %__GFP_HIGH - */ +/// @} + +/// @defgroup WatermarkModifiers Watermark Modifiers +/// @brief Controls access to emergency reserves. +/// @{ + +/// @brief Indicates that the caller cannot reclaim or sleep and is +/// high priority. Users are typically interrupt handlers. This may be +/// used in conjunction with %__GFP_HIGH. #define __GFP_ATOMIC ___GFP_ATOMIC +/// @brief Indicates that the caller is high-priority and that granting +/// the request is necessary before the system can make forward progress. +/// For example, creating an IO context to clean pages. #define __GFP_HIGH ___GFP_HIGH -/** - * DOC: Reclaim modifiers - * - * Reclaim modifiers - * ~~~~~~~~~~~~~~~~~ - * - * %__GFP_IO can start physical IO. - * - * %__GFP_FS can call down to the low-level FS. Clearing the flag avoids the - * allocator recursing into the filesystem which might already be holding - * locks. - * - * %__GFP_DIRECT_RECLAIM indicates that the caller may enter direct reclaim. - * This flag can be cleared to avoid unnecessary delays when a fallback - * option is available. - * - * %__GFP_KSWAPD_RECLAIM indicates that the caller wants to wake kswapd when - * the low watermark is reached and have it reclaim pages until the high - * watermark is reached. A caller may wish to clear this flag when fallback - * options are available and the reclaim is likely to disrupt the system. The - * canonical example is THP allocation where a fallback is cheap but - * reclaim/compaction may cause indirect stalls. - * - * %__GFP_RECLAIM is shorthand to allow/forbid both direct and kswapd reclaim. - */ +/// @} + +/// @defgroup ReclaimModifiers Reclaim Modifiers +/// @brief Enable reclaim operations on a specific region of memory. +/// @{ + +/// @brief Can start physical IO. #define __GFP_IO ___GFP_IO +/// @brief Can call down to the low-level FS. Clearing the flag avoids the +/// allocator recursing into the filesystem which might already be holding +/// locks. #define __GFP_FS ___GFP_FS -// Caller can reclaim. + +/// @brief Indicates that the caller may enter direct reclaim. +/// This flag can be cleared to avoid unnecessary delays when a fallback +/// option is available. #define __GFP_DIRECT_RECLAIM ___GFP_DIRECT_RECLAIM -// Kswapd can wake. +/// @brief Indicates that the caller wants to wake kswapd when +/// the low watermark is reached and have it reclaim pages until the high +/// watermark is reached. A caller may wish to clear this flag when fallback +/// options are available and the reclaim is likely to disrupt the system. The +/// canonical example is THP allocation where a fallback is cheap but +/// reclaim/compaction may cause indirect stalls. #define __GFP_KSWAPD_RECLAIM ___GFP_KSWAPD_RECLAIM +/// @brief Is shorthand to allow/forbid both direct and kswapd reclaim. #define __GFP_RECLAIM (___GFP_DIRECT_RECLAIM | ___GFP_KSWAPD_RECLAIM) -/** - * DOC: Useful GFP flag combinations - * - * Useful GFP flag combinations - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Useful GFP flag combinations that are commonly used. It is recommended - * that subsystems start with one of these combinations and then set/clear - * %__GFP_FOO flags as necessary. - * - * %GFP_ATOMIC users can not sleep and need the allocation to succeed. A lower - * watermark is applied to allow access to "atomic reserves" - * - * %GFP_KERNEL is typical for kernel-internal allocations. The caller requires - * %ZONE_NORMAL or a lower zone for direct access but can direct reclaim. - * - * %GFP_NOWAIT is for kernel allocations that should not stall for direct - * reclaim, start physical IO or use any filesystem callback. - * - * %GFP_NOIO will use direct reclaim to discard clean pages or slab pages - * that do not require the starting of any physical IO. - * Please try to avoid using this flag directly and instead use - * memalloc_noio_{save,restore} to mark the whole scope which cannot - * perform any IO with a short explanation why. All allocation requests - * will inherit GFP_NOIO implicitly. - * - * %GFP_NOFS will use direct reclaim but will not use any filesystem interfaces. - * Please try to avoid using this flag directly and instead use - * memalloc_nofs_{save,restore} to mark the whole scope which cannot/shouldn't - * recurse into the FS layer with a short explanation why. All allocation - * requests will inherit GFP_NOFS implicitly. - * - * %GFP_USER is for userspace allocations that also need to be directly - * accessibly by the kernel or hardware. It is typically used by hardware - * for buffers that are mapped to userspace (e.g. graphics) that hardware - * still must DMA to. cpuset limits are enforced for these allocations. - * - * %GFP_DMA exists for historical reasons and should be avoided where possible. - * The flags indicates that the caller requires that the lowest zone be - * used (%ZONE_DMA or 16M on x86-64). Ideally, this would be removed but - * it would require careful auditing as some users really require it and - * others use the flag to avoid lowmem reserves in %ZONE_DMA and treat the - * lowest zone as a type of emergency reserve. - * - * %GFP_HIGHUSER is for userspace allocations that may be mapped to userspace, - * do not need to be directly accessible by the kernel but that cannot - * move once in use. An example may be a hardware allocation that maps - * data directly into userspace but has no addressing limitations. - */ +/// @} + +/// @defgroup gfp_flag_combinations Flag Combinations +/// @brief Useful GFP flag combinations. +/// @details +/// Useful GFP flag combinations that are commonly used. It is recommended +/// that subsystems start with one of these combinations and then set/clear +/// the flags as necessary. +/// @{ + +/// @brief Users can not sleep and need the allocation to succeed. A lower +/// watermark is applied to allow access to "atomic reserves" #define GFP_ATOMIC (__GFP_HIGH | __GFP_ATOMIC | __GFP_KSWAPD_RECLAIM) +/// @brief is typical for kernel-internal allocations. The caller requires +/// %ZONE_NORMAL or a lower zone for direct access but can direct reclaim. #define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) +/// @brief is for kernel allocations that should not stall for direct +/// reclaim, start physical IO or use any filesystem callback. #define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) +/// @brief will use direct reclaim to discard clean pages or slab pages +/// that do not require the starting of any physical IO. +/// Please try to avoid using this flag directly and instead use +/// memalloc_noio_{save,restore} to mark the whole scope which cannot +/// perform any IO with a short explanation why. All allocation requests +/// will inherit GFP_NOIO implicitly. #define GFP_NOIO (__GFP_RECLAIM) +/// @brief will use direct reclaim but will not use any filesystem interfaces. +/// Please try to avoid using this flag directly and instead use +/// memalloc_nofs_{save,restore} to mark the whole scope which cannot/shouldn't +/// recurse into the FS layer with a short explanation why. All allocation +/// requests will inherit GFP_NOFS implicitly. #define GFP_NOFS (__GFP_RECLAIM | __GFP_IO) +/// @brief is for userspace allocations that also need to be directly +/// accessibly by the kernel or hardware. It is typically used by hardware +/// for buffers that are mapped to userspace (e.g. graphics) that hardware +/// still must DMA to. cpuset limits are enforced for these allocations. #define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS) +/// @brief exists for historical reasons and should be avoided where possible. +/// The flags indicates that the caller requires that the lowest zone be +/// used (%ZONE_DMA or 16M on x86-64). Ideally, this would be removed but +/// it would require careful auditing as some users really require it and +/// others use the flag to avoid lowmem reserves in %ZONE_DMA and treat the +/// lowest zone as a type of emergency reserve. #define GFP_DMA (__GFP_DMA) +/// @brief is for userspace allocations that may be mapped to userspace, +/// do not need to be directly accessible by the kernel but that cannot +/// move once in use. An example may be a hardware allocation that maps +/// data directly into userspace but has no addressing limitations. #define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) + +/// @} + +/// @} diff --git a/mentos/inc/mem/kheap.h b/mentos/inc/mem/kheap.h index 299904f..c3f38d3 100644 --- a/mentos/inc/mem/kheap.h +++ b/mentos/inc/mem/kheap.h @@ -2,7 +2,7 @@ /// @file kheap.h /// @brief Interface for kernel heap functions, also provides a placement /// malloc() for use before the heap is initialised. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,35 +10,33 @@ #include "kernel.h" #include "scheduler.h" -//#define KHEAP_START (void*)(LOAD_MEMORY_ADDRESS + 0x00800000) // 0x00800000 = 8M -//#define KHEAP_MAX_ADDRESS (void*)(LOAD_MEMORY_ADDRESS + 0x0FFFFFFF) // 0x10000000 = 256 M -// M = 1024 * 1024 = 1MB -#define KHEAP_INITIAL_SIZE (4 * M) -#define UHEAP_INITIAL_SIZE (1 * M) - -/** - * @brief Structure of block_t - * size: | 31 bit | 1 bit | - * | first bits of real size | free/alloc | - * To calculate the real size, set to zero the last bit - * prev: -> block_t - * next: -> block_t - **/ -typedef struct block_t { - unsigned int size; - struct block_t *nextfree; - struct block_t *next; -} block_t; - -/// @brief Initialize heap. +/// @brief Initialize heap. /// @param initial_size Starting size. void kheap_init(size_t initial_size); -/// @brief Returns if the kheap is enabled. -bool_t kheap_is_enabled(); - +/// @brief Increase the heap of the kernel. +/// @param increment The amount to increment. +/// @return Pointer to a ready-to-used memory area. void *ksbrk(int increment); +/// @brief Increase the heap of the current process. +/// @param increment The amount to increment. +/// @return Pointer to a ready-to-used memory area. +void *usbrk(int increment); + +/// @brief User malloc. +/// @param addr This argument is treated as an address of a dynamically +/// allocated memory if falls inside the process heap area. +/// Otherwise, it is treated as an amount of memory that +/// should be allocated. +/// @return NULL if there is no more memory available or we were freeing +/// a previously allocated memory area, the address of the +/// allocated space otherwise. +void *sys_brk(void *addr); + +/// @brief Prints the heap visually, for debug. +void kheap_dump(); + #if 0 /// @brief Kmalloc wrapper. /// @details When heap is not created, use a placement memory allocator, when @@ -47,7 +45,7 @@ void *ksbrk(int increment); /// @param align Return a page-aligned memory block. /// @param phys Return the physical address of the memory block. /// @return -uint32_t kmalloc_int(size_t size, bool_t align, uint32_t *phys); +uint32_t kmalloc_int(size_t size, int align, uint32_t *phys); /// @brief Wrapper for kmalloc, get physical address. /// @param sz Size of memory to allocate. @@ -60,51 +58,38 @@ void *kmalloc_p(unsigned int sz, unsigned int *phys); /// @param phys Pointer to the variable where to place the physical address. /// @return void *kmalloc_ap(unsigned int sz, unsigned int *phys); -#endif /// @brief Dynamically allocates memory of the given size. /// @param sz Size of memory to allocate. /// @return -void *kmalloc(unsigned int sz); +//void *kmalloc(unsigned int sz); /// @brief Wrapper for aligned kmalloc. /// @param size Size of memory to allocate. /// @return -void *kmalloc_align(size_t size); +//void *kmalloc_align(size_t size); /// @brief Dynamically allocates memory for (size * num) and memset it. /// @param num Multiplier. /// @param size Size of memory to allocate. /// @return -void *kcalloc(unsigned int num, unsigned int size); +//void *kcalloc(unsigned int num, unsigned int size); /// @brief Wrapper function for realloc. /// @param ptr /// @param size /// @return -void *krealloc(void *ptr, unsigned int size); +//void *krealloc(void *ptr, unsigned int size); /// @brief Wrapper function for free. /// @param p -void kfree(void *p); +//void kfree(void *p); /// @brief Allocates size bytes of uninitialized storage. //void *malloc(unsigned int size); -// We can't do a user heap brk if we don't enable paging... -void *usbrk(int increment); - -/// @brief User malloc -/// @param size The size requested in byte. -/// @return The address of the allocated space. -void *umalloc(unsigned int size); - -/// @brief User free -/// @param p Pointer to the space to free. -void ufree(void *p); - /// @brief Allocates size bytes of uninitialized storage with block align. -//void *malloc_align(struct vm_area_struct *heap, unsigned int size); +//void *malloc_align(vm_area_struct_t *heap, unsigned int size); /// @brief Deallocates previously allocated space. //void free(void *ptr); @@ -116,13 +101,4 @@ void ufree(void *p); /// @return //void *realloc(void *ptr, unsigned int size); -/// @brief Prints the heap visually, for debug. -void kheap_dump(); - -/// @brief Get the pointer to the kernel heap start. -/// @return The kernel heap start pointer. -uint32_t get_kheap_start(); - -/// @brief Get the pointer to the kernel heap curr. -/// @return The kernel heap current pointer. -uint32_t get_kheap_curr(); +#endif \ No newline at end of file diff --git a/mentos/inc/mem/paging.h b/mentos/inc/mem/paging.h index d8e12b9..f9e25f3 100644 --- a/mentos/inc/mem/paging.h +++ b/mentos/inc/mem/paging.h @@ -1,106 +1,255 @@ /// MentOS, The Mentoring Operating system project /// @file paging.h /// @brief Implementation of a memory paging management. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "zone_allocator.h" +#include "proc_access.h" #include "kernel.h" #include "stddef.h" +#include "boot.h" /// Size of a page. -#define PAGE_SIZE 4096 +#define PAGE_SIZE 4096U +/// The start of the process area. +#define PROCAREA_START_ADDR 0x00000000 +/// The end of the process area (and start of the kernel area). +#define PROCAREA_END_ADDR 0xC0000000 -/// @brief Virtual Memory Area. -/// Process segments. -struct vm_area_struct { - /// Memory descriptor associated. - struct mm_struct *vm_mm; +/// @brief An entry of a page directory. +typedef struct page_dir_entry_t { + unsigned int present : 1; ///< TODO: Comment. + unsigned int rw : 1; ///< TODO: Comment. + unsigned int user : 1; ///< TODO: Comment. + unsigned int w_through : 1; ///< TODO: Comment. + unsigned int cache : 1; ///< TODO: Comment. + unsigned int accessed : 1; ///< TODO: Comment. + unsigned int reserved : 1; ///< TODO: Comment. + unsigned int page_size : 1; ///< TODO: Comment. + unsigned int global : 1; ///< TODO: Comment. + unsigned int available : 3; ///< TODO: Comment. + unsigned int frame : 20; ///< TODO: Comment. +} page_dir_entry_t; - /// Start address of the segment. - uint32_t vm_start; +/// @brief An entry of a page table. +typedef struct page_table_entry_t { + unsigned int present : 1; ///< TODO: Comment. + unsigned int rw : 1; ///< TODO: Comment. + unsigned int user : 1; ///< TODO: Comment. + unsigned int w_through : 1; ///< TODO: Comment. + unsigned int cache : 1; ///< TODO: Comment. + unsigned int accessed : 1; ///< TODO: Comment. + unsigned int dirty : 1; ///< TODO: Comment. + unsigned int zero : 1; ///< TODO: Comment. + unsigned int global : 1; ///< TODO: Comment. + unsigned int kernel_cow : 1; ///< TODO: Comment. + unsigned int available : 2; ///< TODO: Comment. + unsigned int frame : 20; ///< TODO: Comment. +} page_table_entry_t; - /// End address of the segment. - uint32_t vm_end; - - /// Next memory area of the list. - struct vm_area_struct *vm_next; - - /// Permissions. - pgprot_t vm_page_prot; - - /// Flags. - unsigned short vm_flags; - - /// rbtree node. - // struct rb_node vm_rb; +/// @brief Flags associated with virtual memory areas. +enum MEMMAP_FLAGS { + MM_USER = 0x1, ///< Area belongs to user. + MM_GLOBAL = 0x2, ///< Area is global. + MM_RW = 0x4, ///< Area has user read/write perm. + MM_PRESENT = 0x8, ///< Area is valid. + // Kernel flags + MM_COW = 0x10, ///< Area is copy on write. + MM_UPDADDR = 0x20, ///< Check? }; -/// @brief Memory Descriptor. -/// Memory description of user process. -struct mm_struct { - /// List of memory area. - struct vm_area_struct *mmap; - // /// rbtree of memory area. - // struct rb_root mm_rb; +/// @brief A page table. +/// @details +/// It contains 1024 entries which can be addressed by 10 bits (log_2(1024)). +typedef struct page_table_t { + page_table_entry_t pages[1024]; ///< Array of pages. +} __attribute__((aligned(PAGE_SIZE))) page_table_t; - /// Last memory area used. - struct vm_area_struct *mmap_cache; +/// @brief A page directory. +/// @details In the two-level paging, this is the first level. +typedef struct page_directory_t { + /// We need a table that contains virtual address, so that we can + /// actually get to the tables (size: 1024 * 4 = 4096 byte). + page_dir_entry_t entries[1024]; +} __attribute__((aligned(PAGE_SIZE))) page_directory_t; - // ///< Process page directory. - // page_directory_t * pgd; +/// @brief Virtual Memory Area, used to store details of a process segment. +typedef struct vm_area_struct_t { + /// Memory descriptor associated. + struct mm_struct_t *vm_mm; + /// Start address of the segment, inclusive. + uint32_t vm_start; + /// End address of the segment, exclusive. + uint32_t vm_end; + /// List of memory areas. + list_head vm_list; + /// Permissions. + pgprot_t vm_page_prot; + /// Flags. + unsigned short vm_flags; + /// rbtree node. + // struct rb_node vm_rb; +} vm_area_struct_t; - /// Number of memory area. - int map_count; +/// @brief Memory Descriptor, used to store details about the memory of a user process. +typedef struct mm_struct_t { + /// List of memory area (vm_area_struct reference). + list_head mmap_list; + // /// rbtree of memory area. + // struct rb_root mm_rb; + /// Last memory area used. + vm_area_struct_t *mmap_cache; + /// Process page directory. + page_directory_t *pgd; + /// Number of memory area. + int map_count; + /// List of mm_struct. + list_head mm_list; + /// CODE start. + uint32_t start_code; + /// CODE end. + uint32_t end_code; + /// DATA start. + uint32_t start_data; + /// DATA end. + uint32_t end_data; + /// HEAP start. + uint32_t start_brk; + /// HEAP end. + uint32_t brk; + /// STACK start. + uint32_t start_stack; + /// ARGS start. + uint32_t arg_start; + /// ARGS end. + uint32_t arg_end; + /// ENVIRONMENT start. + uint32_t env_start; + /// ENVIRONMENT end. + uint32_t env_end; + /// Number of mapped pages. + unsigned int total_vm; +} mm_struct_t; - /// List of mm_struct. - list_head mmlist; +/// @brief Cache used to store page tables. +extern kmem_cache_t *pgtbl_cache; - /// CODE. - uint32_t start_code, end_code; +/// @brief Initializes paging +/// @param info Information coming from bootloader. +void paging_init(boot_info_t *info); - /// DATA. - uint32_t start_data, end_data; +/// @brief Provide access to the main page directory. +/// @return A pointer to the main page directory. +page_directory_t *paging_get_main_directory(); - /// HEAP. - uint32_t start_brk, brk; +/// @brief Provide access to the current paging directory. +/// @return A pointer to the current page directory. +static inline page_directory_t *paging_get_current_directory() +{ + return (page_directory_t *)get_cr3(); +} - /// STACK. - uint32_t start_stack; +/// @brief Switches paging directory, the pointer must be a physical address. +/// @param dir A pointer to the new page directory. +static inline void paging_switch_directory(page_directory_t *dir) +{ + set_cr3((uintptr_t)dir); +} - /// ARGS. - uint32_t arg_start, arg_end; +/// @brief Switches paging directory, the pointer can be a lowmem address. +/// @param dir A pointer to the new page directory. +void paging_switch_directory_va(page_directory_t *dir); - /// ENVIRONMENT. - uint32_t env_start, env_end; +/// @brief Invalidate a single tlb page (the one that maps the specified virtual address) +/// @param addr The address of the page table. +void paging_flush_tlb_single(unsigned long addr); - /// Number of mapped pages. - unsigned int total_vm; -}; +/// @brief Enables paging. +static inline void paging_enable() +{ + // Clear the PSE bit from cr4. + set_cr4(bitmask_clear(get_cr4(), CR4_PSE)); + // Set the PG bit in cr0. + set_cr0(bitmask_set(get_cr0(), CR0_PG)); +} /// @brief Returns if paging is enabled. -bool_t paging_is_enabled(); +/// @return 1 if paging is enables, 0 otherwise. +static inline int paging_is_enabled() +{ + return bitmask_check(get_cr0(), CR0_PG); +} -/// @brief Create a Memory Descriptor. +/// @brief Handles a page fault. +/// @param f The interrupt stack frame. +void page_fault_handler(pt_regs *f); + +/// @brief Gets a page from a virtual address +/// @param pgdir The target page directory. +/// @param virt_start The virtual address to query +/// @param size A pointer to the requested size of the data, size is updated if physical memory is not contiguous +/// @return Pointer to the page. +page_t *mem_virtual_to_page(page_directory_t *pgdir, uint32_t virt_start, size_t *size); + +/// @brief Creates a virtual to physical mapping, incrementing pages usage counters. +/// @param pgd The target page directory. +/// @param virt_start The virtual address to map to. +/// @param phy_start The physical address to map. +/// @param size The size of the segment. +/// @param flags The flags for the memory range. +void mem_upd_vm_area(page_directory_t *pgd, uint32_t virt_start, uint32_t phy_start, size_t size, uint32_t flags); + +/// @brief Clones a range of pages between two distinct page tables +/// @param src_pgd The source page directory. +/// @param dst_pgd The dest page directory. +/// @param src_start The source virtual address for the clone. +/// @param dst_start The destination virtual address for the clone. +/// @param size The size of the segment. +/// @param flags The flags for the new dst memory range. +void mem_clone_vm_area(page_directory_t *src_pgd, + page_directory_t *dst_pgd, + uint32_t src_start, + uint32_t dst_start, + size_t size, + uint32_t flags); + +/// @brief Create a virtual memory area. +/// @param mm The memory descriptor which will contain the new segment. +/// @param virt_start The virtual address to map to. +/// @param size The size of the segment. +/// @param pgflags The flags for the new memory area. +/// @param gfpflags The Get Free Pages flags. +/// @return The virtual address of the starting point of the segment. +uint32_t create_vm_area(mm_struct_t *mm, + uint32_t virt_start, + size_t size, + uint32_t pgflags, + uint32_t gfpflags); + +/// @brief Clone a virtual memory area, using copy on write if specified +/// @param mm The memory descriptor which will contain the new segment. +/// @param area The area to clone +/// @param cow Whether to use copy-on-write or just copy everything. +/// @param gfpflags The Get Free Pages flags. +/// @return Zero on success. +uint32_t clone_vm_area(mm_struct_t *mm, + vm_area_struct_t *area, + int cow, + uint32_t gfpflags); + +/// @brief Creates the main memory descriptor. /// @param stack_size The size of the stack in byte. -/// @return The Memory Descriptor created. -struct mm_struct *create_process_image(size_t stack_size); +/// @return The Memory Descriptor created. +mm_struct_t *create_blank_process_image(size_t stack_size); -/// @brief Create a virtual memory area. -/// @param mm The memory descriptor which will contain the new segment. -/// @param size The size of the segment. -/// @return The virtual address of the starting point of the segment. -uint32_t create_segment(struct mm_struct *mm, size_t size); +/// @brief Create a Memory Descriptor. +/// @param mmp The memory map to clone +/// @return The Memory Descriptor created. +mm_struct_t *clone_process_image(mm_struct_t *mmp); -/// @brief Free Memory Descriptor with all the memory segment contained. +/// @brief Free Memory Descriptor with all the memory segment contained. /// @param mm The Memory Descriptor to free. -void destroy_process_image(struct mm_struct *mm); - -/* - * /// @brief Free a virtual memory area. - * /// @param mm The Memory Descriptor that contains the segment. - * /// @param segment The segment to free. - * void destroy_segment(struct mm_struct *mm, struct vm_area_struct *segment); - */ +void destroy_process_image(mm_struct_t *mm); diff --git a/mentos/inc/mem/slab.h b/mentos/inc/mem/slab.h new file mode 100644 index 0000000..8894278 --- /dev/null +++ b/mentos/inc/mem/slab.h @@ -0,0 +1,169 @@ +/// MentOS, The Mentoring Operating system project +/// @file slab.h +/// @brief Functions and structures for managing memory slabs. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "list_head.h" +#include "stddef.h" +#include "gfp.h" + +//#define ENABLE_CACHE_TRACE +//#define ENABLE_ALLOC_TRACE + +/// @brief Type for slab flags. +typedef unsigned int slab_flags_t; + +/// Create a new cache. +#define KMEM_CREATE(objtype) kmem_cache_create(#objtype, \ + sizeof(objtype), \ + alignof(objtype), \ + GFP_KERNEL, \ + NULL, \ + NULL) + +/// Creates a new cache and allows to specify the constructor. +#define KMEM_CREATE_CTOR(objtype, ctor) kmem_cache_create(#objtype, \ + sizeof(objtype), \ + alignof(objtype), \ + GFP_KERNEL, \ + ((void (*)(void *))(ctor)), \ + NULL) + +/// @brief Stores the information of a cache. +typedef struct kmem_cache_t { + /// Handler for placing it inside a lists of caches. + list_head cache_list; + /// Name of the cache. + const char *name; + /// Size of the cache. + unsigned int size; + /// Size of the objects contained in the cache. + unsigned int object_size; + /// Alignment requirement of the type of objects. + unsigned int align; + /// The total number of slabs. + unsigned int total_num; + /// The number of free slabs. + unsigned int free_num; + /// The Get Free Pages (GFP) flags. + slab_flags_t flags; + /// The order for getting free pages. + unsigned int gfp_order; + /// Constructor for the elements. + void (*ctor)(void *); + /// Destructor for the elements. + void (*dtor)(void *); + /// Handler for the full slabs list. + list_head slabs_full; + /// Handler for the partial slabs list. + list_head slabs_partial; + /// Handler for the free slabs list. + list_head slabs_free; +} kmem_cache_t; + +/// Initialize the slab system +void kmem_cache_init(); + +/// @brief Creates a new slab cache. +/// @param name Name of the cache. +/// @param size Size of the objects contained inside the cache. +/// @param align Memory alignment for the objects inside the cache. +/// @param flags Flags used to define the properties of the cache. +/// @param ctor Constructor for initializing the cache elements. +/// @param dtor Destructor for finalizing the cache elements. +/// @return Pointer to the object used to manage the cache. +kmem_cache_t *kmem_cache_create( + const char *name, + unsigned int size, + unsigned int align, + slab_flags_t flags, + void (*ctor)(void *), + void (*dtor)(void *)); + +/// @brief Deletes the given cache. +/// @param cachep Pointer to the cache. +void kmem_cache_destroy(kmem_cache_t *cachep); + +#ifdef ENABLE_CACHE_TRACE + +#ifndef __FILENAME__ +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +/// @brief Allocs a new object using the provided cache. +/// @param file File where the object is allocated. +/// @param fun Function where the object is allocated. +/// @param line Line inside the file. +/// @param cachep The cache used to allocate the object. +/// @param flags Flags used to define where we are going to Get Free Pages (GFP). +/// @return Pointer to the allocated space. +void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cache_t *cachep, gfp_t flags); + +/// @brief Frees an cache allocated object. +/// @param file File where the object is deallocated. +/// @param fun Function where the object is deallocated. +/// @param line Line inside the file. +/// @param addr Address of the object. +void pr_kmem_cache_free(const char *file, const char *fun, int line, void *addr); + +/// Wrapper that provides the filename, the function and line where the alloc is happening. +#define kmem_cache_alloc(...) pr_kmem_cache_alloc(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +/// Wrapper that provides the filename, the function and line where the free is happening. +#define kmem_cache_free(...) pr_kmem_cache_free(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +#else +/// @brief Allocs a new object using the provided cache. +/// @param cachep The cache used to allocate the object. +/// @param flags Flags used to define where we are going to Get Free Pages (GFP). +/// @return Pointer to the allocated space. +void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags); + +/// @brief Frees an cache allocated object. +/// @param addr Address of the object. +void kmem_cache_free(void *addr); + +#endif + +#ifdef ENABLE_ALLOC_TRACE + +#ifndef __FILENAME__ +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +/// @brief Provides dynamically allocated memory in kernel space. +/// @param file File where the object is allocated. +/// @param fun Function where the object is allocated. +/// @param line Line inside the file. +/// @param size The amount of memory to allocate. +/// @return A pointer to the allocated memory. +void *pr_kmalloc(const char *file, const char *fun, int line, unsigned int size); + +/// @brief Frees dynamically allocated memory in kernel space. +/// @param file File where the object is deallocated. +/// @param fun Function where the object is deallocated. +/// @param line Line inside the file. +/// @param ptr The pointer to the allocated memory. +void pr_kfree(const char *file, const char *fun, int line, void *addr); + +/// Wrapper that provides the filename, the function and line where the alloc is happening. +#define kmalloc(...) pr_kmalloc(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +/// Wrapper that provides the filename, the function and line where the free is happening. +#define kfree(...) pr_kfree(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +#else + +/// @brief Provides dynamically allocated memory in kernel space. +/// @param size The amount of memory to allocate. +/// @return A pointer to the allocated memory. +void *kmalloc(unsigned int size); + +/// @brief Frees dynamically allocated memory in kernel space. +/// @param ptr The pointer to the allocated memory. +void kfree(void *ptr); + +#endif diff --git a/mentos/inc/mem/vmem_map.h b/mentos/inc/mem/vmem_map.h new file mode 100644 index 0000000..1bd11dc --- /dev/null +++ b/mentos/inc/mem/vmem_map.h @@ -0,0 +1,77 @@ +/// MentOS, The Mentoring Operating system project +/// @file vmem_map.h +/// @brief Virtual memory mapping routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "buddysystem.h" +#include "zone_allocator.h" +#include "paging.h" + +/// Size of the virtual memory. +#define VIRTUAL_MEMORY_SIZE_MB 128 + +/// @brief Virtual mapping manager. +typedef struct virt_map_page_manager_t { + /// The buddy system used to manage the pages. + bb_instance_t bb_instance; +} virt_map_page_manager_t; + +/// @brief Virtual mapping. +typedef struct virt_map_page_t { + /// A buddy system page. + bb_page_t bbpage; +} virt_map_page_t; + +/// @brief Initialize the virtual memory mapper +void virt_init(void); + +/// @brief Map a page range to virtual memory +/// @param page The start page of the mapping +/// @param pfn_count The number of pages to map +/// @return The virtual address of the mapping +uint32_t virt_map_physical_pages(page_t *page, int pfn_count); + +/// @brief Allocate a virtual page range of the specified size. +/// @param size The required amount. +/// @return Pointer to the allocated memory. +virt_map_page_t *virt_map_alloc(uint32_t size); + +/// @brief Map a page to a memory area portion. +/// @param mm The memory descriptor. +/// @param vpage Pointer to the virtual page. +/// @param vaddr The starting address of the are. +/// @param size The size of the area. +/// @return Address of the mapped area. +uint32_t virt_map_vaddress(mm_struct_t *mm, + virt_map_page_t *vpage, + uint32_t vaddr, + uint32_t size); + +/// @brief Checks if an address belongs to the virtual memory mapping. +/// @param addr The address to check. +/// @return 1 if it belongs to the virtual memory mapping, 0 otherwise. +int virtual_check_address(uint32_t addr); + +/// @brief Unmap an address. +/// @param addr The address to unmap. +void virt_unmap(uint32_t addr); + +/// @brief Unmap a page. +/// @param page Pointer to the page to unmap. +void virt_unmap_pg(virt_map_page_t *page); + +/// @brief Memcpy from different processes virtual addresses +/// @param dst_mm The destination memory struct +/// @param dst_vaddr The destination memory address +/// @param src_mm The source memory struct +/// @param src_vaddr The source memory address +/// @param size The size in bytes of the copy +void virt_memcpy( + mm_struct_t *dst_mm, + uint32_t dst_vaddr, + mm_struct_t *src_mm, + uint32_t src_vaddr, + uint32_t size); diff --git a/mentos/inc/mem/zone_allocator.h b/mentos/inc/mem/zone_allocator.h index c2cd4f0..8febc9f 100644 --- a/mentos/inc/mem/zone_allocator.h +++ b/mentos/inc/mem/zone_allocator.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file zone_allocator.h /// @brief Implementation of the Zone Allocator -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -9,37 +9,50 @@ #include "gfp.h" #include "math.h" #include "stdint.h" -#include "stdbool.h" #include "list_head.h" +#include "sys/bitops.h" +#include "stdatomic.h" +#include "boot.h" +#include "buddysystem.h" +#include "slab.h" -/// Max order of buddysystem blocks. -#define MAX_ORDER 11 - -/// @brief Buddy system descriptor: collection of free page blocks. -/// Each block represents 2^k free contiguous page. -typedef struct { - /// free_list collectes the first page descriptors of a blocks of 2^k frames - list_head free_list; - /// nr_free specifies the number of blocks of free pages. - int nr_free; -} free_area_t; +#define page_count(p) atomic_read(&(p)->count) ///< Reads the page count. +#define set_page_count(p, v) atomic_set(&(p)->count, v) ///< Sets the page count. +#define page_inc(p) atomic_inc(&(p)->count) ///< Increments the counter for the given page. +#define page_dec(p) atomic_dec(&(p)->count) ///< Decrements the counter for the given page. /// @brief Page descriptor. Use as a bitmap to understand /// the order of the block and if it is free or allocated. -typedef struct { - /// Array of flags encoding also the zone number to which the page frame belongs. - unsigned long flags; - /// Page frame’s reference counter. -1 free, >= 0 used - int _count; - /// If the page is free, this field is used by the buddy system. - unsigned long private; - /// Contains pointers to the least recently used doubly linked list of pages. - struct list_head lru; +typedef struct page_t { + /// Array of flags encoding also the zone number to which the page frame belongs. + unsigned long flags; + /// Page frame’s reference counter. 0 free, 1 used, 2+ copy on write + atomic_t count; + /// Buddy system page definition + bb_page_t bbpage; + /// Contains pointers to the slabs doubly linked list of pages. + list_head slabs; + + // Slab allocator variables + /// Contains the total number of objects in this page, 0 if not managed by the slub + unsigned int slab_objcnt; + /// Tracks the number of free objects in the current page + unsigned int slab_objfree; + /// Holds the first free object (if slab_objfree is > 0) + list_head slab_freelist; + /// @brief This union can either contain the pointer to the slab main page + /// that handles this page, or the cache that contains it. + union { + /// Holds the slab page used to handle this memory region (root page) + struct page_t *slab_main_page; + /// Holds the slab cache pointer on the main page + kmem_cache_t *slab_cache; + } container; } page_t; /// @brief Enumeration for zone_t. enum zone_type { - /* + /* * ZONE_DMA is used when there are devices that are not able * to do DMA to all of addressable memory (ZONE_NORMAL). Then we * carve out the portion of memory that is needed for these devices. @@ -58,18 +71,18 @@ enum zone_type { * <16M. */ - // ///< Direct memory access. - // ZONE_DMA, + // /// Direct memory access. + //ZONE_DMA, - /* + /* * Normal addressable memory is in ZONE_NORMAL. DMA operations can be * performed on pages in ZONE_NORMAL if the DMA devices support * transfers to all addressable memory. */ - /// Direct mapping. Used by the kernel. - ZONE_NORMAL, + /// Direct mapping. Used by the kernel. + ZONE_NORMAL, - /* + /* * A memory area that is only addressable by the kernel through * mapping portions into its own address space. This is for example * used by i386 to allow the kernel to address the memory beyond @@ -77,87 +90,153 @@ enum zone_type { * table entries on i386) for each page that the kernel needs to * access. */ - /// Page tables mapping. Used by user processes. - ZONE_HIGHMEM, + /// Page tables mapping. Used by user processes. + ZONE_HIGHMEM, - __MAX_NR_ZONES + /// The maximum number of zones. + __MAX_NR_ZONES }; /// @brief Data structure to differentiate memory zone. -typedef struct { - /// Number of free pages in the zone. - unsigned long free_pages; - /// BuddySystem structure for the zone. - free_area_t free_area[MAX_ORDER]; - /// Pointer to first page descriptor of the zone. - page_t *zone_mem_map; - /// Index of the first page frame of the zone. - uint32_t zone_start_pfn; - /// Zone's name. - char *name; - /// Zone's size in number of pages. - unsigned long size; +typedef struct zone_t { + /// Number of free pages in the zone. + unsigned long free_pages; + /// Buddy system managing this zone + bb_instance_t buddy_system; + /// Pointer to first page descriptor of the zone. + page_t *zone_mem_map; + /// Index of the first page frame of the zone. + uint32_t zone_start_pfn; + /// Zone's name. + char *name; + /// Zone's size in number of pages. + unsigned long size; } zone_t; /// @brief Data structure to rapresent a memory node. /// In UMA Architecture there is only one node called /// contig_page_data. -typedef struct { - /// Zones of the node. - zone_t node_zones[__MAX_NR_ZONES]; - /// Number of zones in the node. - int nr_zones; - /// Array of pages of the node. - page_t *node_mem_map; - /// Physical address of the first page of the node. - unsigned long node_start_paddr; - /// Index on global mem_map for node_mem_map. - unsigned long node_start_mapnr; - /// Node's size in number of pages. - unsigned long node_size; - /// NID. - int node_id; - /// Pointer to the next node. - struct pglist_data *node_next; +typedef struct pg_data_t { + /// Zones of the node. + zone_t node_zones[__MAX_NR_ZONES]; + /// Number of zones in the node. + int nr_zones; + /// Array of pages of the node. + page_t *node_mem_map; + /// Physical address of the first page of the node. + unsigned long node_start_paddr; + /// Index on global mem_map for node_mem_map. + unsigned long node_start_mapnr; + /// Node's size in number of pages. + unsigned long node_size; + /// NID. + int node_id; + /// Next item in the memory node list. + struct pg_data_t *node_next; } pg_data_t; extern page_t *mem_map; extern pg_data_t *contig_page_data; -/// @brief Find the nearest block's order of size greater -/// than the amount of byte. -/// @param amount The amount of byte which we want to calculate order. -/// @return The block's order greater and nearest than amount. -uint32_t find_nearest_order_greater(uint32_t amount); +/// @brief Find the nearest block's order of size greater than the +/// amount of byte. +/// @param base_addr The start address, used to handle extra page +/// calculation in case of not page aligned addresses. +/// @param amount The amount of byte which we want to calculate order. +/// @return The block's order greater and nearest than amount. +uint32_t find_nearest_order_greater(uint32_t base_addr, uint32_t amount); -/// @brief Physical memory manager initialization (page_t, zones) -/// @param mem_size Size of the memory. -bool_t pmmngr_init(uint32_t mem_size); +/// @brief Physical memory manager initialization. +/// @param boot_info Information coming from the booloader. +/// @return Outcome of the operation. +int pmmngr_init(boot_info_t *boot_info); -/// @brief Find the first free page frame, set it allocated -/// and return the memory address of the page frame. -/// @param gfp_mask GFP_FLAGS to decide the zone allocation. -/// @return Memory address of the first free block. -uint32_t __alloc_page(gfp_t gfp_mask); +/// @brief Alloc a single cached page. +/// @param gfp_mask The GetFreePage mask. +/// @return Pointer to the page. +page_t *alloc_page_cached(gfp_t gfp_mask); -/// @brief Frees the given page frame address. +/// @brief Free a page allocated with alloc_page_cached. +/// @param page Pointer to the page to free. +void free_page_cached(page_t *page); + +/// @brief Find the first free page frame, set it allocated and return the +/// memory address of the page frame. +/// @param gfp_mask GFP_FLAGS to decide the zone allocation. +/// @return Memory address of the first free block. +uint32_t __alloc_page_lowmem(gfp_t gfp_mask); + +/// @brief Frees the given page frame address. /// @param addr The block address. -void free_page(uint32_t addr); +void free_page_lowmem(uint32_t addr); -/// @brief Find the first free 2^order amount of page frames, -/// set it allocated and return the memory address of the first -/// page frame allocated. +/// @brief Find the first free 2^order amount of page frames, set it allocated +/// and return the memory address of the first page frame allocated. /// @param gfp_mask GFP_FLAGS to decide the zone allocation. /// @param order The logarithm of the size of the page frame. -/// @return Memory address of the first free page frame allocated. -uint32_t __alloc_pages(gfp_t gfp_mask, uint32_t order); +/// @return Memory address of the first free page frame allocated. +uint32_t __alloc_pages_lowmem(gfp_t gfp_mask, uint32_t order); -/// @brief Frees from the given page frame address up to -/// 2^order amount of page frames. -/// @param addr The page frame address. -/// @param order The logarithm of the size of the block. -void free_pages(uint32_t addr, uint32_t order); +/// @brief Find the first free 2^order amount of page frames, set it allocated +/// and return the memory address of the first page frame allocated. +/// @param gfp_mask GFP_FLAGS to decide the zone allocation. +/// @param order The logarithm of the size of the page frame. +/// @return Memory address of the first free page frame allocated. +page_t *_alloc_pages(gfp_t gfp_mask, uint32_t order); -/// @brief Get the pointer of the last byte allocated with pmmngr_init() -/// @return The pointer to the memory. -uint32_t get_memory_start(); +/// @brief Get the start address of the corresponding page. +/// @param page A page structure. +/// @return The address that corresponds to the page. +uint32_t get_lowmem_address_from_page(page_t *page); + +/// @brief Get the start physical address of the corresponding page. +/// @param page A page structure +/// @return The physical address that corresponds to the page. +uint32_t get_physical_address_from_page(page_t *page); + +/// @brief Get the page from it's physical address. +/// @param phy_addr The physical address +/// @return The page that corresponds to the physical address. +page_t *get_page_from_physical_address(uint32_t phy_addr); + +/// @brief Get the page that contains the specified address. +/// @param addr A phisical address. +/// @return The page that corresponds to the address. +page_t *get_lowmem_page_from_address(uint32_t addr); + +/// @brief Frees from the given page frame address up to 2^order amount +/// of page frames. +/// @param addr The page frame address. +void free_pages_lowmem(uint32_t addr); + +/// @brief Frees from the given page frame address up to 2^order amount +/// of page frames. +/// @param page The page. +void __free_pages(page_t *page); + +/// @brief Returns the total space for the given zone. +/// @param gfp_mask GFP_FLAGS to decide the zone. +/// @return Total space of the given zone. +unsigned long get_zone_total_space(gfp_t gfp_mask); + +/// @brief Returns the total free space for the given zone. +/// @param gfp_mask GFP_FLAGS to decide the zone. +/// @return Total free space of the given zone. +unsigned long get_zone_free_space(gfp_t gfp_mask); + +/// @brief Returns the total cached space for the given zone. +/// @param gfp_mask GFP_FLAGS to decide the zone. +/// @return Total cached space of the given zone. +unsigned long get_zone_cached_space(gfp_t gfp_mask); + +/// @brief Checks if the specified address points to a page_t (or field) +/// that belongs to lowmem. +/// @param addr The address to check. +/// @return 1 if it belongs to lowmem, 0 otherwise. +static inline int is_lowmem_page_struct(void *addr) +{ + uint32_t start_lowm_map = (uint32_t)contig_page_data->node_zones[ZONE_NORMAL].zone_mem_map; + uint32_t lowmem_map_size = sizeof(page_t) * contig_page_data->node_zones[ZONE_NORMAL].size; + uint32_t map_index = (uint32_t)addr - start_lowm_map; + return map_index < lowmem_map_size; +} diff --git a/mentos/inc/misc/bitops.h b/mentos/inc/misc/bitops.h deleted file mode 100644 index 3268375..0000000 --- a/mentos/inc/misc/bitops.h +++ /dev/null @@ -1,30 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitops.h -/// @brief Bitmasks functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "stdbool.h" - -/// @brief Finds the first bit set in the given irq mask. -int find_first_bit(unsigned short int irq_mask); - -/// @brief Check if the passed value has the given flag set. -/// @param flags The value to check. -/// @param flag The flag to search. -/// @return True If the value contain the flag,
-/// False otherwise. -bool_t has_flag(uint32_t flags, uint32_t flag); - -/// @brief Set the passed flag to the value. -/// @param flags The destination value. -/// @param flag The flag to set. -void set_flag(uint32_t *flags, uint32_t flag); - -/// @brief Clear the passed flag to the value. -/// @param flags The destination value. -/// @param flag The flag to clear. -void clear_flag(uint32_t *flags, uint32_t flag); diff --git a/mentos/inc/misc/clock.h b/mentos/inc/misc/clock.h deleted file mode 100644 index d7e7b64..0000000 --- a/mentos/inc/misc/clock.h +++ /dev/null @@ -1,98 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file clock.h -/// @brief Clock functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// Used to store time values. -typedef unsigned int time_t; - -/// Used to get information about the current time. -typedef struct tm_t { - /// Seconds [0 to 59] - int tm_sec; - /// Minutes [0 to 59] - int tm_min; - /// Hours [0 to 23] - int tm_hour; - /// Day of the month [1 to 31] - int tm_mday; - /// Month [0 to 11] - int tm_mon; - /// Year [since 1900] - int tm_year; - /// Day of the week [0 to 6] - int tm_wday; - /// Day in the year [0 to 365] - int tm_yday; - /// Is Daylight Saving Time. - int tm_isdst; -} tm_t; - -/// Value used to retrieve the seconds. -#define SECOND_RTC 0x00 - -/// Value used to retrieve the minutes. -#define MINUTE_RTC 0x02 - -/// Value used to retrieve the hours. -#define HOUR_RTC 0x04 - -/// Value used to retrieve the day of the week. -#define DAY_W_RTC 0x06 - -/// Value used to retrieve the day of the month. -#define DAY_M_RTC 0x07 - -/// Value used to retrieve the month. -#define MONTH_RTC 0x08 - -/// Value used to retrieve the year. -#define YEAR_RTC 0x09 - -/// @brief Returns the milliseconds. -time_t get_millisecond(); - -/// @brief Returns the seconds. -time_t get_second(); - -/// @brief Returns the hours. -time_t get_hour(); - -/// @brief Returns the minutes. -time_t get_minute(); - -/// @brief Returns the day of the month. -time_t get_day_m(); - -/// @brief returns the month. -time_t get_month(); - -/// @brief Returns the year. -time_t get_year(); - -/// @brief Returns the day of the week. -time_t get_day_w(); - -/// @brief Returns the name of the month. -char *get_month_lng(); - -/// @brief Returns the name of the day. -char *get_day_lng(); - -/// @brief Returns the current time. -time_t time(time_t *timer); - -// TODO: doxygen comment. -time_t difftime(time_t time1, time_t time2); - -// TODO: doxygen comment. -void strhourminutesecond(char *dst); - -// TODO: doxygen comment. -void strdaymonthyear(char *dst); - -// TODO: doxygen comment. -void strdatehour(char *dst); diff --git a/mentos/inc/misc/debug.h b/mentos/inc/misc/debug.h index 9bb2505..85060d8 100644 --- a/mentos/inc/misc/debug.h +++ b/mentos/inc/misc/debug.h @@ -1,67 +1,113 @@ /// MentOS, The Mentoring Operating system project /// @file debug.h /// @brief Debugging primitives. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "kernel.h" -#include "port_io.h" +#include "kernel_levels.h" -/// @brief Used to enable the device. Any I/O to the debug module before this -/// command is sent will simply be ignored. -#define DBG_ENABLE 0x8A00 +struct pt_regs; -/// @brief Disable the I/O interface to the debugger and the memory -/// monitoring functions. -#define DBG_DISABLE 0x8AFF +#ifndef __DEBUG_LEVEL__ +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE +#endif -/// @brief Selects register 0: Memory monitoring range start address -/// (inclusive). -#define SELECTS_REG_0 0x8A01 +#ifndef __DEBUG_HEADER__ +/// Header for identifying outputs coming from a mechanism. +#define __DEBUG_HEADER__ 0 +#endif -/// @brief Selects register 1: Memory monitoring range end address (exclusive). -#define SELECTS_REG_1 0x8A02 +/// @brief Extract the filename from the full path provided by __FILE__. +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) -/// @brief Enable address range memory monitoring as indicated by register 0 -/// and 1 and clears both registers. -#define ENABLE_ADDR_RANGE_MEM_MONITOR 0x8A80 +/// @brief Sets the loglevel. +/// @param level The new loglevel. +void set_log_level(int level); -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE2 -/// to port 0x8A00 after the device has been enabled will enable instruction -/// tracing. -#define INSTRUCTION_TRACE_ENABLE 0x8AE3 +/// @brief Returns the current loglevel. +/// @return The current loglevel +int get_log_level(); -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE2 -/// to port 0x8A00 after the device has been enabled will disable instruction -/// tracing. -#define INSTRUCTION_TRACE_DISABLE 0x8AE2 +/// @brief Prints the given character to debug output. +/// @param c The character to print. +void dbg_putchar(char c); -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE5 -/// to port 0x8A00 after the device has been enabled will enable register -/// tracing. This currently output the value of all the registers for each -/// instruction traced. Note: instruction tracing must be enabled to view -/// the register tracing. -#define REGISTER_TRACE_ENABLE 0x8AE5 - -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE4 -/// to port 0x8A00 after the device has been enabled will disable register -/// tracing. -#define REGISTER_TRACE_DISABLE 0x8AE4 +/// @brief Prints the given string to debug output. +/// @param s The string to print. +void dbg_puts(const char *s); /// @brief Prints the given string to the debug output. -void _dbg_print(const char *file, const char *fun, int line, const char *msg, - ...); +/// @param file The name of the file. +/// @param fun The name of the function. +/// @param line The line inside the file. +/// @param header The header to print. +/// @param format The format to used, see printf. +/// @param ... The list of arguments. +void dbg_printf(const char *file, const char *fun, int line, char *header, const char *format, ...); -#define __FILENAME__ \ - (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : \ - __FILE__) +/// @brief Prints the registers on debug output. +/// @param frame Pointer to the register. +void dbg_print_regs(struct pt_regs *frame); -#define dbg_print(...) _dbg_print(__FILENAME__, __func__, __LINE__, __VA_ARGS__) +/// @brief Transforms the given amount of bytes to a readable string. +/// @param bytes The bytes to turn to string. +/// @return String representing the bytes in human readable form. +const char *to_human_size(unsigned long bytes); -/// @brief Prints the given registers to the debug output. -void print_reg(register_t *reg); +/// @brief Transforms the given value to a binary string. +/// @param value The decimal value. +/// @return String representing the binary value. +const char *dec_to_binary(unsigned long value); -/// @brief Prints the given intrframe to the debug output. -void print_intrframe(pt_regs *frame); +/// Prints a KERN_DEFAULT. +#define pr_default(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_DEFAULT __VA_ARGS__) +/// Prints a KERN_EMERG. +#if __DEBUG_LEVEL__ >= LOGLEVEL_EMERG +#define pr_emerg(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_EMERG __VA_ARGS__) +#else +#define pr_emerg(...) +#endif +/// Prints a KERN_ALERT. +#if __DEBUG_LEVEL__ >= LOGLEVEL_ALERT +#define pr_alert(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_ALERT __VA_ARGS__) +#else +#define pr_alert(...) +#endif +/// Prints a KERN_CRIT. +#if __DEBUG_LEVEL__ >= LOGLEVEL_CRIT +#define pr_crit(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_CRIT __VA_ARGS__) +#else +#define pr_crit(...) +#endif +/// Prints a KERN_ERR. +#if __DEBUG_LEVEL__ >= LOGLEVEL_ERR +#define pr_err(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_ERR __VA_ARGS__) +#else +#define pr_err(...) +#endif +/// Prints a KERN_WARNING. +#if __DEBUG_LEVEL__ >= LOGLEVEL_WARNING +#define pr_warning(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_WARNING __VA_ARGS__) +#else +#define pr_warning(...) +#endif +/// Prints a KERN_NOTICE. +#if __DEBUG_LEVEL__ >= LOGLEVEL_NOTICE +#define pr_notice(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_NOTICE __VA_ARGS__) +#else +#define pr_notice(...) +#endif +/// Prints a KERN_INFO. +#if __DEBUG_LEVEL__ >= LOGLEVEL_INFO +#define pr_info(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_INFO __VA_ARGS__) +#else +#define pr_info(...) +#endif +/// Prints a KERN_DEBUG. +#if __DEBUG_LEVEL__ >= LOGLEVEL_DEBUG +#define pr_debug(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_DEBUG __VA_ARGS__) +#else +#define pr_debug(...) +#endif diff --git a/mentos/inc/multiboot.h b/mentos/inc/multiboot.h index 458a643..3dac645 100644 --- a/mentos/inc/multiboot.h +++ b/mentos/inc/multiboot.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file multiboot.h /// @brief Data structures used for multiboot. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. // Do not inc here in boot.s. @@ -10,46 +10,25 @@ #include "stdint.h" -// The magic field should contain this. -#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 -// This should be in %eax. -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002U ///< The magic field should contain this. +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002U ///< This should be in %eax. -/// Is there basic lower/upper memory information? -#define MULTIBOOT_FLAG_MEM 0x00000001U -/// is there a boot device set? -#define MULTIBOOT_FLAG_DEVICE 0x00000002U -/// is the command-line defined? -#define MULTIBOOT_FLAG_CMDLINE 0x00000004U -/// are there modules to do something with? -#define MULTIBOOT_FLAG_MODS 0x00000008U -/// is there a symbol table loaded? -#define MULTIBOOT_FLAG_AOUT 0x00000010U -/// is there an ELF section header table? -#define MULTIBOOT_FLAG_ELF 0x00000020U -/// is there a full memory map? -#define MULTIBOOT_FLAG_MMAP 0x00000040U -/// Is there drive info? -#define MULTIBOOT_FLAG_DRIVE_INFO 0x00000080U -/// Is there a config table? -#define MULTIBOOT_FLAG_CONFIG_TABLE 0x00000100U -/// Is there a boot loader name? -#define MULTIBOOT_FLAG_BOOT_LOADER_NAME 0x00000200U -/// Is there a APM table? -#define MULTIBOOT_FLAG_APM_TABLE 0x00000400U -/// Is there video information? -#define MULTIBOOT_FLAG_VBE_INFO 0x00000800U -#define MULTIBOOT_FLAG_FRAMEBUFFER_INFO 0x00001000U +#define MULTIBOOT_FLAG_MEM 0x00000001U ///< Is there basic lower/upper memory information? +#define MULTIBOOT_FLAG_DEVICE 0x00000002U ///< Is there a boot device set? +#define MULTIBOOT_FLAG_CMDLINE 0x00000004U ///< Is the command-line defined? +#define MULTIBOOT_FLAG_MODS 0x00000008U ///< Are there modules to do something with? +#define MULTIBOOT_FLAG_AOUT 0x00000010U ///< Is there a symbol table loaded? +#define MULTIBOOT_FLAG_ELF 0x00000020U ///< Is there an ELF section header table? +#define MULTIBOOT_FLAG_MMAP 0x00000040U ///< Is there a full memory map? +#define MULTIBOOT_FLAG_DRIVE_INFO 0x00000080U ///< Is there drive info? +#define MULTIBOOT_FLAG_CONFIG_TABLE 0x00000100U ///< Is there a config table? +#define MULTIBOOT_FLAG_BOOT_LOADER_NAME 0x00000200U ///< Is there a boot loader name? +#define MULTIBOOT_FLAG_APM_TABLE 0x00000400U ///< Is there a APM table? +#define MULTIBOOT_FLAG_VBE_INFO 0x00000800U ///< Is there video information? +#define MULTIBOOT_FLAG_FRAMEBUFFER_INFO 0x00001000U ///< Is there a framebuffer table? -#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 -#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 -#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 - -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 -#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 -#define MULTIBOOT_MEMORY_NVS 4 -#define MULTIBOOT_MEMORY_BADRAM 5 +#define MULTIBOOT_MEMORY_AVAILABLE 1 ///< The memory is available. +#define MULTIBOOT_MEMORY_RESERVED 2 ///< The memory is reserved. // +-------------------+ // 0 | flags | (required) @@ -96,111 +75,186 @@ // +-------------------+ /// The symbol table for a.out. -typedef struct multiboot_aout_symbol_table { - uint32_t tabsize; - uint32_t strsize; - uint32_t addr; - uint32_t reserved; +typedef struct multiboot_aout_symbol_table_t { + /// TODO: Comment. + uint32_t tabsize; + /// TODO: Comment. + uint32_t strsize; + /// TODO: Comment. + uint32_t addr; + /// TODO: Comment. + uint32_t reserved; } multiboot_aout_symbol_table_t; /// The section header table for ELF. -typedef struct multiboot_elf_section_header_table { - uint32_t num; - uint32_t size; - uint32_t addr; - uint32_t shndx; +typedef struct multiboot_elf_section_header_table_t { + /// TODO: Comment. + uint32_t num; + /// TODO: Comment. + uint32_t size; + /// TODO: Comment. + uint32_t addr; + /// TODO: Comment. + uint32_t shndx; } multiboot_elf_section_header_table_t; -// TODO: doxygen comment. -typedef struct multiboot_module { - // The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive. - uint32_t mod_start; - uint32_t mod_end; - // Module command line. - uint32_t cmdline; - // Padding to take it to 16 bytes (must be zero). - uint32_t pad; +/// @brief Stores information about a module. +typedef struct multiboot_module_t { + // The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive. + /// The starting address of the modules. + uint32_t mod_start; + /// The ending address of the modules. + uint32_t mod_end; + /// Module command line. + uint32_t cmdline; + /// Padding to take it to 16 bytes (must be zero). + uint32_t pad; } multiboot_module_t; -typedef struct multiboot_memory_map { - uint32_t size; - uint32_t base_addr_low; - uint32_t base_addr_high; - uint32_t length_low; - uint32_t length_high; - uint32_t type; +/// @brief Stores information about memory mapping. +typedef struct multiboot_memory_map_t { + /// Size of this entry. + uint32_t size; + /// Lower bytes of the base address. + uint32_t base_addr_low; + /// Higher bytes of the base address. + uint32_t base_addr_high; + /// Lower bytes of the length. + uint32_t length_low; + /// Higher bytes of the length. + uint32_t length_high; + /// Memory type. + uint32_t type; } multiboot_memory_map_t; -// TODO: doxygen comment. -typedef struct multiboot_info { - /// Multiboot info version number. - uint32_t flags; +/// @brief Multiboot information structure. +typedef struct multiboot_info_t { + /// Multiboot info version number. + uint32_t flags; + /// Lower memory available from the BIOS. + uint32_t mem_lower; + /// Upper memory available from the BIOS. + uint32_t mem_upper; + /// Boot device ID. + uint32_t boot_device; + /// Pointer to the boot command line. + uint32_t cmdline; + /// Amount of modules loaded. + uint32_t mods_count; + /// Address to the first module structure. + uint32_t mods_addr; + /// Contains data either of an aout or elf file. + union { + /// Symbol table for `aout` file. + multiboot_aout_symbol_table_t aout_sym; + /// Elf section header table for `elf` file. + multiboot_elf_section_header_table_t elf_sec; + } u; + /// Memory map length. + uint32_t mmap_length; + /// Memory map address. + uint32_t mmap_addr; + /// Drive map length. + uint32_t drives_length; + /// Drive map address. + uint32_t drives_addr; + /// ROM configuration table. + uint32_t config_table; + /// Boot Loader Name. + uint32_t boot_loader_name; + /// APM table. + uint32_t apm_table; + /// Pointer to the VBE control info structure. + uint32_t vbe_control_info; + /// Pointer to the VBE mode info structure. + uint32_t vbe_mode_info; + /// Current VBE mode. + uint32_t vbe_mode; + /// VBE3.0 interface segment. + uint32_t vbe_interface_seg; + /// VBE3.0 interface segment offset. + uint32_t vbe_interface_off; + /// VBE3.0 interface segment length. + uint32_t vbe_interface_len; + /// TODO: Comment. + uint32_t framebuffer_addr; + /// TODO: Comment. + uint32_t framebuffer_pitch; + /// TODO: Comment. + uint32_t framebuffer_width; + /// TODO: Comment. + uint32_t framebuffer_height; + /// TODO: Comment. + uint32_t framebuffer_bpp; + /// TODO: Comment. + uint32_t framebuffer_type; + /// TODO: Comment. + union { + /// TODO: Comment. + struct { + /// TODO: Comment. + uint32_t framebuffer_palette_addr; + /// TODO: Comment. + uint16_t framebuffer_palette_num_colors; + } palette_field; + /// TODO: Comment. + struct { + /// TODO: Comment. + uint8_t framebuffer_red_field_position; + /// TODO: Comment. + uint8_t framebuffer_red_mask_size; + /// TODO: Comment. + uint8_t framebuffer_green_field_position; + /// TODO: Comment. + uint8_t framebuffer_green_mask_size; + /// TODO: Comment. + uint8_t framebuffer_blue_field_position; + /// TODO: Comment. + uint8_t framebuffer_blue_mask_size; + } rgb_field; + } framebuffer_info; +} multiboot_info_t; - /// Available memory from BIOS. - uint32_t mem_lower; - uint32_t mem_upper; +/// @brief Returns the first mmap entry. +/// @param info The multiboot info from which we extract the entry. +/// @return The first entry. +multiboot_memory_map_t *mmap_first_entry(multiboot_info_t *info); - /// "root" partition. - uint32_t boot_device; +/// @brief The first entry of the given type. +/// @param info The multiboot info from which we extract the entry. +/// @param type The type of entry we are looking for. +/// @return The first entry of the given type. +multiboot_memory_map_t *mmap_first_entry_of_type(multiboot_info_t *info, uint32_t type); - /// Kernel command line. - uint32_t cmdline; +/// @brief Returns the next mmap entry. +/// @param info The multiboot info from which we extract the entry. +/// @param entry The current entry. +/// @return The next entry. +multiboot_memory_map_t *mmap_next_entry(multiboot_info_t *info, multiboot_memory_map_t *entry); - /// Boot-Module list. - uint32_t mods_count; - uint32_t mods_addr; +/// @brief Returns the next mmap entry of the given type. +/// @param info The multiboot info from which we extract the entry. +/// @param entry The current entry. +/// @param type The type of entry we are looking for. +/// @return The next entry of the given type. +multiboot_memory_map_t *mmap_next_entry_of_type(multiboot_info_t *info, multiboot_memory_map_t *entry, uint32_t type); - union { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; +/// @brief Returns the type of the entry as string. +/// @param entry The current entry. +/// @return String representing the type of entry. +char *mmap_type_name(multiboot_memory_map_t *entry); - /// Memory Mapping buffer. - uint32_t mmap_length; - uint32_t mmap_addr; +/// @brief Finds the first module. +/// @param info The multiboot info from which we extract the information. +/// @return Pointer to the first module. +multiboot_module_t *first_module(multiboot_info_t *info); - /// Drive Info buffer. - uint32_t drives_length; - uint32_t drives_addr; +/// @brief Finds the next module after the one we provide. +/// @param info The multiboot info from which we extract the information. +/// @param mod The current module. +/// @return Pointer to the next module. +multiboot_module_t *next_module(multiboot_info_t *info, multiboot_module_t *mod); - /// ROM configuration table. - uint32_t config_table; - - /// Boot Loader Name. - uint32_t boot_loader_name; - - /// APM table. - uint32_t apm_table; - - /// Video. - uint32_t vbe_control_info; - uint32_t vbe_mode_info; - uint32_t vbe_mode; - uint32_t vbe_interface_seg; - uint32_t vbe_interface_off; - uint32_t vbe_interface_len; - - uint32_t framebuffer_addr; - uint32_t framebuffer_pitch; - uint32_t framebuffer_width; - uint32_t framebuffer_height; - uint32_t framebuffer_bpp; - uint32_t framebuffer_type; - union { - struct { - uint32_t framebuffer_palette_addr; - uint16_t framebuffer_palette_num_colors; - }; - struct { - uint8_t framebuffer_red_field_position; - uint8_t framebuffer_red_mask_size; - uint8_t framebuffer_green_field_position; - uint8_t framebuffer_green_mask_size; - uint8_t framebuffer_blue_field_position; - uint8_t framebuffer_blue_mask_size; - }; - }; -} __attribute__((packed)) multiboot_info_t; - -// TODO: doxygen comment. +/// @brief Prints as debugging output the info regarding the multiboot. +/// @param mboot_ptr The pointer to the multiboot information. void dump_multiboot(multiboot_info_t *mboot_ptr); diff --git a/mentos/inc/proc_access.h b/mentos/inc/proc_access.h new file mode 100644 index 0000000..05a1e3f --- /dev/null +++ b/mentos/inc/proc_access.h @@ -0,0 +1,272 @@ +/// MentOS, The Mentoring Operating system project +/// @file proc_access.h +/// @brief Set of functions and flags used to manage processors registers. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" + +/// Macro that encapsulate asm and volatile directives. +#define ASM(a) __asm__ __volatile__(a) + +#define CR0_PE 0x00000001u ///< Protected mode Enable. +#define CR0_MP 0x00000002u ///< "Math" Present (e.g. npx), wait for it. +#define CR0_EM 0x00000004u ///< EMulate NPX, e.g. trap, don't execute code. +#define CR0_TS 0x00000008u ///< Process has done Task Switch, do NPX save. +#define CR0_ET 0x00000010u ///< 32 bit (if set) vs 16 bit (387 vs 287). +#define CR0_PG 0x80000000u ///< Paging Enable. + +#define CR4_SEE 0x00008000u ///< Secure Enclave Enable XXX. +#define CR4_SMAP 0x00200000u ///< Supervisor-Mode Access Protect. +#define CR4_SMEP 0x00100000u ///< Supervisor-Mode Execute Protect. +#define CR4_OSXSAVE 0x00040000u ///< OS supports XSAVE. +#define CR4_PCIDE 0x00020000u ///< PCID Enable. +#define CR4_RDWRFSGS 0x00010000u ///< RDWRFSGS Enable. +#define CR4_SMXE 0x00004000u ///< Enable SMX operation. +#define CR4_VMXE 0x00002000u ///< Enable VMX operation. +#define CR4_OSXMM 0x00000400u ///< SSE/SSE2 exception support in OS. +#define CR4_OSFXS 0x00000200u ///< SSE/SSE2 OS supports FXSave. +#define CR4_PCE 0x00000100u ///< Performance-Monitor Count Enable. +#define CR4_PGE 0x00000080u ///< Page Global Enable. +#define CR4_MCE 0x00000040u ///< Machine Check Exceptions. +#define CR4_PAE 0x00000020u ///< Physical Address Extensions. +#define CR4_PSE 0x00000010u ///< Page Size Extensions. +#define CR4_DE 0x00000008u ///< Debugging Extensions. +#define CR4_TSD 0x00000004u ///< Time Stamp Disable. +#define CR4_PVI 0x00000002u ///< Protected-mode Virtual Interrupts. +#define CR4_VME 0x00000001u ///< Virtual-8086 Mode Extensions. + +static inline uint16_t get_es(void) +{ + uint16_t es; + ASM("mov %%es, %0" + : "=r"(es)); + return es; +} + +static inline void set_es(uint16_t es) +{ + ASM("mov %0, %%es" + : + : "r"(es)); +} + +static inline uint16_t get_ds(void) +{ + uint16_t ds; + ASM("mov %%ds, %0" + : "=r"(ds)); + return ds; +} + +static inline void set_ds(uint16_t ds) +{ + ASM("mov %0, %%ds" + : + : "r"(ds)); +} + +static inline uint16_t get_fs(void) +{ + uint16_t fs; + ASM("mov %%fs, %0" + : "=r"(fs)); + return fs; +} + +static inline void set_fs(uint16_t fs) +{ + ASM("mov %0, %%fs" + : + : "r"(fs)); +} + +static inline uint16_t get_gs(void) +{ + uint16_t gs; + ASM("mov %%gs, %0" + : "=r"(gs)); + return gs; +} + +static inline void set_gs(uint16_t gs) +{ + ASM("mov %0, %%gs" + : + : "r"(gs)); +} + +static inline uint16_t get_ss(void) +{ + uint16_t ss; + ASM("mov %%ss, %0" + : "=r"(ss)); + return ss; +} + +static inline void set_ss(uint16_t ss) +{ + ASM("mov %0, %%ss" + : + : "r"(ss)); +} + +static inline uintptr_t get_cr0(void) +{ + uintptr_t cr0; + ASM("mov %%cr0, %0" + : "=r"(cr0)); + return (cr0); +} + +static inline void set_cr0(uintptr_t cr0) +{ + ASM("mov %0, %%cr0" + : + : "r"(cr0)); +} + +static inline uintptr_t get_cr3(void) +{ + uintptr_t cr3; + ASM("mov %%cr3, %0" + : "=r"(cr3)); + return (cr3); +} + +static inline void set_cr3(uintptr_t cr3) +{ + ASM("mov %0, %%cr3" + : + : "r"(cr3)); +} + +static inline uintptr_t get_cr4(void) +{ + uintptr_t cr4; + ASM("mov %%cr4, %0" + : "=r"(cr4)); + return (cr4); +} + +static inline void set_cr4(uintptr_t cr4) +{ + ASM("mov %0, %%cr4" + : + : "r"(cr4) + : "memory"); +} + +static inline uintptr_t get_eflags(void) +{ + uintptr_t eflags; + /* "=rm" is safe here, because "pop" adjusts the stack before + * it evaluates its effective address -- this is part of the + * documented behavior of the "pop" instruction. + */ + ASM("pushf ; pop %0" + : "=rm"(eflags) + : /* no input */ + : "memory"); + return eflags; +} + +static inline void clear_ts(void) +{ + ASM("clts"); +} + +static inline unsigned short get_tr(void) +{ + unsigned short seg; + ASM("str %0" + : "=rm"(seg)); + return (seg); +} + +static inline void set_tr(unsigned int seg) +{ + ASM("ltr %0" + : + : "rm"((unsigned short)(seg))); +} + +static inline unsigned short sldt(void) +{ + unsigned short seg; + ASM("sldt %0" + : "=rm"(seg)); + return (seg); +} + +static inline void lldt(unsigned int seg) +{ + ASM("lldt %0" + : + : "rm"((unsigned short)(seg))); +} + +static inline void lgdt(uintptr_t *desc) +{ + ASM("lgdt %0" + : + : "m"(*desc)); +} + +static inline void lidt(uintptr_t *desc) +{ + ASM("lidt %0" + : + : "m"(*desc)); +} + +/// @brief Enable IRQs. +static inline void sti() +{ + ASM("sti" :: + : "memory"); +} + +/// @brief Disable IRQs. +static inline void cli() +{ + ASM("cli" :: + : "memory"); +} + +static inline void swapgs(void) +{ + ASM("swapgs"); +} + +static inline void hlt(void) +{ + ASM("hlt"); +} + +/// @brief Pause. +static inline void pause() +{ + ASM("pause"); +} + +// == Memory clobbers ========================================================= +// Memory clobber implies a fence, and it also impacts how the compiler treats +// potential data aliases. A memory clobber says that the asm block modifies +// memory that is not otherwise mentioned in the asm instructions. +// So, for example, a correct use of memory clobbers would be when using an +// instruction that clears a cache line. The compiler will assume that +// virtually any data may be aliased with the memory changed by that +// instruction. As a result, all required data used after the asm block +// will be reloaded from memory after the asm completes. This is much more +// expensive than the simple fence implied by the "volatile" attribute. + +// == Volatile Block ========================================================== +// Making an inline asm block "volatile" as in this example, ensures that, +// as it optimizes, the compiler does not move any instructions above or +// below the block of asm statements. +// ASM(" addic. %0,%1,%2\n" : "=r"(res): "=r"(a),"r"(a)) +// This can be particularly important in cases when the code is accessing +// shared memory. \ No newline at end of file diff --git a/mentos/inc/process/prio.h b/mentos/inc/process/prio.h index 82a4a63..843dae5 100644 --- a/mentos/inc/process/prio.h +++ b/mentos/inc/process/prio.h @@ -1,15 +1,9 @@ /// MentOS, The Mentoring Operating system project /// @file prio.h /// @brief Defines processes priority value. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#define MAX_NICE +19 - -#define MIN_NICE -20 - -#define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) - // Priority of a process goes from 0..MAX_PRIO-1, valid RT // priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH // tasks are in the range MAX_RT_PRIO..MAX_PRIO-1. Priority @@ -21,35 +15,58 @@ // priority to a value higher than any user task. // Note: MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO. +/// @brief Max niceness value. +#define MAX_NICE +19 + +/// @brief Min niceness value. +#define MIN_NICE -20 + +/// @brief Niceness range. +#define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) + +/// @brief Maximum real-time priority. #define MAX_RT_PRIO 100 +/// @brief Maximum priority. #define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) +/// @brief Default priority. #define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2) -// Convert user-nice values [ -20 ... 0 ... 19 ] -// to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ], -// and back. +/// @brief Converts user-nice values [ -20 ... 0 ... 19 ] +/// to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ]. #define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO) +/// @brief Converts static priority [ MAX_RT_PRIO..MAX_PRIO-1 ] +/// to user-nice values [ -20 ... 0 ... 19 ]. #define PRIO_TO_NICE(prio) ((prio)-DEFAULT_PRIO) -// 'User priority' is the nice value converted to something we -// can work with better when scaling various scheduler parameters, -// it's a [ 0 ... 39 ] range. +/// @brief 'User priority' is the nice value converted to something we +/// can work with better when scaling various scheduler parameters, +/// it's a [ 0 ... 39 ] range. #define USER_PRIO(p) ((p)-MAX_RT_PRIO) +/// @brief Provide easy access to the priority value of a task_struct. #define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) +/// @brief The maximum priority for a user process. #define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) +/// @brief Table that transforms the priority into a weight, used for +/// computing the virtual runtime. static const int prio_to_weight[NICE_WIDTH] = { - /* 100 */ 88761, 71755, 56483, 46273, 36291, - /* 105 */ 29154, 23254, 18705, 14949, 11916, - /* 110 */ 9548, 7620, 6100, 4904, 3906, - /* 115 */ 3121, 2501, 1991, 1586, 1277, - /* 120 */ 1024, 820, 655, 526, 423, - /* 125 */ 335, 272, 215, 172, 137, - /* 130 */ 110, 87, 70, 56, 45, - /* 135 */ 36, 29, 23, 18, 15 + /* 100 */ 88761, 71755, 56483, 46273, 36291, + /* 105 */ 29154, 23254, 18705, 14949, 11916, + /* 110 */ 9548, 7620, 6100, 4904, 3906, + /* 115 */ 3121, 2501, 1991, 1586, 1277, + /* 120 */ 1024, 820, 655, 526, 423, + /* 125 */ 335, 272, 215, 172, 137, + /* 130 */ 110, 87, 70, 56, 45, + /* 135 */ 36, 29, 23, 18, 15 }; + +/// @brief Transforms the priority to weight. +#define GET_WEIGHT(prio) prio_to_weight[USER_PRIO((prio))] + +/// @brief Weight of a default priority. +#define NICE_0_LOAD GET_WEIGHT(DEFAULT_PRIO) diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index aa26b82..a647f8b 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -1,23 +1,14 @@ /// MentOS, The Mentoring Operating system project /// @file process.h /// @brief Process data structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "list.h" -#include "tree.h" -#include "clock.h" -#include "types.h" -#include "stdint.h" -#include "stddef.h" #include "paging.h" -#include "kernel.h" -#include "unistd.h" -#include "list_head.h" -#include "signal_defs.h" -#include "limits.h" +#include "signal.h" +#include "fpu.h" /// The maximum length of a name for a task_struct. #define TASK_NAME_MAX_LENGTH 100 @@ -25,179 +16,163 @@ /// The default dimension of the stack of a process (1 MByte). #define DEFAULT_STACK_SIZE 0x100000 -//==== Task state ============================================================== -// Used in tsk->state -/// The task is running. -#define TASK_RUNNING 0x0000 - -/// The task is interruptible. -#define TASK_INTERRUPTIBLE 0x0001 - -/// The task is uninterruptible. -#define TASK_UNINTERRUPTIBLE 0x0002 - -// Used in tsk->exit_state -/// The task is dead. -#define EXIT_DEAD 0x0010 - -/// The task is zombie. -#define EXIT_ZOMBIE 0x0020 -//============================================================================== - -/// @brief Every task_struck has a sched_entity. This structure is used to track -/// the statistics of a process. While the other variables also play a role in +/// @brief This structure is used to track the statistics of a process. +/// @details +/// While the other variables also play a role in /// CFS decisions'algorithm, vruntime is by far the core variable which needs /// more attention as to understand the scheduling decision process. -/// @details +/// /// The nice value is a user-space and priority 'prio' is the process's actual /// priority that use by Linux kernel. In linux system priorities are 0 to 139 /// in which 0 to 99 for real time and 100 to 139 for users. /// The nice value range is -20 to +19 where -20 is highest, 0 default and +19 /// is lowest. relation between nice value and priority is : PR = 20 + NI. -typedef struct sched_entity { - /// Static execution priority. - int prio; +typedef struct sched_entity_t { + /// Static execution priority. + int prio; - /// Start execution time. - time_t start_runtime; + /// Start execution time. + time_t start_runtime; + /// Last context switch time. + time_t exec_start; + /// Last execution time. + time_t exec_runtime; + /// Overall execution time. + time_t sum_exec_runtime; + /// Weighted execution time. + time_t vruntime; - /// Last context switch time. - time_t exec_start; + /// Expected period of the task + time_t period; + /// Absolute deadline + time_t deadline; + /// Absolute time of arrival of the task + time_t arrivaltime; + /// Has already executed + bool_t executed; + /// Determines if it is a periodic task. + bool_t is_periodic; + /// Determines if we need to analyze the WCET of the process. + bool_t is_under_analysis; + /// Beginning of next period + time_t next_period; + /// Worst case execution time + time_t worst_case_exec; + /// Processor utilization factor + double utilization_factor; +} sched_entity_t; - /// Overall execution time. - time_t sum_exec_runtime; - - /// weighted execution time. - time_t vruntime; - -} sched_entity; - -// x86 thread (x86 and FPU registers). -typedef struct thread_struct { - /// 32-bit base pointer register. - uint32_t ebp; - - /// 32-bit stack pointer register. - uint32_t useresp; - - /// 32-bit base register. - uint32_t ebx; - - /// 32-bit data register. - uint32_t edx; - - /// 32-bit counter. - uint32_t ecx; - - /// 32-bit accumulator register. - uint32_t eax; - - /// Instruction Pointer Register. - uint32_t eip; - - /// 32-bit flag register. - uint32_t eflags; - - /// FS and GS have no hardware-assigned uses. - uint32_t gs; - - /// FS and GS have no hardware-assigned uses. - uint32_t fs; - - /// Extra Segment determined by the programmer. - uint32_t es; - - /// Data Segment. - uint32_t ds; - - /// 32-bit destination register. - uint32_t edi; - - /// 32-bit source register. - uint32_t esi; - - // ///< Code Segment. - // uint32_t cs; - - // ///< Stack Segment. - // uint32_t ss; - - /// Determines if the FPU is enabled. - bool_t fpu_enabled; - - /// Data structure used to save FPU registers. - savefpu fpu_register; -} thread_struct; +/// @brief Stores the status of CPU and FPU registers. +typedef struct thread_struct_t { + /// Stored status of registers. + pt_regs regs; + /// Stored status of registers befor jumping into a signal handler. + pt_regs signal_regs; + /// Determines if the FPU is enabled. + bool_t fpu_enabled; + /// Data structure used to save FPU registers. + savefpu fpu_register; +} thread_struct_t; /// @brief this is our task object. Every process in the system has this, and /// it holds a lot of information. It’ll hold mm information, it’s name, /// statistics, etc.. typedef struct task_struct { - /// The pid of the process. - pid_t pid; + /// The pid of the process. + pid_t pid; + /// The session id of the process + pid_t sid; + /// The Group Id of the process + pid_t gid; + /// The uid of the user owning the process. + pid_t uid; + // -1 unrunnable, 0 runnable, >0 stopped. + /// The current state of the process: + __volatile__ long state; + /// The current opened file descriptors + vfs_file_descriptor_t *fd_list; + /// The maximum supported number of file descriptors + int max_fd; + /// Pointer to process's parent. + struct task_struct *parent; + /// List head for scheduling purposes. + list_head run_list; + /// List of children traced by the process. + list_head children; + /// List of siblings. + list_head sibling; + /// The context of the processors. + thread_struct_t thread; + /// For scheduling algorithms. + sched_entity_t se; + /// Exit code of the process. (parameter of _exit() system call). + int exit_code; + /// The name of the task (Added for debug purpose). + char name[TASK_NAME_MAX_LENGTH]; + /// Task's segments. + mm_struct_t *mm; + /// Task's specific error number. + int error_no; + /// The current working directory. + char cwd[PATH_MAX]; - // -1 unrunnable, 0 runnable, >0 stopped. - /// The current state of the process: - __volatile__ long state; + // struct signal_struct *signal; + /// Instruction Pointer of the LIBC Signal Handler. + uint32_t sigreturn_eip; + /// Pointer to the process’s signal handler descriptor + sighand_t sighand; + /// Mask of blocked signals. + sigset_t blocked; + /// Temporary mask of blocked signals (used by the rt_sigtimedwait() system call) + sigset_t real_blocked; + /// The previous sig mask. + sigset_t saved_sigmask; + /// Data structure storing the private pending signals + sigpending_t pending; - /// Pointer to process's parent. - struct task_struct *parent; + /// Timer for alarm syscall. + struct timer_list* real_timer; - /// List head for scheduling purposes. - list_head run_list; + /// Next value for the real timer (ITIMER_REAL). + unsigned long it_real_incr; + /// Current value for the real timer (ITIMER_REAL). + unsigned long it_real_value; + /// Next value for the virtual timer (ITIMER_VIRTUAL). + unsigned long it_virt_incr; + /// Current value for the virtual timer (ITIMER_VIRTUAL). + unsigned long it_virt_value; + /// Next value for the profiling timer (ITIMER_PROF). + unsigned long it_prof_incr; + /// Current value for the profiling timer (ITIMER_PROF). + unsigned long it_prof_value; - /// List of children traced by the process. - list_head children; + //==== Future work ========================================================= + // - task's attributes: + // struct task_struct __rcu *real_parent; + // int exit_state; + // int exit_signal; + // struct thread_info thread_info; + // List of sibling, namely processes created by parent process + // list_head sibling; - /// List of siblings. - list_head sibling; - - /// The context of the processors. - thread_struct thread; - - /// For scheduling algorithms. - sched_entity se; - - /// Exit code of the process. (parameter of _exit() system call). - int exit_code; - - /// The name of the task (Added for debug purpose). - char name[TASK_NAME_MAX_LENGTH]; - - /// Task's segments. - struct mm_struct *mm; - - /// Task's specific error number. - int error_no; - - /// The current working directory. - char cwd[PATH_MAX]; - - //==== Future work ========================================================= - // - task's attributes: - // struct task_struct __rcu *real_parent; - // int exit_state; - // int exit_signal; - // struct thread_info thread_info; - // List of sibling, namely processes created by parent process - // struct list_head sibling; - - // - task's file descriptor: - // struct files_struct *files; - - // - task's signal handlers: - // struct signal_struct *signal; - // struct sighand_struct *sighand; - // sigset_t blocked; - // sigset_t real_blocked; - // sigset_t saved_sigmask; - // struct sigpending pending; - //========================================================================== + // - task's file descriptor: + // struct files_struct *files; + // - task's signal handlers: + // struct signal_struct *signal; + // struct sighand_struct_t *sighand; + // sigset_t blocked; + // sigset_t real_blocked; + // sigset_t saved_sigmask; + // struct sigpending pending; + //========================================================================== } task_struct; -// TODO: doxygen comment. -char *get_current_dir_name(); +/// @brief Initialize the task management. +/// @return 1 success, 0 failure. +int init_tasking(); /// @brief Create and spawn the init process. -struct task_struct *create_init_process(); +/// @param path Path of the `init` program. +/// @return Pointer to init process. +task_struct *process_create_init(const char *path); diff --git a/mentos/inc/process/scheduler.h b/mentos/inc/process/scheduler.h index 7aacb1c..5e7ded6 100644 --- a/mentos/inc/process/scheduler.h +++ b/mentos/inc/process/scheduler.h @@ -1,68 +1,115 @@ /// MentOS, The Mentoring Operating system project /// @file scheduler.h /// @brief Scheduler structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "clock.h" -#include "kernel.h" -#include "stdint.h" +#include "list_head.h" #include "process.h" +#include "stddef.h" -typedef struct { - /// Number of queued processes. - size_t num_active; - - /// Queue of processes. - list_head queue; - - /// The current running process. - task_struct *curr; +/// @brief Structure that contains information about live processes. +typedef struct runqueue_t { + /// Number of queued processes. + size_t num_active; + /// Number of queued periodic processes. + size_t num_periodic; + /// Queue of processes. + list_head queue; + /// The current running process. + task_struct *curr; } runqueue_t; +/// @brief Structure that describes scheduling parameters. +typedef struct sched_param_t { + /// Static execution priority. + int sched_priority; + /// Expected period of the task + time_t period; + /// Absolute deadline + time_t deadline; + /// Absolute time of arrival of the task + time_t arrivaltime; + /// Is task periodic? + bool_t is_periodic; +} sched_param_t; + +/// @brief Initialize the scheduler. +void scheduler_initialize(); + /// @brief Returns a non-decreasing unique process id. /// @return Process identifier (PID). -uint32_t get_new_pid(); - -/// @brief Initialize the scheduler. -void kernel_initialize_scheduler(); - -/// @brief Activate the given process. -/// @param process Process that has to be activated. -void enqueue_task(task_struct *process); - -/// @brief Removes the given process from the queue. -/// @param process Process that has to be activated. -void dequeue_task(task_struct *process); - -/// @brief Returns the number of active processes. -size_t kernel_get_active_processes(); +uint32_t scheduler_getpid(); /// @brief Returns the pointer to the current active process. -task_struct *kernel_get_current_process(); +/// @return Pointer to the current process. +task_struct *scheduler_get_current_process(); + +/// @brief Returns the maximum vruntime of all the processes in running state. +/// @return A maximum vruntime value. +time_t scheduler_get_maximum_vruntime(); + +/// @brief Returns the number of active processes. +/// @return Number of processes. +size_t scheduler_get_active_processes(); /// @brief Returns a pointer to the process with the given pid. -task_struct *kernel_get_running_process(pid_t pid); +/// @param pid The pid of the process we are looking for. +/// @return Pointer to the process, or NULL if we cannot find it. +task_struct *scheduler_get_running_process(pid_t pid); -task_struct *pick_next_task(runqueue_t *runqueue, time_t delta_exec); +/// @brief Activate the given process. +/// @param process Process that has to be activated. +void scheduler_enqueue_task(task_struct *process); -/// @brief The RR implementation of the scheduler. +/// @brief Removes the given process from the queue. +/// @param process Process that has to be activated. +void scheduler_dequeue_task(task_struct *process); + +/// @brief The RR implementation of the scheduler. /// @param f The context of the process. -void kernel_schedule(pt_regs *f); +void scheduler_run(pt_regs *f); -/// @birief Values from pt_regs to task_struct process. -void update_context(pt_regs *f, task_struct *process); +/// @brief Values from pt_regs to task_struct process. +/// @param f The set of registers we are saving. +/// @param process The process for which we are saving the CPU registers status. +void scheduler_store_context(pt_regs *f, task_struct *process); /// @brief Values from task_struct process to pt_regs. -void do_switch(task_struct *process, pt_regs *f); +/// @param process The process for which we are restoring the registers in CPU . +/// @param f The set of registers we are restoring. +void scheduler_restore_context(task_struct *process, pt_regs *f); -/// @brief Switch CPU to user mode and start running that given process. -/// @param process The process that has to be executed -void enter_user_jmp(uintptr_t location, uintptr_t stack); +/// @brief Switch CPU to user mode and start running that given process. +/// @param location The instruction pointer of the process we are starting. +/// @param stack Address of the stack of that process. +void scheduler_enter_user_jmp(uintptr_t location, uintptr_t stack); -/// @brief Sets the priority value of the given task. -int set_user_nice(task_struct *p, long nice); +/// @brief Picks the next task (in scheduler_algorithm.c). +/// @param runqueue Pointer to the runqueue. +/// @return The next task to execute. +task_struct *scheduler_pick_next_task(runqueue_t *runqueue); +/// @brief Set new scheduling settings for the given process. +/// @param pid ID of the process we are manipulating. +/// @param param New parameters. +/// @return 1 on success, -1 on error. +int sys_sched_setparam(pid_t pid, const sched_param_t *param); +/// @brief Gets the scheduling settings for the given process. +/// @param pid ID of the process we are manipulating. +/// @param param Where we store the parameters. +/// @return 1 on success, -1 on error. +int sys_sched_getparam(pid_t pid, sched_param_t *param); + +/// @brief Puts the process on wait until its next period starts. +/// @return 0 on success, a negative value on failure. +int sys_waitperiod(); + +/// @brief Returns 1 if the given group is orphaned, the session leader of the group +/// is no longer alive. +/// @param gid ID of the group +/// @return 1 if the group is orphan, 0 otherwise. +int is_orphaned_pgrp(pid_t gid); \ No newline at end of file diff --git a/mentos/inc/process/wait.h b/mentos/inc/process/wait.h new file mode 100644 index 0000000..4a6430f --- /dev/null +++ b/mentos/inc/process/wait.h @@ -0,0 +1,111 @@ +/// MentOS, The Mentoring Operating system project +/// @file wait.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "list_head.h" +#include "spinlock.h" + +/// @brief Return immediately if no child is there to be waited for. +#define WNOHANG 0x00000001 +/// @brief Return for children that are stopped, and whose status has not +/// been reported. +#define WUNTRACED 0x00000002 +/// @brief returns true if the child process exited because of a signal that +/// was not caught. +#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) +/// @brief returns true if the child process that caused the return is +/// currently stopped; this is only possible if the call was done using +/// WUNTRACED(). +#define WIFSTOPPED(status) (((status)&0xff) == 0x7f) +/// @brief evaluates to the least significant eight bits of the return code +/// of the child that terminated, which may have been set as the argument +/// to a call to exit() or as the argument for a return statement in the +/// main program. This macro can only be evaluated if WIFEXITED() +/// returned nonzero. +#define WEXITSTATUS(status) (((status)&0xff00) >> 8) +/// @brief returns the number of the signal that caused the child process to +/// terminate. This macro can only be evaluated if WIFSIGNALED() returned +/// nonzero. +#define WTERMSIG(status) ((status)&0x7f) +/// @brief Is nonzero if the child exited normally. +#define WIFEXITED(status) (WTERMSIG(status) == 0) +/// @brief returns the number of the signal that caused the child to stop. +/// This macro can only be evaluated if WIFSTOPPED() returned nonzero. +#define WSTOPSIG(status) (WEXITSTATUS(status)) + +//==== Task States ============================================================ +#define TASK_RUNNING 0x00 ///< The process is either: 1) running on CPU or 2) waiting in a run queue. +#define TASK_INTERRUPTIBLE (1 << 0) ///< The process is sleeping, waiting for some event to occur. +#define TASK_UNINTERRUPTIBLE (1 << 1) ///< Similar to TASK_INTERRUPTIBLE, but it doesn't process signals. +#define TASK_STOPPED (1 << 2) ///< Stopped, it's not running, and not able to run. +#define TASK_TRACED (1 << 3) ///< Is being monitored by other processes such as debuggers. +#define EXIT_ZOMBIE (1 << 4) ///< The process has terminated. +#define EXIT_DEAD (1 << 5) ///< The final state. +//============================================================================== + +/// @defgroup WaitQueueFlags Wait Queue Flags +/// @{ + +/// @brief When an entry has this flag is added to the end of the wait queue. +/// Entries without that flag are, instead, added to the beginning. +#define WQ_FLAG_EXCLUSIVE 0x01 +//#define WQ_FLAG_WOKEN 0x02 +//#define WQ_FLAG_BOOKMARK 0x04 +//#define WQ_FLAG_CUSTOM 0x08 +//#define WQ_FLAG_DONE 0x10 + +/// @} + +/// @brief Head of the waiting queue. +typedef struct wait_queue_head_t { + /// Locking element for the waiting queque. + spinlock_t lock; + /// Head of the waiting queue, it contains wait_queue_entry_t elements. + struct list_head task_list; +} wait_queue_head_t; + +/// @brief Entry of the waiting queue. +typedef struct wait_queue_entry_t { + /// Flags of the type WaitQueueFlags. + unsigned int flags; + /// Task associated with the wait queue entry. + struct task_struct *task; + /// Function associated with the wait queue entry. + int (*func)(struct wait_queue_entry_t *wait, unsigned mode, int sync); + /// Handler for placing the entry inside a waiting queue double linked-list. + struct list_head task_list; +} wait_queue_entry_t; + +/// @brief Initialize the waiting queue entry. +/// @param wq The entry we initialize. +/// @param task The task associated with the entry. +void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task); + +/// @brief Adds the element to the waiting queue. +/// @param head The head of the waiting queue. +/// @param wq The entry we insert inside the waiting queue. +void add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq); + +/// @brief Removes the element from the waiting queue. +/// @param head The head of the waiting queue. +/// @param wq The entry we remove from the waiting queue. +void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq); + +/// @brief The default wake function, a wrapper for try_to_wake_up. +/// @param wait The pointer to the wait queue. +/// @param mode The type of wait (TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE). +/// @param sync Specifies if the wakeup should be synchronous. +/// @return 1 on success, 0 on failure. +int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync); + +/// @brief Sets the state of the current process to TASK_UNINTERRUPTIBLE +/// and inserts it into the specified wait queue. +/// +/// @param wq Waitqueue where to sleep. +/// @return Pointer to the entry inside the wq representing the +/// sleeping process. +wait_queue_entry_t *sleep_on(wait_queue_head_t *wq); \ No newline at end of file diff --git a/mentos/inc/sys/dirent.h b/mentos/inc/sys/dirent.h deleted file mode 100644 index f81fd7a..0000000 --- a/mentos/inc/sys/dirent.h +++ /dev/null @@ -1,48 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file dirent.h -/// @brief Functions used to manage directories. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "limits.h" - -/// @brief Contains the entries of a directory. -typedef struct dirent_t { - /// The inode of the entry. - ino_t d_ino; - - /// The type of the entry. - int d_type; - - /// The nam of the entry. - char d_name[NAME_MAX + 1]; -} dirent_t; - -/// @brief Contains information concerning a directory. -typedef struct DIR { - /// Filesystem directory handle. - int fd; - /// The currently opened entry. - ino_t cur_entry; - /// Path to the directory. - char path[NAME_MAX + 1]; - /// Next directory item. - dirent_t entry; -} DIR; - -/// @brief Opens a directory and returns a handler to it. -/// @param path The path of the directory. -/// @return Pointer to the directory. Otherwise, NULL. -DIR *opendir(const char *path); - -/// @brief Given a pointer to a directory, it closes it. -int closedir(DIR *dirp); - -/// @brief At each call of this function, it returns a pointer to the next -/// element inside the directory. -/// @return A pointer to the next element. If there are no more elments, it -/// returns NULL. -dirent_t *readdir(DIR *dirp); diff --git a/mentos/inc/sys/errno.h b/mentos/inc/sys/errno.h index eeff0de..f7b4530 100644 --- a/mentos/inc/sys/errno.h +++ b/mentos/inc/sys/errno.h @@ -1,383 +1,138 @@ /// MentOS, The Mentoring Operating system project -/// @file process.c -/// @brief System call errors definition. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @file errno.h +/// @brief Errors definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once extern int *__geterrno(); +/// Provide easy access to the error number. #define errno (*__geterrno()) -/// Operation not permitted. -#define EPERM 1 - -/// No such file or directory. -#define ENOENT 2 - -/// No such process. -#define ESRCH 3 - -/// Interrupted system call. -#define EINTR 4 - -///I/O error. -#define EIO 5 - -/// No such device or address. -#define ENXIO 6 - -/// Arg list too long. -#define E2BIG 7 - -/// Exec format error. -#define ENOEXEC 8 - -/// Bad file number. -#define EBADF 9 - -/// No child processes. -#define ECHILD 10 - -/// Try again. -#define EAGAIN 11 - -/// Out of memory. -#define ENOMEM 12 - -/// Permission denied. -#define EACCES 13 - -/// Bad address. -#define EFAULT 14 - -/// Block device required. -#define ENOTBLK 15 - -/// Device or resource busy. -#define EBUSY 16 - -/// File exists. -#define EEXIST 17 - -/// Cross-device link. -#define EXDEV 18 - -/// No such device. -#define ENODEV 19 - -/// Not a directory. -#define ENOTDIR 20 - -/// Is a directory. -#define EISDIR 21 - -/// Invalid argument. -#define EINVAL 22 - -/// File table overflow. -#define ENFILE 23 - -/// Too many open files. -#define EMFILE 24 - -/// Not a typewriter. -#define ENOTTY 25 - -/// Text file busy. -#define ETXTBSY 26 - -/// File too large. -#define EFBIG 27 - -/// No space left on device. -#define ENOSPC 28 - -/// Illegal seek. -#define ESPIPE 29 - -/// Read-only file system. -#define EROFS 30 - -/// Too many links. -#define EMLINK 31 - -/// Broken pipe. -#define EPIPE 32 - -/// Math argument out of domain of func. -#define EDOM 33 - -/// Math result not representable. -#define ERANGE 34 - -/// Resource deadlock would occur. -#define EDEADLK 35 - -/// File name too long. -#define ENAMETOOLONG 36 - -/// No record locks available. -#define ENOLCK 37 - -/// Function not implemented. -#define ENOSYS 38 - -/// Directory not empty. -#define ENOTEMPTY 39 - -/// Too many symbolic links encountered. -#define ELOOP 40 - -/// Operation would block. -#define EWOULDBLOCK EAGAIN - -/// No message of desired type. -#define ENOMSG 42 - -/// Identifier removed. -#define EIDRM 43 - -/// Channel number out of range. -#define ECHRNG 44 - -/// Level 2 not synchronized. -#define EL2NSYNC 45 - -/// Level 3 halted. -#define EL3HLT 46 - -/// Level 3 reset. -#define EL3RST 47 - -/// Link number out of range. -#define ELNRNG 48 - -/// Protocol driver not attached. -#define EUNATCH 49 - -/// No CSI structure available. -#define ENOCSI 50 - -/// Level 2 halted. -#define EL2HLT 51 - -/// Invalid exchange. -#define EBADE 52 - -/// Invalid request descriptor. -#define EBADR 53 - -/// Exchange full. -#define EXFULL 54 - -/// No anode. -#define ENOANO 55 - -/// Invalid request code. -#define EBADRQC 56 - -/// Invalid slot. -#define EBADSLT 57 - -// TODO: doxygen comment. -#define EDEADLOCK EDEADLK - -/// Bad font file format. -#define EBFONT 59 - -/// Device not a stream. -#define ENOSTR 60 - -/// No data available. -#define ENODATA 61 - -/// Timer expired. -#define ETIME 62 - -/// Out of streams resources. -#define ENOSR 63 - -/// Machine is not on the network. -#define ENONET 64 - -/// Package not installed. -#define ENOPKG 65 - -/// Object is remote. -#define EREMOTE 66 - -/// Link has been severed. -#define ENOLINK 67 - -/// Advertise error. -#define EADV 68 - -/// Srmount error. -#define ESRMNT 69 - -/// Communication error on send. -#define ECOMM 70 - -/// Protocol error. -#define EPROTO 71 - -/// Multihop attempted. -#define EMULTIHOP 72 - -/// RFS specific error. -#define EDOTDOT 73 - -/// Not a data message. -#define EBADMSG 74 - -/// Value too large for defined data type. -#define EOVERFLOW 75 - -/// Name not unique on network. -#define ENOTUNIQ 76 - -/// File descriptor in bad state. -#define EBADFD 77 - -/// Remote address changed. -#define EREMCHG 78 - -/// Can not access a needed shared library. -#define ELIBACC 79 - -/// Accessing a corrupted shared library. -#define ELIBBAD 80 - -/// .lib section in a.out corrupted. -#define ELIBSCN 81 - -/// Attempting to link in too many shared libraries. -#define ELIBMAX 82 - -/// Cannot exec a shared library directly. -#define ELIBEXEC 83 - -/// Illegal byte sequence. -#define EILSEQ 84 - -/// Interrupted system call should be restarted. -#define ERESTART 85 - -/// Streams pipe error. -#define ESTRPIPE 86 - -/// Too many users. -#define EUSERS 87 - -///Socket operation on non-socket. -#define ENOTSOCK 88 - -/// Destination address required. -#define EDESTADDRREQ 89 - -/// Message too long. -#define EMSGSIZE 90 - -/// Protocol wrong type for socket. -#define EPROTOTYPE 91 - -/// Protocol not available. -#define ENOPROTOOPT 92 - -/// Protocol not supported. -#define EPROTONOSUPPORT 93 - -/// Socket type not supported. -#define ESOCKTNOSUPPORT 94 - -/// Operation not supported on transport endpoint. -#define EOPNOTSUPP 95 - -/// Protocol family not supported. -#define EPFNOSUPPORT 96 - -/// Address family not supported by protocol. -#define EAFNOSUPPORT 97 - -/// Address already in use. -#define EADDRINUSE 98 - -/// Cannot assign requested address. -#define EADDRNOTAVAIL 99 - -/// Network is down. -#define ENETDOWN 100 - -/// Network is unreachable. -#define ENETUNREACH 101 - -/// Network dropped connection because of reset. -#define ENETRESET 102 - -/// Software caused connection abort. -#define ECONNABORTED 103 - -/// Connection reset by peer. -#define ECONNRESET 104 - -/// No buffer space available. -#define ENOBUFS 105 - -/// Transport endpoint is already connected. -#define EISCONN 106 - -/// Transport endpoint is not connected. -#define ENOTCONN 107 - -/// Cannot send after transport endpoint shutdown. -#define ESHUTDOWN 108 - -/// Too many references: cannot splice. -#define ETOOMANYREFS 109 - -/// Connection timed out. -#define ETIMEDOUT 110 - -/// Connection refused. -#define ECONNREFUSED 111 - -/// Host is down. -#define EHOSTDOWN 112 - -/// No route to host. -#define EHOSTUNREACH 113 - -///Operation already in progress. -#define EALREADY 114 - -/// Operation now in progress. -#define EINPROGRESS 115 - -/// Stale NFS file handle. -#define ESTALE 116 - -/// Structure needs cleaning. -#define EUCLEAN 117 - -/// Not a XENIX named type file. -#define ENOTNAM 118 - -/// No XENIX semaphores available. -#define ENAVAIL 119 - -/// Is a named type file. -#define EISNAM 120 - -/// Remote I/O error. -#define EREMOTEIO 121 - -/// Quota exceeded. -#define EDQUOT 122 - -/// No medium found. -#define ENOMEDIUM 123 - -/// Wrong medium type. -#define EMEDIUMTYPE 124 +#define EPERM 1 ///< Operation not permitted. +#define ENOENT 2 ///< No such file or directory. +#define ESRCH 3 ///< No such process. +#define EINTR 4 ///< Interrupted system call. +#define EIO 5 ///< I/O error. +#define ENXIO 6 ///< No such device or address. +#define E2BIG 7 ///< Arg list too long. +#define ENOEXEC 8 ///< Exec format error. +#define EBADF 9 ///< Bad file number. +#define ECHILD 10 ///< No child processes. +#define EAGAIN 11 ///< Try again. +#define ENOMEM 12 ///< Out of memory. +#define EACCES 13 ///< Permission denied. +#define EFAULT 14 ///< Bad address. +#define ENOTBLK 15 ///< Block device required. +#define EBUSY 16 ///< Device or resource busy. +#define EEXIST 17 ///< File exists. +#define EXDEV 18 ///< Cross-device link. +#define ENODEV 19 ///< No such device. +#define ENOTDIR 20 ///< Not a directory. +#define EISDIR 21 ///< Is a directory. +#define EINVAL 22 ///< Invalid argument. +#define ENFILE 23 ///< File table overflow. +#define EMFILE 24 ///< Too many open files. +#define ENOTTY 25 ///< Not a typewriter. +#define ETXTBSY 26 ///< Text file busy. +#define EFBIG 27 ///< File too large. +#define ENOSPC 28 ///< No space left on device. +#define ESPIPE 29 ///< Illegal seek. +#define EROFS 30 ///< Read-only file system. +#define EMLINK 31 ///< Too many links. +#define EPIPE 32 ///< Broken pipe. +#define EDOM 33 ///< Math argument out of domain of func. +#define ERANGE 34 ///< Math result not representable. +#define EDEADLK 35 ///< Resource deadlock would occur. +#define ENAMETOOLONG 36 ///< File name too long. +#define ENOLCK 37 ///< No record locks available. +#define ENOSYS 38 ///< Function not implemented. +#define ENOTEMPTY 39 ///< Directory not empty. +#define ELOOP 40 ///< Too many symbolic links encountered. +#define EWOULDBLOCK EAGAIN ///< Operation would block. +#define ENOMSG 42 ///< No message of desired type. +#define EIDRM 43 ///< Identifier removed. +#define ECHRNG 44 ///< Channel number out of range. +#define EL2NSYNC 45 ///< Level 2 not synchronized. +#define EL3HLT 46 ///< Level 3 halted. +#define EL3RST 47 ///< Level 3 reset. +#define ELNRNG 48 ///< Link number out of range. +#define EUNATCH 49 ///< Protocol driver not attached. +#define ENOCSI 50 ///< No CSI structure available. +#define EL2HLT 51 ///< Level 2 halted. +#define EBADE 52 ///< Invalid exchange. +#define EBADR 53 ///< Invalid request descriptor. +#define EXFULL 54 ///< Exchange full. +#define ENOANO 55 ///< No anode. +#define EBADRQC 56 ///< Invalid request code. +#define EBADSLT 57 ///< Invalid slot. +#define EDEADLOCK EDEADLK ///< Resource deadlock would occur. +#define EBFONT 59 ///< Bad font file format. +#define ENOSTR 60 ///< Device not a stream. +#define ENODATA 61 ///< No data available. +#define ETIME 62 ///< Timer expired. +#define ENOSR 63 ///< Out of streams resources. +#define ENONET 64 ///< Machine is not on the network. +#define ENOPKG 65 ///< Package not installed. +#define EREMOTE 66 ///< Object is remote. +#define ENOLINK 67 ///< Link has been severed. +#define EADV 68 ///< Advertise error. +#define ESRMNT 69 ///< Srmount error. +#define ECOMM 70 ///< Communication error on send. +#define EPROTO 71 ///< Protocol error. +#define EMULTIHOP 72 ///< Multihop attempted. +#define EDOTDOT 73 ///< RFS specific error. +#define EBADMSG 74 ///< Not a data message. +#define EOVERFLOW 75 ///< Value too large for defined data type. +#define ENOTUNIQ 76 ///< Name not unique on network. +#define EBADFD 77 ///< File descriptor in bad state. +#define EREMCHG 78 ///< Remote address changed. +#define ELIBACC 79 ///< Can not access a needed shared library. +#define ELIBBAD 80 ///< Accessing a corrupted shared library. +#define ELIBSCN 81 ///< .lib section in a.out corrupted. +#define ELIBMAX 82 ///< Attempting to link in too many shared libraries. +#define ELIBEXEC 83 ///< Cannot exec a shared library directly. +#define EILSEQ 84 ///< Illegal byte sequence. +#define ERESTART 85 ///< Interrupted system call should be restarted. +#define ESTRPIPE 86 ///< Streams pipe error. +#define EUSERS 87 ///< Too many users. +#define ENOTSOCK 88 ///< Socket operation on non-socket. +#define EDESTADDRREQ 89 ///< Destination address required. +#define EMSGSIZE 90 ///< Message too long. +#define EPROTOTYPE 91 ///< Protocol wrong type for socket. +#define ENOPROTOOPT 92 ///< Protocol not available. +#define EPROTONOSUPPORT 93 ///< Protocol not supported. +#define ESOCKTNOSUPPORT 94 ///< Socket type not supported. +#define EOPNOTSUPP 95 ///< Operation not supported on transport endpoint. +#define EPFNOSUPPORT 96 ///< Protocol family not supported. +#define EAFNOSUPPORT 97 ///< Address family not supported by protocol. +#define EADDRINUSE 98 ///< Address already in use. +#define EADDRNOTAVAIL 99 ///< Cannot assign requested address. +#define ENETDOWN 100 ///< Network is down. +#define ENETUNREACH 101 ///< Network is unreachable. +#define ENETRESET 102 ///< Network dropped connection because of reset. +#define ECONNABORTED 103 ///< Software caused connection abort. +#define ECONNRESET 104 ///< Connection reset by peer. +#define ENOBUFS 105 ///< No buffer space available. +#define EISCONN 106 ///< Transport endpoint is already connected. +#define ENOTCONN 107 ///< Transport endpoint is not connected. +#define ESHUTDOWN 108 ///< Cannot send after transport endpoint shutdown. +#define ETOOMANYREFS 109 ///< Too many references: cannot splice. +#define ETIMEDOUT 110 ///< Connection timed out. +#define ECONNREFUSED 111 ///< Connection refused. +#define EHOSTDOWN 112 ///< Host is down. +#define EHOSTUNREACH 113 ///< No route to host. +#define EALREADY 114 ///< Operation already in progress. +#define EINPROGRESS 115 ///< Operation now in progress. +#define ESTALE 116 ///< Stale NFS file handle. +#define EUCLEAN 117 ///< Structure needs cleaning. +#define ENOTNAM 118 ///< Not a XENIX named type file. +#define ENAVAIL 119 ///< No XENIX semaphores available. +#define EISNAM 120 ///< Is a named type file. +#define EREMOTEIO 121 ///< Remote I/O error. +#define EDQUOT 122 ///< Quota exceeded. +#define ENOMEDIUM 123 ///< No medium found. +#define EMEDIUMTYPE 124 ///< Wrong medium type. +#define ENOTSCHEDULABLE 125 ///< The periodc process cannot be scheduled. diff --git a/mentos/inc/sys/ipc.h b/mentos/inc/sys/ipc.h index 4bd6eba..6f774e6 100644 --- a/mentos/inc/sys/ipc.h +++ b/mentos/inc/sys/ipc.h @@ -1,53 +1,35 @@ /// MentOS, The Mentoring Operating system project /// @file ipc.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stddef.h" -/// Create key if key does not exist. -#define IPC_CREAT 01000 +#define IPC_CREAT 01000 ///< Create key if key does not exist. +#define IPC_EXCL 02000 ///< Fail if key exists. +#define IPC_NOWAIT 04000 ///< Return error on wait. +#define IPC_RMID 0 ///< Remove identifier. +#define IPC_SET 1 ///< Set `ipc_perm' options. +#define IPC_STAT 2 ///< Get `ipc_perm' options. +#define IPC_INFO 3 ///< See ipcs. -/// Fail if key exists. -#define IPC_EXCL 02000 - -/// Return error on wait. -#define IPC_NOWAIT 04000 - -/// Remove identifier. -#define IPC_RMID 0 - -/// Set `ipc_perm' options. -#define IPC_SET 1 - -/// Get `ipc_perm' options. -#define IPC_STAT 2 - -/// See ipcs. -#define IPC_INFO 3 - -struct ipc_perm { +/// @brief Holds details about IPCs. +typedef struct ipc_perm_t { /// Creator user id. - uid_t cuid; - + uid_t cuid; /// Creator group id. - gid_t cgid; - + gid_t cgid; /// User id. - uid_t uid; - + uid_t uid; /// Group id. - gid_t gid; - + gid_t gid; /// r/w permission. - ushort mode; - + ushort mode; /// Sequence # (to generate unique msg/sem/shm id). - ushort seq; - + ushort seq; /// User specified msg/sem/shm key. - key_t key; -}; + key_t key; +} ipc_perm_t; diff --git a/mentos/inc/sys/kernel_levels.h b/mentos/inc/sys/kernel_levels.h new file mode 100644 index 0000000..5611219 --- /dev/null +++ b/mentos/inc/sys/kernel_levels.h @@ -0,0 +1,31 @@ +/// MentOS, The Mentoring Operating system project +/// @file kernel_levels.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define KERN_SOH "\001" ///< ASCII Start Of Header. +#define KERN_SOH_ASCII '\001' ///< Character version of the SOH. + +#define KERN_EMERG KERN_SOH "0" ///< Emergency messages (precede a crash) +#define KERN_ALERT KERN_SOH "1" ///< Error requiring immediate attention +#define KERN_CRIT KERN_SOH "2" ///< Critical error (hardware or software) +#define KERN_ERR KERN_SOH "3" ///< Error conditions (common in drivers) +#define KERN_WARNING KERN_SOH "4" ///< Warning conditions (could lead to errors) +#define KERN_NOTICE KERN_SOH "5" ///< Not an error but a significant condition +#define KERN_INFO KERN_SOH "6" ///< Informational message +#define KERN_DEBUG KERN_SOH "7" ///< Used only for debug messages +#define KERN_DEFAULT "" ///< Default kernel logging level + +// Integer equivalents of KERN_ +#define LOGLEVEL_DEFAULT (-1) ///< default-level messages. +#define LOGLEVEL_EMERG 0 ///< system is unusable. +#define LOGLEVEL_ALERT 1 ///< action must be taken immediately. +#define LOGLEVEL_CRIT 2 ///< critical conditions. +#define LOGLEVEL_ERR 3 ///< error conditions. +#define LOGLEVEL_WARNING 4 ///< warning conditions. +#define LOGLEVEL_NOTICE 5 ///< normal but significant condition. +#define LOGLEVEL_INFO 6 ///< informational. +#define LOGLEVEL_DEBUG 7 ///< debug-level messages. diff --git a/mentos/inc/sys/module.h b/mentos/inc/sys/module.h index 02386d7..87e110b 100644 --- a/mentos/inc/sys/module.h +++ b/mentos/inc/sys/module.h @@ -1,7 +1,29 @@ /// MentOS, The Mentoring Operating system project /// @file module.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once + +#include "multiboot.h" +#include "stdint.h" + +/// The maximum number of modules. +#define MAX_MODULES 10 + +extern multiboot_module_t modules[MAX_MODULES]; + +/// @brief Ininitialize the modules. +/// @param header Multiboot info used to initialize the modules. +/// @return 1 on success, 0 on error. +int init_modules(multiboot_info_t *header); + +/// @brief Relocates modules to virtual mapped low memory, to allow physical +/// unmapping of the first part of the ram. +/// @return 1 on success, 0 on failure. +int relocate_modules(); + +/// @brief Returns the address where the modules end. +/// @return Address after the modules. +uintptr_t get_address_after_modules(); \ No newline at end of file diff --git a/mentos/inc/sys/reboot.h b/mentos/inc/sys/reboot.h index 8547eff..3fd404e 100644 --- a/mentos/inc/sys/reboot.h +++ b/mentos/inc/sys/reboot.h @@ -1,43 +1,36 @@ /// MentOS, The Mentoring Operating system project /// @file reboot.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -// Magic values required to use _reboot() system call. +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC1 0xfee1dead - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2 672274793 - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2A 85072278 - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2B 369367448 - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2C 537993216 // Commands accepted by the _reboot() system call. /// Restart system using default command and mode. #define LINUX_REBOOT_CMD_RESTART 0x01234567 - /// Stop OS and give system control to ROM monitor, if any. #define LINUX_REBOOT_CMD_HALT 0xCDEF0123 - /// Ctrl-Alt-Del sequence causes RESTART command. #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF - /// Ctrl-Alt-Del sequence sends SIGINT to init task. #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 - /// Stop OS and remove all power from system, if possible. #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC - /// Restart system using given command string. #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 - /// Suspend system using software suspend if compiled in. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 - /// Restart system using a previously loaded Linux kernel #define LINUX_REBOOT_CMD_KEXEC 0x45584543 diff --git a/mentos/inc/sys/stat.h b/mentos/inc/sys/stat.h deleted file mode 100644 index 05aa9b6..0000000 --- a/mentos/inc/sys/stat.h +++ /dev/null @@ -1,41 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stat.h -/// @brief Stat functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "clock.h" -#include "stddef.h" - -/// @brief Data structure which contains information about a file. -typedef struct stat_t -{ - /// ID of device containing file. - dev_t st_dev; - /// Fle serial number. - ino_t st_ino; - /// Mode of file. - mode_t st_mode; - /// User id del file. - uid_t st_uid; - /// Group id del file. - gid_t st_gid; - /// Dimensione del file. - off_t st_size; - /// Time of last access. - time_t st_atime; - /// Time of last data modification. - time_t st_mtime; - /// Time of last status change. - time_t st_ctime; -} stat_t; - -/// @brief Retrieves information about the file at the given location. -/// @param path The file descriptor of the file that is being inquired. -/// @param buf A structure where data about the file will be stored. -/// @return Returns a negative value on failure. -int stat(const char *path, stat_t *buf); - -int mkdir(const char *path, mode_t mode); diff --git a/mentos/inc/sys/types.h b/mentos/inc/sys/types.h index 4457700..42336b2 100644 --- a/mentos/inc/sys/types.h +++ b/mentos/inc/sys/types.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file types.h /// @brief Collection of Kernel datatype -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -15,53 +15,56 @@ typedef unsigned int user_t; /// The type of process status. typedef unsigned int status_t; +/// Type for system keys. +typedef int key_t; + /// Defines the list of flags of a process. typedef enum eflags_list { - /// Carry flag. - EFLAG_CF = (1 << 0), + /// Carry flag. + EFLAG_CF = (1 << 0), - /// Parity flag. - EFLAG_PF = (1 << 2), + /// Parity flag. + EFLAG_PF = (1 << 2), - /// Auxiliary carry flag. - EFLAG_AF = (1 << 4), + /// Auxiliary carry flag. + EFLAG_AF = (1 << 4), - /// Zero flag. - EFLAG_ZF = (1 << 6), + /// Zero flag. + EFLAG_ZF = (1 << 6), - /// Sign flag. - EFLAG_SF = (1 << 7), + /// Sign flag. + EFLAG_SF = (1 << 7), - /// Trap flag. - EFLAG_TF = (1 << 8), + /// Trap flag. + EFLAG_TF = (1 << 8), - /// Interrupt enable flag. - EFLAG_IF = (1 << 9), + /// Interrupt enable flag. + EFLAG_IF = (1 << 9), - /// Direction flag. - EFLAG_DF = (1 << 10), + /// Direction flag. + EFLAG_DF = (1 << 10), - /// Overflow flag. - EFLAG_OF = (1 << 11), + /// Overflow flag. + EFLAG_OF = (1 << 11), - /// Nested task flag. - EFLAG_NT = (1 << 14), + /// Nested task flag. + EFLAG_NT = (1 << 14), - /// Resume flag. - EFLAG_RF = (1 << 16), + /// Resume flag. + EFLAG_RF = (1 << 16), - /// Virtual 8086 mode flag. - EFLAG_VM = (1 << 17), + /// Virtual 8086 mode flag. + EFLAG_VM = (1 << 17), - /// Alignment check flag (486+). - EFLAG_AC = (1 << 18), + /// Alignment check flag (486+). + EFLAG_AC = (1 << 18), - /// Virutal interrupt flag. - EFLAG_VIF = (1 << 19), + /// Virutal interrupt flag. + EFLAG_VIF = (1 << 19), - /// Virtual interrupt pending flag. - EFLAG_VIP = (1 << 20), + /// Virtual interrupt pending flag. + EFLAG_VIP = (1 << 20), - /// ID flag. - EFLAG_ID = (1 << 21), + /// ID flag. + EFLAG_ID = (1 << 21), } eflags_list; diff --git a/mentos/inc/sys/unistd.h b/mentos/inc/sys/unistd.h deleted file mode 100644 index d49cce1..0000000 --- a/mentos/inc/sys/unistd.h +++ /dev/null @@ -1,95 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file unistd.h -/// @brief Functions used to manage files. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "types.h" -#include "stddef.h" - -//======== Standard file descriptors =========================================== -/// Standard input. -#define STDIN_FILENO -3 - -/// Standard output. -#define STDOUT_FILENO -2 - -/// Standard error output. -#define STDERR_FILENO -1 -//============================================================================== - -/// Maximum number of opened file. -#define MAX_OPEN_FD 4 - -/// @brief Read data from a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer. -/// @param nbytes The number of bytes to read. -/// @return The number of read characters. -ssize_t read(int fd, void *buf, size_t nbytes); - -/// @brief Write data into a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer collecting data to written. -/// @param nbytes The number of bytes to write. -/// @return The number of written bytes. -ssize_t write(int fd, void *buf, size_t nbytes); - -/// @brief Opens the file specified by pathname. -/// @param pathname A pathname for a file. -/// @param flags Used to set the file status flags and file access modes -/// of the open file description. -/// @param mode Specifies the file mode bits be applied when a new file -/// is created. -/// @return Returns a file descriptor, a small, nonnegative integer for -/// use in subsequent system calls. -int open(const char *pathname, int flags, mode_t mode); - -/// @brief Close a file descriptor. -/// @param fd The file descriptor. -/// @return The result of the operation. -int close(int fd); - -/// @brief Removes the given directory. -/// @param path The path to the directory to remove. -/// @return -int rmdir(const char *path); - -/// @brief Wrapper for exit system call. -extern void exit(int status); - -/// @brief Return the process identifier of the calling process. -/// @return pid_t process identifier. -extern pid_t getpid(); - -/// @brief Clone the calling process, but without copying the whole address space. -/// The calling process is suspended until the new process exits or is -/// replaced by a call to `execve'. Return -1 for errors, 0 to the new -/// process, and the process ID of the new process to the old process. -/// @return -extern pid_t vfork(); - -/// @brief Replaces the current process image with a new process image. -/// @param path The path to the binary file to execute. -/// @param argv The list of arguments. -/// @param envp -/// @return -extern int execve(const char *path, char *const argv[], char *const envp[]); - -/// @brief Adds inc to the nice value for the calling thread. -/// @param inc The value to add to the nice. -/// @return On success, the new nice value is returned. On error, -1 is -/// returned, and errno is set appropriately. -int nice(int inc); - -/// @brief Reboot system call. -/// @param cmd -/// @param arg -/// @return -int reboot(int magic1, int magic2, unsigned int cmd, void *arg); - -void getcwd(char *path, size_t size); - -void chdir(char const *path); diff --git a/mentos/inc/sys/utsname.h b/mentos/inc/sys/utsname.h index c79cc74..48b945a 100644 --- a/mentos/inc/sys/utsname.h +++ b/mentos/inc/sys/utsname.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file utsname.h /// @brief Functions used to provide information about the machine & OS. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -11,18 +11,19 @@ /// @brief Holds information concerning the machine and the os. typedef struct utsname_t { - /// The name of the system. - char sysname[SYS_LEN]; - - /// The name of the node. - char nodename[SYS_LEN]; - - /// The version of the OS. - char version[SYS_LEN]; - - /// The name of the machine. - char machine[SYS_LEN]; + /// The name of the system. + char sysname[SYS_LEN]; + /// The name of the node. + char nodename[SYS_LEN]; + /// Operating system release (e.g., "2.6.28"). + char release[SYS_LEN]; + /// The version of the OS. + char version[SYS_LEN]; + /// The name of the machine. + char machine[SYS_LEN]; } utsname_t; -/// @brief Sets the values of os_infos. -int uname(utsname_t *os_infos); +/// @brief Returns system information in the structure pointed to by buf. +/// @param buf Buffer where the info will be placed. +/// @return 0 on success, a negative value on failure. +int sys_uname(utsname_t *buf); diff --git a/mentos/inc/system/panic.h b/mentos/inc/system/panic.h index eb7b2ce..e1cbaf6 100644 --- a/mentos/inc/system/panic.h +++ b/mentos/inc/system/panic.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file panic.h /// @brief Functions used to manage kernel panic. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,3 +10,5 @@ /// the kernel. /// @param msg The message that has to be shown. void kernel_panic(const char *msg); + +#define TODO(msg) kernel_panic(#msg); \ No newline at end of file diff --git a/mentos/inc/system/printk.h b/mentos/inc/system/printk.h index c14ed1c..b3adc71 100644 --- a/mentos/inc/system/printk.h +++ b/mentos/inc/system/printk.h @@ -1,10 +1,13 @@ /// MentOS, The Mentoring Operating system project -/// @file printk.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @file printk.h +/// @brief Functions for managing the kernel messages. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once /// @brief Write formatted output to stdout. -void printk(const char *, ...); +/// @param format Output formatted as for printf. +/// @param ... List of arguments. +/// @return The number of bytes written in syslog. +int sys_syslog(const char *format, ...); diff --git a/mentos/inc/system/signal.h b/mentos/inc/system/signal.h new file mode 100644 index 0000000..249c874 --- /dev/null +++ b/mentos/inc/system/signal.h @@ -0,0 +1,325 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.h +/// @brief Signals definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdatomic.h" +#include "spinlock.h" +#include "list_head.h" +#include "syscall.h" + +/// @brief Signal codes. +typedef enum { + SIGHUP = 1, ///< Hang up detected on controlling terminal or death of controlling process. + SIGINT = 2, ///< Issued if the user sends an interrupt signal (Ctrl + C). + SIGQUIT = 3, ///< Issued if the user sends a quit signal (Ctrl + D). + SIGILL = 4, ///< Illegal Instruction. + SIGTRAP = 5, ///< Trace/breakpoint trap. + SIGABRT = 6, ///< Abort signal from abort(). + SIGEMT = 7, ///< Emulator trap. + SIGFPE = 8, ///< Floating-point arithmetic exception. + SIGKILL = 9, ///< If a process gets this signal it must quit immediately and will not perform any clean-up operations. + SIGBUS = 10, ///< Bus error (bad memory access). + SIGSEGV = 11, ///< Invalid memory reference. + SIGSYS = 12, ///< Bad system call (SVr4). + SIGPIPE = 13, ///< Broken pipe: write to pipe with no readers. + SIGALRM = 14, ///< Alarm clock signal (used for timers). + SIGTERM = 15, ///< Software termination signal (sent by kill by default). + SIGUSR1 = 16, ///< User-defined signal 1. + SIGUSR2 = 17, ///< User-defined signal 2. + SIGCHLD = 18, ///< Child stopped or terminated. + SIGPWR = 19, ///< Power failure. + SIGWINCH = 20, ///< Window resize signal. + SIGURG = 21, ///< Urgent condition on socket. + SIGPOLL = 22, ///< Pollable event. + SIGSTOP = 23, ///< Stop process. + SIGTSTP = 24, ///< Stop typed at terminal. + SIGCONT = 25, ///< Continue if stopped. + SIGTTIN = 26, ///< Terminal input for background process. + SIGTTOU = 27, ///< Terminal output for background process. + SIGVTALRM = 28, ///< Virtual alarm clock. + SIGPROF = 29, ///< Profiling timer expired. + SIGXCPU = 30, ///< CPU time limit exceeded. + SIGXFSZ = 31, ///< File size limit exceeded. + NSIG +} signal_type_t; + +/// @brief Codes that indentify the sender of a signal. +typedef enum { + SI_NOINFO, ///< Unable to determine complete signal information. + + // Signal : - + // Enabled fields : si_pid, si_uid + SI_USER, ///< Signal sent by kill(), pthread_kill(), raise(), abort() or alarm(). + + // Signal : - + // Enabled fields : - + SI_KERNEL, ///< Generic kernel function + + // Signal : - + // Enabled fields : si_pid, si_uid, si_value + SI_QUEUE, ///< Signal was sent by sigqueue(). + SI_TIMER, ///< Signal was generated by expiration of a timer set by timer_settimer(). + SI_ASYNCIO, ///< Signal was generated by completion of an asynchronous I/O request. + SI_MESGQ, ///< Signal was generated by arrival of a message on an empty message queue. + + // Signal : SIGILL + // Enabled fields : si_addr (address of failing instruction) + ILL_ILLOPC, ///< Illegal opcode. + ILL_ILLOPN, ///< Illegal operand. + ILL_ILLADR, ///< Illegal addressing mode. + ILL_ILLTRP, ///< Illegal trap. + ILL_PRVOPC, ///< Privileged opcode. + ILL_PRVREG, ///< Privileged register. + ILL_COPROC, ///< Coprocessor error. + ILL_BADSTK, ///< Internal stack error. + + // Signal : SIGFPE + // Enabled fields : si_addr (address of failing instruction) + FPE_INTDIV, ///< Integer divide-by-zero. + FPE_INTOVF, ///< Integer overflow. + FPE_FLTDIV, ///< Floating point divide-by-zero. + FPE_FLTOVF, ///< Floating point overflow. + FPE_FLTUND, ///< Floating point underflow. + FPE_FLTRES, ///< Floating point inexact result. + FPE_FLTINV, ///< Invalid floating point operation. + FPE_FLTSUB, ///< Subscript out of range. + + // Signal : SIGSEGV + // Enabled fields : si_addr (address of faulting memory reference) + SEGV_MAPERR, ///< Address not mapped. + SEGV_ACCERR, ///< Invalid permissions. + + // Signal : SIGBUS + // Enabled fields : si_addr (address of faulting memory reference) + BUS_ADRALN, ///< Invalid address alignment. + BUS_ADRERR, ///< Non-existent physical address. + BUS_OBJERR, ///< Object-specific hardware error. + + // Signal : SIGTRAP + // Enabled fields : - + TRAP_BRKPT, ///< Process breakpoint. + TRAP_TRACE, ///< Process trace trap. + + // Signal : SIGCHLD + // Enabled fields : si_pid (child process ID) + // si_uid (real user ID of process that sent the signal) + // si_status (exit value or signal) + CLD_EXITED, ///< Child has exited. + CLD_KILLED, ///< Child has terminated abnormally and did not create a core file. + CLD_DUMPED, ///< Child has terminated abnormally and created a core file. + CLD_TRAPPED, ///< Traced child has trapped. + CLD_STOPPED, ///< Child has stopped. + CLD_CONTINUED, ///< Stopped child has continued. + + // Signal : SIGIO/SIGPOLL + // Enabled fields : si_band + POLL_IN, ///< Data input available. + POLL_OUT, ///< Output buffers available. + POLL_MSG, ///< Input message available. + POLL_ERR, ///< I/O error. + POLL_PRI, ///< High priority input available. + POLL_HUP, ///< Device disconnected. +} signal_sender_code_t; + +/// @brief Defines what to do with the provided signal mask. +typedef enum { + /// @brief The set of blocked signals is the union of the current set + /// and the set argument. + SIG_BLOCK, + /// @brief The signals in set are removed from the current set of + /// blocked signals. It is permissible to attempt to unblock + /// a signal which is not blocked. + SIG_UNBLOCK, + /// @brief The set of blocked signals is set to the argument set. + SIG_SETMASK +} sigmask_how_t; + +/// @defgroup SigactionFlags Flags associated with a sigaction. +/// @{ + +#define SA_NOCLDSTOP 0x00000001U ///< Turn off SIGCHLD when children stop. +#define SA_NOCLDWAIT 0x00000002U ///< Flag on SIGCHLD to inhibit zombies. +#define SA_SIGINFO 0x00000004U ///< sa_sigaction specifies the signal-handling function for signum. +#define SA_ONSTACK 0x08000000U ///< Indicates that a registered stack_t will be used. +#define SA_RESTART 0x10000000U ///< Flag to get restarting signals (which were the default long ago) +#define SA_NODEFER 0x40000000U ///< Prevents the current signal from being masked in the handler. +#define SA_RESETHAND 0x80000000U ///< Clears the handler when the signal is delivered. + +/// @} + +/// Type of a signal handler. +typedef void (*sighandler_t)(int); + +#define SIG_DFL ((sighandler_t)0) ///< Default signal handling. +#define SIG_IGN ((sighandler_t)1) ///< ignore signal. +#define SIG_ERR ((sighandler_t)-1) ///< error return from signal. + +/// @brief Structure used to mask and unmask signals. +/// @details +/// Each unsigned long consists of 32 bits, thus, the maximum number of signals +/// that may be declared is 64. +/// Signals are divided into two cathegories, identified by the two unsigned longs: +/// [ 1, 31] corresponds to normal signals; +/// [32, 64] corresponds to real-time signals. +typedef struct sigset_t { + /// Signals divided into two cathegories. + unsigned long sig[2]; +} sigset_t; + +/// @brief Holds the information on how to handle a specific signal. +typedef struct sigaction_t { + /// This field specifies the type of action to be performed; its value can be a pointer + /// to the signal handler, SIG_DFL (that is, the value 0) to specify that the default + /// action is performed, or SIG_IGN (that is, the value 1) to specify that the signal is + /// ignored. + sighandler_t sa_handler; + /// This sigset_t variable specifies the signals to be masked when running the signal handler + sigset_t sa_mask; + /// This set of flags specifies how the signal must be handled; + unsigned int sa_flags; +} sigaction_t; + +/// @brief Describes how each signal must be handled. +typedef struct sighand_t { + /// Usage counter of the signal handler descriptor. + atomic_t count; + /// Array of structures specifying the actions to be performed upon delivering the signals + sigaction_t action[NSIG]; + /// Spinlock protecting both the signal descriptor and the signal handler descriptor. + spinlock_t siglock; +} sighand_t; + +/// @brief Data passed with signal info. +typedef union sigval { + int sival_int; ///< Integer value. + void *sival_ptr; ///< Pointer value. +} sigval_t; + +/// @brief Stores information about an occurrence of a specific signal. +typedef struct siginfo_t { + /// The signal number. + int si_signo; + /// A code identifying who raised the signal (see signal_sender_code_t). + int si_code; + /// Signal value. + sigval_t si_value; + /// The error code of the instruction that caused the signal to be raised, or 0 if there was no error. + int si_errno; + /// Process ID of sending process. + pid_t si_pid; + /// Real user ID of sending process. + uid_t si_uid; + /// Address at which fault occurred. + void *si_addr; + /// Exit value or signal for process termination. + int si_status; + /// Band event for SIGPOLL/SIGIO. + int si_band; +} siginfo_t; + +/// @brief An entry of the signal queue. +typedef struct sigqueue_t { + /// Links for the pending signal queue’s list. + list_head list; + /// Flags associated with the queued signal. + int flags; + /// Describes the event that raised the signal. + siginfo_t info; + // Pointer to the user data structure of the process’s owner. + //struct user_struct *user; +} sigqueue_t; + +/// @brief Keeps information of pending signals. +typedef struct sigpending_t { + /// Head of the list of pending signals. + list_head list; + /// The mask which can be queried to know which signals are pending. + sigset_t signal; +} sigpending_t; + +/// These can be the second arg to send_sig_info/send_group_sig_info. +#define SEND_SIG_NOINFO ((siginfo_t *)0) + +/// @brief Handle the return from a signal handler. +/// @param f The stack frame when returning from a signal handler. +/// @return never. +long sys_sigreturn(struct pt_regs *f); + +/// @brief Handles the signals of the current process. +/// @param f The address of the stack area where the User Mode register +/// contents of the current process are saved. +/// @return If we are handling a signal, thus, `regs` have been modified +/// to handle it (e.g., eip is now poiting at the handler). +int do_signal(struct pt_regs *f); + +/// @brief Initialize the signals. +/// @return 1 on success, 0 on failure. +int signals_init(); + +/// @brief Send signal to one specific process. +/// @param pid The PID of the process. +/// @param sig The signal to be sent. +/// @return +int sys_kill(pid_t pid, int sig); + +/// @brief Sets the disposition of the signal signum to handler. +/// @param signum The signal number. +/// @param handler The handler for the signal. +/// @return The previous value of the signal handler, or SIG_ERR on error. +sighandler_t sys_signal(int signum, sighandler_t handler); + +/// @brief Examine and change a signal action. +/// @param signum Specifies the signal and can be any valid signal except SIGKILL and SIGSTOP. +/// @param act If non-NULL, the new action for signal signum is installed from act. +/// @param oldact If non-NULL, the previous action is saved in oldact. +/// @return returns 0 on success; on error, -1 is returned, and errno is set to indicate the error. +int sys_sigaction(int signum, const sigaction_t *act, sigaction_t *oldact); + +/// @brief Examine and change blocked signals. +/// @param how Determines the behavior of the call. +/// @param set The set of signals to manage by the function. +/// @param oldset If non-NULL, the previous value of the signal mask is stored here. +/// @return returns 0 on success, and -1 on error (errno is set to indicate the cause). +/// @details +/// If set is NULL, then the signal mask is unchanged (i.e., how is +/// ignored), but the current value of the signal mask is +/// nevertheless returned in oldset (if it is not NULL). +int sys_sigprocmask(int how, const sigset_t *set, sigset_t *oldset); + +/// @brief Returns the string describing the given signal. +/// @param sig The signal to inquire. +/// @return String representing the signal. +const char *strsignal(int sig); + +/// @brief Prepare an empty set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigemptyset(sigset_t *set); + +/// @brief Prepare a full set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigfillset(sigset_t *set); + +/// @brief Adds the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigaddset(sigset_t *set, int signum); + +/// @brief Removes the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigdelset(sigset_t *set, int signum); + +/// @brief Checks if the given signal is part of the set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 1 if signum is a member of set, +/// 0 if signum is not a member, and -1 on error. +int sigismember(sigset_t *set, int signum); \ No newline at end of file diff --git a/mentos/inc/system/signal_defs.h b/mentos/inc/system/signal_defs.h deleted file mode 100644 index d06a730..0000000 --- a/mentos/inc/system/signal_defs.h +++ /dev/null @@ -1,121 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file signal_defs.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -// Signal names (from the Unix specification on signals) - -/// Hangup. -#define SIGHUP 1 - -/// Interupt. -#define SIGINT 2 - -/// Quit. -#define SIGQUIT 3 - -/// Illegal instruction. -#define SIGILL 4 - -/// A breakpoint or trace instruction has been reached. -#define SIGTRAP 5 - -/// Another process has requested that you abort. -#define SIGABRT 6 - -/// Emulation trap XXX. -#define SIGEMT 7 - -/// Floating-point arithmetic exception. -#define SIGFPE 8 - -/// You have been stabbed repeated with a large knife. -#define SIGKILL 9 - -/// Bus error (device error). -#define SIGBUS 10 - -/// Segmentation fault. -#define SIGSEGV 11 - -/// Bad system call. -#define SIGSYS 12 - -/// Attempted to read or write from a broken pipe. -#define SIGPIPE 13 - -/// This is your wakeup call. -#define SIGALRM 14 - -/// You have been Schwarzenegger'd. -#define SIGTERM 15 - -/// User Defined Signal #1. -#define SIGUSR1 16 - -/// User Defined Signal #2. -#define SIGUSR2 17 - -/// Child status report. -#define SIGCHLD 18 - -/// We need moar powah!. -#define SIGPWR 19 - -/// Your containing terminal has changed size. -#define SIGWINCH 20 - -/// An URGENT! event (On a socket). -#define SIGURG 21 - -/// XXX OBSOLETE; socket i/o possible. -#define SIGPOLL 22 - -/// Stopped (signal). -#define SIGSTOP 23 - -/// ^Z (suspend). -#define SIGTSTP 24 - -/// Unsuspended (please, continue). -#define SIGCONT 25 - -/// TTY input has stopped. -#define SIGTTIN 26 - -/// TTY output has stopped. -#define SIGTTOUT 27 - -/// Virtual timer has expired. -#define SIGVTALRM 28 - -/// Profiling timer expired. -#define SIGPROF 29 - -/// CPU time limit exceeded. -#define SIGXCPU 30 - -/// File size limit exceeded. -#define SIGXFSZ 31 - -/// Herp. -#define SIGWAITING 32 - -/// Die in a fire. -#define SIGDIAF 33 - -/// The sending process does not like you. -#define SIGHATE 34 - -/// Window server event. -#define SIGWINEVENT 35 - -/// Everybody loves cats. -#define SIGCAT 36 - -#define SIGTTOU 37 - -#define NUMSIGNALS 38 - -#define NSIG NUMSIGNALS diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index fa37331..98747e9 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -1,40 +1,56 @@ /// MentOS, The Mentoring Operating system project /// @file syscall.h /// @brief System Call handler definition. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "syscall_types.h" +#include "vfs_types.h" #include "kernel.h" -#include "dirent.h" +#include "sys/dirent.h" #include "types.h" -#include "stat.h" /// @brief Initialize the system calls. void syscall_init(); /// @brief Handler for the system calls. -void syscall_handler(pt_regs *r); +/// @param f The interrupt stack frame. +void syscall_handler(pt_regs *f); + +/// @brief Returns the current interrupt stack frame. +/// @return Pointer to the stack frame. +pt_regs* get_current_interrupt_stack_frame(); /// The exit() function causes normal process termination. +/// @param exit_code The exit code. void sys_exit(int exit_code); -/// @brief Read data from a file descriptor. +/// @brief Read data from a file descriptor. /// @param fd The file descriptor. /// @param buf The buffer. /// @param nbytes The number of bytes to read. -/// @return The number of read characters. +/// @return The number of read characters. ssize_t sys_read(int fd, void *buf, size_t nbytes); -/// @brief Write data into a file descriptor. +/// @brief Write data into a file descriptor. /// @param fd The file descriptor. /// @param buf The buffer collecting data to written. /// @param nbytes The number of bytes to write. -/// @return The number of written bytes. +/// @return The number of written bytes. ssize_t sys_write(int fd, void *buf, size_t nbytes); +/// @brief Repositions the file offset inside a file. +/// @param fd The file descriptor of the file. +/// @param offset The offest to use for the operation. +/// @param whence The type of operation. +/// @return Upon successful completion, returns the resulting offset +/// location as measured in bytes from the beginning of the file. On +/// error, the value (off_t) -1 is returned and errno is set to +/// indicate the error. +off_t sys_lseek(int fd, off_t offset, int whence); + /// @brief Given a pathname for a file, open() returns a file /// descriptor, a small, nonnegative integer for use in /// subsequent system calls. @@ -52,33 +68,134 @@ int sys_open(const char *pathname, int flags, mode_t mode); /// @return int sys_close(int fd); +/// @brief Delete a name and possibly the file it refers to. +/// @param path A pathname for a file. +/// @return On success, zero is returned. On error, -1 is returned, and errno is set appropriately. +int sys_unlink(const char *path); + /// @brief Suspends execution of the calling thread until a child specified -/// by pid argument has changed state. +/// by pid argument has changed state. +/// @param pid The pid to wait. +/// @param status If not NULL, store status information here. +/// @param options Determines the wait behaviour. +/// @return on success, returns the process ID of the terminated +/// child; on error, -1 is returned. pid_t sys_waitpid(pid_t pid, int *status, int options); /// @brief Replaces the current process image with a new process image. -int sys_execve(pt_regs *r); +/// @param f CPU registers whe calling this function. +/// @return 0 on success, -1 on error. +int sys_execve(pt_regs *f); +/// @brief Changes the working directory. +/// @param path The new working directory. void sys_chdir(char const *path); -/// Returns the process ID (PID) of the calling process. +/// @brief Changes the working directory. +/// @param fd File descriptor of the new working directory. +void sys_fchdir(int fd); + +/// @brief Returns the process ID (PID) of the calling process. +/// @return The process ID. pid_t sys_getpid(); -/// @brief Adds the increment to the priority value of the task. -int sys_nice(int increment); +///@brief Return session id of the given process. +/// If pid == 0 return the SID of the calling process +/// If pid != 0 return the SID corresponding to the process having identifier == pid +///@param pid process identifier from wich we want the SID +///@return On success return SID of the session +/// Otherwise return -1 with errno set on: EPERM or ESRCH +pid_t sys_getsid(pid_t pid); -/// Returns the parent process ID (PPID) of the calling process. +///@brief creates a new session if the calling process is not a +/// process group leader. The calling process is the leader of the +/// new session (i.e., its session ID is made the same as its process +/// ID). The calling process also becomes the process group leader +/// of a new process group in the session (i.e., its process group ID +/// is made the same as its process ID). +///@return On success return SID of the session just created +/// Otherwise return -1 with errno : EPERM +pid_t sys_setsid(); + +///@brief returns the group ID of the calling process. +///@return GID of the current process +pid_t sys_getgid(); + +///@brief sets the effective group ID of the calling process. +///@param pid process identifier to +///@return On success, zero is returned. +/// Otherwise returns -1 with errno set to :EINVAL or EPERM +int sys_setgid(pid_t pid); + +/// @brief Returns the parent process ID (PPID) of the calling process. +/// @return The parent process ID. pid_t sys_getppid(); +/// @brief Adds the increment to the priority value of the task. +/// @param increment The modifier to apply to the nice value. +/// @return The new nice value. +int sys_nice(int increment); + +/// @brief Reboots the system, or enables/disables the reboot keystroke. +/// @param magic1 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC1. +/// @param magic2 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC2. +/// @param cmd The command to send to the reboot. +/// @param arg Argument passed with some specific commands. +/// @return For the values of cmd that stop or restart the system, a +/// successful call to reboot() does not return. For the other cmd +/// values, zero is returned on success. In all cases, -1 is +/// returned on failure, and errno is set appropriately. int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg); -void sys_getcwd(char *path, size_t size); +/// @brief Get current working directory. +/// @param buf The array where the CWD will be copied. +/// @param size The size of the array. +/// @return On success, returns the same pointer to buf. +/// On failure, returnr NULL, and errno is set to indicate the error. +char *sys_getcwd(char *buf, size_t size); -/// @brief Create a child process. -pid_t sys_vfork(pt_regs *r); +/// @brief Clone the calling process, but without copying the whole address space. +/// The calling process is suspended until the new process exits or is +/// replaced by a call to `execve'. +/// @param f CPU registers whe calling this function. +/// @return Return -1 for errors, 0 to the new process, and the process ID of +/// the new process to the old process. +pid_t sys_fork(pt_regs *f); +/// @brief Stat the file at the given path. +/// @param path Path to the file for which we are retrieving the statistics. +/// @param buf Buffer where we are storing the statistics. +/// @return 0 on success, a negative number if fails and errno is set. int sys_stat(const char *path, stat_t *buf); +/// @brief Retrieves information about the file at the given location. +/// @param fd The file descriptor of the file that is being inquired. +/// @param buf A structure where data about the file will be stored. +/// @return Returns a negative value on failure. +int sys_fstat(int fd, stat_t *buf); + +/// @brief Creates a new directory at the given path. +/// @param path The path of the new directory. +/// @param mode The permission of the new directory. +/// @return Returns a negative value on failure. int sys_mkdir(const char *path, mode_t mode); -dirent_t *sys_readdir(DIR *dirp); \ No newline at end of file +/// @brief Removes the given directory. +/// @param path The path to the directory to remove. +/// @return Returns a negative value on failure. +int sys_rmdir(const char *path); + +/// Provide access to the directory entries. +/// @param fd The file descriptor of the directory for which we accessing +/// the entries. +/// @param dirp The buffer where de data should be placed. +/// @param count The size of the buffer. +/// @return On success, the number of bytes read is returned. On end of +/// directory, 0 is returned. On error, -1 is returned, and errno is set +/// appropriately. +int sys_getdents(int fd, dirent_t *dirp, unsigned int count); + +/// @brief Returns the current time. +/// @param time Where the time should be stored. +/// @return The current time. +time_t sys_time(time_t *time); diff --git a/mentos/inc/system/syscall_types.h b/mentos/inc/system/syscall_types.h index c964358..cd2bf69 100644 --- a/mentos/inc/system/syscall_types.h +++ b/mentos/inc/system/syscall_types.h @@ -1,220 +1,197 @@ /// MentOS, The Mentoring Operating system project -/// @file syscall_number.h +/// @file syscall_types.h /// @brief System Call numbers. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -// Return result in eax ("=a"). -// System call number in eax ("a"). -#define DEFN_SYSCALL0(__res, num) \ - __asm__ __volatile__("INT $0x80" : "=a"(__res) : "a"(num)) - -// Return result in eax ("=a"). -// System call number in eax ("a"). -// p1 in ebx ("+b"). -#define DEFN_SYSCALL1(__res, num, p1) \ - __asm__ __volatile__("INT $0x80" : "=a"(__res), "+b"(p1) : "a"(num)) - -#define DEFN_SYSCALL2(__res, num, p1, p2) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2) \ - : "a"(num)); - -#define DEFN_SYSCALL3(__res, num, p1, p2, p3) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3) \ - : "a"(num)) - -#define DEFN_SYSCALL4(__res, num, p1, p2, p3, p4) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3), "+S"(p4) \ - : "a"(num)) - -#define DEFN_SYSCALL5(__res, num, p1, p2, p3, p4, p5) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3), \ - "+S"(p4), "+D"(p5) \ - : "a"(num)) - -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_waitpid 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_time 13 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_lchown 16 -#define __NR_stat 18 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_oldumount 22 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_fstat 28 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_brk 45 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_signal 48 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_acct 51 -#define __NR_umount 52 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_setpgid 57 -#define __NR_olduname 59 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sgetmask 68 -#define __NR_ssetmask 69 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_getrlimit 76 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_symlink 83 -#define __NR_lstat 84 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_fchown 95 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_ioperm 101 -#define __NR_socketcall 102 -#define __NR_syslog 103 -#define __NR_setitimer 104 -#define __NR_getitimer 105 -#define __NR_newstat 106 -#define __NR_newlstat 107 -#define __NR_newfstat 108 -#define __NR_uname 109 -#define __NR_iopl 110 -#define __NR_vhangup 111 -#define __NR_idle 112 -#define __NR_vm86old 113 -#define __NR_wait4 114 -#define __NR_swapoff 115 -#define __NR_sysinfo 116 -#define __NR_ipc 117 -#define __NR_fsync 118 -#define __NR_sigreturn 119 -#define __NR_clone 120 -#define __NR_setdomainname 121 -#define __NR_newuname 122 -#define __NR_modify_ldt 123 -#define __NR_adjtimex 124 -#define __NR_mprotect 125 -#define __NR_sigprocmask 126 -#define __NR_create_module 127 -#define __NR_init_module 128 -#define __NR_delete_module 129 -#define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 -#define __NR_getpgid 132 -#define __NR_fchdir 133 -#define __NR_bdflush 134 -#define __NR_sysfs 135 -#define __NR_personality 136 -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#define __NR_llseek 140 -#define __NR_getdents 141 -#define __NR_select 142 -#define __NR_flock 143 -#define __NR_msync 144 -#define __NR_readv 145 -#define __NR_writev 146 -#define __NR_getsid 147 -#define __NR_fdatasync 148 -#define __NR_sysctl 149 -#define __NR_mlock 150 -#define __NR_munlock 151 -#define __NR_mlockall 152 -#define __NR_munlockall 153 -#define __NR_sched_setparam 154 -#define __NR_sched_getparam 155 -#define __NR_sched_setscheduler 156 -#define __NR_sched_getscheduler 157 -#define __NR_sched_yield 158 -#define __NR_sched_get_priority_max 159 -#define __NR_sched_get_priority_min 160 -#define __NR_sched_rr_get_interval 161 -#define __NR_nanosleep 162 -#define __NR_mremap 163 -#define __NR_setresuid 164 -#define __NR_getresuid 165 -#define __NR_vm86 166 -#define __NR_query_module 167 -#define __NR_poll 168 -#define __NR_nfsservctl 169 -#define __NR_setresgid 170 -#define __NR_getresgid 171 -#define __NR_prctl 172 -#define __NR_rt_sigreturn 173 -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigtimedwait 177 -#define __NR_rt_sigqueueinfo 178 -#define __NR_rt_sigsuspend 179 -#define __NR_pread 180 -#define __NR_pwrite 181 -#define __NR_chown 182 -#define __NR_getcwd 183 -#define __NR_capget 184 -#define __NR_capset 185 -#define __NR_sigaltstack 186 -#define __NR_sendfile 187 -#define __NR_vfork 188 -#define __NR_free 189 // TODO: remove me! - -#define SYSCALL_NUMBER 190 \ No newline at end of file +#define __NR_exit 1 ///< System-call number for `exit` +#define __NR_fork 2 ///< System-call number for `fork` +#define __NR_read 3 ///< System-call number for `read` +#define __NR_write 4 ///< System-call number for `write` +#define __NR_open 5 ///< System-call number for `open` +#define __NR_close 6 ///< System-call number for `close` +#define __NR_waitpid 7 ///< System-call number for `waitpid` +#define __NR_creat 8 ///< System-call number for `creat` +#define __NR_link 9 ///< System-call number for `link` +#define __NR_unlink 10 ///< System-call number for `unlink` +#define __NR_execve 11 ///< System-call number for `execve` +#define __NR_chdir 12 ///< System-call number for `chdir` +#define __NR_time 13 ///< System-call number for `time` +#define __NR_mknod 14 ///< System-call number for `mknod` +#define __NR_chmod 15 ///< System-call number for `chmod` +#define __NR_lchown 16 ///< System-call number for `lchown` +#define __NR_stat 18 ///< System-call number for `stat` +#define __NR_lseek 19 ///< System-call number for `lseek` +#define __NR_getpid 20 ///< System-call number for `getpid` +#define __NR_mount 21 ///< System-call number for `mount` +#define __NR_oldumount 22 ///< System-call number for `oldumount` +#define __NR_setuid 23 ///< System-call number for `setuid` +#define __NR_getuid 24 ///< System-call number for `getuid` +#define __NR_stime 25 ///< System-call number for `stime` +#define __NR_ptrace 26 ///< System-call number for `ptrace` +#define __NR_alarm 27 ///< System-call number for `alarm` +#define __NR_fstat 28 ///< System-call number for `fstat` +#define __NR_pause 29 ///< System-call number for `pause` +#define __NR_utime 30 ///< System-call number for `utime` +#define __NR_access 33 ///< System-call number for `access` +#define __NR_nice 34 ///< System-call number for `nice` +#define __NR_sync 36 ///< System-call number for `sync` +#define __NR_kill 37 ///< System-call number for `kill` +#define __NR_rename 38 ///< System-call number for `rename` +#define __NR_mkdir 39 ///< System-call number for `mkdir` +#define __NR_rmdir 40 ///< System-call number for `rmdir` +#define __NR_dup 41 ///< System-call number for `dup` +#define __NR_pipe 42 ///< System-call number for `pipe` +#define __NR_times 43 ///< System-call number for `times` +#define __NR_brk 45 ///< System-call number for `brk` +#define __NR_setgid 46 ///< System-call number for `setgid` +#define __NR_getgid 47 ///< System-call number for `getgid` +#define __NR_signal 48 ///< System-call number for `signal` +#define __NR_geteuid 49 ///< System-call number for `geteuid` +#define __NR_getegid 50 ///< System-call number for `getegid` +#define __NR_acct 51 ///< System-call number for `acct` +#define __NR_umount 52 ///< System-call number for `umount` +#define __NR_ioctl 54 ///< System-call number for `ioctl` +#define __NR_fcntl 55 ///< System-call number for `fcntl` +#define __NR_setpgid 57 ///< System-call number for `setpgid` +#define __NR_olduname 59 ///< System-call number for `olduname` +#define __NR_umask 60 ///< System-call number for `umask` +#define __NR_chroot 61 ///< System-call number for `chroot` +#define __NR_ustat 62 ///< System-call number for `ustat` +#define __NR_dup2 63 ///< System-call number for `dup2` +#define __NR_getppid 64 ///< System-call number for `getppid` +#define __NR_getpgrp 65 ///< System-call number for `getpgrp` +#define __NR_setsid 66 ///< System-call number for `setsid` +#define __NR_sigaction 67 ///< System-call number for `sigaction` +#define __NR_sgetmask 68 ///< System-call number for `sgetmask` +#define __NR_ssetmask 69 ///< System-call number for `ssetmask` +#define __NR_setreuid 70 ///< System-call number for `setreuid` +#define __NR_setregid 71 ///< System-call number for `setregid` +#define __NR_sigsuspend 72 ///< System-call number for `sigsuspend` +#define __NR_sigpending 73 ///< System-call number for `sigpending` +#define __NR_sethostname 74 ///< System-call number for `sethostname` +#define __NR_setrlimit 75 ///< System-call number for `setrlimit` +#define __NR_getrlimit 76 ///< System-call number for `getrlimit` +#define __NR_getrusage 77 ///< System-call number for `getrusage` +#define __NR_gettimeofday 78 ///< System-call number for `gettimeofday` +#define __NR_settimeofday 79 ///< System-call number for `settimeofday` +#define __NR_getgroups 80 ///< System-call number for `getgroups` +#define __NR_setgroups 81 ///< System-call number for `setgroups` +#define __NR_symlink 83 ///< System-call number for `symlink` +#define __NR_lstat 84 ///< System-call number for `lstat` +#define __NR_readlink 85 ///< System-call number for `readlink` +#define __NR_uselib 86 ///< System-call number for `uselib` +#define __NR_swapon 87 ///< System-call number for `swapon` +#define __NR_reboot 88 ///< System-call number for `reboot` +#define __NR_readdir 89 ///< System-call number for `readdir` +#define __NR_mmap 90 ///< System-call number for `mmap` +#define __NR_munmap 91 ///< System-call number for `munmap` +#define __NR_truncate 92 ///< System-call number for `truncate` +#define __NR_ftruncate 93 ///< System-call number for `ftruncate` +#define __NR_fchmod 94 ///< System-call number for `fchmod` +#define __NR_fchown 95 ///< System-call number for `fchown` +#define __NR_getpriority 96 ///< System-call number for `getpriority` +#define __NR_setpriority 97 ///< System-call number for `setpriority` +#define __NR_statfs 99 ///< System-call number for `statfs` +#define __NR_fstatfs 100 ///< System-call number for `fstatfs` +#define __NR_ioperm 101 ///< System-call number for `ioperm` +#define __NR_socketcall 102 ///< System-call number for `socketcall` +#define __NR_syslog 103 ///< System-call number for `syslog` +#define __NR_setitimer 104 ///< System-call number for `setitimer` +#define __NR_getitimer 105 ///< System-call number for `getitimer` +#define __NR_newstat 106 ///< System-call number for `newstat` +#define __NR_newlstat 107 ///< System-call number for `newlstat` +#define __NR_newfstat 108 ///< System-call number for `newfstat` +#define __NR_uname 109 ///< System-call number for `uname` +#define __NR_iopl 110 ///< System-call number for `iopl` +#define __NR_vhangup 111 ///< System-call number for `vhangup` +#define __NR_idle 112 ///< System-call number for `idle` +#define __NR_vm86old 113 ///< System-call number for `vm86old` +#define __NR_wait4 114 ///< System-call number for `wait4` +#define __NR_swapoff 115 ///< System-call number for `swapoff` +#define __NR_sysinfo 116 ///< System-call number for `sysinfo` +#define __NR_ipc 117 ///< System-call number for `ipc` +#define __NR_fsync 118 ///< System-call number for `fsync` +#define __NR_sigreturn 119 ///< System-call number for `sigreturn` +#define __NR_clone 120 ///< System-call number for `clone` +#define __NR_setdomainname 121 ///< System-call number for `setdomainname` +#define __NR_newuname 122 ///< System-call number for `newuname` +#define __NR_modify_ldt 123 ///< System-call number for `modify_ldt` +#define __NR_adjtimex 124 ///< System-call number for `adjtimex` +#define __NR_mprotect 125 ///< System-call number for `mprotect` +#define __NR_sigprocmask 126 ///< System-call number for `sigprocmask` +#define __NR_create_module 127 ///< System-call number for `create_module` +#define __NR_init_module 128 ///< System-call number for `init_module` +#define __NR_delete_module 129 ///< System-call number for `delete_module` +#define __NR_get_kernel_syms 130 ///< System-call number for `get_kernel_syms` +#define __NR_quotactl 131 ///< System-call number for `quotactl` +#define __NR_getpgid 132 ///< System-call number for `getpgid` +#define __NR_fchdir 133 ///< System-call number for `fchdir` +#define __NR_bdflush 134 ///< System-call number for `bdflush` +#define __NR_sysfs 135 ///< System-call number for `sysfs` +#define __NR_personality 136 ///< System-call number for `personality` +#define __NR_setfsuid 138 ///< System-call number for `setfsuid` +#define __NR_setfsgid 139 ///< System-call number for `setfsgid` +#define __NR_llseek 140 ///< System-call number for `llseek` +#define __NR_getdents 141 ///< System-call number for `getdents` +#define __NR_select 142 ///< System-call number for `select` +#define __NR_flock 143 ///< System-call number for `flock` +#define __NR_msync 144 ///< System-call number for `msync` +#define __NR_readv 145 ///< System-call number for `readv` +#define __NR_writev 146 ///< System-call number for `writev` +#define __NR_getsid 147 ///< System-call number for `getsid` +#define __NR_fdatasync 148 ///< System-call number for `fdatasync` +#define __NR_sysctl 149 ///< System-call number for `sysctl` +#define __NR_mlock 150 ///< System-call number for `mlock` +#define __NR_munlock 151 ///< System-call number for `munlock` +#define __NR_mlockall 152 ///< System-call number for `mlockall` +#define __NR_munlockall 153 ///< System-call number for `munlockall` +#define __NR_sched_setparam 154 ///< System-call number for `sched_setparam` +#define __NR_sched_getparam 155 ///< System-call number for `sched_getparam` +#define __NR_sched_setscheduler 156 ///< System-call number for `sched_setscheduler` +#define __NR_sched_getscheduler 157 ///< System-call number for `sched_getscheduler` +#define __NR_sched_yield 158 ///< System-call number for `sched_yield` +#define __NR_sched_get_priority_max 159 ///< System-call number for `sched_get_priority_max` +#define __NR_sched_get_priority_min 160 ///< System-call number for `sched_get_priority_min` +#define __NR_sched_rr_get_interval 161 ///< System-call number for `sched_rr_get_interval` +#define __NR_nanosleep 162 ///< System-call number for `nanosleep` +#define __NR_mremap 163 ///< System-call number for `mremap` +#define __NR_setresuid 164 ///< System-call number for `setresuid` +#define __NR_getresuid 165 ///< System-call number for `getresuid` +#define __NR_vm86 166 ///< System-call number for `vm86` +#define __NR_query_module 167 ///< System-call number for `query_module` +#define __NR_poll 168 ///< System-call number for `poll` +#define __NR_nfsservctl 169 ///< System-call number for `nfsservctl` +#define __NR_setresgid 170 ///< System-call number for `setresgid` +#define __NR_getresgid 171 ///< System-call number for `getresgid` +#define __NR_prctl 172 ///< System-call number for `prctl` +#define __NR_rt_sigreturn 173 ///< System-call number for `rt_sigreturn` +#define __NR_rt_sigaction 174 ///< System-call number for `rt_sigaction` +#define __NR_rt_sigprocmask 175 ///< System-call number for `rt_sigprocmask` +#define __NR_rt_sigpending 176 ///< System-call number for `rt_sigpending` +#define __NR_rt_sigtimedwait 177 ///< System-call number for `rt_sigtimedwait` +#define __NR_rt_sigqueueinfo 178 ///< System-call number for `rt_sigqueueinfo` +#define __NR_rt_sigsuspend 179 ///< System-call number for `rt_sigsuspend` +#define __NR_pread 180 ///< System-call number for `pread` +#define __NR_pwrite 181 ///< System-call number for `pwrite` +#define __NR_chown 182 ///< System-call number for `chown` +#define __NR_getcwd 183 ///< System-call number for `getcwd` +#define __NR_capget 184 ///< System-call number for `capget` +#define __NR_capset 185 ///< System-call number for `capset` +#define __NR_sigaltstack 186 ///< System-call number for `sigaltstack` +#define __NR_sendfile 187 ///< System-call number for `sendfile` +#define __NR_waitperiod 188 ///< System-call number for `waitperiod` +#define __NR_msgctl 189 ///< System-call number for `msgctl` +#define __NR_msgget 190 ///< System-call number for `msgget` +#define __NR_msgrcv 191 ///< System-call number for `msgrcv` +#define __NR_msgsnd 192 ///< System-call number for `msgsnd` +#define __NR_semctl 193 ///< System-call number for `semctl` +#define __NR_semget 194 ///< System-call number for `semget` +#define __NR_semop 195 ///< System-call number for `semop` +#define __NR_shmat 196 ///< System-call number for `shmat` +#define __NR_shmctl 197 ///< System-call number for `shmctl` +#define __NR_shmdt 198 ///< System-call number for `shmdt` +#define __NR_shmget 199 ///< System-call number for `shmget` +#define SYSCALL_NUMBER 200 ///< The total number of system-calls. diff --git a/mentos/inc/ui/command/commands.h b/mentos/inc/ui/command/commands.h deleted file mode 100644 index cbca97a..0000000 --- a/mentos/inc/ui/command/commands.h +++ /dev/null @@ -1,93 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file commands.h -/// @brief Prototypes of functions for the Shell. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "shell.h" - -/// @brief Prints the logo. -void cmd_logo(int argc, char **argv); - -/// @brief Returns the list of commands. -void cmd_help(int argc, char **argv); - -/// @brief Echos the given message. -void cmd_echo(int argc, char **argv); - -/// @brief Power off the machine. -void cmd_poweroff(int argc, char **argv); - -/// @brief Call the uname function. -void cmd_uname(int argc, char **argv); - -/// @brief Prints the credits. -void cmd_credits(int argc, char **argv); - -/// @brief Sleeps the thread of the shell for a given period of time. -void cmd_sleep(int argc, char **argv); - -/// @brief Shows the output of the cpuid function. -void cmd_cpuid(int argc, char **argv); - -/// @brief Lists the directory. -void cmd_ls(int argc, char **argv); - -/// @brief Move to another directory. -void cmd_cd(int argc, char **argv); - -/// @brief Creates a new directory. -void cmd_mkdir(int argc, char **argv); - -/// @brief Removes a file. -void cmd_rm(int argc, char **argv); - -/// @brief Removes a directory. -void cmd_rmdir(int argc, char **argv); - -/// @brief Show the current user name. -void cmd_whoami(int argc, char **argv); - -/// @brief Allows to test some functionalities of the kernel. -void cmd_tester(int argc, char **argv); - -/// @brief Print current working directory. -void cmd_pwd(int argc, char **argv); - -/// @brief Read content of a file. -void cmd_more(int argc, char **argv); - -/// @brief Create a new file. -void cmd_touch(int argc, char **argv); - -/// @brief Create a new file. -void cmd_newfile(int argc, char **argv); - -/// @brief Show task list. -void cmd_ps(int argc, char **argv); - -/// @brief Show date and time. -void cmd_date(int argc, char **argv); - -/// @brief Clears the screen. -void cmd_clear(int argc, char **argv); - -/// @brief Shows the PID of the shell. -void cmd_showpid(int argc, char **argv); - -/// @brief Prints the history. -void cmd_show_history(int argc, char **argv); - -/// @brief Loads the drivers. -void cmd_drv_load(int argc, char **argv); - -/// @brief Show IPC state. -void cmd_ipcs(int argc, char **argv); - -/// @brief Remove IPC resorce from id. -void cmd_ipcrm(int argc, char **argv); - -/// @brief Change the nice value. -void cmd_nice(int argc, char **argv); diff --git a/mentos/inc/ui/init/init.h b/mentos/inc/ui/init/init.h deleted file mode 100644 index 70230d3..0000000 --- a/mentos/inc/ui/init/init.h +++ /dev/null @@ -1,10 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file init.h -/// @brief Scheduler structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// TODO: doxygen comment. -int main_init(); diff --git a/mentos/inc/ui/shell/shell.h b/mentos/inc/ui/shell/shell.h deleted file mode 100644 index e51ff1b..0000000 --- a/mentos/inc/ui/shell/shell.h +++ /dev/null @@ -1,78 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell.h -/// @brief Data structure used to implement the Shell. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "kernel.h" -#include "limits.h" - -/// Maximum length of credentials. -#define CREDENTIALS_LENGTH 50 - -/// Maximum length of commands. -#define CMD_LEN 256 - -/// Maximum length of descriptions. -#define DESC_LEN 256 - -/// Maximum number of saved commands. -#define MAX_NUM_COM 50 - -/// Maximum length of history. -#define HST_LEN 10 - -#define KEY_UP 72 - -#define KEY_DOWN 80 - -#define KEY_LEFT 75 - -#define KEY_RIGHT 77 - -/// Pointer to the function of a commmand. -typedef void (* CommandFunction)(int argc, char **argv); - -/// @brief Holds information about a command. -typedef struct command_t -{ - /// The name of the command. - char cmdname[CMD_LEN]; - - /// The function pointer to the command. - CommandFunction function; - - /// The description of the command. - char cmddesc[DESC_LEN]; -} command_t; - -/// @brief Holds information about the user. -typedef struct userenv_t -{ - /// The username. - char username[CREDENTIALS_LENGTH]; - - /// The current path. - char cur_path[PATH_MAX]; - - /// The user identifier. - unsigned int uid; - - /// The group identifier. - unsigned int gid; -} userenv_t; - -/// Contains the information about the current user. -extern userenv_t current_user; - -/// @brief The shell. -int shell(int argc, char **argv, char **envp); - -/// @brief Moves the cursor left. -void move_cursor_left(void); - -/// @brief Moves the cursor right. -void move_cursor_right(void); diff --git a/mentos/inc/ui/shell/shell_login.h b/mentos/inc/ui/shell/shell_login.h deleted file mode 100644 index 3d0b458..0000000 --- a/mentos/inc/ui/shell/shell_login.h +++ /dev/null @@ -1,14 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell_login.h -/// @brief Functions used to manage login. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "shell.h" -#include "stdbool.h" - -/// @brief Function used to perform login. -void shell_login(); - diff --git a/mentos/inc/version.h b/mentos/inc/version.h index b79875f..d8fc1d1 100644 --- a/mentos/inc/version.h +++ b/mentos/inc/version.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file version.h /// @brief Version information. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,7 +10,10 @@ #define OS_NAME "MentOS" /// The site of the operating system. -#define OS_SITEURL "..." +#define OS_SITEURL "https://mentos-team.github.io/MentOS" + +/// The email of the reference developer. +#define OS_REF_EMAIL "enry.frak@gmail.com" /// Major version of the operating system. #define OS_MAJOR_VERSION 0 @@ -28,7 +31,6 @@ #define OS_STR(x) OS_STR_HELPER(x) /// Complete version of the operating system. -#define OS_VERSION \ - OS_STR(OS_MAJOR_VERSION) "." \ - OS_STR(OS_MINOR_VERSION) "." \ - OS_STR(OS_MICRO_VERSION) +#define OS_VERSION \ + OS_STR(OS_MAJOR_VERSION) \ + "." OS_STR(OS_MINOR_VERSION) "." OS_STR(OS_MICRO_VERSION) diff --git a/mentos/kernel.lds b/mentos/kernel.lds index db0112d..3716d68 100644 --- a/mentos/kernel.lds +++ b/mentos/kernel.lds @@ -1,52 +1,53 @@ OUTPUT_FORMAT("elf") OUTPUT_ARCH(i386) -ENTRY(kernel_entry) +ENTRY(kmain) + +KERNEL_VIRTUAL_ADDRESS = 0xC0000000; + +MEMORY { + USER_SPACE : ORIGIN = 0x00000000, LENGTH = 3072M + KERNEL_LOWMEM : ORIGIN = KERNEL_VIRTUAL_ADDRESS, LENGTH = 896M + KERNEL_HIGHMEM : ORIGIN = 0xF8000000, LENGTH = 128M +} -KERNEL_PHYSICAL_ADDRESS = 0x00100000; -LOAD_MEMORY_ADDRESS = 0x00000000; SECTIONS { - . = KERNEL_PHYSICAL_ADDRESS; - - /* Put the .multiboot_header and .text section. */ - .text : AT(ADDR(.text)) + . = KERNEL_VIRTUAL_ADDRESS; + /* Put the .text section. */ + .text . : AT(ADDR(.text)) { - _multiboot_header_start = .; - *(.multiboot_header) - _multiboot_header_end = .; - _text_start = .; - *(.text) + EXCLUDE_FILE(*boot.*.o) *(.text) _text_end = .; - } + } > KERNEL_LOWMEM /* Read-only data. */ .rodata ALIGN(4K) : AT(ADDR(.rodata)) { _rodata_start = .; - *(.rodata) + EXCLUDE_FILE(*boot.*.o) *(.rodata) _rodata_end = .; - } + } > KERNEL_LOWMEM /* Read-write data (initialized) */ .data ALIGN(4K) : AT(ADDR(.data)) { _data_start = .; - *(.data) + EXCLUDE_FILE(*boot.*.o) *(.data) _data_end = .; - } + } > KERNEL_LOWMEM /* Read-write data (uninitialized) and stack */ .bss ALIGN(4K) : AT(ADDR(.bss)) { _bss_start = .; - *(.bss) + *(.bss*) _bss_end = .; - } + } > KERNEL_LOWMEM /* Put a symbol end here, it tells us where all the kernel code/data ends, it means everything after 'end' can be used for something else. */ - end = .; + _kernel_end = .; } diff --git a/mentos/libmentos_memory.a b/mentos/libmentos_memory.a new file mode 100644 index 0000000000000000000000000000000000000000..16ccc97b6849daeea044193264c10ba302af4f47 GIT binary patch literal 15858 zcmd6Odw5*cb?-hivo&LDB#ot!EaR~?wq;uey$k~UlCiO|u`$><1{`7->oJlBs*Yka&-(G94*IxT^_SxrX&WfIV-_Xjb9m(>m(?{B}|8FvR>1ACav2&_Y zYKl@``+xT9?M?Lz59j()BRzxZQ3Y}!pH4ff*x$c5HM)1SklvNbWHSZT>*w?rca4;D z2GfO9Ay?=bPK}QA^rg?QG{%2TU0+Y%P`ck~`r}I3s#`L!>-_uhL4$4*5{VvH|7PBT8KPfke1q1i~y0r2iSQ3yZ77M`0; zw)MflO%Xyc&n*Y+m=BfY+MiH2n=m=$(;N#P;udjsaaD9Vd-}=*bU2 zAqIiyqBG;qQ+fZxv^IYFS^QPMc=JQ1&ChaNJox-y{b2meWymR`MQcM8F)c!xZh=AM zlDUl{iRkHAG5iem&S{fhhWlpC?03h0vI4qE>H=Gd&{>PDw47^4Y1M&iIi6a^&v+-i zGta*o@J^$y6*aBn?CoC}KlSfHe(dh%ma5&~H0ED_9KH@x3f{+FW8Wu1CC5%gsI~@b zzL1mTJ^1gI#~>+6-a+v0J5ikf%u*X)^nQzxB&67LFNB(}1?#n6qPUEGfvd*hx)ZKJCwyTh$p4jXaU=N> zs^S08@+U(5-Sa~oDhqX6b7JfpW>$>Hn_qhzO@MFP0*@uzf`rNbme6E>-S7vgmgVTJ zngcPq?`U2Eo2%?VWxhahEHFFFQYkwhfHG)~ zgSB>&w$LZq$G%%Tjzs-68ic+x36w+`{&R+3b6*rpbl(?TPF{c4Dz{$9-~WaZ_-kRj zMdC2y@28fZ1fRWkUYvK>`h-u<^wx{u-+Q9w-iNHugpA84-a%3~Apzwhe(XeazdCU3 zd;51xu6h|&6}NE(C-%!$0@C#*wBtETg&w)m^^z=C-$m z%@vdDO^Zzrr#T07S#1NQ$%0e5Nt?TU+@?HQ-rfij8bv~*RrkDd5V|p=3!kC=`NYy=m0pn(eVDFEmAWLTJArRlc?`iA3T={Id zax0V)CYCKIxxv{vj())k#Npcrh0X&9pM(%fjlQY)&|9X5I;|($NuA>d=@!A7VUM(T z-TgSz(ke-qaJZRH=2fh5ESI8t>t8Pzov&FGFJ-CAm!&LJjZ@PfV{fn9J>N&Z#=C_{ zmEZq&;NO=s)+fgi7w@8DrxKD%vfWM3RDT%NP)nTb7|QKRccioVOkb{JYkDL%nknS+ zdpnBR%1-i4>R>eAhvKdd^UUHN5R^)#@Q_nTj}}q`!##s7eC}yYZr!|j zTWZVd_17$#TyL~nlk2u#bBz^9E?AJ9Gs%bu`K&0lHdpK&PAB`*=J~3pkjZ6}nQYRC zCT%!#&{>KBB)hwl!#xPe5O6T}`t&X|E;*X{NIIDrO=fe2WY2JBFq`gA7IMk!S8q>k z-n#ahtwvzZT-urRBv=0IWggxV*qfW#phDhG6r!`BwkbemrS?=>421MsCMx4@yDy4&aw)Bg+$B{Ar%ST0y0Qx zZ(5LLeINcz3F%C>pn@mC*L4lF7zpU?paOyLSvU!J72jpuSE=1kwfVJ_n))OB347c= zS4^#b15nuuIaT?Dh6fd7bwyRvUjm|$@LZjMt*ZH`3pdx%an)y8USP_q;tk{>7QQHU zBc#IZteKry2PqXk#y`pG&jCW=@4!QNVfY;o5f!9TxFtx-6)%Am4Y$Vbqk)@|-f(*j z-Kg*obvmbf#_CWmyf}Cp#583qJ_W|<@a3^6jeVEeS59MDh2JJ;X}lH?3ZG)la%1wz z@NcuGyN+G)bol$MxylgFgdd@^)v>t{crN@?T3KUiPK0Y2(=~=Th0gdo>Z%z4MQfPQ zDsb8SAR`K|3w{RWSZL~Unh#MR*jRBBgewmqJ*x8C_~WT*zo5oFUd8rGrIKVFJ^-28 zQz(MHh(zF!s{JD?wvyfghd3HqP^!BWK?V1EQLkp5A-CI*NW2RtJsM zyGmAjy~Sd+p^M5HFUHEip`ixTo;y5~=)j@=`cFZ95xs0@LkE0bBBA@hk3R~<;C8R7 z3W6iX&o4@TwtJVj=utzlr5r8DQ+*ubeJD3(RPa`>+Omi1XM@cyv2lIKiEW{Vk>@oZaZgl<%uzE3iCwP^$ z8iCj?#%dKh#J1roZ&{hu)e}~yLwr@q>eYU$v)CWYtkt)$b*VL0mzJz9^OlQMbdE_{u`7MryvmgnX{kZ;t>3oW-bmG8i@dP-kjZK$VlS*B!51~j_zpU ze(L_Kkg-G_9)oWQ8MmERR~o(zXW7DfNAI+~(C9U8g_Xde9X9Y8UOmGK9NONno1r8; zvnmD-ZJTuuoS#FT4s!cB`z(rgK=mk)<0s(N$*ik?11ws#Aq3s{!H}xIA*|GmtU882 z!KgQ#BOH^hemg3dcN<2PH^YXp1uV=iIIQMsM*n6Zo2K+DD9^l^Z9ZPH0po9@N$Nwe zW0QJ(o^)Em4CCWVWVL7-V_^!CNmEYie!KNl~8<< z%`kC4?BbRTQ_p0$T4}~O@0&E7Zs5%~jN%fYcX&UeH}Rzx+q5bheR{1#ubPj;cW~z{ zE);K~7&nTisAw|2a}{fF->`~1Zz(B$oQgLjNPh>#xkfRHX>Rj%gIiz{zheY%lzyF` z&h|sE&yo=}Z#D)+aP38;{}7UkjGb#rcGh;g@u7|2wo(9p%qA~4iU*3;O9T8SgGBb>aqMJtfOMB}<-~E78=zeiPyhC^j2! zd#G56qy+XSZA>b`rUv$}Vigvqg}LE=6*yovJXqQ>Mb|;IB}t)uaUMbOVcR?W<&vd+ zbIUC4yIL%%mRX29usi-96s*!~C8gcv?sq%)YF-;$cn@8~TjEJ5+7)|FCE{L(7`%rr zvo}lCw1$F@$US|JT99R22jX0+7O;5uW+=_irtbpxQc+5L9cH})S6ecq7W|a-1M%HZ zSZI{?my{l8D^q%)+bOlMLEfJDMgTXk-r>J2Debw~DSg}sEfb}L*-h`c&XN(;d@UX4 zP4{r(*&Eouv6&{sd>6}@L%heJe$gM$Tpy>JOw{?qpk0Wlo94~0uB!}I&#a#2)mFr? z7Yj72%F2~sVe~YpVD+-f<&{e-H`HN6dbuL#1qLeWykK=@rLFeB4_Eo5mmrQv4J=V( zikQS)pBjSJ%1ZJlG%H)6nSe%gy63l8<+q6S$n!ST(NoPFM9=~G7(FO8t=+q7hKW=! ztz0;REm_t0nX6||wpQDyP_v;ffT>$oF1(?xOg!F>_z>CohUzPktXj;(hOS8#r~7p# z*=vMw#M-m&XI$9*XKl0>mDk$Pq;6)Z4*fByl?iF3=_$5y7K7YS*ENI0Y#Tb$(Iml_ z1e2BCP3ZMG<;EwqV(wJiWN1yp{Fx^WHgQZ6ZN5;Qv;a?HCK)B?3;e=m{jgB-RuXMF zUsS9vx?l}PJeNvLeq30(z9y-^OC|UHo3b&s`ruIjEYsP$7w`w*Vm)M%l9b_{842=) z`u_AlNfuA&rE+gBH+;U<5VC~jz*E{}IrtCC(-jT6VL;avbj3>DP|$S)dU~gh9nn<- z+ADVHSe>3;(BA)Q*0Ba%bM*5%c2s*$U8vrqL zr&u4oS9{lm0>N81YF($j<6Ramr=T9w(a3bx2ZIMb27zwa=)PSrrK5)4sxPn8tB&ZI z1%1gz?R6|P{J`A0t-5-Sp0iQU-l$h@)LlokcPJQZ*R$$$!&w-eN4eI>3f+Y>5pSbz zB^2-{STTUVA;b=-sQBRZI#$&6W4iG{tv{h-9lE)ot556ML%MpSj($RKf>>a^)&Q7^ z2B#Es)g#2Do?6hl_~Xb*R#ij_dP+rLe!Z?O{JyS?bm*#Eb;AdB_4PV&ot_f;5zRmd z5R4RzU{z!Y89%D0Jf$mxIfObb82?+XpNe!NF{{xAg!Xwo`(<4ld0kKWypBMm&4#gv z0CLKhuKZISyH(GN{8%R*wU41Wn^=;Hu!RTq7 z2-YWcL*z%sXJn;r3M`82h0yULk3Os~*{!eMs1pNvM!TMQ?4_4p(sjp}qM2+$hn`_t zQq)t3M-8#kWH}i8NaRVEuj#?5N2rvvO6-ha!RjK%qz0{pr}Rc+qt9u^eoJq7StpL@ z#>jD<9MfF|U4L3vXH0JEJCP7H$EJ{DvNl+ZoYj?QwGLLZgS`rj2&6gwAzghJBUnK5 z9eNp|pqmJ%^=d-ex%BpCbo@F!6Pn%gbZt>rk5Tgyt7&479Msp4Jff=!EA$G&24^9< z+jLt=)g-AyFaFO}s*Aqdnw&<8dK&d2U(+pCe-7c#oGC%I0h3K0kOz+I{K7pgNFOabCkH)N)$0HR(?oFfnU2G1D~@s zpt08W<%&2PG^ee7!|9&9IkQzf6-^c1|6+LI6X7Bjw~;~~21{#Xs;9p{Rp`kKtC4(q zw>hF6O^p`vnSOlJ(u+@FMhiXJKE{yC_owqd#s2iL%BJ_YFJ&_Qdz6z_rQ_k!hb%q$ zbPtShMForu!U>wDhI83LoE5X-CL}~tew@qiY|1{Dp&?%sBRPDSgHL&Iz^!)o3>VX| zWaLu_jZZ@6Yae{7lgg(*T*O(pDrQIR=`!8+;)9?&Z78V}eNjA>@5v6L`RTz-)<#&S zLlJnPw7Hq2dJ4!>e|nFa_@GE)V4GC7XII*Eq#7N{hU7lqCzjKcS+5gm@akKxi=%nbL-YXsHXGb$f4o!=7ETei9Km6Z7=%9pf( zd~R3Dm`K?yW=3s(Oc$g$K1v7w>@t(-g7suN)%Fy|TOKXKp+eZA* z72KeoAdg!~^PCFwKC)GLcSpRL(NO3?>|A5uaWl`0kG;Ie>%@;VAA@<3w-!J5h)o`C zZpY6(tE)BDvA*h%;g5jt9{kDg75;zB{c{>#J7<0|A;b{lX94 z+=(me){X^&D+9)jiQ#3`yJK$DZwWS8UFPcNsB>rRsEgAdbIlWPcSi4G<8hLE8$j8> z=IRh_ECJuWdmz6E8{}^R-@O3=zcY}r@t6`0m{p`RwP(7n)tVLh5H6R;8=_i&V}H&W?#Ih*?x4B*%XMk9V_x zEORc9ZUee`K$>-K4v^;goE!V3KM2~5dD4TR-B>4`1MS8+>Aj%c7$^M+&~9v#J_y>4 zY0^hQyRl6AIA}M9NuL7k#xLoYLA!BF`gPE5tdjnB&~BWP_FqA)F4bTq@nfFwglb~# z1Z|GYK=bk5jScEw2HK4Y(pQ6aV}bOIpxqca-ye>Jd{+=Jiv3rRA7=Zmpu(Y*?+Tg~ zae@(4IDGS6K{F#J7(q2{f+aW^+fb6&eSNt$o8-CQBfS6*(8khU9+(l;BtnVGYT6t~wC zl-y!Ah@mHLjJ|z{f4^7SIhQskK0&*i zwv0&|=E*xnw+(Awj9aeVk+pZayRX~PyA8fO&!&g5)5~W1F0>8wZ$bZi{6D}yVk~(LO1&efv0Rd#3JkO;^=YAhU+@cpUy=G# zSe_~WA~0~VQim{oNgoq@Qt*pF+WWtl&H)_!-!K)p2;XyHK6$h4zQ&u2^Jvn*dche) z#5RYBKQLkHI|RD~uNJ&PaJwKjSw=oDc)#Eg!6ya3EXenf^v^Ywct-FCg0BhwlVAwb zk8&}=S%Mb}UMaX%aI4_$f_Dn;5xifJYY6Q=Ciq3cuM2)tkZT9!ekk}ag6|0~3fTH9 z1vdz87Q9*Tb|QYeg#M`DLxM+%_#GGegy3o6za;dJ1%DxY-hLTht>A1T>e__v7Q9~Y zHmM&Fns3Uff4}g}cm!?6Baq{e_W0V5_%|Z|L&3KNxp`)N4H0@xf*rzNC%9Soy@FZc ze^lrLf}a!q<3c|pXyya_{FTuEAowN`{9g)PjZHk`xQGb;BB7TFUM>6^h3*mh4xxF_ zPQ5Xq4+}mj{4WXpyx163w8;v5!@=+Lrf|)AaqgiUcpZb9wj2a<3fL3@Y{ml z6FevQj-ZG2ih319m3Em_4+k%e}VdoEp{;J>$g8xtI|6b^y2>wzq zi1nNLwM4`=AM{{ zA&M_EmEwJhc&*?L!Gho?1s@Z9M(|sLX9a&L7>9k@YZY82xLvRy$TI-S9Tj{|@Oy%9 z3&tQrxn{u?g4+a#1@9F+EcmqGw**!D=-yp8ngJH_7KdCZpB`>6@U*ubZ_|2Id++F| zYR~5^!OH%0Z*dUkpaVIGm~u~VFOQVlCu)1lRuGC-z)=$dINV9QT6k9NJVnAOoodHt z!RdA!oZyTKCrv`*tg0PnP9xaS?X`Z=LpY-0dFy}mYJworXvP3_*w_A-rL5z{k5({! z)GGXV{WUf*`+#x$D1%#rdHR_zG;UqydJ<#Ke>r=XfQjXy^o0BCJ5zTB!2F4b$lN7w__U|oW=xTJ2($qJ2=*z4vMN8o4LPtLHO!!^_3}i z4v9*#AeXaoGiblx#Fciw(J%SVZw6(jgQBWT*bk3b=H=>s%U33U9_09)k-MDSU10eA zw!$y-PQOf#^ZNkG)M37P9h;-n7be+b{qNxCm%DMH{j7yLyjaKI?+F6T^-;+AFUs(G z0zcfgO8vVJa%f8Fa{c=aaH!+D;4tLA=Lqnf9KR0qx3>%J{b%slUY-HC_WEsst@d23 qR1$-hdv}IPxfp)5<;F6K>R}wPEcf|!?cmpages[j].frame = pfn_phys_start + pg_offset++; + table->pages[j].rw = 1; + table->pages[j].present = 1; + table->pages[j].global = 0; + table->pages[j].user = 0; + } + + boot_pgdir.entries[i].rw = 1; + boot_pgdir.entries[i].present = 1; + boot_pgdir.entries[i].available = 1; + boot_pgdir.entries[i].frame = ((uint32_t)table) >> 12u; + } +} + +/* + * Setup paging mapping all the low memory to two places: + * one is the physical address of the memory itself + * the other is in the virtual kernel address space + * */ +static inline void __setup_boot_paging() +{ + uint32_t kernel_base_phy_page = boot_info.kernel_phy_start >> 12U; + uint32_t kernel_base_virt_page = boot_info.kernel_start >> 12U; + + uint32_t lowmem_last_phy_page = ((uint32_t)(boot_info.lowmem_phy_end - 1)) >> 12U; + + uint32_t num_pages = lowmem_last_phy_page - kernel_base_phy_page + 1; + + // Map lowmem physical pages also to their physical address (to keep bootloader working) + __setup_pages(0, 0, lowmem_last_phy_page); + + // Setup kernel virtual address space + lowmem + __setup_pages(kernel_base_virt_page, kernel_base_phy_page, num_pages); +} + +/// @brief Extract the starting and ending address of the kernel. +/// @param elf_hdr The elf header of the kernel. +/// @param virt_low Output variable where we store the lowest address of the kernel. +/// @param virt_high Output variable where we store the highest address of the kernel. +static void __get_kernel_low_high(elf_header_t *elf_hdr, + uint32_t *virt_low, + uint32_t *virt_high) +{ + // Prepare a pointer to a program header. + elf_program_header_t *program_header; + // Compute the offset for accessing the program headers. + uint32_t offset = (uint32_t)elf_hdr + (uint32_t)elf_hdr->phoff; + // In this two variables we will store the start and end addresses of the segment. + uint32_t segment_start, segment_end; + // Iterate for each program header. + for (int i = 0; i < elf_hdr->phnum; i++) { + program_header = (elf_program_header_t *)(offset + elf_hdr->phentsize * i); + if (program_header->type == PT_LOAD) { + // Take the start and end addresses of the segment from the program header. + segment_start = program_header->vaddr; + segment_end = segment_start + program_header->memsz; + // Take the lowest and highest virtual address. + *virt_low = min(*virt_low, segment_start); + *virt_high = max(*virt_high, segment_end); + } + } +} + +/// @brief Returns the first address after the modules. +/// @param header The multiboot info structure from which we extract the info. +/// @return The address after the modules. +static inline uint32_t __get_address_after_modules(multiboot_info_t *header) +{ + // We set by default the address to the ending physical address + // of the bootloader. + uint32_t addr = boot_info.bootloader_phy_end; + // Get the pointer to the mods. + multiboot_module_t *mod = (multiboot_module_t *)header->mods_addr; + for (int i = 0; (i < header->mods_count) && (i < MAX_MODULES); ++i, ++mod) { + addr = max(max(addr, mod->mod_start), mod->mod_end); + } + return addr; +} + +/// @brief Relocate the kernel image. +/// @param elf_hdr The elf header of the kernel. +static inline void __relocate_kernel_image(elf_header_t *elf_hdr) +{ + // Support variables. + elf_program_header_t *program_header; + char *kernel_start, *virtual_address, *physical_address; + uint32_t offset, valid_size; + + // Get the elf file starting address. + kernel_start = (char *)elf_hdr; + // Compute the offset for accessing the program headers. + offset = (uint32_t)kernel_start + (uint32_t)elf_hdr->phoff; + // Iterate over the program headers. + for (int i = 0; i < elf_hdr->phnum; i++) { + // Get the program header. + program_header = (elf_program_header_t *)(offset + elf_hdr->phentsize * i); + // Get the virtual address of the program header. + virtual_address = (char *)program_header->vaddr; + // Get the physical address of the program header. + physical_address = (char *)(kernel_start + program_header->offset); + // Move only the loadable segments. + if (program_header->type == PT_LOAD) { + // Get the valid size of the segment by taking the minimum between + // the size in bytes of the segment in the file image, in memory. + valid_size = min(program_header->filesz, program_header->memsz); + // Copy the physical data of the image to the corresponding virtual address. + for (int j = 0; j < valid_size; j++) + virtual_address[j] = physical_address[j]; + // Set to 0 parts not present in memory! + for (int j = valid_size; j < program_header->memsz; j++) + virtual_address[j] = 0; + } + } +} + +/// @brief Entry point of the bootloader. +/// @param magic The magic number coming from the multiboot assembly code. +/// @param header Multiboot header provided by the bootloader. +/// @param esp The initial stack pointer. +void boot_main(uint32_t magic, multiboot_info_t *header, uint32_t esp) +{ + __debug_puts("\nbootloader: Start...\n"); + elf_header_t *elf_hdr = (elf_header_t *)LDVAR(kernel_bin); + + // Get the physical addresses of where the kernel starts and ends. + uint32_t boot_start = (uint32_t)_bootloader_start; + uint32_t boot_end = (uint32_t)_bootloader_end; + + // Extract the lowest and highest address of the kernel. + uint32_t kernel_virt_low = 0xFFFFFFFF; + uint32_t kernel_virt_high = 0; + __get_kernel_low_high(elf_hdr, &kernel_virt_low, &kernel_virt_high); + + // Initialize the boot_info_t structure. + __debug_puts("bootloader: Initializing the boot_info structure...\n"); + boot_info.magic = magic; + boot_info.bootloader_phy_start = boot_start; + boot_info.bootloader_phy_end = boot_end; + boot_info.kernel_start = kernel_virt_low; + boot_info.kernel_end = kernel_virt_high; + boot_info.kernel_size = kernel_virt_high - kernel_virt_low; + boot_info.multiboot_header = header; + + // Get the address after the modules. + boot_info.module_end = __get_address_after_modules(header); + + // Get the starting address of the physical pages at the end of the modules. + uint32_t kernel_phy_page_start = __align_rup(boot_info.module_end, PAGE_SIZE); + // Get the starting address of the virtual pages. + uint32_t kernel_virt_page_start = __align_rdown(kernel_virt_low, PAGE_SIZE); + + // Compute the absolute offset of the first virtual page, by subtracting + // the starting address of the virtual pages and the lowest virtual address + // of the kernel. + uint32_t kernel_page_offset = kernel_virt_page_start - kernel_virt_low; + + // If we add the offset we computed earlier to the physical address where + // the modules ends, we obtain the starting address of the physical memory. + boot_info.kernel_phy_start = kernel_phy_page_start + kernel_page_offset; + // The ending address of the physical memory is just the start plus the + // size of the kernel (virt_high - virt_low). + boot_info.kernel_phy_end = boot_info.kernel_phy_start + boot_info.kernel_size; + + boot_info.lowmem_phy_start = __align_rup(boot_info.kernel_phy_end, PAGE_SIZE); + boot_info.lowmem_phy_end = 896 * 1024 * 1024; // 896 MB of low memory max + + uint32_t lowmem_size = boot_info.lowmem_phy_end - boot_info.lowmem_phy_start; + + boot_info.lowmem_start = __align_rup(boot_info.kernel_end, PAGE_SIZE); + boot_info.lowmem_end = boot_info.lowmem_start + lowmem_size; + + boot_info.highmem_phy_start = boot_info.lowmem_phy_end; + boot_info.highmem_phy_end = header->mem_upper * 1024; + boot_info.stack_end = boot_info.lowmem_end; + + // Setup the page directory and page tables for the boot. + __debug_puts("bootloader: Setting up paging...\n"); + __setup_boot_paging(); + + // Switch to the newly created page directory. + __debug_puts("bootloader: Switching page directory...\n"); + paging_switch_directory(&boot_pgdir); + + // Enable paging. + __debug_puts("bootloader: Enabling paging...\n"); + paging_enable(); + + // Reserve space for the kernel stack at the end of lowmem. + boot_info.stack_base = boot_info.lowmem_end; + boot_info.lowmem_phy_end = boot_info.lowmem_phy_end - KERNEL_STACK_SIZE; + boot_info.lowmem_end = boot_info.lowmem_end - KERNEL_STACK_SIZE; + + __debug_puts("bootloader: Relocating kernel image...\n"); + __relocate_kernel_image(elf_hdr); + +#if 0 + for (int i = 0; i < elf_hdr->shnum; i++) { + struct elf_section_header *section_header = + (elf_section_header_t *)(LDVAR(kernel_bin) + elf_hdr->shoff + elf_hdr->shentsize * i); + for (int j = 0; j < section_header->; j++) { + ((char *)section_header->vaddr)[j] = (LDVAR(kernel_bin) + section_header->offset)[j]; + } + } +#endif + + __debug_puts("bootloader: Calling `boot_kernel`...\n\n"); + boot_kernel(boot_info.stack_base, elf_hdr->entry, &boot_info); +} + +/// @} \ No newline at end of file diff --git a/mentos/src/descriptor_tables/exception.asm b/mentos/src/descriptor_tables/exception.S similarity index 86% rename from mentos/src/descriptor_tables/exception.asm rename to mentos/src/descriptor_tables/exception.S index ba386cc..9ac39eb 100644 --- a/mentos/src/descriptor_tables/exception.asm +++ b/mentos/src/descriptor_tables/exception.S @@ -1,12 +1,14 @@ ; MentOS, The Mentoring Operating system project ; @file exception.asm ; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. +extern isr_handler + ; Macro used to define a ISR which does not push an error code. %macro ISR_NOERR 1 - [GLOBAL INT_%1] + global INT_%1 INT_%1: cli ; A normal ISR stub that pops a dummy error code to keep a @@ -18,13 +20,18 @@ ; Macro used to define a ISR which pushes an error code. %macro ISR_ERR 1 - [GLOBAL INT_%1] + global INT_%1 INT_%1: cli push %1 jmp isr_common %endmacro +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + ; Standard X86 interrupt service routines ISR_NOERR 0 ISR_NOERR 1 @@ -46,7 +53,6 @@ ISR_NOERR 16 ISR_NOERR 17 ISR_NOERR 18 ISR_NOERR 19 - ISR_NOERR 20 ISR_NOERR 21 ISR_NOERR 22 @@ -62,8 +68,6 @@ ISR_NOERR 31 ISR_NOERR 80 -[EXTERN isr_handler] - isr_common: ; Save all registers (eax, ecx, edx, ebx, esp, ebp, esi, edi) pusha @@ -91,7 +95,7 @@ isr_common: cld ; Call the interrupt handler. - push esp; + push esp call isr_handler add esp, 0x4 @@ -107,5 +111,6 @@ isr_common: ; Cleanup error code and IRQ # add esp, 0x8 + iret ; pops 5 things at once: ; CS, EIP, EFLAGS, SS, and ESP diff --git a/mentos/src/descriptor_tables/exception.c b/mentos/src/descriptor_tables/exception.c index e346ebc..00e1c13 100644 --- a/mentos/src/descriptor_tables/exception.c +++ b/mentos/src/descriptor_tables/exception.c @@ -1,93 +1,101 @@ /// MentOS, The Mentoring Operating system project -/// @file isr.c +/// @file exception.c /// @brief Functions which manage the Interrupt Service Routines (ISRs). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "panic.h" #include "isr.h" #include "idt.h" #include "stdio.h" #include "debug.h" // Default error messages for exceptions. -static const char *exception_messages[32] = { "Division by zero", - "Debug", - "Non-maskable interrupt", - "Breakpoint", - "Detected overflow", - "Out-of-bounds", - "Invalid opcode", - "No coprocessor", - "Double fault", - "Coprocessor segment overrun", - "Bad TSS", - "Segment not present", - "Stack fault", - "General protection fault", - "Page fault", - "Unknown interrupt", - "Coprocessor fault", - "Alignment check", - "Machine check", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Security exception", - "Triple fault" }; +static const char *exception_messages[32] = { + "Division by zero", + "Debug", + "Non-maskable interrupt", + "Breakpoint", + "Detected overflow", + "Out-of-bounds", + "Invalid opcode", + "No coprocessor", + "Double fault", + "Coprocessor segment overrun", + "Bad TSS", + "Segment not present", + "Stack fault", + "General protection fault", + "Page fault", + "Unknown interrupt", + "Coprocessor fault", + "Alignment check", + "Machine check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Security exception", + "Triple fault" +}; -// Array of interrupt service routines for execptions and interrupts. +/// @brief Array of interrupt service routines for execptions and interrupts. static interrupt_handler_t isr_routines[IDT_SIZE]; +/// @brief Descriptions of routines. static char *isr_routines_description[IDT_SIZE]; -// Default handler for exceptions. -void default_isr_handler(pt_regs *f) +/// @brief Default handler for exceptions. +/// @param f CPU registers when calling this function. +static inline void default_isr_handler(pt_regs *f) { - uint32_t irq_line = f->int_no; + uint32_t irq_line = f->int_no; - printf("Kernel PANIC!\n no handler for execption [%d]\n", irq_line); - printf("Description: %s\n", (irq_line < 32) ? - exception_messages[irq_line] : - "no description"); - // Stop kernel execution. - while (1) - ; + dbg_print_regs(f); - // TODO: call the kernel panic method! + printf("Kernel PANIC!\n no handler for execption [%d]\n", irq_line); + printf("Description: %s\n", (irq_line < 32) ? exception_messages[irq_line] : "no description"); + // Stop kernel execution. + + kernel_panic("Kernel PANIC!\n no handler for execption"); + + while (1) {} +} + +/// @brief Interrupt Service Routines handler called from the assembly. +/// @param f CPU registers when calling this function. +void isr_handler(pt_regs *f) +{ + uint32_t isr_number = f->int_no; + if (isr_number != 80) { + // pr_default("calling ISR %d\n", isr_number); + } + // pr_default("calling ISR %d\n", isr_number); + isr_routines[isr_number](f); + // pr_default("end calling ISR %d\n", isr_number); } void isrs_init() { - // Setting the default_isr_handler as default handler. - for (uint32_t i = 0; i < IDT_SIZE; ++i) { - isr_routines[i] = default_isr_handler; - } + // Setting the default_isr_handler as default handler. + for (uint32_t i = 0; i < IDT_SIZE; ++i) { + isr_routines[i] = default_isr_handler; + } } -int isr_install_handler(uint32_t i, interrupt_handler_t handler, - char *description) +int isr_install_handler(uint32_t i, interrupt_handler_t handler, char *description) { - // Sanity check. - if (!(i >= 0 && i <= 31) && i != 80) { - return -1; - } - isr_routines[i] = handler; - isr_routines_description[i] = description; - return 0; -} - -void isr_handler(pt_regs *f) -{ - uint32_t isr_number = f->int_no; - if (isr_number != 80) { - dbg_print("calling ISR %d\n", isr_number); - } - isr_routines[isr_number](f); + // Sanity check. + if (i > 31 && i != 80) { + return -1; + } + isr_routines[i] = handler; + isr_routines_description[i] = description; + return 0; } diff --git a/mentos/src/descriptor_tables/gdt.S b/mentos/src/descriptor_tables/gdt.S new file mode 100644 index 0000000..ea5c6de --- /dev/null +++ b/mentos/src/descriptor_tables/gdt.S @@ -0,0 +1,30 @@ +; MentOS, The Mentoring Operating system project +; @file gdt.asm +; @brief +; @copyright (c) 2014-2021 This file is distributed under the MIT License. +; See LICENSE.md for details. + +global gdt_flush ; Allows the C code to call gdt_flush(). + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + +gdt_flush: + mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. + lgdt [eax] ; Load the new GDT pointer + + ; The data segments selectors (registers), can be easily modified using + ; simple mov instruction, but the cs can't be used with mov, so you use: + jmp 0x08:flush + ; to load the segment configurations into the the code segment selector. + +flush: + mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment + mov ds, ax ; Load all data segment selectors + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/gdt.asm b/mentos/src/descriptor_tables/gdt.asm deleted file mode 100644 index df68538..0000000 --- a/mentos/src/descriptor_tables/gdt.asm +++ /dev/null @@ -1,23 +0,0 @@ -; MentOS, The Mentoring Operating system project -; @file gdt.asm -; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. -; See LICENSE.md for details. - -[GLOBAL gdt_flush] ; Allows the C code to call gdt_flush(). - -gdt_flush: - mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. - lgdt [eax] ; Load the new GDT pointer - - mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment - mov ds, ax ; Load all data segment selectors - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - - jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump! - -.flush: - ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/gdt.c b/mentos/src/descriptor_tables/gdt.c index 16edba2..8e359e2 100644 --- a/mentos/src/descriptor_tables/gdt.c +++ b/mentos/src/descriptor_tables/gdt.c @@ -1,9 +1,10 @@ /// MentOS, The Mentoring Operating system project /// @file gdt.c /// @brief Functions which manage the Global Descriptor Table (GDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "debug.h" #include "gdt.h" #include "tss.h" @@ -23,79 +24,166 @@ gdt_pointer_t gdt_pointer; void init_gdt() { - // Prepare GDT vector. - for (uint32_t it = 0; it < GDT_SIZE; ++it) { - gdt[it].limit_low = 0; - gdt[it].base_low = 0; - gdt[it].base_middle = 0; - gdt[it].access = 0; - gdt[it].granularity = 0; - gdt[it].base_high = 0; - } + // BEWARE: Look below for a deeper explanation. - // Setup the GDT pointer and limit. - // We have six entries in the GDT: - // - Two for kernel mode. - // - Two for user mode. - // - The NULL descriptor. - // - And one for the TSS (task state segment). - // The limit is the last valid byte from the start of the GDT. - // i.e. the size of the GDT - 1. - gdt_pointer.limit = sizeof(gdt_descriptor_t) * 6 - 1; - gdt_pointer.base = (uint32_t)&gdt; + // Prepare GDT vector. + for (uint32_t it = 0; it < GDT_SIZE; ++it) { + gdt[it].limit_low = 0; + gdt[it].base_low = 0; + gdt[it].base_middle = 0; + gdt[it].access = 0; + gdt[it].granularity = 0; + gdt[it].base_high = 0; + } - // ------------------------------------------------------------------------ - // NULL - // ------------------------------------------------------------------------ - gdt_set_gate(0, 0, 0, 0, 0); + // Setup the GDT pointer and limit. + // We have six entries in the GDT: + // - Two for kernel mode. + // - Two for user mode. + // - The NULL descriptor. + // - And one for the TSS (task state segment). + // The limit is the last valid byte from the start of the GDT. + // i.e. the size of the GDT - 1. + gdt_pointer.limit = sizeof(gdt_descriptor_t) * 6 - 1; + gdt_pointer.base = (uint32_t)&gdt; - // ------------------------------------------------------------------------ - // CODE - // ------------------------------------------------------------------------ - // The base address is 0, the limit is 4GBytes, it uses 4KByte - // granularity, uses 32-bit opcodes, and is a Code Segment descriptor. - gdt_set_gate(1, 0, 0xFFFFFFFF, PRESENT | KERNEL | CODE | 0x0A, - GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // NULL + // ------------------------------------------------------------------------ + gdt_set_gate(0, 0, 0, 0, 0); - // ------------------------------------------------------------------------ - // DATA - // ------------------------------------------------------------------------ - // It's EXACTLY the same as our code segment, but the descriptor type in - // this entry's access byte says it's a Data Segment. - gdt_set_gate(2, 0, 0xFFFFFFFF, PRESENT | KERNEL | DATA | 0x02, - GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // CODE + // ------------------------------------------------------------------------ + // The base address is 0, the limit is 4GBytes, it uses 4KByte + // granularity, uses 32-bit opcodes, and is a Code Segment descriptor. + gdt_set_gate( + 1, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_KERNEL | GDT_CODE | GDT_RW, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // ------------------------------------------------------------------------ - // USER MODE CODE - // ------------------------------------------------------------------------ - gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // DATA + // ------------------------------------------------------------------------ + // It's EXACTLY the same as our code segment, but the descriptor type in + // this entry's access byte says it's a Data Segment. + gdt_set_gate( + 2, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_KERNEL | GDT_DATA, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // ------------------------------------------------------------------------ - // USER MODE DATA - // ------------------------------------------------------------------------ - gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // USER MODE CODE + // ------------------------------------------------------------------------ + gdt_set_gate( + 3, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_USER | GDT_CODE | GDT_RW, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // Initialize the TSS - tss_init(5, 0x10, 0x0); + // ------------------------------------------------------------------------ + // USER MODE DATA + // ------------------------------------------------------------------------ + gdt_set_gate( + 4, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_USER | GDT_DATA, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // Inform the CPU about the changes on the GDT. - gdt_flush((uint32_t)&gdt_pointer); - tss_flush(); + // Initialize the TSS + tss_init(5, 0x10); + + // Inform the CPU about the changes on the GDT. + gdt_flush((uint32_t)&gdt_pointer); + + // Inform the CPU about the changes on the TSS. + tss_flush(); } -void gdt_set_gate(uint8_t index, uint32_t base, uint32_t limit, uint8_t _access, - uint8_t _granul) +void gdt_set_gate(uint8_t index, uint32_t base, uint32_t limit, uint8_t access, uint8_t granul) { - // Setup the descriptor base address. - gdt[index].base_low = (base & 0xFFFF); - gdt[index].base_middle = (base >> 16) & 0xFF; - gdt[index].base_high = (base >> 24) & 0xFF; + // Setup the descriptor base address. + gdt[index].base_low = (base & 0xFFFFU); + gdt[index].base_middle = (base >> 16U) & 0xFFU; + gdt[index].base_high = (base >> 24U) & 0xFFU; - // Setup the descriptor limits. - gdt[index].limit_low = (limit & 0xFFFF); - gdt[index].granularity = (limit >> 16) & 0x0F; + // Setup the descriptor limits. + gdt[index].limit_low = (limit & 0xFFFFU); + gdt[index].granularity = (limit >> 16U) & 0x0FU; - // Finally, set up the granularity and access flags. - gdt[index].access = _access; - gdt[index].granularity |= _granul & 0xF0; + // Finally, set up the granularity and access flags. + gdt[index].granularity |= granul & 0xF0U; + gdt[index].access = access; + pr_debug( + "gdt[%2d] = {.low=0x%x, .mid=0x%x, .high=0x%x, .access=0x%x, .granul=0x%x}\n", + index, gdt[index].base_low, gdt[index].base_middle, + gdt[index].base_high, gdt[index].access, gdt[index].granularity); } + +// +// == VIRTUAL MEMORY SCHEMES ================================================== +// x86 supports two virtual memory schemes: +// segmentation (mandatory): managed using the segment table, GDT. +// paging (optional) : managed using the page table, PDT. +// Most operating systems want to to use paging and don't want the +// segmentation, but its mandatory and can't just be disabled. +// +// So the trick is to disable its effect as it wasn't there. This can usually +// be done by creating 4 large overlapped segments descriptors (beside the +// null segment): +// segment index 0 : null segment descriptor +// segment index 1 : CODE segment desc. for the privileged (kernel) mode +// segment index 2 : DATA segment desc. for the privileged (kernel) mode +// segment index 3 : CODE segment desc. for the non-privileged (user) mode +// segment index 4 : DATA segment desc. for the non-privileged (user) mode +// +// all these segments starts from 0x00000000 up to 0xffffffff, so you end +// up with overlapped large segments that is privileged code and data, and +// non-privileged code and data in the same time. This should open up the +// virtual memory and disable the segmentation effect. +// +// The processor uses the segment selectors (segment registers cs, ds, ss ...) +// to find out the right segment (once again, the segmentation is must). +// +// == SEGMENT SELECTOR ======================================================== +// Every segment selector is 16 bit size and has the following layout (source): +// |15 3| 2|1 0| +// |----- Index (13-bit) ----- | TI | RPL | +// where TI is the Table Indicator: +// 0 - GDT +// 1 - LDT +// and RPL encodes in 2 bits the Requestor Privilege Level (RPL): +// 00 - Highest +// 01 +// 10 +// 11 - Lowest +// Regarding the `privilege level`, x86 supports 4 levels, but only two of them +// are actually used (00 highest, and 11 lowest). +// +// The remaining 13 bits indicates the segment index. +// +// == GDT_FLUSH =============================================================== +// If you look in gdt.S, you will see a `jmp 0x08` at the end of the gdt_flush. +// Now, if you interpret the 0x08 that is loaded in cs, it will be in binary: +// | 0000000000001| 0| 11| +// | index 3 (code) | GDT| privileged| +// +// and the 0x10 that is loaded in ds, ss, ... : +// | 0000000000010| 0| 11| +// | index 3 (code) | GDT| privileged| +// +// == SS of a USER MODE PROGRAM =============================================== +// If you read the segment selectors of any user mode program you should see +// that the cs value is 27 (0x1b) which means: +// | 0000000000011| 0| 11| +// | index 3 (code) | GDT| non-privileged| +// and the data selectors ds, ss, ..., should store 35 (0x23): +// | 0000000000100| 0| 11| +// | index 4 (data) | GDT| non-privileged| +// \ No newline at end of file diff --git a/mentos/src/descriptor_tables/idt.S b/mentos/src/descriptor_tables/idt.S new file mode 100644 index 0000000..3c4db2f --- /dev/null +++ b/mentos/src/descriptor_tables/idt.S @@ -0,0 +1,17 @@ +; MentOS, The Mentoring Operating system project +; @file idt.asm +; @brief +; @copyright (c) 2014-2021 This file is distributed under the MIT License. +; See LICENSE.md for details. + +global idt_flush ; Allows the C code to call idt_flush(). + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + +idt_flush: + mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. + lidt [eax] ; Load the IDT pointer. + ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/idt.asm b/mentos/src/descriptor_tables/idt.asm deleted file mode 100644 index 66cb14c..0000000 --- a/mentos/src/descriptor_tables/idt.asm +++ /dev/null @@ -1,12 +0,0 @@ -; MentOS, The Mentoring Operating system project -; @file idt.asm -; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. -; See LICENSE.md for details. - -[GLOBAL idt_flush] ; Allows the C code to call idt_flush(). - -idt_flush: - mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. - lidt [eax] ; Load the IDT pointer. - ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/idt.c b/mentos/src/descriptor_tables/idt.c index ce15413..207aa42 100644 --- a/mentos/src/descriptor_tables/idt.c +++ b/mentos/src/descriptor_tables/idt.c @@ -1,14 +1,15 @@ /// MentOS, The Mentoring Operating system project /// @file idt.c /// @brief Functions which manage the Interrupt Descriptor Table (IDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "idt.h" #include "gdt.h" -#include "debug.h" +#include "isr.h" /// @brief This function is in idt.asm. +/// @param idt_pointer Address of the idt. extern void idt_flush(uint32_t idt_pointer); /// The IDT itself. @@ -17,113 +18,108 @@ static idt_descriptor_t idt_table[IDT_SIZE]; /// Pointer structure to give to the CPU. idt_pointer_t idt_pointer; -// 1000 0000 -#define IDT_PRESENT 0x80 -// 0000 0000 -#define IDT_KERNEL 0x00 -// 0110 0000 -#define IDT_USER 0x60 -// 0000 1110 -#define IDT_PADDING 0x0E +/// @brief Use this function to set an entry in the IDT. +/// @param index Indice della IDT. +/// @param handler Puntatore alla funzione che gestira' l'interrupt/Eccezione +/// @param options Le opzioni del descrittore (PRESENT,NOTPRESENT,KERNEL,USER) +/// @param seg_sel Il selettore del segmento della GDT. +static inline void __idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, uint8_t seg_sel) +{ + uintptr_t base_prt = (uintptr_t)handler; + + // Assign the base values. + idt_table[index].offset_low = (base_prt & 0xFFFFu); + idt_table[index].offset_high = (base_prt >> 16u) & 0xFFFFu; + + // Set the other fields. + idt_table[index].null_par = 0x00; + idt_table[index].seg_selector = seg_sel; + idt_table[index].options = options | IDT_PADDING; +} void init_idt() { - // Prepare IDT vector. - for (uint32_t it = 0; it < IDT_SIZE; ++it) { - idt_table[it].offset_low = 0; - idt_table[it].seg_selector = 0; - idt_table[it].null_par = 0; - idt_table[it].options = 0; - idt_table[it].offset_high = 0; - } + // Prepare IDT vector. + for (uint32_t it = 0; it < IDT_SIZE; ++it) { + idt_table[it].offset_low = 0; + idt_table[it].seg_selector = 0; + idt_table[it].null_par = 0; + idt_table[it].options = 0; + idt_table[it].offset_high = 0; + } - // Just like the GDT, the IDT has a "limit" field that is set to the last - // valid byte in the IDT, after adding in the start position (i.e. size-1). - idt_pointer.limit = sizeof(idt_descriptor_t) * IDT_SIZE - 1; - idt_pointer.base = (uint32_t)&idt_table; + // Just like the GDT, the IDT has a "limit" field that is set to the last + // valid byte in the IDT, after adding in the start position (i.e. size-1). + idt_pointer.limit = sizeof(idt_descriptor_t) * IDT_SIZE - 1; + idt_pointer.base = (uint32_t)&idt_table; - // Initialize ISR for CPU execptions. - isrs_init(); + // Initialize ISR for CPU execptions. + isrs_init(); - // Initialize ISR for PIC interrupts. - irqs_init(); + // Initialize ISR for PIC interrupts. + irq_init(); - // Register ISR [0-31] + 80, interrupts generated by CPU. - // These interrupts will be initially managed by isr_handler. - // The appropriate handler will be called by looking at the vector - // isr_routines. - idt_set_gate(0, INT_0, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(1, INT_1, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(2, INT_2, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(3, INT_3, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(4, INT_4, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(5, INT_5, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(6, INT_6, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(7, INT_7, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(8, INT_8, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(9, INT_9, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(10, INT_10, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(11, INT_11, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(12, INT_12, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(13, INT_13, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(14, INT_14, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(15, INT_15, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(16, INT_16, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(17, INT_17, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(18, INT_18, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(19, INT_19, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(20, INT_20, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(21, INT_21, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(22, INT_22, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(23, INT_23, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(24, INT_24, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(25, INT_25, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(26, INT_26, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(27, INT_27, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(28, INT_28, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(29, INT_29, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(30, INT_30, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(31, INT_31, IDT_PRESENT | IDT_KERNEL, 0x8); + // Register ISR [0-31] + 80, interrupts generated by CPU. + // These interrupts will be initially managed by isr_handler. + // The appropriate handler will be called by looking at the vector + // isr_routines. + __idt_set_gate(0, INT_0, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(1, INT_1, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(2, INT_2, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(3, INT_3, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(4, INT_4, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(5, INT_5, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(6, INT_6, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(7, INT_7, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(8, INT_8, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(9, INT_9, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(10, INT_10, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(11, INT_11, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(12, INT_12, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(13, INT_13, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(14, INT_14, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(15, INT_15, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(16, INT_16, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(17, INT_17, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(18, INT_18, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(19, INT_19, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(20, INT_20, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(21, INT_21, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(22, INT_22, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(23, INT_23, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(24, INT_24, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(25, INT_25, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(26, INT_26, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(27, INT_27, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(28, INT_28, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(29, INT_29, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(30, INT_30, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(31, INT_31, GDT_PRESENT | GDT_KERNEL, 0x8); - // Registers ISR [32-47] (irq [0-15]), interrupts generated by PIC. - // These interrupts will be initially managed by irq_handler. - // The appropriate handler will be called by looking at the vector - // isr_routines. - idt_set_gate(32, IRQ_0, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(33, IRQ_1, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(34, IRQ_2, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(35, IRQ_3, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(36, IRQ_4, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(37, IRQ_5, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(38, IRQ_6, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(39, IRQ_7, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(40, IRQ_8, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(41, IRQ_9, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(42, IRQ_10, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(43, IRQ_11, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(44, IRQ_12, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(45, IRQ_13, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(46, IRQ_14, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(47, IRQ_15, IDT_PRESENT | IDT_KERNEL, 0x8); + // Registers ISR [32-47] (irq [0-15]), interrupts generated by PIC. + // These interrupts will be initially managed by irq_handler. + // The appropriate handler will be called by looking at the vector + // isr_routines. + __idt_set_gate(32, IRQ_0, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(33, IRQ_1, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(34, IRQ_2, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(35, IRQ_3, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(36, IRQ_4, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(37, IRQ_5, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(38, IRQ_6, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(39, IRQ_7, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(40, IRQ_8, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(41, IRQ_9, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(42, IRQ_10, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(43, IRQ_11, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(44, IRQ_12, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(45, IRQ_13, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(46, IRQ_14, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(47, IRQ_15, GDT_PRESENT | GDT_KERNEL, 0x8); - // System call! - idt_set_gate(128, INT_80, IDT_PRESENT | IDT_USER, 0x8); + // System call! + __idt_set_gate(128, INT_80, GDT_PRESENT | GDT_USER, 0x8); - // Points the processor's internal register to the new IDT. - idt_flush((uint32_t)&idt_pointer); -} - -void idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, - uint8_t seg_sel) -{ - uint32_t base_prt = (uint32_t)handler; - - // Assign the base values. - idt_table[index].offset_low = (base_prt & 0xFFFF); - idt_table[index].offset_high = (base_prt >> 16) & 0xFFFF; - - // Set the other fields. - idt_table[index].null_par = 0x00; - idt_table[index].seg_selector = seg_sel; - idt_table[index].options = options | IDT_PADDING; + // Points the processor's internal register to the new IDT. + idt_flush((uint32_t)&idt_pointer); } diff --git a/mentos/src/descriptor_tables/interrupt.asm b/mentos/src/descriptor_tables/interrupt.S similarity index 87% rename from mentos/src/descriptor_tables/interrupt.asm rename to mentos/src/descriptor_tables/interrupt.S index b095447..0097083 100644 --- a/mentos/src/descriptor_tables/interrupt.asm +++ b/mentos/src/descriptor_tables/interrupt.S @@ -1,11 +1,13 @@ ; MentOS, The Mentoring Operating system project ; @file interrupt.asm ; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. +extern irq_handler + %macro IRQ 2 - [GLOBAL IRQ_%1] + global IRQ_%1 IRQ_%1: cli ; disable interrupt line ; A normal ISR stub that pops a dummy error code to keep a @@ -14,6 +16,12 @@ push %2 ; irq number jmp irq_common %endmacro + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + ; 32 is the first irq, 47 is the last one. DO NOT CHANGE THESE NUMBERS. IRQ 0, 32 IRQ 1, 33 @@ -32,8 +40,6 @@ IRQ 13, 45 IRQ 14, 46 IRQ 15, 47 -[EXTERN irq_handler] - irq_common: ;==== Save CPU registers =================================================== ; when an irq occurs, the following registers are already pushed on stack: diff --git a/mentos/src/descriptor_tables/interrupt.c b/mentos/src/descriptor_tables/interrupt.c index 15f0161..2788a68 100644 --- a/mentos/src/descriptor_tables/interrupt.c +++ b/mentos/src/descriptor_tables/interrupt.c @@ -1,113 +1,99 @@ /// MentOS, The Mentoring Operating system project -/// @file isr.c +/// @file interrupt.c /// @brief Functions which manage the Interrupt Service Routines (ISRs). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "isr.h" -#include "idt.h" -#include "printk.h" -#include "pic8259.h" -#include "pic8259.h" + #include "scheduler.h" +#include "pic8259.h" +#include "printk.h" +#include "assert.h" +#include "stdio.h" +#include "debug.h" +#include "idt.h" -// TODO: lists, double-linked lists should be a Kernel data struture! -/// @brief shared interrupt handlers are stored into a linked list. -/// irq_struct_t is a node of the list +/// @brief Shared interrupt handlers, stored into a double-linked list. typedef struct irq_struct_t { - /// Puntatore alla funzione handler di un IRQ. - interrupt_handler_t handler; - - /// Puntatore alla descrizione dell'handler. - char *description; - - /// Prossimo handler per questo IRQ - struct irq_struct_t *next; + /// Pointer to the IRQ handler. + interrupt_handler_t handler; + /// Pointer to the description of the handler. + char *description; + /// List handler. + list_head siblings; } irq_struct_t; -// For each IRQ, a chain of handlers. -static irq_struct_t *shared_interrupt_handlers[IRQ_NUM]; +/// For each IRQ, a chain of handlers. +static list_head shared_interrupt_handlers[IRQ_NUM]; +/// Cache where we will store the data regarding an irq service. +static kmem_cache_t *irq_cache; -// Default handler for interrupts and exceptions. -void default_irq_handler(pt_regs *f) +/// @brief Creates a new irq struct. +static inline irq_struct_t *__irq_struct_alloc() { - uint32_t irq_line = f->int_no - 32; - - printk("Kernel PANIC!\n no handler for IRQ [%d]\n", irq_line); - - // Stop kernel execution. - while (1) - ; - // TODO: call the kernel panic method! + // Allocate the structure. + irq_struct_t *irq_struct = kmem_cache_alloc(irq_cache, GFP_KERNEL); + assert(irq_struct && "Failed to allocate memory for IRQ structure."); + // Initialize its fields. + irq_struct->description = NULL; + irq_struct->handler = NULL; + list_head_init(&irq_struct->siblings); + return irq_struct; } -void irqs_init() +/// @brief Destroys an irq struct. +static inline void __irq_struct_dealloc(irq_struct_t *irq_struct) { - // Setting the default_irq_handler as default handler for each IRQ line. - irq_struct_t *irq_struct = NULL; - for (uint32_t i = 0; i < IRQ_NUM; ++i) { - irq_struct = (irq_struct_t *)kmalloc(sizeof(irq_struct_t)); - irq_struct->handler = default_irq_handler; - irq_struct->next = NULL; - shared_interrupt_handlers[i] = irq_struct; - } + list_head_del(&irq_struct->siblings); + kmem_cache_free(irq_struct); } -int irq_install_handler(uint32_t i, interrupt_handler_t handler, - char *description) +void irq_init() { - // We have maximun IRQ_NUM IRQ lines. - if (i > IRQ_NUM) { - return -1; - } + // Initialize the cache. + irq_cache = KMEM_CREATE(irq_struct_t); + // Initializing the list for each irq number. + for (uint32_t i = 0; i < IRQ_NUM; ++i) { + list_head_init(&shared_interrupt_handlers[i]); + } +} - // The current handler for this IRQ line. - irq_struct_t *current_irq_struct = shared_interrupt_handlers[i]; - - // Is the current handler the default one? - if (current_irq_struct->handler == default_irq_handler) { - // Set the given handler as a new handler for i-th IRQ. - current_irq_struct->handler = handler; - // Return. Nothing else to do. - return 0; - } - - // Move current_irq_struct to get the last handler for this IRQ line. - while (current_irq_struct->next != NULL) { - current_irq_struct = current_irq_struct->next; - } - - // Create a new irq_struct_t to save the given handler. - irq_struct_t *irq_struct = - (irq_struct_t *)kmalloc(sizeof(irq_struct_t)); - irq_struct->next = NULL; - irq_struct->description = description; - irq_struct->handler = handler; - - // Store the given handler. - current_irq_struct->next = irq_struct; - return 0; +int irq_install_handler(unsigned i, interrupt_handler_t handler, char *description) +{ + // We have maximun IRQ_NUM IRQ lines. + assert((i < IRQ_NUM) && "Unidentified IRQ number."); + // Create a new irq_struct_t to save the given handler. + irq_struct_t *irq_struct = __irq_struct_alloc(); + irq_struct->description = description; + irq_struct->handler = handler; + // Add the handler to the list of his siblings. + list_head_add_tail(&irq_struct->siblings, &shared_interrupt_handlers[i]); + return 0; } void irq_handler(pt_regs *f) { - // Keep in mind, - // because of irq mapping, the first PIC's irq line is shifted by 32. - uint32_t irq_line = f->int_no - 32; - - // Actually, we may have several handlers for a same irq line. - // The Kernel should provide the dev_id to each handler in order to - // let it know if its own device generated the interrupt. - // TODO: get dev_id - - irq_struct_t *irq_struct = shared_interrupt_handlers[irq_line]; - do { - // Call the interrupt function. - irq_struct->handler(f); - // Move to the next interrupt function. - irq_struct = irq_struct->next; - } while (irq_struct != NULL); - - // Send the end-of-interrupt to PIC. - pic8259_send_eoi(irq_line); + // Keep in mind, + // because of irq mapping, the first PIC's irq line is shifted by 32. + unsigned irq_line = f->int_no - 32; + assert((irq_line < IRQ_NUM) && "Unidentified IRQ number."); + // Actually, we may have several handlers for a same irq line. + // The Kernel should provide the dev_id to each handler in order to + // let it know if its own device generated the interrupt. + // TODO: get dev_id + if (list_head_empty(&shared_interrupt_handlers[irq_line])) { + pr_err("Thre are no handler for IRQ `%d`\n", irq_line); + } else { + list_for_each_decl(it, &shared_interrupt_handlers[irq_line]) + { + // Get the interrupt structure. + irq_struct_t *irq_struct = list_entry(it, irq_struct_t, siblings); + assert(irq_struct && "Something went wrong."); + // Call the interrupt function. + irq_struct->handler(f); + } + } + // Send the end-of-interrupt to PIC. + pic8259_send_eoi(irq_line); } diff --git a/mentos/src/descriptor_tables/tss.S b/mentos/src/descriptor_tables/tss.S new file mode 100644 index 0000000..df9fdd0 --- /dev/null +++ b/mentos/src/descriptor_tables/tss.S @@ -0,0 +1,17 @@ +; MentOS, The Mentoring Operating system project +; @file tss.asm +; @brief +; @copyright (c) 2014-2021 This file is distributed under the MIT License. +; See LICENSE.md for details. + +global tss_flush + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + +tss_flush: + mov ax, 0x28 + ltr ax + ret diff --git a/mentos/src/descriptor_tables/tss.asm b/mentos/src/descriptor_tables/tss.asm deleted file mode 100644 index 84275dc..0000000 --- a/mentos/src/descriptor_tables/tss.asm +++ /dev/null @@ -1,12 +0,0 @@ -; MentOS, The Mentoring Operating system project -; @file tss.asm -; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. -; See LICENSE.md for details. - -[GLOBAL tss_flush] - -tss_flush: - mov ax, 0x28 - ltr ax - ret diff --git a/mentos/src/descriptor_tables/tss.c b/mentos/src/descriptor_tables/tss.c index 8cbf775..a6aefd3 100644 --- a/mentos/src/descriptor_tables/tss.c +++ b/mentos/src/descriptor_tables/tss.c @@ -1,59 +1,54 @@ /// MentOS, The Mentoring Operating system project /// @file tss.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "tss.h" -#include "debug.h" + #include "string.h" +#include "debug.h" +#include "gdt.h" static tss_entry_t kernel_tss; -void tss_init(uint8_t idx, uint32_t kss, uint32_t kesp) +void tss_init(uint8_t idx, uint32_t ss0) { - uint32_t base = (uint32_t) &kernel_tss; + uint32_t base = (uint32_t)&kernel_tss; uint32_t limit = base + sizeof(tss_entry_t); // Add the TSS descriptor to the GDT. // Kernel tss, access(E9 = 1 11 0 1 0 0 1) // 1 present - // 11 ring 3 - // 0 should always be 1, why 0? may be this value doesn't matter at all - // 1 code? + // 11 ring 3 (kernel) + // 0 always 0 when dealing with system segments + // 1 execution // 0 can not be executed by ring lower or equal to DPL, // 0 not readable - // 1 access bit, always 0, cpu set this to 1 when accessing this - // sector(why 0 now?) - gdt_set_gate(idx, base, limit, 0xE9, 0x0); + // 1 access bit, always 0, cpu set this to 1 when accessing this sector + gdt_set_gate(idx, base, limit, GDT_PRESENT | GDT_USER | GDT_EX | GDT_AC, 0x0); - // init. tss entry to zero - memset(&kernel_tss, 0x0, sizeof(tss_entry_t)); - - kernel_tss.ss0 = kss; // Note that we usually set tss's esp to 0 when booting our os, however, // we need to set it to the real esp when we've switched to usermode // because the CPU needs to know what esp to use when usermode app is // calling a kernel function(aka system call), that's why we have a // function below called tss_set_stack. - kernel_tss.esp0 = kesp; - kernel_tss.cs = 0x0b; - kernel_tss.ds = 0x13; - kernel_tss.es = 0x13; - kernel_tss.fs = 0x13; - kernel_tss.gs = 0x13; - kernel_tss.ss = 0x13; + memset(&kernel_tss, 0x0, sizeof(tss_entry_t)); + kernel_tss.ss0 = ss0; + kernel_tss.esp0 = 0x0; + kernel_tss.cs = 0x0b; + kernel_tss.ds = 0x13; + kernel_tss.es = 0x13; + kernel_tss.fs = 0x13; + kernel_tss.gs = 0x13; + kernel_tss.ss = 0x13; kernel_tss.iomap = sizeof(tss_entry_t); } -void print_tss() { - printf("TSS: SSO(%p) ESP0(%p)\n", kernel_tss.ss0, kernel_tss.esp0); -} - void tss_set_stack(uint32_t kss, uint32_t kesp) { // Kernel data segment. - kernel_tss.ss0 = kss; + kernel_tss.ss0 = kss; // Kernel stack address. kernel_tss.esp0 = kesp; } diff --git a/mentos/src/devices/fpu.c b/mentos/src/devices/fpu.c index c9a0b7d..44dc693 100644 --- a/mentos/src/devices/fpu.c +++ b/mentos/src/devices/fpu.c @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file fpu.c /// @brief Floating Point Unit (FPU). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "fpu.h" @@ -9,169 +9,169 @@ #include "debug.h" #include "string.h" #include "assert.h" -#include "process.h" #include "scheduler.h" +#include "math.h" +#include "process.h" +#include "signal.h" -#define NO_LAZY_FPU - -struct task_struct *fpu_thread = NULL; +/// Pointerst to the current thread using the FPU. +task_struct *thread_using_fpu = NULL; +/// Temporary aligned buffer for copying around FPU contexts. +uint8_t saves[512] __attribute__((aligned(16))); /// @brief Set the FPU control word. /// @param cw What to set the control word to. -void set_fpu_cw(const uint16_t cw) +static inline void __set_fpu_cw(const uint16_t cw) { - asm volatile("fldcw %0" ::"m"(cw)); + asm volatile("fldcw %0" ::"m"(cw)); } /// @brief Enable the FPU and SSE. -void enable_fpu() +static inline void __enable_fpu() { - asm volatile("clts"); - - size_t t; - - asm volatile("mov %%cr0, %0" : "=r"(t)); - - t &= ~(1 << 2); - - t |= (1 << 1); - - asm volatile("mov %0, %%cr0" ::"r"(t)); - - asm volatile("mov %%cr4, %0" : "=r"(t)); - - t |= 3 << 9; - - asm volatile("mov %0, %%cr4" ::"r"(t)); + asm volatile("clts"); + size_t t; + asm volatile("mov %%cr0, %0" + : "=r"(t)); + t &= ~(1U << 2U); + t |= (1U << 1U); + asm volatile("mov %0, %%cr0" ::"r"(t)); + asm volatile("mov %%cr4, %0" + : "=r"(t)); + t |= 3U << 9U; + asm volatile("mov %0, %%cr4" ::"r"(t)); } /// Disable FPU and SSE so it traps to the kernel. -void disable_fpu() +static inline void __disable_fpu() { - size_t t; + size_t t; - asm volatile("mov %%cr0, %0" : "=r"(t)); + asm volatile("mov %%cr0, %0" + : "=r"(t)); - t |= 1 << 3; + t |= 1U << 3U; - asm volatile("mov %0, %%cr0" ::"r"(t)); + asm volatile("mov %0, %%cr0" ::"r"(t)); } -// Temporary aligned buffer for copying around FPU contexts. -uint8_t saves[512] __attribute__((aligned(16))); - /// @brief Restore the FPU for a process. -void restore_fpu(struct task_struct *proc) +static inline void __restore_fpu(task_struct *proc) { - assert(proc && "Trying to restore FPU of NULL process."); + assert(proc && "Trying to restore FPU of NULL process."); - memcpy(&saves, (uint8_t *)&proc->thread.fpu_register, 512); + memcpy(&saves, (uint8_t *)&proc->thread.fpu_register, 512); - asm volatile("fxrstor (%0)" ::"r"(saves)); + asm volatile("fxrstor (%0)" ::"r"(saves)); } /// Save the FPU for a process. -void save_fpu(struct task_struct *proc) +static inline void __save_fpu(task_struct *proc) { - assert(proc && "Trying to save FPU of NULL process."); + assert(proc && "Trying to save FPU of NULL process."); - asm volatile("fxsave (%0)" ::"r"(saves)); + asm volatile("fxsave (%0)" ::"r"(saves)); - memcpy((uint8_t *)&proc->thread.fpu_register, &saves, 512); + memcpy((uint8_t *)&proc->thread.fpu_register, &saves, 512); } /// Initialize the FPU. -void init_fpu() +static inline void __init_fpu() { - asm volatile("fninit"); + asm volatile("fninit"); } /// Kernel trap for FPU usage when FPU is disabled. -void invalid_op(pt_regs *r) +/// @param f The interrupt stack frame. +static inline void __invalid_op(pt_regs *f) { - // First, turn the FPU on. - enable_fpu(); - if (fpu_thread == kernel_get_current_process()) { - // If this is the thread that last used the FPU, do nothing. - return; - } - if (fpu_thread) { - // If there is a thread that was using the FPU, save its state. - save_fpu(fpu_thread); - } - fpu_thread = kernel_get_current_process(); - if (!fpu_thread->thread.fpu_enabled) { - /* + pr_debug("__invalid_op(%p)\n", f); + // First, turn the FPU on. + __enable_fpu(); + if (thread_using_fpu == scheduler_get_current_process()) { + // If this is the thread that last used the FPU, do nothing. + return; + } + if (thread_using_fpu) { + // If there is a thread that was using the FPU, save its state. + __save_fpu(thread_using_fpu); + } + thread_using_fpu = scheduler_get_current_process(); + if (!thread_using_fpu->thread.fpu_enabled) { + /* * If the FPU has not been used in this thread previously, * we need to initialize it. */ - init_fpu(); - fpu_thread->thread.fpu_enabled = true; - return; - } - // Otherwise we restore the context for this thread. - restore_fpu(fpu_thread); + __init_fpu(); + thread_using_fpu->thread.fpu_enabled = true; + return; + } + // Otherwise we restore the context for this thread. + __restore_fpu(thread_using_fpu); +} + +/// Kernel trap for various integer and floating-point errors +/// @param f The interrupt stack frame. +static inline void __sigfpe_handler(pt_regs* f) +{ + pr_debug("__sigfpe_handler(%p)\n", f); + + // Notifies current process + thread_using_fpu = scheduler_get_current_process(); + sys_kill(thread_using_fpu->pid, SIGFPE); +} + + +/// @brief Ensure basic FPU functionality works. +/// @details +/// For processors without a FPU, this tests that maths libraries link +/// correctly. +static int __fpu_test() +{ + double a = M_PI; + // First test. + for (int i = 0; i < 10000; i++) { + a = a * 1.123 + (a / 3); + a /= 1.111; + while (a > 100.0) + a /= 3.1234563212; + while (a < 2.0) + a += 1.1232132131; + } + if (a != 50.11095685350556294679336133413) + return 0; + // Second test. + a = M_PI; + for (int i = 0; i < 100; i++) + a = a * 3 + (a / 3); + return (a == 60957114488184560000000000000000000000000000000000000.0); } -// Called during a context switch; disable the FPU. void switch_fpu() { -#ifdef NO_LAZY_FPU - save_fpu(kernel_get_current_process()); -#else - disable_fpu(); -#endif + __save_fpu(scheduler_get_current_process()); } void unswitch_fpu() { -#ifdef NO_LAZY_FPU - restore_fpu(kernel_get_current_process()); -#endif + __restore_fpu(scheduler_get_current_process()); } -static bool_t fpu_test_1() +int fpu_install() { - double a = M_PI; - for (int i = 0; i < 10000; i++) { - a = a * 1.123 + (a / 3); - a /= 1.111; - while (a > 100.0) { - a /= 3.1234563212; - } - while (a < 2.0) { - a += 1.1232132131; - } - } - return (a == 50.11095685350556294679336133413); -} + __enable_fpu(); + __init_fpu(); + __save_fpu(scheduler_get_current_process()); -/* - * Ensure basic FPU functionality works. - * - * For processors without a FPU, this tests that maths libraries link - * correctly. - */ -static bool_t fpu_test_2() -{ - double a = M_PI; - for (int i = 0; i < 100; i++) { - a = a * 3 + (a / 3); - } - return (a == 60957114488184560000000000000000000000000000000000000.0); -} + // Install the handler for device missing + isr_install_handler(DEV_NOT_AVL, &__invalid_op, "fpu: device missing"); -// Enable the FPU context handling. -bool_t fpu_install() -{ -#ifdef NO_LAZY_FPU - enable_fpu(); - init_fpu(); - save_fpu(kernel_get_current_process()); -#else - enable_fpu(); - disable_fpu(); -#endif - isr_install_handler(7, &invalid_op, "fpu"); - return fpu_test_1() & fpu_test_2(); + // Install handlers for floating points and integers errors + isr_install_handler(DIVIDE_ERROR, &__sigfpe_handler, "divide error"); + + // NB: The exceptions bolow don't seems to ever trigger + //isr_install_handler(OVERFLOW, &__sigfpe_handler, "overflow"); + //isr_install_handler(FLOATING_POINT_ERR, &__sigfpe_handler, "floating point error"); + + return __fpu_test(); } diff --git a/mentos/src/devices/pci.c b/mentos/src/devices/pci.c index 086feb0..cb077a9 100644 --- a/mentos/src/devices/pci.c +++ b/mentos/src/devices/pci.c @@ -1,20 +1,19 @@ /// MentOS, The Mentoring Operating system project /// @file pci.c /// @brief Routines for PCI initialization. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #include "pci.h" #include "debug.h" -#include "stdio.h" -#include "kheap.h" #include "string.h" #include "port_io.h" void pci_write_field(uint32_t device, int field, int size, uint32_t value) { - (void)size; - outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); + (void)size; + outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); #if 0 if (size == 4) outportl(PCI_VALUE_PORT, value); @@ -23,493 +22,472 @@ void pci_write_field(uint32_t device, int field, int size, uint32_t value) else if (size == 1) outportb(PCI_VALUE_PORT, value); #else - outportl(PCI_VALUE_PORT, value); + outportl(PCI_VALUE_PORT, value); #endif } uint32_t pci_read_field(uint32_t device, int field, int size) { - outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); + outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); - if (size == 4) { - return inportl(PCI_VALUE_PORT); - } else if (size == 2) { - return inports(PCI_VALUE_PORT + (field & 2)); - } else if (size == 1) { - return inportb(PCI_VALUE_PORT + (field & 3)); - } - return 0xFFFF; + if (size == 4) { + return inportl(PCI_VALUE_PORT); + } else if (size == 2) { + return inports(PCI_VALUE_PORT + (field & 2)); + } else if (size == 1) { + return inportb(PCI_VALUE_PORT + (field & 3)); + } + return 0xFFFF; } uint32_t pci_find_type(uint32_t device) { - return pci_read_field(device, PCI_CLASS, 1) << 16 | - pci_read_field(device, PCI_SUBCLASS, 1) << 8 | - pci_read_field(device, PCI_PROG_IF, 1); + return pci_read_field(device, PCI_CLASS, 1) << 16 | + pci_read_field(device, PCI_SUBCLASS, 1) << 8 | + pci_read_field(device, PCI_PROG_IF, 1); } struct { - uint16_t id; - const char *name; + uint16_t id; + const char *name; } _pci_vendors[] = { - { 0x1022, "AMD" }, { 0x106b, "Apple, Inc." }, - { 0x1234, "Bochs/QEMU" }, { 0x1274, "Ensoniq" }, - { 0x15ad, "VMWare" }, { 0x8086, "Intel Corporation" }, - { 0x80EE, "VirtualBox" }, + { 0x1022, "AMD" }, + { 0x106b, "Apple, Inc." }, + { 0x1234, "Bochs/QEMU" }, + { 0x1274, "Ensoniq" }, + { 0x15ad, "VMWare" }, + { 0x8086, "Intel Corporation" }, + { 0x80EE, "VirtualBox" }, }; struct { - uint16_t ven_id; - uint16_t dev_id; - const char *name; + uint16_t ven_id; + uint16_t dev_id; + const char *name; } _pci_devices[] = { - { 0x1022, 0x2000, "PCNet Ethernet Controller (pcnet)" }, - { 0x106b, 0x003f, "OHCI Controller" }, - { 0x1234, 0x1111, "VGA BIOS Graphics Extensions" }, - { 0x1274, 0x1371, "Creative Labs CT2518 (ensoniq audio)" }, - { 0x15ad, 0x0740, "VM Communication Interface" }, - { 0x15ad, 0x0405, "SVGA II Adapter" }, - { 0x15ad, 0x0790, "PCI bridge" }, - { 0x15ad, 0x07a0, "PCI Express Root Port" }, - { 0x8086, 0x100e, "Gigabit Ethernet Controller (e1000)" }, - { 0x8086, 0x100f, "Gigabit Ethernet Controller (e1000)" }, - { 0x8086, 0x1237, "PCI & Memory" }, - { 0x8086, 0x2415, "AC'97 Audio Chipset" }, - { 0x8086, 0x7000, "PCI-to-ISA Bridge" }, - { 0x8086, 0x7010, "IDE Interface" }, - { 0x8086, 0x7110, "PIIX4 ISA" }, - { 0x8086, 0x7111, "PIIX4 IDE" }, - { 0x8086, 0x7113, "Power Management Controller" }, - { 0x8086, 0x7190, "Host Bridge" }, - { 0x8086, 0x7191, "AGP Bridge" }, - { 0x80EE, 0xBEEF, "Bochs/QEMU-compatible Graphics Adapter" }, - { 0x80EE, 0xCAFE, "Guest Additions Device" }, + { 0x1022, 0x2000, "PCNet Ethernet Controller (pcnet)" }, + { 0x106b, 0x003f, "OHCI Controller" }, + { 0x1234, 0x1111, "VGA BIOS Graphics Extensions" }, + { 0x1274, 0x1371, "Creative Labs CT2518 (ensoniq audio)" }, + { 0x15ad, 0x0740, "VM Communication Interface" }, + { 0x15ad, 0x0405, "SVGA II Adapter" }, + { 0x15ad, 0x0790, "PCI bridge" }, + { 0x15ad, 0x07a0, "PCI Express Root Port" }, + { 0x8086, 0x100e, "Gigabit Ethernet Controller (e1000)" }, + { 0x8086, 0x100f, "Gigabit Ethernet Controller (e1000)" }, + { 0x8086, 0x1237, "PCI & Memory" }, + { 0x8086, 0x2415, "AC'97 Audio Chipset" }, + { 0x8086, 0x7000, "PCI-to-ISA Bridge" }, + { 0x8086, 0x7010, "IDE Interface" }, + { 0x8086, 0x7110, "PIIX4 ISA" }, + { 0x8086, 0x7111, "PIIX4 IDE" }, + { 0x8086, 0x7113, "Power Management Controller" }, + { 0x8086, 0x7190, "Host Bridge" }, + { 0x8086, 0x7191, "AGP Bridge" }, + { 0x80EE, 0xBEEF, "Bochs/QEMU-compatible Graphics Adapter" }, + { 0x80EE, 0xCAFE, "Guest Additions Device" }, }; struct { - uint32_t id; - const char *name; + uint32_t id; + const char *name; } _pci_classes[] = { - { 0x000000, "Legacy Device" }, - { 0x000100, "VGA-Compatible Device" }, + { 0x000000, "Legacy Device" }, + { 0x000100, "VGA-Compatible Device" }, - { 0x010000, "SCSI bus controller" }, - { 0x010100, "ISA Compatibility mode-only controller" }, - { 0x010105, "PCI native mode-only controller" }, - { 0x01010a, "ISA Compatibility mode controller, supports both channels " - "switched to PCI native mode" }, - { 0x01010f, "PCI native mode controller, supports both channels switched " - "to ISA compatibility mode" }, - { 0x010180, - "ISA Compatibility mode-only controller, supports bus mastering" }, - { 0x010185, "PCI native mode-only controller, supports bus mastering" }, - { 0x01018a, "ISA Compatibility mode controller, supports both channels " - "switched to PCI native mode, supports bus mastering" }, - { 0x01018f, "PCI native mode controller, supports both channels switched " - "\to ISA compatibility mode, supports bus mastering" }, + { 0x010000, "SCSI bus controller" }, + { 0x010100, "ISA Compatibility mode-only controller" }, + { 0x010105, "PCI native mode-only controller" }, + { 0x01010a, "ISA Compatibility mode controller, supports both channels " + "switched to PCI native mode" }, + { 0x01010f, "PCI native mode controller, supports both channels switched " + "to ISA compatibility mode" }, + { 0x010180, + "ISA Compatibility mode-only controller, supports bus mastering" }, + { 0x010185, "PCI native mode-only controller, supports bus mastering" }, + { 0x01018a, "ISA Compatibility mode controller, supports both channels " + "switched to PCI native mode, supports bus mastering" }, + { 0x01018f, "PCI native mode controller, supports both channels switched " + "\to ISA compatibility mode, supports bus mastering" }, - { 0x010200, "Floppy disk controller" }, - { 0x010300, "IPI bus controller" }, - { 0x010400, "RAID controller" }, - { 0x010520, "ATA controller, single stepping" }, - { 0x010530, "ATA controller, continuous" }, - { 0x010600, "Serial ATA controller - vendor specific interface" }, - { 0x010601, "Serial ATA controller - AHCI 1.0 interface" }, - { 0x010700, "Serial Attached SCSI controller" }, - { 0x018000, "Mass Storage controller" }, + { 0x010200, "Floppy disk controller" }, + { 0x010300, "IPI bus controller" }, + { 0x010400, "RAID controller" }, + { 0x010520, "ATA controller, single stepping" }, + { 0x010530, "ATA controller, continuous" }, + { 0x010600, "Serial ATA controller - vendor specific interface" }, + { 0x010601, "Serial ATA controller - AHCI 1.0 interface" }, + { 0x010700, "Serial Attached SCSI controller" }, + { 0x018000, "Mass Storage controller" }, - { 0x020000, "Ethernet controller" }, - { 0x020100, "Token Ring controller" }, - { 0x020200, "FDDI controller" }, - { 0x020300, "ATM controller" }, - { 0x020400, "ISDN controller" }, - { 0x020500, "WorldFip controller" }, - // { 0x0206xx , "PICMG 2.14 Multi Computing" }, - { 0x028000, "Network controller" }, + { 0x020000, "Ethernet controller" }, + { 0x020100, "Token Ring controller" }, + { 0x020200, "FDDI controller" }, + { 0x020300, "ATM controller" }, + { 0x020400, "ISDN controller" }, + { 0x020500, "WorldFip controller" }, + // { 0x0206xx , "PICMG 2.14 Multi Computing" }, + { 0x028000, "Network controller" }, - { 0x030000, "VGA Display controller" }, - { 0x030001, "8514-compatible Display controller" }, - { 0x030100, "XGA Display controller" }, - { 0x030200, "3D Display controller" }, - { 0x038000, "Display controller" }, + { 0x030000, "VGA Display controller" }, + { 0x030001, "8514-compatible Display controller" }, + { 0x030100, "XGA Display controller" }, + { 0x030200, "3D Display controller" }, + { 0x038000, "Display controller" }, - { 0x040000, "Video device" }, - { 0x040100, "Audio device" }, - { 0x040200, "Computer Telephony device" }, - { 0x048000, "Multimedia device" }, + { 0x040000, "Video device" }, + { 0x040100, "Audio device" }, + { 0x040200, "Computer Telephony device" }, + { 0x048000, "Multimedia device" }, - { 0x050000, "RAM memory controller" }, - { 0x050100, "Flash memory controller" }, - { 0x058000, "Memory controller" }, + { 0x050000, "RAM memory controller" }, + { 0x050100, "Flash memory controller" }, + { 0x058000, "Memory controller" }, - { 0x060000, "Host bridge" }, - { 0x060100, "ISA bridge" }, - { 0x060200, "EISA bridge" }, - { 0x060300, "MCA bridge" }, - { 0x060400, "PCI-to-PCI bridge" }, - { 0x060401, "PCI-to-PCI bridge (subtractive decoding)" }, - { 0x060500, "PCMCIA bridge" }, - { 0x060600, "NuBus bridge" }, - { 0x060700, "CardBus bridge" }, - // { 0x0608xx , "RACEway bridge" }, - { 0x060940, "PCI-to-PCI bridge, Semi-transparent, primary facing Host" }, - { 0x060980, "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" }, - { 0x060A00, "InfiniBand-to-PCI host bridge" }, - { 0x068000, "Bridge device" }, + { 0x060000, "Host bridge" }, + { 0x060100, "ISA bridge" }, + { 0x060200, "EISA bridge" }, + { 0x060300, "MCA bridge" }, + { 0x060400, "PCI-to-PCI bridge" }, + { 0x060401, "PCI-to-PCI bridge (subtractive decoding)" }, + { 0x060500, "PCMCIA bridge" }, + { 0x060600, "NuBus bridge" }, + { 0x060700, "CardBus bridge" }, + // { 0x0608xx , "RACEway bridge" }, + { 0x060940, "PCI-to-PCI bridge, Semi-transparent, primary facing Host" }, + { 0x060980, "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" }, + { 0x060A00, "InfiniBand-to-PCI host bridge" }, + { 0x068000, "Bridge device" }, - { 0x070000, "Generic XT-compatible serial controller" }, - { 0x070001, "16450-compatible serial controller" }, - { 0x070002, "16550-compatible serial controller" }, - { 0x070003, "16650-compatible serial controller" }, - { 0x070004, "16750-compatible serial controller" }, - { 0x070005, "16850-compatible serial controller" }, - { 0x070006, "16950-compatible serial controller" }, + { 0x070000, "Generic XT-compatible serial controller" }, + { 0x070001, "16450-compatible serial controller" }, + { 0x070002, "16550-compatible serial controller" }, + { 0x070003, "16650-compatible serial controller" }, + { 0x070004, "16750-compatible serial controller" }, + { 0x070005, "16850-compatible serial controller" }, + { 0x070006, "16950-compatible serial controller" }, - { 0x070100, "Parallel port" }, - { 0x070101, "Bi-directional parallel port" }, - { 0x070102, "ECP 1.X compliant parallel port" }, - { 0x070103, "IEEE1284 controller" }, - { 0x0701FE, "IEEE1284 target device" }, - { 0x070200, "Multiport serial controller" }, + { 0x070100, "Parallel port" }, + { 0x070101, "Bi-directional parallel port" }, + { 0x070102, "ECP 1.X compliant parallel port" }, + { 0x070103, "IEEE1284 controller" }, + { 0x0701FE, "IEEE1284 target device" }, + { 0x070200, "Multiport serial controller" }, - { 0x070300, "Generic modem" }, - { 0x070301, "Hayes 16450-compatible modem" }, - { 0x070302, "Hayes 16550-compatible modem" }, - { 0x070303, "Hayes 16650-compatible modem" }, - { 0x070304, "Hayes 16750-compatible modem" }, - { 0x070400, "GPIB (IEEE 488.1/2) controller" }, - { 0x070500, "Smart Card" }, - { 0x078000, "Communications device" }, + { 0x070300, "Generic modem" }, + { 0x070301, "Hayes 16450-compatible modem" }, + { 0x070302, "Hayes 16550-compatible modem" }, + { 0x070303, "Hayes 16650-compatible modem" }, + { 0x070304, "Hayes 16750-compatible modem" }, + { 0x070400, "GPIB (IEEE 488.1/2) controller" }, + { 0x070500, "Smart Card" }, + { 0x078000, "Communications device" }, - { 0x080000, "Generic 8259 PIC" }, - { 0x080001, "ISA PIC" }, - { 0x080002, "EISA PIC" }, - { 0x080010, "I/O APIC interrupt controller" }, - { 0x080020, "I/O(x) APIC interrupt controller" }, + { 0x080000, "Generic 8259 PIC" }, + { 0x080001, "ISA PIC" }, + { 0x080002, "EISA PIC" }, + { 0x080010, "I/O APIC interrupt controller" }, + { 0x080020, "I/O(x) APIC interrupt controller" }, - { 0x080100, "Generic 8237 DMA controller" }, - { 0x080101, "ISA DMA controller" }, - { 0x080102, "EISA DMA controller" }, + { 0x080100, "Generic 8237 DMA controller" }, + { 0x080101, "ISA DMA controller" }, + { 0x080102, "EISA DMA controller" }, - { 0x080200, "Generic 8254 system timer" }, - { 0x080201, "ISA system timer" }, - { 0x080202, "EISA system timer-pair" }, + { 0x080200, "Generic 8254 system timer" }, + { 0x080201, "ISA system timer" }, + { 0x080202, "EISA system timer-pair" }, - { 0x080300, "Generic RTC controller" }, - { 0x080301, "ISA RTC controller" }, + { 0x080300, "Generic RTC controller" }, + { 0x080301, "ISA RTC controller" }, - { 0x080400, "Generic PCI Hot-Plug controller" }, - { 0x080500, "SD Host controller" }, - { 0x088000, "System peripheral" }, + { 0x080400, "Generic PCI Hot-Plug controller" }, + { 0x080500, "SD Host controller" }, + { 0x088000, "System peripheral" }, - { 0x090000, "Keyboard controller" }, - { 0x090100, "Digitizer (pen)" }, - { 0x090200, "Mouse controller" }, - { 0x090300, "Scanner controller" }, - { 0x090400, "Generic Gameport controller" }, - { 0x090410, "Legacy Gameport controller" }, - { 0x098000, "Input controller" }, + { 0x090000, "Keyboard controller" }, + { 0x090100, "Digitizer (pen)" }, + { 0x090200, "Mouse controller" }, + { 0x090300, "Scanner controller" }, + { 0x090400, "Generic Gameport controller" }, + { 0x090410, "Legacy Gameport controller" }, + { 0x098000, "Input controller" }, - { 0x0a0000, "Generic docking station" }, - { 0x0a8000, "Docking station" }, + { 0x0a0000, "Generic docking station" }, + { 0x0a8000, "Docking station" }, - { 0x0b0000, "386 Processor" }, - { 0x0b0100, "486 Processor" }, - { 0x0b0200, "Pentium Processor" }, - { 0x0b1000, "Alpha Processor" }, - { 0x0b2000, "PowerPC Processor" }, - { 0x0b3000, "MIPS Processor" }, - { 0x0b4000, "Co-processor" }, + { 0x0b0000, "386 Processor" }, + { 0x0b0100, "486 Processor" }, + { 0x0b0200, "Pentium Processor" }, + { 0x0b1000, "Alpha Processor" }, + { 0x0b2000, "PowerPC Processor" }, + { 0x0b3000, "MIPS Processor" }, + { 0x0b4000, "Co-processor" }, - { 0x0c0000, "IEEE 1394 (FireWire)" }, - { 0x0c0010, "IEEE 1394 -- OpenHCI spec" }, - { 0x0c0100, "ACCESS.bus" }, - { 0x0c0200, "SSA" }, - { 0x0c0300, "Universal Serial Bus (UHC spec)" }, - { 0x0c0310, "Universal Serial Bus (Open Host spec)" }, - { 0x0c0320, "USB2 Host controller (Intel Enhanced HCI spec)" }, - { 0x0c0380, "Universal Serial Bus (no PI spec)" }, - { 0x0c03FE, "USB Target Device" }, - { 0x0c0400, "Fibre Channel" }, - { 0x0c0500, "System Management Bus" }, - { 0x0c0600, "InfiniBand" }, - { 0x0c0700, "IPMI SMIC Interface" }, - { 0x0c0701, "IPMI Kybd Controller Style Interface" }, - { 0x0c0702, "IPMI Block Transfer Interface" }, - // { 0x0c08xx , "SERCOS Interface" }, - { 0x0c0900, "CANbus" }, + { 0x0c0000, "IEEE 1394 (FireWire)" }, + { 0x0c0010, "IEEE 1394 -- OpenHCI spec" }, + { 0x0c0100, "ACCESS.bus" }, + { 0x0c0200, "SSA" }, + { 0x0c0300, "Universal Serial Bus (UHC spec)" }, + { 0x0c0310, "Universal Serial Bus (Open Host spec)" }, + { 0x0c0320, "USB2 Host controller (Intel Enhanced HCI spec)" }, + { 0x0c0380, "Universal Serial Bus (no PI spec)" }, + { 0x0c03FE, "USB Target Device" }, + { 0x0c0400, "Fibre Channel" }, + { 0x0c0500, "System Management Bus" }, + { 0x0c0600, "InfiniBand" }, + { 0x0c0700, "IPMI SMIC Interface" }, + { 0x0c0701, "IPMI Kybd Controller Style Interface" }, + { 0x0c0702, "IPMI Block Transfer Interface" }, + // { 0x0c08xx , "SERCOS Interface" }, + { 0x0c0900, "CANbus" }, - { 0x0d100, "iRDA compatible controller" }, - { 0x0d100, "Consumer IR controller" }, - { 0x0d100, "RF controller" }, - { 0x0d100, "Bluetooth controller" }, - { 0x0d100, "Broadband controller" }, - { 0x0d100, "Ethernet (802.11a 5 GHz) controller" }, - { 0x0d100, "Ethernet (802.11b 2.4 GHz) controller" }, - { 0x0d100, "Wireless controller" }, + { 0x0d100, "iRDA compatible controller" }, + { 0x0d100, "Consumer IR controller" }, + { 0x0d100, "RF controller" }, + { 0x0d100, "Bluetooth controller" }, + { 0x0d100, "Broadband controller" }, + { 0x0d100, "Ethernet (802.11a 5 GHz) controller" }, + { 0x0d100, "Ethernet (802.11b 2.4 GHz) controller" }, + { 0x0d100, "Wireless controller" }, - // { 0x0e00xx , "I2O Intelligent I/O, spec 1.0" }, - { 0x0e0000, "Message FIFO at offset 040h" }, + // { 0x0e00xx , "I2O Intelligent I/O, spec 1.0" }, + { 0x0e0000, "Message FIFO at offset 040h" }, - { 0x0f0100, "TV satellite comm. controller" }, - { 0x0f0200, "Audio satellite comm. controller" }, - { 0x0f0300, "Voice satellite comm. controller" }, - { 0x0f0400, "Data satellite comm. controller" }, + { 0x0f0100, "TV satellite comm. controller" }, + { 0x0f0200, "Audio satellite comm. controller" }, + { 0x0f0300, "Voice satellite comm. controller" }, + { 0x0f0400, "Data satellite comm. controller" }, - { 0x100000, "Network and computing en/decryption" }, - { 0x101000, "Entertainment en/decryption" }, - { 0x108000, "En/Decryption" }, + { 0x100000, "Network and computing en/decryption" }, + { 0x101000, "Entertainment en/decryption" }, + { 0x108000, "En/Decryption" }, - { 0x110000, "DPIO modules" }, - { 0x110100, "Perf. counters" }, - { 0x111000, "Comm. synch., time and freq. test" }, - { 0x112000, "Management card" }, - { 0x118000, "Data acq./Signal proc." }, + { 0x110000, "DPIO modules" }, + { 0x110100, "Perf. counters" }, + { 0x111000, "Comm. synch., time and freq. test" }, + { 0x112000, "Management card" }, + { 0x118000, "Data acq./Signal proc." }, }; const char *pci_vendor_lookup(unsigned short vendor_id) { - for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); ++i) { - if (_pci_vendors[i].id == vendor_id) { - return _pci_vendors[i].name; - } - } + for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); ++i) { + if (_pci_vendors[i].id == vendor_id) { + return _pci_vendors[i].name; + } + } - return "Unknown"; + return "Unknown"; } -const char *pci_device_lookup(unsigned short vendor_id, - unsigned short device_id) +const char *pci_device_lookup(unsigned short vendor_id, unsigned short device_id) { - for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); ++i) { - if (_pci_devices[i].ven_id == vendor_id && - _pci_devices[i].dev_id == device_id) { - return _pci_devices[i].name; - } - } + for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); ++i) { + if (_pci_devices[i].ven_id == vendor_id && + _pci_devices[i].dev_id == device_id) { + return _pci_devices[i].name; + } + } - return "Unknown"; + return "Unknown"; } const char *pci_class_lookup(uint32_t class_code) { - for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); ++i) { - if (_pci_classes[i].id == class_code) { - return _pci_classes[i].name; - } - } + for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); ++i) { + if (_pci_classes[i].id == class_code) { + return _pci_classes[i].name; + } + } - return "Unknown"; + return "Unknown"; } -void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra) +void pci_scan_hit(pci_scan_func_t f, uint32_t dev, void *extra) { - uint16_t dev_vend = (uint16_t)pci_read_field(dev, PCI_VENDOR_ID, 2); - uint16_t dev_dvid = (uint16_t)pci_read_field(dev, PCI_DEVICE_ID, 2); - f(dev, dev_vend, dev_dvid, extra); + uint16_t dev_vend = (uint16_t)pci_read_field(dev, PCI_VENDOR_ID, 2); + uint16_t dev_dvid = (uint16_t)pci_read_field(dev, PCI_DEVICE_ID, 2); + f(dev, dev_vend, dev_dvid, extra); } -void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, - void *extra) +void pci_scan_func(pci_scan_func_t f, int type, int bus, int slot, int func, void *extra) { - uint32_t dev = pci_box_device(bus, slot, func); + uint32_t dev = pci_box_device(bus, slot, func); - if ((type == -1) || (type == pci_find_type(dev))) { - pci_scan_hit(f, dev, extra); - } - if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { - pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), extra); - } + if ((type == -1) || (type == pci_find_type(dev))) { + pci_scan_hit(f, dev, extra); + } + if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { + pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), extra); + } } -void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void *extra) +void pci_scan_slot(pci_scan_func_t f, int type, int bus, int slot, void *extra) { - uint32_t dev = pci_box_device(bus, slot, 0); + uint32_t dev = pci_box_device(bus, slot, 0); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { - return; - } + if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { + return; + } - pci_scan_func(f, type, bus, slot, 0, extra); + pci_scan_func(f, type, bus, slot, 0, extra); - if (!pci_read_field(dev, PCI_HEADER_TYPE, 1)) { - return; - } + if (!pci_read_field(dev, PCI_HEADER_TYPE, 1)) { + return; + } - for (int func = 1; func < 8; func++) { - dev = pci_box_device(bus, slot, func); + for (int func = 1; func < 8; func++) { + dev = pci_box_device(bus, slot, func); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) { - pci_scan_func(f, type, bus, slot, func, extra); - } - } + if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) { + pci_scan_func(f, type, bus, slot, func, extra); + } + } } -void pci_scan_bus(pci_func_t f, int type, int bus, void *extra) +void pci_scan_bus(pci_scan_func_t f, int type, int bus, void *extra) { - for (int slot = 0; slot < 32; ++slot) { - pci_scan_slot(f, type, bus, slot, extra); - } + for (int slot = 0; slot < 32; ++slot) { + pci_scan_slot(f, type, bus, slot, extra); + } } -void pci_scan(pci_func_t f, int type, void *extra) +void pci_scan(pci_scan_func_t f, int type, void *extra) { - if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) { - pci_scan_bus(f, type, 0, extra); - return; - } + if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) { + pci_scan_bus(f, type, 0, extra); + return; + } - for (int func = 0; func < 8; ++func) { - uint32_t dev = pci_box_device(0, 0, func); + for (int func = 0; func < 8; ++func) { + uint32_t dev = pci_box_device(0, 0, func); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { - break; - } - pci_scan_bus(f, type, func, extra); - } + if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { + break; + } + pci_scan_bus(f, type, func, extra); + } } -static void find_isa_bridge(uint32_t device, uint16_t vendorid, - uint16_t deviceid, void *extra) +static void find_isa_bridge(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra) { - if (vendorid == 0x8086 && (deviceid == 0x7000 || deviceid == 0x7110)) { - *((uint32_t *)extra) = device; - } + if (vendorid == 0x8086 && (deviceid == 0x7000 || deviceid == 0x7110)) { + *((uint32_t *)extra) = device; + } } -static uint32_t pci_isa = 0; +static uint32_t pci_isa = 0; static uint32_t pci_remaps[4] = { 0 }; void pci_remap() { - pci_scan(&find_isa_bridge, -1, &pci_isa); + pci_scan(&find_isa_bridge, -1, &pci_isa); - if (pci_isa) { - dbg_print("PCI-to-ISA interrupt mappings by line:\n"); + if (pci_isa) { + pr_default("PCI-to-ISA interrupt mappings by line:\n"); - for (int i = 0; i < 4; ++i) { - pci_remaps[i] = pci_read_field(pci_isa, 0x60 + i, 1); - dbg_print("\tLine %d: 0x%2x\n", i + 1, pci_remaps[i]); - } + for (int i = 0; i < 4; ++i) { + pci_remaps[i] = pci_read_field(pci_isa, 0x60 + i, 1); + pr_default("\tLine %d: 0x%2x\n", i + 1, pci_remaps[i]); + } - uint32_t out = 0; - memcpy(&out, &pci_remaps, 4); - pci_write_field(pci_isa, 0x60, 4, out); - } + uint32_t out = 0; + memcpy(&out, &pci_remaps, 4); + pci_write_field(pci_isa, 0x60, 4, out); + } } int pci_get_interrupt(uint32_t device) { - if (pci_isa == 0) { - return pci_read_field(device, PCI_INTERRUPT_LINE, 1); - } + if (pci_isa == 0) { + return pci_read_field(device, PCI_INTERRUPT_LINE, 1); + } - uint32_t irq_pin = pci_read_field(device, PCI_INTERRUPT_PIN, 1); + uint32_t irq_pin = pci_read_field(device, PCI_INTERRUPT_PIN, 1); - if (irq_pin == 0) { - dbg_print("PCI device does not specific interrupt line\n"); - return pci_read_field(device, PCI_INTERRUPT_LINE, 1); - } + if (irq_pin == 0) { + pr_default("PCI device does not specific interrupt line\n"); + return pci_read_field(device, PCI_INTERRUPT_LINE, 1); + } - int pirq = (irq_pin + pci_extract_slot(device) - 2) % 4; + int pirq = (irq_pin + pci_extract_slot(device) - 2) % 4; - uint32_t int_line = pci_read_field(device, PCI_INTERRUPT_LINE, 1); + uint32_t int_line = pci_read_field(device, PCI_INTERRUPT_LINE, 1); - dbg_print("Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?" - "int_line=%d\n", - pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq], - int_line); + pr_default("Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?" + "int_line=%d\n", + pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq], + int_line); - if (pci_remaps[pirq] == 0x80) { - dbg_print("Not mapped, remapping?\n"); - pci_remaps[pirq] = int_line; - uint32_t out = 0; - memcpy(&out, &pci_remaps, 4); - pci_write_field(pci_isa, 0x60, 4, out); - return pci_read_field(device, PCI_INTERRUPT_LINE, 1); - } + if (pci_remaps[pirq] == 0x80) { + pr_default("Not mapped, remapping?\n"); + pci_remaps[pirq] = int_line; + uint32_t out = 0; + memcpy(&out, &pci_remaps, 4); + pci_write_field(pci_isa, 0x60, 4, out); + return pci_read_field(device, PCI_INTERRUPT_LINE, 1); + } - return pci_remaps[pirq]; + return pci_remaps[pirq]; } -static void scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, - void *extra) +static void __scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra) { - (void)device; - (void)vendorid; - (void)deviceid; - size_t *count = extra; - ++(*count); + (void)device; + (void)vendorid; + (void)deviceid; + size_t *count = extra; + ++(*count); } -static void scan_hit_list(uint32_t device, uint16_t vendorid, uint16_t deviceid, - void *extra) +static void __scan_hit_list(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra) { - (void)extra; - dbg_print("%2x:%2x.%d\n", pci_extract_bus(device), pci_extract_slot(device), - pci_extract_func(device), deviceid); - dbg_print(" Vendor : [0x%06x] %s\n", vendorid, - pci_vendor_lookup(vendorid)); - dbg_print(" Device : [0x%06x] %s\n", deviceid, - pci_device_lookup(vendorid, deviceid)); - dbg_print(" Type : [0x%06x] %s\n", pci_find_type(device), - pci_class_lookup(pci_find_type(device))); - dbg_print(" Status : 0x%4x\n", - pci_read_field(device, PCI_STATUS, 2)); - dbg_print(" Command : 0x%4x\n", - pci_read_field(device, PCI_COMMAND, 2)); - dbg_print(" Revision : %2d\n", - pci_read_field(device, PCI_REVISION_ID, 1)); - dbg_print(" Cache Line Size : %2d\n", - pci_read_field(device, PCI_CACHE_LINE_SIZE, 1)); - dbg_print(" Latency Timer : %2d\n", - pci_read_field(device, PCI_LATENCY_TIMER, 1)); - dbg_print(" Header Type : %2d\n", - pci_read_field(device, PCI_HEADER_TYPE, 1)); - dbg_print(" BIST : %2d\n", pci_read_field(device, PCI_BIST, 1)); - dbg_print(" BAR0 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_0, 4)); - dbg_print(" BAR1 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_1, 4)); - dbg_print(" BAR2 : 0x%08x\n", - pci_read_field(device, PCI_BASE_ADDRESS_2, 4)); - dbg_print(" BAR3 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_3, 4)); - dbg_print(" BAR4 : 0x%08x", pci_read_field(device, PCI_BASE_ADDRESS_4, 4)); - dbg_print(" BAR6 : 0x%08x\n", - pci_read_field(device, PCI_BASE_ADDRESS_5, 4)); - dbg_print(" Cardbus CIS : 0x%08x\n", - pci_read_field(device, PCI_CARDBUS_CIS, 4)); - dbg_print(" Subsystem V. ID : 0x%08x\n", - pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2)); - dbg_print(" Subsystem ID : 0x%08x\n", - pci_read_field(device, PCI_SUBSYSTEM_ID, 2)); - dbg_print(" ROM Base Address: 0x%08x\n", - pci_read_field(device, PCI_ROM_ADDRESS, 4)); - dbg_print(" PCI Cp. LinkList: 0x%08x\n", - pci_read_field(device, PCI_CAPABILITY_LIST, 1)); - dbg_print(" Max Latency : 0x%08x\n", - pci_read_field(device, PCI_MAX_LAT, 1)); - dbg_print(" Min Grant : 0x%08x\n", - pci_read_field(device, PCI_MIN_GNT, 1)); - dbg_print(" Interrupt Pin : %2d\n", - pci_read_field(device, PCI_INTERRUPT_PIN, 1)); - dbg_print(" Interrupt Line : %2d\n", - pci_read_field(device, PCI_INTERRUPT_LINE, 1)); - dbg_print(" Interrupt Number: %2d\n", pci_get_interrupt(device)); - dbg_print("\n"); + (void)extra; + pr_default("%2x:%2x.%d\n", pci_extract_bus(device), pci_extract_slot(device), pci_extract_func(device), deviceid); + pr_default(" Vendor : [0x%06x] %s\n", vendorid, pci_vendor_lookup(vendorid)); + pr_default(" Device : [0x%06x] %s\n", deviceid, pci_device_lookup(vendorid, deviceid)); + pr_default(" Type : [0x%06x] %s\n", pci_find_type(device), pci_class_lookup(pci_find_type(device))); + pr_default(" Status : 0x%4x\n", pci_read_field(device, PCI_STATUS, 2)); + pr_default(" Command : 0x%4x\n", pci_read_field(device, PCI_COMMAND, 2)); + pr_default(" Revision : %2d\n", pci_read_field(device, PCI_REVISION_ID, 1)); + pr_default(" Cache Line Size : %2d\n", pci_read_field(device, PCI_CACHE_LINE_SIZE, 1)); + pr_default(" Latency Timer : %2d\n", pci_read_field(device, PCI_LATENCY_TIMER, 1)); + pr_default(" Header Type : %2d\n", pci_read_field(device, PCI_HEADER_TYPE, 1)); + pr_default(" BIST : %2d\n", pci_read_field(device, PCI_BIST, 1)); + pr_default(" BAR0 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_0, 4)); + pr_default(" BAR1 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_1, 4)); + pr_default(" BAR2 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_2, 4)); + pr_default(" BAR3 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_3, 4)); + pr_default(" BAR4 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_4, 4)); + pr_default(" BAR6 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_5, 4)); + pr_default(" Cardbus CIS : 0x%08x\n", pci_read_field(device, PCI_CARDBUS_CIS, 4)); + pr_default(" Subsystem V. ID : 0x%08x\n", pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2)); + pr_default(" Subsystem ID : 0x%08x\n", pci_read_field(device, PCI_SUBSYSTEM_ID, 2)); + pr_default(" ROM Base Address: 0x%08x\n", pci_read_field(device, PCI_ROM_ADDRESS, 4)); + pr_default(" PCI Cp. LinkList: 0x%08x\n", pci_read_field(device, PCI_CAPABILITY_LIST, 1)); + pr_default(" Max Latency : 0x%08x\n", pci_read_field(device, PCI_MAX_LAT, 1)); + pr_default(" Min Grant : 0x%08x\n", pci_read_field(device, PCI_MIN_GNT, 1)); + pr_default(" Interrupt Pin : %2d\n", pci_read_field(device, PCI_INTERRUPT_PIN, 1)); + pr_default(" Interrupt Line : %2d\n", pci_read_field(device, PCI_INTERRUPT_LINE, 1)); + pr_default(" Interrupt Number: %2d\n", pci_get_interrupt(device)); + pr_default("\n"); } void pci_debug_scan() { - dbg_print("\n--------------------------------------------------\n"); - dbg_print("Counting PCI entities...\n"); - size_t count = 0; - pci_scan(&scan_count, -1, &count); - dbg_print("Total PCI entities: %d\n", count); + pr_default("\n--------------------------------------------------\n"); + pr_default("Counting PCI entities...\n"); + size_t count = 0; + pci_scan(&__scan_count, -1, &count); + pr_default("Total PCI entities: %d\n", count); - dbg_print("Scanning PCI entities...\n"); - pci_scan(&scan_hit_list, -1, NULL); + pr_default("Scanning PCI entities...\n"); + pci_scan(&__scan_hit_list, -1, NULL); - dbg_print("Mapping PCI entities...\n"); - pci_remap(); + pr_default("Mapping PCI entities...\n"); + pci_remap(); - dbg_print("--------------------------------------------------\n"); + pr_default("--------------------------------------------------\n"); } + +///! @endcond \ No newline at end of file diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index f59a706..cb6b209 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -1,74 +1,76 @@ /// MentOS, The Mentoring Operating system project /// @file ata.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #include "ata.h" +#include "spinlock.h" +#include "fcntl.h" +#include "vmem_map.h" +#include "list.h" +#include "stdio.h" #include "isr.h" #include "vfs.h" #include "pci.h" -#include "list.h" #include "debug.h" -#include "stdio.h" #include "kheap.h" #include "assert.h" #include "string.h" #include "kernel.h" #include "pic8259.h" -#include "spinlock.h" +#include "port_io.h" +#include "time.h" -// #define COMPLETE_VFS // #define COMPLETE_SCHEDULER -uint32_t ata_pci = 0x00000000; +static char ata_drive_char = 'a'; +static int cdrom_number = 0; +static uint32_t ata_pci = 0x00000000; +static int atapi_in_progress = 0; +static list_t *atapi_waiter = NULL; -list_t *atapi_waiter; - -bool_t atapi_in_progress = false; - -typedef union { - uint8_t command_bytes[12]; - uint16_t command_words[6]; +typedef union atapi_command_t { + uint8_t command_bytes[12]; + uint16_t command_words[6]; } atapi_command_t; -typedef struct { - uintptr_t offset; - uint16_t bytes; - uint16_t last; +/// @brief Physical Region Descriptor Tables (PRDT). +typedef struct prdt_t { + uintptr_t offset; + uint16_t bytes; + uint16_t last; } prdt_t; -typedef struct { - char name[256]; - int io_base; - int control; - int slave; - bool_t is_atapi; - ata_identify_t identity; - prdt_t *dma_prdt; - uintptr_t dma_prdt_phys; - uint8_t *dma_start; - uintptr_t dma_start_phys; - uint32_t bar4; - uint32_t atapi_lba; - uint32_t atapi_sector_size; +/// @brief Stores information about an ATA device. +typedef struct ata_device_t { + char name[256]; + int io_base; + int control; + int slave; + int is_atapi; + ata_identify_t identity; + /// Physical Region Descriptor Table (PRDT). + prdt_t *dma_prdt; + /// Stores the physical address of the current PRDT in the Bus Master Register, + /// of the Bus Mastering ATA Disk Controller on the PCI bus. + uintptr_t dma_prdt_phys; + uint8_t *dma_start; + uintptr_t dma_start_phys; + uint32_t bar4; + uint32_t atapi_lba; + uint32_t atapi_sector_size; + /// Device root file. + vfs_file_t *fs_root; } ata_device_t; -ata_device_t ata_primary_master = { .io_base = 0x1F0, - .control = 0x3F6, - .slave = 0 }; -ata_device_t ata_primary_slave = { .io_base = 0x1F0, - .control = 0x3F6, - .slave = 1 }; -ata_device_t ata_secondary_master = { .io_base = 0x170, - .control = 0x376, - .slave = 0 }; -ata_device_t ata_secondary_slave = { .io_base = 0x170, - .control = 0x376, - .slave = 1 }; +ata_device_t ata_primary_master = { .io_base = 0x1F0, .control = 0x3F6, .slave = 0 }; +ata_device_t ata_primary_slave = { .io_base = 0x1F0, .control = 0x3F6, .slave = 1 }; +ata_device_t ata_secondary_master = { .io_base = 0x170, .control = 0x376, .slave = 0 }; +ata_device_t ata_secondary_slave = { .io_base = 0x170, .control = 0x376, .slave = 1 }; -// volatile uint8_t ata_lock = 0; -spinlock_t ata_lock = { 0 }; +spinlock_t ata_lock; // TODO: support other sector sizes. #define ATA_SECTOR_SIZE 512 @@ -77,730 +79,728 @@ spinlock_t ata_lock = { 0 }; /// @param dev void ata_io_wait(ata_device_t *dev) { - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); } -uint8_t ata_status_wait(ata_device_t *dev, int timeout) -{ - uint8_t status; - if (timeout > 0) { - for (int i = 0; (status = inportb(dev->io_base + ATA_REG_STATUS)) & - ATA_STAT_BUSY && - (i < timeout); - ++i) - ; - } else { - while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & - ATA_STAT_BUSY) - ; - } +static void ata_device_read_sector(ata_device_t *, uint64_t, uint8_t *); +static void ata_device_read_sector_atapi(ata_device_t *, uint64_t, uint8_t *); +static void ata_device_write_sector_retry(ata_device_t *, uint64_t, uint8_t *); - return status; +static vfs_file_t *ata_open(const char *, int, mode_t); +static int ata_close(vfs_file_t *); +static ssize_t ata_read(vfs_file_t *, char *, off_t, size_t); +static ssize_t atapi_read(vfs_file_t *, char *, off_t, size_t); +static ssize_t ata_write(vfs_file_t *, const void *, off_t, size_t); +static int ata_fstat(vfs_file_t *file, stat_t *stat); +static int ata_stat(const char *path, stat_t *stat); + +// == SUPPORT FUNCTIONS ======================================================= +static uint8_t ata_status_wait(ata_device_t *dev, int timeout) +{ + uint8_t status; + if (timeout > 0) { + for (int i = 0; (status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_STAT_BUSY && (i < timeout); ++i) {} + } else { + while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_STAT_BUSY) {} + } + return status; +} +static int ata_wait(ata_device_t *dev, bool_t advanced) +{ + ata_io_wait(dev); + ata_status_wait(dev, 0); + if (advanced) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if (status & ATA_STAT_ERR) + return 1; + if (status & ATA_STAT_FAULT) + return 1; + if (!(status & ATA_STAT_DRQ)) + return 1; + } + return 0; +} +static void ata_device_select(ata_device_t *dev) +{ + outportb(dev->io_base + 1, 1); + outportb(dev->control, 0); + outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + ata_io_wait(dev); +} +static uint64_t ata_max_offset(ata_device_t *dev) +{ + uint64_t sectors = dev->identity.sectors_48; + if (!sectors) { + // Fall back to sectors_28. + sectors = dev->identity.sectors_28; + } + + return sectors * ATA_SECTOR_SIZE; +} +static uint64_t atapi_max_offset(ata_device_t *dev) +{ + uint64_t max_sector = dev->atapi_lba; + if (!max_sector) { + return 0; + } + return (max_sector + 1) * dev->atapi_sector_size; +} +static int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) +{ + assert(!(size % 4)); + + size_t i = 0; + + while (i < size) { + if (*ptr1 != *ptr2) { + return 1; + } + + ptr1++; + ptr2++; + i += sizeof(uint32_t); + } + return 0; } -int ata_wait(ata_device_t *dev, bool_t advanced) -{ - ata_io_wait(dev); - uint8_t status = ata_status_wait(dev, 0); - if (advanced) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if (status & ATA_STAT_ERR) - return 1; - if (status & ATA_STAT_FAULT) - return 1; - if (!(status & ATA_STAT_DRQ)) - return 1; - } +// == VFS ENTRY GENERATION ==================================================== +/// Filesystem general operations. +static vfs_sys_operations_t ata_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = ata_stat +}; - return 0; +/// ATA filesystem file operations. +static vfs_file_operations_t ata_fs_operations = { + .open_f = ata_open, + .unlink_f = NULL, + .close_f = ata_close, + .read_f = ata_read, + .write_f = ata_write, + .lseek_f = NULL, + .stat_f = ata_fstat, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +/// ATAPI filesystem file operations. +static vfs_file_operations_t atapi_fs_operations = { + .open_f = ata_open, + .unlink_f = NULL, + .close_f = ata_close, + .read_f = atapi_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = ata_fstat, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +static vfs_file_t *atapi_device_create(ata_device_t *device) +{ + char path[PATH_MAX]; + sprintf(path, "/dev/%s", device->name); + // Create the file. + vfs_file_t *file = vfs_open(path, O_RDONLY | O_CREAT, 0); + // Set the device. + file->device = device; + // Set the length + file->length = atapi_max_offset(device); + // Re-set the flags. + file->flags = DT_BLK; + // Change the operations. + file->sys_operations = &ata_sys_operations; + file->fs_operations = &atapi_fs_operations; + return file; +} +static vfs_file_t *ata_device_create(ata_device_t *device) +{ + char path[PATH_MAX]; + sprintf(path, "/dev/%s", device->name); + // Create the file. + vfs_file_t *file = vfs_open(path, O_RDWR | O_CREAT, 0); + // Set the device. + file->device = device; + // Set the length + file->length = ata_max_offset(device); + // Re-set the flags. + file->flags = DT_BLK; + // Change the operations. + file->sys_operations = &ata_sys_operations; + file->fs_operations = &ata_fs_operations; + return file; } -void ata_device_select(ata_device_t *dev) +// == VFS CALLBACKS =========================================================== +static vfs_file_t *ata_open(const char *path, int flags, mode_t mode) { - outportb(dev->io_base + 1, 1); - outportb(dev->control, 0); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); + pr_default("ata_open(%s, %d, %d)\n", path, flags, mode); + if (ata_primary_master.fs_root && (strcmp(path, ata_primary_master.fs_root->name) == 0)) { + ++ata_primary_master.fs_root->count; + return ata_primary_master.fs_root; + } + if (ata_primary_slave.fs_root && (strcmp(path, ata_primary_slave.fs_root->name) == 0)) { + ++ata_primary_slave.fs_root->count; + return ata_primary_slave.fs_root; + } + if (ata_secondary_master.fs_root && (strcmp(path, ata_secondary_master.fs_root->name) == 0)) { + ++ata_secondary_master.fs_root->count; + return ata_secondary_master.fs_root; + } + if (ata_secondary_slave.fs_root && (strcmp(path, ata_secondary_slave.fs_root->name) == 0)) { + ++ata_secondary_slave.fs_root->count; + return ata_secondary_slave.fs_root; + } + return NULL; +} +static int ata_close(vfs_file_t *file) +{ + pr_default("ata_close(%p)\n", file); + if (ata_primary_master.fs_root == file) { + --ata_primary_master.fs_root->count; + } + if (ata_primary_slave.fs_root == file) { + --ata_primary_slave.fs_root->count; + } + if (ata_secondary_master.fs_root == file) { + --ata_secondary_master.fs_root->count; + } + if (ata_secondary_slave.fs_root == file) { + --ata_secondary_slave.fs_root->count; + } + return 0; +} +static ssize_t ata_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) +{ + pr_default("ata_read(%p, %p, %d, %d)\n", file, buffer, offset, size); + ata_device_t *dev = (ata_device_t *)file->device; + assert(dev); + + unsigned int start_block = offset / ATA_SECTOR_SIZE; + unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; + + unsigned int x_offset = 0; + + if (offset > ata_max_offset(dev)) { + return 0; + } + + if (offset + size > ata_max_offset(dev)) { + unsigned int i = ata_max_offset(dev) - offset; + size = i; + } + + if (offset % ATA_SECTOR_SIZE) { + unsigned int prefix_size = (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, start_block, (uint8_t *)tmp); + memcpy(buffer, (void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), prefix_size); + kfree(tmp); + x_offset += prefix_size; + start_block++; + } + if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { + unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, end_block, (uint8_t *)tmp); + memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, postfix_size); + kfree(tmp); + end_block--; + } + while (start_block <= end_block) { + ata_device_read_sector(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + x_offset += ATA_SECTOR_SIZE; + start_block++; + } + return size; +} +static ssize_t atapi_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) +{ + pr_default("atapi_read(%p, %p, %d, %d)\n", file, buffer, offset, size); + ata_device_t *dev = (ata_device_t *)file->device; + unsigned int start_block = offset / dev->atapi_sector_size; + unsigned int end_block = (offset + size - 1) / dev->atapi_sector_size; + unsigned int x_offset = 0; + if (offset > atapi_max_offset(dev)) { + return 0; + } + if (offset + size > atapi_max_offset(dev)) { + unsigned int i = atapi_max_offset(dev) - offset; + size = i; + } + if (offset % dev->atapi_sector_size) { + unsigned int prefix_size = (dev->atapi_sector_size - (offset % dev->atapi_sector_size)); + char *tmp = kmalloc(dev->atapi_sector_size); + ata_device_read_sector_atapi(dev, start_block, (uint8_t *)tmp); + memcpy(buffer, (void *)((uintptr_t)tmp + (offset % dev->atapi_sector_size)), prefix_size); + kfree(tmp); + x_offset += prefix_size; + start_block++; + } + if ((offset + size) % dev->atapi_sector_size && start_block <= end_block) { + unsigned int postfix_size = (offset + size) % dev->atapi_sector_size; + char *tmp = kmalloc(dev->atapi_sector_size); + ata_device_read_sector_atapi(dev, end_block, (uint8_t *)tmp); + memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, postfix_size); + kfree(tmp); + end_block--; + } + while (start_block <= end_block) { + ata_device_read_sector_atapi(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + x_offset += dev->atapi_sector_size; + start_block++; + } + return size; +} +static ssize_t ata_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size) +{ + ata_device_t *dev = (ata_device_t *)file->device; + unsigned int start_block = offset / ATA_SECTOR_SIZE; + unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; + unsigned int x_offset = 0; + if (offset > ata_max_offset(dev)) { + return 0; + } + if (offset + size > ata_max_offset(dev)) { + unsigned int i = ata_max_offset(dev) - offset; + size = i; + } + if (offset % ATA_SECTOR_SIZE) { + unsigned int prefix_size = (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, start_block, (uint8_t *)tmp); + pr_default("Writing first block"); + memcpy((void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), buffer, prefix_size); + ata_device_write_sector_retry(dev, start_block, (uint8_t *)tmp); + kfree(tmp); + x_offset += prefix_size; + start_block++; + } + if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { + unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, end_block, (uint8_t *)tmp); + pr_default("Writing last block"); + memcpy(tmp, (void *)((uintptr_t)buffer + size - postfix_size), postfix_size); + ata_device_write_sector_retry(dev, end_block, (uint8_t *)tmp); + kfree(tmp); + end_block--; + } + while (start_block <= end_block) { + ata_device_write_sector_retry(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + x_offset += ATA_SECTOR_SIZE; + start_block++; + } + return size; +} +static int _ata_stat(const ata_device_t *device, stat_t *stat) +{ + if (device) { + stat->st_dev = 0; + stat->st_ino = 0; + stat->st_mode = 0; + stat->st_uid = 0; + stat->st_gid = 0; + stat->st_atime = sys_time(NULL); + stat->st_mtime = sys_time(NULL); + stat->st_ctime = sys_time(NULL); + stat->st_size = 0; + } + return 0; } -void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf); - -void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, - uint8_t *buf); - -void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, - uint8_t *buf); - -#ifdef COMPLETE_VFS -uint32_t read_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer); -uint32_t write_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer); -void open_ata(fs_node_t *node, unsigned int flags); -void close_ata(fs_node_t *node); -#endif - -uint64_t ata_max_offset(ata_device_t *dev) +/// @brief Retrieves information concerning the file at the given position. +/// @param fid The file struct. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int ata_fstat(vfs_file_t *file, stat_t *stat) { - uint64_t sectors = dev->identity.sectors_48; - if (!sectors) { - // Fall back to sectors_28. - sectors = dev->identity.sectors_28; - } - - return sectors * ATA_SECTOR_SIZE; + return _ata_stat(file->device, stat); } -uint64_t atapi_max_offset(ata_device_t *dev) +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path where the file resides. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int ata_stat(const char *path, stat_t *stat) { - uint64_t max_sector = dev->atapi_lba; - - if (!max_sector) { - return 0; - } - - return (max_sector + 1) * dev->atapi_sector_size; + super_block_t *sb = vfs_get_superblock(path); + if (sb && sb->root) { + return _ata_stat(sb->root->device, stat); + } + return -1; } -#ifdef COMPLETE_VFS -uint32_t read_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer) +// == ATA DEVICE MANAGEMENT =================================================== +static bool_t ata_device_init(ata_device_t *dev) { - ata_device_t *dev = (ata_device_t *)node->device; - - unsigned int start_block = offset / ATA_SECTOR_SIZE; - unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; - - unsigned int x_offset = 0; - - if (offset > ata_max_offset(dev)) { - return 0; - } - - if (offset + size > ata_max_offset(dev)) { - unsigned int i = ata_max_offset(dev) - offset; - size = i; - } - - if (offset % ATA_SECTOR_SIZE) { - unsigned int prefix_size = - (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, start_block, (uint8_t *)tmp); - - memcpy(buffer, (void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), - prefix_size); - - free(tmp); - - x_offset += prefix_size; - start_block++; - } - - if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { - unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, end_block, (uint8_t *)tmp); - - memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, - postfix_size); - - free(tmp); - - end_block--; - } - - while (start_block <= end_block) { - ata_device_read_sector(dev, start_block, - (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += ATA_SECTOR_SIZE; - start_block++; - } - - return size; -} -#endif - -#ifdef COMPLETE_VFS -uint32_t read_atapi(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer) -{ - ata_device_t *dev = (ata_device_t *)node->device; - - unsigned int start_block = offset / dev->atapi_sector_size; - unsigned int end_block = (offset + size - 1) / dev->atapi_sector_size; - - unsigned int x_offset = 0; - - if (offset > atapi_max_offset(dev)) { - return 0; - } - - if (offset + size > atapi_max_offset(dev)) { - unsigned int i = atapi_max_offset(dev) - offset; - size = i; - } - - if (offset % dev->atapi_sector_size) { - unsigned int prefix_size = - (dev->atapi_sector_size - (offset % dev->atapi_sector_size)); - char *tmp = malloc(dev->atapi_sector_size); - ata_device_read_sector_atapi(dev, start_block, (uint8_t *)tmp); - - memcpy(buffer, - (void *)((uintptr_t)tmp + (offset % dev->atapi_sector_size)), - prefix_size); - - free(tmp); - - x_offset += prefix_size; - start_block++; - } - - if ((offset + size) % dev->atapi_sector_size && start_block <= end_block) { - unsigned int postfix_size = (offset + size) % dev->atapi_sector_size; - char *tmp = malloc(dev->atapi_sector_size); - ata_device_read_sector_atapi(dev, end_block, (uint8_t *)tmp); - - memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, - postfix_size); - - free(tmp); - - end_block--; - } - - while (start_block <= end_block) { - ata_device_read_sector_atapi(dev, start_block, - (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += dev->atapi_sector_size; - start_block++; - } - - return size; -} -#endif - -#ifdef COMPLETE_VFS -uint32_t write_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer) -{ - ata_device_t *dev = (ata_device_t *)node->device; - - unsigned int start_block = offset / ATA_SECTOR_SIZE; - unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; - - unsigned int x_offset = 0; - - if (offset > ata_max_offset(dev)) { - return 0; - } - - if (offset + size > ata_max_offset(dev)) { - unsigned int i = ata_max_offset(dev) - offset; - size = i; - } - - if (offset % ATA_SECTOR_SIZE) { - unsigned int prefix_size = - (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); - - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, start_block, (uint8_t *)tmp); - - dbg_print("Writing first block"); - - memcpy((void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), buffer, - prefix_size); - ata_device_write_sector_retry(dev, start_block, (uint8_t *)tmp); - - free(tmp); - x_offset += prefix_size; - start_block++; - } - - if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { - unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; - - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, end_block, (uint8_t *)tmp); - - dbg_print("Writing last block"); - - memcpy(tmp, (void *)((uintptr_t)buffer + size - postfix_size), - postfix_size); - - ata_device_write_sector_retry(dev, end_block, (uint8_t *)tmp); - - free(tmp); - end_block--; - } - - while (start_block <= end_block) { - ata_device_write_sector_retry( - dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += ATA_SECTOR_SIZE; - start_block++; - } - - return size; -} -#endif - -#ifdef COMPLETE_VFS -void open_ata(fs_node_t *node, unsigned int flags) -{ - return; -} -#endif - -#ifdef COMPLETE_VFS -void close_ata(fs_node_t *node) -{ - return; -} -#endif - -#ifdef COMPLETE_VFS -fs_node_t *atapi_device_create(ata_device_t *device) -{ - fs_node_t *fnode = malloc(sizeof(fs_node_t)); - memset(fnode, 0x00, sizeof(fs_node_t)); - fnode->inode = 0; - sprintf(fnode->name, "cdrom%d", cdrom_number); - fnode->device = device; - fnode->uid = 0; - fnode->gid = 0; - fnode->mask = 0660; - fnode->length = atapi_max_offset(device); - fnode->flags = FS_BLOCKDEVICE; - fnode->read = read_atapi; - // No write support. - fnode->write = NULL; - fnode->open = open_ata; - fnode->close = close_ata; - fnode->readdir = NULL; - fnode->finddir = NULL; - // TODO, identify: etc? - fnode->ioctl = NULL; - return fnode; -} -#endif - -#ifdef COMPLETE_VFS -fs_node_t *ata_device_create(ata_device_t *device) -{ - fs_node_t *fnode = malloc(sizeof(fs_node_t)); - memset(fnode, 0x00, sizeof(fs_node_t)); - fnode->inode = 0; - sprintf(fnode->name, "atadev%d", ata_drive_char - 'a'); - fnode->device = device; - fnode->uid = 0; - fnode->gid = 0; - fnode->mask = 0660; - // TODO - fnode->length = ata_max_offset(device); - fnode->flags = FS_BLOCKDEVICE; - fnode->read = read_ata; - fnode->write = write_ata; - fnode->open = open_ata; - fnode->close = close_ata; - fnode->readdir = NULL; - fnode->finddir = NULL; - // TODO: identify, etc? - fnode->ioctl = NULL; - return fnode; -} -#endif - -void ata_soft_reset(ata_device_t *dev) -{ - outportb(dev->control, 0x04); - ata_io_wait(dev); - outportb(dev->control, 0x00); -} - -void ata_irq_handler(pt_regs *r) -{ - inportb(ata_primary_master.io_base + ATA_REG_STATUS); - - if (atapi_in_progress) { - // wakeup_queue(atapi_waiter); - } - - // irq_ack(14); - pic8259_send_eoi(14); -} - -void ata_irq_handler_s(pt_regs *r) -{ - inportb(ata_secondary_master.io_base + ATA_REG_STATUS); - - if (atapi_in_progress) { - // wakeup_queue(atapi_waiter); - } - - // irq_ack(15); - pic8259_send_eoi(15); -} - -bool_t ata_device_init(ata_device_t *dev) -{ - dbg_print("Detected IDE device on bus 0x%3x\n", dev->io_base); - dbg_print("Device name: %s\n", dev->name); - ata_device_select(dev); - outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENT); - ata_io_wait(dev); - uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); - dbg_print("Device status: %d\n", status); - - ata_wait(dev, false); - - uint16_t *buf = (uint16_t *)&dev->identity; - for (int i = 0; i < 256; ++i) { - buf[i] = inports(dev->io_base); - } - - uint8_t *ptr = (uint8_t *)&dev->identity.model; - for (int i = 0; i < 39; i += 2) { - char tmp = ptr[i + 1]; - ptr[i + 1] = ptr[i]; - ptr[i] = tmp; - } - ptr[39] = 0; - - dbg_print("Device Model: %s\n", dev->identity.model); - dbg_print("Sectors (48): %d\n", (uint32_t)dev->identity.sectors_48); - dbg_print("Sectors (24): %d\n", dev->identity.sectors_28); - - dbg_print("Setting up DMA...\n"); - // TODO: Ale. - // dev->dma_prdt = kmalloc_p(sizeof(prdt_t) * 1, &dev->dma_prdt_phys); - // dev->dma_start = kmalloc_p(4096, &dev->dma_start_phys); - - dbg_print("Putting prdt at 0x%x (0x%x phys)\n", dev->dma_prdt, - dev->dma_prdt_phys); - dbg_print("Putting prdt[0] at 0x%x (0x%x phys)\n", dev->dma_start, - dev->dma_start_phys); - - dev->dma_prdt[0].offset = dev->dma_start_phys; - dev->dma_prdt[0].bytes = 512; - dev->dma_prdt[0].last = 0x8000; - - dbg_print("ATA PCI device ID: 0x%x\n", ata_pci); - - uint16_t command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); - dbg_print("COMMAND register before: 0x%4x\n", command_reg); - if (command_reg & (1U << 2U)) { - dbg_print("Bus mastering already enabled.\n"); - } else { - // bit 2. - command_reg |= (1U << 2U); - dbg_print("Enabling bus mastering...\n"); - pci_write_field(ata_pci, PCI_COMMAND, 4, command_reg); - command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); - dbg_print("COMMAND register after: 0x%4x\n", command_reg); - } - - dev->bar4 = pci_read_field(ata_pci, PCI_BASE_ADDRESS_4, 4); - dbg_print("BAR4: 0x%x\n", dev->bar4); - - if (dev->bar4 & 0x00000001U) { - dev->bar4 = dev->bar4 & 0xFFFFFFFC; - } else { - dbg_print("? ATA bus master registers are 'usually' I/O ports.\n"); - - // No DMA because we're not sure what to do here- - return false; - } -#if 0 - pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 0xFE); - if (pci_read_field(ata_pci, PCI_INTERRUPT_LINE, 1) == 0xFE) + pr_default("Detected IDE device on bus 0x%3x\n", dev->io_base); + pr_default("Device name: %s\n", dev->name); + ata_device_select(dev); + outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENT); + ata_io_wait(dev); + uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); + pr_default("Device status: %d\n", status); + + ata_wait(dev, false); + + uint16_t *buf = (uint16_t *)&dev->identity; + for (int i = 0; i < 256; ++i) { + buf[i] = inports(dev->io_base); + } + + uint8_t *ptr = (uint8_t *)&dev->identity.model; + for (int i = 0; i < 39; i += 2) { + char tmp = ptr[i + 1]; + ptr[i + 1] = ptr[i]; + ptr[i] = tmp; + } + ptr[39] = 0; + + pr_default("Device Model: %s\n", dev->identity.model); + pr_default("Sectors (48): %d\n", (uint32_t)dev->identity.sectors_48); + pr_default("Sectors (24): %d\n", dev->identity.sectors_28); + + pr_default("Setting up DMA...\n"); + // dev->dma_prdt = kmalloc_p(sizeof(prdt_t) * 1, &dev->dma_prdt_phys); + // dev->dma_start = kmalloc_p(4096, &dev->dma_start_phys); + // TODO: Check correctness. { + uint32_t order = find_nearest_order_greater(0, sizeof(prdt_t)); + page_t *page = _alloc_pages(GFP_KERNEL, order); + dev->dma_prdt = (void *)get_lowmem_address_from_page(page); + dev->dma_prdt_phys = get_physical_address_from_page(page); + } + { + uint32_t order = find_nearest_order_greater(0, 4096); + page_t *page = _alloc_pages(GFP_KERNEL, order); + dev->dma_start = (void *)get_lowmem_address_from_page(page); + dev->dma_start_phys = get_physical_address_from_page(page); + } + pr_default("Putting prdt at 0x%x (0x%x phys)\n", dev->dma_prdt, dev->dma_prdt_phys); + pr_default("Putting prdt[0] at 0x%x (0x%x phys)\n", dev->dma_start, dev->dma_start_phys); + + dev->dma_prdt[0].offset = dev->dma_start_phys; + dev->dma_prdt[0].bytes = 512; + dev->dma_prdt[0].last = 0x8000; + + pr_default("ATA PCI device ID: 0x%x\n", ata_pci); + + uint16_t command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); + pr_default("COMMAND register before: 0x%4x\n", command_reg); + if (command_reg & (1U << 2U)) { + pr_default("Bus mastering already enabled.\n"); + } else { + // bit 2. + command_reg |= (1U << 2U); + pr_default("Enabling bus mastering...\n"); + pci_write_field(ata_pci, PCI_COMMAND, 4, command_reg); + command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); + pr_default("COMMAND register after: 0x%4x\n", command_reg); + } + + dev->bar4 = pci_read_field(ata_pci, PCI_BASE_ADDRESS_4, 4); + pr_default("BAR4: 0x%x\n", dev->bar4); + + if (dev->bar4 & 0x00000001U) { + dev->bar4 = dev->bar4 & 0xFFFFFFFC; + } else { + pr_default("? ATA bus master registers are 'usually' I/O ports.\n"); + // No DMA because we're not sure what to do here- + return 1; + } + pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 0xFE); + if (pci_read_field(ata_pci, PCI_INTERRUPT_LINE, 1) == 0xFE) { // Needs assignment. pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 14); } -#endif - - return true; + return 0; } - -int atapi_device_init(ata_device_t *dev) +static int atapi_device_init(ata_device_t *dev) { - dbg_print("Detected ATAPI device at io-base 0x%3x, ctrl 0x%3x, slave %d\n", - dev->io_base, dev->control, dev->slave); - dbg_print("Device name: %s\n", dev->name); - ata_device_select(dev); - outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_ID_PCKT); - ata_io_wait(dev); - uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); - dbg_print("Device status: %d\n", status); + pr_default("Detected ATAPI device at io-base 0x%3x, ctrl 0x%3x, slave %d\n", + dev->io_base, dev->control, dev->slave); + pr_default("Device name: %s\n", dev->name); + ata_device_select(dev); + outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_ID_PCKT); + ata_io_wait(dev); + uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); + pr_default("Device status: %d\n", status); - ata_wait(dev, false); + ata_wait(dev, false); - uint16_t *buf = (uint16_t *)&dev->identity; + uint16_t *buf = (uint16_t *)&dev->identity; - for (int i = 0; i < 256; ++i) { - buf[i] = inports(dev->io_base); - } + for (int i = 0; i < 256; ++i) { + buf[i] = inports(dev->io_base); + } - uint8_t *ptr = (uint8_t *)&dev->identity.model; - for (int i = 0; i < 39; i += 2) { - char tmp = ptr[i + 1]; - ptr[i + 1] = ptr[i]; - ptr[i] = tmp; - } - ptr[39] = 0; + uint8_t *ptr = (uint8_t *)&dev->identity.model; + for (int i = 0; i < 39; i += 2) { + char tmp = ptr[i + 1]; + ptr[i + 1] = ptr[i]; + ptr[i] = tmp; + } + ptr[39] = 0; - dbg_print("Device Model: %s\n", dev->identity.model); + pr_default("Device Model: %s\n", dev->identity.model); - // Detect medium. - atapi_command_t command; - command.command_bytes[0] = 0x25; - command.command_bytes[1] = 0; - command.command_bytes[2] = 0; - command.command_bytes[3] = 0; - command.command_bytes[4] = 0; - command.command_bytes[5] = 0; - command.command_bytes[6] = 0; - command.command_bytes[7] = 0; - // Bit 0 = PMI (0, last sector). - command.command_bytes[8] = 0; - // Control. - command.command_bytes[9] = 0; - command.command_bytes[10] = 0; - command.command_bytes[11] = 0; + // Detect medium. + atapi_command_t command; + command.command_bytes[0] = 0x25; + command.command_bytes[1] = 0; + command.command_bytes[2] = 0; + command.command_bytes[3] = 0; + command.command_bytes[4] = 0; + command.command_bytes[5] = 0; + command.command_bytes[6] = 0; + command.command_bytes[7] = 0; + // Bit 0 = PMI (0, last sector). + command.command_bytes[8] = 0; + // Control. + command.command_bytes[9] = 0; + command.command_bytes[10] = 0; + command.command_bytes[11] = 0; - uint16_t bus = dev->io_base; + uint16_t bus = dev->io_base; - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_LBA1, 0x08); - outportb(bus + ATA_REG_LBA2, 0x08); - outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_LBA1, 0x08); + outportb(bus + ATA_REG_LBA2, 0x08); + outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); - // Poll. - while (1) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } + // Poll. + while (1) { + status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + } - for (int i = 0; i < 6; ++i) - outports(bus, command.command_words[i]); + for (int i = 0; i < 6; ++i) + outports(bus, command.command_words[i]); - // Poll. - while (1) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_read; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - if ((status & ATA_STAT_DRQ)) { - break; - } - } + // Poll. + while (1) { + status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_read; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + if ((status & ATA_STAT_DRQ)) { + break; + } + } - uint16_t data[4]; + uint16_t data[4]; - for (int i = 0; i < 4; ++i) { - data[i] = inports(bus); - } + for (int i = 0; i < 4; ++i) { + data[i] = inports(bus); + } -#define htonl(l) \ - ((((l)&0xFF) << 24) | (((l)&0xFF00) << 8) | (((l)&0xFF0000) >> 8) | \ - (((l)&0xFF000000) >> 24)) - uint32_t lba, blocks; - ; +#define htonl(l) \ + ((((l)&0xFF) << 24) | (((l)&0xFF00) << 8) | (((l)&0xFF0000) >> 8) | \ + (((l)&0xFF000000) >> 24)) + uint32_t lba, blocks; - memcpy(&lba, &data[0], sizeof(uint32_t)); + memcpy(&lba, &data[0], sizeof(uint32_t)); - lba = htonl(lba); + lba = htonl(lba); - memcpy(&blocks, &data[2], sizeof(uint32_t)); + memcpy(&blocks, &data[2], sizeof(uint32_t)); - blocks = htonl(blocks); + blocks = htonl(blocks); - dev->atapi_lba = lba; - dev->atapi_sector_size = blocks; + dev->atapi_lba = lba; + dev->atapi_sector_size = blocks; - if (!lba) { - return false; - } + if (!lba) { + return false; + } - dbg_print("Finished! LBA = %x; block length = %x\n", lba, blocks); - return true; + pr_default("Finished! LBA = %x; block length = %x\n", lba, blocks); + return true; atapi_error_read: - dbg_print("ATAPI error; no medium?\n"); - return false; + pr_default("ATAPI error; no medium?\n"); + return false; atapi_error: - dbg_print("ATAPI early error; unsure\n"); - return false; + pr_default("ATAPI early error; unsure\n"); + return false; +} +static void ata_soft_reset(ata_device_t *dev) +{ + outportb(dev->control, 0x04); + ata_io_wait(dev); + outportb(dev->control, 0x00); +} +static int ata_device_detect(ata_device_t *dev) +{ + ata_soft_reset(dev); + ata_io_wait(dev); + outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + ata_io_wait(dev); + ata_status_wait(dev, 10000); + + pr_default("Probing cylinder registers...\n"); + uint8_t cl = inportb(dev->io_base + ATA_REG_LBA1); + uint8_t ch = inportb(dev->io_base + ATA_REG_LBA2); + if ((cl == 0xFF) && (ch == 0xFF)) { + pr_default("No drive(s) present\n"); + return 1; + } + + pr_default("Waiting while busy...\n"); + uint8_t status = ata_status_wait(dev, 5000); + if (status & ATA_STAT_BUSY) { + pr_default("No drive(s) present\n"); + return 1; + } + + pr_default("Device detected: 0x%2x 0x%2x\n", cl, ch); + if ((cl == 0x00 && ch == 0x00) || (cl == 0x3C && ch == 0xC3)) { + // The device is not an ATAPI. + dev->is_atapi = false; + // Parallel ATA device, or emulated SATA + sprintf(dev->name, "hd%c", ata_drive_char); + + dev->fs_root = ata_device_create(dev); + if (!dev->fs_root) { + pr_default("Failed to create ata device!\n"); + return 1; + } + if (!vfs_mount(dev->fs_root->name, dev->fs_root)) { + pr_default("Failed to mount ata device!\n"); + return 1; + } + if (ata_device_init(dev)) { + pr_default("Failed to initialize ata device!\n"); + return 1; + } + ++ata_drive_char; + } else if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { + // The device is an ATAPI. + dev->is_atapi = true; + sprintf(dev->name, "cdrom%d", cdrom_number); + + dev->fs_root = atapi_device_create(dev); + if (!dev->fs_root) { + pr_default("Failed to create atapi device!\n"); + return 1; + } + if (!vfs_mount(dev->fs_root->name, dev->fs_root)) { + pr_default("Failed to mount atapi device!\n"); + return 1; + } + if (atapi_device_init(dev)) { + pr_default("Failed to initialize atapi device!\n"); + return 1; + } + ++cdrom_number; + } + pr_default("\n"); + return 0; } -int ata_device_detect(ata_device_t *dev) +// == ATA SECTOR READ/WRITE FUNCTIONS ========================================= +static void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - static char ata_drive_char = 'a'; - static int cdrom_number = 0; + pr_default("ata_device_read_sector(%p, %d, %p)\n", dev, lba, buffer); - ata_soft_reset(dev); - ata_io_wait(dev); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); - ata_status_wait(dev, 10000); + uint16_t bus = dev->io_base; - dbg_print("Probing cylinder registers...\n"); - uint8_t cl = inportb(dev->io_base + ATA_REG_LBA1); - uint8_t ch = inportb(dev->io_base + ATA_REG_LBA2); - if ((cl == 0xFF) && (ch == 0xFF)) { - dbg_print("No drive(s) present\n"); - return false; - } + uint8_t slave = dev->slave; - dbg_print("Waiting while busy...\n"); - uint8_t status = ata_status_wait(dev, 5000); - if (status & ATA_STAT_BUSY) { - dbg_print("No drive(s) present\n"); + if (dev->is_atapi) { + return; + } - return false; - } + spinlock_lock(&ata_lock); - dbg_print("Device detected: 0x%2x 0x%2x\n", cl, ch); - if ((cl == 0x00 && ch == 0x00) || (cl == 0x3C && ch == 0xC3)) { - // The device is not an ATAPI. - dev->is_atapi = false; - // Parallel ATA device, or emulated SATA - sprintf(dev->name, "/dev/hd%c", ata_drive_char); - -#ifdef COMPLETE_VFS - fs_node_t *node = ata_device_create(dev); - vfs_mount(devname, node); -#endif - - ++ata_drive_char; - if (!ata_device_init(dev)) { - return 0; - } - - return 1; - } else if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { - // The device is an ATAPI. - dev->is_atapi = true; - sprintf(dev->name, "/dev/cdrom%d", cdrom_number); - -#ifdef COMPLETE_VFS - fs_node_t *node = atapi_device_create(dev); - vfs_mount(devname, node); -#endif - - ++cdrom_number; - if (!atapi_device_init(dev)) { - return 0; - } - return 2; - } - dbg_print("\n"); - - // TODO: ATAPI, SATA, SATAPI. - - return 0; -} - -void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) -{ - uint16_t bus = dev->io_base; - - uint8_t slave = dev->slave; - - if (dev->is_atapi) { - return; - } - - spinlock_lock(&ata_lock); - -#if 0 - int errors = 0; - try_again: -#endif - - ata_wait(dev, false); - - // Stop. - outportb(dev->bar4, 0x00); - - // Set the PRDT. - outportl(dev->bar4 + 0x04, dev->dma_prdt_phys); - - // Enable error, irq status. - outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); - - // Set read. - outportb(dev->bar4, 0x08); - - irq_enable(); - - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & ATA_STAT_BUSY)) { - break; - } - } - - outportb(bus + ATA_REG_CONTROL, 0x00); - outportb(bus + ATA_REG_HDDEVSEL, - 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); - ata_io_wait(dev); - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_SECCOUNT0, 1); - outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); - outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); - outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); - // outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ); #if 1 - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } + int errors = 0; +try_again: #endif - outportb(bus + ATA_REG_COMMAND, ATA_CMD_RD_DMA); - ata_io_wait(dev); + pr_default("ata_wait\n"); + ata_wait(dev, false); - outportb(dev->bar4, 0x08 | 0x01); + // Stop. + outportb(dev->bar4, 0x00); - while (1) { - int status = inportb(dev->bar4 + 0x02); - int dstatus = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & 0x04)) { - continue; - } - if (!(dstatus & ATA_STAT_BUSY)) { - break; - } - } - irq_disable(); + pr_default("Set the PRDT.\n"); + // Set the PRDT. + outportl(dev->bar4 + 0x04, dev->dma_prdt_phys); -#if 0 + pr_default("Enable error, irq status.\n"); + // Enable error, irq status. + outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); + + // Set read. + outportb(dev->bar4, 0x08); + + pr_default("irq_enable...\n"); + //sti(); + + while (1) { + pr_default("Wait busy...\n"); + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if (!(status & ATA_STAT_BUSY)) { + break; + } + } + + pr_default("Read.\n"); + outportb(bus + ATA_REG_CONTROL, 0x00); + outportb(bus + ATA_REG_HDDEVSEL, 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); + ata_io_wait(dev); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_SECCOUNT0, 1); + outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); + outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); + outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); + // outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ); + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + } + outportb(bus + ATA_REG_COMMAND, ATA_CMD_RD_DMA); + + ata_io_wait(dev); + + outportb(dev->bar4, 0x08 | 0x01); + + while (1) { + int status = inportb(dev->bar4 + 0x02); + int dstatus = inportb(dev->io_base + ATA_REG_STATUS); + if (!(status & 0x04)) { + continue; + } + if (!(dstatus & ATA_STAT_BUSY)) { + break; + } + } + //cli(); + +#if 1 if (ata_wait(dev, true)) { - dbg_print("Error during ATA read of lba block %d\n", lba); + pr_default("Error during ATA read of lba block %d\n", lba); errors++; - if (errors > 4) - { - dbg_print( - "-- Too many errors trying to read this block. Bailing.\n"); + if (errors > 4) { + pr_default("-- Too many errors trying to read this block. Bailing.\n"); spinlock_unlock(&ata_lock); return; } @@ -808,11 +808,12 @@ void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) } #endif - // Copy from DMA buffer to output buffer. - memcpy(buf, dev->dma_start, 512); + pr_default("Copy from DMA buffer to output buffer.\n"); + // Copy from DMA buffer to output buffer. + memcpy(buffer, dev->dma_start, 512); - // Inform device we are done. - outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); + // Inform device we are done. + outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); #if 0 int size = 256; @@ -820,190 +821,200 @@ void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) ata_wait(dev, false); outportb(bus + ATA_REG_CONTROL, 0x02); #endif - spinlock_unlock(&ata_lock); + spinlock_unlock(&ata_lock); } - -void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, uint8_t *buf) +static void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - if (!dev->is_atapi) { - return; - } + if (!dev->is_atapi) { + return; + } - uint16_t bus = dev->io_base; - spinlock_lock(&ata_lock); + uint16_t bus = dev->io_base; + spinlock_lock(&ata_lock); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); + outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + ata_io_wait(dev); - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_LBA1, dev->atapi_sector_size & 0xFF); - outportb(bus + ATA_REG_LBA2, dev->atapi_sector_size >> 8); - outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_LBA1, dev->atapi_sector_size & 0xFF); + outportb(bus + ATA_REG_LBA2, dev->atapi_sector_size >> 8); + outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); - // Poll. - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { - break; - } - } + // Poll. + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_on_read_setup; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { + break; + } + } - atapi_in_progress = true; + atapi_in_progress = true; - atapi_command_t command; - command.command_bytes[0] = 0xA8; - command.command_bytes[1] = 0; - command.command_bytes[2] = (lba >> 0x18) & 0xFF; - command.command_bytes[3] = (lba >> 0x10) & 0xFF; - command.command_bytes[4] = (lba >> 0x08) & 0xFF; - command.command_bytes[5] = (lba >> 0x00) & 0xFF; - command.command_bytes[6] = 0; - command.command_bytes[7] = 0; - // Bit 0 = PMI (0, last sector). - command.command_bytes[8] = 0; - // Control. - command.command_bytes[9] = 1; - command.command_bytes[10] = 0; - command.command_bytes[11] = 0; + atapi_command_t command; + command.command_bytes[0] = 0xA8; + command.command_bytes[1] = 0; + command.command_bytes[2] = (lba >> 0x18) & 0xFF; + command.command_bytes[3] = (lba >> 0x10) & 0xFF; + command.command_bytes[4] = (lba >> 0x08) & 0xFF; + command.command_bytes[5] = (lba >> 0x00) & 0xFF; + command.command_bytes[6] = 0; + command.command_bytes[7] = 0; + // Bit 0 = PMI (0, last sector). + command.command_bytes[8] = 0; + // Control. + command.command_bytes[9] = 1; + command.command_bytes[10] = 0; + command.command_bytes[11] = 0; - for (int i = 0; i < 6; ++i) { - outports(bus, command.command_words[i]); - } + for (int i = 0; i < 6; ++i) { + outports(bus, command.command_words[i]); + } - // Wait. + // Wait. #ifdef COMPLETE_SCHEDULER - sleep_on(atapi_waiter); + sleep_on(atapi_waiter); #endif - atapi_in_progress = false; + atapi_in_progress = false; - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { - break; - } - } + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_on_read_setup; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { + break; + } + } - uint16_t size_to_read = inportb(bus + ATA_REG_LBA2) << 8; - size_to_read = size_to_read | inportb(bus + ATA_REG_LBA1); + uint16_t size_to_read = inportb(bus + ATA_REG_LBA2) << 8; + size_to_read = size_to_read | inportb(bus + ATA_REG_LBA1); - inportsm(bus, buf, size_to_read / 2); + inportsm(bus, buffer, size_to_read / 2); - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_on_read_setup; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + } -atapi_error_on_read_setup: - spinlock_unlock(&ata_lock); +atapi_error_on_read_setup:; + spinlock_unlock(&ata_lock); } - -void ata_device_write_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) +static void ata_device_write_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - uint16_t bus = dev->io_base; - uint8_t slave = dev->slave; + uint16_t bus = dev->io_base; + uint8_t slave = dev->slave; - spinlock_lock(&ata_lock); + spinlock_lock(&ata_lock); - outportb(bus + ATA_REG_CONTROL, 0x02); + outportb(bus + ATA_REG_CONTROL, 0x02); - ata_wait(dev, false); - outportb(bus + ATA_REG_HDDEVSEL, - 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); - ata_wait(dev, false); + ata_wait(dev, false); + outportb(bus + ATA_REG_HDDEVSEL, 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); + ata_wait(dev, false); - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_SECCOUNT0, 0x01); - outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); - outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); - outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); - outportb(bus + ATA_REG_COMMAND, ATA_CMD_WRITE); - ata_wait(dev, false); - int size = ATA_SECTOR_SIZE / 2; - outportsm(bus, buf, size); - outportb(bus + 0x07, ATA_CMD_CH_FLSH); - ata_wait(dev, false); - spinlock_unlock(&ata_lock); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_SECCOUNT0, 0x01); + outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); + outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); + outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); + outportb(bus + ATA_REG_COMMAND, ATA_CMD_WRITE); + ata_wait(dev, false); + int size = ATA_SECTOR_SIZE / 2; + outportsm(bus, buffer, size); + outportb(bus + 0x07, ATA_CMD_CH_FLSH); + ata_wait(dev, false); + spinlock_unlock(&ata_lock); } - -int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) +static void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - assert(!(size % 4)); - - size_t i = 0; - - while (i < size) { - if (*ptr1 != *ptr2) { - return 1; - } - - ptr1++; - ptr2++; - i += sizeof(uint32_t); - } - return 0; + uint8_t *read_buf = kmalloc(ATA_SECTOR_SIZE); + do { + ata_device_write_sector(dev, lba, buffer); + ata_device_read_sector(dev, lba, read_buf); + } while ( + buffer_compare((uint32_t *)buffer, (uint32_t *)read_buf, ATA_SECTOR_SIZE)); + kfree(read_buf); } -void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, - uint8_t *buf) +// == IRQ HANDLERS ============================================================ +/// @param f The interrupt stack frame. +static void ata_irq_handler_master(pt_regs *f) { - uint8_t *read_buf = kmalloc(ATA_SECTOR_SIZE); - do { - ata_device_write_sector(dev, lba, buf); - ata_device_read_sector(dev, lba, read_buf); - } while ( - buffer_compare((uint32_t *)buf, (uint32_t *)read_buf, ATA_SECTOR_SIZE)); - kfree(read_buf); + inportb(ata_primary_master.io_base + ATA_REG_STATUS); + + if (atapi_in_progress) { + // wakeup_queue(atapi_waiter); + } + + // irq_ack(14); + pic8259_send_eoi(14); } -void find_ata_pci(uint32_t dev, uint16_t vid, uint16_t did, void *extra) +/// @param f The interrupt stack frame. +static void ata_irq_handler_slave(pt_regs *f) { - if ((vid == 0x8086) && (did == 0x7010 || did == 0x7111)) { - *((uint32_t *)extra) = dev; - } + inportb(ata_secondary_master.io_base + ATA_REG_STATUS); + + if (atapi_in_progress) { + // wakeup_queue(atapi_waiter); + } + + // irq_ack(15); + pic8259_send_eoi(15); } +// == PCI FUNCTIONS =========================================================== +static void pci_find_ata(uint32_t dev, uint16_t vid, uint16_t did, void *extra) +{ + if ((vid == 0x8086) && (did == 0x7010 || did == 0x7111)) { + *((uint32_t *)extra) = dev; + } +} + +// == INITIALIZE/FINALIZE ATA ================================================= int ata_initialize() { - // Detect drives and mount them. - // Locate ATA device via PCI. - pci_scan(&find_ata_pci, -1, &ata_pci); + spinlock_init(&ata_lock); - irq_install_handler(14, ata_irq_handler, "ide master"); - irq_install_handler(15, ata_irq_handler_s, "ide slave"); + // Detect drives and mount them. + // Locate ATA device via PCI. + pci_scan(&pci_find_ata, -1, &ata_pci); - atapi_waiter = list_create(); + //irq_install_handler(14, ata_irq_handler_master, "ide master"); + //irq_install_handler(15, ata_irq_handler_slave, "ide slave"); - dbg_print("Detecteing devices...\n"); - dbg_print("Detecteing Primary Master...\n"); - ata_device_detect(&ata_primary_master); - dbg_print("\n"); - dbg_print("Detecteing Primary Slave...\n"); - ata_device_detect(&ata_primary_slave); - dbg_print("\n"); - dbg_print("Detecteing Secondary Master...\n"); - ata_device_detect(&ata_secondary_master); - dbg_print("\n"); - dbg_print("Detecteing Secondary Slave...\n"); - ata_device_detect(&ata_secondary_slave); - dbg_print("\n"); - dbg_print("Done\n"); + // atapi_waiter = list_create(); - return 0; + pr_default("Detecteing devices...\n"); + pr_default("Detecteing Primary Master...\n"); + ata_device_detect(&ata_primary_master); + pr_default("\n"); + pr_default("Detecteing Primary Slave...\n"); + ata_device_detect(&ata_primary_slave); + pr_default("\n"); + pr_default("Detecteing Secondary Master...\n"); + ata_device_detect(&ata_secondary_master); + pr_default("\n"); + pr_default("Detecteing Secondary Slave...\n"); + ata_device_detect(&ata_secondary_slave); + pr_default("\n"); + pr_default("Done\n"); + + return 0; } int ata_finalize() { - return 0; + return 0; } + +///! @endcond \ No newline at end of file diff --git a/mentos/src/drivers/fdc.c b/mentos/src/drivers/fdc.c index 5dbe1a5..420f60f 100644 --- a/mentos/src/drivers/fdc.c +++ b/mentos/src/drivers/fdc.c @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file fdc.c /// @brief Floppy driver controller handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "fdc.h" diff --git a/mentos/src/drivers/keyboard/keyboard.c b/mentos/src/drivers/keyboard/keyboard.c index 49452f9..0e2a392 100644 --- a/mentos/src/drivers/keyboard/keyboard.c +++ b/mentos/src/drivers/keyboard/keyboard.c @@ -1,271 +1,275 @@ /// MentOS, The Mentoring Operating system project /// @file keyboard.c /// @brief Keyboard handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "keyboard.h" -#include "isr.h" -#include "video.h" -#include "stdio.h" -#include "keymap.h" -#include "bitops.h" + #include "port_io.h" #include "pic8259.h" +#include "keymap.h" +#include "sys/bitops.h" +#include "video.h" +#include "debug.h" +#include "ctype.h" +#include "isr.h" +#include "scheduler.h" /// A macro from Ivan to update buffer indexes. -#define STEP(x) (((x) == BUFSIZE - 1) ? 0 : (x + 1)) +#define STEP(x) (((x) == BUFSIZE - 1) ? 0 : ((x) + 1)) /// Circular Buffer where the pressed keys are stored. -static int circular_buffer[BUFSIZE]; - +static int32_t circular_buffer[BUFSIZE] = { 0 }; /// Index inside the buffer... static long buf_r = 0; - /// Index inside the buffer... static long buf_w = 0; - /// Tracks the state of the leds. static uint8_t ledstate = 0; - -/// The shadow option is active. -static bool_t shadow = false; - -/// The shadow character. -static char shadow_character = 0; - /// The flags concerning the keyboard. -static uint32_t keyboard_flags = 0; +static uint32_t kflags = 0; -/// Flag which identifies the left shift. -#define KBD_LEFT_SHIFT 1 +#define KBD_LEFT_SHIFT (1 << 0) ///< Flag which identifies the left shift. +#define KBD_RIGHT_SHIFT (1 << 1) ///< Flag which identifies the right shift. +#define KBD_CAPS_LOCK (1 << 2) ///< Flag which identifies the caps lock. +#define KBD_NUM_LOCK (1 << 3) ///< Flag which identifies the num lock. +#define KBD_SCROLL_LOCK (1 << 4) ///< Flag which identifies the scroll lock. +#define KBD_LEFT_CONTROL (1 << 5) ///< Flag which identifies the left control. +#define KBD_RIGHT_CONTROL (1 << 6) ///< Flag which identifies the right control. +#define KBD_LEFT_ALT (1 << 7) ///< Flag which identifies the left alt. +#define KBD_RIGHT_ALT (1 << 8) ///< Flag which identifies the right alt. -/// Flag which identifies the right shift. -#define KBD_RIGHT_SHIFT 2 - -/// Flag which identifies the caps lock. -#define KBD_CAPS_LOCK 4 - -/// Flag which identifies the num lock. -#define KBD_NUM_LOCK 8 - -/// Flag which identifies the scroll lock. -#define KBD_SCROLL_LOCK 16 - -/// Flag which identifies the left control. -#define KBD_LEFT_CONTROL 32 - -/// Flag which identifies the right control. -#define KBD_RIGHT_CONTROL 64 - -static inline void push_character(char c) +static inline void push_character(uint16_t c) { - // Update buffer. - if (STEP(buf_w) == buf_r) { - buf_r = STEP(buf_r); - } - circular_buffer[buf_w] = c; - buf_w = STEP(buf_w); + // Update buffer. + if (STEP(buf_w) == buf_r) { + buf_r = STEP(buf_r); + } + + circular_buffer[buf_w] = c; + + buf_w = STEP(buf_w); +} + +static inline int read_character() +{ + char c = -1; + if (buf_r != buf_w) { + c = circular_buffer[buf_r]; + buf_r = STEP(buf_r); + } + return c; } void keyboard_install() { - // Install the IRQ. - irq_install_handler(IRQ_KEYBOARD, keyboard_isr, "keyboard"); - // Enable the IRQ. - pic8259_irq_enable(IRQ_KEYBOARD); + // Initialize the keymaps. + init_keymaps(); + // Install the IRQ. + irq_install_handler(IRQ_KEYBOARD, keyboard_isr, "keyboard"); + // Enable the IRQ. + pic8259_irq_enable(IRQ_KEYBOARD); } -void keyboard_isr(pt_regs *r) +void keyboard_isr(pt_regs *f) { - (void)r; + (void)f; - if (!(inportb(0x64) & 1)) { - return; - } + if (!(inportb(0x64U) & 1U)) { + return; + } - // Take scancode from the port. - uint32_t scancode = inportb(0x60); - if (scancode == 0xE0) { - scancode = (scancode << 8) | inportb(0x60); - } + // Take scancode from the port. + uint32_t scancode = inportb(0x60); + if (scancode == 0xE0) { + scancode = (scancode << 8U) | inportb(0x60); + } - // If the key has just been released. - if (scancode & 0x80) { - if (scancode == (KEY_LEFT_SHIFT | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_LEFT_SHIFT); - } else if (scancode == (KEY_RIGHT_SHIFT | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_RIGHT_SHIFT); - } else if (scancode == (KEY_LEFT_CONTROL | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_LEFT_CONTROL); - } else if (scancode == (KEY_RIGHT_CONTROL | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_RIGHT_CONTROL); - } - } else { - int32_t character = 0; + // If the key has just been released. + if (scancode & 0x80U) { + if (scancode == (KEY_LEFT_SHIFT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_LEFT_SHIFT); + } else if (scancode == (KEY_RIGHT_SHIFT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_RIGHT_SHIFT); + } else if (scancode == (KEY_LEFT_CONTROL | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_LEFT_CONTROL); + } else if (scancode == (KEY_RIGHT_CONTROL | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_RIGHT_CONTROL); + } else if (scancode == (KEY_LEFT_ALT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_LEFT_ALT); + } else if (scancode == (KEY_RIGHT_ALT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_RIGHT_ALT); + } + } else { + int32_t character = 0; - // Parse the key. - switch (scancode) { - case KEY_LEFT_SHIFT: - set_flag(&keyboard_flags, KBD_LEFT_SHIFT); - break; - case KEY_RIGHT_SHIFT: - set_flag(&keyboard_flags, KBD_RIGHT_SHIFT); - break; - case KEY_LEFT_CONTROL: - set_flag(&keyboard_flags, KBD_LEFT_CONTROL); - break; - case KEY_RIGHT_CONTROL: - set_flag(&keyboard_flags, KBD_RIGHT_CONTROL); - break; - case KEY_CAPS_LOCK: - keyboard_flags ^= KBD_CAPS_LOCK; - keyboard_update_leds(); - break; - case KEY_NUM_LOCK: - keyboard_flags ^= KBD_NUM_LOCK; - keyboard_update_leds(); - break; - case KEY_SCROLL_LOCK: - keyboard_flags ^= KBD_SCROLL_LOCK; - keyboard_update_leds(); - break; - case KEY_PAGE_UP: - video_scroll_up(); - break; - case KEY_PAGE_DOWN: - video_scroll_down(); - break; - case KEY_ESCAPE: - break; - case KEY_LEFT_ALT: - break; - case KEY_RIGHT_ALT: - break; - case KEY_BACKSPACE: - push_character('\b'); - video_delete_last_character(); - video_set_cursor_auto(); - break; - case KEY_KP_RETURN: - case KEY_ENTER: - push_character('\n'); - video_new_line(); - video_set_cursor_auto(); - pic8259_send_eoi(IRQ_KEYBOARD); - break; - case KEY_UP_ARROW: - push_character('\033'); - push_character('['); - push_character(72); - break; - case KEY_DOWN_ARROW: - push_character('\033'); - push_character('['); - push_character(80); - break; - case KEY_LEFT_ARROW: - push_character('\033'); - push_character('['); - push_character(75); - break; - case KEY_RIGHT_ARROW: - push_character('\033'); - push_character('['); - push_character(77); - break; - default: - if (has_flag(keyboard_flags, KBD_NUM_LOCK)) { - character = keymap_it.numlock[scancode]; - } - if (character <= 0) { - // Apply shift modifier. - if (has_flag(keyboard_flags, - (KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { - character = keymap_it.shift[scancode]; - } else { - character = keymap_it.base[scancode]; - } - } - if (character > 0) { - // Apply caps lock modifier. - if (has_flag(keyboard_flags, KBD_CAPS_LOCK)) { - if (character >= 'a' && character <= 'z') { - character += 'A' - 'a'; - } else if (character >= 'A' && character <= 'Z') { - character += 'a' - 'A'; - } - } - if (!shadow) { - video_putc(character); - } else if (shadow_character != 0) { - video_putc(shadow_character); - } - // Update buffer. - push_character(character); - } - break; - } - } - pic8259_send_eoi(IRQ_KEYBOARD); + // Parse the key. + switch (scancode) { + case KEY_LEFT_SHIFT: + bitmask_set_assign(kflags, KBD_LEFT_SHIFT); + break; + case KEY_RIGHT_SHIFT: + bitmask_set_assign(kflags, KBD_RIGHT_SHIFT); + break; + case KEY_LEFT_CONTROL: + bitmask_set_assign(kflags, KBD_LEFT_CONTROL); + break; + case KEY_RIGHT_CONTROL: + bitmask_set_assign(kflags, KBD_RIGHT_CONTROL); + break; + case KEY_CAPS_LOCK: + bitmask_flip_assign(kflags, KBD_CAPS_LOCK); + keyboard_update_leds(); + break; + case KEY_NUM_LOCK: + bitmask_flip_assign(kflags, KBD_NUM_LOCK); + keyboard_update_leds(); + break; + case KEY_SCROLL_LOCK: + bitmask_flip_assign(kflags, KBD_SCROLL_LOCK); + keyboard_update_leds(); + break; + case KEY_PAGE_UP: + video_shift_one_page_down(); + break; + case KEY_PAGE_DOWN: + video_shift_one_page_up(); + break; + case KEY_ESCAPE: + break; + case KEY_LEFT_ALT: + bitmask_set_assign(kflags, KBD_LEFT_ALT); + push_character(scancode << 16u); + break; + case KEY_RIGHT_ALT: + bitmask_set_assign(kflags, KBD_RIGHT_ALT); + push_character(scancode << 16u); + break; + case KEY_BACKSPACE: + push_character('\b'); + break; + case KEY_DELETE: + push_character(127); + break; + case KEY_KP_RETURN: + case KEY_ENTER: + push_character('\n'); + break; + case KEY_UP_ARROW: + push_character('\033'); + push_character('['); + push_character('A'); + break; + case KEY_DOWN_ARROW: + push_character('\033'); + push_character('['); + push_character('B'); + break; + case KEY_RIGHT_ARROW: + push_character('\033'); + push_character('['); + push_character('C'); + break; + case KEY_LEFT_ARROW: + push_character('\033'); + push_character('['); + push_character('D'); + break; + case KEY_HOME: + push_character('\033'); + push_character('['); + push_character('H'); + break; + case KEY_END: + push_character('\033'); + push_character('['); + push_character('F'); + break; + default: { + // Get the current keymap. + const keymap_t *keymap = get_keymap(); + + if (bitmask_check(kflags, KBD_NUM_LOCK)) { + character = keymap->numlock[scancode]; + } + if ((character <= 0) && bitmask_check(kflags, KBD_RIGHT_ALT) && (bitmask_check(kflags, KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { + if (scancode == KEY_LEFT_BRAKET) { + character = '{'; + } else if (scancode == KEY_RIGHT_BRAKET) { + character = '}'; + } + } + if ((character <= 0) && bitmask_check(kflags, KBD_RIGHT_ALT)) { + if (scancode == KEY_LEFT_BRAKET) { + character = '['; + } else if (scancode == KEY_RIGHT_BRAKET) { + character = ']'; + } else if (scancode == 0x27) { + character = '@'; + } else if (scancode == 0x28) { + character = '#'; + } + } + if ((character <= 0) && (bitmask_check(kflags, KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { + character = keymap->shift[scancode]; + } + if (character <= 0) { + character = keymap->base[scancode]; + } + // We have failed to retrieve the character. + if (character <= 0) { + break; + } + // Apply caps lock modifier. + if (bitmask_check(kflags, KBD_CAPS_LOCK)) { + if (character >= 'a' && character <= 'z') { + character += 'A' - 'a'; + } else if (character >= 'A' && character <= 'Z') { + character += 'a' - 'A'; + } + } + if (bitmask_check(kflags, KBD_LEFT_CONTROL | KBD_RIGHT_CONTROL)) { + push_character('\033'); + push_character('^'); + character = toupper(character); + + // Qui ci arrivi quando stai tenendo premuto ctrl+. + // Qui devi scatenare il SIGINT (ctrl+c). + + // signal(); + } + // Update buffer. + push_character(character); + } + } + } + pic8259_send_eoi(IRQ_KEYBOARD); } void keyboard_update_leds() { - // Handle scroll_loc & num_loc & caps_loc. - (keyboard_flags & KBD_SCROLL_LOCK) ? (ledstate |= 1) : (ledstate ^= 1); - (keyboard_flags & KBD_NUM_LOCK) ? (ledstate |= 2) : (ledstate ^= 2); - (keyboard_flags & KBD_CAPS_LOCK) ? (ledstate |= 4) : (ledstate ^= 4); + // Handle scroll_loc & num_loc & caps_loc. + bitmask_check(kflags, KBD_SCROLL_LOCK) ? (ledstate |= 1) : (ledstate ^= 1); + bitmask_check(kflags, KBD_NUM_LOCK) ? (ledstate |= 2) : (ledstate ^= 2); + bitmask_check(kflags, KBD_CAPS_LOCK) ? (ledstate |= 4) : (ledstate ^= 4); - // Write on the port. - outportb(0x60, 0xED); - outportb(0x60, ledstate); + // Write on the port. + outportb(0x60, 0xED); + outportb(0x60, ledstate); } void keyboard_enable() { - outportb(0x60, 0xF4); + outportb(0x60, 0xF4); } void keyboard_disable() { - outportb(0x60, 0xF5); + outportb(0x60, 0xF5); } -int keyboard_getc(void) +int keyboard_getc() { - int c = -1; - if (buf_r != buf_w) { - c = circular_buffer[buf_r]; - buf_r = STEP(buf_r); - } - return c; -} - -void keyboard_set_shadow(const bool_t value) -{ - shadow = value; - if (shadow == false) { - shadow_character = 0; - } -} - -void keyboard_set_shadow_character(const char _shadow_character) -{ - shadow_character = _shadow_character; -} - -bool_t keyboard_get_shadow() -{ - return shadow; -} - -bool_t keyboard_is_ctrl_pressed() -{ - return (bool_t)(keyboard_flags & (KBD_LEFT_CONTROL)); -} - -bool_t keyboard_is_shifted() -{ - return (bool_t)(keyboard_flags & (KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT)); + return read_character(); } diff --git a/mentos/src/drivers/keyboard/keymap.c b/mentos/src/drivers/keyboard/keymap.c index 79c5c74..e24b688 100644 --- a/mentos/src/drivers/keyboard/keymap.c +++ b/mentos/src/drivers/keyboard/keymap.c @@ -1,207 +1,284 @@ /// MentOS, The Mentoring Operating system project /// @file keymap.c /// @brief Keymap for keyboard. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "keymap.h" +#include "string.h" -const keymap_t keymap_it = { - .base = { - [0] = -1, - [KEY_ESCAPE] = 27, - [KEY_ONE] = '1', - [KEY_TWO] = '2', - [KEY_THREE] = '3', - [KEY_FOUR] = '4', - [KEY_FIVE] = '5', - [KEY_SIX] = '6', - [KEY_SEVEN] = '7', - [KEY_EIGHT] = '8', - [KEY_NINE] = '9', - [KEY_ZERO] = '0', - [KEY_APOSTROPHE] = '\'', - [KEY_I_ACC] = 141, - [KEY_BACKSPACE] = '\b', - [KEY_TAB] = '\t', - [KEY_Q] = 'q', - [KEY_W] = 'w', - [KEY_E] = 'e', - [KEY_R] = 'r', - [KEY_T] = 't', - [KEY_Y] = 'y', - [KEY_U] = 'u', - [KEY_I] = 'i', - [KEY_O] = 'o', - [KEY_P] = 'p', - [KEY_LEFT_BRAKET] = 138, - [KEY_RIGHT_BRAKET] = '+', - [KEY_ENTER] = 13, - [KEY_LEFT_CONTROL] = -1, - [KEY_A] = 'a', - [KEY_S] = 's', - [KEY_D] = 'd', - [KEY_F] = 'f', - [KEY_G] = 'g', - [KEY_H] = 'h', - [KEY_J] = 'j', - [KEY_K] = 'k', - [KEY_L] = 'l', - [KEY_SEMICOLON] = 149, - [KEY_DOUBLE_QUOTES] = 133, - [KEY_GRAVE] = '\\', - [KEY_LEFT_SHIFT] = -1, - [KEY_BACKSLASH] = 151, - [KEY_Z] = 'z', - [KEY_X] = 'x', - [KEY_C] = 'c', - [KEY_V] = 'v', - [KEY_B] = 'b', - [KEY_N] = 'n', - [KEY_M] = 'm', - [KEY_COMMA] = ',', - [KEY_PERIOD] = '.', - [KEY_MINUS] = '-', - [KEY_RIGHT_SHIFT] = -1, - [KEY_KP_MUL] = '*', - [KEY_LEFT_ALT] = -1, - [KEY_SPACE] = ' ', - [KEY_CAPS_LOCK] = -1, - [KEY_F1] = -1, - [KEY_F2] = -1, - [KEY_F3] = -1, - [KEY_F4] = -1, - [KEY_F5] = -1, - [KEY_F6] = -1, - [KEY_F7] = -1, - [KEY_F8] = -1, - [KEY_F9] = -1, - [KEY_F10] = -1, - [KEY_NUM_LOCK] = -1, - [KEY_SCROLL_LOCK] = -1, - [KEY_KP7] = -1, - [KEY_KP8] = -1, - [KEY_KP9] = -1, - [KEY_KP_SUB] = '-', - [KEY_KP4] = -1, - [KEY_KP5] = -1, - [KEY_KP6] = -1, - [KEY_KP_ADD] = '+', - [KEY_KP1] = -1, - [KEY_KP2] = -1, - [KEY_KP3] = -1, - [KEY_KP0] = -1, - [KEY_KP_DEC] = -1, - [84] = -1, - [85] = -1, - [KEY_KP_LESS] = '<', - [87] = -1, - [88] = -1, - [KEY_KP_DIV] = '/' - }, - .shift = { - [0] = -1, - [KEY_ESCAPE] = -1, - [KEY_ONE] = '!', - [KEY_TWO] = '"', - [KEY_THREE] = 156, - [KEY_FOUR] = '$', - [KEY_FIVE] = '%', - [KEY_SIX] = '&', - [KEY_SEVEN] = '/', - [KEY_EIGHT] = '(', - [KEY_NINE] = ')', - [KEY_ZERO] = '=', - [KEY_APOSTROPHE] = '?', - [KEY_I_ACC] = '^', - [KEY_BACKSPACE] = -1, - [KEY_TAB] = -1, - [KEY_Q] = 'Q', - [KEY_W] = 'W', - [KEY_E] = 'E', - [KEY_R] = 'R', - [KEY_T] = 'T', - [KEY_Y] = 'Y', - [KEY_U] = 'U', - [KEY_I] = 'I', - [KEY_O] = 'O', - [KEY_P] = 'P', - [KEY_LEFT_BRAKET] = 130, - [KEY_RIGHT_BRAKET] = '*', - [KEY_ENTER] = -1, - [KEY_LEFT_CONTROL] = -1, - [KEY_A] = 'A', - [KEY_S] = 'S', - [KEY_D] = 'D', - [KEY_F] = 'F', - [KEY_G] = 'G', - [KEY_H] = 'H', - [KEY_J] = 'J', - [KEY_K] = 'K', - [KEY_L] = 'L', - [KEY_SEMICOLON] = 128, - [KEY_DOUBLE_QUOTES] = 167, - [KEY_GRAVE] = '|', - [KEY_LEFT_SHIFT] = -1, - [KEY_BACKSLASH] = -1, - [KEY_Z] = 'Z', - [KEY_X] = 'X', - [KEY_C] = 'C', - [KEY_V] = 'V', - [KEY_B] = 'B', - [KEY_N] = 'N', - [KEY_M] = 'M', - [KEY_COMMA] = ';', - [KEY_PERIOD] = ':', - [KEY_MINUS] = '_', - [KEY_RIGHT_SHIFT] = -1, - [KEY_KP_MUL] = '*', - [KEY_LEFT_ALT] = -1, - [KEY_SPACE] = ' ', - [KEY_CAPS_LOCK] = -1, - [KEY_F1] = -1, - [KEY_F2] = -1, - [KEY_F3] = -1, - [KEY_F4] = -1, - [KEY_F5] = -1, - [KEY_F6] = -1, - [KEY_F7] = -1, - [KEY_F8] = -1, - [KEY_F9] = -1, - [KEY_F10] = -1, - [KEY_NUM_LOCK] = -1, - [KEY_SCROLL_LOCK] = -1, - [KEY_KP7] = -1, - [KEY_KP8] = -1, - [KEY_KP9] = -1, - [KEY_KP_SUB] = '-', - [KEY_KP4] = -1, - [KEY_KP5] = -1, - [KEY_KP6] = -1, - [KEY_KP_ADD] = '+', - [KEY_KP1] = -1, - [KEY_KP2] = -1, - [KEY_KP3] = -1, - [KEY_KP0] = -1, - [KEY_KP_DEC] = -1, - [84] = -1, - [85] = -1, - [KEY_KP_LESS] = '>', - [87] = -1, - [88] = -1, - [KEY_KP_DIV] = '/' - }, - .numlock = { - [KEY_KP_DEC] = '.', - [KEY_KP0] = '0', - [KEY_KP1] = '1', - [KEY_KP2] = '2', - [KEY_KP3] = '3', - [KEY_KP4] = '4', - [KEY_KP5] = '5', - [KEY_KP6] = '6', - [KEY_KP7] = '7', - [KEY_KP8] = '8', - [KEY_KP9] = '9', - }, -}; +/// Identifies the current keymap type. +keymap_type_t keymap_type = KEYMAP_IT; +/// Contains the different keymaps. +keymap_t keymaps[KEYMAP_TYPE_MAX]; + +keymap_type_t get_keymap_type() +{ + return keymap_type; +} + +void set_keymap_type(keymap_type_t type) +{ + if (type != keymap_type) + keymap_type = type; +} + +const keymap_t *get_keymap() +{ + return &keymaps[keymap_type]; +} + +void init_keymaps() +{ + for (int i = 0; i < KEYMAP_TYPE_MAX; ++i) + memset(&keymaps[i], -1, sizeof(keymap_t)); + + // == ITALIAN KEY MAPPING ================================================= + keymaps[KEYMAP_IT].base[KEY_ESCAPE] = 27; + keymaps[KEYMAP_IT].base[KEY_ONE] = '1'; + keymaps[KEYMAP_IT].base[KEY_TWO] = '2'; + keymaps[KEYMAP_IT].base[KEY_THREE] = '3'; + keymaps[KEYMAP_IT].base[KEY_FOUR] = '4'; + keymaps[KEYMAP_IT].base[KEY_FIVE] = '5'; + keymaps[KEYMAP_IT].base[KEY_SIX] = '6'; + keymaps[KEYMAP_IT].base[KEY_SEVEN] = '7'; + keymaps[KEYMAP_IT].base[KEY_EIGHT] = '8'; + keymaps[KEYMAP_IT].base[KEY_NINE] = '9'; + keymaps[KEYMAP_IT].base[KEY_ZERO] = '0'; + keymaps[KEYMAP_IT].base[KEY_APOSTROPHE] = '\''; + keymaps[KEYMAP_IT].base[KEY_I_ACC] = 141; + keymaps[KEYMAP_IT].base[KEY_BACKSPACE] = '\b'; + keymaps[KEYMAP_IT].base[KEY_TAB] = '\t'; + keymaps[KEYMAP_IT].base[KEY_Q] = 'q'; + keymaps[KEYMAP_IT].base[KEY_W] = 'w'; + keymaps[KEYMAP_IT].base[KEY_E] = 'e'; + keymaps[KEYMAP_IT].base[KEY_R] = 'r'; + keymaps[KEYMAP_IT].base[KEY_T] = 't'; + keymaps[KEYMAP_IT].base[KEY_Y] = 'y'; + keymaps[KEYMAP_IT].base[KEY_U] = 'u'; + keymaps[KEYMAP_IT].base[KEY_I] = 'i'; + keymaps[KEYMAP_IT].base[KEY_O] = 'o'; + keymaps[KEYMAP_IT].base[KEY_P] = 'p'; + keymaps[KEYMAP_IT].base[KEY_LEFT_BRAKET] = 138; + keymaps[KEYMAP_IT].base[KEY_RIGHT_BRAKET] = '+'; + keymaps[KEYMAP_IT].base[KEY_ENTER] = 13; + keymaps[KEYMAP_IT].base[KEY_A] = 'a'; + keymaps[KEYMAP_IT].base[KEY_S] = 's'; + keymaps[KEYMAP_IT].base[KEY_D] = 'd'; + keymaps[KEYMAP_IT].base[KEY_F] = 'f'; + keymaps[KEYMAP_IT].base[KEY_G] = 'g'; + keymaps[KEYMAP_IT].base[KEY_H] = 'h'; + keymaps[KEYMAP_IT].base[KEY_J] = 'j'; + keymaps[KEYMAP_IT].base[KEY_K] = 'k'; + keymaps[KEYMAP_IT].base[KEY_L] = 'l'; + keymaps[KEYMAP_IT].base[KEY_SEMICOLON] = 149; + keymaps[KEYMAP_IT].base[KEY_DOUBLE_QUOTES] = 133; + keymaps[KEYMAP_IT].base[KEY_GRAVE] = '\\'; + keymaps[KEYMAP_IT].base[KEY_BACKSLASH] = 151; + keymaps[KEYMAP_IT].base[KEY_Z] = 'z'; + keymaps[KEYMAP_IT].base[KEY_X] = 'x'; + keymaps[KEYMAP_IT].base[KEY_C] = 'c'; + keymaps[KEYMAP_IT].base[KEY_V] = 'v'; + keymaps[KEYMAP_IT].base[KEY_B] = 'b'; + keymaps[KEYMAP_IT].base[KEY_N] = 'n'; + keymaps[KEYMAP_IT].base[KEY_M] = 'm'; + keymaps[KEYMAP_IT].base[KEY_COMMA] = ','; + keymaps[KEYMAP_IT].base[KEY_PERIOD] = '.'; + keymaps[KEYMAP_IT].base[KEY_MINUS] = '-'; + keymaps[KEYMAP_IT].base[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_IT].base[KEY_SPACE] = ' '; + keymaps[KEYMAP_IT].base[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_IT].base[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_IT].base[KEY_KP_LESS] = '<'; + keymaps[KEYMAP_IT].base[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_IT].shift[KEY_ONE] = '!'; + keymaps[KEYMAP_IT].shift[KEY_TWO] = '"'; + keymaps[KEYMAP_IT].shift[KEY_THREE] = 156; + keymaps[KEYMAP_IT].shift[KEY_FOUR] = '$'; + keymaps[KEYMAP_IT].shift[KEY_FIVE] = '%'; + keymaps[KEYMAP_IT].shift[KEY_SIX] = '&'; + keymaps[KEYMAP_IT].shift[KEY_SEVEN] = '/'; + keymaps[KEYMAP_IT].shift[KEY_EIGHT] = '('; + keymaps[KEYMAP_IT].shift[KEY_NINE] = ')'; + keymaps[KEYMAP_IT].shift[KEY_ZERO] = '='; + keymaps[KEYMAP_IT].shift[KEY_APOSTROPHE] = '?'; + keymaps[KEYMAP_IT].shift[KEY_I_ACC] = '^'; + keymaps[KEYMAP_IT].shift[KEY_Q] = 'Q'; + keymaps[KEYMAP_IT].shift[KEY_W] = 'W'; + keymaps[KEYMAP_IT].shift[KEY_E] = 'E'; + keymaps[KEYMAP_IT].shift[KEY_R] = 'R'; + keymaps[KEYMAP_IT].shift[KEY_T] = 'T'; + keymaps[KEYMAP_IT].shift[KEY_Y] = 'Y'; + keymaps[KEYMAP_IT].shift[KEY_U] = 'U'; + keymaps[KEYMAP_IT].shift[KEY_I] = 'I'; + keymaps[KEYMAP_IT].shift[KEY_O] = 'O'; + keymaps[KEYMAP_IT].shift[KEY_P] = 'P'; + keymaps[KEYMAP_IT].shift[KEY_LEFT_BRAKET] = 130; + keymaps[KEYMAP_IT].shift[KEY_RIGHT_BRAKET] = '*'; + keymaps[KEYMAP_IT].shift[KEY_A] = 'A'; + keymaps[KEYMAP_IT].shift[KEY_S] = 'S'; + keymaps[KEYMAP_IT].shift[KEY_D] = 'D'; + keymaps[KEYMAP_IT].shift[KEY_F] = 'F'; + keymaps[KEYMAP_IT].shift[KEY_G] = 'G'; + keymaps[KEYMAP_IT].shift[KEY_H] = 'H'; + keymaps[KEYMAP_IT].shift[KEY_J] = 'J'; + keymaps[KEYMAP_IT].shift[KEY_K] = 'K'; + keymaps[KEYMAP_IT].shift[KEY_L] = 'L'; + keymaps[KEYMAP_IT].shift[KEY_SEMICOLON] = 128; + keymaps[KEYMAP_IT].shift[KEY_DOUBLE_QUOTES] = 167; + keymaps[KEYMAP_IT].shift[KEY_GRAVE] = '|'; + keymaps[KEYMAP_IT].shift[KEY_Z] = 'Z'; + keymaps[KEYMAP_IT].shift[KEY_X] = 'X'; + keymaps[KEYMAP_IT].shift[KEY_C] = 'C'; + keymaps[KEYMAP_IT].shift[KEY_V] = 'V'; + keymaps[KEYMAP_IT].shift[KEY_B] = 'B'; + keymaps[KEYMAP_IT].shift[KEY_N] = 'N'; + keymaps[KEYMAP_IT].shift[KEY_M] = 'M'; + keymaps[KEYMAP_IT].shift[KEY_COMMA] = ';'; + keymaps[KEYMAP_IT].shift[KEY_PERIOD] = ':'; + keymaps[KEYMAP_IT].shift[KEY_MINUS] = '_'; + keymaps[KEYMAP_IT].shift[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_IT].shift[KEY_SPACE] = ' '; + keymaps[KEYMAP_IT].shift[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_IT].shift[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_IT].shift[KEY_KP_LESS] = '>'; + keymaps[KEYMAP_IT].shift[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_IT].numlock[KEY_KP_DEC] = '.'; + keymaps[KEYMAP_IT].numlock[KEY_KP0] = '0'; + keymaps[KEYMAP_IT].numlock[KEY_KP1] = '1'; + keymaps[KEYMAP_IT].numlock[KEY_KP2] = '2'; + keymaps[KEYMAP_IT].numlock[KEY_KP3] = '3'; + keymaps[KEYMAP_IT].numlock[KEY_KP4] = '4'; + keymaps[KEYMAP_IT].numlock[KEY_KP5] = '5'; + keymaps[KEYMAP_IT].numlock[KEY_KP6] = '6'; + keymaps[KEYMAP_IT].numlock[KEY_KP7] = '7'; + keymaps[KEYMAP_IT].numlock[KEY_KP8] = '8'; + keymaps[KEYMAP_IT].numlock[KEY_KP9] = '9'; + + // == US KEY MAPPING ====================================================== + + keymaps[KEYMAP_US].base[KEY_ESCAPE] = 27; + keymaps[KEYMAP_US].base[KEY_ONE] = '1'; + keymaps[KEYMAP_US].base[KEY_TWO] = '2'; + keymaps[KEYMAP_US].base[KEY_THREE] = '3'; + keymaps[KEYMAP_US].base[KEY_FOUR] = '4'; + keymaps[KEYMAP_US].base[KEY_FIVE] = '5'; + keymaps[KEYMAP_US].base[KEY_SIX] = '6'; + keymaps[KEYMAP_US].base[KEY_SEVEN] = '7'; + keymaps[KEYMAP_US].base[KEY_EIGHT] = '8'; + keymaps[KEYMAP_US].base[KEY_NINE] = '9'; + keymaps[KEYMAP_US].base[KEY_ZERO] = '0'; + keymaps[KEYMAP_US].base[KEY_APOSTROPHE] = '-'; + keymaps[KEYMAP_US].base[KEY_I_ACC] = '='; + keymaps[KEYMAP_US].base[KEY_BACKSPACE] = '\b'; + keymaps[KEYMAP_US].base[KEY_TAB] = '\t'; + keymaps[KEYMAP_US].base[KEY_Q] = 'q'; + keymaps[KEYMAP_US].base[KEY_W] = 'w'; + keymaps[KEYMAP_US].base[KEY_E] = 'e'; + keymaps[KEYMAP_US].base[KEY_R] = 'r'; + keymaps[KEYMAP_US].base[KEY_T] = 't'; + keymaps[KEYMAP_US].base[KEY_Y] = 'y'; + keymaps[KEYMAP_US].base[KEY_U] = 'u'; + keymaps[KEYMAP_US].base[KEY_I] = 'i'; + keymaps[KEYMAP_US].base[KEY_O] = 'o'; + keymaps[KEYMAP_US].base[KEY_P] = 'p'; + keymaps[KEYMAP_US].base[KEY_LEFT_BRAKET] = '['; + keymaps[KEYMAP_US].base[KEY_RIGHT_BRAKET] = ']'; + keymaps[KEYMAP_US].base[KEY_ENTER] = 13; + keymaps[KEYMAP_US].base[KEY_A] = 'a'; + keymaps[KEYMAP_US].base[KEY_S] = 's'; + keymaps[KEYMAP_US].base[KEY_D] = 'd'; + keymaps[KEYMAP_US].base[KEY_F] = 'f'; + keymaps[KEYMAP_US].base[KEY_G] = 'g'; + keymaps[KEYMAP_US].base[KEY_H] = 'h'; + keymaps[KEYMAP_US].base[KEY_J] = 'j'; + keymaps[KEYMAP_US].base[KEY_K] = 'k'; + keymaps[KEYMAP_US].base[KEY_L] = 'l'; + keymaps[KEYMAP_US].base[KEY_SEMICOLON] = ';'; + keymaps[KEYMAP_US].base[KEY_DOUBLE_QUOTES] = '\''; + keymaps[KEYMAP_US].base[KEY_GRAVE] = '`'; + keymaps[KEYMAP_US].base[KEY_BACKSLASH] = '\\'; + keymaps[KEYMAP_US].base[KEY_Z] = 'z'; + keymaps[KEYMAP_US].base[KEY_X] = 'x'; + keymaps[KEYMAP_US].base[KEY_C] = 'c'; + keymaps[KEYMAP_US].base[KEY_V] = 'v'; + keymaps[KEYMAP_US].base[KEY_B] = 'b'; + keymaps[KEYMAP_US].base[KEY_N] = 'n'; + keymaps[KEYMAP_US].base[KEY_M] = 'm'; + keymaps[KEYMAP_US].base[KEY_COMMA] = ','; + keymaps[KEYMAP_US].base[KEY_PERIOD] = '.'; + keymaps[KEYMAP_US].base[KEY_MINUS] = '/'; + keymaps[KEYMAP_US].base[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_US].base[KEY_SPACE] = ' '; + keymaps[KEYMAP_US].base[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_US].base[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_US].base[KEY_KP_LESS] = '<'; + keymaps[KEYMAP_US].base[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_US].shift[KEY_ONE] = '!'; + keymaps[KEYMAP_US].shift[KEY_TWO] = '@'; + keymaps[KEYMAP_US].shift[KEY_THREE] = '#'; + keymaps[KEYMAP_US].shift[KEY_FOUR] = '$'; + keymaps[KEYMAP_US].shift[KEY_FIVE] = '%'; + keymaps[KEYMAP_US].shift[KEY_SIX] = '^'; + keymaps[KEYMAP_US].shift[KEY_SEVEN] = '&'; + keymaps[KEYMAP_US].shift[KEY_EIGHT] = '*'; + keymaps[KEYMAP_US].shift[KEY_NINE] = '('; + keymaps[KEYMAP_US].shift[KEY_ZERO] = ')'; + keymaps[KEYMAP_US].shift[KEY_APOSTROPHE] = '_'; + keymaps[KEYMAP_US].shift[KEY_I_ACC] = '+'; + keymaps[KEYMAP_US].shift[KEY_Q] = 'Q'; + keymaps[KEYMAP_US].shift[KEY_W] = 'W'; + keymaps[KEYMAP_US].shift[KEY_E] = 'E'; + keymaps[KEYMAP_US].shift[KEY_R] = 'R'; + keymaps[KEYMAP_US].shift[KEY_T] = 'T'; + keymaps[KEYMAP_US].shift[KEY_Y] = 'Y'; + keymaps[KEYMAP_US].shift[KEY_U] = 'U'; + keymaps[KEYMAP_US].shift[KEY_I] = 'I'; + keymaps[KEYMAP_US].shift[KEY_O] = 'O'; + keymaps[KEYMAP_US].shift[KEY_P] = 'P'; + keymaps[KEYMAP_US].shift[KEY_LEFT_BRAKET] = '{'; + keymaps[KEYMAP_US].shift[KEY_RIGHT_BRAKET] = '}'; + keymaps[KEYMAP_US].shift[KEY_A] = 'A'; + keymaps[KEYMAP_US].shift[KEY_S] = 'S'; + keymaps[KEYMAP_US].shift[KEY_D] = 'D'; + keymaps[KEYMAP_US].shift[KEY_F] = 'F'; + keymaps[KEYMAP_US].shift[KEY_G] = 'G'; + keymaps[KEYMAP_US].shift[KEY_H] = 'H'; + keymaps[KEYMAP_US].shift[KEY_J] = 'J'; + keymaps[KEYMAP_US].shift[KEY_K] = 'K'; + keymaps[KEYMAP_US].shift[KEY_L] = 'L'; + keymaps[KEYMAP_US].shift[KEY_SEMICOLON] = ':'; + keymaps[KEYMAP_US].shift[KEY_DOUBLE_QUOTES] = '"'; + keymaps[KEYMAP_US].shift[KEY_GRAVE] = '~'; + keymaps[KEYMAP_US].shift[KEY_Z] = 'Z'; + keymaps[KEYMAP_US].shift[KEY_X] = 'X'; + keymaps[KEYMAP_US].shift[KEY_C] = 'C'; + keymaps[KEYMAP_US].shift[KEY_V] = 'V'; + keymaps[KEYMAP_US].shift[KEY_B] = 'B'; + keymaps[KEYMAP_US].shift[KEY_N] = 'N'; + keymaps[KEYMAP_US].shift[KEY_M] = 'M'; + keymaps[KEYMAP_US].shift[KEY_COMMA] = '<'; + keymaps[KEYMAP_US].shift[KEY_PERIOD] = '>'; + keymaps[KEYMAP_US].shift[KEY_MINUS] = '?'; + keymaps[KEYMAP_US].shift[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_US].shift[KEY_SPACE] = ' '; + keymaps[KEYMAP_US].shift[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_US].shift[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_US].shift[KEY_KP_LESS] = '>'; + keymaps[KEYMAP_US].shift[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_US].numlock[KEY_KP_DEC] = '.'; + keymaps[KEYMAP_US].numlock[KEY_KP0] = '0'; + keymaps[KEYMAP_US].numlock[KEY_KP1] = '1'; + keymaps[KEYMAP_US].numlock[KEY_KP2] = '2'; + keymaps[KEYMAP_US].numlock[KEY_KP3] = '3'; + keymaps[KEYMAP_US].numlock[KEY_KP4] = '4'; + keymaps[KEYMAP_US].numlock[KEY_KP5] = '5'; + keymaps[KEYMAP_US].numlock[KEY_KP6] = '6'; + keymaps[KEYMAP_US].numlock[KEY_KP7] = '7'; + keymaps[KEYMAP_US].numlock[KEY_KP8] = '8'; + keymaps[KEYMAP_US].numlock[KEY_KP9] = '9'; +} \ No newline at end of file diff --git a/mentos/src/drivers/mouse.c b/mentos/src/drivers/mouse.c index 1073d79..967a608 100644 --- a/mentos/src/drivers/mouse.c +++ b/mentos/src/drivers/mouse.c @@ -1,11 +1,14 @@ /// MentOS, The Mentoring Operating system project /// @file mouse.c /// @brief Driver for *PS2* Mouses. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details.is distributed under the MIT License. +///// See LICENSE.md for details. +/////! @cond Doxygen_Suppress #include "mouse.h" #include "pic8259.h" +#include "port_io.h" static uint8_t mouse_cycle = 0; @@ -17,159 +20,161 @@ static int32_t mouse_y = (600 / 2); void mouse_install() { - // Enable the auxiliary mouse device. - mouse_waitcmd(1); - outportb(0x64, 0xA8); + // Enable the auxiliary mouse device. + mouse_waitcmd(1); + outportb(0x64, 0xA8); - // Enable the interrupts. - mouse_waitcmd(1); - outportb(0x64, 0x20); - mouse_waitcmd(0); - uint8_t status_byte = (inportb(0x60) | 2); - mouse_waitcmd(1); - outportb(0x64, 0x60); - mouse_waitcmd(1); - outportb(0x60, status_byte); + // Enable the interrupts. + mouse_waitcmd(1); + outportb(0x64, 0x20); + mouse_waitcmd(0); + uint8_t status_byte = (inportb(0x60) | 2); + mouse_waitcmd(1); + outportb(0x64, 0x60); + mouse_waitcmd(1); + outportb(0x60, status_byte); - // Tell the mouse to use default settings. - mouse_write(MOUSE_USE_DEFAULT_SETTINGS); - // Acknowledge. - mouse_read(); + // Tell the mouse to use default settings. + mouse_write(MOUSE_USE_DEFAULT_SETTINGS); + // Acknowledge. + mouse_read(); - // Setup the mouse handler. - // pic8259_irq_install_handler(IRQ_MOUSE, mouse_isr); + // Setup the mouse handler. + // pic8259_irq_install_handler(IRQ_MOUSE, mouse_isr); - mouse_enable(); + mouse_enable(); } void mouse_enable() { - // Enable the mouse interrupts. - pic8259_irq_enable(IRQ_MOUSE); - // Disable the mouse. - mouse_write(MOUSE_ENABLE_PACKET); - // Acknowledge. - mouse_read(); + // Enable the mouse interrupts. + pic8259_irq_enable(IRQ_MOUSE); + // Disable the mouse. + mouse_write(MOUSE_ENABLE_PACKET); + // Acknowledge. + mouse_read(); } void mouse_disable() { - // Disable the mouse interrupts. - pic8259_irq_disable(IRQ_MOUSE); - // Disable the mouse. - mouse_write(MOUSE_DISABLE_PACKET); - // Acknowledge. - mouse_read(); + // Disable the mouse interrupts. + pic8259_irq_disable(IRQ_MOUSE); + // Disable the mouse. + mouse_write(MOUSE_DISABLE_PACKET); + // Acknowledge. + mouse_read(); } void mouse_waitcmd(unsigned char type) { - register unsigned int _time_out = 100000; - if (type == 0) { - // DATA. - while (_time_out--) { - if ((inportb(0x64) & 1) == 1) { - return; - } - } - return; - } else { - while (_time_out--) // SIGNALS - { - if ((inportb(0x64) & 2) == 0) { - return; - } - } - return; - } + register unsigned int _time_out = 100000; + if (type == 0) { + // DATA. + while (_time_out--) { + if ((inportb(0x64) & 1) == 1) { + return; + } + } + return; + } else { + while (_time_out--) // SIGNALS + { + if ((inportb(0x64) & 2) == 0) { + return; + } + } + return; + } } void mouse_write(unsigned char data) { - mouse_waitcmd(1); - outportb(0x64, 0xD4); - mouse_waitcmd(1); - outportb(0x60, data); + mouse_waitcmd(1); + outportb(0x64, 0xD4); + mouse_waitcmd(1); + outportb(0x60, data); } unsigned char mouse_read() { - mouse_waitcmd(0); - return inportb(0x60); + mouse_waitcmd(0); + return inportb(0x60); } -void mouse_isr(register_t *r) +void mouse_isr(pt_regs *f) { - (void)r; - // Get the input bytes. - mouse_bytes[mouse_cycle++] = (char)inportb(0x60); - if (mouse_cycle == 3) { - // Reset the mouse cycle. - mouse_cycle = 0; - // ---------------------------- - // Get the X coordinates. - // ---------------------------- - if ((mouse_bytes[0] & 0x40) == 0) { - // Bit number 4 of the first byte (value 0x10) indicates that - // delta X (the 2nd byte) is a negative number, if it is set. - if ((mouse_bytes[0] & 0x10) == 0) { - mouse_x -= mouse_bytes[1]; - } else { - mouse_x += mouse_bytes[1]; - } - } else { - // Overflow. - mouse_x += mouse_bytes[1] / 2; - } - // ---------------------------- - // Get the Y coordinates. - // ---------------------------- - if ((mouse_bytes[0] & 0x80) == 0) { - // Bit number 5 of the first byte (value 0x20) indicates that - // delta Y (the 3rd byte) is a negative number, if it is set. - if ((mouse_bytes[0] & 0x20) == 0) { - mouse_y -= mouse_bytes[2]; - } else { - mouse_y += mouse_bytes[2]; - } - } else { - // Overflow. - mouse_y -= mouse_bytes[2] / 2; - } - // ---------------------------- - // Apply cursor constraint (800x600). - // ---------------------------- - if (mouse_x <= 0) { - mouse_x = 0; - } else if (mouse_x >= (800 - 16)) { - mouse_x = 800 - 16; - } - if (mouse_y <= 0) { - mouse_y = 0; - } else if (mouse_y >= (600 - 24)) { - mouse_y = 600 - 24; - } - // Print the position. - // dbg_print("\rX: %d | Y: %d\n", mouse_x, mouse_y); + (void)f; + // Get the input bytes. + mouse_bytes[mouse_cycle++] = (char)inportb(0x60); + if (mouse_cycle == 3) { + // Reset the mouse cycle. + mouse_cycle = 0; + // ---------------------------- + // Get the X coordinates. + // ---------------------------- + if ((mouse_bytes[0] & 0x40) == 0) { + // Bit number 4 of the first byte (value 0x10) indicates that + // delta X (the 2nd byte) is a negative number, if it is set. + if ((mouse_bytes[0] & 0x10) == 0) { + mouse_x -= mouse_bytes[1]; + } else { + mouse_x += mouse_bytes[1]; + } + } else { + // Overflow. + mouse_x += mouse_bytes[1] / 2; + } + // ---------------------------- + // Get the Y coordinates. + // ---------------------------- + if ((mouse_bytes[0] & 0x80) == 0) { + // Bit number 5 of the first byte (value 0x20) indicates that + // delta Y (the 3rd byte) is a negative number, if it is set. + if ((mouse_bytes[0] & 0x20) == 0) { + mouse_y -= mouse_bytes[2]; + } else { + mouse_y += mouse_bytes[2]; + } + } else { + // Overflow. + mouse_y -= mouse_bytes[2] / 2; + } + // ---------------------------- + // Apply cursor constraint (800x600). + // ---------------------------- + if (mouse_x <= 0) { + mouse_x = 0; + } else if (mouse_x >= (800 - 16)) { + mouse_x = 800 - 16; + } + if (mouse_y <= 0) { + mouse_y = 0; + } else if (mouse_y >= (600 - 24)) { + mouse_y = 600 - 24; + } + // Print the position. + // pr_default("\rX: %d | Y: %d\n", mouse_x, mouse_y); - // Move the cursor. - // video_set_cursor(mouse_x, mouse_y); + // Move the cursor. + // video_set_cursor(mouse_x, mouse_y); - // Here a problem is detected, if the mouse moves - // Pressed keys are detected. - // Detecting keystrokes. - // Center pressed. - if ((mouse_bytes[0] & 0x04) == 0) { - // dbg_print(LNG_MOUSE_MID); - } - // Right pressed. - if ((mouse_bytes[0] & 0x02) == 0) { - // dbg_print(LNG_MOUSE_RIGHT); - } - // Left pressed. - if ((mouse_bytes[0] & 0x01) == 0) { - // dbg_print(LNG_MOUSE_LEFT); - } - } - pic8259_send_eoi(IRQ_MOUSE); + // Here a problem is detected, if the mouse moves + // Pressed keys are detected. + // Detecting keystrokes. + // Center pressed. + if ((mouse_bytes[0] & 0x04) == 0) { + // pr_default(LNG_MOUSE_MID); + } + // Right pressed. + if ((mouse_bytes[0] & 0x02) == 0) { + // pr_default(LNG_MOUSE_RIGHT); + } + // Left pressed. + if ((mouse_bytes[0] & 0x01) == 0) { + // pr_default(LNG_MOUSE_LEFT); + } + } + pic8259_send_eoi(IRQ_MOUSE); } + +///! @endcond \ No newline at end of file diff --git a/mentos/src/drivers/rtc.c b/mentos/src/drivers/rtc.c new file mode 100644 index 0000000..e7cc972 --- /dev/null +++ b/mentos/src/drivers/rtc.c @@ -0,0 +1,138 @@ +/// MentOS, The Mentoring Operating system project +/// @file rtc.c +/// @brief Real Time Clock (RTC) driver. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "rtc.h" + +#include "pic8259.h" +#include "string.h" +#include "port_io.h" +#include "kernel.h" +#include "isr.h" + +#define CMOS_ADDR 0x70 ///< Addess where we need to write the Address. +#define CMOS_DATA 0x71 ///< Addess where we need to write the Data. + +/// Current global time. +tm_t global_time; +/// Previous global time. +tm_t previous_global_time; +/// Data type is BCD. +int is_bcd; + +static inline unsigned int rtc_are_different(tm_t *t0, tm_t *t1) +{ + if (t0->tm_sec != t1->tm_sec) + return 1; + if (t0->tm_min != t1->tm_min) + return 1; + if (t0->tm_hour != t1->tm_hour) + return 1; + if (t0->tm_mon != t1->tm_mon) + return 1; + if (t0->tm_year != t1->tm_year) + return 1; + if (t0->tm_wday != t1->tm_wday) + return 1; + if (t0->tm_mday != t1->tm_mday) + return 1; + return 0; +} + +/// @brief Check if rtc is updating time currently. +static inline unsigned int is_updating_rtc() +{ + outportb(CMOS_ADDR, 0x0A); + uint32_t status = inportb(CMOS_DATA); + return (status & 0x80U); +} + +static inline unsigned char read_register(unsigned char reg) +{ + outportb(CMOS_ADDR, reg); + return inportb(CMOS_DATA); +} + +static inline void write_register(unsigned char reg, unsigned char value) +{ + outportb(CMOS_ADDR, reg); + outportb(CMOS_DATA, value); +} + +static inline unsigned char bcd2bin(unsigned char bcd) +{ + return ((bcd >> 4u) * 10) + (bcd & 0x0Fu); +} + +static inline void rtc_read_datetime() +{ + if (read_register(0x0Cu) & 0x10u) { + if (is_bcd) { + global_time.tm_sec = bcd2bin(read_register(0x00)); + global_time.tm_min = bcd2bin(read_register(0x02)); + global_time.tm_hour = bcd2bin(read_register(0x04)) + 2; + global_time.tm_mon = bcd2bin(read_register(0x08)); + global_time.tm_year = bcd2bin(read_register(0x09)) + 2000; + global_time.tm_wday = bcd2bin(read_register(0x06)); + global_time.tm_mday = bcd2bin(read_register(0x07)); + } else { + global_time.tm_sec = read_register(0x00); + global_time.tm_min = read_register(0x02); + global_time.tm_hour = read_register(0x04) + 2; + global_time.tm_mon = read_register(0x08); + global_time.tm_year = read_register(0x09) + 2000; + global_time.tm_wday = read_register(0x06); + global_time.tm_mday = read_register(0x07); + } + } +} + +static inline void rtc_handler_isr(pt_regs *f) +{ + static unsigned int first_update = 1; + // Wait until rtc is not updating. + while (is_updating_rtc()) + ; + // Read the values. + rtc_read_datetime(); + if (first_update) { + do { + // Save the previous global time. + previous_global_time = global_time; + // Wait until rtc is not updating. + while (is_updating_rtc()) + ; + // Read the values. + rtc_read_datetime(); + } while (!rtc_are_different(&previous_global_time, &global_time)); + first_update = 0; + } +} + +void gettime(tm_t *time) +{ + // Copy the update time. + memcpy(time, &global_time, sizeof(tm_t)); +} + +void rtc_install(void) +{ + unsigned char status; + + status = read_register(0x0B); + status |= 0x02u; // 24 hour clock + status |= 0x10u; // update ended interrupts + status &= ~0x20u; // no alarm interrupts + status &= ~0x40u; // no periodic interrupt + is_bcd = !(status & 0x04u); // check if data type is BCD + write_register(0x0B, status); + + read_register(0x0C); + + // Install the IRQ. + irq_install_handler(IRQ_REAL_TIME_CLOCK, rtc_handler_isr, "Real Time Clock (RTC)"); + // Enable the IRQ. + pic8259_irq_enable(IRQ_REAL_TIME_CLOCK); +} \ No newline at end of file diff --git a/mentos/src/elf/elf.c b/mentos/src/elf/elf.c index 372b0c4..4478c6b 100644 --- a/mentos/src/elf/elf.c +++ b/mentos/src/elf/elf.c @@ -1,74 +1,401 @@ /// MentOS, The Mentoring Operating system project /// @file elf.c /// @brief Function for multiboot support. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Change the header. +#define __DEBUG_HEADER__ "[ELF ]" + #include "elf.h" -#include "debug.h" + +#include "scheduler.h" +#include "vmem_map.h" +#include "process.h" #include "string.h" -#include "multiboot.h" +#include "stddef.h" +#include "debug.h" +#include "stdio.h" +#include "slab.h" +#include "vfs.h" -/// @brief Data structure containg information about the kernel. -elf_symbols_t kernel_elf; - -/* - * This function grabs a pointer to the array of section headers. - * It then grabs a pointer to the section where all the strings are (.shstrtab) - * (remember that each section header has an offset to this .shstrtab) - * - * Then it's just a matter of iterating sections (checking their names via - * indexing shstrtab) until we find strtab and symtab, which is what we're - * looking for - */ -void build_elf_symbols_from_multiboot(multiboot_info_t *mb) +/// @brief Reads the program header from file. +/// @param file The file from which we extract the program header. +/// @param hdr A pointer to the ELF header. +/// @param idx The index of the program header. +/// @param phdr Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_program_header(vfs_file_t *file, elf_header_t *hdr, unsigned idx, elf_program_header_t *phdr) { - uint32_t i; - elf_section_header_t *sh = (elf_section_header_t *)mb->u.elf_sec.addr; - - /* - * .shstrtab has the names of the sections, - * and sh is an array of sections, which themselves contain - * an index to .shstrtab (for their names) - */ - uint32_t shstrtab = sh[mb->u.elf_sec.shndx].addr; - for (i = 0; i < mb->u.elf_sec.num; i++) { - const char *name = - (const char *)(shstrtab + sh[i].name_offset_in_shstrtab); - if (!strcmp(name, ".strtab")) { - kernel_elf.strtab = (const char *)sh[i].addr; - kernel_elf.strtab_size = sh[i].size; - } - if (!strcmp(name, ".symtab")) { - kernel_elf.symtab = (elf_symbol_t *)sh[i].addr; - kernel_elf.symtab_size = sh[i].size; - } - } + return vfs_read(file, phdr, hdr->phoff + hdr->phentsize * idx, sizeof(elf_program_header_t)); } -/* - * Iterate through all the symbols and look for functions... - * Then, as we find functions, check if the symbol is within that - * function's range (given by value and size) - */ -const char *elf_lookup_symbol(uint32_t addr, elf_symbols_t *elf) +/// @brief Reads the section header from file. +/// @param file The file from which we extract the section header. +/// @param hdr A pointer to the ELF header. +/// @param idx The index of the section header. +/// @param shdr Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_section_header(vfs_file_t *file, elf_header_t *hdr, unsigned idx, elf_section_header_t *shdr) { - int i; - int num_symbols = elf->symtab_size / sizeof(elf_symbol_t); - - for (i = 0; i < num_symbols; i++) { - if (ELF32_ST_TYPE(elf->symtab[i].info) != ELF32_TYPE_FUNCTION) { - continue; - } - - if ((addr >= elf->symtab[i].value) && - (addr < (elf->symtab[i].value + elf->symtab[i].size))) { - const char *name = - (const char *)((uint32_t)elf->strtab + - elf->symtab[i].name_offset_in_strtab); - return name; - } - } - - return NULL; + return vfs_read(file, shdr, hdr->shoff + hdr->shentsize * idx, sizeof(elf_program_header_t)); } + +/// @brief Reads the symbol from file. +/// @param file The file from which we extract the symbol. +/// @param shdr A pointer to the ELF symbol table header. +/// @param idx The index of the symbol. +/// @param symbol Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_symbol(vfs_file_t *file, elf_section_header_t *shdr, unsigned idx, elf_symbol_t *symbol) +{ + // TODO: Here it should use `shdr->entsize`. + return vfs_read(file, symbol, shdr->offset + sizeof(elf_symbol_t) * idx, sizeof(elf_symbol_t)); +} + +/// @brief Reads the symbol from file. +/// @param file The file from which we extract the symbol. +/// @param shdr A pointer to the ELF symbol table header. +/// @param idx The index of the symbol. +/// @param symbol Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_symbol_name(vfs_file_t *file, elf_section_header_t *shdr, unsigned offset, char *name, size_t name_len) +{ + return vfs_read(file, name, shdr->offset + offset, name_len); +} + +static inline int elf_find_section_header(vfs_file_t *file, elf_header_t *hdr, int type, elf_section_header_t *shdr) +{ + for (int i = 0; i < hdr->shnum; ++i) { + if (read_elf_section_header(file, hdr, i, shdr) == -1) { + pr_err("Failed to read section header at index %d.\n", i); + return -1; + } + if (shdr->type == type) + return 0; + memset(shdr, 0, sizeof(elf_section_header_t)); + } + return -1; +} + +static inline char *elf_get_strtable(vfs_file_t *file, elf_header_t *hdr, int ndx) +{ + if (ndx == SHT_NULL) + return NULL; + elf_section_header_t shdr; + if (read_elf_section_header(file, hdr, ndx, &shdr) == -1) { + pr_err("Failed to read section header at index %d.\n", ndx); + return NULL; + } + char *strtable = kmalloc(shdr.size); + memset(strtable, 0, shdr.size); + if (vfs_read(file, strtable, shdr.offset, shdr.size) == -1) { + pr_err("Failed to read the string table at %d.\n", shdr.offset); + return NULL; + } +#if 0 + dbg_putchar('{'); + for (int i = 0; i < shdr.size; ++i) { + pr_debug("[%4d] `%c`", i, strtable[i]); + if (strtable[i] == 0) + pr_debug("\n"); + } + dbg_putchar('}'); + dbg_putchar('\n'); +#endif + return strtable; +} + +static inline int elf_load_sigreturn(task_struct *task, vfs_file_t *file, elf_header_t *hdr) +{ + elf_section_header_t shdr; + if (elf_find_section_header(file, hdr, SHT_SYMTAB, &shdr) == -1) + return -1; + char *strtable = elf_get_strtable(file, hdr, shdr.link); + if (strtable == NULL) + return -1; + uint32_t symtab_entries = shdr.size / sizeof(elf_symbol_t); + elf_symbol_t symbol; + for (int i = 0; i < symtab_entries; ++i) { + if (read_elf_symbol(file, &shdr, i, &symbol) == -1) { + pr_err("Failed to read the elf symbol at index %d.\n", i); + break; + } + if (strcmp(strtable + symbol.name, "sigreturn") == 0) { + task->sigreturn_eip = symbol.value; + pr_debug("Found `sigreturn` at index %d with EIP = %p.\n", i, symbol.value); + kfree(strtable); + return 0; + } + } + pr_emerg("Failed to find `sigreturn`!\n"); + kfree(strtable); + return -1; +} + +/// @brief Loads an ELF executable. +/// @param task The task for which we load the ELF. +/// @param file The ELF file. +/// @param hdr The header of the ELF file. +/// @return The ELF entry. +static inline int elf_load_exec(task_struct *task, vfs_file_t *file, elf_header_t *hdr) +{ + elf_program_header_t phdr; + pr_debug(" Type | Mem. Size | File Size | VADDR\n"); + for (int idx = 0; idx < hdr->phnum; ++idx) { + // Get the header. + if (read_elf_program_header(file, hdr, idx, &phdr) == -1) { + pr_err("Failed to read program header at index %d.\n", idx); + return -1; + } + pr_debug(" %-9s | %9s | %9s | 0x%08x - 0x%08x\n", + elf_type_to_string(phdr.type), + to_human_size(phdr.memsz), + to_human_size(phdr.filesz), + phdr.vaddr, phdr.vaddr + phdr.memsz); + if (phdr.type == PT_LOAD) { + uint32_t virt_addr = create_vm_area(task->mm, phdr.vaddr, phdr.memsz, MM_USER | MM_RW | MM_COW, GFP_KERNEL); + virt_map_page_t *vpage = virt_map_alloc(phdr.memsz); + uint32_t dst_addr = virt_map_vaddress(task->mm, vpage, virt_addr, phdr.memsz); + + // Load the memory area. + vfs_read(file, (void *)dst_addr, phdr.offset, phdr.filesz); + + if (phdr.memsz > phdr.filesz) { + uint32_t zmem_sz = phdr.memsz - phdr.filesz; + memset((void *)(dst_addr + phdr.filesz), 0, zmem_sz); + } + virt_unmap_pg(vpage); + } + } + return 0; +} + +static inline void dump_elf_section_headers(vfs_file_t *file, elf_header_t *hdr) +{ + char *strtable = elf_get_strtable(file, hdr, hdr->shstrndx); + if (strtable == NULL) + return; + pr_debug("[Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n"); + elf_section_header_t shdr; + for (int i = 0; i < hdr->shnum; ++i) { + if (read_elf_section_header(file, hdr, i, &shdr) == -1) { + pr_err("Failed to read section header at index %d.\n", i); + } + pr_debug("[%2d] %-20s %-15s %08x %06x %06x %2u %3u %2u %3u %2u\n", + i, strtable + shdr.name, elf_section_header_type_to_string(shdr.type), + shdr.addr, shdr.offset, shdr.size, + shdr.entsize, shdr.flags, shdr.link, shdr.info, shdr.addralign); + } + kfree(strtable); +} + +static inline void dump_elf_symbol_table(vfs_file_t *file, elf_header_t *hdr) +{ + elf_section_header_t shdr; + if (elf_find_section_header(file, hdr, SHT_SYMTAB, &shdr) == -1) + return; + + char *strtable = elf_get_strtable(file, hdr, shdr.link); + if (strtable == NULL) + return; + + // Count the number of entries. + uint32_t symtab_entries = shdr.size / sizeof(elf_symbol_t); + pr_debug("Symbol table '.symtab' contains %d entries (%d/%d):\n", symtab_entries, shdr.size, sizeof(elf_symbol_t)); + pr_debug("[ Nr ] Value Size Type Bind Vis Ndx Name\n"); + elf_symbol_t symbol; + for (int i = 0; i < symtab_entries; ++i) { + if (read_elf_symbol(file, &shdr, i, &symbol) == -1) { + pr_err("Failed to read the elf symbol at index %d.\n", i); + } + pr_debug("[%4d] %08x %5d %-7s %-6s %-8s %3d %s\n", i, symbol.value, symbol.size, + elf_symbol_type_to_string(ELF32_ST_TYPE(symbol.info)), + elf_symbol_bind_to_string(ELF32_ST_BIND(symbol.info)), + "-", + symbol.ndx, + strtable + symbol.name); + } + kfree(strtable); +} + +int elf_load_file(task_struct *task, vfs_file_t *file, uint32_t *entry) +{ + // Open the file. + if (file == NULL) { + pr_err("Cannot find executable!"); + return 0; + } + elf_header_t hdr; + // Set the reading position at the beginning of the file. + vfs_lseek(file, 0, SEEK_SET); + // Read the header. + if (vfs_read(file, &hdr, 0, sizeof(elf_header_t)) != -1) { + if (elf_check_file_header(&hdr)) { + pr_debug("Version : 0x%x\n", hdr.version); + pr_debug("Entry : 0x%x\n", hdr.entry); + pr_debug("Headers offset : 0x%x\n", hdr.phoff); + pr_debug("Headers count : %d\n", hdr.phnum); + //dump_elf_section_headers(file, &hdr); + //dump_elf_symbol_table(file, &hdr); + if (hdr.type == ET_EXEC) { + if (elf_load_sigreturn(task, file, &hdr) == -1) { + return 0; + } + if (elf_load_exec(task, file, &hdr) == -1) { + return 0; + } + // Set the entry. + (*entry) = hdr.entry; + return 1; + } else { + pr_err("ELF type not supported.\n"); + } + } else { + pr_err("ELF file cannot be loaded.\n"); + } + } else { + pr_err("Filed to read ELF header.\n"); + } + return 0; +} + +int elf_check_file_type(vfs_file_t *file, Elf_Type type) +{ + // Open the file. + if (file == NULL) { + pr_err("Cannot find executable!"); + return 0; + } + // Set the reading position at the beginning of the file. + vfs_lseek(file, 0, SEEK_SET); + // Prepare the elf header. + elf_header_t hdr; + // By default we return failure. + int ret = 0; + // Read the header and check the file type. + if (vfs_read(file, &hdr, 0, sizeof(elf_header_t)) != -1) + if (elf_check_file_header(&hdr)) + ret = hdr.type == type; + // Set the reading position at the beginning of the file. + vfs_lseek(file, 0, SEEK_SET); + return ret; +} + +int elf_check_file_header(elf_header_t *hdr) +{ + if (!elf_check_magic_number(hdr)) { + pr_err("Invalid ELF File.\n"); + return 0; + } + if (hdr->ident[EI_CLASS] != ELFCLASS32) { + pr_err("Unsupported ELF File Class.\n"); + return 0; + } + if (hdr->ident[EI_DATA] != ELFDATA2LSB) { + pr_err("Unsupported ELF File byte order.\n"); + return 0; + } + if (hdr->machine != EM_386) { + pr_err("Unsupported ELF File target.\n"); + return 0; + } + if (hdr->ident[EI_VERSION] != EV_CURRENT) { + pr_err("Unsupported ELF File version.\n"); + return 0; + } + if (hdr->type != ET_EXEC) { + pr_err("Unsupported ELF File type.\n"); + return 0; + } + return 1; +} + +int elf_check_magic_number(elf_header_t *hdr) +{ + if (!hdr) + return 0; + if (hdr->ident[EI_MAG0] != ELFMAG0) { + pr_err("ELF Header EI_MAG0 incorrect.\n"); + return 0; + } + if (hdr->ident[EI_MAG1] != ELFMAG1) { + pr_err("ELF Header EI_MAG1 incorrect.\n"); + return 0; + } + if (hdr->ident[EI_MAG2] != ELFMAG2) { + pr_err("ELF Header EI_MAG2 incorrect.\n"); + return 0; + } + if (hdr->ident[EI_MAG3] != ELFMAG3) { + pr_err("ELF Header EI_MAG3 incorrect.\n"); + return 0; + } + return 1; +} + +const char *elf_type_to_string(int type) +{ + if (type == PT_LOAD) + return "LOAD"; + if (type == PT_DYNAMIC) + return "DYNAMIC"; + if (type == PT_INTERP) + return "INTERP"; + if (type == PT_NOTE) + return "NOTE"; + if (type == PT_SHLIB) + return "SHLIB"; + if (type == PT_PHDR) + return "PHDR"; + if (type == PT_EH_FRAME) + return "EH_FRAME"; + if (type == PT_GNU_STACK) + return "GNU_STACK"; + if (type == PT_GNU_RELRO) + return "GNU_RELRO"; + if (type == PT_LOPROC) + return "LOPROC"; + if (type == PT_HIPROC) + return "HIPROC"; + return "NULL"; +} + +const char *elf_section_header_type_to_string(int type) +{ + if (type == SHT_PROGBITS) + return "PROGBITS"; + if (type == SHT_SYMTAB) + return "SYMTAB"; + if (type == SHT_STRTAB) + return "STRTAB"; + if (type == SHT_RELA) + return "RELA"; + if (type == SHT_NOBITS) + return "NOBITS"; + if (type == SHT_REL) + return "REL"; + return "NULL"; +} + +const char *elf_symbol_type_to_string(int type) +{ + if (type == STT_NOTYPE) + return "NOTYPE"; + if (type == STT_OBJECT) + return "OBJECT"; + if (type == STT_FUNC) + return "FUNC"; + return "-1"; +} + +const char *elf_symbol_bind_to_string(int bind) +{ + if (bind == STB_LOCAL) + return "LOCAL"; + if (bind == STB_GLOBAL) + return "GLOBAL"; + if (bind == STB_WEAK) + return "WEAK"; + return "-1"; +} \ No newline at end of file diff --git a/mentos/src/fs/fcntl.c b/mentos/src/fs/fcntl.c deleted file mode 100644 index 68e3b80..0000000 --- a/mentos/src/fs/fcntl.c +++ /dev/null @@ -1,30 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcntl.c -/// @brief Implementation of functions fcntl() and open(). -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "fcntl.h" -#include "string.h" -#include "vfs.h" -#include "limits.h" - -int remove(const char *pathname) -{ - char absolute_path[PATH_MAX]; - strcpy(absolute_path, pathname); - if (pathname[0] != '/') { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) { - return -1; - } - - if (mountpoint_list[mp_id].operations.remove_f == NULL) { - return -1; - } - - return mountpoint_list[mp_id].operations.remove_f(absolute_path); -} diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index c80762e..b7c9dab 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -1,455 +1,681 @@ /// MentOS, The Mentoring Operating system project /// @file initrd.c /// @brief Headers of functions for initrd filesystem. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "stdio.h" +#include "assert.h" +#include "syscall.h" +#include "sys/module.h" +#include "system/panic.h" #include "vfs.h" #include "errno.h" #include "debug.h" -#include "shell.h" #include "kheap.h" #include "fcntl.h" -#include "libgen.h" -#include "bitops.h" +#include "sys/bitops.h" #include "initrd.h" #include "string.h" +#include "stdio.h" +#include "libgen.h" +#include "fcntl.h" -char *module_start[MAX_MODULES]; +/// Maximum length of name in INITRD. +#define INITRD_NAME_MAX 255U +/// Maximum number of files in INITRD. +#define INITRD_MAX_FILES 128U +/// Maximum size of files in INITRD. +#define INITRD_MAX_FS_SIZE 1048576 -initrd_t *fs_specs; -initrd_file_t *fs_headers; -unsigned int fs_end; +/// @brief Information concerning a file. +typedef struct initrd_file_t { + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The inode of the file. + unsigned int inode; + /// The name of the file. + char fileName[INITRD_NAME_MAX]; + /// The type of the file. + short int file_type; + /// The permissions mask. + unsigned int mask; + /// The id of the owner. + unsigned int uid; + /// The id of the group. + unsigned int gid; + /// Time of last access. + unsigned int atime; + /// Time of last data modification. + unsigned int mtime; + /// Time of last status change. + unsigned int ctime; + /// Offset of the starting address. + unsigned int offset; + /// Dimension of the file. + unsigned int length; +} __attribute__((aligned(16))) initrd_file_t; -/// The list of file descriptors. -initrd_fd ird_descriptors[MAX_INITRD_DESCRIPTORS]; +/// @brief The details regarding the filesystem. +/// @brief Contains the number of files inside the initrd filesystem. +static struct initrd_t { + /// Number of files. + unsigned int nfiles; + /// List of headers. + initrd_file_t headers[INITRD_MAX_FILES]; +} fs_specs __attribute__((aligned(16))); -/// The currently opened file descriptor. -unsigned int cur_irdfd; +static vfs_file_t *initrd_create_file_struct( + ino_t ino, + const char *name, + size_t size, + int flags); -static inline initrd_file_t *get_initrd_file(const char *path) +/// @brief Searches for the file at the given path. +/// @param path The path where to search the file. +/// @return The file if found, NULL otherwise. +static inline initrd_file_t *initrd_find_file(const char *path) { - for (uint32_t i = 0; i < MAX_FILES; ++i) { - // Discard the headers which has a different name. - if (strcmp(path, fs_headers[i].fileName) == 0) { - return &(fs_headers[i]); - } - } - - return NULL; + for (unsigned int i = 0; i < INITRD_MAX_FILES; ++i) + if (strcmp(path, fs_specs.headers[i].fileName) == 0) + return &(fs_specs.headers[i]); + return NULL; } -static inline size_t get_free_header(initrd_file_t **free_header) +/// @brief Searches for the file at the given path. +/// @param path The path where to search the file. +/// @return The file if found, NULL otherwise. +static inline ino_t initrd_find_inode(const char *path) { - for (size_t i = 0; i < MAX_FILES; ++i) { - // TODO: If the header has type 0, then I suppose it is not used. - if (fs_headers[i].file_type == 0) { - (*free_header) = &(fs_headers[i]); - return i; - } - } - - return 0; + for (unsigned int i = 0; i < INITRD_MAX_FILES; ++i) + if (strcmp(path, fs_specs.headers[i].fileName) == 0) + return fs_specs.headers[i].inode; + return -1; } -uint32_t initfs_init() +static inline initrd_file_t *get_free_header() { - fs_end = 0; - fs_specs = (initrd_t *)module_start[0]; - fs_headers = (initrd_file_t *)(module_start[0] + sizeof(initrd_t)); - - for (int i = 0; i < MAX_INITRD_DESCRIPTORS; ++i) { - ird_descriptors[i].file_descriptor = -1; - ird_descriptors[i].cur_pos = 0; - } - cur_irdfd = 0; - printf(" * Number of Files: %d\n", fs_specs->nfiles); - fs_end = fs_headers[(fs_specs->nfiles) - 1].offset + - fs_headers[(fs_specs->nfiles) - 1].length; - printf(" * Filesystem end : %d\n", fs_end); - //dump_initrd_fs(); - - return fs_specs->nfiles; + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) + if (fs_specs.headers[i].file_type == 0) + return &(fs_specs.headers[i]); + return NULL; } -DIR *initfs_opendir(const char *path) +static inline bool_t check_if_occupied(size_t offset) { - initrd_file_t *direntry = get_initrd_file(path); - - if ((direntry == NULL) && (strcmp(path, "/") != 0)) { - dbg_print("Cannot find '%s'\n", path); - - return NULL; - } - - DIR *pdir = kmalloc(sizeof(DIR)); - pdir->fd = -1; - pdir->cur_entry = 0; - strcpy(pdir->path, path); - - return pdir; + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *h = &fs_specs.headers[i]; + if ((h->file_type != 0) && (offset >= h->offset) && (offset <= (h->offset + h->length))) { + return true; + } + } + return false; } -int initfs_closedir(DIR *dirp) +static inline int get_free_slot_offset() { - if (dirp != NULL) { - kfree(dirp); - } - - return 0; + int offset = sizeof(struct initrd_t); + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *h = &fs_specs.headers[i]; + if ((h->file_type != 0) && (offset >= h->offset) && (offset <= (h->offset + h->length))) { + offset = (int)(h->offset + h->length); + continue; + } + return offset; + } + return -1; } -dirent_t *initrd_readdir(DIR *dirp) +// TODO: doxygen comment. +static void dump_initrd_fs(void) { - if (dirp->cur_entry >= MAX_FILES) { - return NULL; - } - - for (; dirp->cur_entry < MAX_FILES; ++dirp->cur_entry) { - initrd_file_t *entry = &fs_headers[dirp->cur_entry]; - if (entry->fileName[0] == '\0') { - continue; - } - // Get the directory of the file. - char *filedir = dirname(entry->fileName); - - // Check if directory path and file directory are the same, or if - // the directory is the root and the file directory is dot. - if (strcmp(dirp->path, filedir) == 0) { - dirp->entry.d_ino = dirp->cur_entry; - dirp->entry.d_type = entry->file_type; - strcpy(dirp->entry.d_name, entry->fileName); - ++dirp->cur_entry; - - return &(dirp->entry); - } - } - - return NULL; + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *file = &fs_specs.headers[i]; + pr_debug("[%3d][%c%c%c%c%c%c%c%c%c%c] %s\n", + i, + dt_char_array[file->file_type], + (file->mask & S_IRUSR) != 0 ? 'r' : '-', + (file->mask & S_IWUSR) != 0 ? 'w' : '-', + (file->mask & S_IXUSR) != 0 ? 'x' : '-', + (file->mask & S_IRGRP) != 0 ? 'r' : '-', + (file->mask & S_IWGRP) != 0 ? 'w' : '-', + (file->mask & S_IXGRP) != 0 ? 'x' : '-', + (file->mask & S_IROTH) != 0 ? 'r' : '-', + (file->mask & S_IWOTH) != 0 ? 'w' : '-', + (file->mask & S_IXOTH) != 0 ? 'x' : '-', + file->fileName); + } } -int initrd_mkdir(const char *path, mode_t mode) +/// @brief Reads contents of the directories to a dirent buffer, updating +/// the offset and returning the number of written bytes in the buffer, +/// it assumes that all paths are well-formed. +/// @param file The directory handler. +/// @param dirp The buffer where the data should be written. +/// @param doff The offset inside the buffer where the data should be written. +/// @param count The maximum length of the buffer. +/// @return The number of written bytes in the buffer. +static int initrd_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count) { - (void)mode; - initrd_file_t *direntry = NULL; + if (file->ino >= INITRD_MAX_FILES) { + return -1; + } + memset(dirp, 0, count); - // Check if the directory already exists. - direntry = get_initrd_file(path); - if (direntry != NULL) { - printf("initrd_mkdir: cannot create directory '%s': " - "File exists\n\n", - path); - return -1; - } - // Check if the directories before it exist. - if ((strcmp(dirname(path), ".") != 0) && - (strcmp(dirname(path), "/") != 0)) { - dbg_print("initrd_mkdir: %s\n", dirname(path)); - direntry = get_initrd_file(dirname(path)); - if (direntry == NULL) { - printf("initrd_mkdir: cannot create directory '%s': " - "No such file or directory\n\n", - path); + initrd_file_t *tdir = &fs_specs.headers[file->ino]; + int len = strlen(tdir->fileName); + size_t written = 0; + off_t current = 0; - return -1; - } - if ((direntry->file_type != FS_DIRECTORY) && - (direntry->file_type != FS_MOUNTPOINT)) { - printf("initrd_mkdir: cannot create directory '%s': " - "Not a directory\n\n", - path); - - return -1; - } - } - // Get a free header. - initrd_file_t *free_header = NULL; - get_free_header(&free_header); - if (free_header == NULL) { - printf("initrd_mkdir: cannot create directory '%s': " - "Maximum number of headers reached\n\n", - path); - - return -1; - } - // Create the directory. - free_header->magic = 0xBF; - strcpy(free_header->fileName, path); - free_header->file_type = FS_DIRECTORY; - free_header->uid = current_user.uid; - free_header->offset = ++fs_end; - free_header->length = 0; - // Increase the number of files. - ++fs_specs->nfiles; - - return 1; + char *parent = NULL; + for (off_t it = 0; (it < INITRD_MAX_FILES) && (written < count); ++it) { + initrd_file_t *entry = &fs_specs.headers[it]; + if (entry->fileName[0] == '\0') { + continue; + } + // If the entry is the directory itself, skip. + if (strcmp(tdir->fileName, entry->fileName) == 0) { + continue; + } + // Get the parent directory. + parent = dirname(entry->fileName); + // Check if the entry is inside the directory. + if (strcmp(tdir->fileName, parent) != 0) { + continue; + } + // Skip if already provided. + if (current++ < doff) { + continue; + } + if (*(entry->fileName + len) == '/') + ++len; + // Write on current dirp. + dirp->d_ino = it; + dirp->d_type = entry->file_type; + strcpy(dirp->d_name, entry->fileName + len); + dirp->d_off = sizeof(dirent_t); + dirp->d_reclen = sizeof(dirent_t); + // Increment the written counter. + written += sizeof(dirent_t); + // Move to next writing position. + dirp += 1; + } + return written; } -int initrd_rmdir(const char *path) +/// @brief Creates a new directory. +/// @param path The path to the new directory. +/// @param mode The file mode. +/// @return 0 if success. +static int initrd_mkdir(const char *path, mode_t mode) { - initrd_file_t *direntry = NULL; - - // Check if the directory exists. - direntry = get_initrd_file(path); - if (direntry == NULL) { - //errno = ENOENT; - return -1; - } - - if ((direntry->file_type != FS_DIRECTORY)) { - //errno = ENOTDIR; - return -1; - } - - for (int i = 0; i < MAX_FILES; ++i) { - initrd_file_t *entry = &fs_headers[i]; - if (entry->fileName[0] == '\0') { - continue; - } - // Get the directory of the file. - char *filedir = dirname(entry->fileName); - // Check if directory path and file directory are the same. - if (strcmp(direntry->fileName, filedir) == 0) { - //errno = ENOTEMPTY; - return -1; - } - } - // Remove the directory. - direntry->magic = 0; - memset(direntry->fileName, 0, NAME_MAX); - direntry->file_type = 0; - direntry->uid = 0; - direntry->offset = 0; - direntry->length = 0; - // Decrease the number of files. - --fs_specs->nfiles; - - return 0; + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + return -EPERM; + } + initrd_file_t *direntry = initrd_find_file(path); + if (direntry != NULL) { + return -EEXIST; + } + // Check if the directories before it exist. + char *parent = dirname(path); + if ((strcmp(parent, ".") != 0) && (strcmp(parent, "/") != 0)) { + direntry = initrd_find_file(parent); + if (direntry == NULL) { + return -ENOENT; + } + if (direntry->file_type != DT_DIR) { + return -ENOTDIR; + } + } + // Get a free header. + initrd_file_t *initrd_file = get_free_header(); + if (!initrd_file) { + pr_err("Cannot create initrd_file for `%s`...\n", path); + return -ENFILE; + } + int offset = get_free_slot_offset(); + if (offset < 0) { + pr_err("There are no free slot available for `%s`...\n", path); + return -ENFILE; + } + // Create the file. + initrd_file->magic = 0xBF; + strcpy(initrd_file->fileName, path); + initrd_file->file_type = DT_DIR; + initrd_file->uid = scheduler_get_current_process()->uid; + initrd_file->gid = scheduler_get_current_process()->uid; + initrd_file->offset = offset; + initrd_file->length = 0; + initrd_file->atime = sys_time(NULL); + initrd_file->mtime = sys_time(NULL); + initrd_file->ctime = sys_time(NULL); + // Increase the number of files. + ++fs_specs.nfiles; + return 0; } -int initfs_open(const char *path, int flags, ...) +/// @brief Removes a directory. +/// @param path The path to the directory. +/// @return 0 if success. +static int initrd_rmdir(const char *path) { - // If we have reached the maximum number of descriptors, just try to find - // a non-used one. - if (cur_irdfd >= MAX_INITRD_DESCRIPTORS) { - // Reset the current ird file descriptor. - cur_irdfd = 0; - while ((ird_descriptors[cur_irdfd].file_descriptor != -1) && - (cur_irdfd < MAX_INITRD_DESCRIPTORS)) { - ++cur_irdfd; - } - } - - for (uint32_t it = 0; it < fs_specs->nfiles; ++it) { - // Discard the headers which has a different name. - if (strcmp(path, fs_headers[it].fileName) != 0) { - continue; - } - - // However, if the name is the same, but the file type is different, - // stop the function and return failure value. - if ((fs_headers[it].file_type == FS_DIRECTORY) || - (fs_headers[it].file_type == FS_MOUNTPOINT)) { - //errno = EISDIR; - return -1; - } - - if (has_flag(flags, O_CREAT)) { - //errno = EEXIST; - return -1; - } - - // Otherwise, if the file is correct, update - ird_descriptors[cur_irdfd].file_descriptor = it; - ird_descriptors[cur_irdfd].cur_pos = 0; - if (has_flag(flags, O_APPEND)) { - ird_descriptors[cur_irdfd].cur_pos = fs_headers[it].length; - } - - return cur_irdfd++; - } - if (has_flag(flags, O_CREAT)) { - // Check if the directories before it exist. - if ((strcmp(dirname(path), ".") != 0) && - (strcmp(dirname(path), "/") != 0)) { - initrd_file_t *direntry = get_initrd_file(dirname(path)); - if (direntry == NULL) { - //errno = ENOENT; - return -1; - } - } - // Get a free header. - initrd_file_t *free_header = NULL; - size_t fd = get_free_header(&free_header); - if (free_header == NULL) { - //errno = ENFILE; - return -1; - } - // Create the file. - free_header->magic = 0xBF; - strcpy(free_header->fileName, path); - free_header->file_type = FS_FILE; - free_header->uid = current_user.uid; - free_header->offset = ++fs_end; - free_header->length = 0; - // Set the descriptor. - ird_descriptors[cur_irdfd].file_descriptor = fd; - ird_descriptors[cur_irdfd].cur_pos = 0; - // Increase the number of files. - ++fs_specs->nfiles; - - return cur_irdfd++; - } - - //errno = ENOENT; - return -1; + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + pr_err("initrd_rmdir(%s): Cannot remove `.` or `..`.\n", path); + return -EPERM; + } + // Check if the directory exists. + initrd_file_t *direntry = initrd_find_file(path); + if (direntry == NULL) { + pr_err("initrd_rmdir(%s): Cannot find the directory.\n", path); + return -ENOENT; + } + // Check the type. + if (direntry->file_type != DT_DIR) { + pr_err("initrd_rmdir(%s): The entry is not a directory.\n", path); + return -ENOTDIR; + } + for (int i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *entry = &fs_specs.headers[i]; + if (entry->fileName[0] == '\0') { + continue; + } + // Get the directory of the file. + char *filedir = dirname(entry->fileName); + // Check if directory path and file directory are the same. + if (strcmp(direntry->fileName, filedir) == 0) { + pr_err("initrd_rmdir(%s): The directory is not empty.\n", path); + return -ENOTEMPTY; + } + } + // Remove the directory. + direntry->magic = 0; + memset(direntry->fileName, 0, NAME_MAX); + direntry->file_type = 0; + direntry->uid = 0; + direntry->offset = 0; + direntry->length = 0; + // Decrease the number of files. + --fs_specs.nfiles; + return 0; } -int initfs_remove(const char *path) +/// @brief Open the file at the given path and returns its file descriptor. +/// @param path The path to the file. +/// @param flags The flags used to determine the behavior of the function. +/// @param mode The mode with which we open the file. +/// @return The file descriptor of the opened file, otherwise returns -1. +static vfs_file_t *initrd_open(const char *path, int flags, mode_t mode) { - initrd_file_t *file = get_initrd_file(path); - - if (file == NULL) { - return -1; - } - - if (file->file_type != FS_FILE) { - return -1; - } - - // Remove the directory. - file->magic = 0; - memset(file->fileName, 0, NAME_MAX); - file->file_type = 0; - file->uid = 0; - file->offset = 0; - file->length = 0; - // Decrease the number of files. - --fs_specs->nfiles; - - return 0; + initrd_file_t *initrd_file = initrd_find_file(path); + if (initrd_file != NULL) { + // Check if it is a directory. + if (flags == (O_RDONLY | O_DIRECTORY)) { + if (initrd_file->file_type != DT_DIR) { + pr_err("Is not a directory `%s`...\n", path); + errno = ENOTDIR; + return NULL; + } + // Create the file structure. + vfs_file_t *vfs_file = initrd_create_file_struct( + initrd_file->inode, + initrd_file->fileName, + initrd_file->length, + DT_DIR); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening directory `%s`...\n", path); + errno = ENOMEM; + return NULL; + } + // Update file access. + initrd_file->atime = sys_time(NULL); + return vfs_file; + } else if (initrd_file->file_type == DT_DIR) { + pr_err("Is a directory `%s`...\n", path); + errno = EISDIR; + return NULL; + } + // Check if the open has to create. + if (flags & O_CREAT) { + pr_err("Cannot create, it exists `%s`...\n", path); + errno = EEXIST; + return NULL; + } + // Create the file structure. + vfs_file_t *vfs_file = initrd_create_file_struct( + initrd_file->inode, + initrd_file->fileName, + initrd_file->length, + DT_REG); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENOMEM; + return NULL; + } + // Update file access. + initrd_file->atime = sys_time(NULL); + return vfs_file; + } + if (flags & O_CREAT) { + // Check if the parent directory exists. + char *dir = dirname(path); + if ((strcmp(dir, ".") != 0) && (strcmp(dir, "/") != 0)) { + if (initrd_find_file(dir) == NULL) { + errno = ENOENT; + return NULL; + } + } + // Get a free header. + initrd_file = get_free_header(); + if (!initrd_file) { + pr_err("Cannot create initrd_file for `%s`...\n", path); + errno = ENFILE; + return NULL; + } + int offset = get_free_slot_offset(); + if (offset < 0) { + pr_err("There are no free slot available for `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Create the file. + initrd_file->magic = 0xBF; + strcpy(initrd_file->fileName, path); + initrd_file->file_type = DT_REG; + initrd_file->mask = S_IRWXU; + initrd_file->uid = scheduler_get_current_process()->uid; + initrd_file->gid = scheduler_get_current_process()->uid; + initrd_file->offset = offset; + initrd_file->length = 0; + initrd_file->atime = sys_time(NULL); + initrd_file->mtime = sys_time(NULL); + initrd_file->ctime = sys_time(NULL); + // Increase the number of files. + ++fs_specs.nfiles; + // Create the file structure. + vfs_file_t *vfs_file = initrd_create_file_struct( + initrd_file->inode, + initrd_file->fileName, + initrd_file->length, + DT_REG); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENOMEM; + return NULL; + } + return vfs_file; + } + errno = ENOENT; + return NULL; } -ssize_t initfs_read(int fildes, char *buf, size_t nbyte) +/// @brief Deletes the file at the given path. +/// @param path The path to the file. +/// @return On success, zero is returned. On error, -1 is returned. +static int initrd_unlink(const char *path) { - // If the number of byte to read is zero, skip. - if (nbyte == 0) { - return 0; - } - - // Get the file descriptor of the file. - int lfd = ird_descriptors[fildes].file_descriptor; - - // Get the current position. - int read_pos = ird_descriptors[fildes].cur_pos; - - // Get the legnth of the file. - int file_size = fs_headers[lfd].length; - - // Get the begin of the file. - char *file_start = (module_start[0] + fs_headers[lfd].offset); - - // Declare an iterator. - size_t it = 0; - - while ((it < nbyte) && (read_pos < file_size)) { - *buf++ = file_start[read_pos]; - ++read_pos; - ++it; - } - - ird_descriptors[fildes].cur_pos = read_pos; - if (read_pos == file_size) { - return EOF; - } - - return nbyte; + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + return -EPERM; + } + // Check if the directory exists. + initrd_file_t *file = initrd_find_file(path); + if (file == NULL) { + pr_err("initrd_unlink(%s): Cannot find the file.\n", path); + return -ENOENT; + } + if (file->file_type != DT_REG) { + if (file->file_type == DT_DIR) { + pr_err("initrd_unlink(%s): The file is a directory.\n", path); + return -EISDIR; + } + pr_err("initrd_unlink(%s): The file is not a regular file.\n", path); + return -EACCES; + } + // Remove the directory. + file->magic = 0; + memset(file->fileName, 0, NAME_MAX); + file->file_type = 0; + file->uid = 0; + file->offset = 0; + file->length = 0; + // Decrease the number of files. + --fs_specs.nfiles; + return 0; } -int initrd_stat(const char *path, stat_t *stat) +/// @brief Reads from the file identified by the file descriptor. +/// @param file The file. +/// @param buf Buffer where the read content must be placed. +/// @param offset Offset from which we start reading from the file. +/// @param nbyte The number of bytes to read. +/// @return The number of red bytes. +static ssize_t initrd_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) { - int i; - i = 0; - - while (i < MAX_FILES) { - if (!strcmp(path, fs_headers[i].fileName)) { - stat->st_uid = fs_headers[i].uid; - stat->st_size = fs_headers[i].length; - - break; - } - i++; - } - //dbg_print("Initrd stat function\n"); - //buf->st_uid = 33; - if (i == MAX_FILES) { - return -1; - } else { - return 0; - } + // If the number of byte to read is zero, skip. + if (nbyte == 0) { + return 0; + } + // Get the file descriptor of the file. + int lfd = file->ino; + // Get the current position. + int read_pos = offset; + // Get the length of the file. + int file_size = (int)fs_specs.headers[lfd].length; + // If we have reached the end of the file, return. + if (read_pos == file_size) { + return EOF; + } + // Get the begin of the file. + char *file_start = (char *)(modules[0].mod_start + fs_specs.headers[lfd].offset); + // Declare an iterator, used afterward to return the number of bytes read. + ssize_t it = 0; + while ((it < nbyte) && (read_pos < file_size)) { + *buf++ = file_start[read_pos]; + ++read_pos; + ++it; + } + return it; } -ssize_t initrd_write(int fildes, const void *buf, size_t nbyte) +static int _initrd_stat(const initrd_file_t *file, stat_t *stat) { - // If the number of byte to write is zero, skip. - if (nbyte == 0) { - return 0; - } - - // Make a copy of the buffer. - char *tmp = (char *)kmalloc(strlen(buf) * sizeof(char)); - strcpy(tmp, buf); - - // Get the file descriptor of the file. - int lfd = ird_descriptors[fildes].file_descriptor; - - printf("Please wait, im writing the world...\n"); - printf("And the world begun with those words: %s and his mark his: %d\n", - tmp, lfd); - // Get the begin of the file. - char *file_start = (module_start[0] + fs_headers[lfd].offset + - ird_descriptors[fildes].cur_pos); - // Declare an iterator. - size_t it = 0; - while (it <= nbyte) { - file_start[it] = tmp[it]; - ++it; - } - - // Increment the length of the file. - fs_headers[lfd].length = fs_headers[lfd].length + it; - // Free the memory of the temporary file. - kfree(tmp); - // Return the number of written bytes. - - return it; + stat->st_dev = 0; + stat->st_ino = file - fs_specs.headers; + stat->st_mode = file->mask; + stat->st_uid = file->uid; + stat->st_gid = file->gid; + stat->st_atime = file->atime; + stat->st_mtime = file->mtime; + stat->st_ctime = file->ctime; + stat->st_size = file->length; + return 0; } -int initrd_close(int fildes) +/// @brief Retrieves information concerning the file at the given position. +/// @param file The file struct. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int initrd_fstat(vfs_file_t *file, stat_t *stat) { - ird_descriptors[fildes].file_descriptor = -1; - ird_descriptors[fildes].cur_pos = 0; - - return 0; + return _initrd_stat(&fs_specs.headers[file->ino], stat); } -size_t initrd_nfiles() +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path where the file resides. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int initrd_stat(const char *path, stat_t *stat) { - size_t nfiles = 0; + int i; + i = 0; - for (size_t i = 0; i < MAX_FILES; ++i) { - if (fs_headers[i].file_type != 0) { - ++nfiles; - } - } + while (i < INITRD_MAX_FILES) { + if (!strcmp(path, fs_specs.headers[i].fileName)) { + stat->st_uid = fs_specs.headers[i].uid; + stat->st_size = fs_specs.headers[i].length; + break; + } + i++; + } - return nfiles; + if (i == INITRD_MAX_FILES) { + return -ENOENT; + } else { + return _initrd_stat(&fs_specs.headers[i], stat); + } } -void dump_initrd_fs() +/// @brief Writes the given content inside the file. +/// @param file The file descriptor of the file. +/// @param buf The content to write. +/// @param offset Offset from which we start writing in the file. +/// @param nbyte The number of bytes to write. +/// @return The number of written bytes. +static ssize_t initrd_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) { - for (uint32_t i = 0; i < MAX_FILES; ++i) { - dbg_print("[%2d] %s\n", i, fs_headers[i].fileName); - } + // Get the header. + initrd_file_t *header = &fs_specs.headers[file->ino]; + // If the number of byte to write is zero, skip. + if (nbyte == 0) { + return 0; + } + if (check_if_occupied(offset + nbyte)) { + pr_emerg("We need to move the file.\n"); + TODO("Implement file movement."); + } + // Prepare pointers to the contents. + char *dest = (char *)(&fs_specs + header->offset + offset), *src = (char *)buf; + // Copy the content. + int num = 0; + while ((num < nbyte) && (*dest++ = *src++)) { + ++num; + } + dest[num] = '\0'; + dest[num + 1] = EOF; + // Increment the length of the file. + header->length += num; + return num; } + +static off_t initrd_lseek(vfs_file_t *file, off_t offset, int whence) +{ + // Get the header. + initrd_file_t *header = &fs_specs.headers[file->ino]; + + switch (whence) { + case SEEK_END: + offset += header->length; + break; + case SEEK_CUR: + if (offset == 0) { + return file->f_pos; + } + offset += file->f_pos; + break; + case SEEK_SET: + break; + default: + return -EINVAL; + } + if (offset >= 0) { + if (offset != file->f_pos) { + file->f_pos = offset; + } + return offset; + } + return -EINVAL; +} + +/// @brief Closes the given file. +/// @param file The file structure. +static int initrd_close(vfs_file_t *file) +{ + assert(file && "Received null file."); + // Remove the file from the list of the corresponding entry inside the `header_files`. + list_head_del(&file->siblings); + // Free the memory of the file. + kmem_cache_free(file); + return 0; +} + +/// Filesystem general operations. +static vfs_sys_operations_t initrd_sys_operations = { + .mkdir_f = initrd_mkdir, + .rmdir_f = initrd_rmdir, + .stat_f = initrd_stat +}; + +/// Filesystem file operations. +static vfs_file_operations_t initrd_fs_operations = { + .open_f = initrd_open, + .unlink_f = initrd_unlink, + .close_f = initrd_close, + .read_f = initrd_read, + .write_f = initrd_write, + .lseek_f = initrd_lseek, + .stat_f = initrd_fstat, + .ioctl_f = NULL, + .getdents_f = initrd_getdents +}; + +static vfs_file_t *initrd_create_file_struct( + ino_t ino, + const char *name, + size_t size, + int flags) +{ + vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); + if (!file) { + pr_err("Failed to allocation memory for the file."); + return NULL; + } + memset(file, 0, sizeof(vfs_file_t)); + + strcpy(file->name, name); + file->device = (void *)file; + file->ino = ino; + file->uid = 0; + file->gid = 0; + file->mask = S_IRUSR | S_IRGRP | S_IROTH; + file->length = size; + file->flags = flags; + file->sys_operations = &initrd_sys_operations; + file->fs_operations = &initrd_fs_operations; + + return file; +} + +static vfs_file_t *initrd_mount_callback(const char *path, const char *device) +{ + dump_initrd_fs(); + // Create the associated file. + vfs_file_t *vfs_file = initrd_create_file_struct(0, path, 0, DT_DIR); + assert(vfs_file && "Failed to create vfs_file."); + // Initialize the proc_root. + return vfs_file; +} + +/// Filesystem information. +static file_system_type initrd_file_system_type = { + .name = "initrd", + .fs_flags = 0, + .mount = initrd_mount_callback +}; + +int initrd_init_module(void) +{ + for (int i = 0; i < MAX_MODULES; ++i) { + if (strcmp((char *)modules[i].cmdline, "initrd") == 0) { + assert(sizeof(struct initrd_t) <= (modules[i].mod_end - modules[i].mod_start)); + // Copy the FS specification. + memcpy(&fs_specs, (void *)modules[i].mod_start, sizeof(struct initrd_t)); + // Register the filesystem. + vfs_register_filesystem(&initrd_file_system_type); + } + } + return 0; +} + +int initrd_cleanup_module(void) +{ + vfs_unregister_filesystem(&initrd_file_system_type); + return 0; +} \ No newline at end of file diff --git a/mentos/src/fs/ioctl.c b/mentos/src/fs/ioctl.c new file mode 100644 index 0000000..4cb2090 --- /dev/null +++ b/mentos/src/fs/ioctl.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ioctl.h" +#include "scheduler.h" +#include "printk.h" +#include "stdio.h" +#include "errno.h" +#include "vfs.h" + +int sys_ioctl(int fd, int request, void *data) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + + // Get the file. + vfs_file_t *file = vfd->file_struct; + if (file == NULL) { + return -ENOSYS; + } + + // Perform the ioctl. + return vfs_ioctl(file, request, data); +} \ No newline at end of file diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c new file mode 100644 index 0000000..4234eea --- /dev/null +++ b/mentos/src/fs/namei.c @@ -0,0 +1,24 @@ +/// MentOS, The Mentoring Operating system project +/// @file namei.c +/// @brief Implementation of functions fcntl() and open(). +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "errno.h" +#include "vfs.h" +#include "scheduler.h" + +int sys_unlink(const char *path) +{ + return vfs_unlink(path); +} + +int sys_mkdir(const char *path, mode_t mode) +{ + return vfs_mkdir(path, mode); +} + +int sys_rmdir(const char *path) +{ + return vfs_rmdir(path); +} diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c index a8cf3d5..346adbc 100644 --- a/mentos/src/fs/open.c +++ b/mentos/src/fs/open.c @@ -1,98 +1,93 @@ /// MentOS, The Mentoring Operating system project /// @file open.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "process/scheduler.h" +#include "process/process.h" +#include "system/printk.h" +#include "fcntl.h" #include "syscall.h" #include "string.h" #include "limits.h" #include "debug.h" +#include "errno.h" #include "stdio.h" #include "vfs.h" int sys_open(const char *pathname, int flags, mode_t mode) { - // Allocate a variable for the path. - char absolute_path[PATH_MAX]; + // Get the current task. + task_struct *task = scheduler_get_current_process(); - // Copy the path to the working variable. - strcpy(absolute_path, pathname); + // Search for an unused fd. + int fd; + for (fd = 0; fd < task->max_fd; ++fd) { + if (!task->fd_list[fd].file_struct) { + break; + } + } - // If the first character is not the '/' then get the absolute path. - if (absolute_path[0] != '/') { - if (!get_absolute_path(absolute_path)) { - dbg_print("Cannot get the absolute path.\n"); - return -1; - } - } + // Check if there is not fd available. + if (fd >= MAX_OPEN_FD) { + return -EMFILE; + } - // Search for an unused fd. - for (current_fd = 0; current_fd < MAX_OPEN_FD; ++current_fd) { - if (fd_list[current_fd].mountpoint_id == -1) { - break; - } - } + // If fd limit is reached, try to allocate more + if (fd == task->max_fd) { + if (!vfs_extend_task_fd_list(task)) { + pr_err("Failed to extend the file descriptor list.\n"); + return -EMFILE; + } + } - // Check if there is not fd available. - if (current_fd == MAX_OPEN_FD) { - //errno = EMFILE; - return -1; - } + // Try to open the file. + vfs_file_t *file = vfs_open(pathname, flags, mode); + if (file == NULL) { + return -errno; + } - // Get the mountpoint. - mountpoint_t *mp = get_mountpoint(absolute_path); - if (mp == NULL) { - //errno = ENODEV; - return -1; - } + // Set the file descriptor id. + task->fd_list[fd].file_struct = file; - // Check if the function is implemented. - if (mp->operations.open_f == NULL) { - //errno = ENOSYS; - // Reset the file descriptor. - sys_close(current_fd); - return -1; - } + if (!bitmask_check(flags, O_APPEND)) { + // Reset the offset. + task->fd_list[fd].file_struct->f_pos = 0; + } else { + stat_t stat; + // Stat the file. + file->fs_operations->stat_f(file, &stat); + // Point at the last character + task->fd_list[fd].file_struct->f_pos = stat.st_size; + } - int32_t fs_spec_id = mp->operations.open_f(absolute_path, flags, mode); - if (fs_spec_id == -1) { - // Reset the file descriptor. - sys_close(current_fd); - return -1; - } + // Set the flags. + task->fd_list[fd].flags_mask = flags; - // Set the file descriptor id. - fd_list[current_fd].fs_spec_id = fs_spec_id; - - // Set the mount point id. - fd_list[current_fd].mountpoint_id = mp->mp_id; - - // Reset the offset. - fd_list[current_fd].offset = 0; - - // Set the flags. - fd_list[current_fd].flags_mask = flags; - - // Return the file descriptor and increment it. - return (current_fd++); + // Return the file descriptor and increment it. + return fd; } int sys_close(int fd) { - if (fd < 0) { - return -1; - } - if (fd_list[fd].fs_spec_id >= -1) { - int mp_id = fd_list[fd].mountpoint_id; - if (mountpoint_list[mp_id].operations.close_f != NULL) { - int fs_fd = fd_list[fd].fs_spec_id; - mountpoint_list[mp_id].operations.close_f(fs_fd); - } - fd_list[fd].fs_spec_id = -1; - fd_list[fd].mountpoint_id = -1; - } else { - return -1; - } - return 0; + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file. + vfs_file_t *file = task->fd_list[fd].file_struct; + if (file == NULL) { + return -1; + } + + // Remove the reference to the file. + task->fd_list[fd].file_struct = NULL; + + // Call the close function. + return vfs_close(file); } diff --git a/mentos/src/fs/procfs.c b/mentos/src/fs/procfs.c new file mode 100644 index 0000000..7ed9833 --- /dev/null +++ b/mentos/src/fs/procfs.c @@ -0,0 +1,779 @@ +/// MentOS, The Mentoring Operating system project +/// @file procfs.c +/// @brief Proc file system implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[PROCFS]" + +#include "procfs.h" +#include "vfs.h" +#include "string.h" +#include "errno.h" +#include "debug.h" +#include "fcntl.h" +#include "libgen.h" +#include "assert.h" +#include "time.h" + +/// Maximum length of name in PROCFS. +#define PROCFS_NAME_MAX 255U +/// Maximum number of files in PROCFS. +#define PROCFS_MAX_FILES 512U +/// The magic number used to check if the procfs file is valid. +#define PROCFS_MAGIC_NUMBER 0xBF + +/// @brief Information concerning a file. +typedef struct procfs_file_t { + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The file inode. + int inode; + /// Flags. + unsigned flags; + /// The name of the file. + char name[PROCFS_NAME_MAX]; + /// Associated files. + list_head files; + /// User id of the file. + uid_t uid; + /// Group id of the file. + gid_t gid; + /// Time of last access. + time_t atime; + /// Time of last data modification. + time_t mtime; + /// Time of last status change. + time_t ctime; + /// Pointer to the associated proc_dir_entry_t. + proc_dir_entry_t dir_entry; +} procfs_file_t; + +/// @brief The details regarding the filesystem. +/// @brief Contains the number of files inside the initrd filesystem. +static struct procfs_t { + /// Number of files. + unsigned int nfiles; + /// List of headers. + procfs_file_t headers[PROCFS_MAX_FILES]; +} __attribute__((aligned(16))) fs_specs; + +static inline procfs_file_t *procfs_create_file(const char *path, unsigned flags); + +static inline int procfs_destroy_file(procfs_file_t *procfs_file); + +static inline vfs_file_t *procfs_create_file_struct(procfs_file_t *procfs_file); + +static inline int procfs_get_free_inode() +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + assert(fs_specs.headers[i].magic == PROCFS_MAGIC_NUMBER); + if (fs_specs.headers[i].inode == -1) { + return i; + } + } + return -1; +} + +static inline procfs_file_t *procfs_get_free_entry() +{ + int free_inode = procfs_get_free_inode(); + if (free_inode != -1) { + procfs_file_t *free_entry = &fs_specs.headers[free_inode]; + free_entry->inode = free_inode; + return free_entry; + } else { + pr_err("There are no more free inodes (%d/%d).\n", fs_specs.nfiles, PROCFS_MAX_FILES); + } + return NULL; +} + +static inline procfs_file_t *procfs_find_entry_path(const char *path) +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + if (strcmp(fs_specs.headers[i].name, path) == 0) { + return &fs_specs.headers[i]; + } + } + return NULL; +} + +static inline procfs_file_t *procfs_find_entry_inode(int inode) +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + if (fs_specs.headers[i].inode == inode) { + return &fs_specs.headers[i]; + } + } + return NULL; +} + +static inline int procfs_find_inode(const char *path) +{ + procfs_file_t *file = procfs_find_entry_path(path); + if (file) + return file->inode; + return -1; +} + +static inline int procfs_check_if_empty(const char *path) +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + procfs_file_t *entry = &fs_specs.headers[i]; + // There is nothing here. + if (entry->inode == -1) { + continue; + } + // It's the directory itself. + if (strcmp(path, entry->name) == 0) { + continue; + } + // Get the directory of the file. + char *filedir = dirname(entry->name); + // Check if directory path and file directory are the same. + if (strcmp(path, filedir) == 0) { + return 1; + } + } + return 0; +} + +static void dump_procfs() +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + procfs_file_t *file = &fs_specs.headers[i]; + pr_debug("[%3d]ino:%3d, name:`%s`\n", i, file->inode, file->name); + } +} + +static void procfs_init() +{ + // Initialize the procfs. + memset(&fs_specs, 0, sizeof(struct procfs_t)); + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + fs_specs.headers[i].magic = PROCFS_MAGIC_NUMBER; + fs_specs.headers[i].inode = -1; + } +} + +/// @brief Creates a new directory. +/// @param path The path to the new directory. +/// @param mode The file mode. +/// @return 0 if success. +static int procfs_mkdir(const char *path, mode_t mode) +{ + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + pr_err("procfs_mkdir(%s): Cannot create `.` or `..`.\n", path); + return -EPERM; + } + if (procfs_find_entry_path(path) != NULL) { + return -EEXIST; + } + // Check if the directories before it exist. + procfs_file_t *parent_file = NULL; + char *parent = dirname(path); + if ((strcmp(parent, ".") != 0) && (strcmp(parent, "/") != 0)) { + parent_file = procfs_find_entry_path(parent); + if (parent_file == NULL) { + return -ENOENT; + } + if ((parent_file->flags & DT_DIR) == 0) { + return -ENOTDIR; + } + } + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(path, DT_DIR); + if (!procfs_file) { + pr_err("procfs_mkdir(%s): Cannot create the procfs file.\n", path); + return -ENFILE; + } + return 0; +} + +/// @brief Removes a directory. +/// @param path The path to the directory. +/// @return 0 if success. +static int procfs_rmdir(const char *path) +{ + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + pr_err("procfs_rmdir(%s): Cannot remove `.` or `..`.\n", path); + return -EPERM; + } + // Get the file. + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file == NULL) { + pr_err("procfs_rmdir(%s): Cannot find the file.\n", path); + return -ENOENT; + } + // Check the type. + if ((procfs_file->flags & DT_DIR) == 0) { + pr_err("procfs_rmdir(%s): The entry is not a directory.\n", path); + return -ENOTDIR; + } + // Check if the directory is currently opened. + if (!list_head_empty(&procfs_file->files)) { + pr_err("procfs_rmdir(%s): The directory is opened by someone.\n", path); + return -EBUSY; + } + // Check if its empty. + if (procfs_check_if_empty(procfs_file->name)) { + pr_err("procfs_rmdir(%s): The directory is not empty.\n", path); + return -ENOTEMPTY; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("procfs_rmdir(%s): Failed to remove directory.\n", path); + } + return 0; +} + +static vfs_file_t *procfs_open(const char *path, int flags, mode_t mode) +{ + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file != NULL) { + // Check if it is a directory. + if (flags == (O_RDONLY | O_DIRECTORY)) { + if ((procfs_file->flags & DT_DIR) == 0) { + pr_err("Is not a directory `%s`...\n", path); + errno = ENOTDIR; + return NULL; + } + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening directory `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Update file access. + procfs_file->atime = sys_time(NULL); + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + return vfs_file; + } else if ((procfs_file->flags & DT_DIR) != 0) { + pr_err("Is a directory `%s`...\n", path); + errno = EISDIR; + return NULL; + } + // Check if the open has to create. + if (flags & O_CREAT) { + pr_err("Cannot create, it exists `%s`...\n", path); + errno = EEXIST; + return NULL; + } + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Update file access. + procfs_file->atime = sys_time(NULL); + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + return vfs_file; + } + if (flags & O_CREAT) { + // Check if the directories before it exist. + procfs_file_t *parent_file = NULL; + char *parent_path = dirname(path); + if ((strcmp(parent_path, ".") != 0) && (strcmp(parent_path, "/") != 0)) { + parent_file = procfs_find_entry_path(parent_path); + if (parent_file == NULL) { + pr_err("Cannot find parent `%s`...\n", parent_path); + errno = ENOENT; + return NULL; + } + if ((parent_file->flags & DT_DIR) == 0) { + pr_err("Parent `%s` the parent is not a directory...\n", parent_path); + errno = ENOTDIR; + return NULL; + } + } + // Create the new procfs file. + procfs_file = procfs_create_file(path, DT_REG); + if (!procfs_file) { + pr_err("Cannot create procfs_file for `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + return vfs_file; + } + errno = ENOENT; + return NULL; +} + +static int procfs_close(vfs_file_t *file) +{ + assert(file && "Received null file."); + //pr_debug("procfs_close(%p): VFS file : %p\n", file, file); + // Remove the file from the list of `files` inside its corresponding `procfs_file_t`. + list_head_del(&file->siblings); + // Free the memory of the file. + kmem_cache_free(file); + return 0; +} + +/// @brief Deletes the file at the given path. +/// @param path The path to the file. +/// @return On success, zero is returned. On error, -1 is returned. +static inline int procfs_unlink(const char *path) +{ + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + return -EPERM; + } + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file != NULL) { + return -EEXIST; + } + // Check the type. + if ((procfs_file->flags & DT_REG) == 0) { + if ((procfs_file->flags & DT_DIR) != 0) { + pr_err("procfs_unlink(%s): The file is a directory.\n", path); + return -EISDIR; + } + pr_err("procfs_unlink(%s): The file is not a regular file.\n", path); + return -EACCES; + } + // Check if the procfs file has still some file associated. + if (!list_head_empty(&procfs_file->files)) { + pr_err("procfs_unlink(%s): The file is opened by someone.\n", path); + return -EACCES; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("procfs_unlink(%s): Failed to remove file.\n", path); + } + return 0; +} + +static ssize_t procfs_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (file) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file && procfs_file->dir_entry.fs_operations) + if (procfs_file->dir_entry.fs_operations->read_f) + return procfs_file->dir_entry.fs_operations->read_f(file, buf, offset, nbyte); + } + return -ENOSYS; +} + +static ssize_t procfs_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) +{ + if (file) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file && procfs_file->dir_entry.fs_operations) + if (procfs_file->dir_entry.fs_operations->write_f) + return procfs_file->dir_entry.fs_operations->write_f(file, buf, offset, nbyte); + } + return -ENOSYS; +} + +static int __procfs_stat(procfs_file_t *file, stat_t *stat) +{ + stat->st_uid = file->uid; + stat->st_gid = file->gid; + stat->st_dev = 0; + stat->st_ino = file->inode; + stat->st_mode = file->flags; + stat->st_size = 0; + stat->st_atime = file->atime; + stat->st_mtime = file->mtime; + stat->st_ctime = file->ctime; + return 0; +} + +/// @brief Retrieves information concerning the file at the given position. +/// @param file The file struct. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int procfs_fstat(vfs_file_t *file, stat_t *stat) +{ + if (file && stat) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file) { + if (procfs_file->dir_entry.fs_operations && procfs_file->dir_entry.fs_operations->stat_f) + return procfs_file->dir_entry.fs_operations->stat_f(file, stat); + else + return __procfs_stat(procfs_file, stat); + } + } + return -ENOSYS; +} + +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path to the file. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int procfs_stat(const char *path, stat_t *stat) +{ + if (path && stat) { + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file) { + if (procfs_file->dir_entry.sys_operations && procfs_file->dir_entry.sys_operations->stat_f) + return procfs_file->dir_entry.sys_operations->stat_f(path, stat); + else + return __procfs_stat(procfs_file, stat); + } + } + return -1; +} + +static int procfs_ioctl(vfs_file_t *file, int request, void *data) +{ + if (file) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file) + if (procfs_file->dir_entry.fs_operations && procfs_file->dir_entry.fs_operations->ioctl_f) + return procfs_file->dir_entry.fs_operations->ioctl_f(file, request, data); + } + return -1; +} + +/// @brief Reads contents of the directories to a dirent buffer, updating +/// the offset and returning the number of written bytes in the buffer, +/// it assumes that all paths are well-formed. +/// @param file The directory handler. +/// @param dirp The buffer where the data should be written. +/// @param doff The offset inside the buffer where the data should be written. +/// @param count The maximum length of the buffer. +/// @return The number of written bytes in the buffer. +static inline int procfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count) +{ + if (!file || !dirp) + return -1; + procfs_file_t *direntry = procfs_find_entry_inode(file->ino); + if (direntry == NULL) { + return -ENOENT; + } + if ((direntry->flags & DT_DIR) == 0) + return -ENOTDIR; + memset(dirp, 0, count); + int len = strlen(direntry->name); + size_t written = 0; + off_t current = 0; + char *parent = NULL; + for (off_t it = 0; (it < PROCFS_MAX_FILES) && (written < count); ++it) { + procfs_file_t *entry = &fs_specs.headers[it]; + if (entry->inode == -1) { + continue; + } + // If the entry is the directory itself, skip. + if (strcmp(direntry->name, entry->name) == 0) { + continue; + } + // Get the parent directory. + parent = dirname(entry->name); + // Check if the entry is inside the directory. + if (strcmp(direntry->name, parent) != 0) { + continue; + } + // Skip if already provided. + if (current++ < doff) { + continue; + } + if (*(entry->name + len) == '/') + ++len; + // Write on current dirp. + dirp->d_ino = it; + dirp->d_type = entry->flags; + strcpy(dirp->d_name, entry->name + len); + dirp->d_off = sizeof(dirent_t); + dirp->d_reclen = sizeof(dirent_t); + // Increment the written counter. + written += sizeof(dirent_t); + // Move to next writing position. + dirp += 1; + } + return written; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procfs_sys_operations = { + .mkdir_f = procfs_mkdir, + .rmdir_f = procfs_rmdir, + .stat_f = procfs_stat +}; + +/// Filesystem file operations. +static vfs_file_operations_t procfs_fs_operations = { + .open_f = procfs_open, + .unlink_f = procfs_unlink, + .close_f = procfs_close, + .read_f = procfs_read, + .write_f = procfs_write, + .lseek_f = NULL, + .stat_f = procfs_fstat, + .ioctl_f = procfs_ioctl, + .getdents_f = procfs_getdents +}; + +static inline procfs_file_t *procfs_create_file(const char *path, unsigned flags) +{ + procfs_file_t *file = procfs_get_free_entry(); + if (!file) { + pr_err("Failed to get free entry (%p).\n", file); + return NULL; + } + // Number used as delimiter, it must be set to 0xBF. + assert(file->magic == PROCFS_MAGIC_NUMBER); + // The file inode. + assert(file->inode != -1); + // Flags. + file->flags = flags; + // The name of the file. + strcpy(file->name, path); + // Associated files. + list_head_init(&file->files); + // Time of last access. + file->atime = sys_time(NULL); + // Time of last data modification. + file->mtime = file->atime; + // Time of last status change. + file->ctime = file->atime; + // Initialize the dir_entry. + file->dir_entry.name = basename(file->name); + file->dir_entry.data = NULL; + file->dir_entry.sys_operations = NULL; + file->dir_entry.fs_operations = NULL; + // Increase the number of files. + ++fs_specs.nfiles; + return file; +} + +static inline int procfs_destroy_file(procfs_file_t *procfs_file) +{ + if (!procfs_file) { + pr_err("Received a null entry (%p).\n", procfs_file); + return 1; + } + // Check the number used as delimiter, it must be set to 0xBF. + assert(procfs_file->magic == PROCFS_MAGIC_NUMBER); + + // Reset the inode. + procfs_file->inode = -1; + // Reset the flags. + procfs_file->flags = 0; + // Reset the name of the file. + memset(procfs_file->name, 0, PROCFS_NAME_MAX); + // Reset the list of associated files. + list_head_init(&procfs_file->files); + // Reset the time of last access. + procfs_file->atime = sys_time(NULL); + // Reset the time of last data modification. + procfs_file->mtime = procfs_file->atime; + // Reset the time of last status change. + procfs_file->ctime = procfs_file->atime; + + // Decrease the number of files. + --fs_specs.nfiles; + return 0; +} + +static inline vfs_file_t *procfs_create_file_struct(procfs_file_t *procfs_file) +{ + if (!procfs_file) { + pr_err("procfs_create_file_struct(%p): Procfs file not valid!\n", procfs_file); + return NULL; + } + vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); + if (!file) { + pr_err("procfs_create_file_struct(%p): Failed to allocate memory for VFS file!\n", procfs_file); + return NULL; + } + memset(file, 0, sizeof(vfs_file_t)); + + strcpy(file->name, procfs_file->name); + file->device = &procfs_file->dir_entry; + file->ino = procfs_file->inode; + file->uid = 0; + file->gid = 0; + file->mask = S_IRUSR | S_IRGRP | S_IROTH; + file->length = 0; + file->flags = procfs_file->flags; + file->sys_operations = &procfs_sys_operations; + file->fs_operations = &procfs_fs_operations; + //pr_debug("procfs_create_file_struct(%p): VFS file : %p\n", procfs_file, file); + return file; +} + +static vfs_file_t *procfs_mount_callback(const char *path, const char *device) +{ + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(path, DT_DIR); + assert(procfs_file && "Failed to create procfs_file."); + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + assert(vfs_file && "Failed to create vfs_file."); + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + // Initialize the proc_root. + return vfs_file; +} + +/// Filesystem information. +static file_system_type procfs_file_system_type = { + .name = "procfs", + .fs_flags = 0, + .mount = procfs_mount_callback +}; + +int procfs_module_init() +{ + // Initialize the procfs. + procfs_init(); + // Register the filesystem. + vfs_register_filesystem(&procfs_file_system_type); + return 0; +} + +int procfs_cleanup_module() +{ + // Unregister the filesystem. + vfs_register_filesystem(&procfs_file_system_type); + return 0; +} + +proc_dir_entry_t *proc_dir_entry_get(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Get the procfs entry. + procfs_file_t *procfs_file = procfs_find_entry_path(entry_path); + if (procfs_file == NULL) { + pr_err("proc_dir_entry_get(%s): Cannot find proc entry.\n", entry_path); + return NULL; + } + return &procfs_file->dir_entry; +} + +proc_dir_entry_t *proc_mkdir(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + if (procfs_find_entry_path(entry_path) != NULL) { + pr_err("proc_destroy_entry(%s): Proc entry already exists.\n", entry_path); + errno = EEXIST; + return NULL; + } + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(entry_path, DT_DIR); + if (!procfs_file) { + pr_err("proc_destroy_entry(%s): Cannot create proc entry.\n", entry_path); + errno = ENFILE; + return NULL; + } + return &procfs_file->dir_entry; +} + +int proc_rmdir(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + procfs_file_t *procfs_file = procfs_find_entry_path(entry_path); + if (procfs_file == NULL) { + pr_err("proc_destroy_entry(%s): Cannot find proc entry.\n", entry_path); + return -ENOENT; + } + if ((procfs_file->flags & DT_DIR) == 0) { + pr_err("proc_destroy_entry(%s): Proc entry is not a directory.\n", entry_path); + return -ENOTDIR; + } + // Check if its empty. + if (procfs_check_if_empty(procfs_file->name)) { + pr_err("procfs_rmdir(%s): The directory is not empty.\n", entry_path); + return -ENOTEMPTY; + } + // Check if the procfs file has still some file associated. + if (!list_head_empty(&procfs_file->files)) { + pr_err("proc_destroy_entry(%s): Proc entry is busy.\n", entry_path); + return -EBUSY; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("proc_destroy_entry(%s): Failed to remove file.\n", entry_path); + return -ENOENT; + } + return 0; +} + +proc_dir_entry_t *proc_create_entry(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + if (procfs_find_entry_path(entry_path) != NULL) { + pr_err("proc_destroy_entry(%s): Proc entry already exists.\n", entry_path); + errno = EEXIST; + return NULL; + } + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(entry_path, DT_REG); + if (!procfs_file) { + pr_err("proc_destroy_entry(%s): Cannot create proc entry.\n", entry_path); + errno = ENFILE; + return NULL; + } + return &procfs_file->dir_entry; +} + +int proc_destroy_entry(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + procfs_file_t *procfs_file = procfs_find_entry_path(entry_path); + if (procfs_file == NULL) { + pr_err("proc_destroy_entry(%s): Cannot find proc entry.\n", entry_path); + return -ENOENT; + } + if ((procfs_file->flags & DT_REG) == 0) { + pr_err("proc_destroy_entry(%s): Proc entry is not a regular file.\n", entry_path); + return -ENOENT; + } + // Check if the procfs file has still some file associated. + if (!list_head_empty(&procfs_file->files)) { + pr_err("proc_destroy_entry(%s): Proc entry is busy.\n", entry_path); + return -EBUSY; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("proc_destroy_entry(%s): Failed to remove file.\n", entry_path); + return -ENOENT; + } + return 0; +} \ No newline at end of file diff --git a/mentos/src/fs/read_write.c b/mentos/src/fs/read_write.c index 9bbfb98..3c8e83d 100644 --- a/mentos/src/fs/read_write.c +++ b/mentos/src/fs/read_write.c @@ -1,63 +1,97 @@ /// MentOS, The Mentoring Operating system project /// @file read_write.c /// @brief Read and write functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include -#include "vfs.h" -#include "stdio.h" +#include "scheduler.h" +#include "vfs_types.h" +#include "panic.h" +#include "errno.h" #include "fcntl.h" -#include "unistd.h" -#include "keyboard.h" -#include "video.h" +#include "stdio.h" +#include "vfs.h" ssize_t sys_read(int fd, void *buf, size_t nbytes) { - if (fd == STDIN_FILENO) { - *((char *)buf) = keyboard_getc(); - return 1; - } + // Get the current task. + task_struct *task = scheduler_get_current_process(); - int mp_id = fd_list[fd].mountpoint_id; - int fs_fd = fd_list[fd].fs_spec_id; + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } - if (mountpoint_list[mp_id].operations.read_f != NULL) { - return mountpoint_list[mp_id].operations.read_f(fs_fd, buf, nbytes); - } else { - printf("No READ Found for that file system\n"); - } + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; - return 0; + // Check the permissions. +#if 0 + if (!(vfd->flags_mask & O_RDONLY)) { + return -EROFS; + } +#endif + + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + + // Perform the read. + int read = vfs_read(vfd->file_struct, buf, vfd->file_struct->f_pos, nbytes); + + // Update the offset. + if (read > 0) { + vfd->file_struct->f_pos += read; + } + return read; } ssize_t sys_write(int fd, void *buf, size_t nbytes) { - if ((fd == STDOUT_FILENO) || (fd == STDERR_FILENO)) { - for (size_t i = 0; (i < nbytes); ++i) - video_putc(((char *)buf)[i]); - return nbytes; - } + // Get the current task. + task_struct *task = scheduler_get_current_process(); - if (fd > MAX_OPEN_FD) { - //errno = EBADF; - return -1; - } + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } - if (!(fd_list[fd].flags_mask & O_RDWR)) { - //errno = EROFS; - return -1; - } + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; - mountpoint_t *mp = &mountpoint_list[fd_list[fd].mountpoint_id]; - if (mp == NULL) { - //errno = ENODEV; - return -1; - } - if (mp->operations.write_f == NULL) { - //errno = ENOSYS; - return -1; - } + // Check the permissions. + if (!(vfd->flags_mask & O_WRONLY)) { + return -EROFS; + } - return mp->operations.write_f(fd_list[fd].fs_spec_id, buf, nbytes); + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + + // Perform the write. + int written = vfs_write(vfd->file_struct, buf, vfd->file_struct->f_pos, nbytes); + + // Update the offset. + if (written > 0) { + vfd->file_struct->f_pos += written; + } + return written; } + +off_t sys_lseek(int fd, off_t offset, int whence) +{ + task_struct *task = scheduler_get_current_process(); + if (fd < 0 || fd >= task->max_fd) { + return -1; + } + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + // Perform the lseek. + return vfs_lseek(vfd->file_struct, offset, whence); +} \ No newline at end of file diff --git a/mentos/src/fs/readdir.c b/mentos/src/fs/readdir.c index 6ae887f..e879ae7 100644 --- a/mentos/src/fs/readdir.c +++ b/mentos/src/fs/readdir.c @@ -1,28 +1,54 @@ /// MentOS, The Mentoring Operating system project /// @file readdir.c /// @brief Function for accessing directory entries. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "dirent.h" +#include "sys/dirent.h" +#include "scheduler.h" #include "syscall.h" +#include "printk.h" +#include "errno.h" #include "stdio.h" #include "vfs.h" -dirent_t *sys_readdir(DIR *dirp) +int sys_getdents(int fd, dirent_t *dirp, unsigned int count) { - if (dirp == NULL) { - printf("readdir: cannot read directory :" - "Directory pointer is not valid\n"); + if (dirp == NULL) { + printf("getdents: cannot read directory :" + "Directory pointer is not valid\n"); + return 0; + } + // Get the current task. + task_struct *task = scheduler_get_current_process(); - return NULL; - } - if (mountpoint_list[dirp->fd].dir_op.readdir_f == NULL) { - printf("readdir: cannot read directory '%s':" - "No readdir function\n", - dirp->path); + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } - return NULL; - } - return mountpoint_list[dirp->fd].dir_op.readdir_f(dirp); + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + + // Check the permissions. +#if 0 + if (!(task->fd_list[fd].flags_mask & O_RDONLY)) { + return -EROFS; + } +#endif + + // Get the file. + vfs_file_t *file = vfd->file_struct; + if (file == NULL) { + return -ENOSYS; + } + + // Perform the read. + int actual_read = vfs_getdents(file, dirp, vfd->file_struct->f_pos, count); + + // Update the offset. + if (actual_read > 0) { + vfd->file_struct->f_pos += (actual_read / sizeof(dirent_t)); + } + return actual_read; } diff --git a/mentos/src/fs/stat.c b/mentos/src/fs/stat.c index 9365317..b8531ee 100644 --- a/mentos/src/fs/stat.c +++ b/mentos/src/fs/stat.c @@ -1,9 +1,11 @@ /// MentOS, The Mentoring Operating system project /// @file stat.c /// @brief Stat functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "debug.h" +#include "errno.h" #include "vfs.h" #include "kheap.h" #include "stdio.h" @@ -13,73 +15,33 @@ int sys_stat(const char *path, stat_t *buf) { - // Reset the structure. - buf->st_dev = 0; - buf->st_ino = 0; - buf->st_mode = 0; - buf->st_uid = 0; - buf->st_gid = 0; - buf->st_size = 0; - buf->st_atime = 0; - buf->st_mtime = 0; - buf->st_ctime = 0; - - char absolute_path[PATH_MAX]; - strcpy(absolute_path, path); - - if (path[0] != '/') - { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("stat: cannot execute stat of '%s': Not exists\n", path); - return -1; - } - - buf->st_dev = (uint32_t) mp_id; - if (mountpoint_list[mp_id].stat_op.stat_f == NULL) - { - printf("stat: cannot execute stat of '%s': Not stat function\n", - path); - - return -1; - } - mountpoint_list[mp_id].stat_op.stat_f(absolute_path, buf); - - return 0; + return vfs_stat(path, buf); } -int sys_mkdir(const char *path, mode_t mode) +int sys_fstat(int fd, stat_t *buf) { - char absolute_path[PATH_MAX]; - strcpy(absolute_path, path); - int result = -1; + // Get the current task. + task_struct *task = scheduler_get_current_process(); - if (path[0] != '/') - { - get_absolute_path(absolute_path); + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; } - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("mkdir: cannot create directory '%s':" - "Cannot find mount-point\n", path); + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; - return result; + // Check the permissions. +#if 0 + if (!(vfd->flags_mask & O_RDONLY)) { + return -EROFS; + } +#endif + + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; } - if (mountpoint_list[mp_id].dir_op.mkdir_f == NULL) - { - printf("mkdir: cannot create directory '%s': " - "No mkdir function\n", path); - - return result; - } - result = mountpoint_list[mp_id].dir_op.mkdir_f(absolute_path, mode); - - return result; -} + return vfs_fstat(vfd->file_struct, buf); +} \ No newline at end of file diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 22e1b0c..7b7d694 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -1,169 +1,504 @@ /// MentOS, The Mentoring Operating system project /// @file vfs.c /// @brief Headers for Virtual File System (VFS). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include -#include -#include "video.h" #include "vfs.h" -#include "debug.h" -#include "shell.h" -#include "string.h" -#include "initrd.h" -#include "limits.h" -int current_fd; -char *module_start[MAX_MODULES]; -file_descriptor_t fd_list[MAX_OPEN_FD]; -mountpoint_t mountpoint_list[MAX_MOUNTPOINT]; +#include "scheduler.h" +#include "spinlock.h" +#include "strerror.h" +#include "syscall.h" +#include "hashmap.h" +#include "string.h" +#include "procfs.h" +#include "assert.h" +#include "libgen.h" +#include "debug.h" +#include "panic.h" +#include "stdio.h" + +/// The hashmap that associates a type of Filesystem `name` to its `mount` function; +static hashmap_t *vfs_filesystems; +/// The list of superblocks. +static list_head vfs_super_blocks; +/// The maximum number of filesystem types. +static const unsigned vfs_filesystems_max = 10; +/// Lock for refcount field. +static spinlock_t vfs_spinlock_refcount; +/// Spinlock for the entire virtual filesystem. +static spinlock_t vfs_spinlock; +/// VFS memory cache for superblocks. +static kmem_cache_t *vfs_superblock_cache; + +/// VFS memory cache for files. +kmem_cache_t *vfs_file_cache; void vfs_init() { - current_fd = 0; - - for (int32_t i = 0; i < MAX_MOUNTPOINT; ++i) { - strcpy(mountpoint_list[i].mountpoint, ""); - mountpoint_list[i].mp_id = i; - mountpoint_list[i].uid = 0; - mountpoint_list[i].gid = 0; - mountpoint_list[i].pmask = 0; - mountpoint_list[i].dev_id = 0; - mountpoint_list[i].start_address = 0; - mountpoint_list[i].dir_op.opendir_f = NULL; - mountpoint_list[i].operations.open_f = NULL; - mountpoint_list[i].operations.close_f = NULL; - mountpoint_list[i].operations.read_f = NULL; - mountpoint_list[i].operations.write_f = NULL; - mountpoint_list[i].stat_op.stat_f = NULL; - } - - for (int32_t i = 0; i < MAX_OPEN_FD; ++i) { - fd_list[i].fs_spec_id = -1; - fd_list[i].mountpoint_id = -1; - fd_list[i].offset = 0; - fd_list[i].flags_mask = 0; - } - - strcpy(mountpoint_list[0].mountpoint, "/"); - mountpoint_list[0].uid = 0; - mountpoint_list[0].gid = 0; - mountpoint_list[0].pmask = 0; - mountpoint_list[0].dev_id = 0; - mountpoint_list[0].start_address = (unsigned int)module_start[0]; - mountpoint_list[0].end_address = (unsigned int)module_end[0]; - mountpoint_list[0].dir_op.opendir_f = initfs_opendir; - mountpoint_list[0].dir_op.closedir_f = initfs_closedir; - mountpoint_list[0].dir_op.readdir_f = initrd_readdir; - mountpoint_list[0].dir_op.mkdir_f = initrd_mkdir; - mountpoint_list[0].dir_op.rmdir_f = initrd_rmdir; - mountpoint_list[0].operations.open_f = initfs_open; - mountpoint_list[0].operations.remove_f = initfs_remove; - mountpoint_list[0].operations.close_f = initrd_close; - mountpoint_list[0].operations.read_f = initfs_read; - mountpoint_list[0].operations.write_f = initrd_write; - mountpoint_list[0].stat_op.stat_f = initrd_stat; + // Initialize the list of superblocks. + list_head_init(&vfs_super_blocks); + // Initialize the caches for superblocks and files. + vfs_superblock_cache = KMEM_CREATE(super_block_t); + vfs_file_cache = KMEM_CREATE(vfs_file_t); + // Allocate the hashmap for the different filesystems. + vfs_filesystems = hashmap_create( + vfs_filesystems_max, + hashmap_str_hash, + hashmap_str_comp, + hashmap_do_not_duplicate, + hashmap_do_not_free); + // Initialize the spinlock. + spinlock_init(&vfs_spinlock); + spinlock_init(&vfs_spinlock_refcount); } -int32_t get_mountpoint_id(const char *path) +int vfs_register_filesystem(file_system_type *fs) { - size_t last_mpl = 0; - int32_t last_mp_id = -1; - - for (uint32_t it = 0; it < MAX_MOUNTPOINT; ++it) { - size_t mpl = strlen(mountpoint_list[it].mountpoint); - - if (!strncmp(path, mountpoint_list[it].mountpoint, mpl)) { - if (((mpl > last_mpl) && it > 0) || (it == 0)) { - last_mpl = mpl; - last_mp_id = it; - } - } - } - - return last_mp_id; + if (hashmap_set(vfs_filesystems, (void *)fs->name, (void *)fs) != NULL) { + pr_err("Filesystem already registered.\n"); + return 0; + } + pr_debug("vfs_register_filesystem(`%s`) : %p\n", fs->name, fs); + return 1; } -mountpoint_t *get_mountpoint(const char *path) +int vfs_unregister_filesystem(file_system_type *fs) { - int32_t mp_id = get_mountpoint_id(path); - - if (mp_id < 0) { - return NULL; - } - - return &mountpoint_list[mp_id]; + if (hashmap_remove(vfs_filesystems, (void *)fs->name) != NULL) { + pr_err("Filesystem not present to unregister.\n"); + return 0; + } + return 1; } -mountpoint_t *get_mountpoint_from_id(int32_t mp_id) +super_block_t *vfs_get_superblock(const char *absolute_path) { - if (mp_id >= MAX_MOUNTPOINT) { - return NULL; - } - - return &mountpoint_list[mp_id]; + size_t last_sb_len = 0; + super_block_t *last_sb = NULL, *superblock = NULL; + list_head *it; + list_for_each (it, &vfs_super_blocks) { + superblock = list_entry(it, super_block_t, mounts); + int len = strlen(superblock->name); + if (!strncmp(absolute_path, superblock->name, len)) { + size_t sbl = strlen(superblock->name); + if (sbl > last_sb_len) { + last_sb_len = sbl; + last_sb = superblock; + } + } + } + return last_sb; } -int get_relative_path(uint32_t mp_id, char *path) +vfs_file_t *vfs_open(const char *path, int flags, mode_t mode) { - char relative_path[PATH_MAX]; - - // Get the size of the path. - size_t path_size = strlen(path); - - // Get the size of the mount-point. - size_t mnpt_size = strlen(mountpoint_list[mp_id].mountpoint); - - // Get the size of the relative path. - size_t rltv_size = path_size - mnpt_size; - - // Copy the relative path. - if (rltv_size > 0) { - size_t i; - for (i = 0; i < rltv_size; ++i) { - relative_path[i] = path[mnpt_size + i]; - } - relative_path[i] = '\0'; - strcpy(path, relative_path); - } - - return strlen(path); + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_open(%s): Cannot get the absolute path!\n", path); + errno = ENODEV; + return NULL; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_open(%s): Cannot find the superblock!\n", path); + errno = ENOENT; + return NULL; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_open(%s): Cannot find the superblock root!\n", path); + errno = ENOENT; + return NULL; + } + // Rebase the absolute path. + //size_t name_offset = (strcmp(mp->name, "/") == 0) ? 0 : strlen(mp->name); + // Check if the function is implemented. + if (sb_root->fs_operations->open_f == NULL) { + pr_err("vfs_open(%s): Function not supported in current filesystem.", path); + errno = ENOSYS; + return NULL; + } + // Retrieve the file. + vfs_file_t *file = sb_root->fs_operations->open_f(absolute_path, flags, mode); + if (file == NULL) { + pr_err("vfs_open(%s): Cannot find the given file (%s)!\n", path, strerror(errno)); + errno = ENOENT; + return NULL; + } + // Increment file reference counter. + ++file->count; + // Return the file. + return file; } -int get_absolute_path(char *path) +int vfs_close(vfs_file_t *file) { - if (path[0] != '/') { - char abspath[PATH_MAX]; - memset(abspath, '\0', PATH_MAX); - getcwd(abspath, PATH_MAX); - int abs_size = strlen(abspath); - if (abspath[abs_size - 1] == '/') { - strncat(abspath, path, strlen(path)); - } else { - strncat(abspath, "/", strlen(path)); - strncat(abspath, path, strlen(path)); - } - strcpy(path, abspath); + // Decrement file reference counter. + file->count--; - return strlen(path); - } - - return 0; + // Close file if it's the last reference. + if (file->count == 0) { + // Check if the filesystem has the close function. + if (file->fs_operations->close_f == NULL) { + return -ENOSYS; + } + file->fs_operations->close_f(file); + } + return 0; } -void vfs_dump() +ssize_t vfs_read(vfs_file_t *file, void *buf, size_t offset, size_t nbytes) { - dbg_print("File Descriptors:\n"); - int i = 0; - - while (i < MAX_OPEN_FD) { - if (fd_list[i].mountpoint_id != -1) { - dbg_print("[%d] %s\n", i, - mountpoint_list[fd_list[i].mountpoint_id].mountpoint); - } else { - dbg_print("[%d] Free\n", i); - } - ++i; - } + if (file->fs_operations->read_f == NULL) { + pr_err("No READ function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->read_f(file, buf, offset, nbytes); +} + +ssize_t vfs_write(vfs_file_t *file, void *buf, size_t offset, size_t nbytes) +{ + if (file->fs_operations->write_f == NULL) { + pr_err("No WRITE function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->write_f(file, buf, offset, nbytes); +} + +off_t vfs_lseek(vfs_file_t *file, off_t offset, int whence) +{ + if (file->fs_operations->lseek_f == NULL) { + pr_err("No WRITE function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->lseek_f(file, offset, whence); +} + +int vfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t off, size_t count) +{ + if (file->fs_operations->getdents_f == NULL) { + pr_err("No GETDENTS function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->getdents_f(file, dirp, off, count); +} + +int vfs_ioctl(vfs_file_t *file, int request, void *data) +{ + if (file->fs_operations->ioctl_f == NULL) { + pr_err("No IOCTL function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->ioctl_f(file, request, data); +} + +int vfs_unlink(const char *path) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_unlink(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_unlink(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_unlink(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->fs_operations->unlink_f == NULL) { + pr_err("vfs_unlink(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + return sb_root->fs_operations->unlink_f(absolute_path); +} + +int vfs_mkdir(const char *path, mode_t mode) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_mkdir(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_mkdir(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_mkdir(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->sys_operations->mkdir_f == NULL) { + pr_err("vfs_mkdir(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + return sb_root->sys_operations->mkdir_f(absolute_path, mode); +} + +int vfs_rmdir(const char *path) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_rmdir(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_rmdir(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_rmdir(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->sys_operations->rmdir_f == NULL) { + pr_err("vfs_rmdir(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + // Remove the file. + return sb_root->sys_operations->rmdir_f(absolute_path); +} + +int vfs_stat(const char *path, stat_t *buf) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_stat(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_stat(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_stat(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->sys_operations->stat_f == NULL) { + pr_err("vfs_rmdir(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + // Reset the structure. + buf->st_dev = 0; + buf->st_ino = 0; + buf->st_mode = 0; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = 0; + buf->st_atime = 0; + buf->st_mtime = 0; + buf->st_ctime = 0; + // Retrieve the file. + return sb_root->sys_operations->stat_f(absolute_path, buf); +} + +int vfs_fstat(vfs_file_t *file, stat_t *buf) +{ + if (file->fs_operations->stat_f == NULL) { + pr_err("No FSTAT function found for the current filesystem.\n"); + return -ENOSYS; + } + // Reset the structure. + buf->st_dev = 0; + buf->st_ino = 0; + buf->st_mode = 0; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = 0; + buf->st_atime = 0; + buf->st_mtime = 0; + buf->st_ctime = 0; + return file->fs_operations->stat_f(file, buf); +} + +int vfs_mount(const char *path, vfs_file_t *new_fs_root) +{ + if (!path || path[0] != '/') { + pr_err("vfs_mount(%s): Path must be absolute for superblock.\n", path); + return 0; + } + if (new_fs_root == NULL) { + pr_err("vfs_mount(%s): You must provide a valid file!\n", path); + return 0; + } + // Lock the vfs spinlock. + spinlock_lock(&vfs_spinlock); + pr_debug("Mounting file with path `%s` as root '%s'...\n", new_fs_root->name, path); + // Create the superblock. + super_block_t *sb = kmem_cache_alloc(vfs_superblock_cache, GFP_KERNEL); + if (!sb) { + pr_debug("Cannot allocate memory for the superblock.\n"); + } else { + // Copy the name. + strcpy(sb->name, new_fs_root->name); + // Set the pointer. + sb->root = new_fs_root; + // Add to the list. + list_head_add(&sb->mounts, &vfs_super_blocks); + } + spinlock_unlock(&vfs_spinlock); + pr_debug("Correctly mounted '%s' on '%s'...\n", path, new_fs_root->name); + return 1; +} + +int do_mount(const char *type, const char *path, const char *args) +{ + file_system_type *fst = (file_system_type *)hashmap_get(vfs_filesystems, type); + if (fst == NULL) { + pr_err("Unknown filesystem type: %s\n", type); + return -ENODEV; + } + vfs_file_t *file = fst->mount(path, args); + if (file == NULL) { + pr_err("Mount callback return a null pointer: %s\n", type); + return -ENODEV; + } + if (!vfs_mount(path, file)) { + pr_err("do_mount(`%s`, `%s`, `%s`) : failed to mount.\n", type, path, args); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(path); + if (sb == NULL) { + pr_err("do_mount(`%s`, `%s`, `%s`) : Cannot find the superblock.\n", type, path, args); + return -ENODEV; + } + // Set the filesystem type. + sb->type = fst; + pr_debug("Mounted %s[%s] to `%s`: file = %p\n", type, args, path, file); + return 0; +} + +void vfs_lock(vfs_file_t *file) +{ + spinlock_lock(&vfs_spinlock_refcount); + file->refcount = -1; + spinlock_unlock(&vfs_spinlock_refcount); +} + +int vfs_extend_task_fd_list(struct task_struct *task) +{ + if (!task) { + pr_err("Null process.\n"); + errno = ESRCH; + return 0; + } + // Set the max number of file descriptors. + int new_max_fd = (task->fd_list) ? task->max_fd * 2 + 1 : MAX_OPEN_FD; + // Allocate the memory for the list. + void *new_fd_list = kmalloc(new_max_fd * sizeof(vfs_file_descriptor_t)); + // Check the new list. + if (!new_fd_list) { + pr_err("Failed to allocate memory for `fd_list`.\n"); + errno = EMFILE; + return 0; + } + // Clear the memory of the new list. + memset(new_fd_list, 0, task->max_fd * sizeof(vfs_file_descriptor_t)); + // Deal with pre-existing list. + if (task->fd_list) { + // Copy the old entries. + memcpy(new_fd_list, task->fd_list, task->max_fd * sizeof(vfs_file_descriptor_t)); + // Free the memory of the old list. + kfree(task->fd_list); + } + // Set the new maximum number of file descriptors. + task->max_fd = new_max_fd; + // Set the new list. + task->fd_list = new_fd_list; + return 1; +} + +int vfs_init_task(task_struct *task) +{ + if (!task) { + pr_err("Null process.\n"); + errno = ESRCH; + return 0; + } + // Initialize the file descriptor list. + if (!vfs_extend_task_fd_list(task)) { + pr_err("Error while trying to initialize the `fd_list` for process `%d`: %s\n", task->pid, strerror(errno)); + return 0; + } + // Create the proc entry. + if (proc_create_entry_pid(task)) { + pr_err("Error while trying to create proc entry for process `%d`: %s\n", task->pid, strerror(errno)); + return 0; + } + return 1; +} + +int vfs_dup_task(task_struct *task, task_struct *old_task) +{ + // Copy the maximum number of file descriptors. + task->max_fd = old_task->max_fd; + // Allocate the memory for the new list. + task->fd_list = kmalloc(task->max_fd * sizeof(vfs_file_descriptor_t)); + // Copy the old list. + memcpy(task->fd_list, old_task->fd_list, task->max_fd * sizeof(vfs_file_descriptor_t)); + // Increase the counters to the open files. + for (int fd = 0; fd < task->max_fd; fd++) { + // Check if the file descriptor is associated with a file. + if (task->fd_list[fd].file_struct) { + // Increase the counter. + ++task->fd_list[fd].file_struct->count; + } + } + // Create the proc entry. + if (proc_create_entry_pid(task)) { + pr_err("Error while trying to create proc entry for '%d': %s\n", task->pid, strerror(errno)); + return 0; + } + return 1; +} + +int vfs_destroy_task(task_struct *task) +{ + // Decrease the counters to the open files. + for (int fd = 0; fd < task->max_fd; fd++) { + // Check if the file descriptor is associated with a file. + if (task->fd_list[fd].file_struct) { + // Decrease the counter. + --task->fd_list[fd].file_struct->count; + // If counter is zero, close the file. + if (task->fd_list[fd].file_struct->count == 0) + task->fd_list[fd].file_struct->fs_operations->close_f(task->fd_list[fd].file_struct); + // Clear the pointer to the file structure. + task->fd_list[fd].file_struct = NULL; + } + } + // Set the maximum file descriptors to 0. + task->max_fd = 0; + // Free the memory of the list. + kfree(task->fd_list); + // Remove the proc entry. + if (proc_destroy_entry_pid(task)) { + pr_err("Error while trying to remove proc entry for '%d': %s\n", task->pid, strerror(errno)); + return 0; + } + return 1; } diff --git a/mentos/src/hardware/cpuid.c b/mentos/src/hardware/cpuid.c index 412158e..1c255e8 100644 --- a/mentos/src/hardware/cpuid.c +++ b/mentos/src/hardware/cpuid.c @@ -1,181 +1,177 @@ /// MentOS, The Mentoring Operating system project /// @file cpuid.c /// @brief CPUID-based function to detect CPU type. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "cmd_cpuid.h" +#include "cpuid.h" #include "string.h" void get_cpuid(cpuinfo_t *cpuinfo) { - register_t ereg; + pt_regs ereg; - ereg.eax = ereg.ebx = ereg.ecx = ereg.edx = 0; - cpuid_write_vendor(cpuinfo, &ereg); + ereg.eax = ereg.ebx = ereg.ecx = ereg.edx = 0; + cpuid_write_vendor(cpuinfo, &ereg); - ereg.eax = 1; - ereg.ebx = ereg.ecx = ereg.edx = 0; - cpuid_write_proctype(cpuinfo, &ereg); + ereg.eax = 1; + ereg.ebx = ereg.ecx = ereg.edx = 0; + cpuid_write_proctype(cpuinfo, &ereg); } -void call_cpuid(register_t *registers) +void call_cpuid(pt_regs *registers) { - __asm__("cpuid\n\t" - : "=a"(registers->eax), "=b"(registers->ebx), "=c"(registers->ecx), - "=d"(registers->edx) - : "a"(registers->eax)); + __asm__("cpuid\n\t" + : "=a"(registers->eax), "=b"(registers->ebx), "=c"(registers->ecx), + "=d"(registers->edx) + : "a"(registers->eax)); } -void cpuid_write_vendor(cpuinfo_t *cpuinfo, register_t *registers) +void cpuid_write_vendor(cpuinfo_t *cpuinfo, pt_regs *registers) { - call_cpuid(registers); + call_cpuid(registers); - cpuinfo->cpu_vendor[0] = (char)((registers->ebx & 0x000000FF)); - cpuinfo->cpu_vendor[1] = (char)((registers->ebx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[2] = (char)((registers->ebx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[3] = (char)((registers->ebx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[4] = (char)((registers->edx & 0x000000FF)); - cpuinfo->cpu_vendor[5] = (char)((registers->edx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[6] = (char)((registers->edx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[7] = (char)((registers->edx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[8] = (char)((registers->ecx & 0x000000FF)); - cpuinfo->cpu_vendor[9] = (char)((registers->ecx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[10] = (char)((registers->ecx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[11] = (char)((registers->ecx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[12] = '\0'; + cpuinfo->cpu_vendor[0] = (char)((registers->ebx & 0x000000FF)); + cpuinfo->cpu_vendor[1] = (char)((registers->ebx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[2] = (char)((registers->ebx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[3] = (char)((registers->ebx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[4] = (char)((registers->edx & 0x000000FF)); + cpuinfo->cpu_vendor[5] = (char)((registers->edx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[6] = (char)((registers->edx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[7] = (char)((registers->edx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[8] = (char)((registers->ecx & 0x000000FF)); + cpuinfo->cpu_vendor[9] = (char)((registers->ecx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[10] = (char)((registers->ecx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[11] = (char)((registers->ecx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[12] = '\0'; } -void cpuid_write_proctype(cpuinfo_t *cpuinfo, register_t *registers) +void cpuid_write_proctype(cpuinfo_t *cpuinfo, pt_regs *registers) { - call_cpuid(registers); + call_cpuid(registers); - uint32_t type = cpuid_get_byte(registers->eax, 0xB, 0x3); + uint32_t type = cpuid_get_byte(registers->eax, 0xB, 0x3); - switch (type) { - case 0: - cpuinfo->cpu_type = "Original OEM Processor"; - break; - case 1: - cpuinfo->cpu_type = "Intel Overdrive Processor"; - break; - case 2: - cpuinfo->cpu_type = "Dual processor"; - break; - case 3: - cpuinfo->cpu_type = "(Intel reserved bit)"; - break; - } + switch (type) { + case 0: + cpuinfo->cpu_type = "Original OEM Processor"; + break; + case 1: + cpuinfo->cpu_type = "Intel Overdrive Processor"; + break; + case 2: + cpuinfo->cpu_type = "Dual processor"; + break; + case 3: + cpuinfo->cpu_type = "(Intel reserved bit)"; + break; + } - uint32_t familyID = cpuid_get_byte(registers->eax, 0x7, 0xE); - cpuinfo->cpu_family = familyID; + uint32_t familyID = cpuid_get_byte(registers->eax, 0x7, 0xE); + cpuinfo->cpu_family = familyID; - if (familyID == 0x0F) { - cpuinfo->cpu_family += cpuid_get_byte(registers->eax, 0x13, 0xFF); - } + if (familyID == 0x0F) { + cpuinfo->cpu_family += cpuid_get_byte(registers->eax, 0x13, 0xFF); + } - uint32_t model = cpuid_get_byte(registers->eax, 0x3, 0xE); - cpuinfo->cpu_model = model; + uint32_t model = cpuid_get_byte(registers->eax, 0x3, 0xE); + cpuinfo->cpu_model = model; - if (familyID == 0x06 || familyID == 0x0F) { - uint32_t ext_model = cpuid_get_byte(registers->eax, 0xF, 0xE); - cpuinfo->cpu_model += (ext_model << 4); - } - cpuinfo->apic_id = cpuid_get_byte(registers->ebx, 0x17, 0xFF); + if (familyID == 0x06 || familyID == 0x0F) { + uint32_t ext_model = cpuid_get_byte(registers->eax, 0xF, 0xE); + cpuinfo->cpu_model += (ext_model << 4); + } + cpuinfo->apic_id = cpuid_get_byte(registers->ebx, 0x17, 0xFF); - cpuid_feature_ecx(cpuinfo, registers->ecx); - cpuid_feature_edx(cpuinfo, registers->edx); + cpuid_feature_ecx(cpuinfo, registers->ecx); + cpuid_feature_edx(cpuinfo, registers->edx); - /* Get brand string to identify the processor */ - if (familyID >= 0x0F && model >= 0x03) { - cpuinfo->brand_string = cpuid_brand_string(registers); - } else { - cpuinfo->brand_string = cpuid_brand_index(registers); - } + /* Get brand string to identify the processor */ + if (familyID >= 0x0F && model >= 0x03) { + cpuinfo->brand_string = cpuid_brand_string(registers); + } else { + cpuinfo->brand_string = cpuid_brand_index(registers); + } } void cpuid_feature_ecx(cpuinfo_t *cpuinfo, uint32_t ecx) { - uint32_t temp = ecx; - uint32_t i; + uint32_t temp = ecx; + uint32_t i; - for (i = 0; i < ECX_FLAGS_SIZE; ++i) { - temp = cpuid_get_byte(temp, i, 1); - cpuinfo->cpuid_ecx_flags[i] = temp; - temp = ecx; - } + for (i = 0; i < ECX_FLAGS_SIZE; ++i) { + temp = cpuid_get_byte(temp, i, 1); + cpuinfo->cpuid_ecx_flags[i] = temp; + temp = ecx; + } } void cpuid_feature_edx(cpuinfo_t *cpuinfo, uint32_t edx) { - uint32_t temp = edx; - uint32_t i; + uint32_t temp = edx; + uint32_t i; - for (i = 0; i < EDX_FLAGS_SIZE; ++i) { - temp = cpuid_get_byte(temp, i, 1); - cpuinfo->cpuid_edx_flags[i] = temp; - temp = edx; - } + for (i = 0; i < EDX_FLAGS_SIZE; ++i) { + temp = cpuid_get_byte(temp, i, 1); + cpuinfo->cpuid_edx_flags[i] = temp; + temp = edx; + } } -inline uint32_t cpuid_get_byte(const uint32_t reg, const uint32_t position, - const uint32_t value) +inline uint32_t cpuid_get_byte(uint32_t reg, uint32_t position, uint32_t value) { - return ((reg >> position) & value); + return ((reg >> position) & value); } -char *cpuid_brand_index(register_t *r) +char *cpuid_brand_index(pt_regs *f) { - char *indexes[21] = { "Reserved", - "Intel Celeron", - "Intel Pentium III", - "Intel Pentium III Xeon", - "Mobile Intel Pentium III", - "Mobile Intel Celeron", - "Intel Pentium 4", - "Intel Pentium 4", - "Intel Celeron", - "Intel Xeon MP", - "Intel Xeon MP", - "Mobile Intel Pentium 4", - "Mobile Intel Celeron", - "Mobile Genuine Intel", - "Intel Celeron M", - "Mobile Intel Celeron", - "Intel Celeron", - "Mobile Genuine Intel", - "Intel Pentium M", - "Mobile Intel Celeron", - NULL }; + char *indexes[21] = { "Reserved", + "Intel Celeron", + "Intel Pentium III", + "Intel Pentium III Xeon", + "Mobile Intel Pentium III", + "Mobile Intel Celeron", + "Intel Pentium 4", + "Intel Pentium 4", + "Intel Celeron", + "Intel Xeon MP", + "Intel Xeon MP", + "Mobile Intel Pentium 4", + "Mobile Intel Celeron", + "Mobile Genuine Intel", + "Intel Celeron M", + "Mobile Intel Celeron", + "Intel Celeron", + "Mobile Genuine Intel", + "Intel Pentium M", + "Mobile Intel Celeron", + NULL }; - int bx = (r->ebx & 0xFF); + int bx = (f->ebx & 0xFF); - if (bx > 0x17) { - bx = 0; - } + if (bx > 0x17) { + bx = 0; + } - return indexes[bx]; + return indexes[bx]; } -/// -/// @param r -/// @return -char *cpuid_brand_string(register_t *r) +char *cpuid_brand_string(pt_regs *f) { - char *temp = ""; + char *temp = ""; - for (r->eax = 0x80000002; r->eax <= 0x80000004; (r->eax)++) { - r->ebx = r->ecx = r->edx = 0; - call_cpuid(r); - temp = - strncat(temp, (const char *)r->eax, strlen((const char *)r->eax)); - temp = - strncat(temp, (const char *)r->ebx, strlen((const char *)r->ebx)); - temp = - strncat(temp, (const char *)r->ecx, strlen((const char *)r->ecx)); - temp = - strncat(temp, (const char *)r->edx, strlen((const char *)r->edx)); - } + for (f->eax = 0x80000002; f->eax <= 0x80000004; (f->eax)++) { + f->ebx = f->ecx = f->edx = 0; + call_cpuid(f); + temp = + strncat(temp, (const char *)f->eax, strlen((const char *)f->eax)); + temp = + strncat(temp, (const char *)f->ebx, strlen((const char *)f->ebx)); + temp = + strncat(temp, (const char *)f->ecx, strlen((const char *)f->ecx)); + temp = + strncat(temp, (const char *)f->edx, strlen((const char *)f->edx)); + } - return temp; + return temp; } diff --git a/mentos/src/hardware/pic8259.c b/mentos/src/hardware/pic8259.c index 11cc46e..649cb7b 100644 --- a/mentos/src/hardware/pic8259.c +++ b/mentos/src/hardware/pic8259.c @@ -1,10 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file pic8259.c /// @brief pic8259 definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "pic8259.h" +#include "port_io.h" +#include "stddef.h" /// End-of-interrupt command code. #define EOI 0x20 @@ -65,94 +67,94 @@ static byte_t slave_cur_mask; void pic8259_init_irq() { - // Set the masks for the master and slave. - master_cur_mask = 0xFF; - slave_cur_mask = 0xFF; + // Set the masks for the master and slave. + master_cur_mask = 0xFF; + slave_cur_mask = 0xFF; - // Starts the initialization sequence (in cascade mode). - outportb(MASTER_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); - outportb(SLAVE_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); + // Starts the initialization sequence (in cascade mode). + outportb(MASTER_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); + outportb(SLAVE_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); - // ICW2: Master PIC vector offset. - outportb(MASTER_PORT_DATA, 0x20); - // ICW2: Slave PIC vector offset. - outportb(SLAVE_PORT_DATA, 0x28); + // ICW2: Master PIC vector offset. + outportb(MASTER_PORT_DATA, 0x20); + // ICW2: Slave PIC vector offset. + outportb(SLAVE_PORT_DATA, 0x28); - // ICW3: Tell Master PIC that there is a slave PIC at IRQ2 (0000 0100). - outportb(MASTER_PORT_DATA, 4); - // ICW3: Tell Slave PIC its cascade identity (0000 0010). - outportb(SLAVE_PORT_DATA, 2); + // ICW3: Tell Master PIC that there is a slave PIC at IRQ2 (0000 0100). + outportb(MASTER_PORT_DATA, 4); + // ICW3: Tell Slave PIC its cascade identity (0000 0010). + outportb(SLAVE_PORT_DATA, 2); - outportb(MASTER_PORT_DATA, ICW4_8086); - outportb(SLAVE_PORT_DATA, ICW4_8086); + outportb(MASTER_PORT_DATA, ICW4_8086); + outportb(SLAVE_PORT_DATA, ICW4_8086); - // Restore saved masks. - outportb(MASTER_PORT_DATA, master_cur_mask); - outportb(SLAVE_PORT_DATA, slave_cur_mask); + // Restore saved masks. + outportb(MASTER_PORT_DATA, master_cur_mask); + outportb(SLAVE_PORT_DATA, slave_cur_mask); - pic8259_irq_enable(IRQ_TO_SLAVE_PIC); + pic8259_irq_enable(IRQ_TO_SLAVE_PIC); - outportb(0xFF, MASTER_PORT_DATA); - outportb(0xFF, SLAVE_PORT_DATA); + outportb(0xFF, MASTER_PORT_DATA); + outportb(0xFF, SLAVE_PORT_DATA); } int pic8259_irq_enable(uint32_t irq) { - if (irq >= IRQ_NUM) { - return -1; - } + if (irq >= IRQ_NUM) { + return -1; + } - byte_t cur_mask; - byte_t new_mask; + byte_t cur_mask; + byte_t new_mask; - if (irq < 8) { - new_mask = ~(1 << irq); - cur_mask = inportb(MASTER_PORT_DATA); - outportb(MASTER_PORT_DATA, (new_mask & cur_mask)); - master_cur_mask = (new_mask & cur_mask); - } else { - irq -= 8; - new_mask = ~(1 << irq); - cur_mask = inportb(SLAVE_PORT_DATA); - outportb(SLAVE_PORT_DATA, (new_mask & cur_mask)); - slave_cur_mask = (new_mask & cur_mask); - } + if (irq < 8) { + new_mask = ~(1 << irq); + cur_mask = inportb(MASTER_PORT_DATA); + outportb(MASTER_PORT_DATA, (new_mask & cur_mask)); + master_cur_mask = (new_mask & cur_mask); + } else { + irq -= 8; + new_mask = ~(1 << irq); + cur_mask = inportb(SLAVE_PORT_DATA); + outportb(SLAVE_PORT_DATA, (new_mask & cur_mask)); + slave_cur_mask = (new_mask & cur_mask); + } - return 0; + return 0; } int pic8259_irq_disable(uint32_t irq) { - if (irq >= IRQ_NUM) { - return -1; - } + if (irq >= IRQ_NUM) { + return -1; + } - uint8_t cur_mask; - if (irq < 8) { - cur_mask = inportb(MASTER_PORT_DATA); - cur_mask |= (1 << irq); - outportb(MASTER_PORT_DATA, cur_mask & 0xFF); - } else { - irq = irq - 8; - cur_mask = inportb(SLAVE_PORT_DATA); - cur_mask |= (1 << irq); - outportb(SLAVE_PORT_DATA, cur_mask & 0xFF); - } - return 0; + uint8_t cur_mask; + if (irq < 8) { + cur_mask = inportb(MASTER_PORT_DATA); + cur_mask |= (1 << irq); + outportb(MASTER_PORT_DATA, cur_mask & 0xFF); + } else { + irq = irq - 8; + cur_mask = inportb(SLAVE_PORT_DATA); + cur_mask |= (1 << irq); + outportb(SLAVE_PORT_DATA, cur_mask & 0xFF); + } + return 0; } void pic8259_send_eoi(uint32_t irq) { - // Perhaps the most common command issued to the PIC chips is the end of - // interrupt (EOI) command (code 0x20). This is issued to the PIC chips - // at the end of an IRQ-based interrupt routine. If the IRQ came from the - // Master PIC, it is sufficient to issue this command only to the Master - // PIC; however if the IRQ came from the Slave PIC, it is necessary to - // issue the command to both PIC chips. - if (irq >= 8) { - outportb(SLAVE_PORT_COMMAND, EOI); - } - outportb(MASTER_PORT_COMMAND, EOI); + // Perhaps the most common command issued to the PIC chips is the end of + // interrupt (EOI) command (code 0x20). This is issued to the PIC chips + // at the end of an IRQ-based interrupt routine. If the IRQ came from the + // Master PIC, it is sufficient to issue this command only to the Master + // PIC; however if the IRQ came from the Slave PIC, it is necessary to + // issue the command to both PIC chips. + if (irq >= 8) { + outportb(SLAVE_PORT_COMMAND, EOI); + } + outportb(MASTER_PORT_COMMAND, EOI); } /* diff --git a/mentos/src/hardware/timer.c b/mentos/src/hardware/timer.c index 5a4b795..c38b7d4 100644 --- a/mentos/src/hardware/timer.c +++ b/mentos/src/hardware/timer.c @@ -1,115 +1,747 @@ /// MentOS, The Mentoring Operating system project /// @file timer.c /// @brief Timer implementation. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "timer.h" -#include "list.h" -#include "kheap.h" -#include "debug.h" -#include "stdint.h" + +#include "irqflags.h" +#include "scheduler.h" #include "pic8259.h" #include "port_io.h" -#include "irqflags.h" +#include "stdint.h" +#include "kheap.h" +#include "debug.h" +#include "wait.h" +#include "rtc.h" +#include "isr.h" +#include "fpu.h" +#include "signal.h" +#include "assert.h" +#include "errno.h" -#define SUBTICKS_PER_TICK 1000 +/// Number of ticks per seconds. +#define TICKS_PER_SECOND 1193 -/// This will keep track of how many ticks the system has been running for. -static __volatile__ uint64_t timer_subticks = 0; -/// This will keep track of how many seconds the system has been running for. -static __volatile__ uint64_t timer_ticks = 0; +/// @defgroup picregs Programmable Interval Timer Registers +/// @brief The list of registers used to set the PIT. +/// @{ -/// The list of wakeup functions. -// static list_t *wakeup_list; +/// Channel 0 data port (read/write). +#define PIT_DATAREG0 0x40u + +/// Channel 1 data port (read/write). +#define PIT_DATAREG1 0x41u + +/// Channel 2 data port (read/write). +#define PIT_DATAREG2 0x42u + +/// Mode/Command register (write only, a read is ignored). +#define PIT_COMREG 0x43u + +/// @} + +/// @brief Frequency divider value (1.193182 MHz). +#define PIT_DIVISOR 1193182 + +/// @brief Command used to configure the PIT. +/// @details +/// Bits Usage +/// 6 and 7 [Select channel] +/// 0 0 = Channel 0 +/// 0 1 = Channel 1 +/// 1 0 = Channel 2 +/// 1 1 = Read-back command (8254 only) +/// 4 and 5 [Access mode] +/// 0 0 = Latch count value command +/// 0 1 = Access mode: lobyte only +/// 1 0 = Access mode: hibyte only +/// 1 1 = Access mode: lobyte/hibyte +/// 1 to 3 [Operating mode] +/// 0 0 0 = Mode 0 (interrupt on terminal count) +/// 0 0 1 = Mode 1 (hardware re-triggerable one-shot) +/// 0 1 0 = Mode 2 (rate generator) +/// 0 1 1 = Mode 3 (square wave generator) +/// 1 0 0 = Mode 4 (software triggered strobe) +/// 1 0 1 = Mode 5 (hardware triggered strobe) +/// 1 1 0 = Mode 2 (rate generator, same as 010b) +/// 1 1 1 = Mode 3 (square wave generator, same as 011b) +/// 0 [BCD/Binary mode] +/// 0 = 16-bit binary +/// 1 = four-digit BCD +/// +/// Examples: +/// 0x36 = 00|11|011|0 +/// 0x34 = 00|11|010|0 +#define PIT_CONFIGURATION 0x34u + +/// Mask used to set the divisor. +#define PIT_MASK 0xFFu + +/// The number of ticks since the system started its execution. +static __volatile__ unsigned long timer_ticks = 0; void timer_phase(const uint32_t hz) { - // Calculate our divisor. - uint32_t divisor = PIT_DIVISOR / hz; - // Set our command byte 0x36. - outportb(PIT_COMREG, PIT_CONFIGURATION); - // Set low byte of divisor. - outportb(PIT_DATAREG0, divisor & PIT_MASK); - // Set high byte of divisor. - outportb(PIT_DATAREG0, (divisor >> 8) & PIT_MASK); + // Calculate our divisor. + unsigned int divisor = PIT_DIVISOR / hz; + // Set our command byte 0x36. + outportb(PIT_COMREG, PIT_CONFIGURATION); + // Set low byte of divisor. + outportb(PIT_DATAREG0, divisor & PIT_MASK); + // Set high byte of divisor. + outportb(PIT_DATAREG0, (divisor >> 8u) & PIT_MASK); } void timer_handler(pt_regs *reg) { - (void)reg; - // Check if a second has passed. - if ((++timer_subticks % SUBTICKS_PER_TICK) == 0) { - ++timer_ticks; - } - /* - * if (!list_empty(wakeup_list)) - * { - * listnode_foreach(t, wakeup_list) - * { - * wakeup_info_t *wakeupInfo = (wakeup_info_t *) t->value; - * if (timer_subticks >= wakeupInfo->wakeup_at_jiffy) - * { - * // Call the function. - * wakeupInfo->func(); - * // Reset the timer. - * wakeupInfo->wakeup_at_jiffy = - * timer_subticks + wakeupInfo->period * timer_hz; - * } - * { - * } - */ - - kernel_schedule(reg); - // The ack is sent to PIC only when all handlers terminated! - pic8259_send_eoi(IRQ_TIMER); + // Save current process fpu state. + switch_fpu(); + // Check if a second has passed. + ++timer_ticks; + // Update all timers + run_timer_softirq(); + // Perform the schedule. + scheduler_run(reg); + // Restore fpu state. + unswitch_fpu(); + // The ack is sent to PIC only when all handlers terminated! + pic8259_send_eoi(IRQ_TIMER); } void timer_install() { - // Intialize the wakeup list (before installing the timer). - // wakeup_list = list_create(); - // Set the timer phase. - timer_phase(SUBTICKS_PER_TICK); - // Installs 'timer_handler' to IRQ0. - irq_install_handler(IRQ_TIMER, timer_handler, "timer"); - // Enable the IRQ of the itemer. - pic8259_irq_enable(IRQ_TIMER); + dynamic_timers_install(); + + // Set the timer phase. + timer_phase(TICKS_PER_SECOND); + // Installs 'timer_handler' to IRQ0. + irq_install_handler(IRQ_TIMER, timer_handler, "timer"); + // Enable the IRQ of the timer. + pic8259_irq_enable(IRQ_TIMER); } -uint64_t timer_get_ticks() +uint64_t timer_get_seconds() { - return timer_ticks; + return timer_ticks / TICKS_PER_SECOND; } -uint64_t timer_get_subticks() +unsigned long timer_get_ticks() { - return timer_subticks; + return timer_ticks; } -/* - * void timer_register_wakeup_call(wakeup_callback_t func, uint32_t period) - * { - * - * // Save the function sec, and jiffy to a list, when timer hits that - * // func's jiffy it will call the func, and update next jiffies. - * - * wakeup_info_t *wakeupInfo = kmalloc(sizeof(wakeup_info_t)); - * - * wakeupInfo->func = func; - * - * wakeupInfo->wakeup_at_jiffy = timer_subticks + period * timer_hz; - * - * wakeupInfo->period = period; - * - * list_insert_front(wakeup_list, wakeupInfo); - * } - */ +//====================================================================================== +// Dynamics timers -void sleep(unsigned int seconds) +/// Contains timer for each CPU (for now only one) +static tvec_base_t cpu_base = { 0 }; + +/// Contains all process waiting for a sleep +static wait_queue_head_t sleep_queue; + +/// @brief Initialize dynamic timer system +void dynamic_timers_install() { - uint32_t future_time = timer_ticks + seconds; +#ifndef ENABLE_REAL_TIMER_SYSTEM + list_head_init(&cpu_base.list); +#endif - while (timer_ticks <= future_time) - ; + // Initialize tvec_base structure + tvec_base_t *base = &cpu_base; + base->timer_ticks = 0; + + for(int i = 0; i < TVR_SIZE; ++i) + list_head_init(base->tv1.vec + i); + + for(int i = 0; i < TVN_SIZE; ++i) { + + list_head_init(base->tv2.vec + i); + list_head_init(base->tv3.vec + i); + list_head_init(base->tv4.vec + i); + list_head_init(base->tv5.vec + i); + } + + // Initialize sleeping process list + list_head_init(&sleep_queue.task_list); + spinlock_init(&sleep_queue.lock); } + +/// Prints used slots of timer vector +static void __print_tvec_slots(tvec_base_t *base, int tv_index) { + + if (tv_index < 0 || tv_index > 5) + return; + + // Write buffer + char result[TVN_SIZE + 1]; + result[TVN_SIZE] = '\0'; + + struct timer_vec* tv = NULL; + switch(tv_index) { + + // Root + case 1: { + + pr_debug("base->tv1.vec:"); + for(int i = 0; i < TVR_SIZE; ++i) { + + // New line in order to not clutter the screen + int index = i % TVN_SIZE; + if (i != 0 && index == 0) + pr_debug("\n\t%s", result); + + if (!list_head_empty(base->tv1.vec + i)) + result[index] = '1'; + else + result[index] = '0'; + } + + // The last line + pr_debug("\n\t%s\n", result); + return; + + } break; + + // Normal + case 2: tv = &base->tv2; break; + case 3: tv = &base->tv3; break; + case 4: tv = &base->tv4; break; + case 5: tv = &base->tv5; break; + } + + for(int i = 0; i < TVN_SIZE; ++i) { + if (list_head_empty(tv->vec + i)) + result[i] = '0'; + else + result[i] = '1'; + } + + pr_debug("base->tv%d.vec:\n\t%s\n", tv_index, result); +} + +/// Dump all timer vector in base +static inline void __dump_all_tvec_slots(tvec_base_t *base) { + __print_tvec_slots(base, 1); + __print_tvec_slots(base, 2); + __print_tvec_slots(base, 3); + __print_tvec_slots(base, 4); + __print_tvec_slots(base, 5); +} + +/// Select correct timer vector and position inside of it for the input timer +/// index contains the position inside of the tv_index timer vector +static void __find_tvec(tvec_base_t *base, struct timer_list *timer, int* index, int* tv_index) +{ + assert(index && "index is NULL"); + assert(tv_index && "tv_index is NULL"); + + unsigned long expires = timer->expires; + unsigned long ticks = expires - base->timer_ticks; + + unsigned long tv1_ticks = TIMER_TICKS(0); + unsigned long tv2_ticks = TIMER_TICKS(1); + unsigned long tv3_ticks = TIMER_TICKS(2); + unsigned long tv4_ticks = TIMER_TICKS(3); + + // Can happen if you add a timer with expires == ticks, or in the past + if ((signed long)ticks < 0) { + *index = base->timer_ticks & TVR_MASK; + *tv_index = 1; + } + // tv1 + else if (ticks < tv1_ticks) { + *index = expires & TVR_MASK; + *tv_index = 1; + } + // tv2 + else if (ticks < tv2_ticks) { + *index = (expires >> TIMER_TICKS_BITS(0)) & TVN_MASK; + *tv_index = 2; + } + // tv3 + else if (ticks < tv3_ticks) { + *index = (expires >> TIMER_TICKS_BITS(1)) & TVN_MASK; + *tv_index = 3; + } + // tv4 + else if (ticks < tv4_ticks) { + *index = (expires >> TIMER_TICKS_BITS(2)) & TVN_MASK; + *tv_index = 4; + } + // tv5 + else { + *index = (expires >> TIMER_TICKS_BITS(3)) & TVN_MASK; + *tv_index = 5; + } +} + +/// Add timers into different lists based on their expire time +static void __add_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) { + + int index = 0, tv_index = 0; + __find_tvec(base, timer, &index, &tv_index); + + struct list_head* vec; + switch(tv_index) { + case 1: vec = base->tv1.vec + index; break; + case 2: vec = base->tv2.vec + index; break; + case 3: vec = base->tv3.vec + index; break; + case 4: vec = base->tv4.vec + index; break; + case 5: vec = base->tv5.vec + index; break; + } + + pr_debug("Adding timer at time_index: %d in tv%d\n", index, tv_index); + list_head_add_tail(&timer->entry, vec); + +#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP + __dump_all_tvec_slots(base); +#endif + +} + +/// Remove timer from tvec_base +static void __rem_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) { + + int index = 0, tv_index = 0; + __find_tvec(base, timer, &index, &tv_index); + + struct list_head* vec; + switch(tv_index) { + case 1: vec = base->tv1.vec + index; break; + case 2: vec = base->tv2.vec + index; break; + case 3: vec = base->tv3.vec + index; break; + case 4: vec = base->tv4.vec + index; break; + case 5: vec = base->tv5.vec + index; break; + } + + pr_debug("Removing timer at time_index: %d in tv%d\n", index, tv_index); + list_head_del(&timer->entry); + +#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP + __dump_all_tvec_slots(base); +#endif + +} + +/// Move all timers from tv up one level +static int cascate(tvec_base_t* base, timer_vec* tv, int time_index, int tv_index) { + + if (!list_head_empty(tv->vec + time_index)) { + pr_debug("Relocating timers in tv%d.vec[%d]\n", tv_index, time_index); + + // Reinsert all timers into base in the new correct list + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, tv->vec + time_index) { + struct timer_list *timer = list_entry(it, struct timer_list, entry); + list_head_del(it); + + __add_timer_tvec_base(base, timer); + } + + } + return time_index; +} + +void run_timer_softirq() +{ + tvec_base_t *base = &cpu_base; + spinlock_lock(&base->lock); + +#ifdef ENABLE_REAL_TIMER_SYSTEM + + // While we are not up to date with current ticks + unsigned long current_ticks = timer_get_ticks(); + while (base->timer_ticks <= current_ticks) { + + // Index of the current timer to execute + int current_time_index = base->timer_ticks & TVR_MASK; + + // If the index is zero then all lists in base->tv1 have been checked, so they are empty + if (!current_time_index) { + + // Consider the first invocation of the cascade() function: it receives as arguments + // the address in base, the address of base->tv2, and the index of the list + // in base->tv2 including the timers that will decay in the next 256 ticks. This + // index is determined by looking at the proper bits of the base->timer_ticks value. + // cascade() moves all dynamic timers in the base->tv2 list into the + // proper lists of base->tv1; then, it returns a positive value, unless all base->tv2 + // lists are now empty. If so, cascade() is invoked once more to replenish + // base->tv2 with the timers included in a list of base->tv3, and so on. + + int tv2_index = (base->timer_ticks >> TIMER_TICKS_BITS(0)) & TVN_MASK; + int tv3_index = (base->timer_ticks >> TIMER_TICKS_BITS(1)) & TVN_MASK; + int tv4_index = (base->timer_ticks >> TIMER_TICKS_BITS(2)) & TVN_MASK; + int tv5_index = (base->timer_ticks >> TIMER_TICKS_BITS(3)) & TVN_MASK; + + if (!cascate(base, &base->tv2, tv2_index, 2) && + !cascate(base, &base->tv3, tv3_index, 3) && + !cascate(base, &base->tv4, tv4_index, 4) && + !cascate(base, &base->tv5, tv5_index, 5)); + } + + // If there are timers to execute in this instant + if (!list_head_empty(&base->tv1.vec[current_time_index])) { + pr_notice("Executing dynamic timers at %d ticks from start inside of tv1.vec[%d]\n", + base->timer_ticks, current_time_index); + + // Trigger all timers + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, &base->tv1.vec[current_time_index]) { + struct timer_list *timer = list_entry(it, struct timer_list, entry); + + // Executes timer function + spinlock_unlock(&base->lock); + pr_notice("Executing dynamic timer function...\n"); + timer->function(timer->data); + spinlock_lock(&base->lock); + + // Removes timer from list + list_head_del(it); + kfree(timer); + } + } + + // Advance timer check + ++base->timer_ticks; + } + + base->running_timer = NULL; + +#else + + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, &base->list) { + struct timer_list *timer = list_entry(it, struct timer_list, entry); + if (timer->expires <= timer_get_ticks()) { + base->running_timer = timer; + timer->base = NULL; + + // Executes timer function + spinlock_unlock(&base->lock); + pr_notice("Executing dynamic timer function...\n"); + timer->function(timer->data); + spinlock_lock(&base->lock); + + // Removes timer from list + pr_notice("Removing dynamic timer...\n"); + list_head_del(it); + kfree(timer); + } + } + +#endif + + base->running_timer = NULL; + spinlock_unlock(&base->lock); +} + +void init_timer(struct timer_list *timer) +{ + timer->base = NULL; + list_head_init(&timer->entry); + spinlock_unlock(&timer->lock); +} + +void add_timer(struct timer_list *timer) +{ + tvec_base_t *base = &cpu_base; + timer->base = base; + +#ifdef ENABLE_REAL_TIMER_SYSTEM + __add_timer_tvec_base(base, timer); +#else + list_head_add_tail(&timer->entry, &base->list); +#endif + +} + +void del_timer(struct timer_list *timer) +{ + tvec_base_t *base = &cpu_base; + timer->base = NULL; + +#ifdef ENABLE_REAL_TIMER_SYSTEM + __rem_timer_tvec_base(base, timer); +#else + list_head_del(&timer->entry); +#endif + +} + +//====================================================================================== +// Sleep + +/// @brief Debugging function. +/// @param data The data. +static inline void debug_timeout(unsigned long data) +{ + pr_notice("Il timer è stato attivato con successo: %d, ticks: %d, seconds: %d\n", + data, timer_ticks, timer_get_seconds()); +} + +/// @brief Contains the entry of a wait queue and timespec which keeps trakc of +/// the remaining time. +typedef struct sleep_data_t { + /// POinter to the entry of a wait queue. + wait_queue_entry_t *entry; + /// Keeps track of the remaining time. + timespec *rem; +} sleep_data_t; + +/// @brief Callback for when a sleep timer expires +/// @param data Custom data stored in the timer +void sleep_timeout(unsigned long data) +{ + // NOTE: We could modify the sleep_on and make it return the wait_queue_entry_t + // and then store it in the dynamic timer data member instead of the task pid, + // this would remove the need to iterate the sleep queue list. + + sleep_data_t *sleep_data = (sleep_data_t *)data; + + wait_queue_entry_t *entry = sleep_data->entry; + task_struct *task = entry->task; + + // Executed entry's wakeup test function + int res = entry->func(entry, 0, 0); + if (res == 1) { + // Removes entry from list and memory + remove_wait_queue(&sleep_queue, entry); + kfree(entry); + + pr_debug("Process (pid: %d) restored from sleep\n", task->pid); + } +} + +int sys_nanosleep(const timespec *req, timespec *rem) +{ + // Probabilmente devi salvare rem da qualche parte, perche' dentro ci va + // messo quanto tempo mancava allo scadere del timer nel caso in cui il + // timer venga interrotto prima da un segnale. + pr_debug("sys_nanosleep([s:%d; ns:%d],...)\n", req->tv_sec, req->tv_nsec); + + // Saves pid and rem timespec + sleep_data_t *data = kmalloc(sizeof(sleep_data_t)); + data->rem = rem; + + // Create a dinamic timer to wake up the process after some time + struct timer_list *sleep_timer = kmalloc(sizeof(struct timer_list)); + init_timer(sleep_timer); + + sleep_timer->expires = timer_get_ticks() + TICKS_PER_SECOND * req->tv_sec; + sleep_timer->function = &sleep_timeout; + sleep_timer->data = (unsigned long)data; + + // Removes current process from runqueue and stores it in the waiting queue, + // this must be done at the end, because it changes the current active page + // and invalidates the req and rem pointers (?) + wait_queue_entry_t *entry = sleep_on(&sleep_queue); + data->entry = entry; + + add_timer(sleep_timer); + return -1; +} + +/// @brief Function executed when the real_timer of a process expires, sends SIGALRM to process. +/// @param pid PID of the process whos associated timer has expired +void alarm_timeout(unsigned long pid) +{ + sys_kill(pid, SIGALRM); + + struct task_struct *cur = scheduler_get_current_process(); + cur->real_timer = NULL; +} + +int sys_alarm(int seconds) +{ + pr_debug("sys_alarm(seconds:%d)\n", seconds); + + struct task_struct *current = scheduler_get_current_process(); + struct timer_list *timer; + + // If there is already a timer running + int result = 0; + if (current->real_timer != NULL) { + del_timer(current->real_timer); + result = (current->real_timer->expires - timer_get_ticks()) / TICKS_PER_SECOND; + timer = current->real_timer; + + // Returns only the amount of seconds remaining + if (seconds == 0) { + kfree(current->real_timer); + current->real_timer = NULL; + return result; + } + } else { + if (seconds == 0) + return 0; + + // Allocate new timer + timer = (struct timer_list *)kmalloc(sizeof(struct timer_list)); + } + + current->real_timer = timer; + init_timer(timer); + + timer->expires = timer_get_ticks() + TICKS_PER_SECOND * seconds; + timer->function = &alarm_timeout; + timer->data = current->pid; + + add_timer(timer); + + return result; +} + +static void calc_itimerval(unsigned long incr, unsigned long value, struct itimerval *result) +{ + result->it_interval.tv_sec = incr / TICKS_PER_SECOND; + result->it_interval.tv_usec = incr / TICKS_PER_SECOND * 1000; + + result->it_value.tv_sec = value / TICKS_PER_SECOND; + result->it_value.tv_usec = value / TICKS_PER_SECOND * 1000; +} + +static void update_task_itimerval(int which, const struct itimerval *val) +{ + unsigned long interval_ticks = val->it_interval.tv_sec * TICKS_PER_SECOND; + interval_ticks += val->it_interval.tv_usec * TICKS_PER_SECOND / 1000; + + unsigned long value_ticks = val->it_value.tv_sec * TICKS_PER_SECOND; + value_ticks += val->it_value.tv_usec * TICKS_PER_SECOND / 1000; + + struct task_struct *curr = scheduler_get_current_process(); + switch (which) { + case ITIMER_REAL: + curr->it_real_incr = interval_ticks; + curr->it_real_value = value_ticks; + break; + + case ITIMER_VIRTUAL: + curr->it_virt_incr = interval_ticks; + curr->it_virt_value = value_ticks; + break; + + case ITIMER_PROF: + curr->it_prof_incr = interval_ticks; + curr->it_prof_value = value_ticks; + break; + } +} + +int sys_getitimer(int which, struct itimerval *curr_value) +{ + // Invalid time domain + if (which < 0 || which > 3) + return EINVAL; + + struct task_struct *curr = scheduler_get_current_process(); + switch (which) { + case ITIMER_REAL: { + // Extract remaining time in dynamic timer + unsigned long value = curr->real_timer->expires - timer_get_ticks(); + curr->it_real_value = value; + calc_itimerval(curr->it_real_incr, curr->it_real_value, curr_value); + } break; + case ITIMER_VIRTUAL: + calc_itimerval(curr->it_virt_incr, curr->it_virt_value, curr_value); + break; + case ITIMER_PROF: + calc_itimerval(curr->it_prof_incr, curr->it_prof_value, curr_value); + break; + } + return 0; +} + +// Real timer interval timemout +static void it_real_fn(unsigned long pid) +{ + struct task_struct *cur = scheduler_get_running_process(pid); + sys_kill(pid, SIGALRM); + + // If the real incr is not 0 then restart + if (cur->it_real_incr != 0) { + // Create new timer for process + struct timer_list *real_timer = (struct timer_list *)kmalloc(sizeof(struct timer_list)); + cur->real_timer = real_timer; + init_timer(real_timer); + + real_timer->expires = timer_get_ticks() + cur->it_real_incr; + real_timer->function = &it_real_fn; + real_timer->data = cur->pid; + + add_timer(real_timer); + return; + } + + // No more timer + cur->real_timer = NULL; +} + +int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) +{ + // Invalid time domain + if (which < 0 || which > 3) + return EINVAL; + + // Returns old timer interval + if (old_value != NULL) + sys_getitimer(which, old_value); + + // Get ticks of interval + unsigned long interval_ticks = new_value->it_interval.tv_sec * TICKS_PER_SECOND; + interval_ticks += new_value->it_interval.tv_usec * TICKS_PER_SECOND / 1000; + + // If interval is 0 removes timer + struct task_struct *cur = scheduler_get_current_process(); + if (interval_ticks == 0) { + // Removes real_timer + if (which == ITIMER_REAL && cur->real_timer != NULL) + cur->real_timer = NULL; + + update_task_itimerval(which, new_value); + return -1; + } + + switch (which) { + // Uses Dynamic Timers + case ITIMER_REAL: { + // Remove real_timer if already in use + struct timer_list *timer = cur->real_timer; + if (timer != NULL) { + del_timer(timer); // Recycle memory + } else { + // Alloc new timer + timer = (struct timer_list *)kmalloc(sizeof(struct timer_list)); + } + + init_timer(timer); + + timer->expires = timer_get_ticks() + interval_ticks; + timer->function = &it_real_fn; + timer->data = cur->pid; + + add_timer(timer); + + } break; + + case ITIMER_VIRTUAL: + case ITIMER_PROF: + break; + } + + update_task_itimerval(which, new_value); + return -1; +} + +void update_process_profiling_timer(task_struct *proc) +{ + // If the timer is active + if (proc->it_prof_incr != 0) { + proc->it_prof_value += proc->se.exec_runtime; + if (proc->it_prof_value >= proc->it_prof_incr) { + sys_kill(proc->pid, SIGPROF); + proc->it_prof_value = 0; + } + } +} \ No newline at end of file diff --git a/mentos/src/io/mm_io.c b/mentos/src/io/mm_io.c index b39caa1..0b215b5 100644 --- a/mentos/src/io/mm_io.c +++ b/mentos/src/io/mm_io.c @@ -1,37 +1,37 @@ /// MentOS, The Mentoring Operating system project /// @file mm_io.c /// @brief Memory Mapped IO functions implementation. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "mm_io.h" uint8_t in_memb(uint32_t addr) { - return *((uint8_t *) (addr)); + return *((uint8_t *)(addr)); } uint16_t in_mems(uint32_t addr) { - return *((uint16_t *) (addr)); + return *((uint16_t *)(addr)); } uint32_t in_meml(uint32_t addr) { - return *((uint32_t *) (addr)); + return *((uint32_t *)(addr)); } void out_memb(uint32_t addr, uint8_t value) { - (*((uint8_t *) (addr))) = (value); + (*((uint8_t *)(addr))) = (value); } void out_mems(uint32_t addr, uint16_t value) { - (*((uint16_t *) (addr))) = (value); + (*((uint16_t *)(addr))) = (value); } void out_meml(uint32_t addr, uint32_t value) { - (*((uint32_t *) (addr))) = (value); + (*((uint32_t *)(addr))) = (value); } diff --git a/mentos/src/io/port_io.c b/mentos/src/io/port_io.c index ee85812..7da53bb 100644 --- a/mentos/src/io/port_io.c +++ b/mentos/src/io/port_io.c @@ -1,59 +1,71 @@ /// MentOS, The Mentoring Operating system project /// @file port_io.c /// @brief Byte I/O on ports prototypes. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "port_io.h" inline uint8_t inportb(uint16_t port) { - unsigned char data = 0; - __asm__ __volatile__("inb %%dx, %%al" : "=a"(data) : "d"(port)); + unsigned char data = 0; + __asm__ __volatile__("inb %%dx, %%al" + : "=a"(data) + : "d"(port)); - return data; + return data; } inline uint16_t inports(uint16_t port) { - uint16_t rv; - __asm__ __volatile__("inw %1, %0" : "=a"(rv) : "dN"(port)); + uint16_t rv; + __asm__ __volatile__("inw %1, %0" + : "=a"(rv) + : "dN"(port)); - return rv; + return rv; } void inportsm(uint16_t port, uint8_t *data, unsigned long size) { - __asm__ __volatile__("rep insw" - : "+D"(data), "+c"(size) - : "d"(port) - : "memory"); + __asm__ __volatile__("rep insw" + : "+D"(data), "+c"(size) + : "d"(port) + : "memory"); } inline uint32_t inportl(uint16_t port) { - uint32_t rv; - __asm__ __volatile__("inl %%dx, %%eax" : "=a"(rv) : "dN"(port)); + uint32_t rv; + __asm__ __volatile__("inl %%dx, %%eax" + : "=a"(rv) + : "dN"(port)); - return rv; + return rv; } inline void outportb(uint16_t port, uint8_t data) { - __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); + __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); } inline void outports(uint16_t port, uint16_t data) { - __asm__ __volatile__("outw %1, %0" : : "dN"(port), "a"(data)); + __asm__ __volatile__("outw %1, %0" + : + : "dN"(port), "a"(data)); } void outportsm(uint16_t port, uint8_t *data, uint16_t size) { - asm volatile("rep outsw" : "+S"(data), "+c"(size) : "d"(port)); + asm volatile("rep outsw" + : "+S"(data), "+c"(size) + : "d"(port)); } inline void outportl(uint16_t port, uint32_t data) { - __asm__ __volatile__("outl %%eax, %%dx" : : "dN"(port), "a"(data)); + __asm__ __volatile__("outl %%eax, %%dx" + : + : "dN"(port), "a"(data)); } diff --git a/mentos/src/io/proc_running.c b/mentos/src/io/proc_running.c new file mode 100644 index 0000000..02596c2 --- /dev/null +++ b/mentos/src/io/proc_running.c @@ -0,0 +1,488 @@ +// +// Created by andrea on 02/05/20. +// + +#include "procfs.h" + +#include "process.h" +#include "string.h" +#include "libgen.h" +#include "stdio.h" +#include "errno.h" +#include "debug.h" +#include "prio.h" + +/// @brief Returns the character identifying the process state. +/// @details +/// R Running +/// S Sleeping in an interruptible wait +/// D Waiting in uninterruptible disk sleep +/// Z Zombie +/// T Stopped +/// t Tracing stop +/// X Dead +static inline char get_task_state_char(int state) +{ + if (state == 0x00) // TASK_RUNNING + return 'R'; + if (state == (1 << 0)) // TASK_INTERRUPTIBLE + return 'S'; + if (state == (1 << 1)) // TASK_UNINTERRUPTIBLE + return 'D'; + if (state == (1 << 2)) // TASK_STOPPED + return 'T'; + if (state == (1 << 3)) // TASK_TRACED + return 't'; + if (state == (1 << 4)) // EXIT_ZOMBIE + return 'Z'; + if (state == (1 << 5)) // EXIT_DEAD + return 'X'; + return '?'; +} + +static ssize_t procr_do_cmdline(char *buffer, size_t bufsize, task_struct *task); + +static ssize_t procr_do_stat(char *buffer, size_t bufsize, task_struct *task); + +static ssize_t procr_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (file == NULL) + return -EFAULT; + // Get the entry. + proc_dir_entry_t *entry = (proc_dir_entry_t *)file->device; + if (entry == NULL) + return -EFAULT; + // Get the task. + task_struct *task = (task_struct *)entry->data; + if (task == NULL) + return -EFAULT; + // Prepare a buffer. + char buffer[BUFSIZ]; + memset(buffer, 0, BUFSIZ); + // Call the specific function. + int ret = 0; + if (strcmp(entry->name, "cmdline") == 0) + ret = procr_do_cmdline(buffer, BUFSIZ, task); + else if (strcmp(entry->name, "stat") == 0) + ret = procr_do_stat(buffer, BUFSIZ, task); + // Perform read. + ssize_t it = 0; + if (ret == 1) { + size_t name_len = strlen(buffer); + size_t read_pos = offset; + if (read_pos < name_len) { + while ((it < nbyte) && (read_pos < name_len)) { + *buf++ = buffer[read_pos]; + ++read_pos; + ++it; + } + } + } + return it; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procv_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procv_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procr_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = NULL, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +int proc_create_entry_pid(task_struct *entry) +{ + char path[PATH_MAX]; + proc_dir_entry_t *proc_dir = NULL, *proc_entry = NULL; + { + // Create `/proc/[PID]`. + sprintf(path, "%d", entry->pid); + // Create the proc entry root directory. + if ((proc_dir = proc_mkdir(path, NULL)) == NULL) { + pr_err("[task: %d] Cannot create proc root directory `%s`.\n", entry->pid, path); + return -ENOENT; + } + proc_dir->data = entry; + } + { + // Create `/proc/[PID]/cmdline`. + if ((proc_entry = proc_create_entry("cmdline", proc_dir)) == NULL) { + pr_err("[task: %d] Cannot create proc entry `%s`.\n", entry->pid, path); + return -ENOENT; + } + proc_entry->sys_operations = &procv_sys_operations; + proc_entry->fs_operations = &procv_fs_operations; + proc_entry->data = entry; + } + { + // Create `/proc/[PID]/stat`. + if ((proc_entry = proc_create_entry("stat", proc_dir)) == NULL) { + pr_err("[task: %d] Cannot create proc entry `%s`.\n", entry->pid, path); + return -ENOENT; + } + proc_entry->sys_operations = &procv_sys_operations; + proc_entry->fs_operations = &procv_fs_operations; + proc_entry->data = entry; + } + return 0; +} + +int proc_destroy_entry_pid(task_struct *entry) +{ + // Turn the pid into string. The maximum pid is 32768, thus entry pid is at most 6 chars. + char pid_str[6]; + sprintf(pid_str, "%d", entry->pid); + // Get the root directory. + proc_dir_entry_t *proc_dir = proc_dir_entry_get(pid_str, NULL); + if (proc_dir == NULL) { + pr_err("[task: %d] Cannot find proc root directory `%s`.\n", entry->pid, pid_str); + return -ENOENT; + } + // Destroy `/proc/[PID]/cmdline`. + if (proc_destroy_entry("cmdline", proc_dir)) { + pr_err("[task: %d] Cannot destroy proc cmdline.\n", entry->pid); + return -ENOENT; + } + // Destroy `/proc/[PID]/stat`. + if (proc_destroy_entry("stat", proc_dir)) { + pr_err("[task: %d] Cannot destroy proc stat.\n", entry->pid); + return -ENOENT; + } + // Destroy `/proc/[PID]`. + if (proc_rmdir(pid_str, NULL)) { + pr_err("[task: %d] Cannot remove proc root directory `%s`.\n", entry->pid, pid_str); + return -ENOENT; + } + return 0; +} + +static ssize_t procr_do_cmdline(char *buffer, size_t bufsize, task_struct *task) +{ + strcpy(buffer, task->name); + return 1; +} + +static ssize_t procr_do_stat(char *buffer, size_t bufsize, task_struct *task) +{ + //(1) pid %d + // The process ID. + // + sprintf(buffer, "%d", task->pid); + //(2) comm %s + // The filename of the executable, in parentheses. + // Strings longer than TASK_COMM_LEN (16) characters (in‐ + // cluding the terminating null byte) are silently trun‐ + // cated. This is visible whether or not the executable + // is swapped out. + // + sprintf(buffer, "%s (%s)", buffer, basename(task->name)); + //(3) state %c + // One of the following characters, indicating process state: + // R Running + // S Sleeping in an interruptible wait + // D Waiting in uninterruptible disk sleep + // Z Zombie + // T Stopped + // t Tracing stop + // X Dead + sprintf(buffer, "%s %c", buffer, get_task_state_char(task->state)); + //(4) ppid %d + // The PID of the parent of this process. + // + if (task->parent) + sprintf(buffer, "%s %d", buffer, task->parent->pid); + else + strcat(buffer, " 0"); + //(5) TODO: pgrp %d + // The process group ID of the process. + // + strcat(buffer, " 0"); + //(6) TODO: session %d + // The session ID of the process. + // + strcat(buffer, " 0"); + //(7) TODO: tty_nr %d + // The controlling terminal of the process. (The minor + // device number is contained in the combination of bits + // 31 to 20 and 7 to 0; the major device number is in bits + // 15 to 8.) + // + strcat(buffer, " 0"); + //(8) TODO: tpgid %d + // The ID of the foreground process group of the control‐ + // ling terminal of the process. + // + strcat(buffer, " 0"); + //(9) TODO: flags %u + // The kernel flags word of the process. For bit mean‐ + // ings, see the PF_* defines in the Linux kernel source + // file include/linux/sched.h. Details depend on the ker‐ + // nel version. + // The format for this field was %lu before Linux 2.6. + // + strcat(buffer, " 0"); + //(10) TODO: minflt %lu + // The number of minor faults the process has made which + // have not required loading a memory page from disk. + // + strcat(buffer, " 0"); + //(11) TODO: cminflt %lu + // The number of minor faults that the process's waited- + // for children have made. + // + strcat(buffer, " 0"); + //(12) TODO: majflt %lu + // The number of major faults the process has made which + // have required loading a memory page from disk. + // + strcat(buffer, " 0"); + //(13) TODO: cmajflt %lu + // The number of major faults that the process's waited- + // for children have made. + // + strcat(buffer, " 0"); + //(14) TODO: utime %lu + // Amount of time that this process has been scheduled in + // user mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). This includes guest time, + // guest_time (time spent running a virtual CPU, see be‐ + // low), so that applications that are not aware of the + // guest time field do not lose that time from their cal‐ + // culations. + // + strcat(buffer, " 0"); + //(15) TODO: stime %lu + // Amount of time that this process has been scheduled in + // kernel mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(16) TODO: cutime %ld + // Amount of time that this process's waited-for children + // have been scheduled in user mode, measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). (See also + // times(2).) This includes guest time, cguest_time (time + // spent running a virtual CPU, see below). + // + strcat(buffer, " 0"); + //(17) TODO: cstime %ld + // Amount of time that this process's waited-for children + // have been scheduled in kernel mode, measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(18) priority %ld + // (Explanation for Linux 2.6) For processes running a + // real-time scheduling policy (policy below; see + // sched_setscheduler(2)), this is the negated scheduling + // priority, minus one; that is, a number in the range -2 + // to -100, corresponding to real-time priorities 1 to 99. + // For processes running under a non-real-time scheduling + // policy, this is the raw nice value (setpriority(2)) as + // represented in the kernel. The kernel stores nice val‐ + // ues as numbers in the range 0 (high) to 39 (low), cor‐ + // responding to the user-visible nice range of -20 to 19. + // + // Before Linux 2.6, this was a scaled value based on the + // scheduler weighting given to this process. + // + sprintf(buffer, "%s %ld", buffer, task->se.prio); + //(19) nice %ld + // The nice value (see setpriority(2)), a value in the + // range 19 (low priority) to -20 (high priority). + // + sprintf(buffer, "%s %ld", buffer, PRIO_TO_NICE(task->se.prio)); + //(20) TODO: num_threads %ld + // Number of threads in this process (since Linux 2.6). + // Before kernel 2.6, this field was hard coded to 0 as a + // placeholder for an earlier removed field. + // + strcat(buffer, " 0"); + //(21) TODO: itrealvalue %ld + // The time in jiffies before the next SIGALRM is sent to + // the process due to an interval timer. Since kernel + // 2.6.17, this field is no longer maintained, and is hard + // coded as 0. + // + strcat(buffer, " 0"); + //(22) starttime %llu + // The time the process started after system boot. In + // kernels before Linux 2.6, this value was expressed in + // jiffies. Since Linux 2.6, the value is expressed in + // clock ticks (divide by sysconf(_SC_CLK_TCK)). + // + // The format for this field was %lu before Linux 2.6. + // + sprintf(buffer, "%s %lu", buffer, task->se.exec_start); + //(23) vsize %lu + // Virtual memory size in bytes. + // + sprintf(buffer, "%s %lu", buffer, task->mm->total_vm); + //(24) TODO: rss %ld + // Resident Set Size: number of pages the process has in + // real memory. This is just the pages which count toward + // text, data, or stack space. This does not include + // pages which have not been demand-loaded in, or which + // are swapped out. This value is inaccurate; see + // /proc/[pid]/statm below. + // + strcat(buffer, " 0"); + //(25) TODO: rsslim %lu + // Current soft limit in bytes on the rss of the process; + // see the description of RLIMIT_RSS in getrlimit(2). + // + strcat(buffer, " 0"); + //(26) startcode %lu [PT] + // The address above which program text can run. + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_code); + //(27) endcode %lu [PT] + // The address below which program text can run. + // + sprintf(buffer, "%s %lu", buffer, task->mm->end_code); + //(28) startstack %lu [PT] + // The address of the start (i.e., bottom) of the stack. + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_stack); + //(29) kstkesp %lu [PT] + // The current value of ESP (stack pointer), as found in + // the kernel stack page for the process. + // + sprintf(buffer, "%s %lu", buffer, task->thread.regs.useresp); + //(30) kstkeip %lu [PT] + // The current EIP (instruction pointer). + // + sprintf(buffer, "%s %lu", buffer, task->thread.regs.eip); + //(31) TODO: signal %lu + // The bitmap of pending signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(32) TODO: blocked %lu + // The bitmap of blocked signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(33) TODO: sigignore %lu + // The bitmap of ignored signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(34) TODO: sigcatch %lu + // The bitmap of caught signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(35) TODO: wchan %lu [PT] + // This is the "channel" in which the process is waiting. + // It is the address of a location in the kernel where the + // process is sleeping. The corresponding symbolic name + // can be found in /proc/[pid]/wchan. + // + strcat(buffer, " 0"); + //(36) TODO: nswap %lu + // Number of pages swapped (not maintained). + // + strcat(buffer, " 0"); + //(37) TODO: cnswap %lu + // Cumulative nswap for child processes (not maintained). + // + strcat(buffer, " 0"); + //(38) TODO: exit_signal %d (since Linux 2.1.22) + // Signal to be sent to parent when we die. + // + strcat(buffer, " 0"); + //(39) TODO: processor %d (since Linux 2.2.8) + // CPU number last executed on. + // + strcat(buffer, " 0"); + //(40) TODO: rt_priority %u (since Linux 2.5.19) + // Real-time scheduling priority, a number in the range 1 + // to 99 for processes scheduled under a real-time policy, + // or 0, for non-real-time processes (see + // sched_setscheduler(2)). + // + if (task->se.prio >= 100) + strcat(buffer, " 0"); + else + sprintf(buffer, "%s %u", buffer, task->se.prio); + //(41) TODO: policy %u (since Linux 2.5.19) + // Scheduling policy (see sched_setscheduler(2)). Decode + // using the SCHED_* constants in linux/sched.h. + // The format for this field was %lu before Linux 2.6.22. + // + strcat(buffer, " 0"); + //(42) TODO: delayacct_blkio_ticks %llu (since Linux 2.6.18) + // Aggregated block I/O delays, measured in clock ticks + // (centiseconds). + // + strcat(buffer, " 0"); + //(43) TODO: guest_time %lu (since Linux 2.6.24) + // Guest time of the process (time spent running a virtual + // CPU for a guest operating system), measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(44) TODO: cguest_time %ld (since Linux 2.6.24) + // Guest time of the process's children, measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(45) start_data %lu (since Linux 3.3) [PT] + // Address above which program initialized and uninitial‐ + // ized (BSS) data are placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_data); + //(46) end_data %lu (since Linux 3.3) [PT] + // Address below which program initialized and uninitial‐ + // ized (BSS) data are placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->end_data); + //(47) start_brk %lu (since Linux 3.3) [PT] + // Address above which program heap can be expanded with + // brk(2). + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_brk); + //(48) arg_start %lu (since Linux 3.5) [PT] + // Address above which program command-line arguments + // (argv) are placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->arg_start); + //(49) arg_end %lu (since Linux 3.5) [PT] + // Address below program command-line arguments (argv) are + // placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->arg_end); + //(50) env_start %lu (since Linux 3.5) [PT] + // Address above which program environment is placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->env_start); + //(51) env_end %lu (since Linux 3.5) [PT] + // Address below which program environment is placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->env_end); + //(52) exit_code %d (since Linux 3.5) [PT] + // The thread's exit status in the form reported by + // waitpid(2). + sprintf(buffer, "%s %d\n", buffer, task->exit_code); + return 1; +} \ No newline at end of file diff --git a/mentos/src/io/proc_system.c b/mentos/src/io/proc_system.c new file mode 100644 index 0000000..132f710 --- /dev/null +++ b/mentos/src/io/proc_system.c @@ -0,0 +1,206 @@ +/// MentOS, The Mentoring Operating system project +/// @file proc_system.c +/// @brief Contains callbacks for procfs system files. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "procfs.h" +#include "version.h" +#include "process.h" +#include "string.h" +#include "stdio.h" +#include "errno.h" +#include "debug.h" +#include "timer.h" + +static ssize_t procs_do_uptime(char *buffer, size_t bufsize); + +static ssize_t procs_do_version(char *buffer, size_t bufsize); + +static ssize_t procs_do_mounts(char *buffer, size_t bufsize); + +static ssize_t procs_do_cpuinfo(char *buffer, size_t bufsize); + +static ssize_t procs_do_meminfo(char *buffer, size_t bufsize); + +static ssize_t procs_do_stat(char *buffer, size_t bufsize); + +static ssize_t procs_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (file == NULL) + return -EFAULT; + proc_dir_entry_t *entry = (proc_dir_entry_t *)file->device; + if (entry == NULL) + return -EFAULT; + // Prepare a buffer. + char buffer[BUFSIZ]; + memset(buffer, 0, BUFSIZ); + // Call the specific function. + int ret = 0; + if (strcmp(entry->name, "uptime") == 0) + ret = procs_do_uptime(buffer, BUFSIZ); + else if (strcmp(entry->name, "version") == 0) + ret = procs_do_version(buffer, BUFSIZ); + else if (strcmp(entry->name, "mounts") == 0) + ret = procs_do_mounts(buffer, BUFSIZ); + else if (strcmp(entry->name, "cpuinfo") == 0) + ret = procs_do_cpuinfo(buffer, BUFSIZ); + else if (strcmp(entry->name, "meminfo") == 0) + ret = procs_do_meminfo(buffer, BUFSIZ); + else if (strcmp(entry->name, "stat") == 0) + ret = procs_do_stat(buffer, BUFSIZ); + // Perform read. + ssize_t it = 0; + if (ret == 0) { + size_t name_len = strlen(buffer); + size_t read_pos = offset; + if (read_pos < name_len) { + while ((it < nbyte) && (read_pos < name_len)) { + *buf++ = buffer[read_pos]; + ++read_pos; + ++it; + } + } + } + return it; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procs_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procs_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procs_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = NULL, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +int procs_module_init() +{ + proc_dir_entry_t *system_entry; + // == /proc/uptime ======================================================== + if ((system_entry = proc_create_entry("uptime", NULL)) == NULL) { + pr_err("Cannot create `/proc/uptime`.\n"); + return 1; + } + pr_debug("Created `/proc/uptime` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/version ======================================================== + if ((system_entry = proc_create_entry("version", NULL)) == NULL) { + pr_err("Cannot create `/proc/version`.\n"); + return 1; + } + pr_debug("Created `/proc/version` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/mounts ======================================================== + if ((system_entry = proc_create_entry("mounts", NULL)) == NULL) { + pr_err("Cannot create `/proc/mounts`.\n"); + return 1; + } + pr_debug("Created `/proc/mounts` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/cpuinfo ======================================================== + if ((system_entry = proc_create_entry("cpuinfo", NULL)) == NULL) { + pr_err("Cannot create `/proc/cpuinfo`.\n"); + return 1; + } + pr_debug("Created `/proc/cpuinfo` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/meminfo ======================================================== + if ((system_entry = proc_create_entry("meminfo", NULL)) == NULL) { + pr_err("Cannot create `/proc/meminfo`.\n"); + return 1; + } + pr_debug("Created `/proc/meminfo` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/stat ======================================================== + if ((system_entry = proc_create_entry("stat", NULL)) == NULL) { + pr_err("Cannot create `/proc/stat`.\n"); + return 1; + } + pr_debug("Created `/proc/stat` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + return 0; +} + +static ssize_t procs_do_uptime(char *buffer, size_t bufsize) +{ + sprintf(buffer, "%d", timer_get_seconds()); + return 0; +} + +static ssize_t procs_do_version(char *buffer, size_t bufsize) +{ + sprintf(buffer, + "%s version %s (site: %s) (email: %s)", + OS_NAME, + OS_VERSION, + OS_SITEURL, + OS_REF_EMAIL); + return 0; +} + +static ssize_t procs_do_mounts(char *buffer, size_t bufsize) +{ + return 0; +} + +static ssize_t procs_do_cpuinfo(char *buffer, size_t bufsize) +{ + return 0; +} + +static ssize_t procs_do_meminfo(char *buffer, size_t bufsize) +{ + double total_space = get_zone_total_space(GFP_KERNEL) + + get_zone_total_space(GFP_USER), + free_space = get_zone_free_space(GFP_KERNEL) + + get_zone_free_space(GFP_USER), + cached_space = get_zone_cached_space(GFP_KERNEL) + + get_zone_cached_space(GFP_USER), + used_space = total_space - free_space; + total_space /= (double)K; + free_space /= (double)K; + cached_space /= (double)K; + used_space /= (double)K; + sprintf( + buffer, + "MemTotal : %12.2f Kb\n" + "MemFree : %12.2f Kb\n" + "MemUsed : %12.2f Kb\n" + "Cached : %12.2f Kb\n", + total_space, free_space, used_space, cached_space); + return 0; +} + +static ssize_t procs_do_stat(char *buffer, size_t bufsize) +{ + return 0; +} \ No newline at end of file diff --git a/mentos/src/io/proc_video.c b/mentos/src/io/proc_video.c new file mode 100644 index 0000000..b9597a2 --- /dev/null +++ b/mentos/src/io/proc_video.c @@ -0,0 +1,106 @@ +// +// Created by andrea on 02/05/20. +// + +#include "termios-struct.h" +#include "keyboard.h" +#include "procfs.h" +#include "ioctls.h" +#include "sys/bitops.h" +#include "video.h" +#include "debug.h" +#include "errno.h" +#include "fcntl.h" +#include "vfs.h" + +static termios ktermios = { + .c_cflag = 0, + .c_lflag = (ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG), + .c_oflag = 0, + .c_iflag = 0 +}; + +static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (buf == NULL) { + return -1; + } + // Read the character from the keyboard. + int c = keyboard_getc(); + if (c >= 0) { + // Return the character. + *((char *)buf) = c; + if (bitmask_check(ktermios.c_lflag, ECHO)) { + if ((c == '\b') && + (!bitmask_check(ktermios.c_lflag, ICANON) || !bitmask_check(ktermios.c_lflag, ECHOE))) { + return 1; + } + // Echo the character to video. + video_putc(c); + } + return 1; + } + return 0; +} + +static ssize_t procv_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) +{ + for (size_t i = 0; i < nbyte; ++i) { + video_putc(((char *)buf)[i]); + } + return nbyte; +} + +static int procv_fstat(vfs_file_t *file, stat_t *stat) +{ + return -ENOSYS; +} + +static int procv_ioctl(vfs_file_t *file, int request, void *data) +{ + switch (request) { + case TCGETS: + *((termios *)data) = ktermios; + break; + case TCSETS: + ktermios = *((termios *)data); + break; + default: + break; + } + return 0; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procv_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procv_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procv_read, + .write_f = procv_write, + .lseek_f = NULL, + .stat_f = procv_fstat, + .ioctl_f = procv_ioctl, + .getdents_f = NULL +}; + +int procv_module_init() +{ + proc_dir_entry_t *video = proc_create_entry("video", NULL); + if (video == NULL) { + pr_err("Cannot create `/proc/video`.\n"); + return 1; + } + pr_debug("Created `/proc/video` (%p)\n", video); + // Set the specific operations. + video->sys_operations = &procv_sys_operations; + video->fs_operations = &procv_fs_operations; + return 0; +} \ No newline at end of file diff --git a/mentos/src/io/stdio.c b/mentos/src/io/stdio.c new file mode 100644 index 0000000..0e57909 --- /dev/null +++ b/mentos/src/io/stdio.c @@ -0,0 +1,177 @@ +/// @file stdio.c +/// @brief Standard I/0 functions. +/// @date Apr 2019 + +#include "syscall.h" +#include "errno.h" +#include "stdio.h" +#include "video.h" +#include "ctype.h" +#include "keyboard.h" +#include "string.h" + +void putchar(int character) +{ + video_putc(character); +} + +void puts(char *str) +{ + video_puts(str); +} + +int getchar() +{ + return keyboard_getc(); +} + +char *gets(char *str) +{ + // Check the input string. + if (str == NULL) + return NULL; + // Buffer for reading input. + char buffer[GETS_BUFFERSIZE]; + memset(buffer, '\0', GETS_BUFFERSIZE); + // Char pointer to the buffer. + char *cptr = buffer; + // Character storage and counter to prevent overflow. + int ch, counter = 0; + // Read until we find a newline or we exceed the buffer size. + while (((ch = getchar()) != '\n') && (counter++ < GETS_BUFFERSIZE)) { + // If we encounter EOF, stop. + if (ch == EOF) { + // EOF at start of line return NULL. + if (cptr == str) { + return NULL; + } + break; + } + // Backspace key + if (ch == '\b') { + if (counter > 0) { + counter--; + --cptr; + putchar('\b'); + } + } else { + // The character is stored at address, and the pointer is incremented. + *cptr++ = ch; + } + } + // Add the null-terminating character. + *cptr = '\0'; + // Copy the string we have read. + strcpy(str, buffer); + // Return a pointer to the original string. + return str; +} + +int atoi(const char *str) +{ + // Check the input string. + if (str == NULL) + return 0; + // Initialize sign, the result variable, and two indices. + int sign = (str[0] == '-') ? -1 : +1, result = 0, i; + // Find where the number ends. + for (i = (sign == -1) ? 1 : 0; (str[i] != '\0') && isdigit(str[i]); ++i) + result = (result * 10) + str[i] - '0'; + return sign * result; +} + +long strtol(const char *str, char **endptr, int base) +{ + const char *s; + long acc, cutoff; + int c; + int neg, any, cutlim; + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = str; + do { + c = (unsigned char)*s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? LONG_MIN : LONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char)*s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MIN; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc -= c; + } + } else { + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + *endptr = (char *)(any ? s - 1 : str); + return (acc); +} diff --git a/mentos/src/io/vga/vga.c b/mentos/src/io/vga/vga.c new file mode 100644 index 0000000..a3630ca --- /dev/null +++ b/mentos/src/io/vga/vga.c @@ -0,0 +1,659 @@ +#include "vga.h" + +#include "vga_palette.h" +#include "vga_mode.h" +#include "vga_font.h" + +#include "port_io.h" +#include "stdbool.h" +#include "string.h" +#include "debug.h" +#include "math.h" + +#define COUNT_OF(x) ((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x]))))) + +#define AC_INDEX 0x03C0 +#define AC_WRITE 0x03C0 +#define AC_READ 0x03C1 +#define MISC_WRITE 0x03C2 +#define MISC_READ 0x03CC +#define SC_INDEX 0x03C4 // VGA sequence controller. +#define SC_DATA 0x03C5 +#define PALETTE_MASK 0x03C6 +#define PALETTE_READ 0x03C7 +#define PALETTE_INDEX 0x03C8 // VGA digital-to-analog converter. +#define PALETTE_DATA 0x03C9 +#define GC_INDEX 0x03CE // VGA graphics controller. +#define GC_DATA 0x03CF +#define CRTC_INDEX 0x03D4 // VGA CRT controller. +#define CRTC_DATA 0x03D5 +#define INPUT_STATUS_READ 0x03DA + +typedef struct { + /// Writes a pixel. + void (*write_pixel)(unsigned int x, unsigned int y, unsigned char c); + /// Reads a pixel. + unsigned (*read_pixel)(unsigned int x, unsigned int y); + /// Draws a rectangle. + void (*draw_rect)(unsigned int x, unsigned int y, unsigned int wd, unsigned int ht, unsigned char c); + /// Fills a rectangle. + void (*fill_rect)(unsigned int x, unsigned int y, unsigned int wd, unsigned int ht, unsigned char c); +} vga_ops_t; + +typedef struct { + unsigned char *font; + unsigned width; + unsigned height; +} vga_font_t; + +typedef struct { + int width; + int height; + int bpp; + char *address; + vga_ops_t *ops; +} vga_driver_t; + +static bool_t vga_enable = false; +palette_entry_t stored_palette[256]; +char vidmem[262144]; + +static vga_driver_t *driver = NULL; +static vga_font_t *font; + +// ============================================================================ +// == VGA MODEs =============================================================== +#define MODE_NUM_SEQ_REGS 5 +#define MODE_NUM_CRTC_REGS 25 +#define MODE_NUM_GC_REGS 9 +#define MODE_NUM_AC_REGS (16 + 5) +#define MODE_NUM_REGS (1 + MODE_NUM_SEQ_REGS + MODE_NUM_CRTC_REGS + MODE_NUM_GC_REGS + MODE_NUM_AC_REGS) // 61 + +static inline char *__get_seg(void) +{ + unsigned int seg; + outportb(GC_INDEX, 6); + seg = (inportb(GC_DATA) >> 2) & 3; + if ((seg == 0) || (seg == 1)) + return (char *)0xA0000; + if (seg == 2) + return (char *)0xB0000; + if (seg == 3) + return (char *)0xB8000; + return (char *)seg; +} + +void __vga_set_color_map(unsigned int index, unsigned char r, unsigned char g, unsigned char b) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_INDEX, index); + outportl(PALETTE_DATA, r); + outportl(PALETTE_DATA, g); + outportl(PALETTE_DATA, b); +} + +void __vga_get_color_map(unsigned int index, unsigned char *r, unsigned char *g, unsigned char *b) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_READ, index); + *r = inportl(PALETTE_DATA); + *g = inportl(PALETTE_DATA); + *b = inportl(PALETTE_DATA); +} + +static void __save_palette(palette_entry_t *p, size_t size) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_READ, 0x00); + for (int i = 0; i < size; i++) { + p[i].red = inportl(PALETTE_DATA); + p[i].green = inportl(PALETTE_DATA); + p[i].blue = inportl(PALETTE_DATA); + } +} + +static void __load_palette(palette_entry_t *p, size_t size) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_INDEX, 0x00); + for (int i = 0; i < size; i++) { + outportl(PALETTE_DATA, p[i].red); + outportl(PALETTE_DATA, p[i].green); + outportl(PALETTE_DATA, p[i].blue); + } +} + +static inline void __set_plane(unsigned int plane) +{ + unsigned char pmask; + plane &= 3; + pmask = 1 << plane; + // Set read plane. + outportb(GC_INDEX, 4); + outportb(GC_DATA, plane); + // Set write plane. + outportb(SC_INDEX, 2); + outportb(SC_DATA, pmask); +} + +static unsigned char __read_byte(unsigned int offset) +{ + return (unsigned char)(*(driver->address + offset)); +} + +static void __write_byte(unsigned int offset, unsigned char value) +{ + *(char *)(driver->address + offset) = value; +} + +static void __set_mode(vga_mode_t *vga_mode) +{ + unsigned char *ptr = &vga_mode->misc; + + // Write SEQUENCER regs. + outportb(MISC_WRITE, *ptr); + ++ptr; + + // Write SEQUENCER regs. + for (unsigned i = 0; i < MODE_NUM_SEQ_REGS; i++) { + outportb(SC_INDEX, i); + outportb(SC_DATA, *ptr); + ++ptr; + } + + // Wnlock CRTC registers. + outportb(CRTC_INDEX, 0x03); + outportb(CRTC_DATA, inportb(CRTC_DATA) | 0x80); + outportb(CRTC_INDEX, 0x11); + outportb(CRTC_DATA, inportb(CRTC_DATA) & ~0x80); + + // Make sure they remain unlocked. + ptr[0x03] |= 0x80; + ptr[0x11] &= ~0x80; + + // write CRTC regs. + for (unsigned i = 0; i < MODE_NUM_CRTC_REGS; i++) { + outportb(CRTC_INDEX, i); + outportb(CRTC_DATA, *ptr); + ++ptr; + } + + // write GRAPHICS CONTROLLER regs. + for (unsigned i = 0; i < MODE_NUM_GC_REGS; i++) { + outportb(GC_INDEX, i); + outportb(GC_DATA, *ptr); + ++ptr; + } + + // write ATTRIBUTE CONTROLLER regs. + for (unsigned i = 0; i < MODE_NUM_AC_REGS; i++) { + inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, i); + outportb(AC_WRITE, *ptr); + ++ptr; + } + + // lock 16-color palette and unblank display. + (void)inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, 0x20); +} + +static void __read_registers(vga_mode_t *vga_mode) +{ + unsigned char *ptr = &vga_mode->misc; + + // read MISCELLANEOUS + *ptr = inportb(MISC_READ); + ptr++; + + // read SEQUENCER + for (unsigned i = 0; i < MODE_NUM_SEQ_REGS; i++) { + outportb(SC_INDEX, i); + *ptr = inportb(SC_DATA); + ptr++; + } + + // read CRTC + for (unsigned i = 0; i < MODE_NUM_CRTC_REGS; i++) { + outportb(CRTC_INDEX, i); + *ptr = inportb(CRTC_DATA); + ptr++; + } + + // read GRAPHICS CONTROLLER + for (unsigned i = 0; i < MODE_NUM_GC_REGS; i++) { + outportb(GC_INDEX, i); + *ptr = inportb(GC_DATA); + ptr++; + } + + // read ATTRIBUTE CONTROLLER + for (unsigned i = 0; i < MODE_NUM_AC_REGS; i++) { + (void)inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, i); + *ptr = inportb(AC_READ); + ptr++; + } + + // lock 16-color palette and unblank display + (void)inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, 0x20); +} + +static void __write_font(unsigned char *buf, unsigned font_height) +{ + unsigned char seq2, seq4, gc4, gc5, gc6; + unsigned i; + + /* save registers +__set_plane() modifies GC 4 and SEQ 2, so save them as well */ + outportb(SC_INDEX, 2); + seq2 = inportb(SC_DATA); + + outportb(SC_INDEX, 4); + seq4 = inportb(SC_DATA); + /* turn off even-odd addressing (set flat addressing) +assume: chain-4 addressing already off */ + outportb(SC_DATA, seq4 | 0x04); + + outportb(GC_INDEX, 4); + gc4 = inportb(GC_DATA); + + outportb(GC_INDEX, 5); + gc5 = inportb(GC_DATA); + /* turn off even-odd addressing */ + outportb(GC_DATA, gc5 & ~0x10); + + outportb(GC_INDEX, 6); + gc6 = inportb(GC_DATA); + /* turn off even-odd addressing */ + outportb(GC_DATA, gc6 & ~0x02); + /* write font to plane P4 */ + __set_plane(2); + /* write font 0 */ + for (i = 0; i < 256; i++) { + memcpy((char *)buf, (char *)(i * 32 + 0xA0000), font_height); + buf += font_height; + } + /* restore registers */ + outportb(SC_INDEX, 2); + outportb(SC_DATA, seq2); + outportb(SC_INDEX, 4); + outportb(SC_DATA, seq4); + outportb(GC_INDEX, 4); + outportb(GC_DATA, gc4); + outportb(GC_INDEX, 5); + outportb(GC_DATA, gc5); + outportb(GC_INDEX, 6); + outportb(GC_DATA, gc6); +} + +// ============================================================================ +// == WRITE PIXEL FUNCTIONS =================================================== + +static void __write_pixel_1(unsigned int x, unsigned int y, unsigned char c) +{ + unsigned wd_in_bytes; + unsigned off, mask; + + c = (c & 1) * 0xFF; + wd_in_bytes = driver->width / 8; + off = wd_in_bytes * y + x / 8; + x = (x & 7) * 1; + mask = 0x80 >> x; + __write_byte(off, (__read_byte(off) & ~mask) | (c & mask)); +} + +static void __write_pixel_2(unsigned int x, unsigned int y, unsigned char c) +{ + unsigned wd_in_bytes; + unsigned off, mask; + + c = (c & 3) * 0x55; + wd_in_bytes = driver->width / 4; + off = wd_in_bytes * y + x / 4; + x = (x & 3) * 2; + mask = 0xC0 >> x; + __write_byte(off, (__read_byte(off) & ~mask) | (c & mask)); +} + +static void __write_pixel_4(unsigned int x, unsigned int y, unsigned char color) +{ + int rotation = 0; + int16_t t; + switch (rotation) { + case 1: + t = x; + x = driver->width - 1 - y; + y = t; + break; + case 2: + x = driver->width - 1 - x; + y = driver->height - 1 - y; + break; + case 3: + t = x; + x = y; + y = driver->height - 1 - t; + break; + } + + unsigned wd_in_bytes, off, mask, p, pmask; + + wd_in_bytes = driver->width / 8; + off = wd_in_bytes * y + x / 8; + x = (x & 7) * 1; + mask = 0x80 >> x; + pmask = 1; + for (p = 0; p < 4; p++) { + __set_plane(p); + if (pmask & color) + __write_byte(off, __read_byte(off) | mask); + else + __write_byte(off, __read_byte(off) & ~mask); + pmask <<= 1; + } +} + +static inline void __write_pixel_8(unsigned int x, unsigned int y, unsigned char color) +{ + __set_plane(x); + __write_byte(((y * driver->width) + x) / 4, color); + // (y << 6) + (y << 4) + (x >> 2) +} + +unsigned int reverseBits(char num) +{ + unsigned int NO_OF_BITS = sizeof(num) * 8; + unsigned int reverse_num = 0; + int i; + for (i = 0; i < NO_OF_BITS; i++) { + if ((num & (1 << i))) + reverse_num |= 1 << ((NO_OF_BITS - 1) - i); + } + return reverse_num; +} + +int vga_is_enabled() +{ + return vga_enable; +} + +int vga_width() +{ + if (vga_enable) + return driver->width; + return 0; +} + +int vga_height() +{ + if (vga_enable) + return driver->height; + return 0; +} + +void vga_setfont(const vga_font_t *__font) +{ + font->font = __font->font; + font->width = __font->width; + font->height = __font->height; +} + +void vga_clear_screen() +{ + for (int p = 0; p < 4; p++) { + __set_plane(p); + memset(driver->address, 0, 64 * 1024); + } +} + +void vga_draw_char(unsigned int x, unsigned int y, unsigned char c, unsigned char color) +{ + static unsigned mask[] = { + 1u << 0u, // 1 + 1u << 1u, // 2 + 1u << 2u, // 4 + 1u << 3u, // 8 + 1u << 4u, // 16 + 1u << 5u, // 32 + 1u << 6u, // 64 + 1u << 7u, // 128 + 1u << 8u, // 256 + }; + unsigned char *glyph = font->font + c * font->height; + for (unsigned cy = 0; cy < font->height; ++cy) { + for (unsigned cx = 0; cx < font->width; ++cx) { + driver->ops->write_pixel(x + (font->width - cx), y + cy, glyph[cy] & mask[cx] ? color : 0x00u); + } + } +} + +void vga_draw_string(int x, int y, char *str, unsigned char color) +{ + char i = 0; + while (*str != '\0') { + vga_draw_char(x + i * 8, y, *str, color); + str++; + i++; + } +} + +void vga_draw_line(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned char color) +{ + bool_t steep = abs(y1 - y0) > abs(x1 - x0); + int tmp = 0; + if (steep) { + tmp = x0; + x0 = y0; + y0 = tmp; + + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + + tmp = y0; + y0 = y1; + y1 = tmp; + } + int deltax = x1 - x0; + int deltay = abs(y1 - y0); + int error = deltax / 2; + int ystep; + int y = y0; + if (y0 < y1) + ystep = 1; + else + ystep = -1; + for (int x = x0; x < x1; x++) { + if (steep) + driver->ops->write_pixel(y, x, color); + else + driver->ops->write_pixel(x, y, color); + error = error - deltay; + if (error < 0) { + y = y + ystep; + error = error + deltax; + } + } +} + +void vga_draw_rectangle(unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, unsigned char fill) +{ + vga_draw_line(sx, sy, ex, sy, fill); + vga_draw_line(sx, sy, sx, ey, fill); + vga_draw_line(sx, ey, ex, ey, fill); + vga_draw_line(ex, ey, ex, sy, fill); +} + +void vga_draw_circle(unsigned int xc, unsigned int yc, unsigned int r, unsigned char col) +{ + unsigned int x = 0; + unsigned int y = r; + unsigned int p = 3 - 2 * r; + if (!r) + return; + + while (y >= x) // only formulate 1/8 of circle + { + driver->ops->write_pixel(xc - x, yc - y, col); //upper left left + driver->ops->write_pixel(xc - y, yc - x, col); //upper upper left + driver->ops->write_pixel(xc + y, yc - x, col); //upper upper right + driver->ops->write_pixel(xc + x, yc - y, col); //upper right right + driver->ops->write_pixel(xc - x, yc + y, col); //lower left left + driver->ops->write_pixel(xc - y, yc + x, col); //lower lower left + driver->ops->write_pixel(xc + y, yc + x, col); //lower lower right + driver->ops->write_pixel(xc + x, yc + y, col); //lower right right + if (p < 0) + p += 4 * x++ + 6; + else + p += 4 * (x++ - y--) + 10; + } +} + +void vga_draw_triangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int x3, unsigned int y3, unsigned char col) +{ + vga_draw_line(x1, y1, x2, y2, col); + vga_draw_line(x2, y2, x3, y3, col); + vga_draw_line(x3, y3, x1, y1, col); +} + +static void __test_vga(void) +{ + vga_clear_screen(); + vga_draw_rectangle(1, 1, driver->width - 1, driver->height - 1, 3); + for (unsigned i = 1; i < driver->height - 1; i++) { + driver->ops->write_pixel((driver->width - driver->height) / 2 + i, i, 1); + driver->ops->write_pixel((driver->height + driver->width) / 2 - i, i, 1); + } +} + +// == MODEs and DRIVERs ======================================================= + +static vga_ops_t ops_720_480_16 = { + .write_pixel = __write_pixel_4, + .read_pixel = NULL, + .draw_rect = NULL, + .fill_rect = NULL, +}; + +static vga_ops_t ops_640_480_16 = { + .write_pixel = __write_pixel_4, + .read_pixel = NULL, + .draw_rect = NULL, + .fill_rect = NULL, +}; + +static vga_ops_t ops_320_200_256 = { + .write_pixel = __write_pixel_8, + .read_pixel = NULL, + .draw_rect = NULL, + .fill_rect = NULL, +}; + +static vga_font_t font_8x8 = { + .font = arr_8x8_font, + .width = 8, + .height = 8, +}; + +static vga_font_t font_8x8_basic = { + .font = arr_8x8_basic_font, + .width = 8, + .height = 8, +}; + +static vga_font_t font_8x16 = { + .font = arr_8x16_font, + .width = 8, + .height = 16, +}; + +static vga_driver_t driver_720_480_16 = { + .width = 720, + .height = 480, + .bpp = 16, + .address = (char *)NULL, + .ops = &ops_720_480_16, +}; + +static vga_driver_t driver_640_480_16 = { + .width = 640, + .height = 480, + .bpp = 16, + .address = (char *)NULL, + .ops = &ops_640_480_16, +}; + +static vga_driver_t driver_320_200_256 = { + .width = 320, + .height = 200, + .bpp = 256, + .address = (char *)NULL, + .ops = &ops_320_200_256, +}; + +// == INITIALIZE and FINALIZE ================================================= + +void vga_initialize() +{ + // Initialize the desired mode. +#if defined(VGA_MODE_320_200_256) + // Save the current palette. + __save_palette(stored_palette, 256); + // Write the registers. + __set_mode(&_mode_320_200_256); + // Initialize the mode. + driver = &driver_320_200_256; + // Load the color palette. + __load_palette(ansi_256_palette, 256); +#elif defined(VGA_MODE_640_480_16) + // Save the current palette. + __save_palette(stored_palette, 256); + // Write the registers. + __set_mode(&_mode_640_480_16); + // Initialize the mode. + driver = &driver_640_480_16; + // Load the color palette. + __load_palette(ansi_16_palette, 16); +#elif defined(VGA_MODE_720_480_16) + // Save the current palette. + __save_palette(stored_palette, 256); + // Write the registers. + __set_mode(&_mode_720_480_16); + // Initialize the mode. + driver = &driver_720_480_16; + // Load the color palette. + __load_palette(ansi_16_palette, 16); +#else // VGA_TEXT_MODE + __set_mode(&_mode_80_25_text); + return; +#endif + // Set the address. + driver->address = __get_seg(); + + // Set the font. + vga_setfont(&font_8x8); + + // Save the content of the memory. + memcpy(vidmem, driver->address, 0x4000); + + // Clears the screen. + vga_clear_screen(); + + // Set the vga as enabled. + vga_enable = true; +} + +void vga_finalize() +{ + memcpy(driver->address, vidmem, 256 * 1024); + __set_mode(&_mode_80_25_text); + __load_palette(stored_palette, 256); + vga_enable = false; +} diff --git a/mentos/src/io/video.c b/mentos/src/io/video.c index 6a8760a..92f6ce7 100644 --- a/mentos/src/io/video.c +++ b/mentos/src/io/video.c @@ -1,381 +1,349 @@ /// MentOS, The Mentoring Operating system project /// @file video.c /// @brief Video functions and costants. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "port_io.h" #include "video.h" -#include "debug.h" #include "stdbool.h" +#include "ctype.h" +#include "string.h" +#include "stdio.h" -/// The height of the screen. -#define SCREEN_HEIGHT 25 +#define HEIGHT 25 ///< The height of the +#define WIDTH 80 ///< The width of the +#define W2 (WIDTH * 2) ///< The width of the +#define TOTAL_SIZE (HEIGHT * WIDTH * 2) ///< The total size of the screen. +#define ADDR (char *)0xB8000U ///< The address of the +#define STORED_PAGES 3 ///< The number of stored pages. -/// The width of the screen. -#define SCREEN_WIDTH 80 +/// @brief Stores the association between ANSI colors and pure VIDEO colors. +struct ansi_color_map_t { + /// The ANSI color number. + uint8_t ansi_color; + /// The VIDEO color number. + uint8_t video_color; +} +/// @brief The mapping. +ansi_color_map[] = { + { 30, 0 }, + { 31, 4 }, + { 32, 2 }, + { 33, 6 }, + { 34, 1 }, + { 35, 5 }, + { 36, 3 }, + { 37, 7 }, -/// @brief Structure used to hold information about the screen. -typedef struct screen_t { - /// The width of the screen. - uint32_t width; - /// The height of the screen. - uint32_t height; - /// Pointer to the memory of the screen. - char *memory; - /// Pointer to a position of the screen. - char *pointer; - /// The current foreground color. - char foreground_color; - /// The current background color. - char background_color; - /// The buffer used when scrolling upward. - char upbuffer[SCREEN_HEIGHT][SCREEN_WIDTH * 2]; - /// The buffer used when scrolling downward. - char downbuffer[SCREEN_HEIGHT][SCREEN_WIDTH * 2]; - /// The output has been scrolled. - bool_t is_scrolled; - /// If the output has been shifted at least once. - bool_t is_shifted_once; - /// Used to store the last x coordiantes (used when scrolling). - uint32_t stored_x; - /// Used to store the last y coordiantes (used when scrolling). - uint32_t stored_y; -} screen_t; + { 90, 8 }, + { 91, 12 }, + { 92, 10 }, + { 93, 14 }, + { 94, 9 }, + { 95, 13 }, + { 96, 11 }, + { 97, 15 }, -/// The information concerning the screen. -screen_t screen; + { 40, 0 }, + { 41, 4 }, + { 42, 2 }, + { 43, 6 }, + { 44, 1 }, + { 45, 5 }, + { 46, 3 }, + { 47, 7 }, + + { 100, 8 }, + { 101, 12 }, + { 102, 10 }, + { 103, 14 }, + { 104, 9 }, + { 105, 13 }, + { 106, 11 }, + { 107, 15 }, + + { 0, 0 } +}; + +/// Pointer to a position of the screen writer. +char *pointer = ADDR; +/// The current color. +char color = 7; +/// Used to write on the escape_buffer. If -1, we are not parsing an escape sequence. +int escape_index = -1; +/// Used to store an escape sequence. +char escape_buffer[256]; +/// Buffer where we store the upper scroll history. +char upper_buffer[STORED_PAGES * TOTAL_SIZE] = { 0 }; +/// Buffer where we store the lower scroll history. +char original_page[TOTAL_SIZE] = { 0 }; +/// Determines if the screen is currently scrolled. +int scrolled_page = 0; void video_init() { - // =================================== - screen.height = 25; - screen.width = 80; - screen.memory = (char *)0xb8000; - screen.pointer = (char *)0xb8000; - screen.foreground_color = GREY; - screen.background_color = BLACK; - screen.is_scrolled = false; - screen.is_shifted_once = false; - // =================================== - video_clear(); + video_clear(); } -static inline void _draw_char(char c) +/// @brief Draws the given character. +/// @param c The character to draw. +static inline void __draw_char(char c) { - *(screen.pointer++) = c; - *(screen.pointer++) = - (screen.foreground_color + 16 * screen.background_color); + for (char *ptr = (ADDR + TOTAL_SIZE + (WIDTH * 2)); ptr > pointer; ptr -= 2) { + *(ptr) = *(ptr - 2); + *(ptr + 1) = *(ptr - 1); + } + *(pointer++) = c; + *(pointer++) = color; +} + +/// @brief Sets the provided ansi code. +/// @param ansi_code The ansi code describing background and foreground color. +static inline void __set_color(uint8_t ansi_code) +{ + struct ansi_color_map_t *it = ansi_color_map; + while (it->ansi_color != 0) { + if (ansi_code == it->ansi_color) { + if (((ansi_code >= 30) && (ansi_code <= 37)) || ((ansi_code >= 90) && (ansi_code <= 97))) { + color = (color & 0xF0U) | it->video_color; + } else { + color = (color & 0x0FU) | (it->video_color << 4U); + } + break; + } + ++it; + } +} + +/// @brief Moves the cursor backward. +/// @param erase If 1 also erase the character. +/// @param amount How many times we move backward. +static inline void __move_cursor_backward(int erase, int amount) +{ + for (int i = 0; i < amount; ++i) { + // Bring back the pointer. + pointer -= 2; + if (erase) { + strcpy(pointer, pointer + 2); + } + } + video_set_cursor_auto(); +} + +/// @brief Moves the cursor forward. +/// @param erase If 1 also erase the character. +/// @param amount How many times we move forward. +static inline void __move_cursor_forward(int erase, int amount) +{ + for (int i = 0; i < amount; ++i) { + // Bring forward the pointer. + if (erase) { + __draw_char(' '); + } else { + pointer += 2; + } + } + video_set_cursor_auto(); +} + +/// @brief Issue the vide to move the cursor to the given position. +/// @param x The x coordinate. +/// @param y The y coordinate. +static inline void __video_set_cursor(unsigned int x, unsigned int y) +{ + uint32_t position = x * WIDTH + y; + // Cursor LOW port to vga INDEX register. + outportb(0x3D4, 0x0F); + outportb(0x3D5, (uint8_t)(position & 0xFFU)); + // Cursor HIGH port to vga INDEX register. + outportb(0x3D4, 0x0E); + outportb(0x3D5, (uint8_t)((position >> 8U) & 0xFFU)); } void video_putc(int c) { - // If we have stored the last coordinates, this means that we are - // currently scrolling upwards. Thus, we need to scroll down first. - if (screen.stored_x && screen.stored_y) { - video_scroll_down(); - } - // If the character is '\n' go the new line. - if (c == '\n') { - video_new_line(); - } else if (c == '\033') { - __asm__("nop"); - } else if (c == '\t') { - video_put_tab(); - } else if (c == '\b') { - video_delete_last_character(); - } else if (c == '\r') { - video_cartridge_return(); - } else { - _draw_char(c); - } - video_shift_one_line(); - video_set_cursor_auto(); + // == ESCAPE SEQUENCES ======================================================================== + if (c == '\033') { + escape_index = 0; + return; + } + if (escape_index >= 0) { + if ((escape_index == 0) && (c == '[')) { + return; + } + escape_buffer[escape_index++] = c; + escape_buffer[escape_index] = 0; + if (isalpha(c)) { + escape_buffer[--escape_index] = 0; + if (c == 'C') { + __move_cursor_forward(false, atoi(escape_buffer)); + } else if (c == 'D') { + __move_cursor_backward(false, atoi(escape_buffer)); + } else if (c == 'm') { + __set_color(atoi(escape_buffer)); + } else if (c == 'J') { + video_clear(); + } + escape_index = -1; + } + return; + } + + // == NORMAL CHARACTERS ======================================================================= + // If the character is '\n' go the new line. + if (c == '\n') { + video_new_line(); + //video_shift_one_line_down(); + } else if (c == '\b') { + __move_cursor_backward(true, 1); + } else if (c == '\r') { + video_cartridge_return(); + } else if (c == 127) { + strcpy(pointer, pointer + 2); + } else if ((c >= 0x20) && (c <= 0x7E)) { + __draw_char(c); + } else { + return; + } + + video_shift_one_line_up(); + video_set_cursor_auto(); } void video_puts(const char *str) { - while ((*str) != 0) { - if ((*str) == '\n') { - video_new_line(); - } else if ((*str) == '\033') { - video_set_color(*(++str)); - } else if ((*str) == '\t') { - video_put_tab(); - } else if ((*str) == '\b') { - video_delete_last_character(); - } else if ((*str) == '\r') { - video_cartridge_return(); - } else { - video_putc((*str)); - } - ++str; - } -} - -void video_set_color(const video_color_t foreground) -{ - screen.foreground_color = foreground; -} - -void video_set_background(const video_color_t background) -{ - screen.background_color = background; -} - -void video_delete_last_character() -{ - if (lower_bound_y != video_get_line() || - lower_bound_x < video_get_column()) { - video_set_color(WHITE); - video_set_background(BLACK); - // Delete any tabular. - int x = video_get_column(); - if ((*(screen.pointer - 2)) == '\t') { - for (int i = 0; (i < (4 - x % 4)); ++i) { - if ((*(screen.pointer - 2)) != '\t') - break; - // Bring back the pointer. - screen.pointer -= 2; - // Delete the character. - _draw_char(' '); - // Bring back the pointer. - screen.pointer -= 2; - } - } else { - // Bring back the pointer. - screen.pointer -= 2; - // Delete the character. - _draw_char(' '); - // Bring back the pointer. - screen.pointer -= 2; - } - } -} - -void video_set_cursor(const unsigned int x, const unsigned int y) -{ - uint32_t position = (x * 80) + y; - - // Cursor LOW port to vga INDEX register. - outportb(0x3D4, 0x0F); - outportb(0x3D5, (uint8_t)(position & 0xFF)); - - // Cursor HIGH port to vga INDEX register. - outportb(0x3D4, 0x0E); - outportb(0x3D5, (uint8_t)((position >> 8) & 0xFF)); + while ((*str) != 0) { + video_putc((*str++)); + } } void video_set_cursor_auto() { - long x = ((screen.pointer - screen.memory) / 2) / screen.width; - long y = ((screen.pointer - screen.memory) / 2) % screen.width; - - if (x < 0) { - dbg_print("Negative x while setting auto-cursor.\n"); - x = 0; - } - - if (y < 0) { - dbg_print("Negative x while setting auto-cursor.\n"); - y = 0; - } - - video_set_cursor((uint32_t)x, (uint32_t)y); + __video_set_cursor(((pointer - ADDR) / 2U) / WIDTH, ((pointer - ADDR) / 2U) % WIDTH); } -void video_move_cursor(int x, int y) +void video_move_cursor(unsigned int x, unsigned int y) { - screen.pointer = screen.memory + ((y * screen.width * 2) + (x * 2)); - video_set_cursor_auto(); -} - -void video_put_tab() -{ - int foreground = screen.foreground_color; - video_set_color(screen.background_color); - int x = video_get_column(); - for (int i = 0; i < (4 - x % 4); ++i) - _draw_char('\t'); - video_set_color(foreground); + pointer = ADDR + ((y * WIDTH * 2) + (x * 2)); + video_set_cursor_auto(); } void video_clear() { - screen.pointer = screen.memory; - - for (uint32_t y = 0; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width; x++) { - *(screen.pointer++) = ' '; - *(screen.pointer++) = 0x7; - } - } - - screen.pointer = screen.memory; + memset(upper_buffer, 0, STORED_PAGES * TOTAL_SIZE); + memset(ADDR, 0, TOTAL_SIZE); } void video_new_line() { - screen.pointer = - screen.memory + - ((((screen.pointer - screen.memory) / (screen.width * 2)) + 1) * - (screen.width * 2)); - - video_shift_one_line(); - video_set_cursor_auto(); + pointer = ADDR + ((pointer - ADDR) / W2 + 1) * W2; + video_shift_one_line_up(); + video_set_cursor_auto(); } void video_cartridge_return() { - screen.pointer = - screen.memory + - ((((screen.pointer - screen.memory) / (screen.width * 2)) - 1) * - (screen.width * 2)); - - video_new_line(); - - video_shift_one_line(); - - video_set_cursor_auto(); + pointer = ADDR + ((pointer - ADDR) / W2 - 1) * W2; + video_new_line(); + video_shift_one_line_up(); + video_set_cursor_auto(); } -uint32_t video_get_column() +uint32_t video_get_x() { - if (screen.pointer < screen.memory) { - dbg_print("Get negative value while getting video column.\n"); - - return 0; - } - - return ((screen.pointer - screen.memory) % (screen.width * 2)) / 2; + return ((pointer - ADDR) % (WIDTH * 2)) / 2; } -uint32_t video_get_line() +uint32_t video_get_y() { - if (screen.pointer < screen.memory) { - dbg_print("Get negative value while getting video line.\n"); - - return 0; - } - - return ((screen.pointer - screen.memory) / (screen.width * 2)); + return ((pointer - ADDR) / (WIDTH * 2)); } -void video_shift_one_line(void) +void video_shift_one_line_up(void) { - if (screen.pointer >= - screen.memory + ((screen.height) * screen.width * 2)) { - /* We save the line to be lost in a buffer, this will be useful for - * scrolling. - */ - video_rotate_scroll_buffer(); - uint32_t index; - - for (index = 0; index < screen.width * 2; index++) { - screen.upbuffer[screen.height - 1][index] = - *(screen.memory + index); - } - - for (char *i = screen.memory; - i <= (screen.memory + ((screen.height) * screen.width * 2) + - (screen.width * 2)); - ++i) { - *i = i[screen.width * 2]; - } - screen.pointer = - screen.memory + - ((((screen.pointer - screen.memory) / (screen.width * 2)) - 1) * - (screen.width * 2)); - // Set that the scroll has been shifted at least once. - screen.is_shifted_once = true; - } + if (pointer >= ADDR + TOTAL_SIZE) { + // Move the upper buffer up by one line. + for (int row = 0; row < (STORED_PAGES * HEIGHT); ++row) { + memcpy(upper_buffer + W2 * row, upper_buffer + W2 * (row + 1), 2 * WIDTH); + } + // Copy the first line on the screen inside the last line of the upper buffer. + memcpy(upper_buffer + (TOTAL_SIZE * STORED_PAGES - W2), ADDR, 2 * WIDTH); + // Move the screen up by one line. + for (int row = 0; row < HEIGHT; ++row) { + memcpy(ADDR + (W2 * row), ADDR + (W2 * (row + 1)), 2 * WIDTH); + } + // Update the pointer. + pointer = ADDR + ((pointer - ADDR) / W2 - 1) * W2; + } } -void video_rotate_scroll_buffer() +void video_shift_one_page_up() { - for (uint32_t y = 1; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width * 2; x++) { - screen.upbuffer[y - 1][x] = screen.upbuffer[y][x]; - } - } + if (scrolled_page > 0) { + // Decrese the number of scrolled pages, and compute which page must be loaded. + int page_to_load = (STORED_PAGES - (--scrolled_page)); + // If we have reached 0, restore the original page. + if (scrolled_page == 0) + memcpy(ADDR, original_page, TOTAL_SIZE); + else + memcpy(ADDR, upper_buffer + (page_to_load * TOTAL_SIZE), TOTAL_SIZE); + } } +void video_shift_one_page_down(void) +{ + if (scrolled_page < STORED_PAGES) { + // Increase the number of scrolled pages, and compute which page must be loaded. + int page_to_load = (STORED_PAGES - (++scrolled_page)); + // If we are loading the first history page, save the original. + if (scrolled_page == 1) + memcpy(original_page, ADDR, TOTAL_SIZE); + // Load the specific page. + memcpy(ADDR, upper_buffer + (page_to_load * TOTAL_SIZE), TOTAL_SIZE); + } +} +#if 0 void video_scroll_up() { - char *ptr = screen.memory; + if (is_scrolled) + return; + char *ptr = memory = pointer; + for (unsigned int it = 0; it < TOTAL_SIZE; it++) { + downbuffer[y][x] = *ptr; + *ptr++ = upbuffer[y][x]; + } - if (screen.is_scrolled || !screen.is_shifted_once) { - return; - } + is_scrolled = true; - for (uint32_t y = 0; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width * 2; x++) { - screen.downbuffer[y][x] = *ptr; - *ptr++ = screen.upbuffer[y][x]; - } - } + stored_x = video_get_column(); - screen.is_scrolled = true; + stored_y = video_get_line(); - screen.stored_x = video_get_column(); - - screen.stored_y = video_get_line(); - - video_move_cursor(screen.width, screen.height); + video_move_cursor(width, height); } void video_scroll_down() { - char *ptr = screen.memory; + char *ptr = memory; - // If PAGEUP hasn't been pressed, it's useless to go down, there is nothing. - if (!screen.is_scrolled) { - return; - } - for (uint32_t y = 0; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width * 2; x++) { - *ptr++ = screen.downbuffer[y][x]; - } - } + // If PAGEUP hasn't been pressed, it's useless to go down, there is nothing. + if (!is_scrolled) { + return; + } + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width * 2; x++) { + *ptr++ = downbuffer[y][x]; + } + } - screen.is_scrolled = false; + is_scrolled = false; - video_move_cursor(screen.stored_x, screen.stored_y); + video_move_cursor(stored_x, stored_y); - screen.stored_x = 0; + stored_x = 0; - screen.stored_y = 0; + stored_y = 0; } -void video_print_ok() -{ - video_move_cursor(60, video_get_line()); - - video_set_color(WHITE); - - video_putc('['); - - video_set_color(BRIGHT_GREEN); - - video_puts("OK"); - - video_set_color(WHITE); - - video_puts("]\n"); -} - -void video_print_fail() -{ - video_move_cursor(60, video_get_line()); - - video_set_color(WHITE); - - video_putc('['); - - video_set_color(BRIGHT_RED); - - video_puts("FAIL"); - - video_set_color(WHITE); - - video_puts("]\n"); -} +#endif \ No newline at end of file diff --git a/mentos/src/ipc/msg.c b/mentos/src/ipc/msg.c new file mode 100644 index 0000000..8f975dd --- /dev/null +++ b/mentos/src/ipc/msg.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file msg.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +///! @cond Doxygen_Suppress + +#include "ipc/msg.h" +#include "panic.h" + +long sys_msgget(key_t key, int msgflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_msgctl(int msqid, int cmd, struct msqid_ds *buf) +{ + TODO("Not implemented"); + return 0; +} + +///! @endcond \ No newline at end of file diff --git a/mentos/src/ipc/sem.c b/mentos/src/ipc/sem.c new file mode 100644 index 0000000..7e88a96 --- /dev/null +++ b/mentos/src/ipc/sem.c @@ -0,0 +1,29 @@ +/// MentOS, The Mentoring Operating system project +/// @file sem.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +///! @cond Doxygen_Suppress + +#include "ipc/sem.h" +#include "panic.h" + +long sys_semget(key_t key, int nsems, int semflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_semop(int semid, struct sembuf *sops, unsigned nsops) +{ + TODO("Not implemented"); + return 0; +} + +long sys_semctl(int semid, int semnum, int cmd, unsigned long arg) +{ + TODO("Not implemented"); + return 0; +} + +///! @endcond \ No newline at end of file diff --git a/mentos/src/ipc/shm.c b/mentos/src/ipc/shm.c new file mode 100644 index 0000000..ad55185 --- /dev/null +++ b/mentos/src/ipc/shm.c @@ -0,0 +1,396 @@ +/// MentOS, The Mentoring Operating system project +/// @file shm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +///! @cond Doxygen_Suppress + +#include "ipc/shm.h" +#include "panic.h" + +#if 0 +struct shmid_ds *head = NULL; +static ushort shm_descriptor = 0; + +int syscall_shmctl(int *args) +{ + int shmid = args[0]; + int cmd = args[1]; + + // TODO: for IPC_STAT + // struct shmid_ds * buf = (struct shmid_ds *) args[2]; + + struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); + + if (myshmid_ds == NULL) { + return -1; + } + + // Upgrade shm info. + myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; + myshmid_ds->shm_ctime = time(NULL); + + switch (cmd) { + case IPC_RMID: + if (myshmid_ds->shm_nattch == 0) { + kfree(myshmid_ds->shm_location); + + // Manage list. + if (myshmid_ds == head) { + head = head->next; + } else { + // Finding the previous shmid_ds. + struct shmid_ds *prev = head; + while (prev->next != myshmid_ds) { + prev = prev->next; + } + prev->next = myshmid_ds->next; + } + kfree(myshmid_ds); + } else { + (myshmid_ds->shm_perm).mode |= SHM_DEST; + } + + return 0; + + case IPC_STAT: + break; + case IPC_SET: + break; + case SHM_LOCK: + break; + case SHM_UNLOCK: + break; + default: + break; + } + + return -1; +} + +// Get shared memory segment. +int syscall_shmget(int *args) +{ + int flags = args[2]; + key_t key = (key_t)args[0]; + size_t size = (size_t)args[1]; + + struct shmid_ds *shmid_ds; + + if (flags & IPC_EXCL) { + return -1; + } + + if (flags & IPC_CREAT) { + shmid_ds = find_shm_fromkey(key); + + if (shmid_ds != NULL) { + return -1; + } + + shmid_ds = kmalloc(sizeof(struct shmid_ds)); + pr_default("\n[SHM] shmget() shmid_ds : 0x%p", shmid_ds); + + shmid_ds->shm_location = kmalloc_align(size); + pr_default("\n[SHM] shmget() Location : 0x%p", + shmid_ds->shm_location); + pr_default("\n[SHM] shmget() physLocation : 0x%p", + paging_virtual_to_physical(get_current_page_directory(), + shmid_ds->shm_location)); + + shmid_ds->next = head; + head = shmid_ds; + + shmid_ds->shm_segsz = size; + shmid_ds->shm_atime = 0; + shmid_ds->shm_dtime = 0; + shmid_ds->shm_ctime = 0; + shmid_ds->shm_cpid = scheduler_get_current_process()->pid; + shmid_ds->shm_lpid = scheduler_get_current_process()->pid; + shmid_ds->shm_nattch = 0; + + // No user implementation. + (shmid_ds->shm_perm).cuid = 0; + // No group implementation. + (shmid_ds->shm_perm).cgid = 0; + // No user implementation + (shmid_ds->shm_perm).uid = 0; + // No group implementation. + (shmid_ds->shm_perm).gid = 0; + (shmid_ds->shm_perm).mode = flags & 0777; + (shmid_ds->shm_perm).seq = shm_descriptor++; + (shmid_ds->shm_perm).key = key; + } else { + shmid_ds = find_shm_fromkey(key); + pr_default("\n[SHM] shmget() shmid_ds found : 0x%p", shmid_ds); + + if (shmid_ds == NULL) { + return -1; + } + + if ((flags & 0777) > ((shmid_ds->shm_perm).mode & 0777)) { + return -1; + } + shmid_ds->shm_lpid = scheduler_get_current_process()->pid; + } + + return (shmid_ds->shm_perm).seq; +} + +// Attach shared memory segment. +void *syscall_shmat(int *args) +{ + int shmid = args[0]; + void *shmaddr = (void *)args[1]; + + // TODO: for more settings + // int flags = args[2]; + + struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); + pr_default("\n[SHM] shmat() shmid_ds found : 0x%p", myshmid_ds); + + if (myshmid_ds == NULL) { + return (void *)-1; + } + + void *shm_start = myshmid_ds->shm_location; + + if (shmaddr == NULL) { + void *ret = kmalloc_align(myshmid_ds->shm_segsz); + + uint32_t shm_vaddr_start = (uint32_t)ret & 0xfffff000; + uint32_t shm_vaddr_end = + ((uint32_t)ret + myshmid_ds->shm_segsz) & 0xfffff000; + + uint32_t shm_paddr_start = (uint32_t)paging_virtual_to_physical( + get_current_page_directory(), shm_start); + + free_map_region(get_current_page_directory(), shm_vaddr_start, + shm_vaddr_end, true); + + while (shm_vaddr_start <= shm_vaddr_end) { + paging_allocate_page(get_current_page_directory(), shm_vaddr_start, + shm_paddr_start / PAGE_SIZE, true, true); + shm_vaddr_start += PAGE_SIZE; + shm_paddr_start += PAGE_SIZE; + } + + pr_default("\n[SHM] shmat() vaddr : 0x%p", ret); + pr_default("\n[SHM] shmat() paddr : 0x%p", + (void *)shm_paddr_start); + pr_default("\n[SHM] shmat() paddr after map: 0x%p", + paging_virtual_to_physical(get_current_page_directory(), + ret)); + + // Upgrade shm info. + myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; + (myshmid_ds->shm_nattch)++; + myshmid_ds->shm_atime = time(NULL); + + return ret; + } + + return (void *)-1; +} + +// Detach shared memory segment. +int syscall_shmdt(int *args) +{ + void *shmaddr = (void *)args[0]; + + if (shmaddr == NULL) { + return -1; + } + + struct shmid_ds *myshmid_ds = find_shm_fromvaddr(shmaddr); + pr_default("\n[SHM] shmdt() shmid_ds found : 0x%p", myshmid_ds); + + if (myshmid_ds == NULL) { + return -1; + } + + // ===== Test ============================================================== + uint32_t shm_vaddr_start = (uint32_t)shmaddr & 0xfffff000; + uint32_t shm_vaddr_end = + ((uint32_t)shmaddr + myshmid_ds->shm_segsz) & 0xfffff000; + + free_map_region(get_current_page_directory(), shm_vaddr_start, + shm_vaddr_end, false); + + while (shm_vaddr_start <= shm_vaddr_end) { + paging_allocate_page(get_current_page_directory(), shm_vaddr_start, + shm_vaddr_start / PAGE_SIZE, true, true); + shm_vaddr_start += PAGE_SIZE; + } + // ========================================================================= + + kfree(shmaddr); + + // Upgrade shm info. + myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; + (myshmid_ds->shm_nattch)--; + myshmid_ds->shm_dtime = time(NULL); + + // Manage SHM_DEST flag on. + if (myshmid_ds->shm_nattch == 0 && (myshmid_ds->shm_perm).mode & SHM_DEST) { + kfree(myshmid_ds->shm_location); + + // Manage list. + if (myshmid_ds == head) { + head = head->next; + } else { + // Finding the previous shmid_ds. + struct shmid_ds *prev = head; + while (prev->next != myshmid_ds) { + prev = prev->next; + } + prev->next = myshmid_ds->next; + } + kfree(myshmid_ds); + } + + return 0; +} + +int shmctl(int shmid, int cmd, struct shmid_ds *buf) +{ + int error; + + __asm__("movl %0, %%ecx\n" + "movl %1, %%ebx\n" + "movl %2, %%edx\n" + "movl $6, %%eax\n" + "int $80\n" + : + : "r"(shmid), "r"(cmd), "r"(buf)); + __asm__("movl %%eax, %0\n\t" + : "=r"(error)); + + return error; +} + +int shmget(key_t key, size_t size, int flags) +{ + int id; + + __asm__("movl %0, %%ecx\n" + "movl %1, %%ebx\n" + "movl %2, %%edx\n" + "movl $3, %%eax\n" + "int $80\n" + : + : "r"(key), "r"(size), "r"(flags)); + __asm__("movl %%eax, %0\n\t" + : "=r"(id)); + + return id; +} + +void *shmat(int shmid, void *shmaddr, int flag) +{ + void *addr; + + __asm__("movl %0, %%ecx\n" + "movl %1, %%ebx\n" + "movl %2, %%edx\n" + "movl $4, %%eax\n" + "int $80\n" + : + : "r"(shmid), "r"(shmaddr), "r"(flag)); + // The kernel is serving my system call + + // Now I have the control + __asm__("movl %%eax, %0\n\t" + : "=r"(addr)); + + return addr; +} + +int shmdt(void *shmaddr) +{ + int error; + + __asm__("movl %0, %%ecx\n" + "movl $5, %%eax\n" + "int $80\n" + : + : "r"(shmaddr)); + __asm__("movl %%eax, %0\n\t" + : "=r"(error)); + + return error; +} + +struct shmid_ds *find_shm_fromid(int shmid) +{ + struct shmid_ds *res = head; + + while (res != NULL) { + if ((res->shm_perm).seq == shmid) { + return res; + } + res = res->next; + } + + return NULL; +} + +struct shmid_ds *find_shm_fromkey(key_t key) +{ + struct shmid_ds *res = head; + + while (res != NULL) { + if ((res->shm_perm).key == key) { + return res; + } + res = res->next; + } + + return NULL; +} + +struct shmid_ds *find_shm_fromvaddr(void *shmvaddr) +{ + void *shmpaddr = + paging_virtual_to_physical(get_current_page_directory(), shmvaddr); + void *paddr; + struct shmid_ds *res = head; + + while (res != NULL) { + paddr = paging_virtual_to_physical(get_current_page_directory(), + res->shm_location); + if (paddr == shmpaddr) { + return res; + } + res = res->next; + } + + return NULL; +} +#endif + +long sys_shmat(int shmid, char *shmaddr, int shmflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_shmget(key_t key, size_t size, int flag) +{ + TODO("Not implemented"); + return 0; +} + +long sys_shmdt(char *shmaddr) +{ + TODO("Not implemented"); + return 0; +} + +long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf) +{ + TODO("Not implemented"); + return 0; +} + +///! @endcond diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index 87d6c28..cfb849a 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -1,261 +1,349 @@ /// MentOS, The Mentoring Operating system project /// @file kernel.c /// @brief Kernel main function. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "proc_modules.h" +#include "vmem_map.h" +#include "procfs.h" +#include "pci.h" +#include "ata.h" +#include "idt.h" #include "kernel.h" #include "zone_allocator.h" #include "gdt.h" #include "syscall.h" #include "version.h" #include "video.h" - #include "pic8259.h" - -#include "cmd_cpuid.h" #include "debug.h" #include "fdc.h" #include "initrd.h" #include "irqflags.h" #include "keyboard.h" #include "scheduler.h" -#include "shell.h" -#include "stdio.h" #include "timer.h" #include "vfs.h" - -#include "kheap.h" - -#include "init.h" - -#include "pci.h" #include "fpu.h" -#include "ata.h" - #include "printk.h" +#include "module.h" +#include "rtc.h" +#include "stdio.h" +#include "assert.h" +#include "vga.h" -/// Describe start and end address of grub multiboot modules +/// Describe start address of grub multiboot modules. char *module_start[MAX_MODULES]; +/// Describe end address of grub multiboot modules. char *module_end[MAX_MODULES]; -/// Defined in kernel.ld, points at the multiheader grub info. +// Everything is defined in kernel.ld. + +/// Points at the multiheader grub info, starting address. extern uint32_t _multiboot_header_start; +/// Points at the multiheader grub info, ending address. extern uint32_t _multiboot_header_end; -/// Defined in kernel.ld, points at the kernel code. +/// Points at the kernel code, starting address. extern uint32_t _text_start; +/// Points at the kernel code, ending address. extern uint32_t _text_end; -/// Defined in kernel.ld, points at the read-only kernel data. +/// Points at the read-only kernel data, starting address. extern uint32_t _rodata_start; +/// Points at the read-only kernel data, ending address. extern uint32_t _rodata_end; -/// Defined in kernel.ld, points at the read-write kernel data initialized. +/// Points at the read-write kernel data initialized, starting address. extern uint32_t _data_start; +/// Points at the read-write kernel data initialized, ending address. extern uint32_t _data_end; -/// Defined in kernel.ld, points at the read-write kernel data uninitialized an kernel stack. +/// Points at the read-write kernel data uninitialized an kernel stack, starting address. extern uint32_t _bss_start; +/// Points at the read-write kernel data uninitialized an kernel stack, ending address. extern uint32_t _bss_end; +/// Points at the top of the kernel stack. extern uint32_t stack_top; +/// Points at the bottom of the kernel stack. extern uint32_t stack_bottom; -/// Defined in kernel.ld, points at the end of kernel code/data. +/// Points at the end of kernel code/data. extern uint32_t end; +/// Initial ESP. uintptr_t initial_esp = 0; +/// The boot info. +boot_info_t boot_info; -int kmain(uint32_t magic, multiboot_info_t *header, uintptr_t esp) +/// @brief Prints [OK] at the current row and column 60. +static inline void print_ok() { - dbg_print("\n"); - dbg_print("--------------------------------------------------\n"); - dbg_print("- Booting...\n"); - dbg_print("--------------------------------------------------\n"); - - // Print kernel initial state - dbg_print("\n KERNEL STATE ON BOOTING"); - dbg_print("\nKernel multiboot header: [ 0x%p - 0x%p ] ", - &_multiboot_header_start, &_multiboot_header_end); - dbg_print("\nKernel code: [ 0x%p - 0x%p ] <- text ", - &_text_start, &_text_end); - dbg_print("\nKernel read-only data: [ 0x%p - 0x%p ] <- rodata ", - &_rodata_start, &_rodata_end); - dbg_print("\nKernel initialized data: [ 0x%p - 0x%p ] <- data ", - &_data_start, &_data_end); - dbg_print("\nKernel uninitialized data: [ 0x%p - 0x%p ] <- stack & bss", - &_bss_start, &_bss_end); - dbg_print("\n - Kernel stack: [ 0x%p - 0x%p ] ", - &stack_bottom, &stack_top); - dbg_print("\n - Kernel bss: [ 0x%p - 0x%p ] ", - &stack_top, &_bss_end); - dbg_print("\nKernel image end: 0x%p", &end); - - // Am I booted by a Multiboot-compliant boot loader? - if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { - printk("Invalid magic number: 0x%x\n", (unsigned)magic); - return 1; - } - - // Set the initial esp. - initial_esp = esp; - - // Dump the multiboot structure. - dump_multiboot(header); - - //=== Initialize the video ================================================= - video_init(); - //-------------------------------------------------------------------------- - - //==== Show Operating System Version ======================================= - video_set_color(BRIGHT_GREEN); - printk(OS_NAME " " OS_VERSION); - video_set_color(WHITE); - printk("\nSite:"); - video_set_color(BRIGHT_BLUE); - printk(OS_SITEURL); - printk("\n"); - video_set_color(WHITE); - printk("\n"); - //-------------------------------------------------------------------------- - - //==== Set the starting point and end point of the module ================== - int i = 0; - multiboot_module_t *mod = (multiboot_module_t *)header->mods_addr; - for (; i < header->mods_count && i < MAX_MODULES; i++, mod++) { - module_start[i] = (char *)mod->mod_start; - module_end[i] = (char *)mod->mod_end; - } - //========================================================================== - - //==== Initialize memory =================================================== - printk("Initialize physical memory manager..."); - // Memoria fisica totale allocata: 1096 MB - if (!pmmngr_init(1096 * M + 512 * 4 * K)) { - video_print_fail(); - return 1; - } - video_print_ok(); - //========================================================================== - - //==== Initialize kernel heap ============================================== - dbg_print("Initialize kernel heap.\n"); - printk("Initialize heap..."); - kheap_init(KHEAP_INITIAL_SIZE); - video_print_ok(); - //========================================================================== - - //==== Initialize core modules ============================================= - // The Global Descriptor Table (GDT) is a data structure used by Intel - // x86-family processors starting with the 80286 in order to define the - // characteristics of the various memory areas used during program execution, - // including the base address, the size, and access privileges like - // executability and writability. These memory areas are called segments in - // Intel terminology. - printk("Initialize GDT..."); - init_gdt(); - video_print_ok(); - - // The IDT is used to show the processor what Interrupt Service Routine - // (ISR) to call to handle an exception. IDT entries are also called - // Interrupt requests whenever a device has completed a request and needs to - // be serviced. - // ISRs are used to save the current processor state and set up the - // appropriate segment registers needed for kernel mode before the kernel’s - // C-level interrupt handler is called. To handle the right exception, the - // correct entry in the IDT should be pointed to the correct ISR. - printk("Initialize IDT..."); - init_idt(); - video_print_ok(); - //-------------------------------------------------------------------------- - - // Initialize the system calls. - printk("Initialize system calls..."); - syscall_init(); - video_print_ok(); - - // Set the IRQs. - printk("Initialize IRQ..."); - pic8259_init_irq(); - video_print_ok(); - - //dbg_print("Initialize the paging.\n"); - // Initialize the paging. - //printk("Initialize the paging..."); - //paging_init(); - //video_print_ok(); - - dbg_print("Install the timer.\n"); - // Install the timer. - printk(" * Setting up timer..."); - timer_install(); - video_print_ok(); - - //dbg_print("Alloc and fill CPUID structure.\n"); - // Alloc and fill CPUID structure. - //printk(LNG_INIT_CPUID); - //get_cpuid(&sinfo); - //video_print_ok(); - - dbg_print("Initialize the filesystem.\n"); - // Initialize the filesystem. - printk("Initialize the fylesystem..."); - vfs_init(); - video_print_ok(); - initfs_init(); - - //dbg_print("Get the kernel image from the boot info.\n"); - // Get the kernel image from the boot info - //build_elf_symbols_from_multiboot(header); - - //==== Install/Uninstall devices =========================================== - // For debugging, show the list of PCI devices. - // pci_debug_scan(); - // Scan for ata devices. - // ata_initialize(); - - dbg_print("Install the keyboard.\n"); - printk(" * Setting up keyboard driver..."); - keyboard_install(); - video_print_ok(); - - // dbg_print("Install the mouse.\n"); - // printf(" * Setting up mouse driver..."); - // mouse_install(); // Install the mouse. - // video_print_ok(); - - dbg_print("Uninstall the floppy driver.\n"); - fdc_disable_motor(); // We disable floppy driver motor - //-------------------------------------------------------------------------- - - //==== Initialize the scheduler ============================================ - dbg_print("Initialize the scheduler.\n"); - printk("Initialize the scheduler..."); - kernel_initialize_scheduler(); - video_print_ok(); - //-------------------------------------------------------------------------- - - // create init process - task_struct *init_p = create_init_process(); - - //==== Initialize the FPU ================================================= - // Initialize the Floating Point Unit (FPU). - dbg_print("Initialize the Floating Point Unit (FPU).\n"); - printk("Initialize floating point unit..."); - if (!fpu_install()) { - video_print_fail(); - return 1; - } - video_print_ok(); - - dbg_print("--------------------------------------------------\n"); - dbg_print("- Booting Done\n"); - dbg_print("- Jumping into init process, hopefully...\n"); - dbg_print("--------------------------------------------------\n"); - - // Jump into init process. - enter_user_jmp( - // Entry point. - init_p->thread.eip, - // Stack pointer. - init_p->thread.useresp); - - dbg_print("Dear developer, we have to talk...\n"); - - return 1; + video_move_cursor(75, video_get_y()); + video_puts("[" FG_GREEN_BRIGHT "OK" FG_WHITE "]\n"); +} + +/// @brief Prints [FAIL] at the current row and column 60. +static inline void print_fail() +{ + video_move_cursor(75, video_get_y()); + video_puts("[" FG_RED_BRIGHT "FAIL" FG_WHITE "]\n"); +} + +/// @brief Entry point of the kernel. +/// @param boot_informations Information concerning the boot. +/// @return The exit status of the kernel. +int kmain(boot_info_t *boot_informations) +{ + pr_notice("Booting...\n"); + // Make a copy for when paging is enabled + boot_info = *boot_informations; + // Am I booted by a Multiboot-compliant boot loader? + if (boot_info.magic != MULTIBOOT_BOOTLOADER_MAGIC) { + printf("Invalid magic number: 0x%x\n", (unsigned)boot_info.magic); + return 1; + } + // Set the initial esp. + initial_esp = boot_info.stack_base; + // Dump the multiboot structure. + dump_multiboot(boot_info.multiboot_header); + //========================================================================== + pr_notice("Initialize the video...\n"); + vga_initialize(); + video_init(); + + //========================================================================== + printf(OS_NAME " " OS_VERSION); + printf("\nSite:"); + printf(OS_SITEURL); + printf("\n\n"); + + //========================================================================== + pr_notice("Initialize modules...\n"); + printf("Initialize modules..."); + if (!init_modules(boot_info.multiboot_header)) { + print_fail(); + return 1; + } + print_ok(); + pr_debug("End of modules: 0x%09p\n", get_address_after_modules()); + + //========================================================================== + pr_notice("Initialize physical memory manager...\n"); + printf("Initialize physical memory manager..."); + if (!pmmngr_init(&boot_info)) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize slab allocator.\n"); + printf("Initialize slab..."); + kmem_cache_init(); + print_ok(); + + //========================================================================== + // The Global Descriptor Table (GDT) is a data structure used by Intel + // x86-family processors starting with the 80286 in order to define the + // characteristics of the various memory areas used during program execution, + // including the base address, the size, and access privileges like + // executability and writability. These memory areas are called segments in + // Intel terminology. + pr_notice("Initialize Global Descriptor Table (GDT)...\n"); + printf("Initialize GDT..."); + init_gdt(); + print_ok(); + // The IDT is used to show the processor what Interrupt Service Routine + // (ISR) to call to handle an exception. IDT entries are also called + // Interrupt requests whenever a device has completed a request and needs to + // be serviced. + // ISRs are used to save the current processor state and set up the + // appropriate segment registers needed for kernel mode before the kernel’s + // C-level interrupt handler is called. To handle the right exception, the + // correct entry in the IDT should be pointed to the correct ISR. + pr_notice("Initialize Interrupt Service Routine(ISR)...\n"); + printf("Initialize IDT..."); + init_idt(); + print_ok(); + + //========================================================================== + pr_notice("Initialize system calls...\n"); + printf("Initialize system calls..."); + syscall_init(); + print_ok(); + + //========================================================================== + pr_notice("Initialize IRQ...\n"); + printf("Initialize IRQ..."); + pic8259_init_irq(); + print_ok(); + + //========================================================================== + pr_notice("Relocate modules.\n"); + printf("Relocate modules..."); + relocate_modules(); + print_ok(); + + //========================================================================== + pr_notice("Initialize paging.\n"); + printf("Initialize paging..."); + paging_init(&boot_info); + print_ok(); + + //========================================================================== + pr_notice("Initialize virtual memory mapping.\n"); + printf("Initialize virtual memory mapping..."); + virt_init(); + print_ok(); + + //========================================================================== + pr_notice("Install the timer.\n"); + printf("Setting up timer..."); + timer_install(); + print_ok(); + + //========================================================================== + pr_notice("Install RTC.\n"); + printf("Setting up RTC..."); + rtc_install(); + print_ok(); + + //========================================================================== + pr_notice("Initialize the filesystem.\n"); + printf("Initialize the filesystem..."); + vfs_init(); + print_ok(); + + //========================================================================== + pr_notice(" Initialize 'initrd'...\n"); + printf(" Initialize 'initrd'..."); + if (initrd_init_module()) { + print_fail(); + pr_emerg("Failed to register `initrd`!\n"); + return 1; + } + print_ok(); + if (do_mount("initrd", "/", "/dev/ram0")) { + pr_emerg("Failed to mount root `/`!\n"); + return 1; + } + + //========================================================================== + pr_notice(" Initialize 'procfs'...\n"); + printf(" Initialize 'procfs'..."); + if (procfs_module_init()) { + print_fail(); + pr_emerg("Failed to register `procfs`!\n"); + return 1; + } + print_ok(); + if (do_mount("procfs", "/proc", NULL)) { + pr_emerg("Failed to mount procfs at `/proc`!\n"); + return 1; + } + + //========================================================================== + pr_notice("Initialize video procfs file...\n"); + printf("Initialize video procfs file..."); + if (procv_module_init()) { + print_fail(); + pr_emerg("Failed to initialize `/proc/video`!\n"); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize system procfs file...\n"); + printf("Initialize system procfs file..."); + if (procs_module_init()) { + print_fail(); + pr_emerg("Failed to initialize proc system entries!\n"); + return 1; + } + print_ok(); + + //========================================================================== +#if 0 + // For debugging, show the list of PCI devices. + pci_debug_scan(); + // Scan for ata devices. + ata_initialize(); +#endif + + //========================================================================== + pr_notice("Setting up keyboard driver...\n"); + printf("Setting up keyboard driver..."); + keyboard_install(); + print_ok(); + + //========================================================================== +#if 0 + pr_notice("Install the mouse.\n"); + printf(" * Setting up mouse driver..."); + mouse_install(); // Install the mouse. + print_ok(); +#endif + + //========================================================================== + pr_notice("Uninstall the floppy driver.\n"); + fdc_disable_motor(); + + //========================================================================== + pr_notice("Initialize the scheduler.\n"); + printf("Initialize the scheduler..."); + scheduler_initialize(); + print_ok(); + + //========================================================================== + pr_notice("Init process management...\n"); + printf("Init process management..."); + if (!init_tasking()) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Creating init process...\n"); + printf("Creating init process..."); + task_struct *init_p = process_create_init("/bin/init"); + if (!init_p) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize floating point unit...\n"); + printf("Initialize floating point unit..."); + if (!fpu_install()) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize signals...\n"); + printf("Initialize signals..."); + if (!signals_init()) { + print_fail(); + return 1; + } + print_ok(); + + // We have completed the booting procedure. + pr_notice("Booting done, jumping into init process.\n"); + // Print the welcome message. + printf("\n .: Welcome to MentOS :.\n\n"); + // Switch to the page directory of init. + paging_switch_directory_va(init_p->mm->pgd); + // Jump into init process. + scheduler_enter_user_jmp( + // Entry point. + init_p->thread.regs.eip, + // Stack pointer. + init_p->thread.regs.useresp); + // Enable interrupt requests. + sti(); + for (;;) {} + // We should not be here. + pr_emerg("Dear developer, we have to talk...\n"); + return 1; } diff --git a/mentos/src/kernel/sys.c b/mentos/src/kernel/sys.c index e1c022e..e968039 100644 --- a/mentos/src/kernel/sys.c +++ b/mentos/src/kernel/sys.c @@ -1,72 +1,72 @@ /// MentOS, The Mentoring Operating system project /// @file sys.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "stdio.h" #include "errno.h" -#include "mutex.h" #include "reboot.h" #include "stdatomic.h" +#include "mutex.h" static void machine_power_off() { - while (true) { - cpu_relax(); - } + while (true) { + cpu_relax(); + } } /// @brief Shutdown everything and perform a clean system power_off. static void kernel_power_off() { - // kernel_shutdown_prepare(SYSTEM_POWER_OFF); - // if (pm_power_off_prepare) - // { - // pm_power_off_prepare(); - // } - // migrate_to_reboot_cpu(); - // syscore_shutdown(); - printf("Power down\n"); - // kmsg_dump(KMSG_DUMP_POWEROFF); - machine_power_off(); + // kernel_shutdown_prepare(SYSTEM_POWER_OFF); + // if (pm_power_off_prepare) + // { + // pm_power_off_prepare(); + // } + // migrate_to_reboot_cpu(); + // syscore_shutdown(); + printf("Power down\n"); + // kmsg_dump(KMSG_DUMP_POWEROFF); + machine_power_off(); } int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg) { - static mutex_t reboot_mutex; + static mutex_t reboot_mutex; - // For safety, we require "magic" arguments. - if (magic1 != LINUX_REBOOT_MAGIC1 || - (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && - magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) { - return -EINVAL; - } + // For safety, we require "magic" arguments. + if (magic1 != LINUX_REBOOT_MAGIC1 || + (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && + magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) { + return -EINVAL; + } - mutex_lock(&reboot_mutex, 0); + mutex_lock(&reboot_mutex, 0); - switch (cmd) { - case LINUX_REBOOT_CMD_RESTART: - break; - case LINUX_REBOOT_CMD_CAD_ON: - break; - case LINUX_REBOOT_CMD_CAD_OFF: - break; - case LINUX_REBOOT_CMD_HALT: - break; - case LINUX_REBOOT_CMD_POWER_OFF: - kernel_power_off(); - break; - case LINUX_REBOOT_CMD_RESTART2: - break; - case LINUX_REBOOT_CMD_KEXEC: - break; - case LINUX_REBOOT_CMD_SW_SUSPEND: - break; - default: - return -EINVAL; - } - mutex_unlock(&reboot_mutex); + switch (cmd) { + case LINUX_REBOOT_CMD_RESTART: + break; + case LINUX_REBOOT_CMD_CAD_ON: + break; + case LINUX_REBOOT_CMD_CAD_OFF: + break; + case LINUX_REBOOT_CMD_HALT: + break; + case LINUX_REBOOT_CMD_POWER_OFF: + kernel_power_off(); + break; + case LINUX_REBOOT_CMD_RESTART2: + break; + case LINUX_REBOOT_CMD_KEXEC: + break; + case LINUX_REBOOT_CMD_SW_SUSPEND: + break; + default: + return -EINVAL; + } + mutex_unlock(&reboot_mutex); - return 0; + return 0; } diff --git a/mentos/src/klib/assert.c b/mentos/src/klib/assert.c new file mode 100644 index 0000000..63960a1 --- /dev/null +++ b/mentos/src/klib/assert.c @@ -0,0 +1,21 @@ +/// MentOS, The Mentoring Operating system project +/// @file assert.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "assert.h" +#include "stdio.h" +#include "panic.h" + +void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) +{ + char message[1024]; + sprintf(message, + "FILE: %s\n" + "FUNC: %s\n" + "LINE: %d\n\n" + "Assertion `%s` failed.\n", + file, (function ? function : "NO_FUN"), line, assertion); + kernel_panic(message); +} diff --git a/mentos/src/klib/ctype.c b/mentos/src/klib/ctype.c new file mode 100644 index 0000000..88ce35f --- /dev/null +++ b/mentos/src/klib/ctype.c @@ -0,0 +1,63 @@ +/// MentOS, The Mentoring Operating system project +/// @file ctype.c +/// @brief Functions related to character handling. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ctype.h" + +/// Distance from a uppercase character to the correspondent lowercase in ASCII. +#define OFFSET 32 + +int isdigit(int c) +{ + return (c >= 48 && c <= 57); +} + +int isalpha(int c) +{ + return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)); +} + +int isalnum(int c) +{ + return (isalpha(c) || isdigit(c)); +} + +int isxdigit(int c) +{ + return (isdigit(c) || (c >= 65 && c <= 70)); +} + +int islower(int c) +{ + return (c >= 97 && c <= 122); +} + +int isupper(int c) +{ + return (c >= 65 && c <= 90); +} + +int tolower(int c) +{ + if (isalpha(c) == 0 || islower(c)) { + return c; + } + + return c + OFFSET; +} + +int toupper(int c) +{ + if (isalpha(c) == 0 || isupper(c)) { + return c; + } + + return c - OFFSET; +} + +int isspace(int c) +{ + return (c == ' '); +} diff --git a/mentos/src/klib/fcvt.c b/mentos/src/klib/fcvt.c new file mode 100644 index 0000000..750f564 --- /dev/null +++ b/mentos/src/klib/fcvt.c @@ -0,0 +1,104 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcvt.c +/// @brief Define the functions required to turn double values into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fcvt.h" +#include "math.h" + +static void cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, unsigned buf_size, int eflag) +{ + int r2; + double fi, fj; + char *p, *p1; + + char *buf_end = (buf + buf_size); + + if (ndigits < 0) { + ndigits = 0; + } + + if (ndigits >= buf_size - 1) { + ndigits = buf_size - 2; + } + + r2 = 0; + *sign = 0; + p = &buf[0]; + + if (arg < 0) { + *sign = 1; + arg = -arg; + } + + arg = modf(arg, &fi); + p1 = buf_end; + + if (fi != 0) { + p1 = buf_end; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int)((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < buf_end) { + *p++ = *p1++; + } + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + + p1 = &buf[ndigits]; + if (eflag == 0) { + p1 += r2; + } + + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = '\0'; + return; + } + + while (p <= p1 && p < buf_end) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int)fj + '0'; + } + + if (p1 >= buf_end) { + buf[buf_size - 1] = '\0'; + return; + } + + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) { + ++*--p1; + } else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > buf) + *p = '0'; + p++; + } + } + } + *p = '\0'; +} + +void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, chars, decpt, sign, buf, buf_size, 1); +} + +void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, decimals, decpt, sign, buf, buf_size, 0); +} diff --git a/mentos/src/klib/hashmap.c b/mentos/src/klib/hashmap.c new file mode 100644 index 0000000..27d7d8a --- /dev/null +++ b/mentos/src/klib/hashmap.c @@ -0,0 +1,254 @@ +/// MentOS, The Mentoring Operating system project +/// @file hashmap.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "hashmap.h" +#include "assert.h" +#include "string.h" +#include "slab.h" + +/// @brief Stores information of an entry of the hashmap. +struct hashmap_entry_t { + /// Key of the entry. + char *key; + /// Value of the entry. + void *value; + /// Pointer to the next entry. + struct hashmap_entry_t *next; +}; + +/// @brief Stores information of a hashmap. +struct hashmap_t { + /// Hashing function, used to generate hash keys. + hashmap_hash_t hash_func; + /// Comparison function, used to compare hash keys. + hashmap_comp_t hash_comp; + /// Key duplication function, used to duplicate hash keys. + hashmap_dupe_t hash_key_dup; + /// Key deallocation function, used to free the memory occupied by hash keys. + hashmap_free_t hash_key_free; + /// Size of the hashmap. + unsigned int size; + /// List of entries. + hashmap_entry_t **entries; +}; + +static inline hashmap_t *__alloc_hashmap() +{ + hashmap_t *hashmap = kmalloc(sizeof(hashmap_t)); + memset(hashmap, 0, sizeof(hashmap_t)); + return hashmap; +} + +static inline hashmap_entry_t *__alloc_entry() +{ + hashmap_entry_t *entry = kmalloc(sizeof(hashmap_entry_t)); + memset(entry, 0, sizeof(hashmap_entry_t)); + return entry; +} + +static inline void __dealloc_entry(hashmap_entry_t *entry) +{ + assert(entry && "Invalid pointer to an entry."); + kfree(entry); +} + +static inline hashmap_entry_t **__alloc_entries(unsigned int size) +{ + hashmap_entry_t **entries = kmalloc(sizeof(hashmap_entry_t *) * size); + memset(entries, 0, sizeof(hashmap_entry_t *) * size); + return entries; +} + +static inline void __dealloc_entries(hashmap_entry_t **entries) +{ + assert(entries && "Invalid pointer to entries."); + kfree(entries); +} + +unsigned int hashmap_int_hash(const void *key) +{ + return (unsigned int)key; +} + +int hashmap_int_comp(const void *a, const void *b) +{ + return (int)a == (int)b; +} + +unsigned int hashmap_str_hash(const void *_key) +{ + unsigned int hash = 0; + const char *key = (const char *)_key; + char c; + // This is the so-called "sdbm" hash. It comes from a piece of public + // domain code from a clone of ndbm. + while ((c = *key++)) + hash = c + (hash << 6) + (hash << 16) - hash; + return hash; +} + +int hashmap_str_comp(const void *a, const void *b) +{ + return !strcmp(a, b); +} + +void *hashmap_do_not_duplicate(const void *value) +{ + return (void *)value; +} + +void hashmap_do_not_free(void *value) +{ + (void)value; +} + +hashmap_t *hashmap_create( + unsigned int size, + hashmap_hash_t hash_fun, + hashmap_comp_t comp_fun, + hashmap_dupe_t dupe_fun, + hashmap_free_t key_free_fun) +{ + // Allocate the map. + hashmap_t *map = __alloc_hashmap(); + // Initialize the entries. + map->size = size; + map->entries = __alloc_entries(size); + // Initialize its functions. + map->hash_func = hash_fun; + map->hash_comp = comp_fun; + map->hash_key_dup = dupe_fun; + map->hash_key_free = key_free_fun; + return map; +} + +void hashmap_free(hashmap_t *map) +{ + for (unsigned int i = 0; i < map->size; ++i) { + hashmap_entry_t *x = map->entries[i], *p; + while (x) { + p = x; + x = x->next; + map->hash_key_free(p->key); + __dealloc_entry(p); + } + } + __dealloc_entries(map->entries); +} + +void *hashmap_set(hashmap_t *map, const void *key, void *value) +{ + unsigned int hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; + + if (x == NULL) { + hashmap_entry_t *e = __alloc_entry(); + e->key = map->hash_key_dup(key); + e->value = value; + e->next = NULL; + map->entries[hash] = e; + + return NULL; + } + + hashmap_entry_t *p = NULL; + + do { + if (map->hash_comp(x->key, key)) { + void *out = x->value; + x->value = value; + + return out; + } + p = x; + x = x->next; + } while (x); + + hashmap_entry_t *e = __alloc_entry(); + e->key = map->hash_key_dup(key); + e->value = value; + e->next = NULL; + p->next = e; + + return NULL; +} + +void *hashmap_get(hashmap_t *map, const void *key) +{ + unsigned int hash = map->hash_func(key) % map->size; + for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) + if (map->hash_comp(x->key, key)) + return x->value; + return NULL; +} + +void *hashmap_remove(hashmap_t *map, const void *key) +{ + unsigned int hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; + + if (x == NULL) { + return NULL; + } + if (map->hash_comp(x->key, key)) { + void *out = x->value; + map->entries[hash] = x->next; + map->hash_key_free(x->key); + __dealloc_entry(x); + return out; + } + + hashmap_entry_t *p = x; + x = x->next; + do { + if (map->hash_comp(x->key, key)) { + void *out = x->value; + p->next = x->next; + map->hash_key_free(x->key); + __dealloc_entry(x); + return out; + } + p = x; + x = x->next; + } while (x); + + return NULL; +} + +int hashmap_is_empty(hashmap_t *map) +{ + for (unsigned int i = 0; i < map->size; ++i) + if (map->entries[i]) + return 0; + return 1; +} + +int hashmap_has(hashmap_t *map, const void *key) +{ + unsigned int hash = map->hash_func(key) % map->size; + for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) + if (map->hash_comp(x->key, key)) + return 1; + return 0; +} + +list_t *hashmap_keys(hashmap_t *map) +{ + list_t *l = list_create(); + for (unsigned int i = 0; i < map->size; ++i) + for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) + list_insert_back(l, x->key); + return l; +} + +list_t *hashmap_values(hashmap_t *map) +{ + list_t *l = list_create(); + for (unsigned int i = 0; i < map->size; ++i) + for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) + list_insert_back(l, x->value); + return l; +} diff --git a/mentos/src/klib/libgen.c b/mentos/src/klib/libgen.c new file mode 100644 index 0000000..a3fa585 --- /dev/null +++ b/mentos/src/klib/libgen.c @@ -0,0 +1,142 @@ +/// MentOS, The Mentoring Operating system project +/// @file libgen.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "syscall.h" +#include "libgen.h" +#include "string.h" +#include "initrd.h" +#include "limits.h" +#include "assert.h" +#include "paging.h" + +int parse_path(char *out, char **cur, char sep, size_t max) +{ + if (**cur == '\0') { + return 0; + } + + *out++ = **cur; + ++*cur; + --max; + + while ((max > 0) && (**cur != '\0') && (**cur != sep)) { + *out++ = **cur; + ++*cur; + --max; + } + + *out = '\0'; + + return 1; +} + +char *dirname(const char *path) +{ + static char s[PATH_MAX]; + + static char dot[2] = "."; + + // Check the input path. + if (path == NULL) { + return dot; + } + + // Copy the path to the support string. + strcpy(s, path); + + // Get the last occurrence of '/'. + char *last_slash = strrchr(s, '/'); + + if (last_slash == s) { + // If the slash is acutally the first character of the string, move the + // pointer to the last slash after it. + ++last_slash; + } else if ((last_slash != NULL) && (last_slash[1] == '\0')) { + // If the slash is the last character, we need to search before it. + last_slash = memchr(s, '/', last_slash - s); + } + + if (last_slash != NULL) { + // If we have found it, close the string. + last_slash[0] = '\0'; + } else { + // Otherwise, return '.'. + return dot; + } + + return s; +} + +char *basename(const char *path) +{ + char *p = strrchr(path, '/'); + + return p ? p + 1 : (char *)path; +} + +char *realpath(const char *path, char *resolved) +{ + assert(path && "Provided null path."); + if (resolved == NULL) + resolved = kmalloc(sizeof(char) * PATH_MAX); + char abspath[PATH_MAX]; + // Initialize the absolute path. + memset(abspath, '\0', PATH_MAX); + int remaining; + if (path[0] != '/') { + // Get the current task. + sys_getcwd(abspath, PATH_MAX); + // Check the current task. + assert((strlen(abspath) > 0) && "There is no current task."); + // Check that the current working directory is an absolute path. + assert((abspath[0] == '/') && "Current working directory is not an absolute path."); + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + // Set the number of characters that should be copied, + // based on the current absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Append the path. + strncat(abspath, path, remaining); + } else { + // Copy the path into the absolute path. + strncpy(abspath, path, PATH_MAX - 1); + } + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + + int absidx = 0, pathidx = 0; + + while (abspath[absidx]) { + // Skip multiple consecutive / characters + if (!strncmp("//", abspath + absidx, 2)) { + absidx++; + } + // Go to previous directory if /../ is found + else if (!strncmp("/../", abspath + absidx, 4)) { + // Go to a valid path character (pathidx points to the next one) + if (pathidx) + pathidx--; + while (pathidx && resolved[pathidx] != '/') { + pathidx--; + } + absidx += 3; + } else if (!strncmp("/./", abspath + absidx, 3)) { + absidx += 2; + } else { + resolved[pathidx++] = abspath[absidx++]; + } + } + // Remove the last / + if (pathidx > 1) + resolved[pathidx - 1] = '\0'; + else + resolved[1] = '\0'; + return resolved; +} \ No newline at end of file diff --git a/mentos/src/klib/list.c b/mentos/src/klib/list.c new file mode 100644 index 0000000..4209f2a --- /dev/null +++ b/mentos/src/klib/list.c @@ -0,0 +1,331 @@ +/// MentOS, The Mentoring Operating system project +/// @file list.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "list.h" +#include "assert.h" +#include "string.h" +#include "slab.h" + +static inline listnode_t *__node_alloc() +{ + listnode_t *node = kmalloc(sizeof(listnode_t)); + memset(node, 0, sizeof(listnode_t)); + return node; +} + +static inline void __node_dealloc(listnode_t *node) +{ + assert(node && "Invalid pointer to node."); + kfree(node); +} + +static inline void __list_dealloc(list_t *list) +{ + assert(list && "Invalid pointer to list."); + kfree(list); +} + +list_t *list_create() +{ + list_t *list = kmalloc(sizeof(list_t)); + memset(list, 0, sizeof(list_t)); + return list; +} + +unsigned int list_size(list_t *list) +{ + assert(list && "List is null."); + + return list->size; +} + +int list_empty(list_t *list) +{ + assert(list && "List is null."); + + if (list->size == 0) { + return 1; + } + + return 0; +} + +listnode_t *list_insert_front(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + // Create a new node. + listnode_t *node = __node_alloc(); + + list->head->prev = node; + node->next = list->head; + node->value = value; + + // If it's the first element, then it's both head and tail + if (!list->head) { + list->tail = node; + } + + list->head = node; + list->size++; + + return node; +} + +listnode_t *list_insert_back(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + // Create a new node. + listnode_t *node = __node_alloc(); + node->prev = list->tail; + + if (list->tail != NULL) { + list->tail->next = node; + } + node->value = value; + + if (list->head == NULL) { + list->head = node; + } + + list->tail = node; + list->size++; + return node; +} + +void *list_remove_node(list_t *list, listnode_t *node) +{ + assert(list && "List is null."); + assert(node && "Node is null."); + + if (list->head == node) { + return list_remove_front(list); + } else if (list->tail == node) { + return list_remove_back(list); + } + + void *value = node->value; + node->next->prev = node->prev; + node->prev->next = node->next; + list->size--; + __node_dealloc(node); + + return value; +} + +void *list_remove_front(list_t *list) +{ + assert(list && "List is null."); + + if (list->head == NULL) { + return NULL; + } + + listnode_t *node = list->head; + void *value = node->value; + list->head = node->next; + + if (list->head) { + list->head->prev = NULL; + } + __node_dealloc(node); + list->size--; + + return value; +} + +void *list_remove_back(list_t *list) +{ + assert(list && "List is null."); + + if (list->head == NULL) { + return NULL; + } + + listnode_t *node = list->tail; + void *value = node->value; + list->tail = node->prev; + + if (list->tail) { + list->tail->next = NULL; + } + __node_dealloc(node); + list->size--; + + return value; +} + +listnode_t *list_find(list_t *list, void *value) +{ + listnode_foreach(listnode, list) + { + if (listnode->value == value) { + return listnode; + } + } + + return NULL; +} + +void list_push_back(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + list_insert_back(list, value); +} + +listnode_t *list_pop_back(list_t *list) +{ + assert(list && "List is null."); + + if (!list->head) { + return NULL; + } + + listnode_t *node = list->tail; + list->tail = node->prev; + + if (list->tail) { + list->tail->next = NULL; + } + + list->size--; + + return node; +} + +listnode_t *list_pop_front(list_t *list) +{ + assert(list && "List is null."); + + if (!list->head) { + return NULL; + } + + listnode_t *node = list->head; + list->head = list->head->next; + list->size--; + + return node; +} + +void list_push_front(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + list_insert_front(list, value); +} + +void *list_peek_front(list_t *list) +{ + assert(list && "List is null."); + + if (!list->head) { + return NULL; + } + + return list->head->value; +} + +void *list_peek_back(list_t *list) +{ + assert(list && "List is null."); + + if (!list->tail) { + return NULL; + } + + return list->tail->value; +} + +int list_get_index_of_value(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + int idx = 0; + listnode_foreach(listnode, list) + { + if (listnode->value == value) { + return idx; + } + ++idx; + } + + return -1; +} + +listnode_t *list_get_node_by_index(list_t *list, unsigned int index) +{ + assert(list && "List is null."); + + if (index >= list_size(list)) { + return NULL; + } + + unsigned int curr = 0; + listnode_foreach(listnode, list) + { + if (index == curr) { + return listnode; + } + curr++; + } + return NULL; +} + +void *list_remove_by_index(list_t *list, unsigned int index) +{ + assert(list && "List is null."); + + listnode_t *node = list_get_node_by_index(list, index); + if (node != NULL) { + return list_remove_node(list, node); + } + + return NULL; +} + +void list_destroy(list_t *list) +{ + assert(list && "List is null."); + + // Deallocate each node. + listnode_t *node = list->head; + while (node != NULL) { + listnode_t *save = node; + node = node->next; + __node_dealloc(save); + } + + // Free the list. + __list_dealloc(list); +} + +void list_merge(list_t *target, list_t *source) +{ + assert(target && "Target list is null."); + assert(source && "Source list is null."); + + // Destructively merges source into target. + if (target->tail) { + target->tail->next = source->head; + } else { + target->head = source->head; + } + + if (source->tail) { + target->tail = source->tail; + } + + target->size += source->size; + __list_dealloc(source); +} diff --git a/mentos/src/klib/math.c b/mentos/src/klib/math.c new file mode 100644 index 0000000..35ffffd --- /dev/null +++ b/mentos/src/klib/math.c @@ -0,0 +1,180 @@ +/// MentOS, The Mentoring Operating system project +/// @file math.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "math.h" +#include "stdint.h" + +double round(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; frndint" + : "=t"(out) + : "m"(x)); + return out; +} + +double floor(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x >= 0) + return 0.0; + return -1.0; + } + int i = (int)x; + if (x < 0) + return (double)(i - 1); + return (double)i; +} + +double ceil(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x <= 0) + return 0.0; + return 1.0; + } + int i = (int)x; + if (x > 0) + return (double)(i + 1); + return (double)i; +} + +double pow(double base, double exponent) +{ + double out; + __asm__ __volatile__("fyl2x;" + "fld %%st;" + "frndint;" + "fsub %%st,%%st(1);" + "fxch;" + "fchs;" + "f2xm1;" + "fld1;" + "faddp;" + "fxch;" + "fld1;" + "fscale;" + "fstp %%st(1);" + "fmulp;" + : "=t"(out) + : "0"(base), "u"(exponent) + : "st(1)"); + return out; +} + +double exp(double x) +{ + return pow(M_E, x); +} + +double fabs(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +float fabsf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +double sqrt(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +float sqrtf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +int isinf(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U && + ((unsigned)ieee754.u == 0); +} + +int isnan(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + + ((unsigned)ieee754.u != 0) > + 0x7ff00000; +} + +double log10(double x) +{ + return ln(x) / ln(10); +} + +double ln(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fyl2x" + : "=t"(out) + : "m"(x)); + return out; +} + +double logx(double x, double y) +{ + // Base may not equal 1 or be negative. + if (y == 1.f || y < 0.f || ln(y) == 0.f) + return 0.f; + return ln(x) / ln(y); +} + +/// Max power for forward and reverse projections. +#define MAXPOWTWO 4.503599627370496000E+15 + +double modf(double x, double *intpart) +{ + register double absvalue; + if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { + // It must be an integer. + (*intpart) = x; + } else { + // Shift fraction off right. + (*intpart) = absvalue + MAXPOWTWO; + // Shift back without fraction. + (*intpart) -= MAXPOWTWO; + + // Above arithmetic might round. + while ((*intpart) > absvalue) { + // Test again just to be sure. + (*intpart) -= 1.0; + } + if (x < 0.0) { + (*intpart) = -(*intpart); + } + } + // Signed fractional part. + return (x - (*intpart)); +} diff --git a/mentos/src/klib/mutex.c b/mentos/src/klib/mutex.c new file mode 100644 index 0000000..4d9e1f1 --- /dev/null +++ b/mentos/src/klib/mutex.c @@ -0,0 +1,34 @@ +/// MentOS, The Mentoring Operating system project +/// @file mutex.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "mutex.h" +#include "debug.h" + +void mutex_lock(mutex_t *mutex, uint32_t owner) +{ + pr_debug("[%d] Trying to lock mutex...\n", owner); + int failure = 1; + + while (mutex->state == 0 || failure || mutex->owner != owner) { + failure = 1; + if (mutex->state == 0) { + asm("movl $0x01,%%eax\n\t" // move 1 to eax + "xchg %%eax,%0\n\t" // try to set the lock bit + "mov %%eax,%1\n\t" // export our result to a test var + : "=m"(mutex->state), "=r"(failure) + : "m"(mutex->state) + : "%eax"); + } + if (failure == 0) { + mutex->owner = owner; //test to see if we got the lock bit + } + } +} + +void mutex_unlock(mutex_t *mutex) +{ + mutex->state = 0; +} diff --git a/mentos/src/klib/ndtree.c b/mentos/src/klib/ndtree.c new file mode 100644 index 0000000..3564c5a --- /dev/null +++ b/mentos/src/klib/ndtree.c @@ -0,0 +1,378 @@ +/// MentOS, The Mentoring Operating system project +/// @file ndtree.c +/// @brief Red/Black tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "debug.h" +#include "ndtree.h" +#include "assert.h" +#include "list_head.h" +#include "slab.h" + +// ============================================================================ +// Tree types. + +/// @brief Stores data about an NDTree node. +struct ndtree_node_t { + /// User provided, used indirectly via ndtree_tree_cmp_f. + void *value; + /// Pointer to the parent. + ndtree_node_t *parent; + /// List of siblings. + list_head siblings; + /// List of children. + list_head children; +}; + +/// @brief Stores data about an NDTree. +struct ndtree_t { + /// Comparison function. + ndtree_tree_cmp_f cmp; + /// Size of the tree. + size_t size; + /// Pointer to the root node. + ndtree_node_t *root; + /// List of orphans. + list_head orphans; +}; + +/// @brief Stores data about an NDTree iterator. +struct ndtree_iter_t { + /// Pointer to the head of the list. + list_head *head; + /// Pointer to the current element of the list. + list_head *current; +}; + +// ============================================================================ +// Default Comparison functions. +static inline int __ndtree_tree_node_cmp_ptr_cb(ndtree_t *self, void *a, void *b) +{ + return (a > b) - (a < b); +} + +// ============================================================================ +// Node management functions. + +ndtree_node_t *ndtree_node_alloc() +{ + return kmalloc(sizeof(ndtree_node_t)); +} + +ndtree_node_t *ndtree_node_create(void *value) +{ + ndtree_node_t *node = ndtree_node_alloc(); + node = ndtree_node_init(node, value); + return node; +} + +ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value) +{ + if (node) { + node->value = value; + node->parent = NULL; + list_head_init(&node->siblings); + list_head_init(&node->children); + } + return node; +} + +void ndtree_node_set_value(ndtree_node_t *node, void *value) +{ + if (node && value) { + node->value = value; + } +} + +void *ndtree_node_get_value(ndtree_node_t *node) +{ + if (node) + return node->value; + return NULL; +} + +void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node) +{ + tree->root = node; + ++tree->size; +} + +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value) +{ + ndtree_node_t *node = ndtree_node_create(value); + ndtree_set_root(tree, node); + return node; +} + +ndtree_node_t *ndtree_get_root(ndtree_t *tree) +{ + return tree->root; +} + +void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child) +{ + child->parent = parent; + list_head_add(&child->siblings, &parent->children); + ++tree->size; +} + +ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value) +{ + ndtree_node_t *child = ndtree_node_create(value); + ndtree_add_child_to_node(tree, parent, child); + return child; +} + +unsigned int ndtree_node_count_children(ndtree_node_t *node) +{ + unsigned int children = 0; + if (node) { + list_for_each_decl(it, &node->children) + { + ++children; + } + } + return children; +} + +void ndtree_node_dealloc(ndtree_node_t *node) +{ + if (node) + kfree(node); +} + +// ============================================================================ +// Tree management functions. +ndtree_t *ndtree_tree_alloc() +{ + return kmalloc(sizeof(ndtree_t)); +} + +ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp) +{ + return ndtree_tree_init(ndtree_tree_alloc(), cmp); +} + +ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f node_cmp_cb) +{ + if (tree) { + tree->size = 0; + tree->cmp = node_cmp_cb ? node_cmp_cb : __ndtree_tree_node_cmp_ptr_cb; + tree->root = NULL; + } + return tree; +} + +static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +{ + if (node && node_cb) { + if (!list_head_empty(&node->children)) { + list_head *it_save; + list_for_each_decl(it, &node->children) + { + ndtree_node_t *entry = list_entry(it, ndtree_node_t, siblings); + it_save = it->prev; + list_head_del(it); + it = it_save; + __ndtree_tree_dealloc_rec(tree, entry, node_cb); + } + } + node_cb(tree, node); + kfree(node); + } +} + +void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb) +{ + if (tree && tree->root && node_cb) + __ndtree_tree_dealloc_rec(tree, tree->root, node_cb); + kfree(tree); +} + +static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value, ndtree_node_t *node) +{ + ndtree_node_t *result = NULL; + if (tree && cmp && node && value) { + if (cmp(tree, node->value, value) == 0) { + result = node; + } else if (!list_head_empty(&node->children)) { + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + if ((result = __ndtree_tree_find_rec(tree, cmp, value, child)) != NULL) { + break; + } + } + } + } + return result; +} + +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value) +{ + if (tree && tree->root && value) + return __ndtree_tree_find_rec(tree, cmp, value, tree->root); + return NULL; +} + +ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value) +{ + if (tree && node && value) { + // Check only if the node has children. + if (!list_head_empty(&node->children)) { + // Check which compare function we need to use. + ndtree_tree_cmp_f cmp_fun = cmp ? cmp : tree->cmp; + // If neither the tree nor the function argument are valid, rollback to the + // default comparison function. + if (cmp_fun == NULL) + cmp_fun = __ndtree_tree_node_cmp_ptr_cb; + // Iterate throught the children. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + if (cmp_fun(tree, child->value, value) == 0) + return child; + } + } + } + return NULL; +} + +unsigned int ndtree_tree_size(ndtree_t *tree) +{ + if (tree) + return tree->size; + return 0; +} + +int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +{ + if (tree && node) { + // Remove the node from the parent list. + list_head_del(&node->siblings); + // If the node has children, we need to migrate them. + if (!list_head_empty(&node->children)) { + // The new parent, by default it is NULL. + ndtree_node_t *new_parent = NULL; + // The new list, by default it is the list of orphans of the tree. + list_head *new_list = &tree->orphans; + // If the found node has a parent, we need to set the variables + // so that we can migrate the children. + if (node->parent) { + new_parent = node->parent; + new_list = &node->parent->children; + } + // Migrate the children. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + child->parent = new_parent; + } + // Merge the lists. + list_head_merge(new_list, &node->children); + } + if (node_cb) + node_cb(tree, node); + else + ndtree_node_dealloc(node); + --tree->size; + return 1; + } + return 0; +} + +int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb) +{ + if (tree && value) { + ndtree_node_t *node = ndtree_tree_find(tree, tree->cmp, value); + return ndtree_tree_remove_node_with_cb(tree, node, node_cb); + } + return 0; +} + +// ============================================================================ +// Iterators. +ndtree_iter_t *ndtree_iter_alloc() +{ + ndtree_iter_t *iter = kmalloc(sizeof(ndtree_iter_t)); + iter->head = NULL; + iter->current = NULL; + return iter; +} + +void ndtree_iter_dealloc(ndtree_iter_t *iter) +{ + if (iter) + kfree(iter); +} + +ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter) +{ + if (node && iter) { + if (!list_head_empty(&node->children)) { + iter->head = &node->children; + iter->current = iter->head->next; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter) +{ + if (node && iter) { + if (!list_head_empty(&node->children)) { + iter->head = &node->children; + iter->current = iter->head->prev; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter) +{ + if (iter) { + if (iter->current->next != iter->head) { + iter->current = iter->current->next; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter) +{ + if (iter) { + if (iter->current->next != iter->head) { + iter->current = iter->current->prev; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +// ============================================================================ +// Tree debugging functions. +static void __ndtree_tree_visitor_iter(ndtree_t *tree, + ndtree_node_t *node, + ndtree_tree_node_f enter_fun, + ndtree_tree_node_f exit_fun) +{ + assert(tree); + assert(node); + if (enter_fun) + enter_fun(tree, node); + if (!list_head_empty(&node->children)) + list_for_each_decl(it, &node->children) + __ndtree_tree_visitor_iter(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun); + if (exit_fun) + exit_fun(tree, node); +} + +void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun) +{ + if (tree && tree->root) + __ndtree_tree_visitor_iter(tree, tree->root, enter_fun, exit_fun); +} \ No newline at end of file diff --git a/mentos/src/klib/rbtree.c b/mentos/src/klib/rbtree.c new file mode 100644 index 0000000..ca686c5 --- /dev/null +++ b/mentos/src/klib/rbtree.c @@ -0,0 +1,564 @@ +/// MentOS, The Mentoring Operating system project +/// @file rbtree.c +/// @brief Red/Black tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "rbtree.h" + +#include "assert.h" +#include "debug.h" +#include "slab.h" + +/// @brief Stores information of a node. +struct rbtree_node_t { + /// Color red (1), black (0) + int red; + /// Link left [0] and right [1] + rbtree_node_t *link[2]; + /// User provided, used indirectly via rbtree_tree_node_cmp_f. + void *value; +}; + +/// @brief Stores information of a rbtree. +struct rbtree_t { + /// Root of the tree. + rbtree_node_t *root; + /// Comparison function for insertion. + rbtree_tree_node_cmp_f cmp; + /// Size of the tree. + unsigned int size; +}; + +/// @brief Stores information for iterating a rbtree. +struct rbtree_iter_t { + /// Pointer to the tree itself. + rbtree_t *tree; + /// Current node + rbtree_node_t *node; + /// Traversal path + rbtree_node_t *path[RBTREE_ITER_MAX_HEIGHT]; + /// Top of stack + unsigned int top; +}; + +rbtree_node_t *rbtree_node_alloc() +{ + return kmalloc(sizeof(rbtree_node_t)); +} + +rbtree_node_t *rbtree_node_init(rbtree_node_t *node, void *value) +{ + if (node) { + node->red = 1; + node->link[0] = node->link[1] = NULL; + node->value = value; + } + return node; +} + +rbtree_node_t *rbtree_node_create(void *value) +{ + return rbtree_node_init(rbtree_node_alloc(), value); +} + +void *rbtree_node_get_value(rbtree_node_t *node) +{ + if (node) + return node->value; + return NULL; +} + +void rbtree_node_dealloc(rbtree_node_t *node) +{ + if (node) + kfree(node); +} + +static int rbtree_node_is_red(const rbtree_node_t *node) +{ + return node ? node->red : 0; +} + +static rbtree_node_t *rbtree_node_rotate(rbtree_node_t *node, int dir) +{ + rbtree_node_t *result = NULL; + if (node) { + result = node->link[!dir]; + node->link[!dir] = result->link[dir]; + result->link[dir] = node; + node->red = 1; + result->red = 0; + } + return result; +} + +static rbtree_node_t *rbtree_node_rotate2(rbtree_node_t *node, int dir) +{ + rbtree_node_t *result = NULL; + if (node) { + node->link[!dir] = rbtree_node_rotate(node->link[!dir], !dir); + result = rbtree_node_rotate(node, dir); + } + return result; +} + +// rbtree_t - default callbacks + +static int rbtree_tree_node_cmp_ptr_cb( + rbtree_t *tree, + rbtree_node_t *a, + rbtree_node_t *b) +{ + (void)tree; + return (a->value > b->value) - (a->value < b->value); +} + +static void rbtree_tree_node_dealloc_cb(rbtree_t *tree, rbtree_node_t *node) +{ + if (tree) + if (node) + rbtree_node_dealloc(node); +} + +// rbtree_t + +rbtree_t *rbtree_tree_alloc() +{ + return kmalloc(sizeof(rbtree_t)); +} + +rbtree_t *rbtree_tree_init(rbtree_t *tree, rbtree_tree_node_cmp_f node_cmp_cb) +{ + if (tree) { + tree->root = NULL; + tree->size = 0; + tree->cmp = node_cmp_cb ? node_cmp_cb : rbtree_tree_node_cmp_ptr_cb; + } + return tree; +} + +rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f node_cb) +{ + return rbtree_tree_init(rbtree_tree_alloc(), node_cb); +} + +void rbtree_tree_dealloc(rbtree_t *tree, rbtree_tree_node_f node_cb) +{ + assert(tree); + if (node_cb) { + rbtree_node_t *node = tree->root; + rbtree_node_t *save = NULL; + + // Rotate away the left links so that + // we can treat this like the destruction + // of a linked list + while (node) { + if (node->link[0] == NULL) { + // No left links, just kill the node and move on + save = node->link[1]; + node_cb(tree, node); + kfree(node); + node = NULL; + } else { + // Rotate away the left link and check again + save = node->link[0]; + node->link[0] = save->link[1]; + save->link[1] = node; + } + node = save; + } + } + kfree(tree); +} + +void *rbtree_tree_find(rbtree_t *tree, void *value) +{ + void *result = NULL; + if (tree) { + rbtree_node_t node = { .value = value }; + rbtree_node_t *it = tree->root; + int cmp = 0; + while (it) { + if ((cmp = tree->cmp(tree, it, &node))) { + // If the tree supports duplicates, they should be + // chained to the right subtree for this to work + it = it->link[cmp < 0]; + } else { + break; + } + } + result = it ? it->value : NULL; + } + return result; +} + +void *rbtree_tree_find_by_value(rbtree_t *tree, + rbtree_tree_cmp_f cmp_fun, + void *value) +{ + void *result = NULL; + if (tree) { + rbtree_node_t *it = tree->root; + int cmp = 0; + while (it) { + if ((cmp = cmp_fun(tree, it, value))) { + // If the tree supports duplicates, they should be + // chained to the right subtree for this to work + it = it->link[cmp < 0]; + } else { + break; + } + } + result = it ? it->value : NULL; + } + return result; +} + +// Creates (kmalloc'ates) +int rbtree_tree_insert(rbtree_t *tree, void *value) +{ + return rbtree_tree_insert_node(tree, rbtree_node_create(value)); +} + +// Returns 1 on success, 0 otherwise. +int rbtree_tree_insert_node(rbtree_t *tree, rbtree_node_t *node) +{ + if (tree && node) { + if (tree->root == NULL) { + tree->root = node; + } else { + rbtree_node_t head = { 0 }; // False tree root + rbtree_node_t *g, *t; // Grandparent & parent + rbtree_node_t *p, *q; // Iterator & parent + int dir = 0, last = 0; + + // Set up our helpers + t = &head; + g = p = NULL; + q = t->link[1] = tree->root; + + // Search down the tree for a place to insert + while (1) { + if (q == NULL) { + // Insert node at the first null link. + p->link[dir] = q = node; + } else if (rbtree_node_is_red(q->link[0]) && + rbtree_node_is_red(q->link[1])) { + // Simple red violation: color flip + q->red = 1; + q->link[0]->red = 0; + q->link[1]->red = 0; + } + + if (rbtree_node_is_red(q) && rbtree_node_is_red(p)) { + // Hard red violation: rotations necessary + int dir2 = t->link[1] == g; + if (q == p->link[last]) { + t->link[dir2] = rbtree_node_rotate(g, !last); + } else { + t->link[dir2] = rbtree_node_rotate2(g, !last); + } + } + + // Stop working if we inserted a node. This + // check also disallows duplicates in the tree + if (tree->cmp(tree, q, node) == 0) { + break; + } + + last = dir; + dir = tree->cmp(tree, q, node) < 0; + + // Move the helpers down + if (g != NULL) { + t = g; + } + + g = p, p = q; + q = q->link[dir]; + } + + // Update the root (it may be different) + tree->root = head.link[1]; + } + + // Make the root black for simplified logic + tree->root->red = 0; + ++tree->size; + } + + return 1; +} + +// Returns 1 if the value was removed, 0 otherwise. Optional node callback +// can be provided to dealloc node and/or user data. Use rbtree_tree_node_dealloc +// default callback to deallocate node created by rbtree_tree_insert(...). +int rbtree_tree_remove_with_cb(rbtree_t *tree, + void *value, + rbtree_tree_node_f node_cb) +{ + if (tree->root != NULL) { + rbtree_node_t head = { 0 }; // False tree root + rbtree_node_t node = { .value = value }; // Value wrapper node + rbtree_node_t *q, *p, *g; // Helpers + rbtree_node_t *f = NULL; // Found item + int dir = 1; + + // Set up our helpers + q = &head; + g = p = NULL; + q->link[1] = tree->root; + + // Search and push a red node down + // to fix red violations as we go + while (q->link[dir] != NULL) { + int last = dir; + + // Move the helpers down + g = p, p = q; + q = q->link[dir]; + dir = tree->cmp(tree, q, &node) < 0; + + // Save the node with matching value and keep + // going; we'll do removal tasks at the end + if (tree->cmp(tree, q, &node) == 0) { + f = q; + } + + // Push the red node down with rotations and color flips + if (!rbtree_node_is_red(q) && !rbtree_node_is_red(q->link[dir])) { + if (rbtree_node_is_red(q->link[!dir])) { + p = p->link[last] = rbtree_node_rotate(q, dir); + } else if (!rbtree_node_is_red(q->link[!dir])) { + rbtree_node_t *s = p->link[!last]; + if (s) { + if (!rbtree_node_is_red(s->link[!last]) && + !rbtree_node_is_red(s->link[last])) { + // Color flip + p->red = 0; + s->red = 1; + q->red = 1; + } else { + int dir2 = g->link[1] == p; + if (rbtree_node_is_red(s->link[last])) { + g->link[dir2] = rbtree_node_rotate2(p, last); + } else if (rbtree_node_is_red(s->link[!last])) { + g->link[dir2] = rbtree_node_rotate(p, last); + } + + // Ensure correct coloring + q->red = g->link[dir2]->red = 1; + g->link[dir2]->link[0]->red = 0; + g->link[dir2]->link[1]->red = 0; + } + } + } + } + } + + // Replace and remove the saved node + if (f) { + void *tmp = f->value; + f->value = q->value; + q->value = tmp; + + p->link[p->link[1] == q] = q->link[q->link[0] == NULL]; + + if (node_cb) { + node_cb(tree, q); + } + q = NULL; + } + + // Update the root (it may be different) + tree->root = head.link[1]; + + // Make the root black for simplified logic + if (tree->root != NULL) { + tree->root->red = 0; + } + + --tree->size; + } + return 1; +} + +int rbtree_tree_remove(rbtree_t *tree, void *value) +{ + int result = 0; + if (tree) { + result = rbtree_tree_remove_with_cb( + tree, value, rbtree_tree_node_dealloc_cb); + } + return result; +} + +unsigned int rbtree_tree_size(rbtree_t *tree) +{ + unsigned int result = 0; + if (tree) { + result = tree->size; + } + return result; +} + +// rbtree_iter_t + +rbtree_iter_t *rbtree_iter_alloc() +{ + return kmalloc(sizeof(rbtree_iter_t)); +} + +rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *iter) +{ + if (iter) { + iter->tree = NULL; + iter->node = NULL; + iter->top = 0; + } + return iter; +} + +rbtree_iter_t *rbtree_iter_create() +{ + return rbtree_iter_init(rbtree_iter_alloc()); +} + +void rbtree_iter_dealloc(rbtree_iter_t *iter) +{ + if (iter) { + kfree(iter); + } +} + +// Internal function, init traversal object, dir determines whether +// to begin traversal at the smallest or largest valued node. +static void *rbtree_iter_start(rbtree_iter_t *iter, rbtree_t *tree, int dir) +{ + void *result = NULL; + if (iter) { + iter->tree = tree; + iter->node = tree->root; + iter->top = 0; + + // Save the path for later selfersal + if (iter->node != NULL) { + while (iter->node->link[dir] != NULL) { + iter->path[iter->top++] = iter->node; + iter->node = iter->node->link[dir]; + } + } + + result = iter->node == NULL ? NULL : iter->node->value; + } + return result; +} + +// Traverse a red black tree in the user-specified direction (0 asc, 1 desc) +static void *rbtree_iter_move(rbtree_iter_t *iter, int dir) +{ + if (iter->node->link[dir] != NULL) { + // Continue down this branch + iter->path[iter->top++] = iter->node; + iter->node = iter->node->link[dir]; + while (iter->node->link[!dir] != NULL) { + iter->path[iter->top++] = iter->node; + iter->node = iter->node->link[!dir]; + } + } else { + // Move to the next branch + rbtree_node_t *last = NULL; + do { + if (iter->top == 0) { + iter->node = NULL; + break; + } + last = iter->node; + iter->node = iter->path[--iter->top]; + } while (last == iter->node->link[dir]); + } + return iter->node == NULL ? NULL : iter->node->value; +} + +void *rbtree_iter_first(rbtree_iter_t *iter, rbtree_t *tree) +{ + return rbtree_iter_start(iter, tree, 0); +} + +void *rbtree_iter_last(rbtree_iter_t *iter, rbtree_t *tree) +{ + return rbtree_iter_start(iter, tree, 1); +} + +void *rbtree_iter_next(rbtree_iter_t *iter) +{ + return rbtree_iter_move(iter, 1); +} + +void *rbtree_iter_prev(rbtree_iter_t *iter) +{ + return rbtree_iter_move(iter, 0); +} + +int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root) +{ + int lh, rh; + + if (root == NULL) + return 1; + else { + rbtree_node_t *ln = root->link[0]; + rbtree_node_t *rn = root->link[1]; + + /* Consecutive red links */ + if (rbtree_node_is_red(root)) { + if (rbtree_node_is_red(ln) || rbtree_node_is_red(rn)) { + pr_err("Red violation"); + return 0; + } + } + + lh = rbtree_tree_test(tree, ln); + rh = rbtree_tree_test(tree, rn); + + /* Invalid binary search tree */ + if ((ln != NULL && tree->cmp(tree, ln, root) >= 0) || (rn != NULL && tree->cmp(tree, rn, root) <= 0)) { + pr_err("Binary tree violation"); + return 0; + } + + /* Black height mismatch */ + if (lh != 0 && rh != 0 && lh != rh) { + pr_err("Black violation"); + return 0; + } + + /* Only count black links */ + if (lh != 0 && rh != 0) + return rbtree_node_is_red(root) ? lh : lh + 1; + else + return 0; + } +} + +static void rbtree_tree_print_iter(rbtree_t *tree, + rbtree_node_t *node, + rbtree_tree_node_f fun) +{ + assert(tree); + assert(node); + assert(fun); + fun(tree, node); + if (node->link[0]) + rbtree_tree_print_iter(tree, node->link[0], fun); + if (node->link[1]) + rbtree_tree_print_iter(tree, node->link[1], fun); +} + +void rbtree_tree_print(rbtree_t *tree, rbtree_tree_node_f fun) +{ + assert(tree); + assert(fun); + rbtree_tree_print_iter(tree, tree->root, fun); +} diff --git a/mentos/src/klib/spinlock.c b/mentos/src/klib/spinlock.c new file mode 100644 index 0000000..6f15c43 --- /dev/null +++ b/mentos/src/klib/spinlock.c @@ -0,0 +1,34 @@ +/// MentOS, The Mentoring Operating system project +/// @file spinlock.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "spinlock.h" + +void spinlock_init(spinlock_t *spinlock) +{ + (*spinlock) = SPINLOCK_FREE; +} + +void spinlock_lock(spinlock_t *spinlock) +{ + while (1) { + if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) { + break; + } + while (*spinlock) + cpu_relax(); + } +} + +void spinlock_unlock(spinlock_t *spinlock) +{ + barrier(); + atomic_set(spinlock, SPINLOCK_FREE); +} + +int spinlock_trylock(spinlock_t *spinlock) +{ + return atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0; +} diff --git a/mentos/src/klib/strerror.c b/mentos/src/klib/strerror.c new file mode 100644 index 0000000..a67b78c --- /dev/null +++ b/mentos/src/klib/strerror.c @@ -0,0 +1,480 @@ +/// MentOS, The Mentoring Operating system project +/// @file strerror.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "strerror.h" +#include "string.h" + +char *strerror(int errnum) +{ + static char error[1024]; + + switch (errnum) { + case 0: + strcpy(error, "Success"); + break; +#ifdef ENOENT + case ENOENT: + strcpy(error, "No such file or directory"); + break; +#endif +#ifdef ESRCH + case ESRCH: + strcpy(error, "No such process"); + break; +#endif +#ifdef EINTR + case EINTR: + strcpy(error, "Interrupted system call"); + break; +#endif +#ifdef EIO + case EIO: + strcpy(error, "I/O error"); + break; +#endif +#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) + case ENXIO: + strcpy(error, "No such device or address"); + break; +#endif +#ifdef E2BIG + case E2BIG: + strcpy(error, "Arg list too long"); + break; +#endif +#ifdef ENOEXEC + case ENOEXEC: + strcpy(error, "Exec format error"); + break; +#endif +#ifdef EALREADY + case EALREADY: + strcpy(error, "Socket already connected"); + break; +#endif +#ifdef EBADF + case EBADF: + strcpy(error, "Bad file number"); + break; +#endif +#ifdef ECHILD + case ECHILD: + strcpy(error, "No children"); + break; +#endif +#ifdef EDESTADDRREQ + case EDESTADDRREQ: + strcpy(error, "Destination address required"); + break; +#endif +#ifdef EAGAIN + case EAGAIN: + strcpy(error, "No more processes"); + break; +#endif +#ifdef ENOMEM + case ENOMEM: + strcpy(error, "Not enough space"); + break; +#endif +#ifdef EACCES + case EACCES: + strcpy(error, "Permission denied"); + break; +#endif +#ifdef EFAULT + case EFAULT: + strcpy(error, "Bad address"); + break; +#endif +#ifdef ENOTBLK + case ENOTBLK: + strcpy(error, "Block device required"); + break; +#endif +#ifdef EBUSY + case EBUSY: + strcpy(error, "Device or resource busy"); + break; +#endif +#ifdef EEXIST + case EEXIST: + strcpy(error, "File exists"); + break; +#endif +#ifdef EXDEV + case EXDEV: + strcpy(error, "Cross-device link"); + break; +#endif +#ifdef ENODEV + case ENODEV: + strcpy(error, "No such device"); + break; +#endif +#ifdef ENOTDIR + case ENOTDIR: + strcpy(error, "Not a directory"); + break; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: + strcpy(error, "Host is down"); + break; +#endif +#ifdef EINPROGRESS + case EINPROGRESS: + strcpy(error, "Connection already in progress"); + break; +#endif +#ifdef EISDIR + case EISDIR: + strcpy(error, "Is a directory"); + break; +#endif +#ifdef EINVAL + case EINVAL: + strcpy(error, "Invalid argument"); + break; +#endif +#ifdef EISNAM + case EISNAM: + strcpy(error, "Is a named type file"); + break; +#endif +#ifdef ENETDOWN + case ENETDOWN: + strcpy(error, "Network interface is not configured"); + break; +#endif +#ifdef ENFILE + case ENFILE: + strcpy(error, "Too many open files in system"); + break; +#endif +#ifdef EMFILE + case EMFILE: + strcpy(error, "Too many open files"); + break; +#endif +#ifdef ENOTTY + case ENOTTY: + strcpy(error, "Not a character device"); + break; +#endif +#ifdef ETXTBSY + case ETXTBSY: + strcpy(error, "Text file busy"); + break; +#endif +#ifdef EFBIG + case EFBIG: + strcpy(error, "File too large"); + break; +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: + strcpy(error, "Host is unreachable"); + break; +#endif +#ifdef ENOSPC + case ENOSPC: + strcpy(error, "No space left on device"); + break; +#endif +#ifdef ENOTSUP + case ENOTSUP: + strcpy(error, "Not supported"); + break; +#endif +#ifdef ESPIPE + case ESPIPE: + strcpy(error, "Illegal seek"); + break; +#endif +#ifdef EROFS + case EROFS: + strcpy(error, "Read-only file system"); + break; +#endif +#ifdef EMLINK + case EMLINK: + strcpy(error, "Too many links"); + break; +#endif +#ifdef EPIPE + case EPIPE: + strcpy(error, "Broken pipe"); + break; +#endif +#ifdef EDOM + case EDOM: + strcpy(error, "Math argument"); + break; +#endif +#ifdef ERANGE + case ERANGE: + strcpy(error, "Result too large"); + break; +#endif +#ifdef ENOMSG + case ENOMSG: + strcpy(error, "No message of desired type"); + break; +#endif +#ifdef EIDRM + case EIDRM: + strcpy(error, "Identifier removed"); + break; +#endif +#ifdef EDEADLK + case EDEADLK: + strcpy(error, "Deadlock"); + break; +#endif +#ifdef ENETUNREACH + case ENETUNREACH: + strcpy(error, "Network is unreachable"); + break; +#endif +#ifdef ENOLCK + case ENOLCK: + strcpy(error, "No lock"); + break; +#endif +#ifdef ENOSTR + case ENOSTR: + strcpy(error, "Not a stream"); + break; +#endif +#ifdef ETIME + case ETIME: + strcpy(error, "Stream ioctl timeout"); + break; +#endif +#ifdef ENOSR + case ENOSR: + strcpy(error, "No stream resources"); + break; +#endif +#ifdef ENONET + case ENONET: + strcpy(error, "Machine is not on the network"); + break; +#endif +#ifdef ENOPKG + case ENOPKG: + strcpy(error, "No package"); + break; +#endif +#ifdef EREMOTE + case EREMOTE: + strcpy(error, "Resource is remote"); + break; +#endif +#ifdef ENOLINK + case ENOLINK: + strcpy(error, "Virtual circuit is gone"); + break; +#endif +#ifdef EADV + case EADV: + strcpy(error, "Advertise error"); + break; +#endif +#ifdef ESRMNT + case ESRMNT: + strcpy(error, "Srmount error"); + break; +#endif +#ifdef ECOMM + case ECOMM: + strcpy(error, "Communication error"); + break; +#endif +#ifdef EPROTO + case EPROTO: + strcpy(error, "Protocol error"); + break; +#endif +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + strcpy(error, "Unknown protocol"); + break; +#endif +#ifdef EMULTIHOP + case EMULTIHOP: + strcpy(error, "Multihop attempted"); + break; +#endif +#ifdef EBADMSG + case EBADMSG: + strcpy(error, "Bad message"); + break; +#endif +#ifdef ELIBACC + case ELIBACC: + strcpy(error, "Cannot access a needed shared library"); + break; +#endif +#ifdef ELIBBAD + case ELIBBAD: + strcpy(error, "Accessing a corrupted shared library"); + break; +#endif +#ifdef ELIBSCN + case ELIBSCN: + strcpy(error, ".lib section in a.out corrupted"); + break; +#endif +#ifdef ELIBMAX + case ELIBMAX: + strcpy(error, + "Attempting to link in more shared libraries than system limit"); + break; +#endif +#ifdef ELIBEXEC + case ELIBEXEC: + strcpy(error, "Cannot exec a shared library directly"); + break; +#endif +#ifdef ENOSYS + case ENOSYS: + strcpy(error, "Function not implemented"); + break; +#endif +#ifdef ENMFILE + case ENMFILE: + strcpy(error, "No more files"); + break; +#endif +#ifdef ENOTEMPTY + case ENOTEMPTY: + strcpy(error, "Directory not empty"); + break; +#endif +#ifdef ENAMETOOLONG + case ENAMETOOLONG: + strcpy(error, "File or path name too long"); + break; +#endif +#ifdef ELOOP + case ELOOP: + strcpy(error, "Too many symbolic links"); + break; +#endif +#ifdef ENOBUFS + case ENOBUFS: + strcpy(error, "No buffer space available"); + break; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + strcpy(error, "Address family not supported by protocol family"); + break; +#endif +#ifdef EPROTOTYPE + case EPROTOTYPE: + strcpy(error, "Protocol wrong type for socket"); + break; +#endif +#ifdef ENOTSOCK + case ENOTSOCK: + strcpy(error, "Socket operation on non-socket"); + break; +#endif +#ifdef ENOPROTOOPT + case ENOPROTOOPT: + strcpy(error, "Protocol not available"); + break; +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: + strcpy(error, "Can't send after socket shutdown"); + break; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: + strcpy(error, "Connection refused"); + break; +#endif +#ifdef EADDRINUSE + case EADDRINUSE: + strcpy(error, "Address already in use"); + break; +#endif +#ifdef ECONNABORTED + case ECONNABORTED: + strcpy(error, "Software caused connection abort"); + break; +#endif +#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) + case EWOULDBLOCK: + strcpy(error, "Operation would block"); + break; +#endif +#ifdef ENOTCONN + case ENOTCONN: + strcpy(error, "Socket is not connected"); + break; +#endif +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + strcpy(error, "Socket type not supported"); + break; +#endif +#ifdef EISCONN + case EISCONN: + strcpy(error, "Socket is already connected"); + break; +#endif +#ifdef ECANCELED + case ECANCELED: + strcpy(error, "Operation canceled"); + break; +#endif +#ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: + strcpy(error, "State not recoverable"); + break; +#endif +#ifdef EOWNERDEAD + case EOWNERDEAD: + strcpy(error, "Previous owner died"); + break; +#endif +#ifdef ESTRPIPE + case ESTRPIPE: + strcpy(error, "Streams pipe error"); + break; +#endif +#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) + case EOPNOTSUPP: + strcpy(error, "Operation not supported on socket"); + break; +#endif +#ifdef EMSGSIZE + case EMSGSIZE: + strcpy(error, "Message too long"); + break; +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: + strcpy(error, "Connection timed out"); + break; +#endif +#ifdef ENOTSCHEDULABLE + case ENOTSCHEDULABLE: + strcpy(error, "The process cannot be scheduled"); + break; +#endif + default: + strcpy(error, "Unknown error"); + break; + } + + return error; +} diff --git a/mentos/src/klib/string.c b/mentos/src/klib/string.c new file mode 100644 index 0000000..b3e4b6c --- /dev/null +++ b/mentos/src/klib/string.c @@ -0,0 +1,701 @@ +/// MentOS, The Mentoring Operating system project +/// @file string.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "kheap.h" +#include "stdio.h" +#include "fcntl.h" +#include "string.h" +#include "ctype.h" + +char *strncpy(char *destination, const char *source, size_t num) +{ + char *start = destination; + while (num && (*destination++ = *source++)) { + num--; + } + if (num) { + while (--num) { + *destination++ = '\0'; + } + } + + return start; +} + +int strncmp(const char *s1, const char *s2, size_t n) +{ + if (!n) + return 0; + while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) { + s1++; + s2++; + } + + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +int stricmp(const char *s1, const char *s2) +{ + while (*s2 != 0 && toupper(*s1) == toupper(*s2)) { + s1++, s2++; + } + + return (toupper(*s1) - toupper(*s2)); +} + +int strnicmp(const char *s1, const char *s2, size_t n) +{ + int f, l; + + do { + if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) { + f -= 'A' - 'a'; + } + if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) { + l -= 'A' - 'a'; + } + } while (--n && f && (f == l)); + + return f - l; +} + +char *strchr(const char *s, int ch) +{ + while (*s && *s != (char)ch) { + s++; + } + if (*s == (char)ch) + return (char *)s; + { + return NULL; + } +} + +char *strrchr(const char *s, int ch) +{ + char *start = (char *)s; + + while (*s++) {} + + while (--s != start && *s != (char)ch) {} + + if (*s == (char)ch) { + return (char *)s; + } + + return NULL; +} + +char *strstr(const char *str1, const char *str2) +{ + char *cp = (char *)str1; + char *s1, *s2; + + if (!*str2) { + return (char *)str1; + } + + while (*cp) { + s1 = cp; + s2 = (char *)str2; + + while (*s1 && *s2 && !(*s1 - *s2)) { + s1++, s2++; + } + if (!*s2) { + return cp; + } + cp++; + } + + return NULL; +} + +size_t strspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char NOT in control map stops search. + if (*str) { + n = 0; + while (map[*str >> 3] & (1 << (*str & 7))) { + n++; + str++; + } + + return n; + } + + return 0; +} + +size_t strcspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + n = 0; + map[0] |= 1; + while (!(map[*str >> 3] & (1 << (*str & 7)))) { + n++; + str++; + } + + return n; +} + +char *strpbrk(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + int n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + while (*str) { + if (map[*str >> 3] & (1 << (*str & 7))) { + return (char *)str; + } + str++; + } + + return NULL; +} + +void *memmove(void *dst, const void *src, size_t n) +{ + void *ret = dst; + + if (dst <= src || (char *)dst >= ((char *)src + n)) { + /* Non-overlapping buffers; copy from lower addresses to higher + * addresses. + */ + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst + 1; + src = (char *)src + 1; + } + } else { + // Overlapping buffers; copy from higher addresses to lower addresses. + dst = (char *)dst + n - 1; + src = (char *)src + n - 1; + + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst - 1; + src = (char *)src - 1; + } + } + + return ret; +} + +void *memchr(const void *ptr, int ch, size_t n) +{ + while (n && (*(unsigned char *)ptr != (unsigned char)ch)) { + ptr = (unsigned char *)ptr + 1; + n--; + } + + return (n ? (void *)ptr : NULL); +} + +char *strlwr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)tolower(*p); + p++; + } + + return s; +} + +char *strupr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)toupper(*p); + p++; + } + + return s; +} + +char *strcat(char *dst, const char *src) +{ + char *cp = dst; + + while (*cp) { + cp++; + } + + while ((*cp++ = *src++) != '\0') {} + + return dst; +} + +char *strncat(char *s1, const char *s2, size_t n) +{ + char *start = s1; + + while (*s1++) {} + s1--; + + while (n--) { + if (!(*s1++ = *s2++)) + return start; + } + + *s1 = '\0'; + + return start; +} + +char *strrev(char *s) +{ + char *start = s; + char *left = s; + char ch; + + while (*s++) {} + s -= 2; + + while (left < s) { + ch = *left; + *left++ = *s; + *s-- = ch; + } + + return start; +} + +char *strtok_r(char *str, const char *delim, char **saveptr) +{ + char *s; + const char *ctrl = delim; + + char map[32]; + int n; + + // Clear delim map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in delimiter table. + do { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + } while (*ctrl++); + + /* Initialize s. If str is NULL, set s to the saved + * pointer (i.e., continue breaking tokens out of the str + * from the last strtok call). + */ + if (str) { + s = str; + } else { + s = *saveptr; + } + + /* Find beginning of token (skip over leading delimiters). Note that + * there is no token iff this loop sets s to point to the terminal + * null (*s == '\0'). + */ + while ((map[*s >> 3] & (1 << (*s & 7))) && *s) { + s++; + } + + str = s; + + /* Find the end of the token. If it is not the end of the str, + * put a null there. + */ + for (; *s; s++) { + if (map[*s >> 3] & (1 << (*s & 7))) { + *s++ = '\0'; + + break; + } + } + + // Update nexttoken. + *saveptr = s; + + // Determine if a token has been found. + if (str == (char *)s) { + return NULL; + } else { + return str; + } +} + +// Intrinsic functions. + +/* + * #pragma function(memset) + * #pragma function(memcmp) + * #pragma function(memcpy) + * #pragma function(strcpy) + * #pragma function(strlen) + * #pragma function(strcat) + * #pragma function(strcmp) + * #pragma function(strset) + */ + +void *memset(void *ptr, int value, size_t num) +{ + // Truncate c to 8 bits. + value = (value & 0xFF); + + char *dst = (char *)ptr; + + // Initialize the rest of the size. + while (num--) { + *dst++ = (char)value; + } + + return ptr; +} + +int memcmp(const void *dst, const void *src, size_t n) +{ + if (!n) { + return 0; + } + + while (--n && *(char *)dst == *(char *)src) { + dst = (char *)dst + 1; + src = (char *)src + 1; + } + + return *((unsigned char *)dst) - *((unsigned char *)src); +} + +void *memcpy(void *dst, const void *src, size_t num) +{ + char *_dst = dst; + const char *_src = src; + + while (num--) { + *_dst++ = *_src++; + } + + return dst; +} + +void *memccpy(void *dst, const void *src, int c, size_t n) +{ + while (n && (*((char *)(dst = (char *)dst + 1) - 1) = + *((char *)(src = (char *)src + 1) - 1)) != (char)c) { + n--; + } + + return n ? dst : NULL; +} + +char *strcpy(char *dst, const char *src) +{ + char *save = dst; + + while ((*dst++ = *src++) != '\0') {} + + return save; +} + +size_t strlen(const char *s) +{ + const char *eos; + + for (eos = s; *eos != 0; ++eos) {} + + long len = eos - s; + + return (len < 0) ? 0 : (size_t)len; +} + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; *sc != '\0' && count--; ++sc) {} + + long len = sc - s; + + return (len < 0) ? 0 : (size_t)len; +} + +int strcmp(const char *s1, const char *s2) +{ + int ret = 0; + const char *s1t = s1, *s2t = s2; + + for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) {} + + return (ret < 0) ? -1 : (ret > 0) ? 1 : + 0; +} + +char *strset(char *s, int c) +{ + char *start = s; + + while (*s) { + *s++ = (char)c; + } + + return start; +} + +char *strnset(char *s, int c, size_t n) +{ + while (n-- && *s) { + *s++ = (char)c; + } + + return s; +} + +char *strtok(char *str, const char *delim) +{ + const char *spanp; + int c, sc; + char *tok; + static char *last; + + if (str == NULL && (str = last) == NULL) { + return (NULL); + } + +cont: + c = *str++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) { + goto cont; + } + } + + if (c == 0) { + last = NULL; + + return (NULL); + } + tok = str - 1; + + for (;;) { + c = *str++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + str = NULL; + } else { + str[-1] = 0; + } + last = str; + + return (tok); + } + } while (sc != 0); + } +} + +char *trim(char *str) +{ + size_t len = 0; + char *frontp = str; + char *endp = NULL; + + if (str == NULL) { + return NULL; + } + if (str[0] == '\0') { + return str; + } + + len = strlen(str); + endp = str + len; + + /* Move the front and back pointers to address the first non-whitespace + * characters from each end. + */ + while (isspace((unsigned char)*frontp)) { + ++frontp; + } + if (endp != frontp) { + while (isspace((unsigned char)*(--endp)) && endp != frontp) {} + } + if (str + len - 1 != endp) { + *(endp + 1) = '\0'; + } else if (frontp != str && endp == frontp) { + *str = '\0'; + } + /* Shift the string so that it starts at str so that if it's dynamically + * allocated, we can still free it on the returned pointer. Note the reuse + * of endp to mean the front of the string buffer now. + */ + endp = str; + if (frontp != str) { + while (*frontp) { + *endp++ = *frontp++; + } + *endp = '\0'; + } + + return str; +} + +char *strdup(const char *s) +{ + size_t len = strlen(s) + 1; + char *new = kmalloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = kmalloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + if ((s = *stringp) == NULL) { + return (NULL); + } + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + s = NULL; + } else { + s[-1] = 0; + } + *stringp = s; + + return (tok); + } + } while (sc != 0); + } +} + +char *itoa(char *buffer, unsigned int num, unsigned int base) +{ + // int numval; + char *p, *pbase; + + p = pbase = buffer; + + if (base == 16) { + sprintf(buffer, "%0x", num); + } else { + if (num == 0) { + *p++ = '0'; + } + while (num != 0) { + *p++ = (char)('0' + (num % base)); + num = num / base; + } + *p-- = 0; + + while (p > pbase) { + char tmp; + tmp = *p; + *p = *pbase; + *pbase = tmp; + + p--; + pbase++; + } + } + return buffer; +} + +char *replace_char(char *str, char find, char replace) +{ + char *current_pos = strchr(str, find); + + while (current_pos) { + *current_pos = replace; + current_pos = strchr(current_pos, find); + } + + return str; +} + +void strmode(mode_t mode, char *p) +{ + // Usr. + *p++ = mode & S_IRUSR ? 'r' : '-'; + *p++ = mode & S_IWUSR ? 'w' : '-'; + *p++ = mode & S_IXUSR ? 'x' : '-'; + // Group. + *p++ = mode & S_IRGRP ? 'r' : '-'; + *p++ = mode & S_IWGRP ? 'w' : '-'; + *p++ = mode & S_IXGRP ? 'x' : '-'; + // Other. + *p++ = mode & S_IROTH ? 'r' : '-'; + *p++ = mode & S_IWOTH ? 'w' : '-'; + *p++ = mode & S_IXOTH ? 'x' : '-'; + // Will be a '+' if ACL's implemented. + *p++ = ' '; + *p = '\0'; +} diff --git a/mentos/src/klib/time.c b/mentos/src/klib/time.c new file mode 100644 index 0000000..8898a35 --- /dev/null +++ b/mentos/src/klib/time.c @@ -0,0 +1,115 @@ +/// MentOS, The Mentoring Operating system project +/// @file time.c +/// @brief Clock functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "debug.h" +#include "time.h" +#include "stdio.h" +#include "stddef.h" +#include "port_io.h" +#include "timer.h" +#include "rtc.h" + +static const char *str_weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" }; + +static const char *str_months[] = { "January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December" }; + +time_t sys_time(time_t *time) +{ + tm_t curr_time; + gettime(&curr_time); + // January and February are counted as months 13 and 14 of the previous year. + if (curr_time.tm_mon <= 2) { + curr_time.tm_mon += 12; + curr_time.tm_year -= 1; + } + time_t t; + // Convert years to days + t = (365 * curr_time.tm_year) + (curr_time.tm_year / 4) - (curr_time.tm_year / 100) + + (curr_time.tm_year / 400); + // Convert months to days + t += (30 * curr_time.tm_mon) + (3 * (curr_time.tm_mon + 1) / 5) + curr_time.tm_mday; + // Unix time starts on January 1st, 1970 + t -= 719561; + // Convert days to seconds + t *= 86400; + // Add hours, minutes and seconds + t += (3600 * curr_time.tm_hour) + (60 * curr_time.tm_min) + curr_time.tm_sec; + if (time) { + (*time) = t; + } + return t; +} + +time_t difftime(time_t time1, time_t time2) +{ + return time1 - time2; +} + +/// @brief Computes day of week +/// @param y Year +/// @param m Month of year (in range 1 to 12) +/// @param d Day of month (in range 1 to 31) +/// @return Day of week (in range 1 to 7) +static inline int day_of_week(unsigned int y, unsigned int m, unsigned int d) +{ + int h, j, k; + // January and February are counted as months 13 and 14 of the previous year + if (m <= 2) { + m += 12; + y -= 1; + } + // J is the century + j = (int)(y / 100); + // K the year of the century + k = (int)(y % 100); + // Compute H using Zeller's congruence + h = (int)(d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4)); + // Return the day of the week + return ((h + 5) % 7) + 1; +} + +tm_t *localtime(const time_t *time) +{ + static tm_t date; + unsigned int a, b, c, d, e, f; + time_t t = *time; + // Negative Unix time values are not supported + if (t < 1) { + t = 0; + } + //Retrieve hours, minutes and seconds + date.tm_sec = (int)(t % 60); + t /= 60; + date.tm_min = (int)(t % 60); + t /= 60; + date.tm_hour = (int)(t % 24); + t /= 24; + // Convert Unix time to date + a = (unsigned int)((4 * t + 102032) / 146097 + 15); + b = (unsigned int)(t + 2442113 + a - (a / 4)); + c = (20 * b - 2442) / 7305; + d = b - 365 * c - (c / 4); + e = d * 1000 / 30601; + f = d - e * 30 - e * 601 / 1000; + // January and February are counted as months 13 and 14 of the previous year + if (e <= 13) { + c -= 4716; + e -= 1; + } else { + c -= 4715; + e -= 13; + } + //Retrieve year, month and day + date.tm_year = (int)c; + date.tm_mon = (int)e; + date.tm_mday = (int)f; + // Calculate day of week. + date.tm_wday = day_of_week(date.tm_year, date.tm_mon, date.tm_mday); + return &date; +} \ No newline at end of file diff --git a/mentos/src/klib/vscanf.c b/mentos/src/klib/vscanf.c new file mode 100644 index 0000000..82bf75e --- /dev/null +++ b/mentos/src/klib/vscanf.c @@ -0,0 +1,105 @@ +/// MentOS, The Mentoring Operating system project +/// @file vscanf.c +/// @brief Reading formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "vfs.h" +#include "ctype.h" +#include "string.h" +#include "debug.h" +#include "stdio.h" + +static int vsscanf(const char *buf, const char *s, va_list ap) +{ + int count = 0, noassign = 0, width = 0, base = 0; + const char *tc; + char tmp[BUFSIZ]; + + while (*s && *buf) { + while (isspace(*s)) + ++s; + if (*s == '%') { + ++s; + for (; *s; ++s) { + if (strchr("dibouxcsefg%", *s)) + break; + if (*s == '*') + noassign = 1; + else if (isdigit(*s)) { + for (tc = s; isdigit(*s); ++s) + ; + strncpy(tmp, tc, s - tc); + tmp[s - tc] = '\0'; + width = strtol(tmp, NULL, 10); + --s; + } + } + if (*s == 's') { + while (isspace(*buf)) + ++buf; + if (!width) + width = strcspn(buf, " \t\n\r\f\v"); + if (!noassign) { + char *string = va_arg(ap, char *); + strncpy(string, buf, width); + string[width] = '\0'; + } + buf += width; + } else if (*s == 'c') { + while (isspace(*buf)) + ++buf; + if (!width) + width = 1; + if (!noassign) { + strncpy(va_arg(ap, char *), buf, width); + } + buf += width; + } else if (strchr("duxob", *s)) { + while (isspace(*buf)) + ++buf; + if (*s == 'd' || *s == 'u') + base = 10; + else if (*s == 'x') + base = 16; + else if (*s == 'o') + base = 8; + else if (*s == 'b') + base = 2; + if (!width) { + if (isspace(*(s + 1)) || *(s + 1) == 0) + width = strcspn(buf, " \t\n\r\f\v"); + else + width = strchr(buf, *(s + 1)) - buf; + } + strncpy(tmp, buf, width); + tmp[width] = '\0'; + buf += width; + if (!noassign) + *va_arg(ap, unsigned int *) = strtol(tmp, NULL, base); + } + if (!noassign) + ++count; + width = noassign = 0; + ++s; + } else { + while (isspace(*buf)) + ++buf; + if (*s != *buf) + break; + else + ++s, ++buf; + } + } + return (count); +} + +int sscanf(const char *buf, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + int count = vsscanf(buf, fmt, ap); + va_end(ap); + return count; +} diff --git a/mentos/src/klib/vsprintf.c b/mentos/src/klib/vsprintf.c new file mode 100644 index 0000000..47a123b --- /dev/null +++ b/mentos/src/klib/vsprintf.c @@ -0,0 +1,675 @@ +/// MentOS, The Mentoring Operating system project +/// @file vsprintf.c +/// @brief Print formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "math.h" +#include "ctype.h" +#include "string.h" +#include "stdarg.h" +#include "stdbool.h" +#include "stdint.h" +#include "stdio.h" +#include "video.h" +#include "fcvt.h" + +/// Size of the buffer used to call cvt functions. +#define CVTBUFSIZE 500 + +#define FLAGS_ZEROPAD (1U << 0U) ///< Fill zeros before the number. +#define FLAGS_LEFT (1U << 1U) ///< Left align the value. +#define FLAGS_PLUS (1U << 2U) ///< Print the plus sign. +#define FLAGS_SPACE (1U << 3U) ///< If positive add a space instead of the plus sign. +#define FLAGS_HASH (1U << 4U) ///< Preceed with 0x or 0X, %x or %X respectively. +#define FLAGS_UPPERCASE (1U << 5U) ///< Print uppercase. +#define FLAGS_SIGN (1U << 6U) ///< Print the sign. + +/// The list of digits. +static char *_digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/// The list of uppercase digits. +static char *_upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/// @brief Returns the index of the first non-integer character. +static inline int skip_atoi(const char **s) +{ + int i = 0; + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags) +{ + char c, tmp[66] = { 0 }; + char *dig = _digits; + + if (flags & FLAGS_UPPERCASE) { + dig = _upper_digits; + } + if (flags & FLAGS_LEFT) { + flags &= ~FLAGS_ZEROPAD; + } + if (base < 2 || base > 36) { + return 0; + } + + c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; + + // -------------------------------- + // Set the sign. + // -------------------------------- + char sign = 0; + if (flags & FLAGS_SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (flags & FLAGS_PLUS) { + sign = '+'; + size--; + } else if (flags & FLAGS_SPACE) { + sign = ' '; + size--; + } + } + // Sice I've removed the sign (if negative), i can transform it to unsigned. + uint32_t uns_num = (uint32_t)num; + if (flags & FLAGS_HASH) { + if (base == 16) { + size -= 2; + } else if (base == 8) { + size--; + } + } + + int32_t i = 0; + if (uns_num == 0) { + tmp[i++] = '0'; + } else { + while (uns_num != 0) { + tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; + uns_num = ((unsigned long)uns_num) / (unsigned)base; + } + } + if (i > precision) { + precision = i; + } + size -= precision; + if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { + while (size-- > 0) + *str++ = ' '; + } + if (sign) { + *str++ = sign; + } + if (flags & FLAGS_HASH) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = _digits[33]; + } + } + if (!(flags & FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + while (i < precision--) { + *str++ = '0'; + } + while (i-- > 0) { + *str++ = tmp[i]; + } + while (size-- > 0) { + *str++ = ' '; + } + return str; +} + +static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + char *dig = _digits; + int i, len; + + if (flags & FLAGS_UPPERCASE) { + dig = _upper_digits; + } + + len = 0; + for (i = 0; i < 6; i++) { + if (i != 0) { + tmp[len++] = ':'; + } + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!(flags & FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) { + if (i != 0) { + tmp[len++] = '.'; + } + n = addr[i]; + + if (n == 0) { + tmp[len++] = _digits[0]; + } else { + if (n >= 100) { + tmp[len++] = _digits[n / 100]; + n = n % 100; + tmp[len++] = _digits[n / 10]; + n = n % 10; + } else if (n >= 10) { + tmp[len++] = _digits[n / 10]; + n = n % 10; + } + + tmp[len++] = _digits[n]; + } + } + + if (!(flags & FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static void cfltcvt(double value, char *buffer, char fmt, int precision) +{ + int decpt, sign, exp, pos; + char cvtbuf[CVTBUFSIZE]; + char *digits = cvtbuf; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') { + ecvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) { + fmt = 'e'; + precision -= 1; + } else { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') { + ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE); + + if (sign) { + *buffer++ = '-'; + } + *buffer++ = *digits; + if (precision > 0) { + *buffer++ = '.'; + } + memcpy(buffer, digits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) { + if (value == 0.0) { + exp = 0; + } else { + exp = -1; + } + } else { + exp = decpt - 1; + } + + if (exp < 0) { + *buffer++ = '-'; + exp = -exp; + } else { + *buffer++ = '+'; + } + + buffer[2] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[1] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[0] = (char)((exp % 10) + '0'); + buffer += 3; + } else if (fmt == 'f') { + fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + if (sign) { + *buffer++ = '-'; + } + if (*digits) { + if (decpt <= 0) { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) { + *buffer++ = '0'; + } + while (*digits) { + *buffer++ = *digits++; + } + } else { + pos = 0; + while (*digits) { + if (pos++ == decpt) { + *buffer++ = '.'; + } + *buffer++ = *digits++; + } + } + } else { + *buffer++ = '0'; + if (precision > 0) { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) { + *buffer++ = '0'; + } + } + } + } + + *buffer = '\0'; +} + +static void forcdecpt(char *buffer) +{ + while (*buffer) { + if (*buffer == '.') { + return; + } + if (*buffer == 'e' || *buffer == 'E') { + break; + } + + buffer++; + } + + if (*buffer) { + long n = (long)strlen(buffer); + while (n > 0) { + buffer[n + 1] = buffer[n]; + n--; + } + *buffer = '.'; + } else { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) +{ + char *stop; + + while (*buffer && *buffer != '.') { + buffer++; + } + + if (*buffer++) { + while (*buffer && *buffer != 'e' && *buffer != 'E') { + buffer++; + } + stop = buffer--; + while (*buffer == '0') { + buffer--; + } + if (*buffer == '.') { + buffer--; + } + while ((*++buffer = *stop++)) {} + } +} + +static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags) +{ + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding. + if (flags & FLAGS_LEFT) + flags &= ~FLAGS_ZEROPAD; + + // Determine padding and sign char. + c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; + sign = 0; + if (flags & FLAGS_SIGN) { + if (num < 0.0) { + sign = '-'; + num = -num; + size--; + } else if (flags & FLAGS_PLUS) { + sign = '+'; + size--; + } else if (flags & FLAGS_SPACE) { + sign = ' '; + size--; + } + } + + // Compute the precision value. + if (precision < 0) { + // Default precision: 6. + precision = 6; + } else if (precision == 0 && fmt == 'g') { + // ANSI specified. + precision = 1; + } + + // Convert floating point number to text. + cfltcvt(num, tmp, fmt, precision); + + // '#' and precision == 0 means force a decimal point. + if ((flags & FLAGS_HASH) && precision == 0) { + forcdecpt(tmp); + } + + // 'g' format means crop zero unless '#' given. + if (fmt == 'g' && !(flags & FLAGS_HASH)) { + cropzeros(tmp); + } + + n = strlen(tmp); + + // Output number with alignment and padding. + size -= n; + if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { + while (size-- > 0) { + *str++ = ' '; + } + } + + if (sign) { + *str++ = sign; + } + + if (!(flags & FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + + for (i = 0; i < n; i++) { + *str++ = tmp[i]; + } + + while (size-- > 0) { + *str++ = ' '; + } + + return str; +} + +int vsprintf(char *str, const char *fmt, va_list args) +{ + int base; + char *tmp; + char *s; + + // Flags to number(). + unsigned flags; + + // 'h', 'l', or 'L' for integer fields. + char qualifier; + + for (tmp = str; *fmt; fmt++) { + if (*fmt != '%') { + *tmp++ = *fmt; + + continue; + } + + // Process flags- + flags = 0; + repeat: + // This also skips first '%'. + fmt++; + switch (*fmt) { + case '-': + flags |= FLAGS_LEFT; + goto repeat; + case '+': + flags |= FLAGS_PLUS; + goto repeat; + case ' ': + flags |= FLAGS_SPACE; + goto repeat; + case '#': + flags |= FLAGS_HASH; + goto repeat; + case '0': + flags |= FLAGS_ZEROPAD; + goto repeat; + } + + // Get the width of the output field. + int32_t field_width; + field_width = -1; + + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int32_t); + if (field_width < 0) { + field_width = -field_width; + flags |= FLAGS_LEFT; + } + } + + /* Get the precision, thus the minimum number of digits for + * integers; max number of chars for from string. + */ + int32_t precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + // Get the conversion qualifier. + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base. + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & FLAGS_LEFT)) { + while (--field_width > 0) { + *tmp++ = ' '; + } + } + *tmp++ = va_arg(args, char); + while (--field_width > 0) { + *tmp++ = ' '; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) { + s = ""; + } + + int32_t len = (int32_t)strnlen(s, (uint32_t)precision); + if (!(flags & FLAGS_LEFT)) { + while (len < field_width--) { + *tmp++ = ' '; + } + } + + int32_t it; + for (it = 0; it < len; ++it) { + *tmp++ = *s++; + } + while (len < field_width--) { + *tmp++ = ' '; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= FLAGS_ZEROPAD; + } + tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + continue; + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (tmp - str); + } else { + int *ip = va_arg(args, int *); + *ip = (tmp - str); + } + continue; + case 'A': + flags |= FLAGS_UPPERCASE; + break; + case 'a': + if (qualifier == 'l') + tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + else + tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + // Integer number formats - set up the flags and "break". + case 'o': + base = 8; + break; + + case 'X': + flags |= FLAGS_UPPERCASE; + break; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= FLAGS_SIGN; + + case 'u': + break; + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, flags | FLAGS_SIGN); + continue; + default: + if (*fmt != '%') + *tmp++ = '%'; + if (*fmt) + *tmp++ = *fmt; + else + --fmt; + continue; + } + + if (flags & FLAGS_SIGN) { + long num; + if (qualifier == 'l') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = va_arg(args, short); + } else { + num = va_arg(args, int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } else { + unsigned long num; + if (qualifier == 'l') { + num = va_arg(args, unsigned long); + } else if (qualifier == 'h') { + num = va_arg(args, unsigned short); + } else { + num = va_arg(args, unsigned int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } + } + + *tmp = '\0'; + return tmp - str; +} + +int printf(const char *format, ...) +{ + char buffer[4096]; + va_list ap; + int len; + // Start variabile argument's list. + va_start(ap, format); + len = vsprintf(buffer, format, ap); + va_end(ap); + video_puts(buffer); + return len; +} + +int sprintf(char *str, const char *fmt, ...) +{ + va_list args; + int len; + + va_start(args, fmt); + len = vsprintf(str, fmt, args); + va_end(args); + + return len; +} diff --git a/mentos/src/libc/assert.c b/mentos/src/libc/assert.c deleted file mode 100644 index 6e49f96..0000000 --- a/mentos/src/libc/assert.c +++ /dev/null @@ -1,22 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file assert.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "assert.h" -#include "stdio.h" -#include "panic.h" - -void __assert_fail(const char *assertion, const char *file, unsigned int line, - const char *function) -{ - char message[1024]; - sprintf(message, - "FILE: %s\n" - "LINE: %d\n" - "FUNC: %s\n\n" - "Assertion `%s` failed.\n", - file, line, (function ? function : "NO_FUN"), assertion); - kernel_panic(message); -} diff --git a/mentos/src/libc/bitset.c b/mentos/src/libc/bitset.c deleted file mode 100644 index b9f3112..0000000 --- a/mentos/src/libc/bitset.c +++ /dev/null @@ -1,120 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitset.c -/// @brief Bitset data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "bitset.h" -#include "math.h" -#include "string.h" -#include "stdlib.h" -#include "assert.h" - -/// @brief -/// @param bit -/// @param index -/// @param mask -static void get_index_and_mask(size_t bit, size_t *index, size_t *mask) -{ - assert(index); - - assert(mask); - - (*index) = bit >> 3; - - bit = bit - (*index) * 8; - - size_t offset = bit & 7; - - (*mask) = 1UL << offset; -} - -/// @brief -/// @param set -/// @param size - -/* - * static void bitset_resize(bitset_t *set, size_t size) - * { - * assert(set && "Received NULL set."); - * - * if (set->size >= size) - * { - * return; - * } - * set->data = realloc(set->data, size); - * - * memset(set->data + set->size, 0, size - set->size); - * - * set->size = size; - * - * } - */ - -void bitset_init(bitset_t *set, size_t size) -{ - assert(set && "Received NULL set."); - - set->size = ceil(size, 8); - - set->data = calloc(set->size, 1); -} - -void bitset_free(bitset_t *set) -{ - assert(set && "Received NULL set."); - - free(set->data); -} - -/* - * void bitset_set(bitset_t *set, size_t bit) - * { - * assert(set && "Received NULL set."); - * size_t index, mask; - * - * get_index_and_mask(bit, &index, &mask); - * - * if (set->size <= index) - * { - * bitset_resize(set, set->size << 1); - * } - * - * set->data[index] |= mask; - * } - */ - -void bitset_clear(bitset_t *set, size_t bit) -{ - assert(set && "Received NULL set."); - - size_t index, mask; - - get_index_and_mask(bit, &index, &mask); - - set->data[index] &= ~mask; -} - -bool_t bitset_test(bitset_t *set, size_t bit) -{ - assert(set && "Received NULL set."); - - size_t index, mask; - - get_index_and_mask(bit, &index, &mask); - - return (mask & set->data[index]) != 0; -} - -signed long bitset_find_first_unset_bit(bitset_t *set) -{ - assert(set && "Received NULL set."); - - for (size_t i = 0; i < (set->size * 8); i++) { - if (!bitset_test(set, i)) { - return (signed long)i; - } - } - - return -1; -} diff --git a/mentos/src/libc/ctype.c b/mentos/src/libc/ctype.c deleted file mode 100644 index e1e6a50..0000000 --- a/mentos/src/libc/ctype.c +++ /dev/null @@ -1,63 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ctype.c -/// @brief Functions related to character handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "ctype.h" - -/// Distance from a uppercase character to the correspondent lowercase in ASCII. -#define OFFSET 32 - -int isdigit(int c) -{ - return (c >= 48 && c <= 57); -} - -int isalpha(int c) -{ - return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)); -} - -int isalnum(int c) -{ - return (isalpha(c) || isdigit(c)); -} - -int isxdigit(int c) -{ - return (isdigit(c) || (c >= 65 && c <= 70)); -} - -int islower(int c) -{ - return (c >= 97 && c <= 122); -} - -int isupper(int c) -{ - return (c >= 65 && c <= 90); -} - -int tolower(int c) -{ - if (isalpha(c) == 0 || islower(c)) { - return c; - } - - return c + OFFSET; -} - -int toupper(int c) -{ - if (isalpha(c) == 0 || isupper(c)) { - return c; - } - - return c - OFFSET; -} - -int isspace(int c) -{ - return (c == ' '); -} diff --git a/mentos/src/libc/fcvt.c b/mentos/src/libc/fcvt.c deleted file mode 100644 index cb5eb67..0000000 --- a/mentos/src/libc/fcvt.c +++ /dev/null @@ -1,114 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcvt.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "fcvt.h" -#include - -/* - * cvt.c - IEEE floating point formatting routines for FreeBSD - * from GNU libc-4.6.27 - */ - -#define CVTBUFSIZE 500 - -static char *cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, - int eflag) -{ - int r2; - double fi, fj; - char *p, *p1; - - if (ndigits < 0) { - ndigits = 0; - } - - if (ndigits >= CVTBUFSIZE - 1) { - ndigits = CVTBUFSIZE - 2; - } - - r2 = 0; - *sign = 0; - p = &buf[0]; - - if (arg < 0) { - *sign = 1; - arg = -arg; - } - - arg = modf(arg, &fi); - p1 = &buf[CVTBUFSIZE]; - - if (fi != 0) { - p1 = &buf[CVTBUFSIZE]; - while (fi != 0) { - fj = modf(fi / 10, &fi); - *--p1 = (int)((fj + .03) * 10) + '0'; - r2++; - } - while (p1 < &buf[CVTBUFSIZE]) { - *p++ = *p1++; - } - } else if (arg > 0) { - while ((fj = arg * 10) < 1) { - arg = fj; - r2--; - } - } - - p1 = &buf[ndigits]; - if (eflag == 0) { - p1 += r2; - } - - *decpt = r2; - if (p1 < &buf[0]) { - buf[0] = '\0'; - - return buf; - } - - while (p <= p1 && p < &buf[CVTBUFSIZE]) { - arg *= 10; - arg = modf(arg, &fj); - *p++ = (int)fj + '0'; - } - - if (p1 >= &buf[CVTBUFSIZE]) { - buf[CVTBUFSIZE - 1] = '\0'; - - return buf; - } - - p = p1; - *p1 += 5; - while (*p1 > '9') { - *p1 = '0'; - if (p1 > buf) { - ++*--p1; - } else { - *p1 = '1'; - (*decpt)++; - if (eflag == 0) { - if (p > buf) - *p = '0'; - p++; - } - } - } - *p = '\0'; - - return buf; -} - -char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf) -{ - return cvt(arg, ndigits, decpt, sign, buf, 1); -} - -char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf) -{ - return cvt(arg, ndigits, decpt, sign, buf, 0); -} diff --git a/mentos/src/libc/hashmap.c b/mentos/src/libc/hashmap.c deleted file mode 100644 index c553e63..0000000 --- a/mentos/src/libc/hashmap.c +++ /dev/null @@ -1,267 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file hashmap.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "hashmap.h" -#include "string.h" -#include "stdlib.h" - -struct hashmap_entry_t { - char *key; - void *value; - struct hashmap_entry_t *next; -}; - -struct hashmap_t { - hashmap_hash_t hash_func; - hashmap_comp_t hash_comp; - hashmap_dupe_t hash_key_dup; - hashmap_free_t hash_key_free; - hashmap_free_t hash_val_free; - size_t size; - hashmap_entry_t **entries; -}; - -size_t hashmap_string_hash(void *_key) -{ - size_t hash = 0; - char *key = (char *)_key; - int c; - // This is the so-called "sdbm" hash. It comes from a piece of public - // domain code from a clone of ndbm. - while ((c = *key++)) { - hash = c + (hash << 6) + (hash << 16) - hash; - } - return hash; -} - -bool_t hashmap_string_comp(void *a, void *b) -{ - return !strcmp(a, b); -} - -void *hashmap_string_dupe(void *key) -{ - return strdup(key); -} - -static size_t hashmap_int_hash(void *key) -{ - return (size_t)key; -} - -static bool_t hashmap_int_comp(void *a, void *b) -{ - return (int)a == (int)b; -} - -static void *hashmap_int_dupe(void *key) -{ - return key; -} - -static void hashmap_int_free(void *ptr) -{ - (void)ptr; -} - -hashmap_t *hashmap_create(size_t size) -{ - hashmap_t *map = malloc(sizeof(hashmap_t)); - - map->hash_func = &hashmap_string_hash; - map->hash_comp = &hashmap_string_comp; - map->hash_key_dup = &hashmap_string_dupe; - map->hash_key_free = &free; - map->hash_val_free = &free; - - map->size = size; - map->entries = malloc(sizeof(hashmap_entry_t *) * size); - memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); - - return map; -} - -hashmap_t *hashmap_create_int(size_t size) -{ - hashmap_t *map = malloc(sizeof(hashmap_t)); - - map->hash_func = &hashmap_int_hash; - map->hash_comp = &hashmap_int_comp; - map->hash_key_dup = &hashmap_int_dupe; - map->hash_key_free = &hashmap_int_free; - map->hash_val_free = &free; - - map->size = size; - map->entries = malloc(sizeof(hashmap_entry_t *) * size); - memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); - - return map; -} - -void hashmap_free(hashmap_t *map) -{ - for (size_t i = 0; i < map->size; ++i) { - hashmap_entry_t *x = map->entries[i], *p; - while (x) { - p = x; - x = x->next; - map->hash_key_free(p->key); - map->hash_val_free(p); - } - } - - free(map->entries); -} - -void *hashmap_set(hashmap_t *map, void *key, void *value) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) { - hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - map->entries[hash] = e; - - return NULL; - } - - hashmap_entry_t *p = NULL; - - do { - if (map->hash_comp(x->key, key)) { - void *out = x->value; - x->value = value; - - return out; - } - p = x; - x = x->next; - } while (x); - - hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - p->next = e; - - return NULL; -} - -void *hashmap_get(hashmap_t *map, void *key) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) { - return NULL; - } - do { - if (map->hash_comp(x->key, key)) { - return x->value; - } - x = x->next; - } while (x); - - return NULL; -} - -void *hashmap_remove(hashmap_t *map, void *key) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) { - return NULL; - } - if (map->hash_comp(x->key, key)) { - void *out = x->value; - map->entries[hash] = x->next; - map->hash_key_free(x->key); - map->hash_val_free(x); - - return out; - } - - hashmap_entry_t *p = x; - x = x->next; - do { - if (map->hash_comp(x->key, key)) { - void *out = x->value; - p->next = x->next; - map->hash_key_free(x->key); - map->hash_val_free(x); - - return out; - } - p = x; - x = x->next; - } while (x); - - return NULL; -} - -bool_t hashmap_is_empty(hashmap_t *map) -{ - for (size_t i = 0; i < map->size; ++i) { - if (map->entries[i]) { - return false; - } - } - - return true; -} - -bool_t hashmap_has(hashmap_t *map, void *key) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) { - return false; - } - do { - if (map->hash_comp(x->key, key)) { - return true; - } - x = x->next; - } while (x); - - return false; -} - -list_t *hashmap_keys(hashmap_t *map) -{ - list_t *l = list_create(); - - for (size_t i = 0; i < map->size; ++i) { - hashmap_entry_t *x = map->entries[i]; - while (x) { - list_insert_back(l, x->key); - x = x->next; - } - } - - return l; -} - -list_t *hashmap_values(hashmap_t *map) -{ - list_t *l = list_create(); - - for (size_t i = 0; i < map->size; ++i) { - hashmap_entry_t *x = map->entries[i]; - - while (x) { - list_insert_back(l, x->value); - x = x->next; - } - } - - return l; -} diff --git a/mentos/src/libc/libgen.c b/mentos/src/libc/libgen.c deleted file mode 100644 index 47f2bab..0000000 --- a/mentos/src/libc/libgen.c +++ /dev/null @@ -1,74 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file libgen.c -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "libgen.h" -#include "string.h" -#include "initrd.h" -#include "limits.h" - -int parse_path(char *out, char **cur, char sep, size_t max) -{ - if (**cur == '\0') { - return 0; - } - - *out++ = **cur; - ++*cur; - --max; - - while ((max > 0) && (**cur != '\0') && (**cur != sep)) { - *out++ = **cur; - ++*cur; - --max; - } - - *out = '\0'; - - return 1; -} - -char *dirname(const char *path) -{ - static char s[PATH_MAX]; - - static char dot[2] = "."; - - // Check the input path. - if (path == NULL) - return dot; - - // Copy the path to the support string. - strcpy(s, path); - - // Get the last occurrence of '/'. - char *last_slash = strrchr(s, '/'); - - if (last_slash == s) { - // If the slash is acutally the first character of the string, move the - // pointer to the last slash after it. - ++last_slash; - } else if ((last_slash != NULL) && (last_slash[1] == '\0')) { - // If the slash is the last character, we need to search before it. - last_slash = memchr(s, '/', last_slash - s); - } - - if (last_slash != NULL) { - // If we have found it, close the string. - last_slash[0] = '\0'; - } else { - // Otherwise, return '.'. - return dot; - } - - return s; -} - -char *basename(const char *path) -{ - char *p = strrchr(path, '/'); - - return p ? p + 1 : (char *)path; -} diff --git a/mentos/src/libc/list.c b/mentos/src/libc/list.c deleted file mode 100644 index 7f50731..0000000 --- a/mentos/src/libc/list.c +++ /dev/null @@ -1,344 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file list.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "list.h" -#include "assert.h" -#include "stdlib.h" - -list_t *list_create() -{ - list_t *list = calloc(sizeof(list_t), 1); - - return list; -} - -size_t list_size(list_t *list) -{ - assert(list && "List is null."); - - return list->size; -} - -bool_t list_empty(list_t *list) -{ - assert(list && "List is null."); - - if (list->size == 0) { - return true; - } - - return false; -} - -listnode_t *list_insert_front(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - // Create a new node. - listnode_t *node = calloc(sizeof(listnode_t), 1); - - list->head->prev = node; - node->next = list->head; - node->value = value; - - // If it's the first element, then it's both head and tail - if (!list->head) { - list->tail = node; - } - - list->head = node; - list->size++; - - return node; -} - -listnode_t *list_insert_back(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - // Create a new node. - listnode_t *node = calloc(sizeof(listnode_t), 1); - node->prev = list->tail; - - if (list->tail) { - list->tail->next = node; - } - node->value = value; - - if (!list->head) { - list->head = node; - } - - list->tail = node; - list->size++; - - return node; -} - -void list_insert_node_back(list_t *list, listnode_t *node) -{ - assert(list && "List is null."); - assert(node && "Node is null."); - - node->prev = list->tail; - if (list->tail) { - list->tail->next = node; - } - - if (!list->head) { - list->head = node; - } - - list->tail = node; - list->size++; -} - -void *list_remove_node(list_t *list, listnode_t *node) -{ - assert(list && "List is null."); - assert(node && "Node is null."); - - if (list->head == node) { - return list_remove_front(list); - } else if (list->tail == node) { - return list_remove_back(list); - } - - void *value = node->value; - node->next->prev = node->prev; - node->prev->next = node->next; - list->size--; - free(node); - - return value; -} - -void *list_remove_front(list_t *list) -{ - assert(list && "List is null."); - - if (list->head == NULL) { - return NULL; - } - - listnode_t *node = list->head; - void *value = node->value; - list->head = node->next; - - if (list->head) { - list->head->prev = NULL; - } - free(node); - list->size--; - - return value; -} - -void *list_remove_back(list_t *list) -{ - assert(list && "List is null."); - - if (list->head == NULL) { - return NULL; - } - - listnode_t *node = list->tail; - void *value = node->value; - list->tail = node->prev; - - if (list->tail) { - list->tail->next = NULL; - } - free(node); - list->size--; - - return value; -} - -listnode_t *list_find(list_t *list, void *value) -{ - listnode_foreach(listnode, list) - { - if (listnode->value == value) { - return listnode; - } - } - - return NULL; -} - -void list_push(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - list_insert_back(list, value); -} - -listnode_t *list_pop_back(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - listnode_t *node = list->tail; - list->tail = node->prev; - - if (list->tail) { - list->tail->next = NULL; - } - - list->size--; - - return node; -} - -listnode_t *list_pop_front(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - listnode_t *node = list->head; - list->head = list->head->next; - list->size--; - - return node; -} - -void list_enqueue(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - list_insert_front(list, value); -} - -listnode_t *list_dequeue(list_t *list) -{ - assert(list && "List is null."); - - return list_pop_back(list); -} - -void *list_peek_front(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - return list->head->value; -} - -void *list_peek_back(list_t *list) -{ - assert(list && "List is null."); - - if (!list->tail) { - return NULL; - } - - return list->tail->value; -} - -int list_contain(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - int idx = 0; - listnode_foreach(listnode, list) - { - if (listnode->value == value) { - return idx; - } - ++idx; - } - - return -1; -} - -listnode_t *list_get_node_by_index(list_t *list, size_t index) -{ - assert(list && "List is null."); - - if (index >= list_size(list)) { - return NULL; - } - - size_t curr = 0; - listnode_foreach(listnode, list) - { - if (index == curr) { - return listnode; - } - curr++; - } - return NULL; -} - -void *list_remove_by_index(list_t *list, size_t index) -{ - assert(list && "List is null."); - - listnode_t *node = list_get_node_by_index(list, index); - if (node != NULL) { - return list_remove_node(list, node); - } - - return NULL; -} - -void list_destroy(list_t *list) -{ - assert(list && "List is null."); - - // Free each node's value and the node itself. - listnode_t *node = list->head; - while (node != NULL) { - listnode_t *save = node; - node = node->next; - free(save); - } - - // Free the list. - free(list); -} - -void listnode_destroy(listnode_t *node) -{ - assert(node && "Node is null."); - - free(node); -} - -void list_merge(list_t *target, list_t *source) -{ - assert(target && "Target list is null."); - assert(source && "Source list is null."); - - // Destructively merges source into target. - if (target->tail) { - target->tail->next = source->head; - } else { - target->head = source->head; - } - - if (source->tail) { - target->tail = source->tail; - } - - target->size += source->size; - free(source); -} diff --git a/mentos/src/libc/math.c b/mentos/src/libc/math.c deleted file mode 100644 index 19ca124..0000000 --- a/mentos/src/libc/math.c +++ /dev/null @@ -1,150 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file math.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "math.h" -#include - -double round(double x) -{ - double out; - __asm__ __volatile__("fldln2; fldl %1; frndint" : "=t"(out) : "m"(x)); - return out; -} - -double floor(double x) -{ - if (x > -1.0 && x < 1.0) { - if (x >= 0) - return 0.0; - return -1.0; - } - int i = (int)x; - if (x < 0) - return (double)(i - 1); - return (double)i; -} - -double pow(double x, double y) -{ - double out; - __asm__ __volatile__("fyl2x;" - "fld %%st;" - "frndint;" - "fsub %%st,%%st(1);" - "fxch;" - "fchs;" - "f2xm1;" - "fld1;" - "faddp;" - "fxch;" - "fld1;" - "fscale;" - "fstp %%st(1);" - "fmulp;" - : "=t"(out) - : "0"(x), "u"(y) - : "st(1)"); - return out; -} - -long find_nearest_pow_greater(double base, double value) -{ - if (base <= 1) - return -1; - long pow_value = 0; - while (pow(base, pow_value) < value) - pow_value++; - return pow_value; -} - -double exp(double x) -{ - return pow(M_E, x); -} - -double fabs(double x) -{ - double out; - __asm__ __volatile__("fldln2; fldl %1; fabs" : "=t"(out) : "m"(x)); - return out; -} - -double sqrt(double x) -{ - double out; - __asm__ __volatile__("fldln2; fldl %1; fsqrt" : "=t"(out) : "m"(x)); - return out; -} - -int isinf(double x) -{ - union { - unsigned long long u; - double f; - } ieee754; - ieee754.f = x; - return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U && - ((unsigned)ieee754.u == 0); -} - -int isnan(double x) -{ - union { - unsigned long long u; - double f; - } ieee754; - ieee754.f = x; - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + - ((unsigned)ieee754.u != 0) > - 0x7ff00000; -} - -double log10(double x) -{ - return ln(x) / ln(10); -} - -double ln(double x) -{ - double out; - __asm__ __volatile__("fldln2; fldl %1; fyl2x" : "=t"(out) : "m"(x)); - return out; -} - -double logx(double x, double y) -{ - // Base may not equal 1 or be negative. - if (y == 1.f || y < 0.f || ln(y) == 0.f) - return 0.f; - return ln(x) / ln(y); -} - -#define MAXPOWTWO 4.503599627370496000E+15 - -double modf(double x, double *intpart) -{ - register double absvalue; - if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { - // It must be an integer. - (*intpart) = x; - } else { - // Shift fraction off right. - (*intpart) = absvalue + MAXPOWTWO; - // Shift back without fraction. - (*intpart) -= MAXPOWTWO; - - // Above arithmetic might round. - while ((*intpart) > absvalue) { - // Test again just to be sure. - (*intpart) -= 1.0; - } - if (x < 0.0) { - (*intpart) = -(*intpart); - } - } - // Signed fractional part. - return (x - (*intpart)); -} diff --git a/mentos/src/libc/mutex.c b/mentos/src/libc/mutex.c deleted file mode 100644 index 59a5cc6..0000000 --- a/mentos/src/libc/mutex.c +++ /dev/null @@ -1,38 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file mutex.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "mutex.h" -#include "debug.h" - -void mutex_lock(mutex_t *mutex, uint32_t owner) -{ - dbg_print("[%d] Trying to lock mutex...\n", owner); - int failure = 1; - - while (mutex->state == 0 || failure || mutex->owner != owner) { - failure = 1; - if (mutex->state == 0) { - asm("movl $0x01,%%eax\n\t" // move 1 to eax - "xchg %%eax,%0\n\t" // try to set the lock bit - "mov %%eax,%1\n\t" // export our result to a test var - : "=m"(mutex->state), "=r"(failure) - : "m"(mutex->state) - : "%eax"); - } - - if (failure == 0) { - dbg_print("[%d] Acquired mutex...\n", owner); - mutex->owner = - owner; //test to see if we got the lock bit - } - } -} - -void mutex_unlock(mutex_t *mutex) -{ - dbg_print("[%d] Unlock mutex...\n", mutex->owner); - mutex->state = 0; -} diff --git a/mentos/src/libc/ordered_array.c b/mentos/src/libc/ordered_array.c deleted file mode 100644 index c9c3af9..0000000 --- a/mentos/src/libc/ordered_array.c +++ /dev/null @@ -1,90 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ordered_array.c -/// @brief Interface for creating, inserting and deleting from ordered arrays. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "ordered_array.h" -#include "string.h" -#include "assert.h" -#include "stdlib.h" - -int8_t standard_lessthan_predicate(array_type_t a, array_type_t b) -{ - return (a < b); -} - -ordered_array_t create_ordered_array(uint32_t max_size, - lessthan_predicate_t less_than) -{ - ordered_array_t to_ret; - to_ret.array = malloc(max_size * sizeof(array_type_t)); - memset(to_ret.array, 0, max_size * sizeof(array_type_t)); - to_ret.size = 0; - to_ret.max_size = max_size; - to_ret.less_than = less_than; - - return to_ret; -} - -ordered_array_t place_ordered_array(void *addr, uint32_t max_size, - lessthan_predicate_t less_than) -{ - ordered_array_t to_ret; - to_ret.array = (array_type_t *)addr; - memset(to_ret.array, 0, max_size * sizeof(array_type_t)); - to_ret.size = 0; - to_ret.max_size = max_size; - to_ret.less_than = less_than; - - return to_ret; -} - -void destroy_ordered_array(ordered_array_t *array) -{ - free(array->array); -} - -void insert_ordered_array(array_type_t item, ordered_array_t *array) -{ - assert(array->less_than); - - uint32_t iterator = 0; - while (iterator < array->size && - array->less_than(array->array[iterator], item)) { - iterator++; - } - - // Just add at the end of the array. - if (iterator == array->size) { - array->array[array->size++] = item; - } else { - array_type_t tmp = array->array[iterator]; - array->array[iterator] = item; - - while (iterator < array->size) { - iterator++; - array_type_t tmp2 = array->array[iterator]; - array->array[iterator] = tmp; - tmp = tmp2; - } - array->size++; - } -} - -array_type_t lookup_ordered_array(uint32_t i, ordered_array_t *array) -{ - assert(i < array->size); - - return array->array[i]; -} - -void remove_ordered_array(uint32_t i, ordered_array_t *array) -{ - while (i < array->size) { - array->array[i] = array->array[i + 1]; - i++; - } - - array->size--; -} diff --git a/mentos/src/libc/queue.c b/mentos/src/libc/queue.c deleted file mode 100644 index c4e08dc..0000000 --- a/mentos/src/libc/queue.c +++ /dev/null @@ -1,162 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file queue.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "queue.h" -#include "stdio.h" -#include "stdlib.h" -#include "string.h" - -queue_t queue_create(size_t data_size) -{ - queue_t queue = malloc(sizeof(struct queue_t)); - if (queue == NULL) { - printf("Cannot create the queue.\n"); - return NULL; - } - queue->data_size = data_size; - queue->front = NULL; - queue->back = NULL; - - return queue; -} - -bool_t queue_destroy(queue_t queue) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - return false; - } - queue_clear(queue); - free(queue); - - return true; -} - -bool_t queue_is_empty(queue_t queue) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - - return (bool_t)(queue->front == NULL); -} - -bool_t queue_enqueue(queue_t queue, void *data) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (data == NULL) { - printf("Data is NULL.\n"); - - return false; - } - queue_node_t *node = malloc(sizeof(struct queue_node_t)); - - if (node == NULL) { - printf("New node of the queue is NULL.\n"); - - return false; - } - // Initialize the new node. - node->data = data; - // Since the new node is the tail, nobody is behind it. - node->next = NULL; - // If the queue is empty, the data is placed also on the front. - if (queue->front == NULL) { - queue->front = node; - } else { - queue->back->next = node; - } - queue->back = node; - - return true; -} - -bool_t queue_dequeue(queue_t queue) -{ - struct queue_node_t *front_node = queue->front; - if (front_node == NULL) { - printf("Queue is Empty\n"); - - return false; - } - if (queue->front == queue->back) { - queue->front = queue->back = NULL; - } else { - queue->front = queue->front->next; - } - free(front_node); - - return true; -} - -bool_t queue_front(queue_t queue, void *data) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (queue->front == NULL) { - printf("Queue is empty\n"); - - return false; - } - memcpy(data, queue->front->data, queue->data_size); - - return true; -} - -bool_t queue_back(queue_t queue, void *data) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (queue->back == NULL) { - printf("Queue is empty\n"); - - return false; - } - memcpy(data, queue->back->data, queue->data_size); - - return true; -} - -bool_t queue_front_and_dequeue(queue_t queue, void *data) -{ - if (!queue_front(queue, data)) { - return false; - } - if (!queue_dequeue(queue)) { - return false; - } - - return true; -} - -bool_t queue_clear(queue_t queue) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (queue->front == NULL) { - return true; - } - while (queue->front) { - queue_dequeue(queue); - } - - return true; -} diff --git a/mentos/src/libc/rbtree.c b/mentos/src/libc/rbtree.c deleted file mode 100644 index 99fda45..0000000 --- a/mentos/src/libc/rbtree.c +++ /dev/null @@ -1,613 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file rbtree.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "rbtree.h" -#include "assert.h" -#include "stdlib.h" - -struct rbtree_node_t { - /// Color red (1), black (0) - int red; - /// Link left [0] and right [1] - rbtree_node_t *link[2]; - /// User provided, used indirectly via rbtree_tree_node_cmp_f. - void *value; -}; - -struct rbtree_t { - rbtree_node_t *root; - rbtree_tree_node_cmp_f cmp; - size_t size; -}; - -struct rbtree_iter_t { - /// Pointer to the tree itself. - rbtree_t *tree; - /// Current node - rbtree_node_t *node; - /// Traversal path - rbtree_node_t *path[RBTREE_ITER_MAX_HEIGHT]; - /// Top of stack - size_t top; -}; - -rbtree_node_t *rbtree_node_alloc() -{ - return malloc(sizeof(rbtree_node_t)); -} - -rbtree_node_t *rbtree_node_init(rbtree_node_t *self, void *value) -{ - if (self) { - self->red = 1; - self->link[0] = self->link[1] = NULL; - self->value = value; - } - - return self; -} - -rbtree_node_t *rbtree_node_create(void *value) -{ - return rbtree_node_init(rbtree_node_alloc(), value); -} - -void *rbtree_node_get_value(rbtree_node_t *self) -{ - if (self) { - return self->value; - } - - return NULL; -} - -void rbtree_node_dealloc(rbtree_node_t *self) -{ - if (self) { - free(self); - } -} - -static int rbtree_node_is_red(const rbtree_node_t *self) -{ - return self ? self->red : 0; -} - -static rbtree_node_t *rbtree_node_rotate(rbtree_node_t *self, int dir) -{ - rbtree_node_t *result = NULL; - if (self) { - result = self->link[!dir]; - self->link[!dir] = result->link[dir]; - result->link[dir] = self; - self->red = 1; - result->red = 0; - } - - return result; -} - -static rbtree_node_t *rbtree_node_rotate2(rbtree_node_t *self, int dir) -{ - rbtree_node_t *result = NULL; - if (self) { - self->link[!dir] = rbtree_node_rotate(self->link[!dir], !dir); - result = rbtree_node_rotate(self, dir); - } - - return result; -} - -// rbtree_t - default callbacks. - -static int rbtree_tree_node_cmp_ptr_cb(rbtree_t *self, rbtree_node_t *a, - rbtree_node_t *b) -{ - (void)self; - - return (a->value > b->value) - (a->value < b->value); -} - -static void rbtree_tree_node_dealloc_cb(rbtree_t *self, rbtree_node_t *node) -{ - if (self) { - if (node) { - rbtree_node_dealloc(node); - } - } -} - -// rbtree_t - -rbtree_t *rbtree_tree_alloc() -{ - return malloc(sizeof(rbtree_t)); -} - -rbtree_t *rbtree_tree_init(rbtree_t *self, rbtree_tree_node_cmp_f node_cmp_cb) -{ - if (self) { - self->root = NULL; - self->size = 0; - self->cmp = - node_cmp_cb ? node_cmp_cb : rbtree_tree_node_cmp_ptr_cb; - } - - return self; -} - -rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f node_cb) -{ - return rbtree_tree_init(rbtree_tree_alloc(), node_cb); -} - -void rbtree_tree_dealloc(rbtree_t *self, rbtree_tree_node_f node_cb) -{ - assert(self); - if (node_cb) { - rbtree_node_t *node = self->root; - rbtree_node_t *save = NULL; - - /* Rotate away the left links so that - * we can treat this like the destruction - * of a linked list. - */ - while (node) { - if (node->link[0] == NULL) { - // No left links, just kill the node and move on. - save = node->link[1]; - node_cb(self, node); - free(node); - node = NULL; - } else { - // Rotate away the left link and check again. - save = node->link[0]; - node->link[0] = save->link[1]; - save->link[1] = node; - } - node = save; - } - } - - free(self); -} - -void *rbtree_tree_find(rbtree_t *self, void *value) -{ - void *result = NULL; - if (self) { - rbtree_node_t node = { .value = value }; - rbtree_node_t *it = self->root; - int cmp = 0; - while (it) { - if ((cmp = self->cmp(self, it, &node))) { - /* If the tree supports duplicates, they should be - * chained to the right subtree for this to work. - */ - it = it->link[cmp < 0]; - } else { - break; - } - } - result = it ? it->value : NULL; - } - - return result; -} - -void *rbtree_tree_find_by_value(rbtree_t *self, rbtree_tree_cmp_f cmp_fun, - void *value) -{ - void *result = NULL; - if (self) { - rbtree_node_t *it = self->root; - int cmp = 0; - while (it) { - if ((cmp = cmp_fun(self, it, value))) { - /* If the tree supports duplicates, they should be - * chained to the right subtree for this to work. - */ - it = it->link[cmp < 0]; - } else { - break; - } - } - result = it ? it->value : NULL; - } - - return result; -} - -// Creates (malloc'ates). -int rbtree_tree_insert(rbtree_t *self, void *value) -{ - return rbtree_tree_insert_node(self, rbtree_node_create(value)); -} - -// Returns 1 on success, 0 otherwise. -int rbtree_tree_insert_node(rbtree_t *self, rbtree_node_t *node) -{ - if (self && node) { - if (self->root == NULL) { - self->root = node; - } else { - // False tree root. - rbtree_node_t head = { 0 }; - // Grandparent & parent. - rbtree_node_t *g, *t; - // Iterator & parent. - rbtree_node_t *p, *q; - int dir = 0, last = 0; - - // Set up our helpers. - t = &head; - g = p = NULL; - q = t->link[1] = self->root; - - // Search down the tree for a place to insert. - while (1) { - if (q == NULL) { - // Insert node at the first null link. - p->link[dir] = q = node; - } else if (rbtree_node_is_red(q->link[0]) && - rbtree_node_is_red(q->link[1])) { - // Simple red violation: color flip. - q->red = 1; - q->link[0]->red = 0; - q->link[1]->red = 0; - } - - if (rbtree_node_is_red(q) && - rbtree_node_is_red(p)) { - // Hard red violation: rotations necessary. - int dir2 = t->link[1] == g; - - if (q == p->link[last]) { - t->link[dir2] = - rbtree_node_rotate( - g, !last); - } else { - t->link[dir2] = - rbtree_node_rotate2( - g, !last); - } - } - /* Stop working if we inserted a node. This - * check also disallows duplicates in the tree. - */ - if (self->cmp(self, q, node) == 0) { - break; - } - last = dir; - dir = self->cmp(self, q, node) < 0; - - // Move the helpers down. - if (g != NULL) { - t = g; - } - g = p, p = q; - q = q->link[dir]; - } - // Update the root (it may be different). - self->root = head.link[1]; - } - // Make the root black for simplified logic. - self->root->red = 0; - ++self->size; - } - - return 1; -} - -/* Returns 1 if the value was removed, 0 otherwise. Optional node callback - * can be provided to dealloc node and/or user data. Use - * rbtree_tree_node_dealloc - * default callback to deallocate node created by rbtree_tree_insert(...). - */ -int rbtree_tree_remove_with_cb(rbtree_t *self, void *value, - rbtree_tree_node_f node_cb) -{ - if (self->root != NULL) { - // False tree root. - rbtree_node_t head = { 0 }; - // Value wrapper node. - rbtree_node_t node = { .value = value }; - // Helpers. - rbtree_node_t *q, *p, *g; - // Found item. - rbtree_node_t *f = NULL; - int dir = 1; - - // Set up our helpers. - q = &head; - g = p = NULL; - q->link[1] = self->root; - - /* Search and push a red node down - * to fix red violations as we go. - */ - while (q->link[dir] != NULL) { - int last = dir; - - // Move the helpers down. - g = p, p = q; - q = q->link[dir]; - dir = self->cmp(self, q, &node) < 0; - - /* Save the node with matching value and keep - * going; we'll do removal tasks at the end. - */ - if (self->cmp(self, q, &node) == 0) { - f = q; - } - - // Push the red node down with rotations and color flips. - if (!rbtree_node_is_red(q) && - !rbtree_node_is_red(q->link[dir])) { - if (rbtree_node_is_red(q->link[!dir])) { - p = p->link[last] = - rbtree_node_rotate(q, dir); - } else if (!rbtree_node_is_red(q->link[!dir])) { - rbtree_node_t *s = p->link[!last]; - if (s) { - if (!rbtree_node_is_red( - s->link[!last]) && - !rbtree_node_is_red( - s->link[last])) { - // Color flip. - p->red = 0; - s->red = 1; - q->red = 1; - } else { - int dir2 = - g->link[1] == p; - if (rbtree_node_is_red( - s->link[last])) { - g->link[dir2] = rbtree_node_rotate2( - p, - last); - } else if ( - rbtree_node_is_red( - s->link[!last])) { - g->link[dir2] = rbtree_node_rotate( - p, - last); - } - // Ensure correct coloring. - q->red = g->link[dir2] - ->red = - 1; - g->link[dir2] - ->link[0] - ->red = 0; - g->link[dir2] - ->link[1] - ->red = 0; - } - } - } - } - } - // Replace and remove the saved node. - if (f) { - void *tmp = f->value; - f->value = q->value; - q->value = tmp; - - p->link[p->link[1] == q] = q->link[q->link[0] == NULL]; - - if (node_cb) { - node_cb(self, q); - } - q = NULL; - } - - // Update the root (it may be different). - self->root = head.link[1]; - - // Make the root black for simplified logic. - if (self->root != NULL) { - self->root->red = 0; - } - - --self->size; - } - - return 1; -} - -int rbtree_tree_remove(rbtree_t *self, void *value) -{ - int result = 0; - if (self) { - result = rbtree_tree_remove_with_cb( - self, value, rbtree_tree_node_dealloc_cb); - } - - return result; -} - -size_t rbtree_tree_size(rbtree_t *self) -{ - size_t result = 0; - if (self) { - result = self->size; - } - - return result; -} - -// rbtree_iter_t - -rbtree_iter_t *rbtree_iter_alloc() -{ - return malloc(sizeof(rbtree_iter_t)); -} - -rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *self) -{ - if (self) { - self->tree = NULL; - self->node = NULL; - self->top = 0; - } - - return self; -} - -rbtree_iter_t *rbtree_iter_create() -{ - return rbtree_iter_init(rbtree_iter_alloc()); -} - -void rbtree_iter_dealloc(rbtree_iter_t *self) -{ - if (self) { - free(self); - } -} - -/* Internal function, init traversal object, dir determines whether - * to begin traversal at the smallest or largest valued node. - */ -static void *rbtree_iter_start(rbtree_iter_t *self, rbtree_t *tree, int dir) -{ - void *result = NULL; - if (self) { - self->tree = tree; - self->node = tree->root; - self->top = 0; - // Save the path for later selfersal. - if (self->node != NULL) { - while (self->node->link[dir] != NULL) { - self->path[self->top++] = self->node; - self->node = self->node->link[dir]; - } - } - - result = self->node == NULL ? NULL : self->node->value; - } - - return result; -} - -// Traverse a red black tree in the user-specified direction (0 asc, 1 desc). -static void *rbtree_iter_move(rbtree_iter_t *self, int dir) -{ - if (self->node->link[dir] != NULL) { - // Continue down this branch. - self->path[self->top++] = self->node; - self->node = self->node->link[dir]; - while (self->node->link[!dir] != NULL) { - self->path[self->top++] = self->node; - self->node = self->node->link[!dir]; - } - } else { - // Move to the next branch. - rbtree_node_t *last = NULL; - do { - if (self->top == 0) { - self->node = NULL; - - break; - } - last = self->node; - self->node = self->path[--self->top]; - } while (last == self->node->link[dir]); - } - - return self->node == NULL ? NULL : self->node->value; -} - -void *rbtree_iter_first(rbtree_iter_t *self, rbtree_t *tree) -{ - return rbtree_iter_start(self, tree, 0); -} - -void *rbtree_iter_last(rbtree_iter_t *self, rbtree_t *tree) -{ - return rbtree_iter_start(self, tree, 1); -} - -void *rbtree_iter_next(rbtree_iter_t *self) -{ - return rbtree_iter_move(self, 1); -} - -void *rbtree_iter_prev(rbtree_iter_t *self) -{ - return rbtree_iter_move(self, 0); -} - -int rbtree_tree_test(rbtree_t *self, rbtree_node_t *root) -{ - int lh, rh; - - if (root == NULL) { - return 1; - } else { - rbtree_node_t *ln = root->link[0]; - rbtree_node_t *rn = root->link[1]; - - // Consecutive red links. - if (rbtree_node_is_red(root)) { - if (rbtree_node_is_red(ln) || rbtree_node_is_red(rn)) { - printf("Red violation"); - - return 0; - } - } - - lh = rbtree_tree_test(self, ln); - rh = rbtree_tree_test(self, rn); - - // Invalid binary search tree. - if ((ln != NULL && self->cmp(self, ln, root) >= 0) || - (rn != NULL && self->cmp(self, rn, root) <= 0)) { - puts("Binary tree violation"); - - return 0; - } - - // Black height mismatch. - if (lh != 0 && rh != 0 && lh != rh) { - puts("Black violation"); - - return 0; - } - - // Only count black links. - if (lh != 0 && rh != 0) { - return rbtree_node_is_red(root) ? lh : lh + 1; - } else { - return 0; - } - } -} - -static void rbtree_tree_print_iter(rbtree_t *self, rbtree_node_t *node, - rbtree_tree_node_f fun) -{ - assert(self); - assert(node); - assert(fun); - - fun(self, node); - if (node->link[0]) { - rbtree_tree_print_iter(self, node->link[0], fun); - } - if (node->link[1]) { - rbtree_tree_print_iter(self, node->link[1], fun); - } -} - -void rbtree_tree_print(rbtree_t *self, rbtree_tree_node_f fun) -{ - assert(self); - assert(fun); - - rbtree_tree_print_iter(self, self->root, fun); -} diff --git a/mentos/src/libc/spinlock.c b/mentos/src/libc/spinlock.c deleted file mode 100644 index 235906d..0000000 --- a/mentos/src/libc/spinlock.c +++ /dev/null @@ -1,34 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file spinlock.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "spinlock.h" - -void spinlock_init(spinlock_t *spinlock) -{ - (*spinlock) = 0; -} - -void spinlock_lock(spinlock_t *spinlock) -{ - while (true) { - if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) { - break; - } - while (*spinlock) - cpu_relax(); - } -} - -void spinlock_unlock(spinlock_t *spinlock) -{ - barrier(); - atomic_set(spinlock, SPINLOCK_FREE); -} - -bool_t spinlock_trylock(spinlock_t *spinlock) -{ - return (bool_t)(atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0); -} diff --git a/mentos/src/libc/stdio.c b/mentos/src/libc/stdio.c deleted file mode 100644 index a1b246c..0000000 --- a/mentos/src/libc/stdio.c +++ /dev/null @@ -1,160 +0,0 @@ -/// @file stdio.c -/// @brief Standard I/0 functions. -/// @date Apr 2019 - -#include "stdio.h" -#include "video.h" -#include "ctype.h" -#include "string.h" -#include "keyboard.h" -#include "unistd.h" -#include "debug.h" - -void putchar(int character) -{ - write(STDOUT_FILENO, &character, 1); -} - -void puts(char *str) -{ - write(STDOUT_FILENO, str, strlen(str)); -} - -int getchar(void) -{ - char c; - while (true) { - read(STDIN_FILENO, &c, 1); - if (c != -1) - break; - } - return c; -} - -char *gets(char *str) -{ - // Check the input string. - if (str == NULL) - return NULL; - // Buffer for reading input. - char buffer[GETS_BUFFERSIZE]; - memset(buffer, '\0', GETS_BUFFERSIZE); - // Char pointer to the buffer. - char *cptr = buffer; - // Character storage and counter to prevent overflow. - int ch, counter = 0; - // Read until we find a newline or we exceed the buffer size. - while (((ch = getchar()) != '\n') && (counter++ < GETS_BUFFERSIZE)) { - // If we encounter EOF, stop. - if (ch == EOF) { - // EOF at start of line return NULL. - if (cptr == str) - return NULL; - break; - } - // The character is stored at address, and the pointer is incremented. - *cptr++ = ch; - } - // Add the null-terminating character. - *cptr = '\0'; - // Copy the string we have read. - strcpy(str, buffer); - // Return a pointer to the original string. - return str; -} - -int atoi(const char *str) -{ - // Check the input string. - if (str == NULL) - return 0; - // Initialize sign. - int sign = (str[0] == '-') ? -1 : +1; - // Initialize the result. - int result = 0; - // Check that the rest of the numbers are digits. - for (int i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) - if (!isdigit(str[i])) - return -1; - // Iterate through all digits and update the result. - for (int i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) - result = (result * 10) + str[i] - '0'; - return sign * result; -} - -int printf(const char *format, ...) -{ - char buffer[4096]; - va_list ap; - - // Start variabile argument's list. - va_start(ap, format); - - int len = vsprintf(buffer, format, ap); - va_end(ap); - - // Write the contento to standard output. - write(STDOUT_FILENO, buffer, len); - - return len; -} - -size_t scanf(const char *format, ...) -{ - size_t count = 0; - va_list scan; - va_start(scan, format); - - for (; *format; format++) { - if (*format == '%') { - // Declare an input string. - char input[GETS_BUFFERSIZE]; - // Get the input string. - gets(input); - // Evaluate the length of the string. - size_t input_length = strlen(input); - // Add the length of the input to the counter. - count += input_length; - // Evaluate the maximum number of input characters. - size_t max_chars = 0; - if (isdigit(*++format)) { - char max_char_num[16]; - int i = 0; - - while (isdigit(*format)) { - max_char_num[i++] = *format; - format++; - } - max_char_num[i] = '\0'; - int number = atoi(max_char_num); - - if (number > 0) { - max_chars = (size_t)number; - } - } - switch (*format) { - case 's': { - char *s_ptr = va_arg(scan, char *); - if (max_chars == 0 || input_length <= max_chars) - strncpy(s_ptr, input, input_length); - else - strncpy(s_ptr, input, max_chars); - break; - } - case 'd': { - int *d_ptr = va_arg(scan, int *); - if (max_chars != 0 && input_length > max_chars) { - input[max_chars] = '\0'; - } - (*d_ptr) = atoi(input); - break; - } - default: - break; - } - } - } - va_end(scan); - - return count; -} diff --git a/mentos/src/libc/stdlib.c b/mentos/src/libc/stdlib.c deleted file mode 100644 index 8cde7c1..0000000 --- a/mentos/src/libc/stdlib.c +++ /dev/null @@ -1,48 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdlib.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "syscall.h" -#include "stdlib.h" -#include "string.h" - -void *malloc(unsigned int size) -{ - void *_res; - DEFN_SYSCALL1(_res, __NR_brk, size); - - return _res; -} - -void free(void *p) -{ - int _res; - DEFN_SYSCALL1(_res, __NR_free, p); -} - -void *calloc(size_t element_number, size_t element_size) -{ - void *ptr = malloc(element_number * element_size); - if (ptr) { - memset(ptr, 0, element_number * element_size); - } - - return ptr; -} - -/// Seed used to generate random numbers. -static int rseed = 0; - -inline void srand(int x) -{ - rseed = x; -} - -/// @brief Returns a pseudo-random integral number in the range -/// between 0 and RAND_MAX. -inline int rand() -{ - return rseed = (rseed * 1103515245U + 12345U) & RAND_MAX; -} diff --git a/mentos/src/libc/strerror.c b/mentos/src/libc/strerror.c deleted file mode 100644 index afcb482..0000000 --- a/mentos/src/libc/strerror.c +++ /dev/null @@ -1,470 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file strerror.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "strerror.h" -#include "string.h" - -char *strerror(int errnum) -{ - static char error[1024]; - - switch (errnum) { - case 0: - strcpy(error, "Success"); - break; -#ifdef ENOENT - case ENOENT: - strcpy(error, "No such file or directory"); - break; -#endif -#ifdef ESRCH - case ESRCH: - strcpy(error, "No such process"); - break; -#endif -#ifdef EINTR - case EINTR: - strcpy(error, "Interrupted system call"); - break; -#endif -#ifdef EIO - case EIO: - strcpy(error, "I/O error"); - break; -#endif -#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) - case ENXIO: - strcpy(error, "No such device or address"); - break; -#endif -#ifdef E2BIG - case E2BIG: - strcpy(error, "Arg list too long"); - break; -#endif -#ifdef ENOEXEC - case ENOEXEC: - strcpy(error, "Exec format error"); - break; -#endif -#ifdef EALREADY - case EALREADY: - strcpy(error, "Socket already connected"); - break; -#endif -#ifdef EBADF - case EBADF: - strcpy(error, "Bad file number"); - break; -#endif -#ifdef ECHILD - case ECHILD: - strcpy(error, "No children"); - break; -#endif -#ifdef EDESTADDRREQ - case EDESTADDRREQ: - strcpy(error, "Destination address required"); - break; -#endif -#ifdef EAGAIN - case EAGAIN: - strcpy(error, "No more processes"); - break; -#endif -#ifdef ENOMEM - case ENOMEM: - strcpy(error, "Not enough space"); - break; -#endif -#ifdef EACCES - case EACCES: - strcpy(error, "Permission denied"); - break; -#endif -#ifdef EFAULT - case EFAULT: - strcpy(error, "Bad address"); - break; -#endif -#ifdef ENOTBLK - case ENOTBLK: - strcpy(error, "Block device required"); - break; -#endif -#ifdef EBUSY - case EBUSY: - strcpy(error, "Device or resource busy"); - break; -#endif -#ifdef EEXIST - case EEXIST: - strcpy(error, "File exists"); - break; -#endif -#ifdef EXDEV - case EXDEV: - strcpy(error, "Cross-device link"); - break; -#endif -#ifdef ENODEV - case ENODEV: - strcpy(error, "No such device"); - break; -#endif -#ifdef ENOTDIR - case ENOTDIR: - strcpy(error, "Not a directory"); - break; -#endif -#ifdef EHOSTDOWN - case EHOSTDOWN: - strcpy(error, "Host is down"); - break; -#endif -#ifdef EINPROGRESS - case EINPROGRESS: - strcpy(error, "Connection already in progress"); - break; -#endif -#ifdef EISDIR - case EISDIR: - strcpy(error, "Is a directory"); - break; -#endif -#ifdef EINVAL - case EINVAL: - strcpy(error, "Invalid argument"); - break; -#endif -#ifdef ENETDOWN - case ENETDOWN: - strcpy(error, "Network interface is not configured"); - break; -#endif -#ifdef ENFILE - case ENFILE: - strcpy(error, "Too many open files in system"); - break; -#endif -#ifdef EMFILE - case EMFILE: - strcpy(error, "Too many open files"); - break; -#endif -#ifdef ENOTTY - case ENOTTY: - strcpy(error, "Not a character device"); - break; -#endif -#ifdef ETXTBSY - case ETXTBSY: - strcpy(error, "Text file busy"); - break; -#endif -#ifdef EFBIG - case EFBIG: - strcpy(error, "File too large"); - break; -#endif -#ifdef EHOSTUNREACH - case EHOSTUNREACH: - strcpy(error, "Host is unreachable"); - break; -#endif -#ifdef ENOSPC - case ENOSPC: - strcpy(error, "No space left on device"); - break; -#endif -#ifdef ENOTSUP - case ENOTSUP: - strcpy(error, "Not supported"); - break; -#endif -#ifdef ESPIPE - case ESPIPE: - strcpy(error, "Illegal seek"); - break; -#endif -#ifdef EROFS - case EROFS: - strcpy(error, "Read-only file system"); - break; -#endif -#ifdef EMLINK - case EMLINK: - strcpy(error, "Too many links"); - break; -#endif -#ifdef EPIPE - case EPIPE: - strcpy(error, "Broken pipe"); - break; -#endif -#ifdef EDOM - case EDOM: - strcpy(error, "Math argument"); - break; -#endif -#ifdef ERANGE - case ERANGE: - strcpy(error, "Result too large"); - break; -#endif -#ifdef ENOMSG - case ENOMSG: - strcpy(error, "No message of desired type"); - break; -#endif -#ifdef EIDRM - case EIDRM: - strcpy(error, "Identifier removed"); - break; -#endif -#ifdef EDEADLK - case EDEADLK: - strcpy(error, "Deadlock"); - break; -#endif -#ifdef ENETUNREACH - case ENETUNREACH: - strcpy(error, "Network is unreachable"); - break; -#endif -#ifdef ENOLCK - case ENOLCK: - strcpy(error, "No lock"); - break; -#endif -#ifdef ENOSTR - case ENOSTR: - strcpy(error, "Not a stream"); - break; -#endif -#ifdef ETIME - case ETIME: - strcpy(error, "Stream ioctl timeout"); - break; -#endif -#ifdef ENOSR - case ENOSR: - strcpy(error, "No stream resources"); - break; -#endif -#ifdef ENONET - case ENONET: - strcpy(error, "Machine is not on the network"); - break; -#endif -#ifdef ENOPKG - case ENOPKG: - strcpy(error, "No package"); - break; -#endif -#ifdef EREMOTE - case EREMOTE: - strcpy(error, "Resource is remote"); - break; -#endif -#ifdef ENOLINK - case ENOLINK: - strcpy(error, "Virtual circuit is gone"); - break; -#endif -#ifdef EADV - case EADV: - strcpy(error, "Advertise error"); - break; -#endif -#ifdef ESRMNT - case ESRMNT: - strcpy(error, "Srmount error"); - break; -#endif -#ifdef ECOMM - case ECOMM: - strcpy(error, "Communication error"); - break; -#endif -#ifdef EPROTO - case EPROTO: - strcpy(error, "Protocol error"); - break; -#endif -#ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: - strcpy(error, "Unknown protocol"); - break; -#endif -#ifdef EMULTIHOP - case EMULTIHOP: - strcpy(error, "Multihop attempted"); - break; -#endif -#ifdef EBADMSG - case EBADMSG: - strcpy(error, "Bad message"); - break; -#endif -#ifdef ELIBACC - case ELIBACC: - strcpy(error, "Cannot access a needed shared library"); - break; -#endif -#ifdef ELIBBAD - case ELIBBAD: - strcpy(error, "Accessing a corrupted shared library"); - break; -#endif -#ifdef ELIBSCN - case ELIBSCN: - strcpy(error, ".lib section in a.out corrupted"); - break; -#endif -#ifdef ELIBMAX - case ELIBMAX: - strcpy(error, - "Attempting to link in more shared libraries than system limit"); - break; -#endif -#ifdef ELIBEXEC - case ELIBEXEC: - strcpy(error, "Cannot exec a shared library directly"); - break; -#endif -#ifdef ENOSYS - case ENOSYS: - strcpy(error, "Function not implemented"); - break; -#endif -#ifdef ENMFILE - case ENMFILE: - strcpy(error, "No more files"); - break; -#endif -#ifdef ENOTEMPTY - case ENOTEMPTY: - strcpy(error, "Directory not empty"); - break; -#endif -#ifdef ENAMETOOLONG - case ENAMETOOLONG: - strcpy(error, "File or path name too long"); - break; -#endif -#ifdef ELOOP - case ELOOP: - strcpy(error, "Too many symbolic links"); - break; -#endif -#ifdef ENOBUFS - case ENOBUFS: - strcpy(error, "No buffer space available"); - break; -#endif -#ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - strcpy(error, "Address family not supported by protocol family"); - break; -#endif -#ifdef EPROTOTYPE - case EPROTOTYPE: - strcpy(error, "Protocol wrong type for socket"); - break; -#endif -#ifdef ENOTSOCK - case ENOTSOCK: - strcpy(error, "Socket operation on non-socket"); - break; -#endif -#ifdef ENOPROTOOPT - case ENOPROTOOPT: - strcpy(error, "Protocol not available"); - break; -#endif -#ifdef ESHUTDOWN - case ESHUTDOWN: - strcpy(error, "Can't send after socket shutdown"); - break; -#endif -#ifdef ECONNREFUSED - case ECONNREFUSED: - strcpy(error, "Connection refused"); - break; -#endif -#ifdef EADDRINUSE - case EADDRINUSE: - strcpy(error, "Address already in use"); - break; -#endif -#ifdef ECONNABORTED - case ECONNABORTED: - strcpy(error, "Software caused connection abort"); - break; -#endif -#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) - case EWOULDBLOCK: - strcpy(error, "Operation would block"); - break; -#endif -#ifdef ENOTCONN - case ENOTCONN: - strcpy(error, "Socket is not connected"); - break; -#endif -#ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - strcpy(error, "Socket type not supported"); - break; -#endif -#ifdef EISCONN - case EISCONN: - strcpy(error, "Socket is already connected"); - break; -#endif -#ifdef ECANCELED - case ECANCELED: - strcpy(error, "Operation canceled"); - break; -#endif -#ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: - strcpy(error, "State not recoverable"); - break; -#endif -#ifdef EOWNERDEAD - case EOWNERDEAD: - strcpy(error, "Previous owner died"); - break; -#endif -#ifdef ESTRPIPE - case ESTRPIPE: - strcpy(error, "Streams pipe error"); - break; -#endif -#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) - case EOPNOTSUPP: - strcpy(error, "Operation not supported on socket"); - break; -#endif -#ifdef EMSGSIZE - case EMSGSIZE: - strcpy(error, "Message too long"); - break; -#endif -#ifdef ETIMEDOUT - case ETIMEDOUT: - strcpy(error, "Connection timed out"); - break; -#endif - default: - strcpy(error, "Unknown error"); - break; - } - - return error; -} diff --git a/mentos/src/libc/string.c b/mentos/src/libc/string.c deleted file mode 100644 index 196163d..0000000 --- a/mentos/src/libc/string.c +++ /dev/null @@ -1,850 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file string.c -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include "string.h" -#include "ctype.h" -#include "stdlib.h" -#include "stdio.h" - -char *strncpy(char *destination, const char *source, size_t num) -{ - char *start = destination; - while (num && (*destination++ = *source++)) { - num--; - } - if (num) { - while (--num) { - *destination++ = '\0'; - } - } - - return start; -} - -int strncmp(const char *s1, const char *s2, size_t n) -{ - if (!n) - return 0; - while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) { - s1++; - s2++; - } - - return *(unsigned char *)s1 - *(unsigned char *)s2; -} - -int stricmp(const char *s1, const char *s2) -{ - while (*s2 != 0 && toupper(*s1) == toupper(*s2)) { - s1++, s2++; - } - - return (toupper(*s1) - toupper(*s2)); -} - -int strnicmp(const char *s1, const char *s2, size_t n) -{ - int f, l; - - do { - if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) { - f -= 'A' - 'a'; - } - if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) { - l -= 'A' - 'a'; - } - } while (--n && f && (f == l)); - - return f - l; -} - -char *strchr(const char *s, int ch) -{ - while (*s && *s != (char)ch) { - s++; - } - if (*s == (char)ch) - return (char *)s; - { - return NULL; - } -} - -char *strrchr(const char *s, int ch) -{ - char *start = (char *)s; - - while (*s++) - ; - - while (--s != start && *s != (char)ch) - ; - - if (*s == (char)ch) { - return (char *)s; - } - - return NULL; -} - -char *strstr(const char *str1, const char *str2) -{ - char *cp = (char *)str1; - char *s1, *s2; - - if (!*str2) { - return (char *)str1; - } - - while (*cp) { - s1 = cp; - s2 = (char *)str2; - - while (*s1 && *s2 && !(*s1 - *s2)) { - s1++, s2++; - } - if (!*s2) { - return cp; - } - cp++; - } - - return NULL; -} - -size_t strspn(const char *string, const char *control) -{ - const char *str = string; - const char *ctrl = control; - - char map[32]; - size_t n; - - // Clear out bit map. - for (n = 0; n < 32; n++) { - map[n] = 0; - } - - // Set bits in control map. - while (*ctrl) { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - ctrl++; - } - - // 1st char NOT in control map stops search. - if (*str) { - n = 0; - while (map[*str >> 3] & (1 << (*str & 7))) { - n++; - str++; - } - - return n; - } - - return 0; -} - -size_t strcspn(const char *string, const char *control) -{ - const char *str = string; - const char *ctrl = control; - - char map[32]; - size_t n; - - // Clear out bit map. - for (n = 0; n < 32; n++) - map[n] = 0; - - // Set bits in control map. - while (*ctrl) { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - ctrl++; - } - - // 1st char in control map stops search. - n = 0; - map[0] |= 1; - while (!(map[*str >> 3] & (1 << (*str & 7)))) { - n++; - str++; - } - - return n; -} - -char *strpbrk(const char *string, const char *control) -{ - const char *str = string; - const char *ctrl = control; - - char map[32]; - int n; - - // Clear out bit map. - for (n = 0; n < 32; n++) - map[n] = 0; - - // Set bits in control map. - while (*ctrl) { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - ctrl++; - } - - // 1st char in control map stops search. - while (*str) { - if (map[*str >> 3] & (1 << (*str & 7))) { - return (char *)str; - } - str++; - } - - return NULL; -} - -void *memmove(void *dst, const void *src, size_t n) -{ - void *ret = dst; - - if (dst <= src || (char *)dst >= ((char *)src + n)) { - /* Non-overlapping buffers; copy from lower addresses to higher - * addresses. - */ - while (n--) { - *(char *)dst = *(char *)src; - dst = (char *)dst + 1; - src = (char *)src + 1; - } - } else { - // Overlapping buffers; copy from higher addresses to lower addresses. - dst = (char *)dst + n - 1; - src = (char *)src + n - 1; - - while (n--) { - *(char *)dst = *(char *)src; - dst = (char *)dst - 1; - src = (char *)src - 1; - } - } - - return ret; -} - -void *memchr(const void *buf, int ch, size_t n) -{ - while (n && (*(unsigned char *)buf != (unsigned char)ch)) { - buf = (unsigned char *)buf + 1; - n--; - } - - return (n ? (void *)buf : NULL); -} - -char *strlwr(char *s) -{ - char *p = s; - - while (*p) { - *p = (char)tolower(*p); - p++; - } - - return s; -} - -char *strupr(char *s) -{ - char *p = s; - - while (*p) { - *p = (char)toupper(*p); - p++; - } - - return s; -} - -char *strncat(char *s1, const char *s2, size_t n) -{ - char *start = s1; - - while (*s1++) - ; - { - s1--; - } - - while (n--) { - if (!(*s1++ = *s2++)) - return start; - } - - *s1 = '\0'; - - return start; -} - -char *strnset(char *s, int c, size_t n) -{ - while (n-- && *s) { - *s++ = (char)c; - } - - return s; -} - -char *strrev(char *s) -{ - char *start = s; - char *left = s; - char ch; - - while (*s++) - ; - s -= 2; - - while (left < s) { - ch = *left; - *left++ = *s; - *s-- = ch; - } - - return start; -} - -char *strtok_r(char *string, const char *control, char **lasts) -{ - char *str; - const char *ctrl = control; - - char map[32]; - int n; - - // Clear control map. - for (n = 0; n < 32; n++) { - map[n] = 0; - } - - // Set bits in delimiter table. - do { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - } while (*ctrl++); - - /* Initialize str. If string is NULL, set str to the saved - * pointer (i.e., continue breaking tokens out of the string - * from the last strtok call). - */ - if (string) { - str = string; - } else { - str = *lasts; - } - - /* Find beginning of token (skip over leading delimiters). Note that - * there is no token iff this loop sets str to point to the terminal - * null (*str == '\0'). - */ - while ((map[*str >> 3] & (1 << (*str & 7))) && *str) { - str++; - } - - string = str; - - /* Find the end of the token. If it is not the end of the string, - * put a null there. - */ - for (; *str; str++) { - if (map[*str >> 3] & (1 << (*str & 7))) { - *str++ = '\0'; - - break; - } - } - - // Update nexttoken. - *lasts = str; - - // Determine if a token has been found. - if (string == (char *)str) { - return NULL; - } else { - return string; - } -} - -// Intrinsic functions. - -/* - * #pragma function(memset) - * #pragma function(memcmp) - * #pragma function(memcpy) - * #pragma function(strcpy) - * #pragma function(strlen) - * #pragma function(strcat) - * #pragma function(strcmp) - * #pragma function(strset) - */ - -void *memset(void *ptr, int value, size_t num) -{ - // Truncate c to 8 bits. - value = (value & 0xFF); - - char *dst = (char *)ptr; - - // Initialize the rest of the size. - while (num--) { - *dst++ = (char)value; - } - - return ptr; -} - -int memcmp(const void *dst, const void *src, size_t n) -{ - if (!n) { - return 0; - } - - while (--n && *(char *)dst == *(char *)src) { - dst = (char *)dst + 1; - src = (char *)src + 1; - } - - return *((unsigned char *)dst) - *((unsigned char *)src); -} - -void *memcpy(void *_dst, const void *_src, size_t num) -{ - char *dst = _dst; - const char *src = _src; - - while (num--) { - *dst++ = *src++; - } - - return _dst; -} - -void *memccpy(void *dst, const void *src, int c, size_t n) -{ - while (n && (*((char *)(dst = (char *)dst + 1) - 1) = - *((char *)(src = (char *)src + 1) - 1)) != (char)c) { - n--; - } - - return n ? dst : NULL; -} - -char *strcpy(char *dst, const char *src) -{ - char *save = dst; - - while ((*dst++ = *src++) != '\0') - ; - - return save; -} - -size_t strlen(const char *s) -{ - const char *eos; - - for (eos = s; *eos != 0; ++eos) - ; - - long len = eos - s; - - return (len < 0) ? 0 : (size_t)len; -} - -size_t strnlen(const char *s, size_t count) -{ - const char *sc; - - for (sc = s; *sc != '\0' && count--; ++sc) - ; - - long len = sc - s; - - return (len < 0) ? 0 : (size_t)len; -} - -int strcmp(const char *s1, const char *s2) -{ - int ret = 0; - const char *s1t = s1, *s2t = s2; - - for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) - ; - - return (ret < 0) ? -1 : (ret > 0) ? 1 : 0; -} - -char *strcat(char *dst, const char *src) -{ - char *cp = dst; - - while (*cp) { - cp++; - } - - while ((*cp++ = *src++) != '\0') - ; - - return dst; -} - -char *strset(char *s, int c) -{ - char *start = s; - - while (*s) { - *s++ = (char)c; - } - - return start; -} - -char *strtok(char *str, const char *delim) -{ - const char *spanp; - int c, sc; - char *tok; - static char *last; - - if (str == NULL && (str = last) == NULL) { - return (NULL); - } - -cont: - c = *str++; - for (spanp = delim; (sc = *spanp++) != 0;) { - if (c == sc) { - goto cont; - } - } - - if (c == 0) { - last = NULL; - - return (NULL); - } - tok = str - 1; - - for (;;) { - c = *str++; - spanp = delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) { - str = NULL; - } else { - str[-1] = 0; - } - last = str; - - return (tok); - } - } while (sc != 0); - } -} - -int _kstrncmp(const char *s1, const char *s2, size_t num) -{ - /* If the number of characters that has to be checked is equal to zero, - * just return 0. - */ - if (num == 0) { - return 0; - } - size_t sn = 0; - - while ((*s1 == *s2) && (sn < (num - 1))) { - ++s1; - ++s2; - ++sn; - } - - if (*s1 > *s2) { - return 1; - } - - if (*s1 < *s2) { - return -1; - } - - return 0; -} - -char *trim(char *str) -{ - size_t len = 0; - char *frontp = str; - char *endp = NULL; - - if (str == NULL) { - return NULL; - } - if (str[0] == '\0') { - return str; - } - - len = strlen(str); - endp = str + len; - - /* Move the front and back pointers to address the first non-whitespace - * characters from each end. - */ - while (isspace((unsigned char)*frontp)) { - ++frontp; - } - if (endp != frontp) { - while (isspace((unsigned char)*(--endp)) && endp != frontp) - ; - } - if (str + len - 1 != endp) { - *(endp + 1) = '\0'; - } else if (frontp != str && endp == frontp) { - *str = '\0'; - } - /* Shift the string so that it starts at str so that if it's dynamically - * allocated, we can still free it on the returned pointer. Note the reuse - * of endp to mean the front of the string buffer now. - */ - endp = str; - if (frontp != str) { - while (*frontp) { - *endp++ = *frontp++; - } - *endp = '\0'; - } - - return str; -} - -char *strdup(const char *s) -{ - size_t len = strlen(s) + 1; - - return memcpy(malloc(len), s, len); -} - -char *kstrdup(const char *s) -{ - size_t len = strlen(s) + 1; - - return memcpy(kmalloc(len), s, len); -} - -char *strsep(char **stringp, const char *delim) -{ - char *s; - const char *spanp; - int c, sc; - char *tok; - if ((s = *stringp) == NULL) { - return (NULL); - } - for (tok = s;;) { - c = *s++; - spanp = delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) { - s = NULL; - } else { - s[-1] = 0; - } - *stringp = s; - - return (tok); - } - } while (sc != 0); - } -} - -list_t *str_split(const char *str, const char *delim, unsigned int *num) -{ - list_t *ret_list = list_create(); - char *s = strdup(str); - char *token, *rest = s; - - while ((token = strsep(&rest, delim)) != NULL) { - if (!strcmp(token, ".")) { - continue; - } - if (!strcmp(token, "..")) { - if (list_size(ret_list) > 0) - list_pop_back(ret_list); - - continue; - } - list_push(ret_list, strdup(token)); - - if (num) { - (*num)++; - } - } - free(s); - - return ret_list; -} - -char *list2str(list_t *list, const char *delim) -{ - char *ret = malloc(256); - memset(ret, 0, 256); - size_t len = 0; - size_t ret_len = 256; - - while (list_size(list) > 0) { - char *temp = list_pop_back(list)->value; - size_t len_temp = strlen(temp); - if (len + len_temp + 1 + 1 > ret_len) { - ret_len = ret_len * 2; - free(ret); - ret = malloc(ret_len); - len = len + len_temp + 1; - } - strcat(ret, delim); - strcat(ret, temp); - } - - return ret; -} - -void int_to_str(char *buffer, unsigned int num, unsigned int base) -{ - // int numval; - char *p, *pbase; - - p = pbase = buffer; - - if (base == 16) { - sprintf(buffer, "%0x", num); - } else { - if (num == 0) { - *p++ = '0'; - } - while (num != 0) { - *p++ = (char)('0' + (num % base)); - num = num / base; - } - *p-- = 0; - - while (p > pbase) { - char tmp; - tmp = *p; - *p = *pbase; - *pbase = tmp; - - p--; - pbase++; - } - } -} - -void _knntos(char *buffer, int num, int base) -{ - // int numval; - char *p, *pbase; - - p = pbase = buffer; - - if (num < 0) { - num = (~num) + 1; - *p++ = '-'; - pbase++; - } - while (num > 0) { - *p++ = (char)('0' + (num % base)); - num = num / base; - } - - *p-- = 0; - while (p > pbase) { - char tmp; - tmp = *p; - *p = *pbase; - *pbase = tmp; - - p--; - pbase++; - } -} - -char *replace_char(char *str, char find, char replace) -{ - char *current_pos = strchr(str, find); - - while (current_pos) { - *current_pos = replace; - current_pos = strchr(current_pos, find); - } - - return str; -} - -void strmode(mode_t mode, char *p) -{ - // Usr. - if (mode & 0400) { - *p++ = 'r'; - } else { - *p++ = '-'; - } - if (mode & 0200) { - *p++ = 'w'; - } else { - *p++ = '-'; - } - if (mode & 0100) { - *p++ = 'x'; - } else { - *p++ = '-'; - } - - // Group. - if (mode & 0040) { - *p++ = 'r'; - } else { - *p++ = '-'; - } - if (mode & 0020) { - *p++ = 'w'; - } else { - *p++ = '-'; - } - if (mode & 0010) { - *p++ = 'x'; - } else { - *p++ = '-'; - } - - // Other. - if (mode & 0004) { - *p++ = 'r'; - } else { - *p++ = '-'; - } - if (mode & 0002) { - *p++ = 'w'; - } else { - *p++ = '-'; - } - if (mode & 0001) { - *p++ = 'x'; - } else { - *p++ = '-'; - } - - // Will be a '+' if ACL's implemented. - *p++ = ' '; - *p = '\0'; -} diff --git a/mentos/src/libc/tree.c b/mentos/src/libc/tree.c deleted file mode 100644 index 3afd337..0000000 --- a/mentos/src/libc/tree.c +++ /dev/null @@ -1,217 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file tree.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "tree.h" -#include "list.h" -#include "stdlib.h" -#include "debug.h" -#include "panic.h" - -tree_t *tree_create() -{ - tree_t *out = malloc(sizeof(tree_t)); - out->nodes = 0; - out->root = NULL; - - return out; -} - -tree_node_t *tree_set_root(tree_t *tree, void *value) -{ - tree_node_t *root = tree_node_create(value); - tree->root = root; - tree->nodes = 1; - - return root; -} - -void tree_node_destroy(tree_node_t *node) -{ - listnode_foreach(child, node->children) - { - tree_node_destroy((tree_node_t *)child->value); - } - free(node->value); -} - -void tree_destroy(tree_t *tree) -{ - if (tree->root) { - tree_node_destroy(tree->root); - } -} - -/// @brief Free a node and its children, but not their contents. -static void tree_node_free(tree_node_t *node) -{ - if (!node) { - return; - } - listnode_foreach(child, node->children) - { - tree_node_free(child->value); - } - list_destroy(node->children); - free(node); -} - -void tree_free(tree_t *tree) -{ - tree_node_free(tree->root); -} - -tree_node_t *tree_node_create(void *value) -{ - tree_node_t *out = malloc(sizeof(tree_node_t)); - out->value = value; - out->children = list_create(); - out->parent = NULL; - - return out; -} - -void tree_node_insert_child_node(tree_t *tree, tree_node_t *parent, - tree_node_t *node) -{ - list_insert_back(parent->children, node); - node->parent = parent; - tree->nodes++; -} - -tree_node_t *tree_node_insert_child(tree_t *tree, tree_node_t *parent, - void *value) -{ - tree_node_t *out = tree_node_create(value); - tree_node_insert_child_node(tree, parent, out); - - return out; -} - -tree_node_t *tree_node_find_parent(tree_node_t *haystack, tree_node_t *needle) -{ - tree_node_t *found = NULL; - listnode_foreach(child, haystack->children) - { - if (child->value == needle) { - return haystack; - } - found = tree_node_find_parent((tree_node_t *)child->value, needle); - if (found) { - break; - } - } - - return found; -} - -/// @brief Return the parent of a node, inefficiently. -tree_node_t *tree_find_parent(tree_t *tree, tree_node_t *node) -{ - if (!tree->root) { - return NULL; - } - - return tree_node_find_parent(tree->root, node); -} - -/// @brief Return the number of children this node has. -static size_t tree_count_children(tree_node_t *node) -{ - if (!node) { - return 0; - } - - if (!node->children) { - return 0; - } - - size_t out = node->children->size; - listnode_foreach(child, node->children) - { - out += tree_count_children((tree_node_t *)child->value); - } - - return out; -} - -void tree_node_parent_remove(tree_t *tree, tree_node_t *parent, - tree_node_t *node) -{ - tree->nodes -= tree_count_children(node) + 1; - list_remove_node(parent->children, list_find(parent->children, node)); - tree_node_free(node); -} - -void tree_node_remove(tree_t *tree, tree_node_t *node) -{ - tree_node_t *parent = node->parent; - if (!parent) { - if (node == tree->root) { - tree->nodes = 0; - tree->root = NULL; - tree_node_free(node); - } else - kernel_panic("Found node with no parent which is not root."); - } - tree_node_parent_remove(tree, parent, node); -} - -void tree_remove(tree_t *tree, tree_node_t *node) -{ - tree_node_t *parent = node->parent; - /* This is something we just can't do. We don't know how to merge our - * children into our "parent" because then we'd have more than one root node. - * A good way to think about this is actually what this tree struct - * primarily exists for: processes. Trying to remove the root is equivalent - * to trying to kill init! Which is bad. We immediately fault on such - * a case anyway ("Tried to kill init, shutting down!"). - */ - if (!parent) { - return; - } - tree->nodes--; - list_remove_node(parent->children, list_find(parent->children, node)); - listnode_foreach(child, node->children) - { - // Reassign the parents. - ((tree_node_t *)child->value)->parent = parent; - } - list_merge(parent->children, node->children); - free(node); -} - -void tree_break_off(tree_t *tree, tree_node_t *node) -{ - tree_node_t *parent = node->parent; - if (!parent) { - return; - } - list_remove_node(parent->children, list_find(parent->children, node)); -} - -/// @brief Searches the item inside tree and returns the node which contains it. -tree_node_t *tree_node_find(tree_node_t *node, void *search, - tree_comparator_t comparator) -{ - if (comparator(node->value, search)) { - return node; - } - tree_node_t *found; - listnode_foreach(child, node->children) - { - found = tree_node_find((tree_node_t *)child->value, search, comparator); - if (found) { - return found; - } - } - - return NULL; -} - -tree_node_t *tree_find(tree_t *tree, void *value, tree_comparator_t comparator) -{ - return tree_node_find(tree->root, value, comparator); -} diff --git a/mentos/src/libc/unistd/chdir.c b/mentos/src/libc/unistd/chdir.c deleted file mode 100644 index 790f5e5..0000000 --- a/mentos/src/libc/unistd/chdir.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file chdir.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "syscall.h" -#include "errno.h" - -void chdir(char const * path) -{ - ssize_t retval; - DEFN_SYSCALL1(retval, __NR_chdir, path); - if (retval < 0) - { - errno = -retval; - } -} diff --git a/mentos/src/libc/unistd/close.c b/mentos/src/libc/unistd/close.c deleted file mode 100644 index 5be29f7..0000000 --- a/mentos/src/libc/unistd/close.c +++ /dev/null @@ -1,20 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file close.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int close(int fd) -{ - int retval; - DEFN_SYSCALL1(retval, __NR_close, fd); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} diff --git a/mentos/src/libc/unistd/execve.c b/mentos/src/libc/unistd/execve.c deleted file mode 100644 index c0351ee..0000000 --- a/mentos/src/libc/unistd/execve.c +++ /dev/null @@ -1,24 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file execve.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int execve(const char *path, char *const argv[], char *const envp[]) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_execve, path, argv, envp); - - if (retval < 0) - { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/exit.c b/mentos/src/libc/unistd/exit.c deleted file mode 100644 index aa8a3f5..0000000 --- a/mentos/src/libc/unistd/exit.c +++ /dev/null @@ -1,17 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file exit.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "types.h" -#include "syscall.h" - -void exit(int status) { - int _res; - - DEFN_SYSCALL1(_res, __NR_exit, status); - - // The process never returns from this system call! -} diff --git a/mentos/src/libc/unistd/getcwd.c b/mentos/src/libc/unistd/getcwd.c deleted file mode 100644 index 2e2ad1b..0000000 --- a/mentos/src/libc/unistd/getcwd.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file getcwd.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "syscall.h" -#include "errno.h" - -void getcwd(char * path, size_t size) -{ - ssize_t retval; - DEFN_SYSCALL2(retval, __NR_getcwd, path, size); - if (retval < 0) - { - errno = -retval; - } -} diff --git a/mentos/src/libc/unistd/getpid.c b/mentos/src/libc/unistd/getpid.c deleted file mode 100644 index c1059b8..0000000 --- a/mentos/src/libc/unistd/getpid.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file getpid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" - -#include "syscall.h" -#include "types.h" - -pid_t getpid() -{ - pid_t ret; - - DEFN_SYSCALL0(ret, __NR_getpid); - - return ret; -} diff --git a/mentos/src/libc/unistd/getppid.c b/mentos/src/libc/unistd/getppid.c deleted file mode 100644 index c881377..0000000 --- a/mentos/src/libc/unistd/getppid.c +++ /dev/null @@ -1,18 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file getppid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "types.h" -#include "syscall.h" - -pid_t getppid() -{ - pid_t ret; - - DEFN_SYSCALL0(ret, __NR_getppid); - - return ret; -} diff --git a/mentos/src/libc/unistd/mkdir.c b/mentos/src/libc/unistd/mkdir.c deleted file mode 100644 index 6ccad7a..0000000 --- a/mentos/src/libc/unistd/mkdir.c +++ /dev/null @@ -1,21 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file mkdir.c -/// @brief Make directory functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "syscall.h" -#include "unistd.h" -#include "errno.h" -#include "stat.h" - -int mkdir(const char *path, mode_t mode) -{ - ssize_t retval; - DEFN_SYSCALL2(retval, __NR_mkdir, path, mode); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} diff --git a/mentos/src/libc/unistd/nice.c b/mentos/src/libc/unistd/nice.c deleted file mode 100644 index dd6d8c9..0000000 --- a/mentos/src/libc/unistd/nice.c +++ /dev/null @@ -1,17 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file nice.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "syscall.h" - -int nice(int inc) -{ - ssize_t _res; - - DEFN_SYSCALL1(_res, __NR_nice, inc); - - return _res; -} diff --git a/mentos/src/libc/unistd/open.c b/mentos/src/libc/unistd/open.c deleted file mode 100644 index 20b1e71..0000000 --- a/mentos/src/libc/unistd/open.c +++ /dev/null @@ -1,22 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file open.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int open(const char *pathname, int flags, mode_t mode) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_open, pathname, flags, mode); - - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} diff --git a/mentos/src/libc/unistd/read.c b/mentos/src/libc/unistd/read.c deleted file mode 100644 index 396dafa..0000000 --- a/mentos/src/libc/unistd/read.c +++ /dev/null @@ -1,23 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file read.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -ssize_t read(int fd, void *buf, size_t nbytes) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_read, fd, buf, nbytes); - - if (retval < 0) { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/reboot.c b/mentos/src/libc/unistd/reboot.c deleted file mode 100644 index f1e347d..0000000 --- a/mentos/src/libc/unistd/reboot.c +++ /dev/null @@ -1,23 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file reboot.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int reboot(int magic1, int magic2, unsigned int cmd, void *arg) -{ - ssize_t retval; - - DEFN_SYSCALL4(retval, __NR_reboot, magic1, magic2, cmd, arg); - - if (retval < 0) { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/stat.c b/mentos/src/libc/unistd/stat.c deleted file mode 100644 index d301501..0000000 --- a/mentos/src/libc/unistd/stat.c +++ /dev/null @@ -1,21 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stat.c -/// @brief Stat functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "syscall.h" -#include "unistd.h" -#include "errno.h" -#include "stat.h" - -int stat(const char *path, stat_t *buf) -{ - ssize_t retval; - DEFN_SYSCALL2(retval, __NR_stat, path, buf); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} diff --git a/mentos/src/libc/unistd/vfork.c b/mentos/src/libc/unistd/vfork.c deleted file mode 100644 index f6c4f18..0000000 --- a/mentos/src/libc/unistd/vfork.c +++ /dev/null @@ -1,26 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file vfork.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "types.h" -#include "errno.h" -#include "syscall.h" -#include -#include -#include - -pid_t vfork() -{ - pid_t retval = 0; - - DEFN_SYSCALL0(retval, __NR_vfork); - - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} diff --git a/mentos/src/libc/unistd/waitpid.c b/mentos/src/libc/unistd/waitpid.c deleted file mode 100644 index 04320ec..0000000 --- a/mentos/src/libc/unistd/waitpid.c +++ /dev/null @@ -1,41 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file waitpid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "wait.h" -#include "errno.h" -#include "syscall.h" -#include "scheduler.h" - -pid_t wait(int *status) -{ - return waitpid(-1, status, 0); -} - -pid_t waitpid(pid_t pid, int *status, int options) -{ - pid_t retval; - - int _status = 0, *_status_ptr = &_status; - - do - { - DEFN_SYSCALL3(retval, __NR_waitpid, pid, _status_ptr, options); - } while (((*_status_ptr) != EXIT_ZOMBIE) && !(options && WNOHANG)); - - if (status != NULL) - { - (*status) = (*_status_ptr); - } - - if (retval < 0) - { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/write.c b/mentos/src/libc/unistd/write.c deleted file mode 100644 index 50c994f..0000000 --- a/mentos/src/libc/unistd/write.c +++ /dev/null @@ -1,25 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file write.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -ssize_t write(int fd, void *buf, size_t nbytes) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_write, fd, buf, nbytes); - - if (retval < 0) - { - errno = -retval; - retval = -1; - - } - - return retval; -} diff --git a/mentos/src/libc/vsprintf.c b/mentos/src/libc/vsprintf.c deleted file mode 100644 index 92554d3..0000000 --- a/mentos/src/libc/vsprintf.c +++ /dev/null @@ -1,787 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file vsprintf.c -/// @brief Print formatting routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "math.h" -#include "fcvt.h" -#include "ctype.h" -#include "string.h" -#include "stdarg.h" -#include "stdbool.h" - -#define CVTBUFSIZE 500 - -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_SIGN (1U << 6U) - -/* 'ftoa' conversion buffer size, this must be big enough to hold one converted - * float number including padded zeros (dynamically created on stack) - * default: 32 byte. - */ -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U -#endif - -/// The list of digits. -static char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; - -/// The list of uppercase digits. -static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -/// @brief Returns the index of the first non-integer character. -static int skip_atoi(const char **s) -{ - int i = 0; - do { - i = i * 10 + *((*s)++) - '0'; - } while (isdigit(**s)); - - return i; -} - -#if 0 -char *dtoa(char *str, - double num, - int32_t precision) -{ - // Handle special cases. - if (isnan(num)) - { - strcpy(str, "nan"); - } - else if (isinf(num)) - { - strcpy(str, "inf"); - } - else if (num == 0.0) - { - strcpy(str, "0"); - } - else - { - int digit, m, m1; - char *c = str; - int neg = (num < 0); - if (neg) - { - num = -num; - } - // Calculate magnitude. - m = log10(num); - int useExp = (m >= 14 || (neg && m >= 9) || m <= -9); - if (neg) - { - *(c++) = '-'; - } - // Set up for scientific notation. - if (useExp) - { - if (m < 0) - { - m -= 1.0; - } - num = num / pow(10.0, m); - m1 = m; - m = 0; - } - if (m < 1.0) - { - m = 0; - } - // Convert the number. - while (num > precision || m >= 0) - { - double weight = pow(10.0, m); - if (weight > 0 && !isinf(weight)) - { - digit = floor(num / weight); - num -= (digit * weight); - *(c++) = '0' + digit; - } - if (m == 0 && num > 0) - *(c++) = '.'; - m--; - } - if (useExp) - { - // Convert the exponent. - int i, j; - *(c++) = 'e'; - if (m1 > 0) - { - *(c++) = '+'; - } - else - { - *(c++) = '-'; - m1 = -m1; - } - m = 0; - while (m1 > 0) - { - *(c++) = '0' + m1 % 10; - m1 /= 10; - m++; - } - c -= m; - for (i = 0, j = m - 1; i < j; i++, j--) - { - // Swap without temporary. - c[i] ^= c[j]; - c[j] ^= c[i]; - c[i] ^= c[j]; - } - c += m; - } - *(c) = '\0'; - } - - return str; -} -#endif - -static char *number(char *str, long num, int base, int size, int32_t precision, - int type) -{ - char c, tmp[66]; - char *dig = digits; - - if (type & FLAGS_UPPERCASE) { - dig = upper_digits; - } - if (type & FLAGS_LEFT) { - type &= ~FLAGS_ZEROPAD; - } - if (base < 2 || base > 36) { - return 0; - } - - c = (type & FLAGS_ZEROPAD) ? '0' : ' '; - - // -------------------------------- - // Set the sign. - // -------------------------------- - char sign = 0; - if (type & FLAGS_SIGN) { - if (num < 0) { - sign = '-'; - num = -num; - size--; - } else if (type & FLAGS_PLUS) { - sign = '+'; - size--; - } else if (type & FLAGS_SPACE) { - sign = ' '; - size--; - } - } - // Sice I've removed the sign (if negative), i can transform it to unsigned. - uint32_t uns_num = (uint32_t)num; - - if (type & FLAGS_HASH) { - if (base == 16) { - size -= 2; - } else if (base == 8) { - size--; - } - } - - int32_t i = 0; - if (uns_num == 0) { - tmp[i++] = '0'; - } else { - while (uns_num != 0) { - tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; - uns_num = ((unsigned long)uns_num) / (unsigned)base; - } - } - - if (i > precision) { - precision = i; - } - size -= precision; - if (!(type & (FLAGS_ZEROPAD | FLAGS_LEFT))) { - while (size-- > 0) - *str++ = ' '; - } - if (sign) { - *str++ = sign; - } - - if (type & FLAGS_HASH) { - if (base == 8) - *str++ = '0'; - else if (base == 16) { - *str++ = '0'; - *str++ = digits[33]; - } - } - - if (!(type & FLAGS_LEFT)) { - while (size-- > 0) { - *str++ = c; - } - } - while (i < precision--) { - *str++ = '0'; - } - while (i-- > 0) { - *str++ = tmp[i]; - } - while (size-- > 0) { - *str++ = ' '; - } - - return str; -} - -static char *eaddr(char *str, unsigned char *addr, int size, int precision, - int type) -{ - (void)precision; - char tmp[24]; - char *dig = digits; - int i, len; - - if (type & FLAGS_UPPERCASE) { - dig = upper_digits; - } - - len = 0; - for (i = 0; i < 6; i++) { - if (i != 0) { - tmp[len++] = ':'; - } - tmp[len++] = dig[addr[i] >> 4]; - tmp[len++] = dig[addr[i] & 0x0F]; - } - - if (!(type & FLAGS_LEFT)) { - while (len < size--) { - *str++ = ' '; - } - } - - for (i = 0; i < len; ++i) { - *str++ = tmp[i]; - } - - while (len < size--) { - *str++ = ' '; - } - - return str; -} - -static char *iaddr(char *str, unsigned char *addr, int size, int precision, - int type) -{ - (void)precision; - char tmp[24]; - int i, n, len; - - len = 0; - for (i = 0; i < 4; i++) { - if (i != 0) { - tmp[len++] = '.'; - } - n = addr[i]; - - if (n == 0) { - tmp[len++] = digits[0]; - } else { - if (n >= 100) { - tmp[len++] = digits[n / 100]; - n = n % 100; - tmp[len++] = digits[n / 10]; - n = n % 10; - } else if (n >= 10) { - tmp[len++] = digits[n / 10]; - n = n % 10; - } - - tmp[len++] = digits[n]; - } - } - - if (!(type & FLAGS_LEFT)) { - while (len < size--) { - *str++ = ' '; - } - } - - for (i = 0; i < len; ++i) { - *str++ = tmp[i]; - } - - while (len < size--) { - *str++ = ' '; - } - - return str; -} - -static void cfltcvt(double value, char *buffer, char fmt, int precision) -{ - int decpt, sign, exp, pos; - char *digits = NULL; - char cvtbuf[CVTBUFSIZE]; - int capexp = 0; - int magnitude; - - if (fmt == 'G' || fmt == 'E') { - capexp = 1; - fmt += 'a' - 'A'; - } - - if (fmt == 'g') { - digits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf); - magnitude = decpt - 1; - if (magnitude < -4 || magnitude > precision - 1) { - fmt = 'e'; - precision -= 1; - } else { - fmt = 'f'; - precision -= decpt; - } - } - - if (fmt == 'e') { - digits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf); - - if (sign) { - *buffer++ = '-'; - } - *buffer++ = *digits; - if (precision > 0) { - *buffer++ = '.'; - } - memcpy(buffer, digits + 1, precision); - buffer += precision; - *buffer++ = capexp ? 'E' : 'e'; - - if (decpt == 0) { - if (value == 0.0) { - exp = 0; - } else { - exp = -1; - } - } else { - exp = decpt - 1; - } - - if (exp < 0) { - *buffer++ = '-'; - exp = -exp; - } else { - *buffer++ = '+'; - } - - buffer[2] = (char)((exp % 10) + '0'); - exp = exp / 10; - buffer[1] = (char)((exp % 10) + '0'); - exp = exp / 10; - buffer[0] = (char)((exp % 10) + '0'); - buffer += 3; - } else if (fmt == 'f') { - digits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf); - if (sign) { - *buffer++ = '-'; - } - if (*digits) { - if (decpt <= 0) { - *buffer++ = '0'; - *buffer++ = '.'; - for (pos = 0; pos < -decpt; pos++) { - *buffer++ = '0'; - } - while (*digits) { - *buffer++ = *digits++; - } - } else { - pos = 0; - while (*digits) { - if (pos++ == decpt) { - *buffer++ = '.'; - } - *buffer++ = *digits++; - } - } - } else { - *buffer++ = '0'; - if (precision > 0) { - *buffer++ = '.'; - for (pos = 0; pos < precision; pos++) { - *buffer++ = '0'; - } - } - } - } - - *buffer = '\0'; -} - -static void forcdecpt(char *buffer) -{ - while (*buffer) { - if (*buffer == '.') { - return; - } - if (*buffer == 'e' || *buffer == 'E') { - break; - } - - buffer++; - } - - if (*buffer) { - long n = (long)strlen(buffer); - while (n > 0) { - buffer[n + 1] = buffer[n]; - n--; - } - *buffer = '.'; - } else { - *buffer++ = '.'; - *buffer = '\0'; - } -} - -static void cropzeros(char *buffer) -{ - char *stop; - - while (*buffer && *buffer != '.') { - buffer++; - } - - if (*buffer++) { - while (*buffer && *buffer != 'e' && *buffer != 'E') { - buffer++; - } - stop = buffer--; - while (*buffer == '0') { - buffer--; - } - if (*buffer == '.') { - buffer--; - } - while ((*++buffer = *stop++)) - ; - } -} - -static char *flt(char *str, double num, int size, int precision, char fmt, - int flags) -{ - char tmp[80]; - char c, sign; - int n, i; - - // Left align means no zero padding. - if (flags & FLAGS_LEFT) - flags &= ~FLAGS_ZEROPAD; - - // Determine padding and sign char. - c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; - sign = 0; - if (flags & FLAGS_SIGN) { - if (num < 0.0) { - sign = '-'; - num = -num; - size--; - } else if (flags & FLAGS_PLUS) { - sign = '+'; - size--; - } else if (flags & FLAGS_SPACE) { - sign = ' '; - size--; - } - } - - // Compute the precision value. - if (precision < 0) { - // Default precision: 6. - precision = 6; - } else if (precision == 0 && fmt == 'g') { - // ANSI specified. - precision = 1; - } - - // Convert floating point number to text. - cfltcvt(num, tmp, fmt, precision); - - // '#' and precision == 0 means force a decimal point. - if ((flags & FLAGS_HASH) && precision == 0) { - forcdecpt(tmp); - } - - // 'g' format means crop zero unless '#' given. - if (fmt == 'g' && !(flags & FLAGS_HASH)) { - cropzeros(tmp); - } - - n = strlen(tmp); - - // Output number with alignment and padding. - size -= n; - if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { - while (size-- > 0) { - *str++ = ' '; - } - } - - if (sign) { - *str++ = sign; - } - - if (!(flags & FLAGS_LEFT)) { - while (size-- > 0) { - *str++ = c; - } - } - - for (i = 0; i < n; i++) { - *str++ = tmp[i]; - } - - while (size-- > 0) { - *str++ = ' '; - } - - return str; -} - -int vsprintf(char *str, const char *fmt, va_list args) -{ - int base; - char *tmp; - char *s; - - // Flags to number(). - int flags; - - // 'h', 'l', or 'L' for integer fields. - int qualifier; - - for (tmp = str; *fmt; fmt++) { - if (*fmt != '%') { - *tmp++ = *fmt; - - continue; - } - - // Process flags- - flags = 0; - repeat: - // This also skips first '%'. - fmt++; - switch (*fmt) { - case '-': - flags |= FLAGS_LEFT; - goto repeat; - case '+': - flags |= FLAGS_PLUS; - goto repeat; - case ' ': - flags |= FLAGS_SPACE; - goto repeat; - case '#': - flags |= FLAGS_HASH; - goto repeat; - case '0': - flags |= FLAGS_ZEROPAD; - goto repeat; - } - - // Get the width of the output field. - int32_t field_width; - field_width = -1; - - if (isdigit(*fmt)) { - field_width = skip_atoi(&fmt); - } else if (*fmt == '*') { - fmt++; - field_width = va_arg(args, int32_t); - if (field_width < 0) { - field_width = -field_width; - flags |= FLAGS_LEFT; - } - } - - /* Get the precision, thus the minimum number of digits for - * integers; max number of chars for from string. - */ - int32_t precision = -1; - if (*fmt == '.') { - ++fmt; - if (isdigit(*fmt)) { - precision = skip_atoi(&fmt); - } else if (*fmt == '*') { - ++fmt; - precision = va_arg(args, int); - } - if (precision < 0) { - precision = 0; - } - } - - // Get the conversion qualifier. - qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { - qualifier = *fmt; - fmt++; - } - - // Default base. - base = 10; - - switch (*fmt) { - case 'c': - if (!(flags & FLAGS_LEFT)) { - while (--field_width > 0) { - *tmp++ = ' '; - } - } - *tmp++ = va_arg(args, char); - while (--field_width > 0) { - *tmp++ = ' '; - } - continue; - - case 's': - s = va_arg(args, char *); - if (!s) { - s = ""; - } - - int32_t len = (int32_t)strnlen(s, (uint32_t)precision); - if (!(flags & FLAGS_LEFT)) { - while (len < field_width--) { - *tmp++ = ' '; - } - } - - int32_t it; - for (it = 0; it < len; ++it) { - *tmp++ = *s++; - } - while (len < field_width--) { - *tmp++ = ' '; - } - continue; - - case 'p': - if (field_width == -1) { - field_width = 2 * sizeof(void *); - flags |= FLAGS_ZEROPAD; - } - tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, - field_width, precision, flags); - continue; - - case 'n': - if (qualifier == 'l') { - long *ip = va_arg(args, long *); - *ip = (tmp - str); - } else { - int *ip = va_arg(args, int *); - *ip = (tmp - str); - } - continue; - - case 'A': - flags |= FLAGS_UPPERCASE; - break; - - case 'a': - if (qualifier == 'l') - tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, - precision, flags); - else - tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, - precision, flags); - continue; - - // Integer number formats - set up the flags and "break". - case 'o': - base = 8; - break; - - case 'X': - flags |= FLAGS_UPPERCASE; - break; - - case 'x': - base = 16; - break; - - case 'd': - case 'i': - flags |= FLAGS_SIGN; - - case 'u': - break; - case 'E': - case 'G': - case 'e': - case 'f': - case 'g': - tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, - flags | FLAGS_SIGN); - continue; - default: - if (*fmt != '%') - *tmp++ = '%'; - if (*fmt) - *tmp++ = *fmt; - else - --fmt; - continue; - } - - if (flags & FLAGS_SIGN) { - long num; - if (qualifier == 'l') { - num = va_arg(args, long); - } else if (qualifier == 'h') { - num = va_arg(args, short); - } else { - num = va_arg(args, int); - } - tmp = number(tmp, num, base, field_width, precision, flags); - } else { - unsigned long num; - if (qualifier == 'l') { - num = va_arg(args, unsigned long); - } else if (qualifier == 'h') { - num = va_arg(args, unsigned short); - } else { - num = va_arg(args, unsigned int); - } - tmp = number(tmp, num, base, field_width, precision, flags); - } - } - - *tmp = '\0'; - return tmp - str; -} - -int sprintf(char *str, const char *fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vsprintf(str, fmt, args); - va_end(args); - - return n; -} diff --git a/mentos/src/mem/buddysystem.c b/mentos/src/mem/buddysystem.c index ad3dac2..900087c 100644 --- a/mentos/src/mem/buddysystem.c +++ b/mentos/src/mem/buddysystem.c @@ -1,173 +1,417 @@ +/// MentOS, The Mentoring Operating system project /// @file buddysystem.c /// @brief Buddy System. -/// @date Apr 2019. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[BUDDY ]" #include "buddysystem.h" -#include "debug.h" +#include "paging.h" #include "assert.h" +#include "debug.h" +#include "panic.h" + +/// @brief Cache level low limit after which allocation starts. +#define LOW_WATERMARK_LEVEL 10 +/// @brief Cache level high limit, above it deallocation happens. +#define HIGH_WATERMARK_LEVEL 70 +/// @brief Cache level midway limit. +#define MID_WATERMARK_LEVEL ((LOW_WATERMARK_LEVEL + HIGH_WATERMARK_LEVEL) / 2) + +/// @brief Bitwise flags for identifying page types and statuses. +enum bb_flag { + FREE_PAGE = 0, ///< Bit position that identifies when a page is free or not. + ROOT_PAGE = 1 ///< Bit position that identifies when a page is the root page. +}; + +/// @brief Sets the given flag in the page. +/// @param page The page of which we want to modify the flag. +/// @param flag The flag we want to set. +static inline void __bb_set_flag(bb_page_t *page, int flag) +{ + set_bit(flag, &page->flags); +} + +/// @brief Clears the given flag from the page. +/// @param page The page of which we want to modify the flag. +/// @param flag The flag we want to clear. +static inline void __bb_clear_flag(bb_page_t *page, int flag) +{ + clear_bit(flag, &page->flags); +} + +/// @brief Gets the given flag from the page. +/// @param page The page of which we want to modify the flag. +/// @param flag The flag we want to test. +/// @return 1 if the bit is set, 0 otherwise. +static inline int __bb_test_flag(bb_page_t *page, int flag) +{ + return test_bit(flag, &page->flags); +} + +/// @brief Returns the page at the given index, starting from the given base. +/// @param instance The buddy system instance we are working with. +/// @param base The base page from which we move at the given index. +/// @param index The number of pages we want to move from the base. +/// @return The page we found. +static inline bb_page_t *__get_page_from_base(bb_instance_t *instance, bb_page_t *base, unsigned int index) +{ + return (bb_page_t *)(((uint32_t)base) + instance->pgs_size * index); +} + +/// @brief Returns the page at the given index, starting from the first page of the BB system. +/// @param instance The buddy system instance we are working with. +/// @param index The number of pages we want to move from the first page. +/// @return The page we found. +static inline bb_page_t *__get_page_at_index(bb_instance_t *instance, unsigned int index) +{ + return __get_page_from_base(instance, instance->base_page, index); +} + +/// @brief Computes the number of pages separating the two pages (begin, end). +/// @param instance The buddy system instance we are working with. +/// @param begin The first page. +/// @param end The second page. +/// @return The number of pages between begin and end. +static inline unsigned int __get_page_range(bb_instance_t *instance, bb_page_t *begin, bb_page_t *end) +{ + return (((uintptr_t)end) - ((uintptr_t)begin)) / instance->pgs_size; +} /// @brief Get the buddy index of a page. +/// @details +/// ----------------------- xor ----------------------- +/// | page_idx ^ (1UL << order) = buddy_idx | +/// | 1 1 0 | +/// | 0 1 1 | +/// --------------------------------------------------- +/// If the bit of page_idx that corresponds to the block +/// size, is 1, then we have to take the block on the +/// left (0), otherwise we have to take the block on the right (1). /// @param page_idx A page index. /// @param order The logarithm of the size of the block. /// @return The page index of the buddy of page. -static unsigned long get_buddy_idx(unsigned long page_idx, unsigned int order) +static inline unsigned long __get_buddy_at_index(unsigned long page_idx, unsigned int order) { - /* Get the index of the buddy block. - * - * ----------------------- xor ----------------------- - * | page_idx ^ (1UL << order) = buddy_idx | - * | 1 1 0 | - * | 0 1 1 | - * --------------------------------------------------- - * - * If the bit of page_idx that corresponds to the block - * size, is 1, then we have to take the block on the - * left (0), otherwise we have to take the block on the right (1). - */ - unsigned long buddy_idx = page_idx ^ (1UL << order); - - return buddy_idx; + return (page_idx ^ (1UL << order)); } - -page_t *bb_alloc_pages(zone_t *zone, unsigned int order) +static inline bool_t __page_is_buddy(bb_page_t *page, unsigned int order) { - page_t *page = NULL; - free_area_t *area = NULL; + return __bb_test_flag(page, FREE_PAGE) && (page->order == order); +} - // Search for a free_area_t with at least one available block of pages. - unsigned int current_order; - for (current_order = order; current_order < MAX_ORDER; ++current_order) { - // get the free_area_t at index 'current_order' - area = // ... +bb_page_t *bb_alloc_pages(bb_instance_t *instance, unsigned int order) +{ + bb_page_t *page = NULL; + bb_free_area_t *area = NULL; - // check if area is not empty (is there at least a block here?) - if (!list_head_empty(&/*...*/)) { - goto block_found; - } - } + // Cyclic search through each list for an available block, + // starting with the list for the requested order and + // continuing if necessary to larger orders. + unsigned int current_order; + for (current_order = order; current_order < MAX_BUDDYSYSTEM_GFP_ORDER; ++current_order) { + // Get the free_area_t at index 'current_order'. + area = /* ... */; + // Check if area is not empty, which means that there is at least a block inside the list. + if (!list_head_empty(&/* ... */)) { + goto block_found; + } + } - // No suitable free block has been found. - return NULL; + // No suitable free block has been found. + return NULL; block_found: - // Get a block of pages from the found free_area_t. - // Here we have to manage pages. Recall, free_area_t collects the first - // page_t of each free block of 2^order contiguous page frames. + // Get a block of pages from the found free_area_t. Here we have to manage + // pages. Recall, free_area_t collects the first page_t of each free block + // of 2^order contiguous page frames. + page = list_entry(/* ... */, /* ... */, /* ... */); - page = list_entry(/*...*/); + // Remove the descriptor of its first page frame. + list_head_del(&/* ... */); - // Remove page from the list_head in the found free_area_t. - list_head_del(/*...*/); + // Set the page as allocated, thus, remove the flag FREE_PAGE. + __bb_clear_flag(/* ... */, /* ... */); - // Set page as taken. - page->_count = // ... - page->private = 0; + // Check that the page is a ROOT_PAGE + assert(__bb_test_flag(/* ... */, /* ... */)); - // Decrease the number of free blocks in the found free_area_t. - // ... + // Decrease the number of free block of the free_area_t. + /* ... */ -= 1; - /* We found a block with 2^k page frames to satisfy a request - * of 2^h page frames. If h < k, then we can split the block with 2^k - * pages until it is large 2^h pages, namely k == h. - */ + // Found a block of 2^k page frames, to satisfy a request + // for 2^h page frames (h < k) the program allocates + // the first 2^h page frames and iteratively reassigns + // the last 2^k – 2^h page frames to the free_area lists + // that have indexes between h and k. + unsigned int size = 1UL << current_order; + while (current_order > order) { + // At each loop, we have to set free the right half of the found block. - // We can exploit size(=2^k) to have at each loop the address the page that - // resides in the midle of the found block. - unsigned int size = 1 << current_order; - while (current_order > order) { + // Refer to the lower free_area_t and order. + area--; + current_order--; - // At each loop, we have to set free the right half of the found block. + // Split the block and set free the second half. + size >>= /* ... */; - // Split the block size in half - size = // ... + // Get the address of the page in the midle of the found block. + bb_page_t *buddy = __get_page_from_base(/* ... */, /* ... */, /* ... */); - // get the address of the page in the midle of the found block. - page_t *buddy = // ... + // Check that the buddy is free and not a root page. + assert(/* ... */ &&!/* ... */); - // set the order of pages after the buddy page_t (the field 'private') - // ... + // Insert buddy as first element in the list of available blocks (free_list). + list_head_add(&/* ... */.siblings, &/* ... */); - // get the free_area_t collecting blocks with 2^(k-1) page frames - area = // ... + // Increase the number of free block of the free_area_t. + /* ... */ += 1; - // add the buddy block in its list of available blocks - // ... + // Save the order of the buddy. + /* ... */ = current_order; - // Increase the number of free blocks of the free_area_t. - // ... - } + // Set the buddy as a root page. + /* ... */; + } - buddy_system_dump(zone); + // Set the order of the page we are returning. + /* ... */; - return page; + // Set the page as root page. + /* ... */; + + // Clear the free-page status from the page. + /* ... */; + +#if 0 + buddy_system_dump(instance); +#endif + return page; } -void bb_free_pages(zone_t *zone, page_t *page, unsigned int order) +void bb_free_pages(bb_instance_t *instance, bb_page_t *page) { - // Take the first page descriptor of the zone. - page_t *base = zone->zone_mem_map; + // Take the first page descriptor of the zone. + bb_page_t *base = instance->base_page; + // Take the page frame index of page compared to the zone. + unsigned long page_idx = __get_page_range(instance, base, page); + // Set the page freed, but do not set the private + // field because we want to try to merge. FIXME: Add this line on zone page deallocate! + // set_page_count(page, -1); - // Take the page frame index of page compared to the zone. - unsigned long page_idx = page - base; + unsigned int order = page->order; - // Set the given page as free - page->_count = // ... + // Check that the page is free, or that it is not a root page. + if (/* ... */ || !/* ... */) { + kernel_panic("Double deallocation in buddy system!"); + } - // At each loop, check if the buddy block can be merged with page. - while (order < MAX_ORDER - 1) { - // Get the index of the buddy block of page. - unsigned long buddy_idx = get_buddy_idx(page_idx, order); - // Get the page_t of the buddy block, namely its first page frame. - page_t *buddy = base + buddy_idx; + // Performs a cycle that starts with the smallest + // order block and moves up to the top order. + while (order < MAX_BUDDYSYSTEM_GFP_ORDER - 1) { + // Get the base page index. + page = __get_page_from_base(instance, base, page_idx); - // If the buddy is free and it has the same size of page, then - // they can be merged. Otherwise, we can stop the while-loop and insert - // page in the list of free blocks. + // Get the index of the buddy. + unsigned long buddy_idx = __get_buddy_at_index(page_idx, order); - if (!(/*...it is free...*/ && /*...they have the same size...*/)) { - break; - } + // Return the page descriptor of the buddy. + bb_page_t *buddy = __get_page_from_base(instance, base, buddy_idx); - // we are here only if buddy is free and can be merged with page. + // If the page is not a buddy, stop the loop. So it should not be free + // and having the same size. + if (!(/* ... */ &&/* ... */)) + break; - // remove buddy from the list of available blocks in its free_area_t - // .... + // If buddy is free, remove buddy from the current free list, + // because then the coalesced block will be inserted on a + // upper order. + /* ... */; - // Decrease the number of free block of the current free_area_t. - // ... + // Decrease the number of free block of the current free_area_t. + /* ... */; - // buddy no longer represents a free block, so clear the private field. - buddy->private = 0; + // Get the page that gets forgotten, it's always the one on the right (the greatest) + bb_page_t *forgot_page = buddy > page ? buddy : page; + + // Clear the root flag from the forgotten page. + /* ... */; - // Update the page index with the index of the coalesced block. - // ... + // Set the forgotten page as a free page. + /* ... */; - order++; - } + // Update the page index with the index of the coalesced block. + /* ... */ &= /* ... */; - // The coalesced block is the result of the merging procedure. - page_t *coalesced = base + page_idx; + order++; + } - // Update the field private to set the size. - coalesced->private = // ... + // Take the coalesced block with the order reached up. + bb_page_t *coalesced = __get_page_from_base(instance, base, page_idx); - // Insert the coalesced block in the free_area as available block - // ... + // Update the order of the coalesced page. + /* ... */; + + // Set it to be a root page and a free page + /* ... */; + /* ... */; - // Increase the number of free blocks of the free_area. - // ... + // Insert coalesced as first element in the free list. + list_head_add(&/* ... */, &/* ... */); + + // Increase the number of free block of the free_area_t. + /* ... */; - buddy_system_dump(zone); +#if 0 + buddy_system_dump(instance); +#endif } -void buddy_system_dump(zone_t *zone) +void buddy_system_init(bb_instance_t *instance, + const char *name, + void *pages_start, + uint32_t bbpage_offset, + uint32_t pages_stride, + uint32_t pages_count) { - // Print free_list's size of each area of the zone. - dbg_print("Zone\t%s\t", zone->name); - for (int order = 0; order < MAX_ORDER; order++) { - free_area_t *area = zone->free_area + order; - dbg_print("%d\t", area->nr_free); - } - dbg_print("\n"); + // Compute the base bb page of the struct + instance->base_page = ((bb_page_t *)(((uint32_t)pages_start) + bbpage_offset)); + + // Save all needed page info + instance->bbpg_offset = bbpage_offset; + instance->pgs_size = pages_stride; + instance->size = pages_count; + instance->name = name; + + // Initialize all pages + for (uint32_t i = 0; i < pages_count; i++) { + bb_page_t *page = __get_page_at_index(instance, i); + page->flags = 0; + // Mark page as free + __bb_set_flag(page, FREE_PAGE); + // Initialize siblings list + list_head_init(&(page->location.siblings)); + } + + // Initialize the free_lists of each area of the zone. + for (unsigned int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; order++) { + bb_free_area_t *area = instance->free_area + order; + area->nr_free = 0; + list_head_init(&area->free_list); + } + + // Current base page descriptor of the zone. + bb_page_t *page = instance->base_page; + // Address of the last page descriptor of the zone. + bb_page_t *last_page = __get_page_from_base(instance, page, instance->size); + + // Get the free area collecting the larges block of page frames. + const unsigned int order = MAX_BUDDYSYSTEM_GFP_ORDER - 1; + bb_free_area_t *area = instance->free_area + order; + + // Add all zone's pages to the largest free area block. + uint32_t block_size = 1UL << order; + while ((page + block_size) <= last_page) { + // Save the order of the page. + page->order = order; + // Set the page as root + __bb_set_flag(page, ROOT_PAGE); + + // Insert page as first element in the list. + list_head_add_tail(&page->location.siblings, &area->free_list); + // Increase the number of free block of the free_area_t. + area->nr_free++; + + page = __get_page_from_base(instance, page, block_size); + } + + assert(page == last_page && + "Memory size is not aligned to MAX_ORDER size!"); +} + +void buddy_system_dump(bb_instance_t *instance) +{ + // Print free_list's size of each area of the zone. + pr_debug("Zone %-12s ", instance->name); + for (int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; order++) { + bb_free_area_t *area = instance->free_area + order; + pr_debug("%2d ", area->nr_free); + } + pr_debug(": %s\n", to_human_size(buddy_system_get_free_space(instance))); +} + +unsigned long buddy_system_get_total_space(bb_instance_t *instance) +{ + return instance->size * PAGE_SIZE; +} + +unsigned long buddy_system_get_free_space(bb_instance_t *instance) +{ + unsigned int size = 0; + for (int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; ++order) + size += instance->free_area[order].nr_free * (1UL << order) * PAGE_SIZE; + return size; +} + +unsigned long buddy_system_get_cached_space(bb_instance_t *instance) +{ + unsigned int size = 0; + for (int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; ++order) + size += instance->free_pages_cache_size * PAGE_SIZE; + return size; +} + +static void __cache_extend(bb_instance_t *instance, int count) +{ + for (int i = 0; i < count; i++) { + bb_page_t *page = bb_alloc_pages(instance, 0); + list_head_add(&page->location.cache, &instance->free_pages_cache_list); + instance->free_pages_cache_size++; + } +} + +static void __cache_shrink(bb_instance_t *instance, int count) +{ + for (int i = 0; i < count; i++) { + list_head *page_list = list_head_pop(&instance->free_pages_cache_list); + bb_page_t *page = list_entry(page_list, bb_page_t, location.cache); + bb_free_pages(instance, page); + instance->free_pages_cache_size--; + } +} + +static bb_page_t *__cached_alloc(bb_instance_t *instance) +{ + if (instance->free_pages_cache_size < LOW_WATERMARK_LEVEL) { + // Request pages from the buddy system + uint32_t pages_to_request = MID_WATERMARK_LEVEL - instance->free_pages_cache_size; + __cache_extend(instance, pages_to_request); + } + list_head *page_list = list_head_pop(&instance->free_pages_cache_list); + bb_page_t *page = list_entry(page_list, bb_page_t, location.cache); + return page; +} + +static void __cached_free(bb_instance_t *instance, bb_page_t *page) +{ + list_head_add(&page->location.cache, &instance->free_pages_cache_list); + + if (instance->free_pages_cache_size > HIGH_WATERMARK_LEVEL) { + // Free pages to the buddy system + uint32_t pages_to_free = instance->free_pages_cache_size - MID_WATERMARK_LEVEL; + __cache_shrink(instance, pages_to_free); + } +} + +bb_page_t *bb_alloc_page_cached(bb_instance_t *instance) +{ + return __cached_alloc(instance); +} + +void bb_free_page_cached(bb_instance_t *instance, bb_page_t *page) +{ + __cached_free(instance, page); } diff --git a/mentos/src/mem/kheap.c b/mentos/src/mem/kheap.c index 244e770..12adba4 100644 --- a/mentos/src/mem/kheap.c +++ b/mentos/src/mem/kheap.c @@ -1,93 +1,77 @@ /// MentOS, The Mentoring Operating system project /// @file kheap.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Change the header. +#define __DEBUG_HEADER__ "[KHEAP ]" + #include "kheap.h" #include "math.h" #include "debug.h" #include "string.h" #include "paging.h" #include "assert.h" +#include "list_head.h" -#define PAGE_SIZE 4096 +/// Overhead given by the block_t itself. +#define OVERHEAD sizeof(block_t) +/// Align the given address. +#define ADDR_ALIGN(addr) ((((uint32_t)(addr)) & 0xFFFFF000) + 0x1000) +/// Checks if the given address is aligned. +#define IS_ALIGN(addr) ((((uint32_t)(addr)) & 0x00000FFF) == 0) +/// Returns a rounded up, away from zero, to the nearest multiple of b. +#define CEIL(NUMBER, BASE) (((NUMBER) + (BASE)-1) & ~((BASE)-1)) +/// User heap initial size ( 1 Megabyte). +#define UHEAP_INITIAL_SIZE (1 * M) -// 16 byte. -// #define OVERHEAD (sizeof(unsigned int) + sizeof(block_t)) -#define OVERHEAD sizeof(block_t) -#define ADDR_ALIGN(addr) ((((uint32_t)(addr)) & 0xFFFFF000) + 0x1000) -#define IS_ALIGN(addr) ((((uint32_t)(addr)) & 0x00000FFF) == 0) +/// @brief Identifies a block of memory. +typedef struct block_t { + /// @brief Identifies the side of the block and also if it is free or allocated. + /// @details + /// | 31 bit | 1 bit | + /// | first bits of real size | free/alloc | + /// To calculate the real size, set to zero the last bit + unsigned int size; + /// Pointer to the next free block. + struct block_t *nextfree; + /// Pointer to the next block. + struct block_t *next; +} block_t; +/// Kernel heap section. +static vm_area_struct_t kernel_heap; +/// Top of the kernel heap. +static uint32_t kernel_heap_top; -//============= Heap memory descriptor ========================================= -// Kernel heap section. -static struct vm_area_struct kernel_heap; -// Top of heap. -static uint32_t kernel_heap_curr; -static bool_t kheap_enabled = false; -//============================================================================== - -static void *__do_brk(uint32_t *pointer_heap_curr, - struct vm_area_struct *heap, - int increment) -{ - assert(pointer_heap_curr && "Pointer to heap curr is NULL."); - assert(heap && "Heap do not exist."); - - uint32_t heap_curr = *pointer_heap_curr; - uint32_t old_heap_curr = heap_curr; - - uint32_t heap_end = heap->vm_end; - -// dbg_print("BRK> %s: heap_start: %p, free space: %d\n", -// (heap == &kernel_heap)? "KERNEL" : "USER", -// heap->vm_start, heap_end - heap_curr); - - if (increment > 0) - { - uint32_t new_boundary = heap_curr + increment; - /* If new size is smaller or equal to end, simply add size to - * kernel_heap_curr and return the old kernel_heap_curr. - */ - if (new_boundary <= heap_end) - { - *pointer_heap_curr = new_boundary; - - return (void *) old_heap_curr; - } - } - - return NULL; -} - -/// @brief Given the field size in a Block(which contain free/alloc -/// information), extract the size. +/// @brief Given the field size in a Block(which contain free/alloc +/// information), extract the size. /// @param size /// @return -static inline uint32_t get_real_size(uint32_t size) +static inline uint32_t blkmngr_get_real_size(uint32_t size) { return (size >> 1U) << 1U; } /// @brief Sets the free/alloc bit of the size field. -static inline void blkmngr_set_free(uint32_t *size, bool_t x) +static inline void blkmngr_set_free(uint32_t *size, int x) { - (*size) = (x) ? ((*size) | 1U) - : ((*size) & 0xFFFFFFFE); + (*size) = (x) ? ((*size) | 1U) : ((*size) & 0xFFFFFFFE); } /// @brief Checks if a block is freed or allocated. -static inline bool_t blkmngr_is_free(block_t *block) +static inline int blkmngr_is_free(block_t *block) { - if (block == NULL) return false; + if (block == NULL) + return 0; return (block->size & 1U); } /* * /// @brief Checks if it is the end block. - * static inline bool_t blkmngr_is_end(block_t * block) + * static inline int blkmngr_is_end(block_t * block) * { * assert(block && "Received null block."); * assert(tail && "Tail has not set."); @@ -100,11 +84,11 @@ static inline bool_t blkmngr_is_free(block_t *block) /// @param block The given block. /// @param size The size to check /// @return -static inline bool_t blkmngr_does_it_fit(block_t *block, uint32_t size) +static inline int blkmngr_does_it_fit(block_t *block, uint32_t size) { assert(block && "Received null block."); - return (block->size >= get_real_size(size)) && blkmngr_is_free(block); + return (block->size >= blkmngr_get_real_size(size)) && blkmngr_is_free(block); } /// @brief Removes the block from freelist. @@ -113,20 +97,16 @@ static inline void blkmngr_remove_from_freelist(block_t *block, uint32_t *freeli assert(block && "Received null block."); assert(freelist && "Freelist is a null pointer."); - block_t *first_free_block = (block_t *) *freelist; + block_t *first_free_block = (block_t *)*freelist; assert(first_free_block && "Freelist is empty."); - if (block == first_free_block) - { - *freelist = (uint32_t) block->nextfree; - } - else - { + if (block == first_free_block) { + *freelist = (uint32_t)block->nextfree; + } else { block_t *prev = first_free_block; while (prev != NULL && prev->nextfree != block) prev = prev->nextfree; - if (prev) - { + if (prev) { prev->nextfree = block->nextfree; } } @@ -139,30 +119,26 @@ static inline void blkmngr_add_to_freelist(block_t *block, uint32_t *freelist) { assert(block && "Received null block."); assert(freelist && "Freelist is a null pointer."); - block_t * first_free_block = (block_t *) *freelist; - block->nextfree = first_free_block; - *freelist = (uint32_t) block; + block_t *first_free_block = (block_t *)*freelist; + block->nextfree = first_free_block; + *freelist = (uint32_t)block; } /// @brief Find the best fitting block in the memory pool. static inline block_t *blkmngr_find_best_fitting(uint32_t size, uint32_t *freelist) { assert(freelist && "Freelist is a null pointer."); - block_t *first_free_block = (block_t *) *freelist; + block_t *first_free_block = (block_t *)*freelist; - if (first_free_block == NULL) - { + if (first_free_block == NULL) { return NULL; } block_t *best_fitting = NULL; - for (block_t * current = first_free_block; current; current = current->nextfree) - { - if (!blkmngr_does_it_fit(current, size)) - { + for (block_t *current = first_free_block; current; current = current->nextfree) { + if (!blkmngr_does_it_fit(current, size)) { continue; } - if ((best_fitting == NULL) || (current->size < best_fitting->size)) - { + if ((best_fitting == NULL) || (current->size < best_fitting->size)) { best_fitting = current; } } @@ -175,16 +151,16 @@ static inline block_t *blkmngr_get_previous_block(block_t *block, uint32_t *head assert(block && "Received null block."); assert(head && "The head of the list is not set."); - block_t *head_block = (block_t *) *head; + block_t *head_block = (block_t *)*head; assert(head_block && "The head of the list is not set."); - if (block == head_block) - { + if (block == head_block) { return NULL; } block_t *prev = head_block; - while (prev->next != block) - { + + // FIXME: Sometimes enters infinite loop! + while (prev->next != block) { prev = prev->next; } @@ -197,21 +173,87 @@ static inline block_t *blkmngr_get_next_block(block_t *block, uint32_t *tail) assert(block && "Received null block."); assert(tail && "The tail of the list is not set."); - block_t *tail_block = (block_t *) *tail; + block_t *tail_block = (block_t *)*tail; assert(tail_block && "The head of the list is not set."); - if (block == tail_block) - { + if (block == tail_block) { return NULL; } return block->next; } -/// @brief Allocates size bytes of uninitialized storage. -static void *__do_malloc(struct vm_area_struct *heap, size_t size) +/// @brief Find the current user heap. +/// @return The heap structure if heap exists, otherwise NULL. +static vm_area_struct_t *__find_user_heap() { - if (size == 0) return NULL; + // Get the memory descriptor of the current process. + task_struct *current_task = scheduler_get_current_process(); + if (current_task == NULL) { + pr_emerg("There is no current task!\n"); + return NULL; + } + mm_struct_t *current_mm = current_task->mm; + if (current_mm == NULL) { + pr_emerg("The mm_struct of the current task is not initialized!\n"); + return NULL; + } + // Get the starting address of the heap. + uint32_t start_heap = current_mm->start_brk; + // If not set return NULL. + if (start_heap == 0) { + return NULL; + } + // Otherwise find the respective heap segment. + vm_area_struct_t *segment = NULL; + list_for_each_decl(it, ¤t_mm->mmap_list) + { + segment = list_entry(it, vm_area_struct_t, vm_list); + if (segment->vm_start == start_heap) { + return segment; + } + } + return NULL; +} + +/// @brief Extends the provided heap of the given increment. +/// @param heap_top Current top of the heap. +/// @param heap Pointer to the heap. +/// @param increment Increment to the heap. +/// @return Pointer to the old top of the heap, ready to be used. +static void *__do_brk(uint32_t *heap_top, vm_area_struct_t *heap, int increment) +{ + assert(heap_top && "Pointer to the current top of the heap is NULL."); + assert(heap && "Pointer to the heap is NULL."); + // pr_default("BRK> %s: heap_start: %p, free space: %d\n", + // (heap == &kernel_heap)? "KERNEL" : "USER", + // heap->vm_start, heap_end - heap_curr); + if (increment > 0) { + // Compute the new boundary. + uint32_t new_boundary = *heap_top + increment; + // If new boundary is smaller or equal to end, simply + // update the heap_top to the new boundary and return + // the old heap_top. + if (new_boundary <= heap->vm_end) { + // Save the old top of the heap. + uint32_t old_heap_top = *heap_top; + // Overwrite the top of the heap. + *heap_top = new_boundary; + // Return the old top of the heap. + return (void *)old_heap_top; + } + } + return NULL; +} + +/// @brief Allocates size bytes of uninitialized storage. +/// @param heap Heap from which we get the unallocated memory. +/// @param size Size of the desired memory area. +/// @return Pointer to the allocated memory area. +static void *__do_malloc(vm_area_struct_t *heap, size_t size) +{ + if (size == 0) + return NULL; // Get: // 1) First memory block. @@ -222,18 +264,18 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) // block_t *freelist = NULL; // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); + uint32_t *head = (uint32_t *)(heap->vm_start); + uint32_t *tail = (uint32_t *)(heap->vm_start + sizeof(block_t *)); + uint32_t *freelist = (uint32_t *)(heap->vm_start + 2 * sizeof(block_t *)); // assert(head && tail && freelist && "Heap block lists point to null."); // We will use these others in reading. - block_t *head_block = (block_t *) *head; - block_t *tail_block = (block_t *) *tail; + block_t *head_block = (block_t *)*head; + block_t *tail_block = (block_t *)*tail; // block_t *first_free_block = (block_t *) *freelist; // Calculate real size that's used, round it to multiple of 16. - uint32_t rounded_size = ceil(size, 16); + uint32_t rounded_size = CEIL(size, 16); // The block size takes into account also the block_t overhead. uint32_t block_size = rounded_size + OVERHEAD; @@ -241,39 +283,33 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) // best-fit node when there is more than one such node in tree. block_t *best_fitting = blkmngr_find_best_fitting(rounded_size, freelist); - if (best_fitting != NULL) - { + if (best_fitting != NULL) { // and! put a SIZE to the last four byte of the chunk - void *block_ptr = (void *) best_fitting; + char *block_ptr = (void *)best_fitting; // Store a pointer to the next block. void *stored_next_block = blkmngr_get_next_block(best_fitting, tail); // Get the size of the chunk. - uint32_t chunk_size = get_real_size(best_fitting->size) + OVERHEAD; + uint32_t chunk_size = blkmngr_get_real_size(best_fitting->size) + OVERHEAD; // Get what's left. uint32_t remaining_size = chunk_size - block_size; // Get the real size. - uint32_t real_size = (remaining_size < (8 + OVERHEAD)) ? chunk_size - : block_size; + uint32_t real_size = (remaining_size < (8 + OVERHEAD)) ? chunk_size : block_size; // Set the size of the best fitting block. best_fitting->size = real_size - OVERHEAD; // Set the content of the block as free. - blkmngr_set_free(&(best_fitting->size), false); + blkmngr_set_free(&(best_fitting->size), 0); // Store the base pointer. void *base_ptr = block_ptr; - block_ptr = (void *) (block_ptr + real_size); + block_ptr = (char *)block_ptr + real_size; - if (remaining_size < (8 + OVERHEAD)) - { + if (remaining_size < (8 + OVERHEAD)) { goto no_split; - } - else if (remaining_size >= (8 + OVERHEAD)) - { - if (blkmngr_is_free(stored_next_block)) - { + } else if (remaining_size >= (8 + OVERHEAD)) { + if (blkmngr_is_free(stored_next_block)) { // Choice b) merge! // Gather info about next block - void *nextblock = stored_next_block; + void *nextblock = stored_next_block; block_t *n_nextblock = nextblock; /* Remove next from list because it no longer exists(just * unlink it) @@ -281,78 +317,66 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) blkmngr_remove_from_freelist(n_nextblock, freelist); // Merge! - block_t *t = block_ptr; - t->size = remaining_size + get_real_size(n_nextblock->size); - blkmngr_set_free(&(t->size), true); + block_t *t = (block_t *)block_ptr; + t->size = remaining_size + blkmngr_get_real_size(n_nextblock->size); + blkmngr_set_free(&(t->size), 1); t->next = blkmngr_get_next_block(stored_next_block, tail); - if (nextblock == tail_block) - { + if (nextblock == tail_block) { // I don't want to set it to tail now, instead, reclaim it - *tail = (uint32_t) t; - // int reclaimSize = get_real_size(t->size) + OVERHEAD; + *tail = (uint32_t)t; + // int reclaimSize = blkmngr_get_real_size(t->size) + OVERHEAD; // ksbrk(-reclaimSize); // goto no_split; } // then add merged one into the front of the list blkmngr_add_to_freelist(t, freelist); - } - else - { + } else { // Choice a) seperate! - block_t *putThisBack = block_ptr; - putThisBack->size = remaining_size - OVERHEAD; - blkmngr_set_free(&(putThisBack->size), true); + block_t *putThisBack = (block_t *)block_ptr; + putThisBack->size = remaining_size - OVERHEAD; + blkmngr_set_free(&(putThisBack->size), 1); putThisBack->next = stored_next_block; - if (base_ptr == tail_block) - { - *tail = (uint32_t) putThisBack; - // int reclaimSize = get_real_size(putThisBack->size) +OVERHEAD; + if (base_ptr == tail_block) { + *tail = (uint32_t)putThisBack; + // int reclaimSize = blkmngr_get_real_size(putThisBack->size) +OVERHEAD; // ksbrk(-reclaimSize); // goto no_split; } blkmngr_add_to_freelist(putThisBack, freelist); } - ((block_t *) base_ptr)->next = block_ptr; + ((block_t *)base_ptr)->next = (block_t *)block_ptr; } - no_split: + no_split: // Remove the block from the free list. blkmngr_remove_from_freelist(base_ptr, freelist); - return base_ptr + sizeof(block_t); - } - else - { + return (char *)base_ptr + sizeof(block_t); + } else { uint32_t realsize = block_size; block_t *ret; - if (heap == &kernel_heap) - { + if (heap == &kernel_heap) { ret = ksbrk(realsize); - } - else - { + } else { ret = usbrk(realsize); } assert(ret != NULL && "Heap is running out of space\n"); - if (!head_block) - { - *head = (uint32_t) ret; - } - else - { + if (!head_block) { + *head = (uint32_t)ret; + } else { tail_block->next = ret; } - ret->next = NULL; + ret->next = NULL; ret->nextfree = NULL; - *tail = (uint32_t) ret; + *tail = (uint32_t)ret; void *save = ret; @@ -361,282 +385,284 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) */ ret->size = block_size - OVERHEAD; blkmngr_set_free(&(ret->size), - false); + 0); // Set the block allocated. // ptr = ptr + block_size - sizeof(uint32_t); // trailing_space = ptr; // *trailing_space = ret->size; // Now, return it! - return save + sizeof(block_t); - } -} - -/// @brief Allocates size bytes of uninitialized storage with block align. -static void *__do_malloc_align(struct vm_area_struct *heap, uint32_t size) -{ - if (size == 0) return NULL; - - // Get: - // 1) First memory block. - // static block_t *head = NULL; - // 2) Last memory block. - // static block_t *tail = NULL; - // 3) All the memory blocks that are freed. - // static block_t *freelist = NULL; - - // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); - assert(head && tail && freelist && "Heap block lists point to null."); - - // We will use these others in reading. - block_t *head_block = (block_t *) *head; - block_t *tail_block = (block_t *) *tail; - // block_t *first_free_block = (block_t *) *freelist; - - // Calculate real size that's used, round it to multiple of 16. - uint32_t rounded_size = ceil(size, 16); - - /* Find bestfit in avl tree. This bestfit function will remove - * thebest-fit node when there is more than one such node in tree. - */ - block_t *best_fitting = blkmngr_find_best_fitting(rounded_size, freelist); - if (best_fitting != NULL && (IS_ALIGN(best_fitting + sizeof(block_t)))) - { - return kmalloc(size); - } - else - { - void *needed_addr = (void *) ADDR_ALIGN( - ((uint32_t) kernel_heap_curr + sizeof(block_t)) & 0xFFFFF000); - block_t *block_addr = needed_addr - sizeof(block_t); - - uint32_t realsize = - (uint32_t) block_addr - (uint32_t) (kernel_heap_curr) + OVERHEAD + - rounded_size; - block_t *ret; - if(heap == &kernel_heap) - { - ret = ksbrk(realsize); - } - else - { - ret = usbrk(realsize); - } - assert(ret != NULL && "Heap is running out of space\n"); - if (!head_block) - { - *head = (uint32_t) block_addr; - } - else - { - tail_block->next = block_addr; - } - - ret->next = NULL; - ret->nextfree = NULL; - *tail = (uint32_t) block_addr; - - /* After sbrk(), split the block into half [block_size | the rest], - * and put the rest into the tree. - */ - block_addr->size = rounded_size; - blkmngr_set_free(&(block_addr->size), - false); - // Set the block allocated. - // ptr = ptr + block_size - sizeof(uint32_t); - // trailing_space = ptr; - // *trailing_space = block_addr->size; - - // Now, return it! - return needed_addr; - } -} - -/// @brief Reallocates the given area of memory. It must be still allocated -/// and not yet freed with a call to free or realloc. -/// @param ptr -/// @param size -/// @return -static void *__do_realloc(struct vm_area_struct *heap, void *ptr, uint32_t size) -{ - // Get: - // 1) First memory block. - // static block_t *head = NULL; - // 2) Last memory block. - // static block_t *tail = NULL; - // 3) All the memory blocks that are freed. - // static block_t *freelist = NULL; - - // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); - assert(head && tail && freelist && "Heap block lists point to null."); - - // We will use these others in reading. - block_t *head_block = (block_t *) *head; - block_t *tail_block = (block_t *) *tail; - // block_t *first_free_block = (block_t *) *freelist; - - uint32_t *trailing_space = NULL; - if (!ptr) - { - return kmalloc(size); - } - if (size == 0 && ptr != NULL) - { - kfree(ptr); - - return NULL; - } - uint32_t rounded_size = ceil(size, 16); - uint32_t block_size = rounded_size + OVERHEAD; - block_t *nextBlock; - block_t *prevBlock; - - /* Shrink or expand? - * - * Shrink: - * Now, we would just return the same address, later we may split this - * block. - * - * Expand: - * First, try if the actual size of the memory block is enough - * to hold the current size. - * Second, if not, try if merging the next block works. - * Third, if none of the above works, malloc another block, move all the - * data there, and then free the original block. - */ - block_t *nptr = ptr - sizeof(block_t); - nextBlock = blkmngr_get_next_block(nptr, tail); - prevBlock = blkmngr_get_previous_block(nptr, head); - if (nptr->size == size) - { - return ptr; - } - if (nptr->size < size) - { - // Expand, size of the block is just not enough. - if (tail_block != nptr && blkmngr_is_free(nextBlock) && - (get_real_size(nptr->size) + OVERHEAD + - get_real_size(nextBlock->size)) >= rounded_size) - { - // Merge with the next block, and return! - // Change size to curr's size + OVERHEAD + next's size. - blkmngr_remove_from_freelist(nextBlock, freelist); - nptr->size = get_real_size(nptr->size) + OVERHEAD + - get_real_size(nextBlock->size); - blkmngr_set_free(&(nptr->size), false); - trailing_space = - (void *) nptr + sizeof(block_t) + get_real_size(nptr->size); - *trailing_space = nptr->size; - if (tail_block == nextBlock) - { - // Set it to tail for now, or we can reclaim it. - *tail = (uint32_t) nptr; - } - return nptr + 1; - } - // Hey! Try merging with the previous block! - else if (head_block != nptr && blkmngr_is_free(prevBlock) && - (get_real_size(nptr->size) + OVERHEAD + - get_real_size(prevBlock->size)) >= rounded_size) - { - // db_print(); - uint32_t originalSize = get_real_size(nptr->size); - // Hey! one more thing to do , copy data over to new block. - blkmngr_remove_from_freelist(prevBlock, freelist); - prevBlock->size = - originalSize + OVERHEAD + get_real_size(prevBlock->size); - blkmngr_set_free(&(prevBlock->size), false); - trailing_space = (void *) prevBlock + sizeof(block_t) + - get_real_size(prevBlock->size); - *trailing_space = prevBlock->size; - if (tail_block == nptr) - { - *tail = (uint32_t) prevBlock; - } - memcpy(prevBlock + 1, ptr, originalSize); - - return prevBlock + 1; - } - - // Move to somewhere else. - void *newplace = kmalloc(size); - // Copy data over. - memcpy(newplace, ptr, get_real_size(nptr->size)); - // Free original one - kfree(ptr); - - return newplace; - } - else - { - /* Shrink/Do nothing, you can leave it as it's, but yeah... shrink - * it What's left after shrinking the original block. - */ - uint32_t rest = get_real_size(nptr->size) + OVERHEAD - block_size; - if (rest < 8 + OVERHEAD) return ptr; - - nptr->size = block_size - OVERHEAD; - blkmngr_set_free(&(nptr->size), false); - trailing_space = - (void *) nptr + sizeof(block_t) + get_real_size(nptr->size); - *trailing_space = nptr->size; - /* - * if(tail == nptr) - * { - * ksbrk(-reclaimSize); - * - * return ptr; - * } - */ - block_t *splitBlock = (void *) trailing_space + sizeof(uint32_t); - - /* Set the next, if the next of the next is also freed.. then merge!! - * Wait... what if after merge, I get a much much more bigger block - * than I even need? split again hahahahahah fuck! - * Instead of spliting after merge, let's give splitBlock. - */ - if (nextBlock && blkmngr_is_free(nextBlock)) - { - splitBlock->size = rest + get_real_size(nextBlock->size); - blkmngr_set_free(&(splitBlock->size), true); - trailing_space = (void *) splitBlock + sizeof(block_t) + - get_real_size(splitBlock->size); - *trailing_space = splitBlock->size; - - // Remove next block from freelist. - blkmngr_remove_from_freelist(nextBlock, freelist); - // This can be deleted when you correctly implemented malloc() - if (tail_block == nextBlock) - { - *tail = (uint32_t) splitBlock; - } - // Add splitblock to freelist. - blkmngr_add_to_freelist(splitBlock, freelist); - - return ptr; - } - // Separate! - splitBlock->size = rest - OVERHEAD; - blkmngr_set_free(&(splitBlock->size), true); - trailing_space = (void *) splitBlock + sizeof(block_t) + - get_real_size(splitBlock->size); - *trailing_space = splitBlock->size; - - // Add this mo** f**r to the freelist! - blkmngr_add_to_freelist(splitBlock, freelist); - - return ptr; + return (char *)save + sizeof(block_t); } } +// +///// @brief Allocates size bytes of uninitialized storage with block align. +//static void *__do_malloc_align(vm_area_struct_t *heap, uint32_t size) +//{ +// if (size == 0) return NULL; +// +// // Get: +// // 1) First memory block. +// // static block_t *head = NULL; +// // 2) Last memory block. +// // static block_t *tail = NULL; +// // 3) All the memory blocks that are freed. +// // static block_t *freelist = NULL; +// +// // We will use these in writing. +// uint32_t *head = (uint32_t *) (heap->vm_start); +// uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); +// uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); +// assert(head && tail && freelist && "Heap block lists point to null."); +// +// // We will use these others in reading. +// block_t *head_block = (block_t *) *head; +// block_t *tail_block = (block_t *) *tail; +// // block_t *first_free_block = (block_t *) *freelist; +// +// // Calculate real size that's used, round it to multiple of 16. +// uint32_t rounded_size = CEIL(size, 16); +// +// /* Find bestfit in avl tree. This bestfit function will remove +// * thebest-fit node when there is more than one such node in tree. +// */ +// block_t *best_fitting = blkmngr_find_best_fitting(rounded_size, freelist); +// if (best_fitting != NULL && (IS_ALIGN(best_fitting + sizeof(block_t)))) +// { +// return kmalloc(size); +// } +// else +// { +// void *needed_addr = (void *) ADDR_ALIGN( +// ((uint32_t) kernel_heap_top + sizeof(block_t)) & 0xFFFFF000); +// block_t *block_addr = needed_addr - sizeof(block_t); +// +// uint32_t realsize = +// (uint32_t) block_addr - (uint32_t) (kernel_heap_top) + OVERHEAD + +// rounded_size; +// block_t *ret; +// if(heap == &kernel_heap) +// { +// ret = ksbrk(realsize); +// } +// else +// { +// ret = usbrk(realsize); +// } +// assert(ret != NULL && "Heap is running out of space\n"); +// if (!head_block) +// { +// *head = (uint32_t) block_addr; +// } +// else +// { +// tail_block->next = block_addr; +// } +// +// ret->next = NULL; +// ret->nextfree = NULL; +// *tail = (uint32_t) block_addr; +// +// /* After sbrk(), split the block into half [block_size | the rest], +// * and put the rest into the tree. +// */ +// block_addr->size = rounded_size; +// blkmngr_set_free(&(block_addr->size), +// 0); +// // Set the block allocated. +// // ptr = ptr + block_size - sizeof(uint32_t); +// // trailing_space = ptr; +// // *trailing_space = block_addr->size; +// +// // Now, return it! +// return needed_addr; +// } +//} +// +///// @brief Reallocates the given area of memory. It must be still allocated +///// and not yet freed with a call to free or realloc. +///// @param ptr +///// @param size +///// @return +//static void *__do_realloc(vm_area_struct_t *heap, void *ptr, uint32_t size) +//{ +// // Get: +// // 1) First memory block. +// // static block_t *head = NULL; +// // 2) Last memory block. +// // static block_t *tail = NULL; +// // 3) All the memory blocks that are freed. +// // static block_t *freelist = NULL; +// +// // We will use these in writing. +// uint32_t *head = (uint32_t *) (heap->vm_start); +// uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); +// uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); +// assert(head && tail && freelist && "Heap block lists point to null."); +// +// // We will use these others in reading. +// block_t *head_block = (block_t *) *head; +// block_t *tail_block = (block_t *) *tail; +// // block_t *first_free_block = (block_t *) *freelist; +// +// uint32_t *trailing_space = NULL; +// if (!ptr) +// { +// return kmalloc(size); +// } +// if (size == 0 && ptr != NULL) +// { +// kfree(ptr); +// +// return NULL; +// } +// uint32_t rounded_size = CEIL(size, 16); +// uint32_t block_size = rounded_size + OVERHEAD; +// block_t *nextBlock; +// block_t *prevBlock; +// +// /* Shrink or expand? +// * +// * Shrink: +// * Now, we would just return the same address, later we may split this +// * block. +// * +// * Expand: +// * First, try if the actual size of the memory block is enough +// * to hold the current size. +// * Second, if not, try if merging the next block works. +// * Third, if none of the above works, malloc another block, move all the +// * data there, and then free the original block. +// */ +// block_t *nptr = ptr - sizeof(block_t); +// nextBlock = blkmngr_get_next_block(nptr, tail); +// prevBlock = blkmngr_get_previous_block(nptr, head); +// if (nptr->size == size) +// { +// return ptr; +// } +// if (nptr->size < size) +// { +// // Expand, size of the block is just not enough. +// if (tail_block != nptr && blkmngr_is_free(nextBlock) && +// (blkmngr_get_real_size(nptr->size) + OVERHEAD + +// blkmngr_get_real_size(nextBlock->size)) >= rounded_size) +// { +// // Merge with the next block, and return! +// // Change size to curr's size + OVERHEAD + next's size. +// blkmngr_remove_from_freelist(nextBlock, freelist); +// nptr->size = blkmngr_get_real_size(nptr->size) + OVERHEAD + +// blkmngr_get_real_size(nextBlock->size); +// blkmngr_set_free(&(nptr->size), 0); +// trailing_space = +// (void *) nptr + sizeof(block_t) + blkmngr_get_real_size(nptr->size); +// *trailing_space = nptr->size; +// if (tail_block == nextBlock) +// { +// // Set it to tail for now, or we can reclaim it. +// *tail = (uint32_t) nptr; +// } +// return nptr + 1; +// } +// // Hey! Try merging with the previous block! +// else if (head_block != nptr && blkmngr_is_free(prevBlock) && +// (blkmngr_get_real_size(nptr->size) + OVERHEAD + +// blkmngr_get_real_size(prevBlock->size)) >= rounded_size) +// { +// // db_print(); +// uint32_t originalSize = blkmngr_get_real_size(nptr->size); +// // Hey! one more thing to do , copy data over to new block. +// blkmngr_remove_from_freelist(prevBlock, freelist); +// prevBlock->size = +// originalSize + OVERHEAD + blkmngr_get_real_size(prevBlock->size); +// blkmngr_set_free(&(prevBlock->size), 0); +// trailing_space = (void *) prevBlock + sizeof(block_t) + +// blkmngr_get_real_size(prevBlock->size); +// *trailing_space = prevBlock->size; +// if (tail_block == nptr) +// { +// *tail = (uint32_t) prevBlock; +// } +// memcpy(prevBlock + 1, ptr, originalSize); +// +// return prevBlock + 1; +// } +// +// // Move to somewhere else. +// void *newplace = kmalloc(size); +// // Copy data over. +// memcpy(newplace, ptr, blkmngr_get_real_size(nptr->size)); +// // Free original one +// kfree(ptr); +// +// return newplace; +// } +// else +// { +// /* Shrink/Do nothing, you can leave it as it's, but yeah... shrink +// * it What's left after shrinking the original block. +// */ +// uint32_t rest = blkmngr_get_real_size(nptr->size) + OVERHEAD - block_size; +// if (rest < 8 + OVERHEAD) return ptr; +// +// nptr->size = block_size - OVERHEAD; +// blkmngr_set_free(&(nptr->size), 0); +// trailing_space = +// (void *) nptr + sizeof(block_t) + blkmngr_get_real_size(nptr->size); +// *trailing_space = nptr->size; +// /* +// * if(tail == nptr) +// * { +// * ksbrk(-reclaimSize); +// * +// * return ptr; +// * } +// */ +// block_t *splitBlock = (void *) trailing_space + sizeof(uint32_t); +// +// /* Set the next, if the next of the next is also freed.. then merge!! +// * Wait... what if after merge, I get a much much more bigger block +// * than I even need? split again hahahahahah fuck! +// * Instead of spliting after merge, let's give splitBlock. +// */ +// if (nextBlock && blkmngr_is_free(nextBlock)) +// { +// splitBlock->size = rest + blkmngr_get_real_size(nextBlock->size); +// blkmngr_set_free(&(splitBlock->size), 1); +// trailing_space = (void *) splitBlock + sizeof(block_t) + +// blkmngr_get_real_size(splitBlock->size); +// *trailing_space = splitBlock->size; +// +// // Remove next block from freelist. +// blkmngr_remove_from_freelist(nextBlock, freelist); +// // This can be deleted when you correctly implemented malloc() +// if (tail_block == nextBlock) +// { +// *tail = (uint32_t) splitBlock; +// } +// // Add splitblock to freelist. +// blkmngr_add_to_freelist(splitBlock, freelist); +// +// return ptr; +// } +// // Separate! +// splitBlock->size = rest - OVERHEAD; +// blkmngr_set_free(&(splitBlock->size), 1); +// trailing_space = (void *) splitBlock + sizeof(block_t) + +// blkmngr_get_real_size(splitBlock->size); +// *trailing_space = splitBlock->size; +// +// // Add this mo** f**r to the freelist! +// blkmngr_add_to_freelist(splitBlock, freelist); +// +// return ptr; +// } +//} /// @brief Deallocates previously allocated space. -static void __do_free(struct vm_area_struct * heap, void * ptr) +/// @param heap Heap to which we return the allocated memory. +/// @param ptr Pointer to the allocated memory. +static void __do_free(vm_area_struct_t *heap, void *ptr) { assert(ptr); @@ -649,92 +675,75 @@ static void __do_free(struct vm_area_struct * heap, void * ptr) // static block_t *freelist = NULL; // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); + uint32_t *head = (uint32_t *)(heap->vm_start); + uint32_t *tail = (uint32_t *)(heap->vm_start + sizeof(block_t *)); + uint32_t *freelist = (uint32_t *)(heap->vm_start + 2 * sizeof(block_t *)); assert(head && tail && freelist && "Heap block lists point to null."); // We will use these others in reading. - block_t *tail_block = (block_t *) *tail; + block_t *tail_block = (block_t *)*tail; - block_t *curr = ptr - sizeof(block_t); + block_t *curr = (block_t *)((char *)ptr - sizeof(block_t)); block_t *prev = blkmngr_get_previous_block(curr, head); block_t *next = blkmngr_get_next_block(curr, tail); - if (blkmngr_is_free(prev) && blkmngr_is_free(next)) - { + if (blkmngr_is_free(prev) && blkmngr_is_free(next)) { prev->size = - get_real_size(prev->size) + 2 * OVERHEAD + - get_real_size(curr->size) + - get_real_size(next->size); - blkmngr_set_free(&(prev->size), true); + blkmngr_get_real_size(prev->size) + 2 * OVERHEAD + + blkmngr_get_real_size(curr->size) + + blkmngr_get_real_size(next->size); + blkmngr_set_free(&(prev->size), 1); prev->next = blkmngr_get_next_block(next, tail); // If next used to be tail, set prev = tail. - if (tail_block == next) - { - *tail = (uint32_t) prev; + if (tail_block == next) { + *tail = (uint32_t)prev; } blkmngr_remove_from_freelist(next, freelist); - } - else if (blkmngr_is_free(prev)) - { + } else if (blkmngr_is_free(prev)) { prev->size = - get_real_size(prev->size) + OVERHEAD + get_real_size(curr->size); - blkmngr_set_free(&(prev->size), true); + blkmngr_get_real_size(prev->size) + OVERHEAD + blkmngr_get_real_size(curr->size); + blkmngr_set_free(&(prev->size), 1); prev->next = next; - if (tail_block == curr) - { - *tail = (uint32_t) prev; + if (tail_block == curr) { + *tail = (uint32_t)prev; } - } - else if (blkmngr_is_free(next)) - { + } else if (blkmngr_is_free(next)) { // Change size to curr's size + OVERHEAD + next's size. curr->size = - get_real_size(curr->size) + OVERHEAD + get_real_size(next->size); - blkmngr_set_free(&(curr->size), true); + blkmngr_get_real_size(curr->size) + OVERHEAD + blkmngr_get_real_size(next->size); + blkmngr_set_free(&(curr->size), 1); curr->next = blkmngr_get_next_block(next, tail); - if (tail_block == next) - { - *tail = (uint32_t) curr; + if (tail_block == next) { + *tail = (uint32_t)curr; } blkmngr_remove_from_freelist(next, freelist); blkmngr_add_to_freelist(curr, freelist); - } - else - { + } else { // Just mark curr freed. - blkmngr_set_free(&(curr->size), true); + blkmngr_set_free(&(curr->size), 1); blkmngr_add_to_freelist(curr, freelist); } } void kheap_init(size_t initial_size) { - // Starting allocate kheap after paging. - if (paging_is_enabled()) - { - assert(0 && "Paging must not be enabled!"); - } - - unsigned int order = find_nearest_order_greater(initial_size); - + unsigned int order = find_nearest_order_greater(0, initial_size); // Kernel_heap_start. - kernel_heap.vm_start = __alloc_pages(GFP_KERNEL, order); - kernel_heap.vm_end = kernel_heap.vm_start + ((1UL << order) * PAGE_SIZE); - + kernel_heap.vm_start = __alloc_pages_lowmem(GFP_KERNEL, order); + kernel_heap.vm_end = kernel_heap.vm_start + ((1UL << order) * PAGE_SIZE); // Kernel_heap_start. - kernel_heap_curr = kernel_heap.vm_start; + kernel_heap_top = kernel_heap.vm_start; + // FIXME!! // Set kernel_heap vm_area_struct info: - kernel_heap.vm_next = NULL; - kernel_heap.vm_mm = NULL; + // kernel_heap.vm_next = NULL; + // kernel_heap.vm_mm = NULL; // Reserved space for: // 1) First memory block. @@ -743,100 +752,39 @@ void kheap_init(size_t initial_size) // static block_t *tail = NULL; // 3) All the memory blocks that are freed. // static block_t *freelist = NULL; - memset((void *)kernel_heap_curr, 0, 3 * sizeof(block_t *)); - kernel_heap_curr += 3 * sizeof(block_t *); - - kheap_enabled = true; -} - -bool_t kheap_is_enabled() -{ - return kheap_enabled; + memset((void *)kernel_heap_top, 0, 3 * sizeof(block_t *)); + kernel_heap_top += 3 * sizeof(block_t *); } void *ksbrk(int increment) { - return __do_brk(&kernel_heap_curr, &kernel_heap, increment); + return __do_brk(&kernel_heap_top, &kernel_heap, increment); } - - -void *kmalloc(uint32_t sz) +void *usbrk(int increment) { - assert(kheap_enabled && "KHEAP not initialized!"); + task_struct *current_task = scheduler_get_current_process(); + mm_struct_t *task_mm = current_task->mm; + uint32_t *heap_curr = &task_mm->brk; - return __do_malloc(&kernel_heap, sz); + vm_area_struct_t *heap_segment = __find_user_heap(); + + return __do_brk(heap_curr, heap_segment, increment); } -// TODO: check! -void *kmalloc_align(size_t size) -{ - assert(kheap_enabled && "KHEAP not initialized!"); - - return __do_malloc_align(&kernel_heap, size); -} - -void *kcalloc(uint32_t num, uint32_t size) -{ - void *ptr = kmalloc(num * size); - if (ptr) - { - memset(ptr, 0, num * size); - } - - return ptr; -} - -// TODO: check! -void *krealloc(void *ptr, uint32_t size) -{ - return __do_realloc(&kernel_heap, ptr, size); -} - -void kfree(void *ptr) -{ - __do_free(&kernel_heap, ptr); -} - -/// @brief Find the current user heap. -/// @return The heap structure if heap exists, otherwise NULL. -static struct vm_area_struct *find_user_heap() -{ - // Get the memory descriptor of the current process. - task_struct *current_task = kernel_get_current_process(); - struct mm_struct *current_mm = current_task->mm; - - // Get the starting address of the heap. - uint32_t start_heap = current_mm->start_brk; - // If not set return NULL. - if(start_heap == 0) - { - return NULL; - } - - // Otherwise find the respective heap segment. - struct vm_area_struct *segment = current_mm->mmap; - while(segment->vm_start != start_heap) - { - segment = segment->vm_next; - } - - return segment; -} - -// TODO: rename in sys_brk -void *umalloc(unsigned int size) +void *sys_brk(void *addr) { // Get user heap segment structure. - struct vm_area_struct *heap_segment = find_user_heap(); + vm_area_struct_t *heap_segment = __find_user_heap(); // Allocate the segment if don't exist. - if(heap_segment == NULL) - { - task_struct *current_task = kernel_get_current_process(); - struct mm_struct *current_mm = current_task->mm; - current_mm->start_brk = create_segment(current_mm, UHEAP_INITIAL_SIZE); - current_mm->brk = current_mm->start_brk; + if (heap_segment == NULL) { + task_struct *current_task = scheduler_get_current_process(); + mm_struct_t *current_mm = current_task->mm; + current_mm->start_brk = create_vm_area(current_mm, + 0x40000000 /*FIXME! stabilize this*/, + UHEAP_INITIAL_SIZE, MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR, GFP_HIGHUSER); + current_mm->brk = current_mm->start_brk; // Reserved space for: // 1) First memory block. // static block_t *head = NULL; @@ -845,34 +793,16 @@ void *umalloc(unsigned int size) // 3) All the memory blocks that are freed. // static block_t *freelist = NULL; current_mm->brk += 3 * sizeof(block_t *); - heap_segment = find_user_heap(); + heap_segment = __find_user_heap(); } - - return __do_malloc(heap_segment, size); -} - -// TODO: rename in sys_brk -void ufree(void *p) -{ - // Get user heap segment structure. - struct vm_area_struct *heap_segment = find_user_heap(); - - // If the segment exists, user did a malloc previously. - if (heap_segment != NULL) - { - __do_free(heap_segment, p); + // If the address falls inside the memory region, call the free function, + // otherwise execute a malloc of the specified amount. + if (((uintptr_t)addr > heap_segment->vm_start) && + ((uintptr_t)addr < heap_segment->vm_end)) { + __do_free(heap_segment, addr); + return NULL; } -} - -void *usbrk(int increment) -{ - task_struct *current_task = kernel_get_current_process(); - struct mm_struct *task_mm = current_task->mm; - uint32_t *heap_curr = &task_mm->brk; - - struct vm_area_struct *heap_segment = find_user_heap(); - - return __do_brk(heap_curr, heap_segment, increment); + return __do_malloc(heap_segment, (uintptr_t)addr); } void kheap_dump() @@ -885,57 +815,43 @@ void kheap_dump() // static block_t *freelist = NULL; // We will use these in writing. - uint32_t *head = (uint32_t *) (kernel_heap.vm_start); - uint32_t *tail = (uint32_t *) (kernel_heap.vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (kernel_heap.vm_start + 2 * sizeof(block_t *)); + uint32_t *head = (uint32_t *)(kernel_heap.vm_start); + uint32_t *tail = (uint32_t *)(kernel_heap.vm_start + sizeof(block_t *)); + uint32_t *freelist = (uint32_t *)(kernel_heap.vm_start + 2 * sizeof(block_t *)); assert(head && tail && freelist && "Heap block lists point to null."); // We will use these others in reading. - block_t *head_block = (block_t *) *head; + block_t *head_block = (block_t *)*head; // block_t *tail_block = (block_t *) *tail; - block_t *first_free_block = (block_t *) *freelist; - - if (!head_block) - { - dbg_print("your heap is empty now\n"); + block_t *first_free_block = (block_t *)*freelist; + if (!head_block) { + pr_debug("your heap is empty now\n"); return; } - // dbg_print("HEAP:\n"); - uint32_t total = 0; + // pr_debug("HEAP:\n"); + uint32_t total = 0; uint32_t total_overhead = 0; - block_t * it = head_block; - while (it) - { - dbg_print("[%c] %12u (%12u) from 0x%p to 0x%p\n", - (blkmngr_is_free(it)) ? 'F' : 'A', - get_real_size(it->size), - it->size, - it, - (void *) it + OVERHEAD + get_real_size(it->size)); - total += get_real_size(it->size); + block_t *it = head_block; + while (it) { + pr_debug("[%c] %12u (%12u) from 0x%p to 0x%p\n", + (blkmngr_is_free(it)) ? 'F' : 'A', + blkmngr_get_real_size(it->size), + it->size, + it, + (char *)it + OVERHEAD + blkmngr_get_real_size(it->size)); + total += blkmngr_get_real_size(it->size); total_overhead += OVERHEAD; it = it->next; } - dbg_print("\nTotal usable bytes : %d", total); - dbg_print("\nTotal overhead bytes : %d", total_overhead); - dbg_print("\nTotal bytes : %d", total + total_overhead); - dbg_print("\nFreelist: "); - for (it = first_free_block; it != NULL; it = it->nextfree) - { - dbg_print("(%p)->", it); + pr_debug("\nTotal usable bytes : %d", total); + pr_debug("\nTotal overhead bytes : %d", total_overhead); + pr_debug("\nTotal bytes : %d", total + total_overhead); + pr_debug("\nFreelist: "); + for (it = first_free_block; it != NULL; it = it->nextfree) { + pr_debug("(%p)->", it); } - dbg_print("\n\n"); -} - -uint32_t get_kheap_start() -{ - return kernel_heap.vm_start; -} - -uint32_t get_kheap_curr() -{ - return kernel_heap_curr; + pr_debug("\n\n"); } diff --git a/mentos/src/mem/paging.c b/mentos/src/mem/paging.c index e304439..34357f4 100644 --- a/mentos/src/mem/paging.c +++ b/mentos/src/mem/paging.c @@ -1,88 +1,626 @@ /// MentOS, The Mentoring Operating system project /// @file paging.c /// @brief Implementation of a memory paging management. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Change the header. +#define __DEBUG_HEADER__ "[PAGING]" + #include "paging.h" +#include "isr.h" +#include "vmem_map.h" #include "zone_allocator.h" #include "kheap.h" #include "debug.h" #include "assert.h" #include "string.h" +#include "panic.h" -bool_t paging_is_enabled() +/// Cache for storing mm_struct. +kmem_cache_t *mm_cache; +/// Cache for storing vm_area_struct. +kmem_cache_t *vm_area_cache; +/// Cache for storing page directories. +kmem_cache_t *pgdir_cache; +/// Cache for storing page tables. +kmem_cache_t *pgtbl_cache; + +/// The mm_struct of the kernel. +static mm_struct_t *main_mm; + +/// @brief Structure for iterating page directory entries. +typedef struct page_iterator_s { + /// Pointer to the entry. + page_dir_entry_t *entry; + /// Pointer to the page table. + page_table_t *table; + /// Page Frame Number (PFN). + uint32_t pfn; + /// Last PNF. + uint32_t last_pfn; + /// Contains MEMMAP_FLAGS flags. + uint32_t flags; +} page_iterator_t; + +/// @brief Structure for iterating page table entries. +typedef struct pg_iter_entry_s { + /// Pointer to the page table entry. + page_table_entry_t *entry; + /// Page Frame Number (PFN). + uint32_t pfn; +} pg_iter_entry_t; + +page_directory_t *paging_get_main_directory() { - return false; + return main_mm->pgd; } -struct mm_struct *create_process_image(size_t stack_size) +/// @brief Switches paging directory, the pointer can be a lowmem address +void paging_switch_directory_va(page_directory_t *dir) { - // Allocate the mm_struct. - struct mm_struct *mm = kmalloc(sizeof(struct mm_struct)); - memset(mm, 0, sizeof(struct mm_struct)); - - // Allocate the stack segment. - mm->start_stack = create_segment(mm, stack_size); - - return mm; + page_t *page = get_lowmem_page_from_address((uintptr_t)dir); + paging_switch_directory((page_directory_t *)get_physical_address_from_page(page)); } -uint32_t create_segment(struct mm_struct *mm, size_t size) +void paging_flush_tlb_single(unsigned long addr) { - // Allocate on kernel space the structure for the segment. - struct vm_area_struct *new_segment = kmalloc(sizeof(struct vm_area_struct)); - - // Allocate the space requested for the segment on user space. - if (paging_is_enabled()) { - assert(0 && "Paging must not be enabled"); - } - - unsigned int order = find_nearest_order_greater(size); - uint32_t vm_start = __alloc_pages(GFP_HIGHUSER, order); - - // Update vm_area_struct info. - new_segment->vm_start = vm_start; - new_segment->vm_end = vm_start + (1 << order) * PAGE_SIZE; - new_segment->vm_mm = mm; - - // Update memory descriptor list of vm_area_struct. - new_segment->vm_next = mm->mmap; - mm->mmap = new_segment; - mm->mmap_cache = new_segment; - - // Update memory descriptor info. - mm->map_count++; - - mm->total_vm += (1 << order); - - return vm_start; + ASM("invlpg (%0)" ::"r"(addr) + : "memory"); } -void destroy_process_image(struct mm_struct *mm) +uint32_t create_vm_area(mm_struct_t *mm, + uint32_t virt_start, + size_t size, + uint32_t pgflags, + uint32_t gfpflags) { - assert(mm != NULL); + // Allocate on kernel space the structure for the segment. + vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL); - // Free each segment inside mm. - struct vm_area_struct *segment = mm->mmap; - while (segment != NULL) { - // Free the pages represented by the segment. - if (paging_is_enabled()) { - assert(0 && "Paging must not be enabled"); - } + uint32_t order = find_nearest_order_greater(virt_start, size); - size_t size = segment->vm_end - segment->vm_start; - unsigned int order = find_nearest_order_greater(size); + uint32_t phy_vm_start; - free_pages(segment->vm_start, order); + if (pgflags & MM_COW) { + pgflags &= ~(MM_PRESENT | MM_UPDADDR); + phy_vm_start = 0; + } else { + pgflags |= MM_UPDADDR; + page_t *page = _alloc_pages(gfpflags, order); + phy_vm_start = get_physical_address_from_page(page); + } - struct vm_area_struct *tmp = segment; - segment = segment->vm_next; + mem_upd_vm_area(mm->pgd, virt_start, phy_vm_start, size, pgflags); - // Free the vm_area_struct. - kfree(tmp); - } + uint32_t vm_start = virt_start; - // Free the mm_struct. - kfree(mm); + // Update vm_area_struct info. + new_segment->vm_start = vm_start; + new_segment->vm_end = vm_start + size; + new_segment->vm_mm = mm; + + // Update memory descriptor list of vm_area_struct. + list_head_add(&new_segment->vm_list, &mm->mmap_list); + mm->mmap_cache = new_segment; + + // Update memory descriptor info. + mm->map_count++; + + mm->total_vm += (1U << order); + + return vm_start; +} + +uint32_t clone_vm_area(mm_struct_t *mm, vm_area_struct_t *area, int cow, uint32_t gfpflags) +{ + vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL); + memcpy(new_segment, area, sizeof(vm_area_struct_t)); + + new_segment->vm_mm = mm; + + uint32_t size = new_segment->vm_end - new_segment->vm_start; + uint32_t order = find_nearest_order_greater(area->vm_start, size); + + if (!cow) { + // If not copy-on-write, allocate directly the physical pages + page_t *dst_page = _alloc_pages(gfpflags, order); + uint32_t phy_vm_start = get_physical_address_from_page(dst_page); + + // Then update the virtual memory map + mem_upd_vm_area(mm->pgd, new_segment->vm_start, phy_vm_start, size, + MM_RW | MM_PRESENT | MM_UPDADDR | MM_USER); + + // Copy virtual memory of source area into dest area by using a virtual mapping + virt_memcpy(mm, area->vm_start, area->vm_mm, area->vm_start, size); + } else { + // If copy-on-write, set the original pages as read-only + mem_upd_vm_area(area->vm_mm->pgd, area->vm_start, 0, size, + MM_COW | MM_PRESENT | MM_USER); + + // Do a cow of the whole virtual memory area, handling fragmented physical memory + // and set it as read-only + mem_clone_vm_area(area->vm_mm->pgd, + mm->pgd, + area->vm_start, + new_segment->vm_start, + size, + MM_COW | MM_PRESENT | MM_UPDADDR | MM_USER); + } + + // Update memory descriptor list of vm_area_struct. + list_head_add(&new_segment->vm_list, &mm->mmap_list); + mm->mmap_cache = new_segment; + + // Update memory descriptor info. + mm->map_count++; + + mm->total_vm += (1U << order); + + return 0; +} + +static void __init_pagedir(page_directory_t *pdir) +{ + *pdir = (page_directory_t){ 0 }; +} + +static void __init_pagetable(page_table_t *ptable) +{ + *ptable = (page_table_t){ 0 }; +} + +void paging_init(boot_info_t *info) +{ + mm_cache = KMEM_CREATE(mm_struct_t); + vm_area_cache = KMEM_CREATE(vm_area_struct_t); + + pgdir_cache = KMEM_CREATE_CTOR(page_directory_t, __init_pagedir); + pgtbl_cache = KMEM_CREATE_CTOR(page_table_t, __init_pagetable); + + main_mm = kmem_cache_alloc(mm_cache, GFP_KERNEL); + + main_mm->pgd = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); + + uint32_t lowkmem_size = info->stack_end - info->kernel_start; + + // Map the first 1MB of memory with physical mapping to access video memory and other bios stuff + mem_upd_vm_area(main_mm->pgd, 0, 0, 1024 * 1024, MM_RW | MM_PRESENT | MM_GLOBAL | MM_UPDADDR); + + mem_upd_vm_area(main_mm->pgd, info->kernel_start, info->kernel_phy_start, lowkmem_size, + MM_RW | MM_PRESENT | MM_GLOBAL | MM_UPDADDR); + + isr_install_handler(PAGE_FAULT, page_fault_handler, "page_fault_handler"); + + paging_switch_directory_va(main_mm->pgd); + paging_enable(); +} + +// Error code interpretation. +#define ERR_PRESENT 0x01 ///< Page not present. +#define ERR_RW 0x02 ///< Page is read only. +#define ERR_USER 0x04 ///< Page is privileged. +#define ERR_RESERVED 0x08 ///< Overwrote reserved bit. +#define ERR_INST 0x10 ///< Instruction fetch. + +static inline void __set_pg_table_flags(page_table_entry_t *table, uint32_t flags) +{ + table->rw = (flags & MM_RW) != 0; + table->present = (flags & MM_PRESENT) != 0; + table->kernel_cow = (flags & MM_COW) != 0; // Store the cow/not cow status + table->available = 1; // Future kernel data 2 bits + table->global = (flags & MM_GLOBAL) != 0; + table->user = (flags & MM_USER) != 0; +} + +/// @brief Prints stack frame data and calls kernel_panic. +/// @param f The interrupt stack frame. +/// @param addr The faulting address. +static void __page_fault_panic(pt_regs *f, uint32_t addr) +{ + asm volatile("cli"); + + // Gather fault info and print to screen + pr_err("Faulting address (cr2): 0x%p\n", addr); + + pr_err("EIP: 0x%p\n", f->eip); + + pr_err("Page fault: 0x%x\n", addr); + + pr_err("Possible causes: [ "); + if (!(f->err_code & ERR_PRESENT)) + pr_err("Page not present "); + if (f->err_code & ERR_RW) + pr_err("Page is read only "); + if (f->err_code & ERR_USER) + pr_err("Page is privileged "); + if (f->err_code & ERR_RESERVED) + pr_err("Overwrote reserved bits "); + if (f->err_code & ERR_INST) + pr_err("Instruction fetch "); + pr_err("]\n"); + dbg_print_regs(f); + + kernel_panic("Page fault!"); + + // Make directory accessible + // main_mm->pgd->entries[addr/(1024*4096)].user = 1; + // main_directory->entries[addr/(1024*4096)]. = 1; + + asm volatile("cli"); +} + +static void __page_handle_cow(page_table_entry_t *entry) +{ + // Check if the page is Copy On Write (COW). + if (entry->kernel_cow) { + // Set the entry is no longer COW. + entry->kernel_cow = 0; + // Check if the entry is not present (allocated). + if (!entry->present) { + // Allocate a new page. + page_t *page = _alloc_pages(GFP_HIGHUSER, 0); + // Clear the new page. + uint32_t vaddr = virt_map_physical_pages(page, 1); + memset((void *)vaddr, 0, PAGE_SIZE); + // Unmap the virtual address. + virt_unmap(vaddr); + // Set it as current table entry frame. + entry->frame = get_physical_address_from_page(page) >> 12U; + // Set it as allocated. + entry->present = 1; + return; + } + } + kernel_panic("Page not cow!"); +} + +static page_table_t *__mem_pg_entry_alloc(page_dir_entry_t *entry, uint32_t flags) +{ + if (!entry->present) { + // Alloc page table if not present + // Present should be always 1, to indicate that the page tables + // have been allocated and allow lazy physical pages allocation + entry->present = 1; + entry->rw = 1; + entry->global = (flags & MM_GLOBAL) != 0; + entry->user = (flags & MM_USER) != 0; + entry->accessed = 0; + entry->available = 1; + return kmem_cache_alloc(pgtbl_cache, GFP_KERNEL); + } else { + entry->present |= (flags & MM_PRESENT) != 0; + entry->rw |= (flags & MM_RW) != 0; + + // We should not remove a global flag from a page directory, + // if this happens there is probably a bug in the kernel + assert(!entry->global || (flags & MM_GLOBAL)); + + entry->global &= (flags & MM_GLOBAL) != 0; + entry->user |= (flags & MM_USER) != 0; + return (page_table_t *)get_lowmem_address_from_page( + get_page_from_physical_address(((uint32_t)entry->frame) << 12U)); + } +} + +static inline void __set_pg_entry_frame(page_dir_entry_t *entry, page_table_t *table) +{ + page_t *table_page = get_lowmem_page_from_address((uint32_t)table); + uint32_t phy_addr = get_physical_address_from_page(table_page); + entry->frame = phy_addr >> 12u; +} + +void page_fault_handler(pt_regs *f) +{ + // Here you will find the `Demand Paging` mechanism. + // From `Understanding The Linux Kernel 3rd Edition`: + // The term demand paging denotes a dynamic memory allocation + // technique that consists of deferring page frame allocation + // until the last possible moment—until the process attempts + // to address a page that is not present in RAM, thus causing + // a Page Fault exception. + + // First, read the linear address that caused the Page Fault. + // When the exception occurs, the CPU control unit stores that + // value in the cr2 control register. + uint32_t faulting_addr; + asm volatile("mov %%cr2, %0" + : "=r"(faulting_addr)); + // Get the physical address of the current page directory. + uint32_t phy_dir = (uint32_t)paging_get_current_directory(); + // Get the page directory. + page_directory_t *lowmem_dir = (page_directory_t *)get_lowmem_address_from_page(get_page_from_physical_address(phy_dir)); + // Get the directory entry. + page_dir_entry_t *direntry = &lowmem_dir->entries[faulting_addr / (1024U * PAGE_SIZE)]; + // TODO: Panic only if page is in kernel memory, else abort process with sigsegv + if (!direntry->present) { + __page_fault_panic(f, faulting_addr); + } + // Get the physical address of the page table. + uint32_t phy_table = direntry->frame << 12U; + // Get the page table. + page_table_t *lowmem_table = (page_table_t *)get_lowmem_address_from_page(get_page_from_physical_address(phy_table)); + // Get the entry inside the table that caused the fault. + uint32_t table_index = (faulting_addr / PAGE_SIZE) % 1024U; + // Get the corresponding page table entry. + page_table_entry_t *entry = &lowmem_table->pages[table_index]; + // There was a page fault on a virtual mapped address, + // so we must first update the original mapped page + if (virtual_check_address(faulting_addr)) { + // Get the original page table entry from the virtually mapped one. + page_table_entry_t *orig_entry = (page_table_entry_t *)(*(uint32_t *)entry); + // Check if the page is Copy on Write (CoW). + __page_handle_cow(orig_entry); + // Update the page table entry frame. + entry->frame = orig_entry->frame; + // Update the entry flags. + __set_pg_table_flags(entry, MM_PRESENT | MM_RW | MM_GLOBAL | MM_COW | MM_UPDADDR); + } else { + // Check if the page is Copy on Write (CoW). + __page_handle_cow(entry); + } + // Invalidate the page table entry. + paging_flush_tlb_single(faulting_addr); +} + +/// @brief Initialize a page iterator. +/// @param iter The iterator to initialize. +/// @param pgd The page directory to iterate. +/// @param addr_start The starting address. +/// @param size The total amount we want to iterate. +/// @param flags Allocation flags. +static void __pg_iter_init(page_iterator_t *iter, + page_directory_t *pgd, + uint32_t addr_start, + uint32_t size, + uint32_t flags) +{ + uint32_t start_pfn = addr_start / PAGE_SIZE; + + uint32_t end_pfn = (addr_start + size + PAGE_SIZE - 1) / PAGE_SIZE; + + uint32_t base_pgt = start_pfn / 1024; + iter->entry = pgd->entries + base_pgt; + iter->pfn = start_pfn; + iter->last_pfn = end_pfn; + iter->flags = flags; + + iter->table = __mem_pg_entry_alloc(iter->entry, flags); + __set_pg_entry_frame(iter->entry, iter->table); +} + +/// @brief Checks if the iterator has a next entry. +/// @param iter The iterator. +/// @return If we can continue the iteration. +static int __pg_iter_has_next(page_iterator_t *iter) +{ + return iter->pfn < iter->last_pfn; +} + +/// @brief Moves the iterator to the next entry. +/// @param iter The itetator. +/// @return The iterator after moving to the next entry. +static pg_iter_entry_t __pg_iter_next(page_iterator_t *iter) +{ + pg_iter_entry_t result = { + .entry = &iter->table->pages[iter->pfn % 1024], + .pfn = iter->pfn + }; + + if (++iter->pfn % 1024 == 0) { + // Create a new page only if we haven't reached the end + // The page directory is always aligned to page boundaries, + // so we can easily know when we've skipped the last page by checking + // if the address % PAGE_SIZE is equal to zero. + if (iter->pfn != iter->last_pfn && ((uint32_t)++iter->entry) % 4096 != 0) { + iter->table = __mem_pg_entry_alloc(iter->entry, iter->flags); + __set_pg_entry_frame(iter->entry, iter->table); + } + } + + return result; +} + +page_t *mem_virtual_to_page(page_directory_t *pgdir, uint32_t virt_start, size_t *size) +{ + uint32_t virt_pfn = virt_start / PAGE_SIZE; + uint32_t virt_pgt = virt_pfn / 1024; + uint32_t virt_pgt_offset = virt_pfn % 1024; + + page_t *pgd_page = mem_map + pgdir->entries[virt_pgt].frame; + + page_table_t *pgt_address = (page_table_t *)get_lowmem_address_from_page(pgd_page); + + uint32_t pfn = pgt_address->pages[virt_pgt_offset].frame; + + page_t *page = mem_map + pfn; + + // FIXME: handle unaligned page mapping + // to return the correct to-block-end size + // instead of 0 (1 page at a time) + if (size) { + uint32_t pfn_count = 1U << page->bbpage.order; + uint32_t bytes_count = pfn_count * PAGE_SIZE; + *size = min(*size, bytes_count); + } + + return page; +} + +void mem_upd_vm_area(page_directory_t *pgd, + uint32_t virt_start, + uint32_t phy_start, + size_t size, + uint32_t flags) +{ + page_iterator_t virt_iter; + __pg_iter_init(&virt_iter, pgd, virt_start, size, flags); + + uint32_t phy_pfn = phy_start / PAGE_SIZE; + + while (__pg_iter_has_next(&virt_iter)) { + pg_iter_entry_t it = __pg_iter_next(&virt_iter); + if (flags & MM_UPDADDR) { + it.entry->frame = phy_pfn++; + // Flush the tlb to allow address update + // TODO: Check if it's always needed (ex. when the pgdir is not the current one) + paging_flush_tlb_single(it.pfn * PAGE_SIZE); + } + __set_pg_table_flags(it.entry, flags); + } +} + +void mem_clone_vm_area(page_directory_t *src_pgd, + page_directory_t *dst_pgd, + uint32_t src_start, + uint32_t dst_start, + size_t size, + uint32_t flags) +{ + page_iterator_t src_iter; + page_iterator_t dst_iter; + + __pg_iter_init(&src_iter, src_pgd, src_start, size, flags); + __pg_iter_init(&dst_iter, dst_pgd, dst_start, size, flags); + + while (__pg_iter_has_next(&src_iter) && __pg_iter_has_next(&dst_iter)) { + pg_iter_entry_t src_it = __pg_iter_next(&src_iter); + pg_iter_entry_t dst_it = __pg_iter_next(&dst_iter); + + if (src_it.entry->kernel_cow) { + *(uint32_t *)dst_it.entry = (uint32_t)src_it.entry; + // This is to make it clear that the page is not present, + // can be omitted because the .entry address is aligned to 4 bytes boundary + // so it's first two bytes are always zero + dst_it.entry->present = 0; + } else { + dst_it.entry->frame = src_it.entry->frame; + __set_pg_table_flags(dst_it.entry, flags); + } + + // Flush the tlb to allow address update + // TODO: Check if it's always needed (ex. when the pgdir is not the current one) + paging_flush_tlb_single(dst_it.pfn * PAGE_SIZE); + } +} + +mm_struct_t *create_blank_process_image(size_t stack_size) +{ + // Allocate the mm_struct. + mm_struct_t *mm = kmem_cache_alloc(mm_cache, GFP_KERNEL); + memset(mm, 0, sizeof(mm_struct_t)); + + list_head_init(&mm->mmap_list); + + // TODO: Use this field + list_head_init(&mm->mm_list); + + page_directory_t *pdir_cpy = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); + memcpy(pdir_cpy, paging_get_main_directory(), sizeof(page_directory_t)); + + mm->pgd = pdir_cpy; + + // Initialize vm areas list + list_head_init(&mm->mmap_list); + + // Allocate the stack segment. + mm->start_stack = create_vm_area(mm, PROCAREA_END_ADDR - stack_size, stack_size, + MM_PRESENT | MM_RW | MM_USER | MM_COW, GFP_HIGHUSER); + return mm; +} + +mm_struct_t *clone_process_image(mm_struct_t *mmp) +{ + // Allocate the mm_struct. + mm_struct_t *mm = kmem_cache_alloc(mm_cache, GFP_KERNEL); + memcpy(mm, mmp, sizeof(mm_struct_t)); + + // Initialize the process with the main directory, to avoid page tables data races. + // Pages from the old process are copied/cow when segments are cloned + page_directory_t *pdir_cpy = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); + memcpy(pdir_cpy, paging_get_main_directory(), sizeof(page_directory_t)); + + mm->pgd = pdir_cpy; + + vm_area_struct_t *vm_area = NULL; + + // Reset vm areas to allow easy clone + list_head_init(&mm->mmap_list); + mm->map_count = 0; + mm->total_vm = 0; + + // Clone each memory area to the new process! + list_head *it; + list_for_each (it, &mmp->mmap_list) { + vm_area = list_entry(it, vm_area_struct_t, vm_list); + clone_vm_area(mm, vm_area, 0, GFP_HIGHUSER); + } + + // + // // Allocate the stack segment. + // mm->start_stack = create_segment(mm, stack_size); + + return mm; +} + +void destroy_process_image(mm_struct_t *mm) +{ + assert(mm != NULL); + + if ((uint32_t)paging_get_current_directory() == get_physical_address_from_page(get_lowmem_page_from_address((uint32_t)mm->pgd))) { + paging_switch_directory_va(paging_get_main_directory()); + } + + // Free each segment inside mm. + vm_area_struct_t *segment = NULL; + + list_head *it = mm->mmap_list.next; + while (!list_head_empty(it)) { + segment = list_entry(it, vm_area_struct_t, vm_list); + + size_t size = segment->vm_end - segment->vm_start; + + uint32_t area_start = segment->vm_start; + + while (size > 0) { + size_t area_size = size; + page_t *phy_page = mem_virtual_to_page(mm->pgd, area_start, &area_size); + + // If the pages are marked as copy-on-write, do not deallocate them! + if (page_count(phy_page) > 1) { + uint32_t order = phy_page->bbpage.order; + uint32_t block_size = 1UL << order; + for (int i = 0; i < block_size; i++) { + page_dec(phy_page + i); + } + } else { + __free_pages(phy_page); + } + + size -= area_size; + area_start += area_size; + } + // Free the vm_area_struct. + + // Delete segment from the mmap + it = segment->vm_list.next; + list_head_del(&segment->vm_list); + --mm->map_count; + + kmem_cache_free(segment); + } + + // Free all the page tables + for (int i = 0; i < 1024; i++) { + page_dir_entry_t *entry = &mm->pgd->entries[i]; + if (entry->present && !entry->global) { + page_t *pgt_page = get_page_from_physical_address(entry->frame * PAGE_SIZE); + uint32_t pgt_addr = get_lowmem_address_from_page(pgt_page); + kmem_cache_free((void *)pgt_addr); + } + } + kmem_cache_free((void *)mm->pgd); + + // Free the mm_struct. + kmem_cache_free(mm); } diff --git a/mentos/src/mem/slab.c b/mentos/src/mem/slab.c new file mode 100644 index 0000000..7417b68 --- /dev/null +++ b/mentos/src/mem/slab.c @@ -0,0 +1,354 @@ +/// MentOS, The Mentoring Operating system project +/// @file mouse.h +/// @brief Driver for *PS2* Mouses. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[SLAB ]" + +#include "zone_allocator.h" +#include "paging.h" +#include "assert.h" +#include "debug.h" +#include "slab.h" + +/// @brief Use it to manage cached pages. +typedef struct kmem_obj { + /// The list_head for this object. + list_head objlist; +} kmem_obj; + +/// Max order of kmalloc cache allocations, if greater raw page allocation is done. +#define MAX_KMALLOC_CACHE_ORDER 12 + +#define KMEM_OBJ_OVERHEAD sizeof(kmem_obj) +#define KMEM_START_OBJ_COUNT 8 +#define KMEM_MAX_REFILL_OBJ_COUNT 64 +#define KMEM_OBJ(cachep, addr) ((kmem_obj *)(addr)) +#define ADDR_FROM_KMEM_OBJ(cachep, kmem_obj) ((void *)(kmem_obj)) + +// The list of caches. +static list_head kmem_caches_list; +// Cache where we will store the data about caches. +static kmem_cache_t kmem_cache; +// Caches for each order of the malloc. +static kmem_cache_t *malloc_blocks[MAX_KMALLOC_CACHE_ORDER]; + +static int __alloc_slab_page(kmem_cache_t *cachep, gfp_t flags) +{ + page_t *page = _alloc_pages(flags, cachep->gfp_order); + if (!page) { + pr_crit("Failed to allocate a new page from slab.\n"); + return -1; + } + + list_head_init(&page->slabs); + + // Save in the root page the kmem_cache_t pointer, + // to allow freeing arbitrary pointers + page[0].container.slab_cache = cachep; + + // Update slab main pages of all child pages, to allow + // reconstructing which page handles a specified address + for (unsigned int i = 1; i < (1U << cachep->gfp_order); i++) { + page[i].container.slab_main_page = page; + } + + unsigned int slab_size = PAGE_SIZE * (1U << cachep->gfp_order); + + // Update the page objects counters + page->slab_objcnt = slab_size / cachep->size; + page->slab_objfree = page->slab_objcnt; + + unsigned int pg_addr = get_lowmem_address_from_page(page); + + list_head_init(&page->slab_freelist); + + // Build the objects structures + for (unsigned int i = 0; i < page->slab_objcnt; i++) { + kmem_obj *obj = KMEM_OBJ(cachep, pg_addr + cachep->size * i); + list_head_add(&obj->objlist, &page->slab_freelist); + } + + // Add the page to the slab list and update the counters + list_head_add(&page->slabs, &cachep->slabs_free); + cachep->total_num += page->slab_objcnt; + cachep->free_num += page->slab_objcnt; + + return 0; +} + +static void __kmem_cache_refill(kmem_cache_t *cachep, unsigned int free_num, gfp_t flags) +{ + while (cachep->free_num < free_num) { + if (__alloc_slab_page(cachep, flags) < 0) { + pr_warning("Cannot allocate a page, abort refill\n"); + break; + } + } +} + +static unsigned int __find_next_alignment(unsigned int size, unsigned int align) +{ + return (size / align + (size % align ? 1 : 0)) * align; +} + +static void __compute_size_and_order(kmem_cache_t *cachep) +{ + // Align the whole object to the required padding + cachep->size = __find_next_alignment( + max(cachep->object_size, KMEM_OBJ_OVERHEAD), + max(8, cachep->align)); + + // Compute the gfp order + unsigned int size = __find_next_alignment(cachep->size, PAGE_SIZE) / PAGE_SIZE; + while ((size /= 2) > 0) { + cachep->gfp_order++; + } +} + +static void __kmem_cache_create(kmem_cache_t *cachep, const char *name, unsigned int size, unsigned int align, slab_flags_t flags, void (*ctor)(void *), void (*dtor)(void *), unsigned int start_count) +{ + pr_info("Creating new cache `%s` with objects of size `%d`.\n", name, size); + + *cachep = (kmem_cache_t){ + .name = name, + .object_size = size, + .align = align, + .flags = flags, + .ctor = ctor, + .dtor = dtor + }; + + list_head_init(&cachep->slabs_free); + list_head_init(&cachep->slabs_partial); + list_head_init(&cachep->slabs_full); + + __compute_size_and_order(cachep); + + __kmem_cache_refill(cachep, start_count, flags); + + list_head_add(&cachep->cache_list, &kmem_caches_list); +} + +static inline void *__kmem_cache_alloc_slab(kmem_cache_t *cachep, page_t *slab_page) +{ + list_head *elem_listp = list_head_pop(&slab_page->slab_freelist); + if (!elem_listp) { + pr_warning("There are no FREE element inside the slab_freelist\n"); + return NULL; + } + slab_page->slab_objfree--; + cachep->free_num--; + + kmem_obj *obj = list_entry(elem_listp, kmem_obj, objlist); + + // Get the element from the kmem_obj object + void *elem = ADDR_FROM_KMEM_OBJ(cachep, obj); + + if (cachep->ctor) + cachep->ctor(elem); + + return elem; +} + +static inline void __kmem_cache_free_slab(kmem_cache_t *cachep, page_t *slab_page) +{ + cachep->free_num -= slab_page->slab_objfree; + cachep->total_num -= slab_page->slab_objcnt; + // Clear objcnt, used as a flag to check if the page belongs to the slab + slab_page->slab_objcnt = 0; + slab_page->container.slab_main_page = NULL; + + // Reset all non-root slab pages + for (unsigned int i = 1; i < (1U << cachep->gfp_order); i++) { + (slab_page + i)->container.slab_main_page = NULL; + } + + __free_pages(slab_page); +} + +void kmem_cache_init() +{ + // Initialize the list of caches. + list_head_init(&kmem_caches_list); + // Create a cache to store the data about caches. + __kmem_cache_create( + &kmem_cache, + "kmem_cache_t", + sizeof(kmem_cache_t), + alignof(kmem_cache_t), + GFP_KERNEL, + NULL, + NULL, 32); + for (unsigned int i = 0; i < MAX_KMALLOC_CACHE_ORDER; i++) { + malloc_blocks[i] = kmem_cache_create( + "kmalloc", + 1u << i, + 1u << i, + GFP_KERNEL, + NULL, + NULL); + } +} + +kmem_cache_t *kmem_cache_create(const char *name, unsigned int size, unsigned int align, slab_flags_t flags, void (*ctor)(void *), void (*dtor)(void *)) +{ + kmem_cache_t *cachep = (kmem_cache_t *)kmem_cache_alloc(&kmem_cache, GFP_KERNEL); + if (!cachep) + return cachep; + + __kmem_cache_create(cachep, name, size, align, flags, ctor, dtor, KMEM_START_OBJ_COUNT); + + return cachep; +} + +void kmem_cache_destroy(kmem_cache_t *cachep) +{ + while (!list_head_empty(&cachep->slabs_free)) { + list_head *slab_list = list_head_pop(&cachep->slabs_free); + __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); + } + + while (!list_head_empty(&cachep->slabs_partial)) { + list_head *slab_list = list_head_pop(&cachep->slabs_partial); + __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); + } + + while (!list_head_empty(&cachep->slabs_full)) { + list_head *slab_list = list_head_pop(&cachep->slabs_full); + __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); + } + + kmem_cache_free(cachep); + list_head_del(&cachep->cache_list); +} + +#ifdef ENABLE_CACHE_TRACE +void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cache_t *cachep, gfp_t flags) +#else +void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags) +#endif +{ + if (list_head_empty(&cachep->slabs_partial)) { + if (list_head_empty(&cachep->slabs_free)) { + if (flags == 0) + flags = cachep->flags; + + // Refill the cache in an exponential fashion, capping at KMEM_MAX_REFILL_OBJ_COUNT to avoid + // too big allocations + __kmem_cache_refill(cachep, min(cachep->total_num, KMEM_MAX_REFILL_OBJ_COUNT), flags); + if (list_head_empty(&cachep->slabs_free)) { + pr_crit("Cannot allocate more slabs in `%s`\n", cachep->name); + return NULL; + } + } + + // Add a free slab to partial list because in any case an element will + // be removed before the function returns + list_head *free_slab = list_head_pop(&cachep->slabs_free); + list_head_add(free_slab, &cachep->slabs_partial); + } + + page_t *slab_page = list_entry(list_head_front(&cachep->slabs_partial), page_t, slabs); + void *ptr = __kmem_cache_alloc_slab(cachep, slab_page); + + // If the slab is now full, add it to the full slabs list + if (slab_page->slab_objfree == 0) { + list_head *slab_full_elem = list_head_pop(&cachep->slabs_partial); + list_head_add(slab_full_elem, &cachep->slabs_full); + } +#ifdef ENABLE_CACHE_TRACE + pr_notice("kmem_cache_alloc : (%-16s:%3d)[%-16s] : 0x%p\n", file, line, cachep->name, ptr); +#endif + return ptr; +} + +#ifdef ENABLE_CACHE_TRACE +void pr_kmem_cache_free(const char *file, const char *fun, int line, void *ptr) +#else +void kmem_cache_free(void *ptr) +#endif +{ + page_t *slab_page = get_lowmem_page_from_address((uint32_t)ptr); + + // If the slab main page is a lowmem page, change to it as it's the root page + if (is_lowmem_page_struct(slab_page->container.slab_main_page)) { + slab_page = slab_page->container.slab_main_page; + } + + kmem_cache_t *cachep = slab_page->container.slab_cache; + +#ifdef ENABLE_CACHE_TRACE + pr_notice("kmem_cache_free : (%-16s:%3d)[%-16s] : 0x%p\n", file, line, cachep->name, ptr); +#endif + if (cachep->dtor) + cachep->dtor(ptr); + + kmem_obj *obj = KMEM_OBJ(cachep, ptr); + + // Add object to the free list + list_head_add(&obj->objlist, &slab_page->slab_freelist); + slab_page->slab_objfree++; + cachep->free_num++; + + // Now page is completely free + if (slab_page->slab_objfree == slab_page->slab_objcnt) { + // Remove page from partial list + list_head_del(&slab_page->slabs); + // Add page to free list + list_head_add(&slab_page->slabs, &cachep->slabs_free); + } + // Now page is not full, so change its list + else if (slab_page->slab_objfree == 1) { + // Remove page from full list + list_head_del(&slab_page->slabs); + // Add page to partial list + list_head_add(&slab_page->slabs, &cachep->slabs_partial); + } +} + +#ifdef ENABLE_ALLOC_TRACE +void *pr_kmalloc(const char *file, const char *fun, int line, unsigned int size) +#else +void *kmalloc(unsigned int size) +#endif +{ + unsigned int order = 0; + while (size != 0) { + order++; + size /= 2; + } + + // If size does not fit in the maximum cache order, allocate raw pages + void *ptr; + if (order >= MAX_KMALLOC_CACHE_ORDER) { + ptr = (void *)__alloc_pages_lowmem(GFP_KERNEL, order - 12); + } else { + ptr = kmem_cache_alloc(malloc_blocks[order], GFP_KERNEL); + } +#ifdef ENABLE_ALLOC_TRACE + pr_notice("kmalloc : (%-16s:%3d) : 0x%p\n", file, line, ptr); +#endif + return ptr; +} + +#ifdef ENABLE_ALLOC_TRACE +void pr_kfree(const char *file, const char *fun, int line, void *ptr) +#else +void kfree(void *ptr) +#endif +{ +#ifdef ENABLE_ALLOC_TRACE + pr_notice("kfree : (%-16s:%3d) : 0x%p\n", file, line, ptr); +#endif + page_t *page = get_lowmem_page_from_address((uint32_t)ptr); + + // If the address is part of the cache + if (page->container.slab_main_page) { + kmem_cache_free(ptr); + } else { + free_pages_lowmem((uint32_t)ptr); + } +} diff --git a/mentos/src/mem/vmem_map.c b/mentos/src/mem/vmem_map.c new file mode 100644 index 0000000..4d45cc7 --- /dev/null +++ b/mentos/src/mem/vmem_map.c @@ -0,0 +1,176 @@ +/// MentOS, The Mentoring Operating system project +/// @file vmem_map.c +/// @brief Virtual memory mapping routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[VMEM ]" + +#include "vmem_map.h" +#include "string.h" +#include "panic.h" + +/// Virtual addresses manager. +static virt_map_page_manager_t virt_default_mapping; + +/// TODO: check. +#define VIRTUAL_MEMORY_PAGES_COUNT (VIRTUAL_MEMORY_SIZE_MB * 256) +/// TODO: check. +#define VIRTUAL_MAPPING_BASE (PROCAREA_END_ADDR + 0x38000000) +/// TODO: check. +#define VIRT_PAGE_TO_ADDRESS(page) ((((page)-virt_pages) * PAGE_SIZE) + VIRTUAL_MAPPING_BASE) +/// TODO: check. +#define VIRT_ADDRESS_TO_PAGE(addr) ((((addr)-VIRTUAL_MAPPING_BASE) / PAGE_SIZE) + virt_pages) + +/// Array of virtual pages. +virt_map_page_t virt_pages[VIRTUAL_MEMORY_PAGES_COUNT]; + +void virt_init(void) +{ + buddy_system_init( + &virt_default_mapping.bb_instance, + "virt_manager", + virt_pages, + BBSTRUCT_OFFSET(virt_map_page_t, bbpage), + sizeof(virt_map_page_t), + VIRTUAL_MEMORY_PAGES_COUNT); + + page_directory_t *mainpgd = paging_get_main_directory(); + + uint32_t start_virt_pfn = VIRTUAL_MAPPING_BASE / PAGE_SIZE; + uint32_t start_virt_pgt = start_virt_pfn / 1024; + uint32_t start_virt_tbl_idx = start_virt_pfn % 1024; + + uint32_t pfn_num = VIRTUAL_MEMORY_PAGES_COUNT; + + // Alloc all page tables inside the main directory, so they will be shared across + // all page directories of processes + for (uint32_t i = start_virt_pgt; i < 1024 && (pfn_num > 0); i++) { + page_dir_entry_t *entry = mainpgd->entries + i; + + page_table_t *table; + + // Alloc virtual page table + entry->present = 1; + entry->rw = 0; + entry->global = 1; + entry->user = 0; + entry->accessed = 0; + entry->available = 1; + table = kmem_cache_alloc(pgtbl_cache, GFP_KERNEL); + + uint32_t start_page = (i == start_virt_pgt) ? start_virt_tbl_idx : 0; + + for (uint32_t j = start_page; j < 1024 && (pfn_num > 0); j++, pfn_num--) { + table->pages[j].frame = 0; + table->pages[j].rw = 0; + table->pages[j].present = 0; + table->pages[j].global = 1; + table->pages[j].user = 0; + } + + page_t *table_page = get_lowmem_page_from_address((uint32_t)table); + uint32_t phy_addr = get_physical_address_from_page(table_page); + entry->frame = phy_addr >> 12u; + } +} + +static virt_map_page_t *_alloc_virt_pages(uint32_t pfn_count) +{ + int order = find_nearest_order_greater(0, pfn_count << 12); + virt_map_page_t *vpage = PG_FROM_BBSTRUCT(bb_alloc_pages(&virt_default_mapping.bb_instance, order), virt_map_page_t, bbpage); + return vpage; +} + +uint32_t virt_map_physical_pages(page_t *page, int pfn_count) +{ + virt_map_page_t *vpage = _alloc_virt_pages(pfn_count); + if (!vpage) + return 0; + + uint32_t virt_address = VIRT_PAGE_TO_ADDRESS(vpage); + uint32_t phy_address = get_physical_address_from_page(page); + + mem_upd_vm_area(paging_get_main_directory(), virt_address, phy_address, + pfn_count * PAGE_SIZE, MM_PRESENT | MM_RW | MM_GLOBAL | MM_UPDADDR); + return virt_address; +} + +virt_map_page_t *virt_map_alloc(uint32_t size) +{ + uint32_t pages_count = (size + PAGE_SIZE - 1) / PAGE_SIZE; + return _alloc_virt_pages(pages_count); +} + +uint32_t virt_map_vaddress(mm_struct_t *mm, virt_map_page_t *vpage, uint32_t vaddr, uint32_t size) +{ + uint32_t start_map_virt_address = VIRT_PAGE_TO_ADDRESS(vpage); + + // Clone the source vaddr the the requested virtual memory portion + mem_clone_vm_area(mm->pgd, + paging_get_main_directory(), + vaddr, + start_map_virt_address, + size, + MM_PRESENT | MM_RW | MM_GLOBAL | MM_UPDADDR); + return start_map_virt_address; +} + +int virtual_check_address(uint32_t addr) +{ + return addr >= VIRTUAL_MAPPING_BASE; // && addr < VIRTUAL_MAPPING_BASE + VIRTUAL_MEMORY_PAGES_COUNT * PAGE_SIZE; +} + +void virt_unmap(uint32_t addr) +{ + virt_map_page_t *page = VIRT_ADDRESS_TO_PAGE(addr); + virt_unmap_pg(page); +} + +void virt_unmap_pg(virt_map_page_t *page) +{ + uint32_t addr = VIRT_PAGE_TO_ADDRESS(page); + + // Set all virtual pages as not present + mem_upd_vm_area(paging_get_main_directory(), addr, 0, + (1 << page->bbpage.order) * PAGE_SIZE, MM_GLOBAL); + + // and avoiding unwanted memory accesses by the kernel + bb_free_pages(&virt_default_mapping.bb_instance, &page->bbpage); +} + +// FIXME: Check if this function should support unaligned page-boundaries copy +void virt_memcpy(mm_struct_t *dst_mm, uint32_t dst_vaddr, mm_struct_t *src_mm, uint32_t src_vaddr, uint32_t size) +{ + const uint32_t VMEM_BUFFER_SIZE = 65536; + + uint32_t buffer_size = min(VMEM_BUFFER_SIZE, size); + + virt_map_page_t *src_vpage = virt_map_alloc(size); + virt_map_page_t *dst_vpage = virt_map_alloc(size); + + if (!src_vpage || !dst_vpage) { + kernel_panic("Cannot copy virtual memory address, unable to reserve vmem!"); + } + + for (;;) { + uint32_t src_map = virt_map_vaddress(src_mm, src_vpage, src_vaddr, buffer_size); + uint32_t dst_map = virt_map_vaddress(dst_mm, dst_vpage, dst_vaddr, buffer_size); + + uint32_t cpy_size = min(buffer_size, size); + + memcpy((void *)dst_map, (void *)src_map, cpy_size); + + if (size <= buffer_size) { + break; + } + + size -= cpy_size; + src_vaddr += cpy_size; + dst_vaddr += cpy_size; + } + + virt_unmap_pg(src_vpage); + virt_unmap_pg(dst_vpage); +} diff --git a/mentos/src/mem/zone_allocator.c b/mentos/src/mem/zone_allocator.c index ddb3989..67ffa16 100644 --- a/mentos/src/mem/zone_allocator.c +++ b/mentos/src/mem/zone_allocator.c @@ -1,229 +1,204 @@ /// MentOS, The Mentoring Operating system project /// @file zone_allocator.c /// @brief Implementation of the Zone Allocator -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Change the header. +#define __DEBUG_HEADER__ "[PMM ]" + #include "zone_allocator.h" #include "buddysystem.h" -#include "debug.h" -#include "kheap.h" +#include "list_head.h" #include "kernel.h" #include "assert.h" #include "paging.h" +#include "string.h" +#include "debug.h" -#define MAX_ORDER_ALIGN(addr) \ - ((addr) & (~(PAGE_SIZE * (1 << (MAX_ORDER - 1)) - 1))) + \ - (PAGE_SIZE * (1 << (MAX_ORDER - 1))) - -/// Defined in kernel.ld, points at the end of kernel's data segment. -extern uint32_t end; - -/// End address of the kernel's data segment. -static uint8_t *_mmngr_memory_start = (uint8_t *)(&end); +/// TODO: Comment. +#define MIN_PAGE_ALIGN(addr) ((addr) & (~(PAGE_SIZE - 1))) +/// TODO: Comment. +#define MAX_PAGE_ALIGN(addr) (((addr) & (~(PAGE_SIZE - 1))) + PAGE_SIZE) +/// TODO: Comment. +#define MIN_ORDER_ALIGN(addr) ((addr) & (~((PAGE_SIZE << (MAX_BUDDYSYSTEM_GFP_ORDER - 1)) - 1))) +/// TODO: Comment. +#define MAX_ORDER_ALIGN(addr) \ + (((addr) & (~((PAGE_SIZE << (MAX_BUDDYSYSTEM_GFP_ORDER - 1)) - 1))) + \ + (PAGE_SIZE << (MAX_BUDDYSYSTEM_GFP_ORDER - 1))) /// Array of all physical blocks page_t *mem_map = NULL; - /// Memory node. pg_data_t *contig_page_data = NULL; +/// Low memory virtual base address. +uint32_t lowmem_virt_base = 0; +/// Low memory base address. +uint32_t lowmem_page_base = 0; -/// @brief Get the zone that contains a page frame. -/// @param page A page descriptor. -/// @return The zone requested. +page_t *get_lowmem_page_from_address(uint32_t addr) +{ + unsigned int offset = addr - lowmem_virt_base; + return mem_map + lowmem_page_base + (offset / PAGE_SIZE); +} + +uint32_t get_lowmem_address_from_page(page_t *page) +{ + unsigned int offset = (page - mem_map) - lowmem_page_base; + return lowmem_virt_base + offset * PAGE_SIZE; +} + +uint32_t get_physical_address_from_page(page_t *page) +{ + return (page - mem_map) * PAGE_SIZE; +} + +page_t *get_page_from_physical_address(uint32_t phy_addr) +{ + return mem_map + (phy_addr / PAGE_SIZE); +} + +/// @brief Get the zone that contains a page frame. +/// @param page A page descriptor. +/// @return The zone requested. static zone_t *get_zone_from_page(page_t *page) { - zone_t *zone = NULL; - int nr_zones = contig_page_data->nr_zones; - - for (int zone_index = 0; zone_index < nr_zones; zone_index++) { - zone = contig_page_data->node_zones + zone_index; - page_t *last_page = zone->zone_mem_map + zone->size; - - if (page < last_page) { - return zone; - } - } - - // Error: page is over memory size. - return (zone_t *)NULL; + zone_t *zone; + page_t *last_page; + // Iterate over all the zones. + for (int zone_index = 0; zone_index < contig_page_data->nr_zones; zone_index++) { + // Get the zone at the given index. + zone = contig_page_data->node_zones + zone_index; + assert(zone && "Failed to retrieve the zone."); + // Get the last page of the zone. + last_page = zone->zone_mem_map + zone->size; + assert(last_page && "Failed to retrieve the last page of the zone."); + // Check if the page is before the last page of the zone. + if (page < last_page) + return zone; + } + // Error: page is over memory size. + return (zone_t *)NULL; } -/// @brief Get a zone from gfp_mask -/// @param gfp_mask GFP_FLAG see gfp.h. -/// @return The zone requested. +/// @brief Get a zone from gfp_mask +/// @param gfp_mask GFP_FLAG see gfp.h. +/// @return The zone requested. static zone_t *get_zone_from_flags(gfp_t gfp_mask) { - switch (gfp_mask) { - case GFP_KERNEL: - case GFP_ATOMIC: - case GFP_NOFS: - case GFP_NOIO: - case GFP_NOWAIT: - return &contig_page_data->node_zones[ZONE_NORMAL]; - case GFP_HIGHUSER: - return &contig_page_data->node_zones[ZONE_HIGHMEM]; - default: - return (zone_t *)NULL; - } + switch (gfp_mask) { + case GFP_KERNEL: + case GFP_ATOMIC: + case GFP_NOFS: + case GFP_NOIO: + case GFP_NOWAIT: + return &contig_page_data->node_zones[ZONE_NORMAL]; + case GFP_HIGHUSER: + return &contig_page_data->node_zones[ZONE_HIGHMEM]; + default: + return (zone_t *)NULL; + } } -static bool_t is_memory_clean(gfp_t gfp_mask) +static int is_memory_clean(gfp_t gfp_mask) { - bool_t memory_clean = true; - zone_t *zone = get_zone_from_flags(gfp_mask); - assert((zone != NULL) && "Invalid zone flag!"); - -#ifdef ENABLE_BUDDYSYSTEM - /* Check every field nr_free of the buddy system - * descriptor of the zone that all blocks are - * allocated on the last free list. - */ - unsigned int order = 0; - for (; order < MAX_ORDER - 1; ++order) { - free_area_t *area = zone->free_area + order; - if (area->nr_free != 0) { - memory_clean = false; - - break; - } - } - - if (memory_clean && - (zone->free_area[order].nr_free != (zone->size / (1UL << order)))) { - memory_clean = false; - } - -#else - /* Check every field _count of the page descriptor - * of the zone as free. - */ - for (int i = 0; i < zone->size; ++i) { - page_t *page = zone->zone_mem_map + i; - if (page->_count != -1) { - memory_clean = false; - - break; - } - } -#endif - - return memory_clean; + // Get the corresponding zone. + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Failed to retrieve the zone given the gfp_mask!"); + // Get the last free area list of the buddy system. + bb_free_area_t *area = zone->buddy_system.free_area + (MAX_BUDDYSYSTEM_GFP_ORDER - 1); + assert(area && "Failed to retrieve the last free_area for the given zone!"); + // Compute the total size of the zone. + unsigned int total_size = (zone->size / (1UL << (MAX_BUDDYSYSTEM_GFP_ORDER - 1))); + // Check if the size of the zone is equal to the remaining pages inside the free area. + if (area->nr_free != total_size) { + pr_crit("Number of blocks of free pages is different than expected (%d vs %d).\n", area->nr_free, total_size); + buddy_system_dump(&zone->buddy_system); + return 0; + } + return 1; } /// @brief Checks if the physical memory manager is working properly. /// @return If the check was done correctly. -static bool_t pmm_check() +static int pmm_check() { - dbg_print( - "\n=================== ZONE ALLOCATOR TEST ==================== \n"); + pr_debug( + "\n=================== ZONE ALLOCATOR TEST ==================== \n"); - dbg_print("\t[STEP1] One page frame in kernel-space... "); - dbg_print("\n\t ===== [STEP1] One page frame in kernel-space ====\n"); - uint32_t ptr1 = __alloc_page(GFP_KERNEL); - free_page(ptr1); - if (!is_memory_clean(GFP_KERNEL)) { - return false; - } + pr_debug("\t[STEP1] One page frame in kernel-space... "); + pr_debug("\n\t ===== [STEP1] One page frame in kernel-space ====\n"); + pr_debug("\n\t ----- ALLOC -------------------------------------\n"); + uint32_t ptr1 = __alloc_page_lowmem(GFP_KERNEL); + pr_debug("\n\t ----- FREE --------------------------------------\n"); + free_page_lowmem(ptr1); + if (!is_memory_clean(GFP_KERNEL)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } - dbg_print("\t[STEP2] Five page frames in user-space... "); - dbg_print("\n\t ===== [STEP2] Five page frames in user-space ====\n"); - uint32_t ptr2[5]; - for (int i = 0; i < 5; i++) { - ptr2[i] = __alloc_page(GFP_HIGHUSER); - } - for (int i = 0; i < 5; i++) { - free_page(ptr2[i]); - } - if (!is_memory_clean(GFP_HIGHUSER)) { - return false; - } + pr_debug("\t[STEP2] Five page frames in user-space... "); + pr_debug("\n\t ===== [STEP2] Five page frames in user-space ====\n"); + page_t *ptr2[5]; + for (int i = 0; i < 5; i++) { + ptr2[i] = _alloc_pages(GFP_HIGHUSER, 0); + } + for (int i = 0; i < 5; i++) { + __free_pages(ptr2[i]); + } + if (!is_memory_clean(GFP_HIGHUSER)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } - dbg_print("\t[STEP3] 2^{3} page frames in kernel-space... "); - dbg_print("\n\t ===== [STEP3] 2^{3} page frames in kernel-space ====\n"); - uint32_t ptr3 = __alloc_pages(GFP_KERNEL, 3); - free_pages(ptr3, 3); - if (!is_memory_clean(GFP_KERNEL)) { - return false; - } + pr_debug("\t[STEP3] 2^{3} page frames in kernel-space... "); + pr_debug("\n\t ===== [STEP3] 2^{3} page frames in kernel-space ====\n"); + uint32_t ptr3 = __alloc_pages_lowmem(GFP_KERNEL, 3); + free_pages_lowmem(ptr3); + if (!is_memory_clean(GFP_KERNEL)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } - dbg_print("\t[STEP4] Five 2^{i} page frames in user-space... "); - dbg_print("\n\t ===== [STEP4] Five 2^{i} page frames in user-space ====\n"); - uint32_t ptr4[5]; - for (int i = 0; i < 5; i++) { - ptr4[i] = __alloc_pages(GFP_HIGHUSER, i); - } - for (int i = 0; i < 5; i++) { - free_pages(ptr4[i], i); - } - if (!is_memory_clean(GFP_HIGHUSER)) { - return false; - } + pr_debug("\t[STEP4] Five 2^{i} page frames in user-space... "); + pr_debug("\n\t ===== [STEP4] Five 2^{i} page frames in user-space ====\n"); + page_t *ptr4[5]; + for (int i = 0; i < 5; i++) { + ptr4[i] = _alloc_pages(GFP_HIGHUSER, i); + } + for (int i = 0; i < 5; i++) { + __free_pages(ptr4[i]); + } + if (!is_memory_clean(GFP_HIGHUSER)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } - dbg_print("\t[STEP5] Mixed page frames in kernel-space... "); - dbg_print("\n\t ===== [STEP5] Mixed page frames in kernel-space ====\n"); - int **ptr = (int **)__alloc_page(GFP_KERNEL); - int i = 0; - for (; i < 5; ++i) { - ptr[i] = (int *)__alloc_page(GFP_KERNEL); - } - for (; i < 20; ++i) { - ptr[i] = (int *)__alloc_pages(GFP_KERNEL, 2); - } + pr_debug("\t[STEP5] Mixed page frames in kernel-space... "); + pr_debug("\n\t ===== [STEP5] Mixed page frames in kernel-space ====\n"); + int **ptr = (int **)__alloc_page_lowmem(GFP_KERNEL); + int i = 0; + for (; i < 5; ++i) { + ptr[i] = (int *)__alloc_page_lowmem(GFP_KERNEL); + } + for (; i < 20; ++i) { + ptr[i] = (int *)__alloc_pages_lowmem(GFP_KERNEL, 2); + } - int j = 0; - for (; j < 5; ++j) { - free_page((uint32_t)ptr[j]); - } - for (; j < 20; ++j) { - free_pages((uint32_t)ptr[j], 2); - } - free_page((uint32_t)ptr1); + int j = 0; + for (; j < 5; ++j) { + free_page_lowmem((uint32_t)ptr[j]); + } + for (; j < 20; ++j) { + free_pages_lowmem((uint32_t)ptr[j]); + } + free_page_lowmem((uint32_t)ptr1); - if (!is_memory_clean(GFP_KERNEL)) { - return false; - } - return true; -} - -/// @brief Initialize Buddy System. -/// @param zone A memory zone. -static void buddy_system_init(zone_t *zone) -{ - // Initialize the free_lists of each area of the zone. - for (unsigned int order = 0; order < MAX_ORDER; order++) { - free_area_t *area = zone->free_area + order; - area->nr_free = 0; - list_head_init(&area->free_list); - } - - // Current base page descriptor of the zone. - page_t *page = zone->zone_mem_map; - // Address of the last page descriptor of the zone. - page_t *last_page = page + zone->size; - - // Get the free area collecting the larges block of page frames. - const unsigned int order = MAX_ORDER - 1; - free_area_t *area = zone->free_area + order; - - // Add all zone's pages to the largest free area block. - uint32_t block_size = 1UL << order; - while ((page + block_size) <= last_page) { - /* page has already the _count field set to -1, - * therefore only save the order of the page. - */ - page->private = order; - - // Insert page as first element in the list. - list_head_add_tail(&page->lru, &area->free_list); - // Increase the number of free block of the free_area_t. - area->nr_free++; - - page += block_size; - } - - assert(page == last_page && - "Memory size is not aligned to MAX_ORDER size!"); + if (!is_memory_clean(GFP_KERNEL)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } + return 1; } /// @brief Initializes the memory attributes. @@ -231,244 +206,274 @@ static void buddy_system_init(zone_t *zone) /// @param zone_index Zone's index. /// @param adr_from the lowest address of the zone /// @param adr_to the highest address of the zone (not included!) -static void zone_init(char *name, int zone_index, uint32_t adr_from, - uint32_t adr_to) +static void zone_init(char *name, int zone_index, uint32_t adr_from, uint32_t adr_to) { - assert((adr_from < adr_to) && ((adr_from & 0xfffff000) == adr_from) && - ((adr_to & 0xfffff000) == adr_to) && - "Inserted bad block addresses!"); + assert((adr_from < adr_to) && "Inserted bad block addresses!"); + assert(((adr_from & 0xfffff000) == adr_from) && "Inserted bad block addresses!"); + assert(((adr_to & 0xfffff000) == adr_to) && "Inserted bad block addresses!"); + assert((zone_index < contig_page_data->nr_zones) && "The index is above the number of zones."); + // Take the zone_t structure that correspondes to the zone_index. + zone_t *zone = contig_page_data->node_zones + zone_index; + assert(zone && "Failed to retrieve the zone."); + // Number of page frames in the zone. + size_t num_page_frames = (adr_to - adr_from) / PAGE_SIZE; + // Index of the first page frame of the zone. + uint32_t first_page_frame = adr_from / PAGE_SIZE; + // Update zone info. + zone->name = name; + zone->size = num_page_frames; + zone->free_pages = num_page_frames; + zone->zone_mem_map = mem_map + first_page_frame; + zone->zone_start_pfn = first_page_frame; + // Dump the information. + pr_debug("ZONE %s, first page: %p, last page: %p, npages:%d\n", zone->name, + zone->zone_mem_map, zone->zone_mem_map + zone->size, zone->size); + // Set to zero all page structures. + memset(zone->zone_mem_map, 0, zone->size * sizeof(page_t)); + // Initialize the buddy system for the new zone. + buddy_system_init(&zone->buddy_system, + name, + zone->zone_mem_map, + BBSTRUCT_OFFSET(page_t, bbpage), + sizeof(page_t), + num_page_frames); + buddy_system_dump(&zone->buddy_system); +} - // Take the zone_t structure that correspondes to the zone_index. - zone_t *zone = contig_page_data->node_zones + zone_index; +/* + * AAAABBBBCCCC + * ZZZZZZ + * + * */ - // Number of page frames in the zone. - size_t num_page_frames = (adr_to - adr_from) / PAGE_SIZE; +unsigned int find_nearest_order_greater(uint32_t base_addr, uint32_t amount) +{ + uint32_t start_pfn = base_addr / PAGE_SIZE; + uint32_t end_pfn = (base_addr + amount + PAGE_SIZE - 1) / PAGE_SIZE; + // Get the number of pages. + uint32_t npages = end_pfn - start_pfn; + // Find the fitting order. + unsigned int order = 0; + while ((1UL << order) < npages) { + ++order; + } + return order; +} - // Index of the first page frame of the zone. - uint32_t first_page_frame = adr_from / PAGE_SIZE; +int pmmngr_init(boot_info_t *boot_info) +{ + //======================================================================= - // Update zone info. - zone->name = name; - zone->size = num_page_frames; - zone->free_pages = num_page_frames; - zone->zone_mem_map = mem_map + first_page_frame; - zone->zone_start_pfn = first_page_frame; + uint32_t lowmem_phy_start = boot_info->lowmem_phy_start; - dbg_print("ZONE %s, first page: %p, last page: %p, npages:%d\n", zone->name, - zone->zone_mem_map, zone->zone_mem_map + zone->size, zone->size); + // Now we have skipped all modules in physical space, is time to + // consider also virtual lowmem space! + uint32_t lowmem_virt_start = boot_info->lowmem_start + (lowmem_phy_start - boot_info->lowmem_phy_start); -#ifdef ENABLE_BUDDYSYSTEM - buddy_system_init(zone); - buddy_system_dump(zone); + pr_debug("Start memory address after skip modules (phy => virt) : 0x%p => 0x%p \n", + lowmem_phy_start, lowmem_virt_start); + //======================================================================= + + //==== Initialize array of page_t ======================================= + pr_debug("Initializing low memory map structure...\n"); + mem_map = (page_t *)lowmem_virt_start; + + uint32_t mem_size = boot_info->highmem_phy_end; + + // Total number of blocks (all lowmem+highmem RAM). + uint32_t mem_num_frames = mem_size / PAGE_SIZE; + + // Initialize each page_t. + for (int page_index = 0; page_index < mem_num_frames; ++page_index) { + page_t *page = mem_map + page_index; + // Mark page as free. + set_page_count(page, 0); + } + //======================================================================= + + //==== Skip memory space used for page_t[] ============================== + lowmem_phy_start += sizeof(page_t) * mem_num_frames; + lowmem_virt_start += sizeof(page_t) * mem_num_frames; + pr_debug("Size of mem_map : %i byte [0x%p - 0x%p]\n", + (char *)lowmem_virt_start - (char *)mem_map, mem_map, + lowmem_virt_start); + //======================================================================= + + //==== Initialize contig_page_data node ================================= + pr_debug("Initializing contig_page_data node...\n"); + contig_page_data = (pg_data_t *)lowmem_virt_start; + // ZONE_NORMAL and ZONE_HIGHMEM + contig_page_data->nr_zones = __MAX_NR_ZONES; + // NID start from 0. + contig_page_data->node_id = 0; + // Corresponds with mem_map. + contig_page_data->node_mem_map = mem_map; + // In UMA we have only one node. + contig_page_data->node_next = NULL; + // All the memory. + contig_page_data->node_size = mem_num_frames; + // mem_map[0]. + contig_page_data->node_start_mapnr = 0; + // The first physical page. + contig_page_data->node_start_paddr = 0x0; + //======================================================================= + + //==== Skip memory space used for pg_data_t ============================= + lowmem_phy_start += sizeof(pg_data_t); + lowmem_virt_start += sizeof(pg_data_t); + //======================================================================= + + //==== Initialize zones zone_t ========================================== + pr_debug("Initializing zones...\n"); + + // ZONE_NORMAL [ memory_start - mem_size/4 ] + uint32_t start_normal_addr = MAX_PAGE_ALIGN(lowmem_phy_start); + uint32_t stop_normal_addr = MIN_PAGE_ALIGN(boot_info->lowmem_phy_end); + + // Move the stop address so that the size is a multiple of max buddysystem order + uint32_t normal_area_size = MIN_ORDER_ALIGN(stop_normal_addr - start_normal_addr); + stop_normal_addr = start_normal_addr + normal_area_size; + + uint32_t phv_delta = start_normal_addr - lowmem_phy_start; + lowmem_virt_base = lowmem_virt_start + phv_delta; + lowmem_page_base = start_normal_addr / PAGE_SIZE; + zone_init("Normal", ZONE_NORMAL, start_normal_addr, stop_normal_addr); + + // ZONE_HIGHMEM [ mem_size/4 - mem_size ] + uint32_t start_high_addr = MAX_PAGE_ALIGN((uint32_t)boot_info->highmem_phy_start); + uint32_t stop_high_addr = MIN_PAGE_ALIGN(boot_info->highmem_phy_end); + + // Move the stop address so that the size is a multiple of max buddysystem order + uint32_t high_area_size = MIN_ORDER_ALIGN(stop_high_addr - start_high_addr); + stop_high_addr = start_high_addr + high_area_size; + + zone_init("HighMem", ZONE_HIGHMEM, start_high_addr, stop_high_addr); + //======================================================================= + + pr_debug("Memory Size : %u MB \n", mem_size / M); + pr_debug("Total page frames (MemorySize/4096) : %u \n", mem_num_frames); + pr_debug("mem_map address : 0x%p \n", mem_map); + pr_debug("Memory Start : 0x%p \n", lowmem_phy_start); + + // With the caching enabled, the pmm check is useless. + //return pmm_check(); + return 1; +} + +page_t *alloc_page_cached(gfp_t gfp_mask) +{ + zone_t *zone = get_zone_from_flags(gfp_mask); + return PG_FROM_BBSTRUCT(bb_alloc_page_cached(&zone->buddy_system), page_t, bbpage); +} + +void free_page_cached(page_t *page) +{ + zone_t *zone = get_zone_from_page(page); + bb_free_page_cached(&zone->buddy_system, &page->bbpage); +} + +uint32_t __alloc_page_lowmem(gfp_t gfp_mask) +{ + return get_lowmem_address_from_page(alloc_page_cached(gfp_mask)); +} + +void free_page_lowmem(uint32_t addr) +{ + page_t *page = get_lowmem_page_from_address(addr); + free_page_cached(page); +} + +uint32_t __alloc_pages_lowmem(gfp_t gfp_mask, uint32_t order) +{ + assert((order <= (MAX_BUDDYSYSTEM_GFP_ORDER - 1)) && gfp_mask == GFP_KERNEL && "Order is exceeding limit."); + + page_t *page = _alloc_pages(gfp_mask, order); + + // Get the index of the first page frame of the block. + uint32_t block_frame_adr = get_lowmem_address_from_page(page); + if (block_frame_adr == -1) { + pr_emerg("MEM. REQUEST FAILED"); + } +#if 0 + else { + pr_debug("BS-G: addr: %p (page: %p order: %d)\n", block_frame_adr, page, order); + } #endif + return block_frame_adr; } -unsigned int find_nearest_order_greater(uint32_t amount) +page_t *_alloc_pages(gfp_t gfp_mask, uint32_t order) { - amount = (amount + PAGE_SIZE - 1) / PAGE_SIZE; - unsigned int order = 0; - while ((1UL << order) < amount) { - ++order; - } + uint32_t block_size = 1UL << order; - return order; + zone_t *zone = get_zone_from_flags(gfp_mask); + page_t *page = NULL; + + // Search for a block of page frames by using the BuddySystem. + page = PG_FROM_BBSTRUCT(bb_alloc_pages(&zone->buddy_system, order), page_t, bbpage); + + // Set page counters + for (int i = 0; i < block_size; i++) { + set_page_count(&page[i], 1); + } + + assert(page && "Cannot allocate pages."); + + // Decrement the number of pages in the zone. + if (page) { + zone->free_pages -= block_size; + } + + return page; } -bool_t pmmngr_init(uint32_t mem_size) +void free_pages_lowmem(uint32_t addr) { - //==== Get RAM size ===================================================== - // Align mem_size to a page frame size, namely 4KB. - uint32_t _mmngr_memory_size = MAX_ORDER_ALIGN(mem_size); - - // Total number of blocks (all RAM). - uint32_t _mmngr_num_frames = _mmngr_memory_size / PAGE_SIZE; - //======================================================================= - - //==== Skip modules ===================================================== - dbg_print("[PMM] Start memory address previous skip modules : 0x%p \n", - _mmngr_memory_start); - - for (int i = 0; i < MAX_MODULES; i++) { - uint8_t *skip_addr = (uint8_t *)module_end[i]; - if (skip_addr != NULL && _mmngr_memory_start < skip_addr) { - _mmngr_memory_start = skip_addr; - } - } - - dbg_print("[PMM] Start memory address after skip modules : 0x%p \n", - _mmngr_memory_start); - //======================================================================= - - //==== Initialize array of page_t ======================================= - dbg_print("[PMM] Initializing memory map structure...\n"); - mem_map = (page_t *)_mmngr_memory_start; - - // Initialize each page_t. - for (int page_index = 0; page_index < _mmngr_num_frames; ++page_index) { - page_t *page = mem_map + page_index; - // Mark page as free. - page->_count = -1; - list_head_init(&(page->lru)); - } - //======================================================================= - - //==== Skip memory space used for page_t[] ============================== - _mmngr_memory_start += sizeof(page_t) * _mmngr_num_frames; - dbg_print( - "[PMM] Size of mem_map : %i byte [0x%p - 0x%p]\n", - (void *)_mmngr_memory_start - (void *)mem_map, mem_map, - _mmngr_memory_start); - //======================================================================= - - //==== Initialize contig_page_data node ================================= - dbg_print("[PMM] Initializing contig_page_data node...\n"); - contig_page_data = (pg_data_t *)_mmngr_memory_start; - // ZONE_NORMAL and ZONE_HIGHMEM - contig_page_data->nr_zones = __MAX_NR_ZONES; - // NID start from 0. - contig_page_data->node_id = 0; - // Corresponds with mem_map. - contig_page_data->node_mem_map = mem_map; - // In UMA we have only one node. - contig_page_data->node_next = NULL; - // All the memory. - contig_page_data->node_size = _mmngr_num_frames; - // mem_map[0]. - contig_page_data->node_start_mapnr = 0; - // The first physical page. - contig_page_data->node_start_paddr = 0x0; - //======================================================================= - - //==== Skip memory space used for pg_data_t ============================= - _mmngr_memory_start += sizeof(pg_data_t); - //======================================================================= - - //==== Initialize zones zone_t ========================================== - dbg_print("[PMM] Initializing zones...\n"); - - // ZONE_NORMAL [ memory_start - mem_size/4 ] - uint32_t start_normal_addr = MAX_ORDER_ALIGN((uint32_t)_mmngr_memory_start); - uint32_t stop_normal_addr = MAX_ORDER_ALIGN(_mmngr_memory_size >> 2); - zone_init("Normal", ZONE_NORMAL, start_normal_addr, stop_normal_addr); - - // ZONE_HIGHMEM [ mem_size/4 - mem_size ] - uint32_t start_high_addr = stop_normal_addr; - uint32_t stop_high_addr = _mmngr_memory_size; - zone_init("HighMem", ZONE_HIGHMEM, start_high_addr, stop_high_addr); - //======================================================================= - - dbg_print("[PMM] Memory Size : %u MB \n", - _mmngr_memory_size / M); - dbg_print("[PMM] Total page frames (MemorySize/4096) : %u \n", - _mmngr_num_frames); - dbg_print("[PMM] mem_map address : 0x%p \n", - mem_map); - dbg_print("[PMM] Memory Start : 0x%p \n", - _mmngr_memory_start); - - return pmm_check(); + page_t *page = get_lowmem_page_from_address(addr); + assert(page && "Page is over memory size."); + __free_pages(page); } -uint32_t __alloc_page(gfp_t gfp_mask) +void __free_pages(page_t *page) { - return __alloc_pages(gfp_mask, 0); -} + zone_t *zone = get_zone_from_page(page); + assert(zone && "Page is over memory size."); -void free_page(uint32_t addr) -{ - free_pages(addr, 0); -} + assert(zone->zone_mem_map <= page && "Page is below the selected zone!"); -uint32_t __alloc_pages(gfp_t gfp_mask, uint32_t order) -{ - zone_t *zone = get_zone_from_flags(gfp_mask); - assert((zone != NULL) && "Invalid zone flag!"); - assert((order <= (MAX_ORDER - 1)) && "Invalid order!"); + uint32_t order = page->bbpage.order; + uint32_t block_size = 1UL << order; - int block_size = 1UL << order; + for (int i = 0; i < block_size; i++) { + set_page_count(&page[i], 0); + } - page_t *page = NULL; -#ifdef ENABLE_BUDDYSYSTEM - // Search for a block of page frames by using the BuddySystem. - page = bb_alloc_pages(zone, order); + bb_free_pages(&zone->buddy_system, &page->bbpage); -#else - // First page of the zone. - page_t *block = zone->zone_mem_map; - // Last page of the zone. - page_t *last_frame = zone->zone_mem_map + zone->size - order + 1; - // When true, then we found a block of pages - int found = 0; - // Search for a block of pages - while (block < last_frame && found == 0) { - // Suppose this is the right block. - found = 1; - // Check if enough pages are available in current block. - for (unsigned int i = 0; i < block_size; ++i) { - found = ((block + i)->_count == -1); - if (!found) { - /* The block is not large enough. We have to skip it - * and restart to search for another one. - */ - block += i + 1; - - break; - } - } - - if (found) { - // Set the found block of pages as taken, and not available. - for (unsigned int i = 0; i < block_size; ++i) { - (block + i)->_count = 0; - } - } - } - page = (found == 1) ? block : NULL; + zone->free_pages += block_size; +#if 0 + pr_debug("BS-F: (page: %p order: %d)\n", page, order); #endif - - uint32_t block_frame_adr = -1; - if (page != NULL) { - // Decrement the number of pages in the zone. - zone->free_pages -= block_size; - // Get the index of the first page frame of the block. - uint32_t delta_block_frame = (uint32_t)(page - zone->zone_mem_map); - block_frame_adr = zone->zone_start_pfn + delta_block_frame; - block_frame_adr = block_frame_adr * PAGE_SIZE; - } - - if (block_frame_adr == -1) { - dbg_print("MEM. REQUEST FAILED"); - } else { - dbg_print("BS-G: addr: %p (page: %p order: %d)\n", block_frame_adr, - page, order); - } - - return block_frame_adr; + //buddy_system_dump(&zone->buddy_system); } -void free_pages(uint32_t addr, uint32_t order) +unsigned long get_zone_total_space(gfp_t gfp_mask) { - page_t *page = mem_map + (addr / PAGE_SIZE); - zone_t *zone = get_zone_from_page(page); - assert((zone != NULL) && "Page is over memory size"); - - int block_size = 1UL << order; - -#ifdef ENABLE_BUDDYSYSTEM - bb_free_pages(zone, page, order); -#else - // Set the given block of page frames as free, and available again. - for (unsigned int i = 0; i < block_size; ++i) { - (page + i)->_count = -1; - } -#endif - - zone->free_pages += block_size; - - dbg_print("BS-F: addr: %p (page: %p order: %d)\n", addr, page, order); + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Cannot retrieve the correct zone."); + return buddy_system_get_total_space(&zone->buddy_system); } -uint32_t get_memory_start() +unsigned long get_zone_free_space(gfp_t gfp_mask) { - return (uint32_t)_mmngr_memory_start; + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Cannot retrieve the correct zone."); + return buddy_system_get_free_space(&zone->buddy_system); } + +unsigned long get_zone_cached_space(gfp_t gfp_mask) +{ + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Cannot retrieve the correct zone."); + return buddy_system_get_cached_space(&zone->buddy_system); +} \ No newline at end of file diff --git a/mentos/src/misc/bitops.c b/mentos/src/misc/bitops.c deleted file mode 100644 index 7bcecbf..0000000 --- a/mentos/src/misc/bitops.c +++ /dev/null @@ -1,38 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitops.c -/// @brief Bitmasks functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "bitops.h" -#include "video.h" - -int find_first_bit(unsigned short int irq_mask) -{ - int i = 0; - if (irq_mask == 0) { - return 0; - } - for (i = 0; i < 8; i++) { - if ((1 << i) & irq_mask) { - break; - } - } - - return i; -} - -bool_t has_flag(uint32_t flags, uint32_t flag) -{ - return (bool_t)((flags & flag) != 0); -} - -void set_flag(uint32_t *flags, uint32_t flag) -{ - (*flags) |= flag; -} - -void clear_flag(uint32_t *flags, uint32_t flag) -{ - (*flags) &= ~flag; -} diff --git a/mentos/src/misc/clock.c b/mentos/src/misc/clock.c deleted file mode 100644 index cfb2918..0000000 --- a/mentos/src/misc/clock.c +++ /dev/null @@ -1,178 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file clock.c -/// @brief Clock functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "clock.h" -#include "timer.h" -#include "stdio.h" -#include "stddef.h" -#include "port_io.h" - -time_t get_millisecond() -{ - return timer_get_subticks(); -} - -time_t get_second() -{ - outportb(0x70, SECOND_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_minute() -{ - outportb(0x70, MINUTE_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_hour() -{ - outportb(0x70, HOUR_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_day_w() -{ - outportb(0x70, DAY_W_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_day_m() -{ - outportb(0x70, DAY_M_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_month() -{ - outportb(0x70, MONTH_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_year() -{ - outportb(0x70, YEAR_RTC); - time_t c = inportb(0x71); - - return c; -} - -char *get_month_lng() -{ - switch (get_month()) { - case 1: - return "January"; - case 2: - return "February"; - case 3: - return "March"; - case 4: - return "April"; - case 5: - return "May"; - case 6: - return "June"; - case 7: - return "July"; - case 8: - return "August"; - case 9: - return "September"; - case 10: - return "October"; - case 11: - return "November"; - case 12: - return "December"; - default: - break; - } - return ""; -} - -char *get_day_lng() -{ - switch (get_day_w()) { - case 1: - return "Sunday"; - case 2: - return "Monday"; - case 3: - return "Tuesday"; - case 4: - return "Wednesday"; - case 5: - return "Thursday"; - case 6: - return "Friday"; - case 7: - return "Saturday"; - default: - break; - } - return ""; -} - -time_t time(time_t *timer) -{ - // Jan 1, 1970 - time_t t = 0; - t += get_millisecond(); - t += get_second(); - t += get_minute() * 60; - t += get_hour() * 3600; - t += get_day_m() * 86400; - t += get_month() * 2629743; - t += (1970 - get_year()) * 31556926; - if (timer != NULL) { - (*timer) = t; - } - - return t; -} - -time_t difftime(time_t time1, time_t time2) -{ - return time1 - time2; -} - -void strhourminutesecond(char *dst) -{ - time_t s, m, h; - s = get_second(); - m = get_minute(); - h = get_hour(); - - sprintf(dst, "%i:%i:%i", h, m, s); -} - -void strdaymonthyear(char *dst) -{ - sprintf(dst, "%s %i %s %i", get_day_lng(), get_day_m(), get_month_lng(), - 1970 - get_year()); -} - -void strdatehour(char *dst) -{ - time_t s, m, h; - s = get_second(); - m = get_minute(); - h = get_hour(); - - sprintf(dst, "%s %i %s %i %i:%i:%i", get_day_lng(), get_day_m(), - get_month_lng(), 1970 - get_year(), h, m, s); -} diff --git a/mentos/src/misc/debug.c b/mentos/src/misc/debug.c index 2c4cd5e..7f7ff75 100644 --- a/mentos/src/misc/debug.c +++ b/mentos/src/misc/debug.c @@ -1,99 +1,194 @@ /// MentOS, The Mentoring Operating system project /// @file debug.c /// @brief Debugging primitives. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "sys/bitops.h" #include "debug.h" -#include "stdio.h" -#include "string.h" #include "spinlock.h" +#include "port_io.h" +#include "kernel.h" +#include "string.h" +#include "stdio.h" +#include "video.h" /// Serial port for QEMU. #define SERIAL_COM1 (0x03F8) +/// Determines the log level. +static int max_log_level = LOGLEVEL_DEBUG; -#define DEBUG_BUFFER_SIZE 1024 - -static inline void dbg_putchar(char c) +void dbg_putchar(char c) { #if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) - outportb(SERIAL_COM1, c); + outportb(SERIAL_COM1, (uint8_t)c); #endif } -static inline void dbg_print_header(const char *file, const char *fun, int line) +void dbg_puts(const char *s) { - static char prefix[300], final_prefix[300]; - - sprintf(prefix, "[%s:%s:%d", file, fun, line); - - sprintf(final_prefix, "%-40s] ", prefix); - - for (register int it = 0; final_prefix[it] != 0; ++it) { - dbg_putchar(final_prefix[it]); - } +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + while ((*s) != 0) + dbg_putchar(*s++); +#endif } -void _dbg_print(const char *file, const char *fun, int line, const char *msg, - ...) +static inline void __debug_print_header(const char *file, const char *fun, int line, int log_level, char *header) { - // Define a buffer for the formatted string. - static char formatted[DEBUG_BUFFER_SIZE]; - static bool_t new_line = true; - - // Stage 1: FORMAT - if (strlen(msg) >= 1024) { - return; - } - // Start variabile argument's list. - va_list ap; - va_start(ap, msg); - // Format the message. - vsprintf(formatted, msg, ap); - // End the list of arguments. - va_end(ap); - - // Stage 2: SEND - if (new_line) { - dbg_print_header(file, fun, line); - new_line = false; - } - for (int it = 0; (formatted[it] != 0) && (it < DEBUG_BUFFER_SIZE); ++it) { - dbg_putchar(formatted[it]); - if (formatted[it] != '\n') { - continue; - } - if ((it + 1) >= DEBUG_BUFFER_SIZE) { - continue; - } - if (formatted[it + 1] == 0) { - new_line = true; - } else { - dbg_print_header(file, fun, line); - } - } + static char tmp_prefix[BUFSIZ], final_prefix[BUFSIZ]; + if (log_level == LOGLEVEL_EMERG) + dbg_puts(FG_RED_BRIGHT); + else if (log_level == LOGLEVEL_ALERT) + dbg_puts(FG_RED_BRIGHT); + else if (log_level == LOGLEVEL_CRIT) + dbg_puts(FG_RED); + else if (log_level == LOGLEVEL_ERR) + dbg_puts(FG_RED); + else if (log_level == LOGLEVEL_WARNING) + dbg_puts(FG_YELLOW_BRIGHT); + else if (log_level == LOGLEVEL_DEBUG) + dbg_puts(FG_CYAN); + else if (log_level == LOGLEVEL_NOTICE) + dbg_puts(FG_RESET); + else if (log_level == LOGLEVEL_INFO) + dbg_puts(FG_RESET); + else + dbg_puts(FG_RESET); + dbg_putchar('['); + dbg_puts( + (log_level == LOGLEVEL_EMERG) ? " EM " /*"EMERG "*/ : + (log_level == LOGLEVEL_ALERT) ? " AL " /*"ALERT "*/ : + (log_level == LOGLEVEL_CRIT) ? " CR " /*"CRIT "*/ : + (log_level == LOGLEVEL_ERR) ? " ER " /*"ERR "*/ : + (log_level == LOGLEVEL_WARNING) ? " WR " /*"WARNING"*/ : + (log_level == LOGLEVEL_NOTICE) ? " NT " /*"NOTICE "*/ : + (log_level == LOGLEVEL_INFO) ? " IN " /*"INFO "*/ : + (log_level == LOGLEVEL_DEBUG) ? " DB " /*"DEBUG "*/ : + " DF " /*"DEFAULT*/); + dbg_putchar('|'); + sprintf(tmp_prefix, "%s:%d", file, line); + sprintf(final_prefix, " %-20s ", tmp_prefix); + dbg_puts(final_prefix); + dbg_putchar('|'); + sprintf(final_prefix, " %-25s ]", fun); + dbg_puts(final_prefix); + dbg_putchar(' '); + if (header) { + dbg_puts(header); + dbg_putchar(' '); + } } -void print_intrframe(pt_regs *frame) +void set_log_level(int level) { - dbg_print("Interrupt stack frame:\n"); - dbg_print("GS = 0x%-04x\n", frame->gs); - dbg_print("FS = 0x%-04x\n", frame->fs); - dbg_print("ES = 0x%-04x\n", frame->es); - dbg_print("DS = 0x%-04x\n", frame->ds); - dbg_print("EDI = 0x%-09x\n", frame->edi); - dbg_print("ESI = 0x%-09x\n", frame->esi); - dbg_print("EBP = 0x%-09x\n", frame->ebp); - dbg_print("ESP = 0x%-09x\n", frame->esp); - dbg_print("EBX = 0x%-09x\n", frame->ebx); - dbg_print("EDX = 0x%-09x\n", frame->edx); - dbg_print("ECX = 0x%-09x\n", frame->ecx); - dbg_print("EAX = 0x%-09x\n", frame->eax); - dbg_print("INT_NO = %-9d\n", frame->int_no); - dbg_print("ERR_CD = %-9d\n", frame->err_code); - dbg_print("EIP = 0x%-09x\n", frame->eip); - dbg_print("CS = 0x%-04x\n", frame->cs); - dbg_print("EFLAGS = 0x%-09x\n", frame->eflags); - dbg_print("UESP = 0x%-09x\n", frame->useresp); - dbg_print("SS = 0x%-04x\n", frame->ss); + if ((level >= LOGLEVEL_EMERG) && (level <= LOGLEVEL_DEBUG)) + max_log_level = level; +} + +int get_log_level() +{ + return max_log_level; +} + +void dbg_printf(const char *file, const char *fun, int line, char *header, const char *format, ...) +{ + // Define a buffer for the formatted string. + static char formatted[BUFSIZ]; + static short new_line = 1; + + // Stage 1: FORMAT + if (strlen(format) >= BUFSIZ) + return; + + // Check the log level. + int log_level = LOGLEVEL_DEFAULT; + if ((format[0] != '\0') && (format[0] == KERN_SOH_ASCII)) { + // Remove the Start Of Header. + ++format; + // Compute the log level. + log_level = (format[0] - '0'); + // Check the log_level. + if ((log_level < LOGLEVEL_EMERG) || (log_level > LOGLEVEL_DEBUG)) + log_level = 8; + // Remove the log_level; + ++format; + } + if (log_level > max_log_level) { + return; + } + + // Start variabile argument's list. + va_list ap; + va_start(ap, format); + // Format the message. + vsprintf(formatted, format, ap); + // End the list of arguments. + va_end(ap); + + // Stage 2: SEND + if (new_line) { + __debug_print_header(file, fun, line, log_level, header); + new_line = false; + } + for (int it = 0; (formatted[it] != 0) && (it < BUFSIZ); ++it) { + dbg_putchar(formatted[it]); + if (formatted[it] != '\n') { + continue; + } + if ((it + 1) >= BUFSIZ) { + continue; + } + if (formatted[it + 1] == 0) { + new_line = true; + } else { + __debug_print_header(file, fun, line, log_level, header); + } + } +} + +void dbg_print_regs(pt_regs *frame) +{ + pr_debug("Interrupt stack frame:\n"); + pr_debug("GS = 0x%-04x\n", frame->gs); + pr_debug("FS = 0x%-04x\n", frame->fs); + pr_debug("ES = 0x%-04x\n", frame->es); + pr_debug("DS = 0x%-04x\n", frame->ds); + pr_debug("EDI = 0x%-09x\n", frame->edi); + pr_debug("ESI = 0x%-09x\n", frame->esi); + pr_debug("EBP = 0x%-09x\n", frame->ebp); + pr_debug("ESP = 0x%-09x\n", frame->esp); + pr_debug("EBX = 0x%-09x\n", frame->ebx); + pr_debug("EDX = 0x%-09x\n", frame->edx); + pr_debug("ECX = 0x%-09x\n", frame->ecx); + pr_debug("EAX = 0x%-09x\n", frame->eax); + pr_debug("INT_NO = %-9d\n", frame->int_no); + pr_debug("ERR_CD = %-9d\n", frame->err_code); + pr_debug("EIP = 0x%-09x\n", frame->eip); + pr_debug("CS = 0x%-04x\n", frame->cs); + pr_debug("EFLAGS = 0x%-09x\n", frame->eflags); + pr_debug("UESP = 0x%-09x\n", frame->useresp); + pr_debug("SS = 0x%-04x\n", frame->ss); +} + +const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) + dblBytes = bytes / 1024.0; + } + sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); + return output; +} + +const char *dec_to_binary(unsigned long value) +{ + static char buffer[33]; + for (int i = 0; i < 32; ++i) + buffer[i] = bit_check(value, 31 - i) ? '1' : '0'; + return buffer; } diff --git a/mentos/src/multiboot.c b/mentos/src/multiboot.c index 3597250..6874ea1 100644 --- a/mentos/src/multiboot.c +++ b/mentos/src/multiboot.c @@ -1,201 +1,204 @@ /// MentOS, The Mentoring Operating system project /// @file multiboot.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#define __DEBUG_LEVEL__ 0 + #include "multiboot.h" -#include "bitops.h" +#include "kernel.h" +#include "sys/bitops.h" +#include "stddef.h" #include "debug.h" #include "panic.h" +#include "stddef.h" -#define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit))) -static inline multiboot_memory_map_t *first_mmap_entry(multiboot_info_t *info) +multiboot_memory_map_t *mmap_first_entry(multiboot_info_t *info) { - if (!has_flag(info->flags, MULTIBOOT_FLAG_MMAP)) - return NULL; - return (multiboot_memory_map_t *)((uintptr_t)info->mmap_addr); + if (!bitmask_check(info->flags, MULTIBOOT_FLAG_MMAP)) + return NULL; + return (multiboot_memory_map_t *)((uintptr_t)info->mmap_addr); } -static inline multiboot_memory_map_t * -next_mmap_entry(multiboot_info_t *info, multiboot_memory_map_t *entry) +multiboot_memory_map_t *mmap_first_entry_of_type(multiboot_info_t *info, + uint32_t type) { - uintptr_t next = ((uintptr_t)entry) + entry->size + sizeof(entry->size); - if (next >= info->mmap_addr + info->mmap_length) - return NULL; - return (multiboot_memory_map_t *)next; + multiboot_memory_map_t *entry = mmap_first_entry(info); + if (entry && (entry->type == type)) + return entry; + return mmap_next_entry_of_type(info, entry, type); } -static inline multiboot_memory_map_t * -next_mmap_entry_of_type(multiboot_info_t *info, multiboot_memory_map_t *entry, - uint32_t type) +multiboot_memory_map_t *mmap_next_entry(multiboot_info_t *info, + multiboot_memory_map_t *entry) { - do { - entry = next_mmap_entry(info, entry); - } while (entry && entry->type != type); - return entry; + uintptr_t next = ((uintptr_t)entry) + entry->size + sizeof(entry->size); + if (next >= (info->mmap_addr + info->mmap_length)) + return NULL; + return (multiboot_memory_map_t *)next; } -static inline multiboot_memory_map_t * -first_mmap_entry_of_type(multiboot_info_t *info, uint32_t type) +multiboot_memory_map_t *mmap_next_entry_of_type(multiboot_info_t *info, + multiboot_memory_map_t *entry, + uint32_t type) { - multiboot_memory_map_t *entry = first_mmap_entry(info); - if (entry && (entry->type == type)) - return entry; - return next_mmap_entry_of_type(info, entry, type); + do { + entry = mmap_next_entry(info, entry); + } while (entry && entry->type != type); + return entry; } -static inline char *mmap_type_name(multiboot_memory_map_t *entry) +char *mmap_type_name(multiboot_memory_map_t *entry) { - if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) - return "AVAILABLE"; - if (entry->type == MULTIBOOT_MEMORY_RESERVED) - return "RESERVED"; - return "NONE"; + if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) + return "AVAILABLE"; + if (entry->type == MULTIBOOT_MEMORY_RESERVED) + return "RESERVED"; + return "NONE"; } -static inline multiboot_module_t *first_module(multiboot_info_t *info) +multiboot_module_t *first_module(multiboot_info_t *info) { - if (!has_flag(info->flags, MULTIBOOT_FLAG_MODS)) - return NULL; - if (!info->mods_count) - return NULL; - return (multiboot_module_t *)(uintptr_t)info->mods_addr; + if (!bitmask_check(info->flags, MULTIBOOT_FLAG_MODS)) + return NULL; + if (!info->mods_count) + return NULL; + return (multiboot_module_t *)(uintptr_t)info->mods_addr; } -static inline multiboot_module_t *next_module(multiboot_info_t *info, - multiboot_module_t *mod) +multiboot_module_t *next_module(multiboot_info_t *info, + multiboot_module_t *mod) { - multiboot_module_t *first = - (multiboot_module_t *)((uintptr_t)info->mods_addr); - ++mod; - if ((mod - first) >= info->mods_count) - return NULL; - return mod; + multiboot_module_t *first = (multiboot_module_t *)((uintptr_t)info->mods_addr); + ++mod; + if ((mod - first) >= info->mods_count) + return NULL; + return mod; } void dump_multiboot(multiboot_info_t *mbi) { - dbg_print("\n--------------------------------------------------\n"); - dbg_print("MULTIBOOT header at 0x%x:\n", mbi); + pr_debug("\n--------------------------------------------------\n"); + pr_debug("MULTIBOOT header at 0x%x:\n", mbi); - // Print out the flags. - dbg_print("%-16s = 0x%x\n", "flags", mbi->flags); + // Print out the flags. + pr_debug("%-16s = 0x%x\n", "flags", mbi->flags); - // Are mem_* valid? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_MEM)) { - dbg_print("%-16s = %u Kb (%u Mb)\n", "mem_lower", mbi->mem_lower, - mbi->mem_lower / K); - dbg_print("%-16s = %u Kb (%u Mb)\n", "mem_upper", mbi->mem_upper, - mbi->mem_upper / K); - dbg_print("%-16s = %u Kb (%u Mb)\n", "total", - mbi->mem_lower + mbi->mem_upper, - (mbi->mem_lower + mbi->mem_upper) / K); - } + // Are mem_* valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_MEM)) { + pr_debug("%-16s = %u Kb (%u Mb)\n", "mem_lower", mbi->mem_lower, + mbi->mem_lower / K); + pr_debug("%-16s = %u Kb (%u Mb)\n", "mem_upper", mbi->mem_upper, + mbi->mem_upper / K); + pr_debug("%-16s = %u Kb (%u Mb)\n", "total", + mbi->mem_lower + mbi->mem_upper, + (mbi->mem_lower + mbi->mem_upper) / K); + } - // Is boot_device valid? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_DEVICE)) { - dbg_print("%-16s = 0x%x (0x%x)", "boot_device", mbi->boot_device); - switch ((mbi->boot_device) & 0xFF000000) { - case 0x00000000: - dbg_print("(floppy)\n"); - break; - case 0x80000000: - dbg_print("(disk)\n"); - break; - default: - dbg_print("(unknown)\n"); - } - } + // Is boot_device valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_DEVICE)) { + pr_debug("%-16s = 0x%x (0x%x)", "boot_device", mbi->boot_device); + switch ((mbi->boot_device) & 0xFF000000) { + case 0x00000000: + pr_debug("(floppy)\n"); + break; + case 0x80000000: + pr_debug("(disk)\n"); + break; + default: + pr_debug("(unknown)\n"); + } + } - // Is the command line passed? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { - dbg_print("%-16s = %s\n", "cmdline", (char *)mbi->cmdline); - } + // Is the command line passed? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { + pr_debug("%-16s = %s\n", "cmdline", (char *)mbi->cmdline); + } - // Are mods_* valid? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_MODS)) { - dbg_print("%-16s = %d\n", "mods_count", mbi->mods_count); - dbg_print("%-16s = 0x%x\n", "mods_addr", mbi->mods_addr); - multiboot_module_t *mod = first_module(mbi); - for (int i = 0; mod; ++i, mod = next_module(mbi, mod)) { - dbg_print(" [%2d] " - "mod_start = 0x%x, " - "mod_end = 0x%x, " - "cmdline = %s\n", - i, mod->mod_start, mod->mod_end, (char *)mod->cmdline); - } - } - // Bits 4 and 5 are mutually exclusive! - if (has_flag(mbi->flags, MULTIBOOT_FLAG_AOUT) && - has_flag(mbi->flags, MULTIBOOT_FLAG_ELF)) { - kernel_panic("Both bits 4 and 5 are set.\n"); - return; - } + // Are mods_* valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_MODS)) { + pr_debug("%-16s = %d\n", "mods_count", mbi->mods_count); + pr_debug("%-16s = 0x%x\n", "mods_addr", mbi->mods_addr); + multiboot_module_t *mod = first_module(mbi); + for (int i = 0; mod; ++i, mod = next_module(mbi, mod)) { + pr_debug(" [%2d] " + "mod_start = 0x%x, " + "mod_end = 0x%x, " + "cmdline = %s\n", + i, mod->mod_start, mod->mod_end, (char *)mod->cmdline); + } + } + // Bits 4 and 5 are mutually exclusive! + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_AOUT) && + bitmask_check(mbi->flags, MULTIBOOT_FLAG_ELF)) { + kernel_panic("Both bits 4 and 5 are set.\n"); + return; + } - // Is the symbol table of a.out valid? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_AOUT)) { - multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym); - dbg_print("multiboot_aout_symbol_table: tabsize = 0x%0x, " - "strsize = 0x%x, addr = 0x%x\n", - multiboot_aout_sym->tabsize, multiboot_aout_sym->strsize, - multiboot_aout_sym->addr); - } + // Is the symbol table of a.out valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_AOUT)) { + multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym); + pr_debug("multiboot_aout_symbol_table: tabsize = 0x%0x, " + "strsize = 0x%x, addr = 0x%x\n", + multiboot_aout_sym->tabsize, multiboot_aout_sym->strsize, + multiboot_aout_sym->addr); + } - // Is the section header table of ELF valid? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_ELF)) { - multiboot_elf_section_header_table_t *multiboot_elf_sec = - &(mbi->u.elf_sec); - dbg_print("multiboot_elf_sec: num = %u, size = 0x%x," - " addr = 0x%x, shndx = 0x%x\n", - multiboot_elf_sec->num, multiboot_elf_sec->size, - multiboot_elf_sec->addr, multiboot_elf_sec->shndx); - } + // Is the section header table of ELF valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_ELF)) { + multiboot_elf_section_header_table_t *multiboot_elf_sec = + &(mbi->u.elf_sec); + pr_debug("multiboot_elf_sec: num = %u, size = 0x%x," + " addr = 0x%x, shndx = 0x%x\n", + multiboot_elf_sec->num, multiboot_elf_sec->size, + multiboot_elf_sec->addr, multiboot_elf_sec->shndx); + } - // Are mmap_* valid? - if (has_flag(mbi->flags, MULTIBOOT_FLAG_MMAP)) { - dbg_print("%-16s = 0x%x\n", "mmap_addr", mbi->mmap_addr); - dbg_print("%-16s = 0x%x (%d entries)\n", "mmap_length", - mbi->mmap_length, - mbi->mmap_length / sizeof(multiboot_memory_map_t)); - multiboot_memory_map_t *mmap = first_mmap_entry(mbi); - for (int i = 0; mmap; ++i, mmap = next_mmap_entry(mbi, mmap)) { - dbg_print(" [%2d] " - "base_addr = 0x%09x%09x, " - "length = 0x%09x%09x, " - "type = 0x%x (%s)\n", - i, mmap->base_addr_high, mmap->base_addr_low, - mmap->length_high, mmap->length_low, mmap->type, - mmap_type_name(mmap)); - } - } + // Are mmap_* valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_MMAP)) { + pr_debug("%-16s = 0x%x\n", "mmap_addr", mbi->mmap_addr); + pr_debug("%-16s = 0x%x (%d entries)\n", "mmap_length", + mbi->mmap_length, + mbi->mmap_length / sizeof(multiboot_memory_map_t)); + multiboot_memory_map_t *mmap = mmap_first_entry(mbi); + for (int i = 0; mmap; ++i, mmap = mmap_next_entry(mbi, mmap)) { + pr_debug(" [%2d] " + "base_addr = 0x%09x%09x, " + "length = 0x%09x%09x, " + "type = 0x%x (%s)\n", + i, mmap->base_addr_high, mmap->base_addr_low, + mmap->length_high, mmap->length_low, mmap->type, + mmap_type_name(mmap)); + } + } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_DRIVE_INFO)) { - dbg_print("Drives: 0x%x\n", mbi->drives_length); - dbg_print("Addr : 0x%x\n", mbi->drives_addr); - } + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_DRIVE_INFO)) { + pr_debug("Drives: 0x%x\n", mbi->drives_length); + pr_debug("Addr : 0x%x\n", mbi->drives_addr); + } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_CONFIG_TABLE)) { - dbg_print("Config: 0x%x\n", mbi->config_table); - } + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_CONFIG_TABLE)) { + pr_debug("Config: 0x%x\n", mbi->config_table); + } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_BOOT_LOADER_NAME)) { - dbg_print("boot_loader_name: %s\n", (char *)mbi->boot_loader_name); - } + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_BOOT_LOADER_NAME)) { + pr_debug("boot_loader_name: %s\n", (char *)mbi->boot_loader_name); + } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_APM_TABLE)) { - dbg_print("APM : 0x%x\n", mbi->apm_table); - } + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_APM_TABLE)) { + pr_debug("APM : 0x%x\n", mbi->apm_table); + } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_VBE_INFO)) { - dbg_print("VBE Co: 0x%x\n", mbi->vbe_control_info); - dbg_print("VBE Mo: 0x%x\n", mbi->vbe_mode_info); - dbg_print("VBE In: 0x%x\n", mbi->vbe_mode); - dbg_print("VBE se: 0x%x\n", mbi->vbe_interface_seg); - dbg_print("VBE of: 0x%x\n", mbi->vbe_interface_off); - dbg_print("VBE le: 0x%x\n", mbi->vbe_interface_len); - } - dbg_print("--------------------------------------------------\n"); - dbg_print("\n"); + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_VBE_INFO)) { + pr_debug("VBE Co: 0x%x\n", mbi->vbe_control_info); + pr_debug("VBE Mo: 0x%x\n", mbi->vbe_mode_info); + pr_debug("VBE In: 0x%x\n", mbi->vbe_mode); + pr_debug("VBE se: 0x%x\n", mbi->vbe_interface_seg); + pr_debug("VBE of: 0x%x\n", mbi->vbe_interface_off); + pr_debug("VBE le: 0x%x\n", mbi->vbe_interface_len); + } + pr_debug("--------------------------------------------------\n"); + pr_debug("\n"); } diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index 4f07c0c..528a831 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -1,308 +1,498 @@ /// MentOS, The Mentoring Operating system project /// @file process.c /// @brief Process data structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. + +#include "kernel_levels.h" + +//#ifndef __DEBUG_LEVEL__ +//#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG +//#endif + +#include #include "process.h" -#include "prio.h" -#include "init.h" -#include "panic.h" -#include "kheap.h" -#include "debug.h" -#include "unistd.h" -#include "string.h" -#include "list_head.h" -#include "stdatomic.h" #include "scheduler.h" +#include "assert.h" +#include "libgen.h" +#include "string.h" +#include "timer.h" +#include "fcntl.h" +#include "panic.h" +#include "debug.h" +#include "wait.h" +#include "prio.h" +#include "vfs.h" +#include "elf.h" -#define PUSH_ON_STACK(stack, type, item) \ - *((type *)(stack -= sizeof(type))) = item - +/// Cache for creating the task structs. +static kmem_cache_t *task_struct_cache; /// @brief The task_struct of the init process. static task_struct *init_proc; -void exit_handler() +static inline int __push_args_on_stack(uintptr_t *esp, char *args[], char ***argsptr) { - exit(1); - kernel_panic("I should not be here.\n"); + int argc = 0; + char *args_ptr[256]; + // Count the number of arguments. + while (args[argc] != NULL) { + ++argc; + } + + // Prepare args with space for the terminating NULL. + for (int i = argc - 1; i >= 0; --i) { + for (int j = strlen(args[i]); j >= 0; --j) { + PUSH_ARG((*esp), char, args[i][j]); + } + args_ptr[i] = (char *)(*esp); + } + // Push terminating NULL. + PUSH_ARG((*esp), char *, (char *)NULL); + // Push array of pointers to the arguments. + for (int i = argc - 1; i >= 0; --i) { + PUSH_ARG((*esp), char *, args_ptr[i]); + } + (*argsptr) = (char **)(*esp); + + return argc; } -task_struct *create_init_process() +static int __reset_process(task_struct *task) { - dbg_print("Building init process...\n"); - // Create a new task_struct. - init_proc = kmalloc(sizeof(task_struct)); - // TODO: process is IN USER SPACE! it should be in KERNEL SPACE! - memset(init_proc, 0, sizeof(task_struct)); - // Set the id of the process. - init_proc->pid = get_new_pid(); - // Set the name of the process. - strcpy(init_proc->name, "init"); - // Set the statistics of the process. - init_proc->se.prio = DEFAULT_PRIO; - init_proc->se.start_runtime = 0; - init_proc->se.exec_start = 0; - init_proc->se.sum_exec_runtime = 0; - init_proc->se.vruntime = 0; - // Initialize the list_head. - list_head_init(&init_proc->run_list); - // Initialize the children list_head. - list_head_init(&init_proc->children); - // Initialize the sibling list_head. - list_head_init(&init_proc->sibling); - // Create a new stack segment. - init_proc->mm = create_process_image(DEFAULT_STACK_SIZE); - char *stack = (char *)init_proc->mm->start_stack; - // Clean stack space. - memset(stack, 0, DEFAULT_STACK_SIZE); - // Set the base address of the stack. - char *ebp = (char *)(stack + DEFAULT_STACK_SIZE); - // Create a pointer to keep track of the top of the stack. - char *esp = ebp; - // Set exit_handler as terminating function for init. - PUSH_ON_STACK(esp, uintptr_t, (uintptr_t)&exit_handler); - // Set the top address of the stack. - init_proc->thread.useresp = (uintptr_t)esp; - // Set the base address of the stack. - init_proc->thread.ebp = (uintptr_t)ebp; - // Set the program counter. - init_proc->thread.eip = (uintptr_t)&main_init; - // Enable the interrupts. - init_proc->thread.eflags = init_proc->thread.eflags | EFLAG_IF; - // Clear the current working directory. - memset(init_proc->cwd, '\0', PATH_MAX); - // Set the state of the process as running. - init_proc->state = TASK_RUNNING; - // Active the current process. - enqueue_task(init_proc); + pr_debug("__reset_process(%p `%s`)\n", task, task->name); + // Create a new stack segment. + task->mm = create_blank_process_image(DEFAULT_STACK_SIZE); + if (task->mm == NULL) { + pr_err("Failed to initialize process mm structure.\n"); + return 0; + } - dbg_print("--------------------------------------------------\n"); - dbg_print("- %s process (PID: %d, eflags: %d)\n", init_proc->name, - init_proc->pid, init_proc->thread.eflags); - dbg_print("\tStack: [0x%p - 0x%p]\n", init_proc->mm->start_stack, - init_proc->mm->start_stack + DEFAULT_STACK_SIZE); - dbg_print("\tebp: 0x%p\n", init_proc->thread.ebp); - dbg_print("\tesp: 0x%p\n", init_proc->thread.useresp); - dbg_print("\teip: 0x%p\n", init_proc->thread.eip); - dbg_print("--------------------------------------------------\n"); + // Save the current page directory. + page_directory_t *crtdir = paging_get_current_directory(); + // FIXME: Now to clear the stack a pgdir switch is made, it should be a kernel mmapping. + paging_switch_directory_va(task->mm->pgd); - return init_proc; + // Clean stack space. + memset((char *)task->mm->start_stack, 0, DEFAULT_STACK_SIZE); + // Set the base address of the stack. + task->thread.regs.ebp = (uintptr_t)(task->mm->start_stack + DEFAULT_STACK_SIZE); + // Set the top address of the stack. + task->thread.regs.useresp = task->thread.regs.ebp; + // Enable the interrupts. + task->thread.regs.eflags = task->thread.regs.eflags | EFLAG_IF; + + // Restore previous pgdir + paging_switch_directory(crtdir); + + return 1; } -char *get_current_dir_name() +static int __load_executable(const char *path, task_struct *task, uint32_t *entry) { - task_struct *current_process = kernel_get_current_process(); - if (current_process != NULL) { - return strdup(current_process->cwd); - } - - return kstrdup("/"); + pr_debug("__load_executable(`%s`, %p `%s`, %p)\n", path, task, task->name, entry); + vfs_file_t *file = vfs_open(path, O_RDONLY, 0); + if (file == NULL) { + pr_err("Cannot find executable!\n"); + return 0; + } + // Check that the file is actually an executable before destroying the `mm`. + if (!elf_check_file_type(file, ET_EXEC)) { + pr_err("This is not a valid ELF executable `%s`!\n", path); + return 0; + } + // FIXME: When threads will be implemented + // they should share the mm, so the destroy_process_image must be called + // only when all the threads are terminated. This can be accomplished by using + // an internal counter on the mm. + if (task->mm) + destroy_process_image(task->mm); + // Return code variable. + int ret = 0; + // Recreate the memory of the process. + if (__reset_process(task)) { + // Load the elf file, check if 0 is returned and print the error. + if (!(ret = elf_load_file(task, file, entry))) { + pr_err("Failed to load ELF file `%s`!\n", path); + } + } + // Close the file. + vfs_close(file); + return ret; } -void sys_getcwd(char *path, size_t size) +static inline int __count_args_bytes(char **args) { - task_struct *current_process = kernel_get_current_process(); - if ((current_process != NULL) && (path != NULL)) { - strncpy(path, current_process->cwd, size); - } + int argc = 0; + int bytes = 0; + + // Count the number of arguments. + while (args[argc] != NULL) { + ++argc; + } + + for (int i = 0; i < argc; i++) { + bytes += strlen(args[i]) + 1; + } + + return bytes + (argc + 1 /* The NULL terminator */) * sizeof(char *); +} + +static inline task_struct *__alloc_task(task_struct *source, task_struct *parent, const char *name) +{ + // Create a new task_struct. + task_struct *proc = kmem_cache_alloc(task_struct_cache, GFP_KERNEL); + // Clear the memory. + memset(proc, 0, sizeof(task_struct)); + // Set the id of the process. + proc->pid = scheduler_getpid(); + // Set the state of the process as running. + proc->state = TASK_RUNNING; + // Set the current opened file descriptors and the maximum number of file descriptors. + if (source) + vfs_dup_task(proc, source); + else + vfs_init_task(proc); + // Set the pointer to process's parent. + proc->parent = parent; + // Initialize the list_head. + list_head_init(&proc->run_list); + // Initialize the children list_head. + list_head_init(&proc->children); + // Initialize the sibling list_head. + list_head_init(&proc->sibling); + // If we have a parent, set the sibling child relation. + if (parent) { + // Set the new_process as child of current. + list_head_add_tail(&proc->sibling, &parent->children); + } + if (source) + memcpy(&proc->thread, &source->thread, sizeof(thread_struct_t)); + // Set the statistics of the process. + proc->uid = 0; + proc->sid = 0; + proc->gid = 0; + proc->se.prio = DEFAULT_PRIO; + proc->se.start_runtime = timer_get_ticks(); + proc->se.exec_start = timer_get_ticks(); + proc->se.exec_runtime = 0; + proc->se.sum_exec_runtime = 0; + proc->se.vruntime = 0; + proc->se.period = 0; + proc->se.deadline = 0; + proc->se.arrivaltime = timer_get_ticks(); + proc->se.executed = false; + proc->se.is_periodic = false; + proc->se.is_under_analysis = false; + proc->se.next_period = 0; + proc->se.worst_case_exec = 0; + proc->se.utilization_factor = 0; + // Initialize the exit code of the process. + proc->exit_code = 0; + // Copy the name. + if (name) + strcpy(proc->name, name); + // Do not touch the task's segments. + proc->mm = NULL; + // Initialize the error number. + proc->error_no = 0; + // Initialize the current working directory. + if (source) + strcpy(proc->cwd, source->cwd); + else + strcpy(proc->cwd, "/"); + // Clear the signal handler. + memset(&proc->sighand, 0x00, sizeof(sighand_t)); + spinlock_init(&proc->sighand.siglock); + atomic_set(&proc->sighand.count, 0); + for (int i = 0; i < NSIG; ++i) { + proc->sighand.action[i].sa_handler = SIG_DFL; + sigemptyset(&proc->sighand.action[i].sa_mask); + proc->sighand.action[i].sa_flags = 0; + } + // Clear the masks. + sigemptyset(&proc->blocked); + sigemptyset(&proc->real_blocked); + sigemptyset(&proc->saved_sigmask); + // Initialzie the data structure storing the pending signals. + list_head_init(&proc->pending.list); + sigemptyset(&proc->pending.signal); + + // Initalize real_timer for intervals + proc->real_timer = NULL; + + return proc; +} + +int init_tasking() +{ + if ((task_struct_cache = KMEM_CREATE(task_struct)) == NULL) { + return 0; + } + return 1; +} + +task_struct *process_create_init(const char *path) +{ + pr_debug("Building init process...\n"); + // Allocate the memory for the process. + init_proc = __alloc_task(NULL, NULL, "init"); + + // == INITIALIZE `/proc/video` ============================================ + // Check that the fd_list is initialized. + assert(init_proc->fd_list && "File descriptor list not initialized."); + assert((init_proc->max_fd > 3) && "File descriptor list cannot contain the standard IOs."); + + // Create STDIN descriptor. + vfs_file_t *stdin = vfs_open("/proc/video", O_RDONLY, 0); + stdin->count++; + init_proc->fd_list[STDIN_FILENO].file_struct = stdin; + init_proc->fd_list[STDIN_FILENO].flags_mask = O_RDONLY; + pr_debug("`/proc/video` stdin : %p\n", stdin); + + // Create STDOUT descriptor. + vfs_file_t *stdout = vfs_open("/proc/video", O_WRONLY, 0); + stdout->count++; + init_proc->fd_list[STDOUT_FILENO].file_struct = stdout; + init_proc->fd_list[STDOUT_FILENO].flags_mask = O_WRONLY; + pr_debug("`/proc/video` stdout : %p\n", stdout); + + // Create STDERR descriptor. + vfs_file_t *stderr = vfs_open("/proc/video", O_WRONLY, 0); + stderr->count++; + init_proc->fd_list[STDERR_FILENO].file_struct = stderr; + init_proc->fd_list[STDERR_FILENO].flags_mask = O_WRONLY; + pr_debug("`/proc/video` stderr : %p\n", stderr); + // ------------------------------------------------------------------------ + + // == INITIALIZE TASK MEMORY ============================================== + // Load the executable. + if (!__load_executable(path, init_proc, &init_proc->thread.regs.eip)) { + pr_err("Entry for init: %d\n", init_proc->thread.regs.eip); + kernel_panic("Init not valid (%d)!"); + } + // ------------------------------------------------------------------------ + + // == INITIALIZE PROGRAM ARGUMENTS ======================================== + // Save the current page directory. + page_directory_t *crtdir = paging_get_current_directory(); + // Switch to init page directory. + paging_switch_directory_va(init_proc->mm->pgd); + + // Prepare argv and envp for the init process. + char **argv_ptr, **envp_ptr; + int argc = 1; + static char *argv[] = { + "/bin/init", + (char *)NULL + }; + static char *envp[] = { + (char *)NULL + }; + // Save where the arguments start. + init_proc->mm->arg_start = init_proc->thread.regs.useresp; + // Push the arguments on the stack. + __push_args_on_stack(&init_proc->thread.regs.useresp, argv, &argv_ptr); + // Save where the arguments end. + init_proc->mm->arg_end = init_proc->thread.regs.useresp; + // Save where the environmental variables start. + init_proc->mm->env_start = init_proc->thread.regs.useresp; + // Push the environment on the stack. + __push_args_on_stack(&init_proc->thread.regs.useresp, envp, &envp_ptr); + // Save where the environmental variables end. + init_proc->mm->env_end = init_proc->thread.regs.useresp; + // Push the `main` arguments on the stack (argc, argv, envp). + PUSH_ARG(init_proc->thread.regs.useresp, char **, envp_ptr); + PUSH_ARG(init_proc->thread.regs.useresp, char **, argv_ptr); + PUSH_ARG(init_proc->thread.regs.useresp, int, argc); + + // Restore previous pgdir + paging_switch_directory(crtdir); + // ------------------------------------------------------------------------ + + // Active the current process. + scheduler_enqueue_task(init_proc); + + pr_debug("Executing '%s' (pid: %d)...\n", init_proc->name, init_proc->pid); + + return init_proc; +} + +char *sys_getcwd(char *buf, size_t size) +{ + task_struct *current_process = scheduler_get_current_process(); + if ((current_process != NULL) && (buf != NULL)) { + strncpy(buf, current_process->cwd, size); + return buf; + } + return (char *)-EACCES; } void sys_chdir(char const *path) { - task_struct *current_process = kernel_get_current_process(); - if ((current_process != NULL) && (path != NULL)) { - strcpy(current_process->cwd, path); - } + task_struct *current_process = scheduler_get_current_process(); + if ((current_process != NULL) && (path != NULL)) { + char absolute_path[PATH_MAX]; + realpath(path, absolute_path); + strcpy(current_process->cwd, absolute_path); + } } -pid_t sys_vfork(pt_regs *r) +void sys_fchdir(int fd) { - task_struct *current = kernel_get_current_process(); - if (current == NULL) { - kernel_panic("There is no current process!"); - } - - dbg_print("Forking '%s'(%d) process...\n", current->name, current->pid); - - // Create a new task_struct. - // TODO: process is IN USER SPACE! it should be in KERNEL SPACE! - task_struct *new_process = kmalloc(sizeof(task_struct)); - - // TODO: this is NOT a deep copy. should a deep copy be used here? - memcpy(new_process, current, sizeof(task_struct)); - - // Set the id of the process. - new_process->pid = get_new_pid(); - - // Set the statistics of the process. - new_process->se.prio = DEFAULT_PRIO; - new_process->se.start_runtime = 0; - new_process->se.exec_start = 0; - new_process->se.sum_exec_runtime = 0; - // TODO: vruntime should be the scheduled highest values so far. - new_process->se.vruntime = current->se.vruntime; - - // Create a new stack segment. - new_process->mm = create_process_image(DEFAULT_STACK_SIZE); - char *stack = (char *)new_process->mm->start_stack; - // Copy the father's stack. - memcpy((char *)new_process->mm->start_stack, - (char *)current->mm->start_stack, DEFAULT_STACK_SIZE); - // Set the base address of the stack. - char *ebp = stack + DEFAULT_STACK_SIZE; // TODO: da controllare - // Create a pointer to keep track of the top of the stack. - char *esp = stack + (r->useresp - current->mm->start_stack); - - // Set the top address of the stack. - new_process->thread.useresp = (uintptr_t)esp; - // Set the base address of the stack. - new_process->thread.ebp = (uintptr_t)ebp; - // Set the program counter. - new_process->thread.eip = r->eip; - - // Set the base registers. - new_process->thread.eax = 0; - new_process->thread.ebx = r->ebx; - new_process->thread.ecx = r->ecx; - new_process->thread.edx = r->edx; - - // Enable the interrupts. - new_process->thread.eflags = new_process->thread.eflags | EFLAG_IF; - - // Set the state of the process as running. - new_process->state = TASK_RUNNING; - - // Set current as parent for the new process - new_process->parent = current; - - // Initialize the list_head. - list_head_init(&new_process->run_list); - - // Initialize the children list_head. - list_head_init(&new_process->children); - - // Initialize the children list_head. - list_head_init(&new_process->sibling); - - // Set the new_process as child of current. - list_head_add_tail(¤t->children, &new_process->sibling); - - // Active the new process. - enqueue_task(new_process); - - dbg_print("--------------------------------------------------\n"); - dbg_print("- %s process (PID: %d, eflags: %d)\n", new_process->name, - new_process->pid, new_process->thread.eflags); - dbg_print("\teip : 0x%p\n", new_process->thread.eip); - dbg_print("\tebp : 0x%p\n", new_process->thread.ebp); - dbg_print("\tesp : 0x%p\n", new_process->thread.useresp); - dbg_print("\tStack : 0x%p\n", new_process->mm->start_stack); - dbg_print("\tRunList: 0x%p\n", &new_process->run_list); - dbg_print("--------------------------------------------------\n"); - - dbg_print("Fork of '%s' (child pid: %d) process completed.\n", - current->name, current->pid); - - // Return PID of child process to parent. - return new_process->pid; + // Get the current task. + task_struct *task = scheduler_get_current_process(); + // Check the current FD. + if (fd >= 0 && fd < task->max_fd) { + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + // Check the file. + if (vfd->file_struct != NULL) { + char absolute_path[PATH_MAX]; + realpath(vfd->file_struct->name, absolute_path); + strcpy(task->cwd, absolute_path); + } + } } -static inline int push_args_on_stack(uintptr_t *esp, char *args[], - char ***argsptr) +pid_t sys_fork(pt_regs *f) { - int argc = 0; - char *args_ptr[256]; - // Count the number of arguments. - while (args[argc] != NULL) { - ++argc; - } - // Push terminating NULL. - PUSH_ON_STACK((*esp), char *, (char *)NULL); - // Prepare args with space for the terminating NULL. - for (int i = argc - 1; i >= 0; --i) { - for (int j = strlen(args[i]); j >= 0; --j) { - PUSH_ON_STACK((*esp), char, args[i][j]); - } - args_ptr[i] = (char *)(*esp); - } - // Push terminating NULL. - PUSH_ON_STACK((*esp), char *, (char *)NULL); - // Push array of pointers to the arguments. - for (int i = argc - 1; i >= 0; --i) { - PUSH_ON_STACK((*esp), char *, args_ptr[i]); - } - (*argsptr) = (char **)(*esp); + task_struct *current = scheduler_get_current_process(); + if (current == NULL) + kernel_panic("There is no current process!"); - return argc; + pr_debug("Forking '%s' (pid: %d)...\n", current->name, current->pid); + + // Update current process registers, they should be equal + // to the ones of the child process, except for eax. + scheduler_store_context(f, current); + // Allocate the memory for the process. + task_struct *proc = __alloc_task(current, current, current->name); + // Copy the father's stack, memory, heap etc... to the child process + proc->mm = clone_process_image(current->mm); + // Set the eax as 0, to indicate the child process + proc->thread.regs.eax = 0; + // Enable the interrupts. + proc->thread.regs.eflags = proc->thread.regs.eflags | EFLAG_IF; + + // Copy session and group id of the parent into the child + proc->sid = current->sid; + proc->gid = current->gid; + + // Active the new process. + scheduler_enqueue_task(proc); + + pr_debug("Forked '%s' (pid: %d, gid: %d, sid: %d)...\n", proc->name, proc->pid, proc->gid, proc->sid); + + // Return PID of child process to parent. + return proc->pid; } -int sys_execve(pt_regs *r) +int sys_execve(pt_regs *f) { - char **argv, **_argv, **envp, **_envp; - // Check the current process. - task_struct *current = kernel_get_current_process(); - if (current == NULL) { - kernel_panic("There is no current process!"); - } + // Check the current process. + task_struct *current = scheduler_get_current_process(); + if (current == NULL) + kernel_panic("There is no current process!"); - // Get the filename. - uintptr_t *filename = (uintptr_t *)r->ebx; - if (filename == NULL) { - return -1; - } + char **origin_argv, **saved_argv, **final_argv; + char **origin_envp, **saved_envp, **final_envp; + char name_buffer[NAME_MAX]; - // Get the arguments. - argv = (char **)r->ecx; - // Get the environment. - envp = (char **)r->edx; + // Get the filename. + char *filename = (char *)f->ebx; + if (filename == NULL) { + pr_err("Received NULL filename.\n"); + return -1; + } + // Get the arguments + origin_argv = (char **)f->ecx; + // Get the environment. + origin_envp = (char **)f->edx; + // Check the argument, the environment, and that at least the name is provided. + if (origin_argv == NULL) { + pr_err("sys_execve failed: must provide argv.\n"); + return -1; + } + if (origin_argv[0] == NULL) { + pr_err("sys_execve failed: must provide the name.\n"); + return -1; + } + if (origin_envp == NULL) { + pr_err("sys_execve failed: must provide the environment.\n"); + return -1; + } - // Check the argument and that at least the name is provided. - if ((argv == NULL) || (argv[0] == NULL)) { - return -1; - } + // Save the name of the process. + strcpy(name_buffer, origin_argv[0]); - // Check that the environment is provided. - if (envp == NULL) { - kernel_panic("You must provide at least an empty list for envp!"); - } + // == COPY PROGRAM ARGUMENTS ============================================== + // Copy argv and envp to kernel memory, because all the old process memory will be discarded. + int argv_bytes = __count_args_bytes(origin_argv); + int envp_bytes = __count_args_bytes(origin_envp); + if ((argv_bytes < 0) || (envp_bytes < 0)) { + pr_err("Failed to count required memory to store arguments and environment (%d + %d).\n", + argv_bytes, envp_bytes); + return -1; + } + void *args_mem = kmalloc(argv_bytes + envp_bytes); + if (!args_mem) { + pr_err("Failed to allocate memory for arguments and environment %d (%d + %d).\n", + argv_bytes + envp_bytes, argv_bytes, envp_bytes); + return -1; + } + // Copy the arguments. + uint32_t args_mem_ptr = (uint32_t)args_mem + (argv_bytes + envp_bytes); + __push_args_on_stack(&args_mem_ptr, origin_argv, &saved_argv); + __push_args_on_stack(&args_mem_ptr, origin_envp, &saved_envp); + // Check the memory pointer. + assert(args_mem_ptr == (uint32_t)args_mem); + // ------------------------------------------------------------------------ - // Set the name. - strcpy(current->name, argv[0]); + // == INITIALIZE TASK MEMORY ============================================== + if (!__load_executable(filename, current, ¤t->thread.regs.eip)) { + pr_err("Failed to load executable!\n"); + // Free the temporary args memory. + kfree(args_mem); + return -1; + } + // ------------------------------------------------------------------------ - // Set the top address of the stack. - current->thread.useresp = (uintptr_t)current->thread.ebp; + // == INITIALIZE PROGRAM ARGUMENTS ======================================== + // Save the current page directory. + page_directory_t *crtdir = paging_get_current_directory(); - // Set the program counter. - current->thread.eip = (uintptr_t)filename; + // Change the page directory to point to the newly created process + paging_switch_directory_va(current->mm->pgd); - int argc = push_args_on_stack(¤t->thread.useresp, argv, &_argv); - push_args_on_stack(¤t->thread.useresp, envp, &_envp); + // Save where the arguments start. + current->mm->arg_start = current->thread.regs.useresp; + // Push the arguments on the stack. + int argc = __push_args_on_stack(¤t->thread.regs.useresp, saved_argv, &final_argv); + // Save where the arguments end, and the env starts. + current->mm->env_start = current->mm->arg_end = current->thread.regs.useresp; + // Push the environment on the stack. + int envc = __push_args_on_stack(¤t->thread.regs.useresp, saved_envp, &final_envp); + // Save where the environmental variables end. + current->mm->env_end = current->thread.regs.useresp; + // Push the `main` arguments on the stack (argc, argv, envp). + PUSH_ARG(current->thread.regs.useresp, char **, final_envp); + PUSH_ARG(current->thread.regs.useresp, char **, final_argv); + PUSH_ARG(current->thread.regs.useresp, int, argc); - PUSH_ON_STACK(current->thread.useresp, char **, _envp); - PUSH_ON_STACK(current->thread.useresp, char **, _argv); - PUSH_ON_STACK(current->thread.useresp, int, argc); - PUSH_ON_STACK(current->thread.useresp, uintptr_t, (uintptr_t)exit_handler); + // Restore previous pgdir + paging_switch_directory(crtdir); + // ------------------------------------------------------------------------ - // dbg_print("_ARGV:0x%09x {\n", _argv); - // for (int i = 0; _argv[i] != NULL; ++i) { - // dbg_print("\t[%d][0x%09x]%s\n", i, _argv[i], _argv[i]); - // } - // dbg_print("}\n"); - // - // if (_envp != NULL) { - // dbg_print("_ENVP:0x%09x {\n", _envp); - // for (int i = 0; _envp[i] != NULL; ++i) { - // dbg_print("\t[%d][0x%09x]%s\n", i, _envp[i], _envp[i]); - // } - // dbg_print("}\n"); - // } + // Change the name of the process. + strcpy(current->name, name_buffer); - // Perform the switch to the new process. - do_switch(current, r); + // Free the temporary args memory. + kfree(args_mem); - dbg_print("Executing '0x%p' for process %d with %d arguments (0x%p)...\n", - filename, current->pid, argc, argv); + // Perform the switch to the new process. + scheduler_restore_context(current, f); - return 0; + pr_debug("Executing '%s' (pid: %d)...\n", current->name, current->pid); + return 0; } diff --git a/mentos/src/process/scheduler.c b/mentos/src/process/scheduler.c index feac995..e578c85 100644 --- a/mentos/src/process/scheduler.c +++ b/mentos/src/process/scheduler.c @@ -1,9 +1,15 @@ /// MentOS, The Mentoring Operating system project /// @file scheduler.c /// @brief Scheduler structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Change the header. +#define __DEBUG_HEADER__ "[SCHED ]" + +#include "assert.h" +#include "strerror.h" +#include "vfs.h" #include "scheduler.h" #include "tss.h" #include "fpu.h" @@ -12,11 +18,13 @@ #include "kheap.h" #include "panic.h" #include "debug.h" -#include "clock.h" +#include "time.h" #include "errno.h" -#include "rbtree.h" -#include "stdlib.h" #include "list_head.h" +#include "paging.h" +#include "timer.h" +#include "math.h" +#include "stdio.h" /// @brief Assembly function setting the kernel stack to jump into /// location in Ring 3 mode (USER mode). @@ -27,357 +35,647 @@ extern void enter_userspace(uintptr_t location, uintptr_t stack); /// The list of processes. runqueue_t runqueue; -uint32_t get_new_pid(void) +void scheduler_initialize() { - /// The current unused PID. - static unsigned long int tid = 1; - - // Return the pid and increment. - return tid++; + // Initialize the runqueue list of tasks. + list_head_init(&runqueue.queue); + // Reset the current task. + runqueue.curr = NULL; + // Reset the number of active tasks. + runqueue.num_active = 0; } -task_struct *kernel_get_current_process() +uint32_t scheduler_getpid(void) { - return runqueue.curr; + /// The current unused PID. + static unsigned long int tid = 1; + + // Return the pid and increment. + return tid++; } -task_struct *kernel_get_running_process(pid_t pid) +task_struct *scheduler_get_current_process() { - list_head *it; - list_for_each (it, &runqueue.queue) { - task_struct *entry = list_entry(it, task_struct, run_list); - if (entry != NULL) { - if (entry->pid == pid) { - return entry; - } - } - } - return NULL; + return runqueue.curr; } -size_t kernel_get_active_processes() +time_t scheduler_get_maximum_vruntime() { - return runqueue.num_active; + time_t vruntime = 0; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + // Check if we reached the head of list_head, and skip it. + if (it == &runqueue.queue) + continue; + // Get the current entry. + entry = list_entry(it, task_struct, run_list); + // Skip the process if it is a periodic one, we are issued to skip + // periodic tasks, and the entry is not a periodic task under + // analysis. + if (entry->se.is_periodic && !entry->se.is_under_analysis) + continue; + if (entry->se.vruntime > vruntime) + vruntime = entry->se.vruntime; + } + return vruntime; } -void kernel_initialize_scheduler() +size_t scheduler_get_active_processes() { - // Initialize the runqueue list of tasks. - list_head_init(&runqueue.queue); - // Reset the current task. - runqueue.curr = NULL; - // Reset the number of active tasks. - runqueue.num_active = 0; + return runqueue.num_active; } -void enqueue_task(task_struct *process) +task_struct *scheduler_get_running_process(pid_t pid) { - // If current_process is NULL, then process is the current process. - if (runqueue.curr == NULL) { - runqueue.curr = process; - } - // Add the new process at the end. - list_head_add_tail(&process->run_list, &runqueue.queue); - // Increment the number of active processes. - ++runqueue.num_active; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + if (entry->pid == pid) + return entry; + } + return NULL; } -void dequeue_task(task_struct *process) +void scheduler_enqueue_task(task_struct *process) { - // Delete the process from the list of running processes. - list_head_del(&process->run_list); - // Decrement the number of active processes. - --runqueue.num_active; + // If current_process is NULL, then process is the current process. + if (runqueue.curr == NULL) { + runqueue.curr = process; + } + // Add the new process at the end. + list_head_add_tail(&process->run_list, &runqueue.queue); + // Increment the number of active processes. + ++runqueue.num_active; } -void kernel_schedule(pt_regs *f) +void scheduler_dequeue_task(task_struct *process) { - // Check if there is a running process. - if (runqueue.curr == NULL) { - return; - } - - //==== Update Statistics =================================================== - time_t delta_exec = get_millisecond() - runqueue.curr->se.exec_start; - // dbg_print("[%3d] %d = %d - %d\n", runqueue.curr->pid, delta_exec, - // get_millisecond(), runqueue.curr->se.exec_start); - // set the sum_exec_runtime - runqueue.curr->se.sum_exec_runtime += delta_exec; - //========================================================================== - - //==== Handle Zombies ====================================================== - task_struct *next_process = NULL; - if (runqueue.curr->state == EXIT_ZOMBIE) { - // get the next process after the current one - list_head *nNode = runqueue.curr->run_list.next; - // check if we reached the head of list_head - if (nNode == &runqueue.queue) - nNode = nNode->next; - // get the task_struct - next_process = list_entry(nNode, task_struct, run_list); - // Remove the zombie task. - dequeue_task(runqueue.curr); - } else { - //==== Scheduling ====================================================== - // Pointer to the next process to be executed. - next_process = pick_next_task(&runqueue, delta_exec); - //====================================================================== - } - //========================================================================== - - // Print, for debugging purpose, data about the current process. - if (runqueue.num_active > 2) { - dbg_print("PID:%3d, PRIO:%3d, VRUNTIME:%9d, SUM_EXEC:%9d\n", - next_process->pid, next_process->se.prio, - next_process->se.vruntime, next_process->se.sum_exec_runtime); - } - - //==== Context switch ====================================================== - // Update the context of the current process. - update_context(f, runqueue.curr); - // Check if the next and current processes are different. - if (next_process != runqueue.curr) { - // Copy into Kernel stack the next process's context. - do_switch(next_process, f); - runqueue.curr->se.sum_exec_runtime = get_millisecond(); - // Update the last context switch time of the next process. - next_process->se.exec_start = get_millisecond(); - } - //========================================================================== - - // Update the start execution time if it is executed for the first time - if (next_process->se.start_runtime == 0) - next_process->se.start_runtime = get_millisecond(); + // Delete the process from the list of running processes. + list_head_del(&process->run_list); + // Decrement the number of active processes. + --runqueue.num_active; + if (process->se.is_periodic) + runqueue.num_periodic--; } -void update_context(pt_regs *f, task_struct *process) +void scheduler_run(pt_regs *f) { - // Store the registers. - process->thread.gs = f->gs; - process->thread.fs = f->fs; - process->thread.es = f->es; - process->thread.ds = f->ds; - process->thread.edi = f->edi; - process->thread.esi = f->esi; - process->thread.ebp = f->ebp; - process->thread.ebx = f->ebx; - process->thread.edx = f->edx; - process->thread.ecx = f->ecx; - process->thread.eax = f->eax; - process->thread.eip = f->eip; - process->thread.eflags = f->eflags; - process->thread.useresp = f->useresp; - // TODO: Check if the following registers should be saved. - // process->thread.cs = f->cs; - // process->thread.ss = f->ss; - // Store the FPU. - switch_fpu(); + // Check if there is a running process. + if (runqueue.curr == NULL) + return; + + task_struct *next = NULL; + + // Update the context of the current process. + scheduler_store_context(f, runqueue.curr); + + // We check the existence of pending signals every time we finish + // handling an interrupt or an exception. + if (!do_signal(f)) { +#if 1 + if (runqueue.curr->state == EXIT_ZOMBIE) { + //==== Handle Zombies ================================================= + //pr_debug("Handle zombie %d\n", runqueue.curr->pid); + // get the next process after the current one + list_head *nNode = runqueue.curr->run_list.next; + // check if we reached the head of list_head + if (nNode == &runqueue.queue) { + nNode = nNode->next; + } + // get the task_struct + next = list_entry(nNode, task_struct, run_list); + // Remove the zombie task. + scheduler_dequeue_task(runqueue.curr); + assert(next && "No valid task selected after removing ZOMBIE."); + //===================================================================== + } else { +#endif + //==== Scheduling ===================================================== + // If we are currently executing a periodic process, and this process + // has yet to complete, keep executing it. +#ifdef SCHEDULER_EDF + if (runqueue.curr->se.is_periodic) + if (!runqueue.curr->se.executed) + return; +#endif + // Pointer to the next process to be executed. + next = scheduler_pick_next_task(&runqueue); + //===================================================================== + } + // Check if the next and current processes are different. + if (next != runqueue.curr) { + // Copy into Kernel stack the next process's context. + scheduler_restore_context(next, f); + } + } + //========================================================================== } -void do_switch(task_struct *process, pt_regs *f) +void scheduler_store_context(pt_regs *f, task_struct *process) { - // Switch to the next process. - runqueue.curr = process; - // Restore the registers. - f->gs = process->thread.gs; - f->fs = process->thread.fs; - f->es = process->thread.es; - f->ds = process->thread.ds; - f->edi = process->thread.edi; - f->esi = process->thread.esi; - f->ebp = process->thread.ebp; - f->ebx = process->thread.ebx; - f->edx = process->thread.edx; - f->ecx = process->thread.ecx; - f->eax = process->thread.eax; - f->eip = process->thread.eip; - f->eflags = process->thread.eflags; - f->useresp = process->thread.useresp; - // TODO: Check if the following registers should be restored. - // f->cs = process->thread.cs; - // f->ss = process->thread.ss; - // Restore the FPU. - unswitch_fpu(); + // Store the registers. + process->thread.regs = *f; } -int set_user_nice(task_struct *p, long nice) +void scheduler_restore_context(task_struct *process, pt_regs *f) { - if (PRIO_TO_NICE(p->se.prio) != nice && nice >= MIN_NICE && - nice <= MAX_NICE) { - p->se.prio = NICE_TO_PRIO(nice); - } - - return PRIO_TO_NICE(p->se.prio); + // Switch to the next process. + runqueue.curr = process; + // Restore the registers. + *f = process->thread.regs; + // TODO: Explain paging switch (ring 0 doesn't need page switching) + // Switch to process page directory + paging_switch_directory_va(process->mm->pgd); } -void enter_user_jmp(uintptr_t location, uintptr_t stack) +void scheduler_enter_user_jmp(uintptr_t location, uintptr_t stack) { - // Reset stack pointer for kernel. - tss_set_stack(0x10, initial_esp); + // Reset stack pointer for kernel. + tss_set_stack(0x10, initial_esp); - // update start execution time. - runqueue.curr->se.start_runtime = get_millisecond(); + // update start execution time. + runqueue.curr->se.start_runtime = timer_get_ticks(); - // last context switch time. - runqueue.curr->se.exec_start = get_millisecond(); + // last context switch time. + runqueue.curr->se.exec_start = timer_get_ticks(); - // Jump in location. - enter_userspace(location, stack); + // Jump in location. + enter_userspace(location, stack); +} + +/// @brief Awakens a sleeping process. +/// @param process The process that should be awakened +/// @param mode The type of wait (TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE). +/// @param sync Specifies if the wakeup should be synchronous. +/// @return 1 on success, 0 on failure. +static inline int try_to_wake_up(task_struct *process, int mode, int sync) +{ + // Only tasks in the state TASK_UNINTERRUPTIBLE can be woke up + if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_STOPPED) { + //TODO: Recalc task priority + process->state = TASK_RUNNING; + return 1; + } + return 0; +} + +int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) +{ + task_struct *p = wait->task; + return try_to_wake_up(p, mode, sync); +} + +wait_queue_entry_t *sleep_on(wait_queue_head_t *wq) +{ + // Save the sleeping process registers state + task_struct *sleeping_task = scheduler_get_current_process(); + +#if 0 + pt_regs* f = get_current_interrupt_stack_frame(); + scheduler_store_context(f, sleeping_task); + + // Select next process in the runqueue as the current, restore it's context, + // we assume that the first process is init wich does not sleep (I hope). + // This is necessary to make the scheduler_run() in syscall_handler work. + task_struct *next = list_entry(runqueue.queue.next, task_struct, run_list); + assert((next != sleeping_task) && "The next selected process in the runqueue is the sleeping process"); + scheduler_restore_context(next, f); +#endif + + // Stops task from runqueue making it unrunnable + sleeping_task->state = TASK_UNINTERRUPTIBLE; + + // Add sleeping process to sleep wait queue + wait_queue_entry_t *wait_entry = kmalloc(sizeof(struct wait_queue_entry_t)); + init_waitqueue_entry(wait_entry, sleeping_task); + add_wait_queue(wq, wait_entry); + + return wait_entry; +} + +int is_orphaned_pgrp(pid_t gid) +{ + pid_t sid = 0; + + // Obtain SID of the group from a member + list_head *it; + list_for_each (it, &runqueue.queue) { + task_struct *task = list_entry(it, task_struct, run_list); + if (task->gid == gid) { + sid = task->sid; + break; + } + } + + // Check if the process leader of the session is alive + list_for_each (it, &runqueue.queue) { + task_struct *task = list_entry(it, task_struct, run_list); + if (task->pid == sid) { + return 0; + } + } + + return 1; } pid_t sys_getpid() { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - // Return the process identifer of the process. - return runqueue.curr->pid; + // Return the process identifer of the process. + return runqueue.curr->pid; +} + +pid_t sys_getsid(pid_t pid) +{ + //If pid == 0 return SID of the calling process + if (pid == 0) { + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } + // Return the session identifer of the process. + return runqueue.curr->sid; + } + //If != 0 get SID of the specified process + list_head *it; + list_for_each (it, &runqueue.queue) { + task_struct *task = list_entry(it, task_struct, run_list); + if (task->pid == pid) + { + if(runqueue.curr->sid != task->sid) + return -EPERM; + + return task->sid; + } + } + return -ESRCH; +} + +pid_t sys_setsid() +{ + task_struct *task = runqueue.curr; + if (task == NULL) { + kernel_panic("There is no current process!"); + } + if (task->sid == task->pid) + { + pr_debug("Process %d is already a session leader.", task->pid); + return -EPERM; + } + + task->sid = task->pid; + task->gid = task->pid; + + return task->sid; +} + +pid_t sys_getgid() +{ + task_struct *curr = runqueue.curr; + if (curr == NULL) { + kernel_panic("There is no current process!"); + } + + return curr->gid; +} + +int sys_setgid(pid_t gid) +{ + task_struct *curr = runqueue.curr; + if (curr == NULL) { + kernel_panic("There is no current process!"); + } + + if (curr->gid == curr->pid) + pr_debug("Process %d is already a session leader.", task->pid); + + curr->gid = curr->pid; + return 0; } pid_t sys_getppid() { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } - if (runqueue.curr->parent == NULL) { - return 0; - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } + if (runqueue.curr->parent == NULL) { + return 0; + } - // Return the parent process identifer of the process. - return runqueue.curr->parent->pid; + // Return the parent process identifer of the process. + return runqueue.curr->parent->pid; } int sys_nice(int increment) { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - if (increment < -40) { - increment = -40; - } - if (increment > 40) { - increment = 40; - } + if (increment < -40) { + increment = -40; + } + if (increment > 40) { + increment = 40; + } - int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment; - dbg_print("New nice value would be : %d\n", newNice); + int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment; + pr_debug("New nice value would be : %d\n", newNice); - if (newNice < MIN_NICE) { - newNice = MIN_NICE; - } - if (newNice > MAX_NICE) { - newNice = MAX_NICE; - } + if (newNice < MIN_NICE) { + newNice = MIN_NICE; + } + if (newNice > MAX_NICE) { + newNice = MAX_NICE; + } - int actualNice = set_user_nice(runqueue.curr, newNice); - dbg_print("Actual new nice value is: %d\n", actualNice); + if (PRIO_TO_NICE(runqueue.curr->se.prio) != newNice && newNice >= MIN_NICE && newNice <= MAX_NICE) { + runqueue.curr->se.prio = NICE_TO_PRIO(newNice); + } + int actualNice = PRIO_TO_NICE(runqueue.curr->se.prio); - return actualNice; + pr_debug("Actual new nice value is: %d\n", actualNice); + + return actualNice; } pid_t sys_waitpid(pid_t pid, int *status, int options) { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - /* For now we do not support waiting for processes inside the given + /* For now we do not support waiting for processes inside the given * process group (pid < -1). */ - if ((pid < -1) || (pid == 0)) { - errno = ESRCH; - - return (-1); - } - if (pid == runqueue.curr->pid) { - errno = ECHILD; - - return (-1); - } - if (options != 0 && options != WNOHANG) { - errno = EINVAL; - - return (-1); - } - if (status == NULL) { - errno = EFAULT; - - return (-1); - } - list_head *it; - list_for_each (it, &runqueue.curr->children) { - task_struct *entry = list_entry(it, task_struct, sibling); - if (entry == NULL) { - continue; - } - if (entry->state != EXIT_ZOMBIE) { - continue; - } - if ((pid > 1) && (entry->pid != pid)) { - continue; - } - // Save the pid to return. - pid_t ppid = entry->pid; - // Save the state. - (*status) = entry->state; //TODO: da rivedere - // Remove entry from children of parent. - list_head_del(&entry->sibling); - // Delete the task_struct. - kfree(entry); - dbg_print("Freeing memory of process %d.\n", ppid); - - return ppid; - } - - return 0; + if ((pid < -1) || (pid == 0)) { + return -ESRCH; + } + if (pid == runqueue.curr->pid) { + return -ECHILD; + } + if (options != 0 && options != WNOHANG) { + return -EINVAL; + } +#if 0 + if (status == NULL) { + return -EFAULT; + } +#endif + if (list_head_empty(&runqueue.curr->children)) { + return -ECHILD; + } + list_head *it; + list_for_each (it, &runqueue.curr->children) { + task_struct *entry = list_entry(it, task_struct, sibling); + if (entry == NULL) { + continue; + } + if (entry->state != EXIT_ZOMBIE) { + continue; + } + if ((pid > 1) && (entry->pid != pid)) { + continue; + } + // Save the pid to return. + pid_t ppid = entry->pid; + // Save the state (TODO: Improve status set). + if (status) + (*status) = entry->state; + // Finalize the VFS structures. + vfs_destroy_task(entry); + // Remove entry from children of parent. + list_head_del(&entry->sibling); + // Remove entry from the scheduling queue. + scheduler_dequeue_task(entry); + // Delete the task_struct. + kmem_cache_free(entry); + pr_debug("Process %d is freeing memory of process %d.\n", runqueue.curr->pid, ppid); + return ppid; + } + return 0; } void sys_exit(int exit_code) { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - task_struct *init_proc = kernel_get_running_process(1); - if (runqueue.curr == init_proc) { - kernel_panic("Init process cannot call sys_exit!"); - } - // Set the termination code of the process. - runqueue.curr->exit_code = (exit_code << 8) & 0xFF00; - // Set the state of the process to zombie. - runqueue.curr->state = EXIT_ZOMBIE; - // If it has children, then init process has to take care of them. - if (!list_head_empty(&runqueue.curr->children)) { - dbg_print("Moving children of %s(%d) to init(%d): {\n", - runqueue.curr->name, runqueue.curr->pid, init_proc->pid); - // TODO: Try to plug the list of children instead of iterating. - list_head *it; - list_for_each (it, &runqueue.curr->children) { - task_struct *entry = list_entry(it, task_struct, sibling); - dbg_print(" [%d] %s\n", entry->pid, entry->name); - it = entry->sibling.prev; - list_head_del(&entry->sibling); - list_head_add_tail(&init_proc->children, &entry->sibling); - entry->parent = init_proc; - } - dbg_print("}\n"); - dbg_print("Listing children of init(%d): {\n", init_proc->pid); - list_for_each (it, &init_proc->children) { - task_struct *entry = list_entry(it, task_struct, sibling); - dbg_print(" [%d] %s\n", entry->pid, entry->name); - } - dbg_print("}\n"); - } - // Free the space occupied by the stack. - destroy_process_image(runqueue.curr->mm); - // Debugging message. - dbg_print("Process %d exited with value %d\n", runqueue.curr->pid, - exit_code); + // Get the process. + task_struct *init_proc = scheduler_get_running_process(1); + if (runqueue.curr == init_proc) { + kernel_panic("Init process cannot call sys_exit!"); + } + + // Set the termination code of the process. + runqueue.curr->exit_code = (exit_code << 8) & 0xFF00; + // Set the state of the process to zombie. + runqueue.curr->state = EXIT_ZOMBIE; + // Send a SIGCHLD to the parent process. + if (runqueue.curr->parent) { + int ret = sys_kill(runqueue.curr->parent->pid, SIGCHLD); + if (ret == -1) { + printf("[%d] %5d failed sending signal %d : %s\n", ret, runqueue.curr->parent->pid, + SIGCHLD, strerror(errno)); + } + } + + // If it has children, then init process has to take care of them. + if (!list_head_empty(&runqueue.curr->children)) { + pr_debug("Moving children of %s(%d) to init(%d): {\n", + runqueue.curr->name, runqueue.curr->pid, init_proc->pid); + // Change the parent. + pr_debug("Moving children (%d): {\n", init_proc->pid); + list_for_each_decl(it, &runqueue.curr->children) + { + task_struct *entry = list_entry(it, task_struct, sibling); + pr_debug(" [%d] %s\n", entry->pid, entry->name); + entry->parent = init_proc; + } + pr_debug("}\n"); + // Plug the list of children. + list_head_merge(&init_proc->children, &runqueue.curr->children); + // Print the list of children. + pr_debug("New list of init children (%d): {\n", init_proc->pid); + list_for_each_decl(it, &init_proc->children) + { + task_struct *entry = list_entry(it, task_struct, sibling); + pr_debug(" [%d] %s\n", entry->pid, entry->name); + } + pr_debug("}\n"); + } + // Free the space occupied by the stack. + destroy_process_image(runqueue.curr->mm); + // Debugging message. + pr_debug("Process %d exited with value %d\n", runqueue.curr->pid, exit_code); } + +int sys_sched_setparam(pid_t pid, const sched_param_t *param) +{ + list_head *it; + // Iter over the runqueue to find the task + list_for_each (it, &runqueue.queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + if (entry->pid == pid) { + if (!entry->se.is_periodic && param->is_periodic) + runqueue.num_periodic++; + else if (entry->se.is_periodic && !param->is_periodic) + runqueue.num_periodic--; + // Sets the parameters from param to the "se" struct parameters. + entry->se.prio = param->sched_priority; + entry->se.period = param->period; + entry->se.arrivaltime = param->arrivaltime; + entry->se.is_periodic = param->is_periodic; + entry->se.deadline = timer_get_ticks() + param->deadline; + entry->se.next_period = timer_get_ticks(); + + entry->se.is_under_analysis = true; + entry->se.executed = false; + return 1; + } + } + return -1; +} + +int sys_sched_getparam(pid_t pid, sched_param_t *param) +{ + list_head *it; + // Iter over the runqueue to find the task + list_for_each (it, &runqueue.queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + if (entry->pid == pid) { + //Sets the parameters from the "se" struct to param + param->sched_priority = entry->se.prio; + param->period = entry->se.period; + param->deadline = entry->se.deadline; + param->arrivaltime = entry->se.arrivaltime; + return 1; + } + } + return -1; +} + +/// @brief Performs the response time analysis for the current list +/// of periodic processes. +/// @return 1 if scheduling periodic processes is feasable, 0 otherwise. +static int __response_time_analysis() +{ + task_struct *entry, *previous; + time_t r, previous_r = 0; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + if (entry->se.is_periodic) { + // Put r equal to worst case exec because is the first point in time that the task could possibly complete + r = entry->se.worst_case_exec; + previous_r = 0; + // The analysis can be completed either missing the deadline or reaching a fixed point + while (r < entry->se.deadline && r != previous_r) { + previous_r = r; + r = entry->se.worst_case_exec; + list_for_each_decl(it2, &runqueue.queue) + { + previous = list_entry(it2, task_struct, run_list); + // Check the interferences of higher priority processes + if (previous->se.is_periodic && previous->se.period < entry->se.period) { + r += (int)ceil((double)previous_r / (double)previous->se.period) * previous->se.worst_case_exec; + pr_debug("%d += (%.2f / %.2f) * %d\n", r, (double)previous_r, (double)previous->se.period, previous->se.worst_case_exec); + pr_debug("Response Time Analysis -> [%s]vs[%s] R = %d\n\n", entry->name, previous->name, r); + } + } + } + // Feasibility of scheduler is guaranteed if and only if response time analysis is lower than deadline. + if (r > entry->se.deadline) + return 1; + } + } + return 0; +} + +int sys_waitperiod() +{ + if (runqueue.curr) { + if (runqueue.curr->se.is_periodic) { + // Update the Worst Case Execution Time (WCET). + time_t wcet = timer_get_ticks() - runqueue.curr->se.exec_start; + if (runqueue.curr->se.worst_case_exec < wcet) + runqueue.curr->se.worst_case_exec = wcet; + // Update thye utilization factor. + runqueue.curr->se.utilization_factor = ((double)runqueue.curr->se.worst_case_exec / (double)runqueue.curr->se.period); + + // If the task is under analysis, we need to test if the process can be + // placed with the other periodic tasks. + if (runqueue.curr->se.is_under_analysis) { + runqueue.curr->se.worst_case_exec = runqueue.curr->se.sum_exec_runtime; + bool_t is_not_schedulable = false; +#if defined(SCHEDULER_EDF) + double u = 0; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + // Sum the utilization factor of all periodic tasks. + if (entry->se.is_periodic) + u += entry->se.utilization_factor; + } + if (u > 1) { + is_not_schedulable = true; + } + pr_debug("utilization factor = %f\n", u); +#elif defined(SCHEDULER_RM) + // Calculating least upper bound of utilization factor. + // For large amount of processes ulub asymptotically should reach ln(2). + double ulub = (runqueue.num_periodic * (pow(2, (1.0 / runqueue.num_periodic)) - 1)); + double u = 0; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + // Sum the utilization factor of all periodic tasks. + if (entry->se.is_periodic) + u += entry->se.utilization_factor; + } + // If the sum of utilization factor is bounded between ulub and 1 we need to calculate + // the response time analysis for each process. + if (u > 1) { + is_not_schedulable = true; + } else if (u <= ulub) + is_not_schedulable = false; + else + is_not_schedulable = __response_time_analysis(); +#endif + // If it is not schedulable, we need to tell it to the process. + if (is_not_schedulable) + return -ENOTSCHEDULABLE; + + // Otherwise, it is schedulable. + runqueue.curr->se.is_under_analysis = false; + + // The task has been executed as non-periodic process so that his deadline is not been updated + // by the scheduling algorithm of periodic tasks. We need to update it manually. + runqueue.curr->se.next_period = timer_get_ticks(); + runqueue.curr->se.deadline = timer_get_ticks() + runqueue.curr->se.period; + } + + if (timer_get_ticks() > runqueue.curr->se.deadline) + pr_warning("%d > %d Missing deadline...\n", timer_get_ticks(), runqueue.curr->se.deadline); + + // Tell the scheduler that we have executed the periodic process. + runqueue.curr->se.executed = true; + + } else + pr_warning("An aperiodic task is calling `waitperiod`, ignoring...\n"); + return 0; + } + return -ESRCH; +} \ No newline at end of file diff --git a/mentos/src/process/scheduler_algorithm.c b/mentos/src/process/scheduler_algorithm.c index eb5667d..9cb3994 100644 --- a/mentos/src/process/scheduler_algorithm.c +++ b/mentos/src/process/scheduler_algorithm.c @@ -1,73 +1,125 @@ +/// MentOS, The Mentoring Operating system project /// @file scheduler_algorithm.c /// @brief Round Robin algorithm. -/// @date Mar 2019. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +#include "timer.h" #include "prio.h" #include "debug.h" #include "assert.h" #include "list_head.h" +#include "wait.h" #include "scheduler.h" -#define GET_WEIGHT(prio) prio_to_weight[USER_PRIO((prio))] -#define NICE_0_LOAD GET_WEIGHT(DEFAULT_PRIO) - -task_struct *pick_next_task(runqueue_t *runqueue, time_t delta_exec) +static inline task_struct *scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) { - // Pointer to the next task to schedule. - task_struct *next = NULL; + // If there is just one process, return it. + if ((runqueue->curr->run_list.next == &runqueue->queue) && + (runqueue->curr->run_list.prev == &runqueue->queue)) { + return runqueue->curr; + } + // By default, the next process is the current one. + task_struct *next = NULL, *entry = NULL; + // Search for the next process (BEWARE: We do not start from the head, so INSIDE skip the head). + list_for_each_decl(it, &runqueue->curr->run_list) + { + // Check if we reached the head of list_head, and skip it. + if (it == &runqueue->queue) + continue; + // Get the current entry. + entry = list_entry(it, task_struct, run_list); + // We consider only runnable processes + if (entry->state != TASK_RUNNING) + continue; + + // Skip the process if it is a periodic one, we are issued to skip + // periodic tasks, and the entry is not a periodic task under + // analysis. + if (entry->se.is_periodic && skip_periodic && !entry->se.is_under_analysis) + continue; + + // We have our next entry. + next = entry; + break; + } + return next; +} + +static inline task_struct *scheduler_priority(runqueue_t *runqueue, bool_t skip_periodic) +{ + return scheduler_rr(runqueue, skip_periodic); +} + +static inline task_struct *scheduler_cfs(runqueue_t *runqueue, bool_t skip_periodic) +{ + return scheduler_rr(runqueue, skip_periodic); +} + +static inline task_struct *scheduler_aedf(runqueue_t *runqueue) +{ + return scheduler_rr(runqueue, false); +} + +static inline task_struct *scheduler_edf(runqueue_t *runqueue) +{ + return scheduler_rr(runqueue, false); +} + +static inline task_struct *scheduler_rm(runqueue_t *runqueue) +{ + return scheduler_rr(runqueue, false); +} + +task_struct *scheduler_pick_next_task(runqueue_t *runqueue) +{ + // While periodic task is under analysis is executed with aperiodic + // scheduler and can be preempted by a "true" periodic task. + // We need to sum all the execution spots to calculate the WCET even + // if is a more pessimistic evaluation. + // Update the delta exec. + runqueue->curr->se.exec_runtime = timer_get_ticks() - runqueue->curr->se.exec_start; + update_process_profiling_timer(runqueue->curr); + + // set the sum_exec_runtime. + runqueue->curr->se.sum_exec_runtime += runqueue->curr->se.exec_runtime; + + // If the task is not a periodic task we have to update the virtual runtime. + if (!runqueue->curr->se.is_periodic) { + // Get the weight of the current process. + time_t weight = GET_WEIGHT(runqueue->curr->se.prio); + if (weight != NICE_0_LOAD) { + // get the multiplicative factor for its delta_exec. + double factor = ((double)NICE_0_LOAD) / ((double)weight); + // weight the delta_exec with the multiplicative factor. + runqueue->curr->se.exec_runtime = (int)(((double)runqueue->curr->se.exec_runtime) * factor); + } + // Update vruntime of the current process. + runqueue->curr->se.vruntime += runqueue->curr->se.exec_runtime; + } + + // Pointer to the next task to schedule. + task_struct *next = NULL; #if defined(SCHEDULER_RR) - //==== Implementatin of the Round-Robin Scheduling algorithm ============ - - - - //======================================================================= + next = scheduler_rr(runqueue, false); #elif defined(SCHEDULER_PRIORITY) - //==== Implementatin of the Priority Scheduling algorithm =============== - - // get the first element of the list - next = list_entry(/*...*/); - - // Get its static priority. - time_t min = /*...*/ - - list_head *it; - // Inter over the runqueue to find the task with the smallest priority value - list_for_each (it, &runqueue->queue) { - task_struct *entry = list_entry(/*...*/); - // Check entry has a lower priority - if (/*...*/) { - /*...*/ - } - } - - //======================================================================= + next = scheduler_priority(runqueue, false); #elif defined(SCHEDULER_CFS) - //==== Implementatin of the Completely Fair Scheduling ================== - - // Get the weight of the current process. - // (use GET_WEIGHT macro!) - int weight = /*...*/ - - if (weight != NICE_0_LOAD) { - // get the multiplicative factor for its delta_exec. - double factor = /*...*/ - - // weight the delta_exec with the multiplicative factor. - delta_exec = // ... - } - - // Update vruntime of the current process. - // ... - - // Inter over the runqueue to find the task with the smallest vruntime value - // ... - - //======================================================================== + next = scheduler_cfs(runqueue, false); +#elif defined(SCHEDULER_EDF) + next = scheduler_edf(runqueue); +#elif defined(SCHEDULER_RM) + next = scheduler_rm(runqueue); +#elif defined(SCHEDULER_AEDF) + next = scheduler_aedf(runqueue); #else #error "You should enable a scheduling algorithm!" #endif - assert(next && "No valid task selected. Have you implemented a scheduling algorithm?"); + assert(next && "No valid task selected by the scheduling algorithm."); - return next; + // Update the last context switch time of the next process. + next->se.exec_start = timer_get_ticks(); + + return next; } diff --git a/mentos/src/process/user.asm b/mentos/src/process/user.S similarity index 69% rename from mentos/src/process/user.asm rename to mentos/src/process/user.S index 4116e81..044fb0a 100644 --- a/mentos/src/process/user.asm +++ b/mentos/src/process/user.S @@ -1,23 +1,38 @@ ; MentOS, The Mentoring Operating system project ; @file user.asm ; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. ; Enter userspace (ring3) (from Ring 0, namely Kernel) ; Usage: enter_userspace(uintptr_t location, uintptr_t stack); ; On stack -; | stack | -; | location | -; | return address | -; | EBP | EBP +; | stack | [ebp + 0x0C] ARG1 +; | location | [ebp + 0x08] ARG0 +; | return address | [ebp + 0x04] +; | EBP | [ebp + 0x00] ; | SS | ; | ESP | ; | EFLAGS | ; | CS | ; | EIP | +; We can use the following macros to access the arguments ONLY AFTER 0x23 is +; pushed onto the stack, in fact, the first argument is after 0x08 because +; we just pushed first `ebp` and then `0x23`. +%define ARG0 [ebp + 0x08] ; Argument 0 +%define ARG1 [ebp + 0x0C] ; Argument 1 +%define ARG2 [ebp + 0x10] ; Argument 2 +%define ARG3 [ebp + 0x14] ; Argument 3 +%define ARG4 [ebp + 0x18] ; Argument 4 + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + global enter_userspace ; Allows the C code to call enter_userspace(...). + enter_userspace: push ebp ; Save current ebp @@ -45,38 +60,28 @@ enter_userspace: ;--------------------------------------------------------------------------- ;==== (ESP) Stack address ================================================== - mov eax, [ebp + 0xC] ; get uintptr_t stack + mov eax, ARG1 ; get uintptr_t stack push eax ; push process's stack address on Kernel's stack ;--------------------------------------------------------------------------- ;==== (EFLAGS) ============================================================= pushf ; push EFLAGS into Kernel's stack pop eax ; pop EFLAGS into eax - or eax, 0x200 ; enable interrupt ?request ring3 + or eax, 0x200 ; enable interrupt push eax ; push new EFLAGS on Kernel's stack ;--------------------------------------------------------------------------- ;==== (CS) Code Segment ==================================================== - push 0x1B ; + push 0x1b ; ;--------------------------------------------------------------------------- ;==== (EIP) Entry point ==================================================== - mov eax, [ebp + 0x8] ; get uintptr_t location + mov eax, ARG0 ; get uintptr_t location push eax ; push uintptr_t location on Kernel's stack ;--------------------------------------------------------------------------- iret ; interrupt return + pop ebp + ret - ; WE SHOULD NOT STILL BE HERE! :( - - ;==== Reset segment selector =============================================== - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - ;--------------------------------------------------------------------------- - - add esp, 0x14 ; reset stack pointer (20 bytes) - pop ebp ; reset value of ebp - ret ; return to kernel code + ; WE SHOULD NOT STILL BE HERE! :(p \ No newline at end of file diff --git a/mentos/src/process/wait.c b/mentos/src/process/wait.c new file mode 100644 index 0000000..6680e27 --- /dev/null +++ b/mentos/src/process/wait.c @@ -0,0 +1,42 @@ +/// MentOS, The Mentoring Operating system project +/// @file wait.c +/// @brief wait functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[WAIT ]" + +#include "wait.h" + +static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + list_head_add_tail(&wq->task_list, &head->task_list); +} + +static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + list_head_del(&wq->task_list); +} + +void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task) +{ + wq->flags = 0; + wq->task = task; + wq->func = default_wake_function; +} + +void add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + wq->flags &= ~WQ_FLAG_EXCLUSIVE; + spinlock_lock(&head->lock); + __add_wait_queue(head, wq); + spinlock_unlock(&head->lock); +} + +void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + spinlock_lock(&head->lock); + __remove_wait_queue(head, wq); + spinlock_unlock(&head->lock); +} \ No newline at end of file diff --git a/mentos/src/sys/dirent.c b/mentos/src/sys/dirent.c deleted file mode 100644 index 18a851e..0000000 --- a/mentos/src/sys/dirent.c +++ /dev/null @@ -1,84 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file dirent.c -/// @brief Functions used to manage directories. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "dirent.h" -#include "string.h" -#include "vfs.h" -#include "kheap.h" -#include "stdio.h" -#include "initrd.h" -#include "debug.h" -#include "syscall_types.h" -#include "assert.h" -#include "errno.h" - -DIR *opendir(const char *path) -{ - char absolute_path[PATH_MAX]; - DIR *pdir = NULL; - - strcpy(absolute_path, path); - // If the first character is not the '/' then get the absolute path. - if (absolute_path[0] != '/') { - if (!get_absolute_path(absolute_path)) { - dbg_print("Cannot get the absolute path.\n"); - - return NULL; - } - } - - // Get the mount point id. - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) { - printf("opendir: cannot open directory '%s':" - "Cannot find mount-point\n", - absolute_path); - - return NULL; - } - - if (mountpoint_list[mp_id].dir_op.opendir_f == NULL) { - printf("opendir: cannot open directory '%s':" - "No opendir function\n", - absolute_path); - - return NULL; - } - - pdir = mountpoint_list[mp_id].dir_op.opendir_f(absolute_path); - // If the directiry is correctly open, set the handle. - if (pdir != NULL) { - pdir->fd = mp_id; - } - - return pdir; -} - -int closedir(DIR *dirp) -{ - if (dirp == NULL) { - printf("closedir: cannot close directory :" - "Directory pointer is not valid\n"); - - return -1; - } - if (mountpoint_list[dirp->fd].dir_op.closedir_f == NULL) { - printf("closedir: cannot close directory '%s':" - "No closedir function\n", - dirp->path); - - return -1; - } - return mountpoint_list[dirp->fd].dir_op.closedir_f(dirp); -} - -dirent_t *readdir(DIR *dirp) -{ - dirent_t *dent; - // Call the readdir system call. - DEFN_SYSCALL1(dent, __NR_readdir, dirp); - return dent; -} diff --git a/mentos/src/sys/module.c b/mentos/src/sys/module.c index 62bce4e..761103d 100644 --- a/mentos/src/sys/module.c +++ b/mentos/src/sys/module.c @@ -1,7 +1,73 @@ /// MentOS, The Mentoring Operating system project /// @file module.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "slab.h" #include "module.h" +#include "string.h" +#include "sys/bitops.h" +#include "debug.h" + +/// Defined in kernel.ld, points at the end of kernel's data segment. +extern void *_kernel_end; +/// List of modules. +multiboot_module_t modules[MAX_MODULES]; + +int init_modules(multiboot_info_t *header) +{ + for (int i = 0; i < MAX_MODULES; ++i) { + modules[i].mod_start = 0; + modules[i].mod_end = 0; + modules[i].cmdline = 0; + modules[i].pad = 0; + } + if (!bitmask_check(header->flags, MULTIBOOT_FLAG_MODS)) + return 0; + multiboot_module_t *mod = first_module(header); + for (int i = 0; (mod != 0) && (i < MAX_MODULES); + ++i, mod = next_module(header, mod)) { + memcpy(&modules[i], mod, sizeof(multiboot_module_t)); + } + return 1; +} + +int relocate_modules() +{ + for (int i = 0; i < MAX_MODULES; ++i) { + // Exit if modules are finished + if (!modules[i].mod_start) + break; + + // Get module and cmdline sizes + uint32_t mod_size = modules[i].mod_end - modules[i].mod_start; + uint32_t cmdline_size = strlen((const char *)modules[i].cmdline) + 1; + + // Allocate needed memory, to copy both module and command line + uint32_t memory = (uint32_t)kmalloc(mod_size + cmdline_size); + + if (!memory) + return 0; + + // Copy module and its command line + memcpy((char *)memory, (char *)modules[i].mod_start, mod_size); + memcpy((char *)memory + mod_size, (char *)modules[i].cmdline, cmdline_size); + + // Change the module address to point to new allocated memory + modules[i].mod_start = memory; + modules[i].mod_end = modules[i].cmdline = memory + mod_size; + } + return 1; +} + +uintptr_t get_address_after_modules() +{ + // By default the first valid address is end. + uintptr_t address_after_modules = (uintptr_t)&_kernel_end; + for (int i = 0; i < MAX_MODULES; ++i) { + if (modules[i].mod_start != 0) + address_after_modules = modules[i].mod_end; + } + return address_after_modules; +} diff --git a/mentos/src/sys/shm.c b/mentos/src/sys/shm.c deleted file mode 100644 index a2c8d21..0000000 --- a/mentos/src/sys/shm.c +++ /dev/null @@ -1,362 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shm.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "shm.h" - -struct shmid_ds *head = NULL; -static ushort shm_descriptor = 0; - -int syscall_shmctl(int *args) -{ - int shmid = args[0]; - int cmd = args[1]; - - // TODO: for IPC_STAT - // struct shmid_ds * buf = (struct shmid_ds *) args[2]; - - struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); - - if (myshmid_ds == NULL) { - return -1; - } - - // Upgrade shm info. - myshmid_ds->shm_lpid = kernel_get_current_process()->pid; - myshmid_ds->shm_ctime = time(NULL); - - switch (cmd) { - case IPC_RMID: - if (myshmid_ds->shm_nattch == 0) { - kfree(myshmid_ds->shm_location); - - // Manage list. - if (myshmid_ds == head) { - head = head->next; - } else { - // Finding the previous shmid_ds. - struct shmid_ds *prev = head; - while (prev->next != myshmid_ds) { - prev = prev->next; - } - prev->next = myshmid_ds->next; - } - kfree(myshmid_ds); - } else { - (myshmid_ds->shm_perm).mode |= SHM_DEST; - } - - return 0; - - case IPC_STAT: - break; - case IPC_SET: - break; - case SHM_LOCK: - break; - case SHM_UNLOCK: - break; - default: - break; - } - - return -1; -} - -// Get shared memory segment. -int syscall_shmget(int *args) -{ - int flags = args[2]; - key_t key = (key_t)args[0]; - size_t size = (size_t)args[1]; - - struct shmid_ds *shmid_ds; - - if (flags & IPC_EXCL) { - return -1; - } - - if (flags & IPC_CREAT) { - shmid_ds = find_shm_fromkey(key); - - if (shmid_ds != NULL) { - return -1; - } - - shmid_ds = kmalloc(sizeof(struct shmid_ds)); - dbg_print("\n[SHM] shmget() shmid_ds : 0x%p", shmid_ds); - - shmid_ds->shm_location = kmalloc_align(size); - dbg_print("\n[SHM] shmget() Location : 0x%p", - shmid_ds->shm_location); - dbg_print("\n[SHM] shmget() physLocation : 0x%p", - paging_virtual_to_physical(get_current_page_directory(), - shmid_ds->shm_location)); - - shmid_ds->next = head; - head = shmid_ds; - - shmid_ds->shm_segsz = size; - shmid_ds->shm_atime = 0; - shmid_ds->shm_dtime = 0; - shmid_ds->shm_ctime = 0; - shmid_ds->shm_cpid = kernel_get_current_process()->pid; - shmid_ds->shm_lpid = kernel_get_current_process()->pid; - shmid_ds->shm_nattch = 0; - - // No user implementation. - (shmid_ds->shm_perm).cuid = 0; - // No group implementation. - (shmid_ds->shm_perm).cgid = 0; - // No user implementation - (shmid_ds->shm_perm).uid = 0; - // No group implementation. - (shmid_ds->shm_perm).gid = 0; - (shmid_ds->shm_perm).mode = flags & 0777; - (shmid_ds->shm_perm).seq = shm_descriptor++; - (shmid_ds->shm_perm).key = key; - } else { - shmid_ds = find_shm_fromkey(key); - dbg_print("\n[SHM] shmget() shmid_ds found : 0x%p", shmid_ds); - - if (shmid_ds == NULL) { - return -1; - } - - if ((flags & 0777) > ((shmid_ds->shm_perm).mode & 0777)) { - return -1; - } - shmid_ds->shm_lpid = kernel_get_current_process()->pid; - } - - return (shmid_ds->shm_perm).seq; -} - -// Attach shared memory segment. -void *syscall_shmat(int *args) -{ - int shmid = args[0]; - void *shmaddr = (void *)args[1]; - - // TODO: for more settings - // int flags = args[2]; - - struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); - dbg_print("\n[SHM] shmat() shmid_ds found : 0x%p", myshmid_ds); - - if (myshmid_ds == NULL) { - return (void *)-1; - } - - void *shm_start = myshmid_ds->shm_location; - - if (shmaddr == NULL) { - void *ret = kmalloc_align(myshmid_ds->shm_segsz); - - uint32_t shm_vaddr_start = (uint32_t)ret & 0xfffff000; - uint32_t shm_vaddr_end = - ((uint32_t)ret + myshmid_ds->shm_segsz) & 0xfffff000; - - uint32_t shm_paddr_start = (uint32_t)paging_virtual_to_physical( - get_current_page_directory(), shm_start); - - free_map_region(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_end, true); - - while (shm_vaddr_start <= shm_vaddr_end) { - paging_allocate_page(get_current_page_directory(), shm_vaddr_start, - shm_paddr_start / PAGE_SIZE, true, true); - shm_vaddr_start += PAGE_SIZE; - shm_paddr_start += PAGE_SIZE; - } - - dbg_print("\n[SHM] shmat() vaddr : 0x%p", ret); - dbg_print("\n[SHM] shmat() paddr : 0x%p", - (void *)shm_paddr_start); - dbg_print("\n[SHM] shmat() paddr after map: 0x%p", - paging_virtual_to_physical(get_current_page_directory(), - ret)); - - // Upgrade shm info. - myshmid_ds->shm_lpid = kernel_get_current_process()->pid; - (myshmid_ds->shm_nattch)++; - myshmid_ds->shm_atime = time(NULL); - - return ret; - } - - return (void *)-1; -} - -// Detach shared memory segment. -int syscall_shmdt(int *args) -{ - void *shmaddr = (void *)args[0]; - - if (shmaddr == NULL) { - return -1; - } - - struct shmid_ds *myshmid_ds = find_shm_fromvaddr(shmaddr); - dbg_print("\n[SHM] shmdt() shmid_ds found : 0x%p", myshmid_ds); - - if (myshmid_ds == NULL) { - return -1; - } - - // ===== Test ============================================================== - uint32_t shm_vaddr_start = (uint32_t)shmaddr & 0xfffff000; - uint32_t shm_vaddr_end = - ((uint32_t)shmaddr + myshmid_ds->shm_segsz) & 0xfffff000; - - free_map_region(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_end, false); - - while (shm_vaddr_start <= shm_vaddr_end) { - paging_allocate_page(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_start / PAGE_SIZE, true, true); - shm_vaddr_start += PAGE_SIZE; - } - // ========================================================================= - - kfree(shmaddr); - - // Upgrade shm info. - myshmid_ds->shm_lpid = kernel_get_current_process()->pid; - (myshmid_ds->shm_nattch)--; - myshmid_ds->shm_dtime = time(NULL); - - // Manage SHM_DEST flag on. - if (myshmid_ds->shm_nattch == 0 && (myshmid_ds->shm_perm).mode & SHM_DEST) { - kfree(myshmid_ds->shm_location); - - // Manage list. - if (myshmid_ds == head) { - head = head->next; - } else { - // Finding the previous shmid_ds. - struct shmid_ds *prev = head; - while (prev->next != myshmid_ds) { - prev = prev->next; - } - prev->next = myshmid_ds->next; - } - kfree(myshmid_ds); - } - - return 0; -} - -int shmctl(int shmid, int cmd, struct shmid_ds *buf) -{ - int error; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $6, %%eax\n" - "int $80\n" - : - : "r"(shmid), "r"(cmd), "r"(buf)); - __asm__("movl %%eax, %0\n\t" : "=r"(error)); - - return error; -} - -int shmget(key_t key, size_t size, int flags) -{ - int id; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $3, %%eax\n" - "int $80\n" - : - : "r"(key), "r"(size), "r"(flags)); - __asm__("movl %%eax, %0\n\t" : "=r"(id)); - - return id; -} - -void *shmat(int shmid, void *shmaddr, int flag) -{ - void *addr; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $4, %%eax\n" - "int $80\n" - : - : "r"(shmid), "r"(shmaddr), "r"(flag)); - // The kernel is serving my system call - - // Now I have the control - __asm__("movl %%eax, %0\n\t" : "=r"(addr)); - - return addr; -} - -int shmdt(void *shmaddr) -{ - int error; - - __asm__("movl %0, %%ecx\n" - "movl $5, %%eax\n" - "int $80\n" - : - : "r"(shmaddr)); - __asm__("movl %%eax, %0\n\t" : "=r"(error)); - - return error; -} - -struct shmid_ds *find_shm_fromid(int shmid) -{ - struct shmid_ds *res = head; - - while (res != NULL) { - if ((res->shm_perm).seq == shmid) { - return res; - } - res = res->next; - } - - return NULL; -} - -struct shmid_ds *find_shm_fromkey(key_t key) -{ - struct shmid_ds *res = head; - - while (res != NULL) { - if ((res->shm_perm).key == key) { - return res; - } - res = res->next; - } - - return NULL; -} - -struct shmid_ds *find_shm_fromvaddr(void *shmvaddr) -{ - void *shmpaddr = - paging_virtual_to_physical(get_current_page_directory(), shmvaddr); - void *paddr; - struct shmid_ds *res = head; - - while (res != NULL) { - paddr = paging_virtual_to_physical(get_current_page_directory(), - res->shm_location); - if (paddr == shmpaddr) { - return res; - } - res = res->next; - } - - return NULL; -} diff --git a/mentos/src/sys/unistd.c b/mentos/src/sys/unistd.c deleted file mode 100644 index fb76d9d..0000000 --- a/mentos/src/sys/unistd.c +++ /dev/null @@ -1,37 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file unistd.c -/// @brief Functions used to manage files. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" - -#include "string.h" -#include "limits.h" -#include "stdio.h" -#include "vfs.h" - -int rmdir(const char *path) -{ - char absolute_path[PATH_MAX]; - strcpy(absolute_path, path); - - if (path[0] != '/') { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) { - printf("rmdir: failed to remove '%s':" - "Cannot find mount-point\n", - path); - - return -1; - } - - if (mountpoint_list[mp_id].dir_op.rmdir_f == NULL) { - return -1; - } - - return mountpoint_list[mp_id].dir_op.rmdir_f(absolute_path); -} diff --git a/mentos/src/sys/utsname.c b/mentos/src/sys/utsname.c index c72a8d7..cd3e6eb 100644 --- a/mentos/src/sys/utsname.c +++ b/mentos/src/sys/utsname.c @@ -1,20 +1,54 @@ /// MentOS, The Mentoring Operating system project /// @file utsname.c /// @brief Functions used to provide information about the machine & OS. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "string.h" #include "utsname.h" #include "version.h" +#include "debug.h" +#include "errno.h" +#include "fcntl.h" +#include "vfs.h" -int uname(utsname_t *os_infos) +static inline int __gethostname(char *name, size_t len) { - // Uname code goes here. - strcpy(os_infos->sysname, OS_NAME); - strcpy(os_infos->version, OS_VERSION); - strcpy(os_infos->nodename, "testbed"); - strcpy(os_infos->machine, "i686"); - - return 0; + // Check if name is an invalid address. + if (!name) + return -EFAULT; + // Check if len is negative. + if (len < 0) + return -EINVAL; + // Open the file. + vfs_file_t *file = vfs_open("/etc/hostname", O_RDONLY, 0); + if (file == NULL) { + pr_err("Cannot find `/etc/hostname`.\n"); + return -ENOENT; + } + // Clear the buffer. + memset(name, 0, len); + // Read the content of the file. + ssize_t ret = vfs_read(file, name, 0UL, len); + if (ret < 0) { + pr_err("Failed to read `/etc/hostname`.\n"); + return ret; + } + // Close the file. + vfs_close(file); + return 0; +} + +int sys_uname(utsname_t *buf) +{ + if (buf == NULL) { + return -EFAULT; + } + // Uname code goes here. + strcpy(buf->sysname, OS_NAME); + strcpy(buf->version, OS_VERSION); + strcpy(buf->release, OS_VERSION); + __gethostname(buf->nodename, SYS_LEN); + strcpy(buf->machine, "i686"); + return 0; } diff --git a/mentos/src/system/errno.c b/mentos/src/system/errno.c index a48bdfc..c52a17f 100644 --- a/mentos/src/system/errno.c +++ b/mentos/src/system/errno.c @@ -1,22 +1,20 @@ /// MentOS, The Mentoring Operating system project /// @file errno.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "errno.h" #include "scheduler.h" +/// @brief Returns the error number for the current process. +/// @return Pointer to the error number. int *__geterrno() { - static int _errno = 0; - - task_struct *current_process = kernel_get_current_process(); - - if (current_process == NULL) - { + static int _errno = 0; + task_struct *current_process = scheduler_get_current_process(); + if (current_process == NULL) { return &_errno; } - return ¤t_process->error_no; } diff --git a/mentos/src/system/panic.c b/mentos/src/system/panic.c index 57e0030..c37b61e 100644 --- a/mentos/src/system/panic.c +++ b/mentos/src/system/panic.c @@ -1,18 +1,16 @@ /// MentOS, The Mentoring Operating system project /// @file panic.c /// @brief Functions used to manage kernel panic. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "panic.h" -#include "elf.h" -#include "stdio.h" -#include "kernel.h" #include "debug.h" void kernel_panic(const char *msg) { - dbg_print("\nPANIC:\n%s\n\nWelcome to Kernel Debugging Land...\n\n", msg); - dbg_print("\n"); - for (;;); + pr_emerg("\nPANIC:\n%s\n\nWelcome to Kernel Debugging Land...\n\n", msg); + pr_emerg("\n"); + asm("cli"); // Disable interrupts + for (;;) asm("hlt"); // Decrease power consumption with hlt } diff --git a/mentos/src/system/printk.c b/mentos/src/system/printk.c index a0a308b..9d64247 100644 --- a/mentos/src/system/printk.c +++ b/mentos/src/system/printk.c @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file printk.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Functions for managing the kernel messages. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "printk.h" @@ -9,15 +9,14 @@ #include "stdio.h" #include "video.h" -void printk(const char *format, ...) +int sys_syslog(const char *format, ...) { - char buffer[4096]; - va_list ap; - // Start variabile argument's list. - va_start(ap, format); - int len = vsprintf(buffer, format, ap); - va_end(ap); - - for (size_t i = 0; (i < len); ++i) - video_putc(buffer[i]); + char buffer[4096]; + va_list ap; + // Start variabile argument's list. + va_start(ap, format); + int len = vsprintf(buffer, format, ap); + va_end(ap); + video_puts(buffer); + return len; } diff --git a/mentos/src/system/signal.c b/mentos/src/system/signal.c new file mode 100644 index 0000000..17b8467 --- /dev/null +++ b/mentos/src/system/signal.c @@ -0,0 +1,798 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.c +/// @brief Signals definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[SIGNAL]" + +#include "signal.h" +#include "wait.h" +#include "scheduler.h" +#include "process.h" +#include "errno.h" +#include "assert.h" +#include "debug.h" +#include "string.h" +#include "irqflags.h" + +/// SLAB caches for signal bits. +static kmem_cache_t *sigqueue_cachep; + +/// Contains all stopped process waiting for a continue signal +static struct wait_queue_head_t stopped_queue; + +static const char *sys_siglist[] = { + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "USR1", + "USR2", + "CHLD", + "PWR", + "WINCH", + "URG", + "POLL", + "STOP", + "TSTP", + "CONT", + "TTIN", + "TTOU", + "VTALRM", + "PROF", + "XCPU", + "XFSZ", + NULL, +}; + +static inline void __copy_siginfo(siginfo_t *to, const siginfo_t *from) +{ + memcpy(to, from, sizeof(*to)); +} + +static inline void __clear_siginfo(siginfo_t *info) +{ + memset(info, 0, sizeof(*info)); +} + +static inline void __lock_task_sighand(struct task_struct *t) +{ + assert(t && "Null task struct."); + spinlock_lock(&t->sighand.siglock); +} + +static inline void __unlock_task_sighand(struct task_struct *t) +{ + assert(t && "Null task struct."); + spinlock_unlock(&t->sighand.siglock); +} + +static sighandler_t __get_handler(struct task_struct *t, int sig) +{ + assert(t && "Null task struct."); + return t->sighand.action[sig - 1].sa_handler; +} + +static int __sig_is_ignored(struct task_struct *t, int sig) +{ + // Blocked signals are never ignored, since the + // signal handler may change by the time it is + // unblocked. + if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig)) + return 0; + // Get the signal handler. + sighandler_t handler = __get_handler(t, sig); + // Check the type of the handler. + return (handler == SIG_IGN) && (sig != SIGCHLD); + // TODO: do_signal() specifically checks if the handler is IGN and the signal + // is SIGCHLD, in that case it forces a wait for the parent, that's why + // here I'm also accepting as not-ignored a SIG_IGN which is a SIGCHLD. +} + +/// @brief Allocate a new signal queue record. +/// @param t The task to which the signal belongs. +/// @param sig The signal to set. +/// @param flags Flags identifying from where we are going to take the memory. +static sigqueue_t *__sigqueue_alloc(struct task_struct *t, int sig, gfp_t flags) +{ + sigqueue_t *q = NULL; + if ((q = kmem_cache_alloc(sigqueue_cachep, flags)) == NULL) + return NULL; + // Initiliaze the values. + q->flags = 0; + list_head_init(&q->list); + return q; +} + +static void __sigqueue_free(sigqueue_t *q) +{ + if (q) + kmem_cache_free(q); +} + +/// @brief +/// @param sig Signal to be sent. +/// @param info The signal info +/// @param t The process to which we send the signal. +/// @return +static int __send_signal(int sig, siginfo_t *info, struct task_struct *t) +{ + // Lock the signal handling for the given task. + __lock_task_sighand(t); + pr_debug("Trying to add signal (%2d)`%s` to task (%2d)`%s`, currently pending `%d, %d`.\n", + sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]); + // Check if the signal is ignored. + if (__sig_is_ignored(t, sig)) { + pr_debug("Trying to send signal (%2d)`%s` to task (%2d)`%s`: ignored.\n", + sig, strsignal(sig), t->pid, t->name); + __unlock_task_sighand(t); + return 0; + } + // Check if the process is in an invalid status. + if ((t->state == EXIT_ZOMBIE) || (t->state == EXIT_DEAD)) { + pr_debug("Trying to send signal (%2d)`%s` to task (%2d)`%s`: zombie or dead.\n", + sig, strsignal(sig), t->pid, t->name); + __unlock_task_sighand(t); + return -EINVAL; + } + sigqueue_t *q = __sigqueue_alloc(t, sig, GFP_KERNEL); + if (q == NULL) { + __unlock_task_sighand(t); + return -EAGAIN; + } + list_head_add_tail(&q->list, &t->pending.list); + if (info != SEND_SIG_NOINFO) + memcpy(&q->info, info, sizeof(siginfo_t)); + // Set that there is a signal pending. + sigaddset(&t->pending.signal, sig); + pr_debug("Added pending signal (%2d)`%s` to task (%2d)`%s`, pending `%d, %d`.\n", + sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]); + __unlock_task_sighand(t); + return 0; +} + +static inline int __next_signal(sigpending_t *pending, sigset_t *mask) +{ + pr_debug("__next_signal(%p, %p)\n", pending, mask); + assert(pending && "Null `pending` structure."); + assert(mask && "Null `mask` structure."); + unsigned long x; + if ((x = bitmask_clear(pending->signal.sig[0], mask->sig[0])) != 0) + return 1 + find_first_non_zero(x); + if ((x = bitmask_clear(pending->signal.sig[1], mask->sig[1])) != 0) + return 33 + find_first_non_zero(x); + return 0; +} + +static inline void __collect_signal(int sig, sigpending_t *list, siginfo_t *info) +{ + pr_debug("__collect_signal(%d, %p, %p)\n", sig, list, info); + assert(list && "Null `list` structure."); + assert(info && "Null `info` structure."); + + sigqueue_t *queue_entry = NULL; + bool_t still_pending = false; + // Collect the siginfo appropriate to this signal. Check if + // there is another siginfo for the same signal. + list_for_each_decl(it, &list->list) + { + sigqueue_t *q = list_entry(it, sigqueue_t, list); + pr_debug("__collect_signal(%d, %p, %p) : Signal in queue : %p(%d : %s).\n", sig, list, info, + q, q->info.si_signo, strsignal(q->info.si_signo)); + if (q->info.si_signo == sig) { + // If the entry is already set, this means that there are several handlers + // pending for this particular signal. + if (queue_entry) { + pr_debug("__collect_signal(%d, %p, %p) : Still pending, do not remove from set.\n", sig, list, info); + still_pending = true; + break; + } + // Store the entry we encounter. + queue_entry = q; + } + } + // If there are no other signals pending of the same type, + // remove the signal from the set. + if (!still_pending) { + sigdelset(&list->signal, sig); + pr_debug("__collect_signal(%d, %p, %p) : Remove signal from set: %d.\n", sig, list, info, + list->signal.sig[0]); + } + // If we have found an entry. + if (queue_entry) { + pr_debug("__collect_signal(%d, %p, %p) : Remove and delete sigqueue entry : %p.\n", sig, list, info, queue_entry); + // Remove the entry from the queue. + list_head_del(&queue_entry->list); + // Copy the details about the entry inside the info structure. + __copy_siginfo(info, &queue_entry->info); + // Free the memory for the queue entry. + __sigqueue_free(queue_entry); + } else { + pr_debug("__collect_signal(%d, %p, %p) : Cannot find the signal in the queue.\n", sig, list, info); + // Ok, it wasn't in the queue, zero out the info. + __clear_siginfo(info); + // Get the current process. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Initialize the info. + info->si_signo = sig; + info->si_code = SI_USER; + info->si_value.sival_int = 0; + info->si_errno = 0; + info->si_pid = current->pid; + info->si_uid = current->uid; + info->si_addr = NULL; + info->si_status = 0; + info->si_band = 0; + } +} + +static inline int __dequeue_signal(sigpending_t *pending, sigset_t *mask, siginfo_t *info) +{ + pr_debug("__dequeue_signal(%p, %p, %p)\n", pending, mask, info); + // The dequeue_signal( ) always considers the lowest-numbered pending signal. + // It updates the data structures to indicate that the signal is no longer + // pending and returns its number. + int sig = __next_signal(pending, mask); + if ((sig > 0) && (sig < NSIG)) { + __collect_signal(sig, pending, info); + } + return sig; +} + +static inline int __handle_signal(int signr, siginfo_t *info, sigaction_t *ka, struct pt_regs *regs) +{ + pr_debug("__handle_signal(%d, %p, %p, %p)\n", signr, info, ka, regs); + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + errno = ESRCH; + return 0; + } + // Save the previous signal mask. + memcpy(¤t->saved_sigmask, ¤t->blocked, sizeof(sigset_t)); + + // Add the signal to the list of blocked signals. + sigaddset(¤t->blocked, signr); + + // Store the registers before setting the ones required by the signal handling. + current->thread.signal_regs = *regs; + + // Restore the registers for the process that has set the signal. + *regs = current->thread.regs; + + // Set the instruction pointer. + regs->eip = (uintptr_t)ka->sa_handler; + + // If the user is also asking for the signal info, push it into the stack. + if (bitmask_check(ka->sa_flags, SA_SIGINFO)) { + // Move the stack so that we have space for storing the siginfo. + regs->useresp -= sizeof(siginfo_t); + // Save the pointer where the siginfo is stored. + siginfo_t *siginfo_addr = (siginfo_t *)regs->useresp; + // We push on the stack the entire siginfo. + __copy_siginfo(siginfo_addr, info); + // We push on the stack the pointer to the siginfo we copied on the stack. + PUSH_ARG(regs->useresp, siginfo_t *, siginfo_addr); + } + + // Push on the stack the signal number, first and only argument of the handler. + PUSH_ARG(regs->useresp, int, signr); + + // Push on the stack the function required to handle the signal return. + PUSH_ARG(regs->useresp, uint32_t, current->sigreturn_eip); + + return 1; +} + +long sys_sigreturn(struct pt_regs *f) +{ + pr_debug("sys_sigreturn(%p)\n", f); + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Restore the registers before the signal handling. + *f = current->thread.signal_regs; + // Restore the previous signal mask. + memcpy(¤t->blocked, ¤t->saved_sigmask, sizeof(sigset_t)); + // Switch to process page directory + paging_switch_directory_va(current->mm->pgd); + pr_debug("sys_sigreturn(%p) : done!\n", f); + return 0; +} + +// Send signal to parent +static int __notify_parent(struct task_struct *current, int signr) +{ + siginfo_t info; + info.si_signo = signr; + info.si_code = SI_KERNEL; + info.si_value.sival_int = 0; + info.si_errno = 0; + info.si_pid = current->pid; + info.si_uid = current->uid; + info.si_addr = NULL; + info.si_status = 0; + info.si_band = 0; + return __send_signal(signr, &info, current->parent); +} + +// Removes from the pending signal queue q the pending signals corresponding to +// the bit mask mask +static void __rm_from_queue(sigset_t *mask, sigpending_t *q) +{ + list_head *it, *tmp; + list_for_each_safe (it, tmp, &q->list) { + struct sigqueue_t *entry = list_entry(it, struct sigqueue_t, list); + int sig = entry->info.si_signo; + + if (sigismember(mask, sig)) { + list_head_del(it); + kfree(entry); + } + } +} + +// We do not consider group stopping because for now we don't have thread groups +static void __do_signal_stop(struct task_struct *current, struct pt_regs *f, int signr) +{ + // The do_signal( ) function also sends a SIGCHLD signal to + // the parent process of current, unless the parent has set + // the SA_NOCLDSTOP flag of SIGCHLD. + if (!(SA_NOCLDSTOP & current->parent->sighand.action[SIGCHLD - 1].sa_flags)) + if (__notify_parent(current, SIGCHLD) != 0) + pr_debug("Failed to notify parent with signal: %d", signr); + + // The state is now TASK_UNINTERRUPTABLE + sleep_on(&stopped_queue); + + current->state = TASK_STOPPED; + current->exit_code = signr; + + scheduler_run(f); +} + +int do_signal(struct pt_regs *f) +{ + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + if (current == NULL) + return 0; + + // First, checks whether the function itself was triggered by an interrupt; + // if so, it simply returns. Otherwise, if the function was triggered by an + // exception that was raised while the process was running in User Mode, + // the function continues executing. + if ((f->cs & 3) != 3) + return 0; + + // Create a siginfo. + siginfo_t info; + // The return code of __dequeue_signal( ) is stored in signr. + int signr, exit_code; + + // Lock the signal handling for the given task. + __lock_task_sighand(current); + + // The heart of the do_signal( ) function consists of a loop that + // repeatedly invokes the __dequeue_signal( ) function until no + // non-blocked pending signals are left. + while (!list_head_empty(¤t->pending.list)) { + // Get the signal to deliver. + signr = exit_code = __dequeue_signal(¤t->pending, ¤t->blocked, &info); + + // Check the signal that we want to send. + if ((signr < 0) || (signr >= NSIG)) { + pr_err("Wrong signal number!\n"); + break; + } + + // If its value is 0, it means that all pending signals have been + // handled and do_signal( ) can finish. + if (signr == 0) { + pr_notice("There are no more signals to handle.\n"); + __unlock_task_sighand(current); + return 0; + } + + // Get the associated signal action. + sigaction_t *ka = ¤t->sighand.action[signr - 1]; + + // The only exception comes when the receiving process is init, in + // which case the signal is discarded. + if (current->pid == 1) + continue; + + // When a delivered signal is explicitly ignored, the do_signal( ) + // function normally just continues with a new execution of the loop + // and therefore considers another pending signal. + if (ka->sa_handler == SIG_IGN) { + if (signr == SIGCHLD) + while (sys_waitpid(-1, NULL, WNOHANG) > 0) {} + continue; + } + + // When a delivered signal is the default one, do_signal( ) must + // perform the default action of the signal. + if (ka->sa_handler == SIG_DFL) { + // For other processes, since the default action depends on the + // type of signal, the function executes a switch statement based + // on the value of signr. + switch (signr) { + // The signals whose default action is "ignore" are easily handled: + case SIGCONT: + case SIGCHLD: + case SIGURG: + case SIGWINCH: + continue; + // The signals whose default action is "stop" may stop the + // current process. To do this, do_signal( ) sets the state + // of current to TASK_STOPPED and then invokes the schedule( ) + // function (see Section 11.2.2). + // The difference between SIGSTOP and the other signals is: + // SIGSTOP always stops the process; + // The other signals stop the process only if it is not + // in an "orphaned process group." + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + if (is_orphaned_pgrp(current->gid)) + continue; + + case SIGSTOP: + __unlock_task_sighand(current); + __do_signal_stop(current, f, signr); + __lock_task_sighand(current); + + continue; + case SIGQUIT: + case SIGILL: + case SIGTRAP: + + case SIGABRT: + sys_exit(3); + + continue; + case SIGFPE: + case SIGSEGV: + case SIGBUS: + case SIGSYS: + case SIGXCPU: + case SIGXFSZ: +#if 0 + if (do_coredump(signr, f)) + exit_code |= 0x80; +#endif + default: +#if 0 + current->flags |= PF_SIGNALED; +#endif + sys_exit(exit_code); + __unlock_task_sighand(current); + return 1; + } + } + if (__handle_signal(signr, &info, ka, f) == 1) { + __unlock_task_sighand(current); + return 1; + } + pr_emerg("Failed to handle signal.\n"); + } + __unlock_task_sighand(current); + return 0; +} + +int signals_init() +{ + if ((sigqueue_cachep = KMEM_CREATE(sigqueue_t)) == NULL) { + pr_emerg("Failed to allocate cache for signals.\n"); + return 0; + } + + list_head_init(&stopped_queue.task_list); + return 1; +} + +/// @brief Checks for some types of signals that might nullify other pending +/// signals for the destination thread group +/// @param sig Signal number +/// @param info siginfo struct of the signal +/// @param p Target process of the signal +void handle_stop_signal(int sig, siginfo_t *info, struct task_struct *p) +{ + // remove the SIGCONT signal from the shared + // pending signal queue p->signal->shared_pending and from the private + // queues of all members of the thread group. + if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) { + // TODO: shared and thread group + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCONT); + + __rm_from_queue(&mask, &p->pending); + } + + // remove any SIGSTOP, SIGTSTP, SIGTTIN, and SIGTTOU signal from the shared pending signal queue p->signal->shared_pending; + // then, removes the same signals from the private pending signal queues of the processes belonging to the thread + // group, and awakens them + if (sig == SIGCONT) { + sigset_t mask; + sigemptyset(&mask); + + sigaddset(&mask, SIGSTOP); + sigaddset(&mask, SIGTSTP); + sigaddset(&mask, SIGTTIN); + sigaddset(&mask, SIGTTOU); + + __rm_from_queue(&mask, &p->pending); + + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, &stopped_queue.task_list) { + struct wait_queue_entry_t *entry = list_entry(it, struct wait_queue_entry_t, task_list); + + // Select only the waiting entry for the timer task pid + task_struct *task = entry->task; + if (task->pid == p->pid) { + // Executed entry's wakeup test function + int res = entry->func(entry, 0, 0); + if (res == 1) { + // Removes entry from list and memory + remove_wait_queue(&stopped_queue, entry); + kfree(entry); + + pr_debug("Process (pid: %d) restored from stop\n", p->pid); + } + break; + } + } + } +} + +/// @brief Send siginfo to target process +/// @param sig Signal number +/// @param info siginfo struct of the signal to be sent +/// @param p Target process where the signal will be sent +/// @return Returns 0 if there is no error, otherwise returns an error code +int __send_sig_info(int sig, siginfo_t *info, struct task_struct *p) +{ + if (sig < 0 || sig > NSIG) + return -EINVAL; + + // If the signal is being sent by a User Mode process, + // it checks whether the operation is allowed. + if (info->si_code == SI_USER) { + // TODO + } + + // If the sig parameter has the value 0, + // it returns immediately without generating any signal + if (!sig) + return 0; + + __lock_task_sighand(p); + + // Checks for some types of signals that might nullify other pending + // signals for the destination thread group + handle_stop_signal(sig, info, p); + +#if 0 + // Checks whether the signal is non-real-time and another occurrence of the same + // signal is already pending in the shared pending signal queue of the thread group + if (sig < 32 && sigismember(&p->signal->shared_pending.signal,sig)) + return 0; +#endif + + __unlock_task_sighand(p); + __send_signal(sig, info, p); + return 0; +} + +int sys_kill(pid_t pid, int sig) +{ + pr_debug("sys_kill(%d, %d)\n", pid, sig); + struct task_struct *current = scheduler_get_running_process(pid); + // Check the task associated with the pid. + if (!current) + return -ESRCH; + // Check the signal that we want to send. + if ((sig < 0) || (sig >= NSIG)) + return -EINVAL; + siginfo_t info; + info.si_signo = sig; + info.si_code = SI_USER; + info.si_value.sival_int = 0; + info.si_errno = 0; + info.si_pid = current->pid; + info.si_uid = current->uid; + info.si_addr = NULL; + info.si_status = 0; + info.si_band = 0; + return __send_sig_info(sig, &info, current); +} + +sighandler_t sys_signal(int signum, sighandler_t handler) +{ + pr_notice("sys_signal(%d, %p)\n", signum, handler); + // Check the signal that we want to send. + if ((signum < 0) || (signum >= NSIG)) { + pr_err("sys_signal(%d, %p): Wrong signal number!\n", signum, handler); + return SIG_ERR; + } + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + pr_err("sys_signal(%d, %p): Cannot signal number!\n", signum, handler); + return SIG_ERR; + } + // Create a new signal action. + sigaction_t new_sigaction; + // Set the handler. + new_sigaction.sa_handler = handler; + // Set the handler. + new_sigaction.sa_flags = SA_RESETHAND | SA_NODEFER; + // Reset the set for the signal action. + sigemptyset(&new_sigaction.sa_mask); + // Lock the signal handling for the given task. + __lock_task_sighand(current); + // Get the old sigaction. + sigaction_t *old_sigaction = ¤t->sighand.action[signum - 1]; + pr_err("sys_signal(%d, %p): Signal action ptr %p\n", signum, handler, old_sigaction); + pr_err("sys_signal(%d, %p): Old signal handler %p\n", signum, handler, old_sigaction->sa_handler); + // Get the old handler (to return). + sighandler_t old_handler = current->sighand.action[signum - 1].sa_handler; + // Set the new action. + memcpy(old_sigaction, &new_sigaction, sizeof(sigaction_t)); + // Unlock the signal handling for the given task. + __unlock_task_sighand(current); + // Return the old sighandler. + return old_handler; +} + +int sys_sigaction(int signum, const sigaction_t *act, sigaction_t *oldact) +{ + pr_debug("sys_sigaction(%d, %p, %p)\n", signum, act, oldact); + // Check the signal that we want to send. + if ((signum < 0) || (signum >= NSIG)) { + pr_debug("sys_sigaction(%d, %p, %p): Wrong signal number!\n", signum, act, oldact); + return -EINVAL; + } + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + pr_debug("sys_sigaction(%d, %p, %p): Cannot set signal for init!\n", signum, act, oldact); + return -EINVAL; + } + // Lock the signal handling for the given task. + __lock_task_sighand(current); + // Get a pointer to the entry in the sighand.action array. + sigaction_t *current_sigaction = ¤t->sighand.action[signum - 1]; + pr_debug("sys_sigaction(%d, %p, %p): : Signal old action ptr %p\n", signum, act, oldact, current_sigaction); + // If requested, get the old sigaction. + if (oldact) { + memcpy(oldact, current_sigaction, sizeof(sigaction_t)); + } + // Set the new action. + memcpy(current_sigaction, act, sizeof(sigaction_t)); + // Unlock the signal handling for the given task. + __unlock_task_sighand(current); + // Return the old sighandler. + return 0; +} + +int sys_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) +{ + pr_notice("sys_sigprocmask(%d, %p, %p)\n", how, set, oldset); + if (!set && !oldset) { + return -EFAULT; + } + if ((how < SIG_BLOCK) || (how > SIG_SETMASK)) { + return -EINVAL; + } + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + pr_notice("sys_sigprocmask(%d, %p, %p): Cannot set signal for init!\n", how, set, oldset); + return -EINVAL; + } + // If `oldset` is not, return the old set. + if (oldset) { + oldset->sig[0] = current->blocked.sig[0]; + oldset->sig[1] = current->blocked.sig[1]; + } + // Set the new signal mask. + if (set) { + if (how == SIG_BLOCK) { + // The set of blocked signals is the union of the current set + // and the set argument. + current->blocked.sig[0] |= set->sig[0]; + current->blocked.sig[1] |= set->sig[1]; + } else if (how == SIG_UNBLOCK) { + // The signals in set are removed from the current set of + // blocked signals. It is permissible to attempt to unblock + // a signal which is not blocked. + current->blocked.sig[0] &= ~(set->sig[0]); + current->blocked.sig[1] &= ~(set->sig[1]); + } else if (how == SIG_SETMASK) { + // The set of blocked signals is set to the argument set. + current->blocked.sig[0] = set->sig[0]; + current->blocked.sig[1] = set->sig[1]; + } + } + return 0; +} + +const char *strsignal(int sig) +{ + if ((sig >= SIGHUP) && (sig < NSIG)) + return sys_siglist[sig - 1]; + return NULL; +} + +int sigemptyset(sigset_t *set) +{ + if (set) { + set->sig[0] = 0; + return 0; + } + return -1; +} + +int sigfillset(sigset_t *set) +{ + if (set) { + set->sig[0] = ~0UL; + return 0; + } + return -1; +} + +int sigaddset(sigset_t *set, int signum) +{ + if (set && ((signum))) { + bit_set_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigdelset(sigset_t *set, int signum) +{ + if (set) { + bit_clear_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigismember(sigset_t *set, int signum) +{ + if (set) + return bit_check(set->sig[(signum - 1) / 32], (signum - 1) % 32); + return -1; +} diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index d794e52..c727119 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -1,100 +1,153 @@ /// MentOS, The Mentoring Operating system project /// @file syscall.c /// @brief System Call management functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "fpu.h" +#include "kheap.h" #include "syscall.h" -#include "shm.h" #include "isr.h" #include "errno.h" -#include "video.h" -#include "fcntl.h" #include "kernel.h" -#include "unistd.h" #include "process.h" -#include "process.h" -#include "irqflags.h" #include "scheduler.h" +#include "utsname.h" +#include "ioctl.h" +#include "timer.h" -/// @brief The signature of a function call. +#include "ipc/msg.h" +#include "ipc/sem.h" +#include "ipc/shm.h" + +/// The signature of a function call. typedef int (*SystemCall)(); -/// @brief The signature used to call the system call. -typedef uint32_t (*SystemCallFun)(uint32_t, ...); - -/// @brief The list of function call. +/// The list of function call. SystemCall sys_call_table[SYSCALL_NUMBER]; -// Linux provides a "not implemented" system call, sys_ni_syscall(), which does -// nothing except return ENOSYS, the error corresponding to an invalid -// system call. This function is used to "plug the hole" in the rare event that -// a syscall is removed or otherwise made unavailable. -int sys_ni_syscall() +/// Last interupt stack frame +static pt_regs *current_interrupt_stack_frame; + +/// @brief A Not Implemented (NI) system-call. +/// @return Always returns -ENOSYS. +/// @details +/// Linux provides a "not implemented" system call, sys_ni_syscall(), which does +/// nothing except return ENOSYS, the error corresponding to an invalid +/// system call. This function is used to "plug the hole" in the rare event that +/// a syscall is removed or otherwise made unavailable. +static inline int sys_ni_syscall() { - return ENOSYS; + return -ENOSYS; } void syscall_init() { - // Initialize the list of system calls. - for (uint32_t it = 0; it < SYSCALL_NUMBER; ++it) { - sys_call_table[it] = sys_ni_syscall; - } + // Initialize the list of system calls. + for (uint32_t it = 0; it < SYSCALL_NUMBER; ++it) { + sys_call_table[it] = sys_ni_syscall; + } - sys_call_table[__NR_exit] = (SystemCall)sys_exit; - sys_call_table[__NR_read] = (SystemCall)sys_read; - sys_call_table[__NR_write] = (SystemCall)sys_write; - sys_call_table[__NR_open] = (SystemCall)sys_open; - sys_call_table[__NR_close] = (SystemCall)sys_close; - sys_call_table[__NR_stat] = (SystemCall)sys_stat; - sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; - sys_call_table[__NR_readdir] = (SystemCall)sys_readdir; - sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; - sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; - sys_call_table[__NR_vfork] = (SystemCall)sys_vfork; - sys_call_table[__NR_execve] = (SystemCall)sys_execve; - sys_call_table[__NR_nice] = (SystemCall)sys_nice; - sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; - sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; - sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; - sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; - sys_call_table[__NR_brk] = (SystemCall)umalloc; // TODO: sys_brk - sys_call_table[__NR_free] = (SystemCall)ufree; // TODO: sys_brk + sys_call_table[__NR_exit] = (SystemCall)sys_exit; + sys_call_table[__NR_read] = (SystemCall)sys_read; + sys_call_table[__NR_write] = (SystemCall)sys_write; + sys_call_table[__NR_open] = (SystemCall)sys_open; + sys_call_table[__NR_close] = (SystemCall)sys_close; + sys_call_table[__NR_stat] = (SystemCall)sys_stat; + sys_call_table[__NR_fstat] = (SystemCall)sys_fstat; + sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; + sys_call_table[__NR_rmdir] = (SystemCall)sys_rmdir; + sys_call_table[__NR_unlink] = (SystemCall)sys_unlink; + sys_call_table[__NR_getdents] = (SystemCall)sys_getdents; + sys_call_table[__NR_lseek] = (SystemCall)sys_lseek; + sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; + sys_call_table[__NR_getsid] = (SystemCall)sys_getsid; + sys_call_table[__NR_setsid] = (SystemCall)sys_setsid; + sys_call_table[__NR_getgid] =(SystemCall)sys_getgid; + sys_call_table[__NR_setgid] =(SystemCall)sys_setgid; + sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; + sys_call_table[__NR_sigaction] = (SystemCall)sys_sigaction; + sys_call_table[__NR_fork] = (SystemCall)sys_fork; + sys_call_table[__NR_execve] = (SystemCall)sys_execve; + sys_call_table[__NR_nice] = (SystemCall)sys_nice; + sys_call_table[__NR_kill] = (SystemCall)sys_kill; + sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; + sys_call_table[__NR_uname] = (SystemCall)sys_uname; + sys_call_table[__NR_sigreturn] = (SystemCall)sys_sigreturn; + sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; + sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; + sys_call_table[__NR_fchdir] = (SystemCall)sys_fchdir; + sys_call_table[__NR_time] = (SystemCall)sys_time; + sys_call_table[__NR_sigprocmask] = (SystemCall)sys_sigprocmask; + sys_call_table[__NR_brk] = (SystemCall)sys_brk; + sys_call_table[__NR_signal] = (SystemCall)sys_signal; + sys_call_table[__NR_ioctl] = (SystemCall)sys_ioctl; + sys_call_table[__NR_sched_setparam] = (SystemCall)sys_sched_setparam; + sys_call_table[__NR_sched_getparam] = (SystemCall)sys_sched_getparam; + sys_call_table[__NR_nanosleep] = (SystemCall)sys_nanosleep; + sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; + sys_call_table[__NR_waitperiod] = (SystemCall)sys_waitperiod; + sys_call_table[__NR_msgctl] = (SystemCall)sys_msgctl; + sys_call_table[__NR_msgget] = (SystemCall)sys_msgget; + sys_call_table[__NR_msgrcv] = (SystemCall)sys_msgrcv; + sys_call_table[__NR_msgsnd] = (SystemCall)sys_msgsnd; + sys_call_table[__NR_semctl] = (SystemCall)sys_semctl; + sys_call_table[__NR_semget] = (SystemCall)sys_semget; + sys_call_table[__NR_semop] = (SystemCall)sys_semop; + sys_call_table[__NR_shmat] = (SystemCall)sys_shmat; + sys_call_table[__NR_shmctl] = (SystemCall)sys_shmctl; + sys_call_table[__NR_shmdt] = (SystemCall)sys_shmdt; + sys_call_table[__NR_shmget] = (SystemCall)sys_shmget; + sys_call_table[__NR_alarm] = (SystemCall)sys_alarm; + sys_call_table[__NR_setitimer] = (SystemCall)sys_setitimer; + sys_call_table[__NR_getitimer] = (SystemCall)sys_getitimer; - isr_install_handler(SYSTEM_CALL, &syscall_handler, "syscall_handler"); + isr_install_handler(SYSTEM_CALL, &syscall_handler, "syscall_handler"); +} + +pt_regs *get_current_interrupt_stack_frame() +{ + return current_interrupt_stack_frame; } void syscall_handler(pt_regs *f) { - // print_intrframe(f); + // Saves current interrupt stack frame + current_interrupt_stack_frame = f; - // The index of the requested system call. - uint32_t sc_index = f->eax; + // dbg_print_regs(f); + // Save current process fpu state. + switch_fpu(); - // The result of the system call. - int ret; - if (sc_index >= SYSCALL_NUMBER) { - ret = ENOSYS; - } else { - uintptr_t ptr = (uintptr_t)sys_call_table[sc_index]; + // The index of the requested system call. + uint32_t sc_index = f->eax; - SystemCallFun func = (SystemCallFun)ptr; + // The result of the system call. + int ret; + if (sc_index >= SYSCALL_NUMBER) { + ret = ENOSYS; + } else { + uintptr_t ptr = (uintptr_t)sys_call_table[sc_index]; - uint32_t arg0 = f->ebx; - uint32_t arg1 = f->ecx; - uint32_t arg2 = f->edx; - uint32_t arg3 = f->esi; - uint32_t arg4 = f->edi; - if ((sc_index == __NR_vfork) || (sc_index == __NR_clone)) { - arg0 = (uintptr_t)f; - } else if (sc_index == __NR_execve) { - arg0 = (uintptr_t)f; - } - ret = func(arg0, arg1, arg2, arg3, arg4); - } - f->eax = ret; + SystemCall func = (SystemCall)ptr; - // Schedule next process. - kernel_schedule(f); + uint32_t arg0 = f->ebx; + uint32_t arg1 = f->ecx; + uint32_t arg2 = f->edx; + uint32_t arg3 = f->esi; + uint32_t arg4 = f->edi; + if ((sc_index == __NR_fork) || + (sc_index == __NR_clone) || + (sc_index == __NR_execve) || + (sc_index == __NR_sigreturn)) { + arg0 = (uintptr_t)f; + } + ret = func(arg0, arg1, arg2, arg3, arg4); + } + f->eax = ret; + + // Schedule next process. + scheduler_run(f); + // Restore fpu state. + unswitch_fpu(); } diff --git a/mentos/src/ui/command/cmd_cd.c b/mentos/src/ui/command/cmd_cd.c deleted file mode 100644 index a38e5f9..0000000 --- a/mentos/src/ui/command/cmd_cd.c +++ /dev/null @@ -1,59 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_cd.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include "commands.h" -#include "vfs.h" -#include "stdio.h" -#include "dirent.h" -#include "string.h" -#include "libgen.h" - -void cmd_cd(int argc, char **argv) -{ - DIR *dirp = NULL; - char path[PATH_MAX]; - memset(path, 0, PATH_MAX); - - char current_path[PATH_MAX]; - getcwd(current_path, PATH_MAX); - - if (argc <= 1) { - strcpy(path, "/"); - } else if (argc > 2) { - printf("%s: too many arguments\n\n", argv[0]); - - return; - } else if (strncmp(argv[1], "..", 2) == 0) { - if (strcmp(current_path, dirname(current_path)) == 0) { - return; - } - strcpy(path, dirname(current_path)); - } else if (strncmp(argv[1], ".", 1) == 0) { - return; - } else { - // Copy the current path. - strcpy(path, current_path); - // Get the absolute path. - get_absolute_path(path); - // If the current directory is not the root, add a '/'. - if (strcmp(path, "/") != 0) { - strncat(path, "/", 1); - } - // Concatenate the input dir. - strncat(path, argv[1], strlen(argv[1])); - } - - dirp = opendir(path); - if (dirp == NULL) { - printf("%s: no such file or directory: %s\n\n", argv[0], argv[1]); - - return; - } - chdir(path); - closedir(dirp); -} diff --git a/mentos/src/ui/command/cmd_clear.c b/mentos/src/ui/command/cmd_clear.c deleted file mode 100644 index fa2060b..0000000 --- a/mentos/src/ui/command/cmd_clear.c +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_clear.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "video.h" - -void cmd_clear(int argc, char **argv) -{ - (void) argc; - (void) argv; - video_clear(); -} diff --git a/mentos/src/ui/command/cmd_cpuid.c b/mentos/src/ui/command/cmd_cpuid.c deleted file mode 100644 index fc9ab06..0000000 --- a/mentos/src/ui/command/cmd_cpuid.c +++ /dev/null @@ -1,118 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_cpuid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "string.h" -#include "cmd_cpuid.h" - -void cmd_cpuid(int argc, char **argv) -{ - (void)argc; - (void)argv; - - // List of features. - const char *ecx_features[ECX_FLAGS_SIZE] = { - "SSE3", - "Reserved", - "Reserved", - "Monitor/MWAIT", - "CPL Debug Store", - "Virtual Machine", - "Safer Mode", - "Enhanced Intel SpeedStep Technology", - "Thermal Monitor 2", - "SSSE3", - "L1 Context ID", - "Reserved", - "Reserved", - "CMPXCHG16B", - "xTPR Update Control", - "Perfmon and Debug Capability", - "Reserved", - "Reserved", - "DCA", - "SSE4.1", - "SSE4.2", - "Reserved", - "Reserved", - "POPCNT" - }; - - const char *edx_features[EDX_FLAGS_SIZE] = { - "x87 FPU", - "Virtual 8086 Mode", - "Debugging Extensions", - "Page Size Extensions", - "Time Stamp Counter", - "RDMSR and WRMSR", - "Physical Address Extensions", - "Machine Check Exception", - "CMPXCHG8B", - "APIC On-chip", - "Reserved", - "SYSENTER and SYSEXIT", - "Memory Type Range Registers", - "PTE Global Bit", - "Machine Check Architecture", - "Conditional Move Instructions", - "Page Attribute Table", - "36-bit Page Size", - "Processor Serial Number", - "Reserved", - "Debug Store", - "Thermal Monitor and Clock Facilities", - "Intel MMX", - "FXSAVE and FXRSTOR", - "SSE", - "SSE2", - "Self Snoop", - "Multi-Threading", - "TTC", - "Reserved", - "Pending Break Enable" - }; - - int i; - int verbose = 0; - - // Examine possible options. - if (argv[1] != NULL) { - if (strcmp(argv[1], "-v") == 0) { - verbose = 1; - } else { - printf("Unknown option %s\n", argv[1]); - printf("CPUID help message\n" - "-v : shows verbose CPUID information\n"); - - return; - } - } - - printf("----- CPU ID Information -----\n"); - if (strcmp(sinfo.brand_string, "Reserved") != 0) { - printf("%s\n", sinfo.brand_string); - } - printf("Vendor: %s\n", sinfo.cpu_vendor); - printf("Type: %s, Family: %x, Model: %x\n", sinfo.cpu_type, - sinfo.cpu_family, sinfo.cpu_model); - printf("Apic ID: %d\n", sinfo.apic_id); - - if (verbose == 1) { - printf("\n--- Supported features ---\n"); - for (i = 0; i < ECX_FLAGS_SIZE; i++) { - if (sinfo.cpuid_ecx_flags[i] == 1) { - printf("%s\n", ecx_features[i]); - } - } - for (i = 0; i < EDX_FLAGS_SIZE; i++) { - if (sinfo.cpuid_edx_flags[i] == 1) { - printf("%s\n", edx_features[i]); - } - } - printf("---------------------------\n"); - } -} diff --git a/mentos/src/ui/command/cmd_credits.c b/mentos/src/ui/command/cmd_credits.c deleted file mode 100644 index b6d7cd8..0000000 --- a/mentos/src/ui/command/cmd_credits.c +++ /dev/null @@ -1,28 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file credits.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "video.h" -#include "stdio.h" -#include "version.h" - -void cmd_credits(int argc, char **argv) -{ - (void) argc; - (void) argv; - video_set_color(BRIGHT_BLUE); - printf(OS_NAME" Credits\n\n"); - printf("Main Developers:\n"); - video_set_color(GREEN); - printf("Enrico Fraccaroli (Galfurian)\n"); - printf("Alessando Danese\n"); - video_set_color(BRIGHT_BLUE); - printf("Developers:\n"); - video_set_color(GREEN); - printf("Luigi C.\n" - "Mirco D.\n\n"); - video_set_color(WHITE); -} diff --git a/mentos/src/ui/command/cmd_date.c b/mentos/src/ui/command/cmd_date.c deleted file mode 100644 index 3c96565..0000000 --- a/mentos/src/ui/command/cmd_date.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_date.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "clock.h" -#include "irqflags.h" - -void cmd_date(int argc, char **argv) -{ - (void)argc; - (void)argv; - printf("It's %x:%x:%x of %s %02x %s %02x\n", get_hour(), - get_minute(), get_second(), get_day_lng(), - get_day_m(), get_month_lng(), 0x2000 + get_year()); -} diff --git a/mentos/src/ui/command/cmd_drv_load.c b/mentos/src/ui/command/cmd_drv_load.c deleted file mode 100644 index 685eef1..0000000 --- a/mentos/src/ui/command/cmd_drv_load.c +++ /dev/null @@ -1,53 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_drv_load.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "mouse.h" -#include "string.h" -#include "stdio.h" - -void cmd_drv_load(int argc, char **argv) -{ - if (argc < 2) { - printf( - "No driver inserted or bad usage! Type %s --help for the usage.\n", - argv[0]); - } else { - if ((_kstrncmp(argv[1], "-r", 2) == 0)) { - if ((argv[2] != NULL)) { - if (_kstrncmp(argv[2], "mouse", 5) == 0) { - printf("Disattivamento %s in corso..\n", argv[2]); - mouse_disable(); - } else - printf("FATAL: Driver %s not found.\n", argv[2]); - } else - printf("Warning, no driver name inserted!\n"); - } else if (_kstrncmp(argv[1], "mouse", 5) == 0) { - // Enabling mouse. - mouse_install(); - } else if ((_kstrncmp(argv[1], "--help", 6) == 0) || - (_kstrncmp(argv[1], "-h", 2) == 0)) { - printf("---------------------------------------------------\n" - "Driver tool to load and kill driver\n" - "Simple to use, just type:\n" - "\n" - "Usage: %s - driver_name\n" - "\t-> %s module_name - to load driver\n" - "\t-> %s -r module_name - to kill driver\n" - "---------------------------------------------------\n", - argv[0], argv[0], argv[0]); - } else { - if ((_kstrncmp(argv[1], "-r", 2) == 0) && - (_kstrncmp(argv[2], "mouse", 5) == -1)) { - printf("FATAL: Driver %s not found.\n", argv[2]); - } - - else { - printf("FATAL: Driver %s not found.\n", argv[1]); - } - } - } -} diff --git a/mentos/src/ui/command/cmd_echo.c b/mentos/src/ui/command/cmd_echo.c deleted file mode 100644 index d8967b5..0000000 --- a/mentos/src/ui/command/cmd_echo.c +++ /dev/null @@ -1,22 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file echo.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" - -void cmd_echo(int argc, char **argv) -{ - int i = argc; - int j = 0; - if (argc == 1) { - printf(""); - } else { - while (--i > 0) { - printf("%s ", argv[++j]); - } - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_ipcrm.c b/mentos/src/ui/command/cmd_ipcrm.c deleted file mode 100644 index 05856ca..0000000 --- a/mentos/src/ui/command/cmd_ipcrm.c +++ /dev/null @@ -1,50 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_ipcrm.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "stdio.h" -#include "shm.h" -#include "string.h" - -extern struct shmid_ds *head; - -void cmd_ipcrm(int argc, char **argv) -{ - if (argc != 2) { - printf("Bad arguments: you have to specify only IPC id, see ipcs.\n"); - - return; - } - - struct shmid_ds *shmid_ds = head; - struct shmid_ds *prev = NULL; - - while (shmid_ds != NULL) { - char strid[10]; - int_to_str(strid, (shmid_ds->shm_perm).seq, 10); - - if (strcmp(strid, argv[1]) == 0) { - break; - } - - prev = shmid_ds; - shmid_ds = shmid_ds->next; - } - - if (shmid_ds == NULL) { - printf("No shared memory find. \n"); - } else { - kfree(shmid_ds->shm_location); - - // shmid_ds = head. - if (prev == NULL) { - head = head->next; - } else { - prev->next = shmid_ds->next; - } - - kfree(shmid_ds); - } -} diff --git a/mentos/src/ui/command/cmd_ipcs.c b/mentos/src/ui/command/cmd_ipcs.c deleted file mode 100644 index 9f39744..0000000 --- a/mentos/src/ui/command/cmd_ipcs.c +++ /dev/null @@ -1,83 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_ipcs.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "shm.h" -#include "stdio.h" -#include "string.h" -#include "version.h" - -extern struct shmid_ds *head; - -static void print_sem_stat() -{ - printf("Semaphores: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - printf("%-20s %-10s %-20s \n\n", "", "Semaphores not implemented", ""); -} - -static void print_shm_stat() -{ - struct shmid_ds *shm_list = head; - - printf("Shared Memory: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - - while (shm_list != NULL) { - char mode[12]; - strmode((shm_list->shm_perm).mode, mode); - - printf("%-10s %-10i %-10i %-10s %-10s %-10s \n", "m", - (shm_list->shm_perm).seq, (shm_list->shm_perm).key, mode, "-", - "-"); - - shm_list = shm_list->next; - } - - printf("\n"); -} - -static void print_msg_stat() -{ - printf("Message Queues: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - printf("%-20s %-10s %-20s \n\n", "", "Message Queues not implemented", ""); -} - -void cmd_ipcs(int argc, char **argv) -{ - if (argc > 2) { - printf("Too much arguments.\n"); - - return; - } - - char datehour[100] = ""; - strdatehour(datehour); - - printf("IPC status from " OS_NAME " as of %s\n", datehour); - - if (argc == 2) { - if (strcmp(argv[1], "-s") == 0) { - print_sem_stat(); - } else if (strcmp(argv[1], "-m") == 0) { - print_shm_stat(); - } else if (strcmp(argv[1], "-q") == 0) { - print_msg_stat(); - } else { - printf("Option not recognize.\n"); - } - } else { - print_sem_stat(); - print_shm_stat(); - print_msg_stat(); - } - - return; -} diff --git a/mentos/src/ui/command/cmd_logo.c b/mentos/src/ui/command/cmd_logo.c deleted file mode 100644 index a1eb2e5..0000000 --- a/mentos/src/ui/command/cmd_logo.c +++ /dev/null @@ -1,38 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_logo.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "video.h" -#include "stdio.h" -#include "version.h" - -#define LOGO_TAB " " -void cmd_logo(int argc, char **argv) -{ - (void)argc; - (void)argv; - video_set_color(BRIGHT_GREEN); - printf(LOGO_TAB " __ __ _ ___ ____ \n"); - printf(LOGO_TAB "| \\/ | ___ _ __ | |_ / _ \\ / ___| \n"); - printf(LOGO_TAB "| |\\/| | / _ \\ | '_ \\ | __| | | | | \\___ \\ \n"); - printf(LOGO_TAB "| | | | | __/ | | | | | |_ | |_| | ___) |\n"); - printf(LOGO_TAB "|_| |_| \\___| |_| |_| \\__| \\___/ |____/ \n"); - video_set_color(BRIGHT_BLUE); - printf("\n"); - printf(LOGO_TAB " Welcome to "); - video_set_color(WHITE); - printf(OS_NAME "\n"); - video_set_color(BRIGHT_BLUE); - printf(LOGO_TAB " The "); - video_set_color(WHITE); - printf("Mentoring Operating System"); - video_set_color(BRIGHT_BLUE); - printf(" ver. "); - video_set_color(WHITE); - printf(OS_VERSION "\n"); - printf("\n"); - video_set_color(WHITE); -} diff --git a/mentos/src/ui/command/cmd_ls.c b/mentos/src/ui/command/cmd_ls.c deleted file mode 100644 index 8e10f1d..0000000 --- a/mentos/src/ui/command/cmd_ls.c +++ /dev/null @@ -1,104 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ls.c -/// @brief Command 'ls'. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include "commands.h" -#include "vfs.h" -#include "stdio.h" -#include "video.h" -#include "dirent.h" -#include "string.h" -#include "bitops.h" -#include "libgen.h" -#include "strerror.h" - -#define FLAG_L 1 - -static void print_ls(DIR *dirp, uint32_t flags) -{ - // If the directory is open. - if (dirp == NULL) { - return; - } - - size_t total_size = 0; - dirent_t *dirent = readdir(dirp); - while (dirent != NULL) { - if (dirent->d_type == FS_DIRECTORY) { - video_set_color(BRIGHT_CYAN); - } - if (dirent->d_type == FS_MOUNTPOINT) { - video_set_color(BRIGHT_GREEN); - } - if (has_flag(flags, FLAG_L)) { - stat_t entry_stat; - if (stat(dirent->d_name, &entry_stat) != -1) { - printf("%d %3d %3d %8d %s\n", dirent->d_type, entry_stat.st_uid, - entry_stat.st_gid, entry_stat.st_size, - basename(dirent->d_name)); - total_size += entry_stat.st_size; - } - } else { - printf("%s ", basename(dirent->d_name)); - } - video_set_color(WHITE); - dirent = readdir(dirp); - } - - closedir(dirp); - printf("\n"); - if (has_flag(flags, FLAG_L)) { - printf("Total: %d byte\n", total_size); - } - printf("\n"); -} - -void cmd_ls(int argc, char **argv) -{ - // Create a variable to store flags. - uint32_t flags = 0; - // Check the number of arguments. - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "--help") == 0) { - printf("List information about files inside a given directory.\n"); - printf("Usage:\n"); - printf(" ls [options] [directory]\n\n"); - - return; - } else if (strcmp(argv[i], "-l") == 0) { - set_flag(&flags, FLAG_L); - } - } - - bool_t no_directory = true; - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-l") == 0) { - continue; - } - - no_directory = false; - DIR *dirp = opendir(argv[i]); - if (dirp == NULL) { - printf("%s: cannot access '%s': %s\n\n", argv[0], argv[i], - "unknown" /*strerror(errno)*/); - - continue; - } - printf("%s:\n", argv[i]); - print_ls(dirp, flags); - } - if (no_directory) { - char cwd[PATH_MAX]; - getcwd(cwd, PATH_MAX); - DIR *dirp = opendir(cwd); - if (dirp == NULL) { - printf("%s: cannot access '%s': %s\n\n", argv[0], cwd, "unknown"); - } else { - print_ls(dirp, flags); - } - } -} diff --git a/mentos/src/ui/command/cmd_mkdir.c b/mentos/src/ui/command/cmd_mkdir.c deleted file mode 100644 index f1fca69..0000000 --- a/mentos/src/ui/command/cmd_mkdir.c +++ /dev/null @@ -1,29 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_mkdir.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stat.h" -#include "stdio.h" -#include "string.h" - -void cmd_mkdir(int argc, char **argv) -{ - // Check the number of arguments. - if (argc != 2) { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - if (strcmp(argv[1], "--help") == 0) { - printf("Creates a new directory.\n"); - printf("Usage:\n"); - printf(" %s \n", argv[0]); - - return; - } - mkdir(argv[1], 0); -} diff --git a/mentos/src/ui/command/cmd_more.c b/mentos/src/ui/command/cmd_more.c deleted file mode 100644 index 0cc3575..0000000 --- a/mentos/src/ui/command/cmd_more.c +++ /dev/null @@ -1,47 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_more.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "fcntl.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" - -void cmd_more(int argc, char **argv) -{ - if (argc != 2) { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) { - printf("Prints the content of the given file.\n"); - printf("Usage:\n"); - printf(" %s \n\n", argv[0]); - - return; - } - - int fd = open(argv[1], O_RDONLY, 42); - if (fd < 0) { - printf("%s: Cannot stat file '%s': %s\n\n", argv[0], argv[1], - "unknown" /*strerror(errno)*/); - - return; - } - - char c; - // Put on the standard output the characters. - while (read(fd, &c, 1) > 0) { - putchar(c); - } - putchar('\n'); - putchar('\n'); - close(fd); -} diff --git a/mentos/src/ui/command/cmd_newfile.c b/mentos/src/ui/command/cmd_newfile.c deleted file mode 100644 index 2ac8ef3..0000000 --- a/mentos/src/ui/command/cmd_newfile.c +++ /dev/null @@ -1,57 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_newfile.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "fcntl.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" -#include - -void cmd_newfile(int argc, char **argv) -{ - if (argc != 2) { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) { - printf("Makes a new file, and prompt for it's content.\n"); - printf("Usage:\n"); - printf(" %s \n", argv[0]); - - return; - } - - char text[256]; - printf("Filename: %s\n", argv[1]); - int fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0); - if (fd < 0) { - printf("%s: Cannot create file '%s': %s\n\n", argv[0], argv[1], - "unknown" /*strerror(errno)*/); - - return; - } - - printf("Type one line of text here (new line to complete):\n"); - scanf("%s", text); - if (write(fd, text, strlen(text)) == -1) { - printf("%s: Cannot write on file '%s': %s\n\n", argv[0], argv[1], - "unknown" /*strerror(errno)*/); - - return; - } - - if (close(fd) == -1) { - printf("%s: Cannot close file '%s': %s\n\n", argv[0], argv[1], - "unknown" /*strerror(errno)*/); - - return; - } -} diff --git a/mentos/src/ui/command/cmd_nice.c b/mentos/src/ui/command/cmd_nice.c deleted file mode 100644 index 497d366..0000000 --- a/mentos/src/ui/command/cmd_nice.c +++ /dev/null @@ -1,43 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_nice.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "unistd.h" -#include "string.h" - -void cmd_nice(int argc, char **argv) -{ - if (argc == 1) { - int current = nice(0); - printf("%d \n\n", current); - - return; - } - - if (argc != 2) { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (!strcmp(argv[1], "--help")) { - printf("Usage: %s \n\n", argv[0]); - - return; - } - - int increment = atoi(argv[1]); - if ((increment < -40) || (increment > 40)) { - printf("Error: You must provide a value between (-40,+40). \n\n", - increment); - - return; - } - int newNice = nice(increment); - printf("Your new nice value is %d.\n\n", newNice); -} diff --git a/mentos/src/ui/command/cmd_poweroff.c b/mentos/src/ui/command/cmd_poweroff.c deleted file mode 100644 index 4c9ea1d..0000000 --- a/mentos/src/ui/command/cmd_poweroff.c +++ /dev/null @@ -1,21 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file poweroff.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "reboot.h" -#include "unistd.h" - -void cmd_poweroff(int argc, char **argv) -{ - (void) argc; - (void) argv; - printf("Executing power-off...\n"); - reboot(LINUX_REBOOT_MAGIC1, - LINUX_REBOOT_MAGIC2, - LINUX_REBOOT_CMD_POWER_OFF, - NULL); -} diff --git a/mentos/src/ui/command/cmd_ps.c b/mentos/src/ui/command/cmd_ps.c deleted file mode 100644 index fd59ba8..0000000 --- a/mentos/src/ui/command/cmd_ps.c +++ /dev/null @@ -1,54 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_ps.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "list.h" -#include "stdio.h" -#include "string.h" -#include "bitops.h" -#include "scheduler.h" - -#define PS_OPT_F (1 << 1) - -void cmd_ps(int argc, char **argv) -{ - // Flag variable. - uint32_t flags = 0; - - // Check arguments. - for (int i = 0; i < argc; ++i) { - size_t optlen = strlen(argv[i]); - if (optlen == 0) { - continue; - } - - if (argv[i][0] != '-') { - continue; - } - - for (size_t j = 1; j < optlen; ++j) { - if (argv[i][j] == 'f') { - set_flag(&flags, PS_OPT_F); - } - } - } - - if (has_flag(flags, PS_OPT_F)) { - // Print the header. - printf("%-6s", "PID"); - printf("%-6s", "STATE"); - printf("%-50s", "COMMAND"); - printf("\n"); - // print_tree(task_structree->root, 0); - } else { - // Print the header. - printf("%-6s", "PID"); - printf("%-6s", "STATE"); - printf("%-50s", "COMMAND"); - printf("\n"); - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_pwd.c b/mentos/src/ui/command/cmd_pwd.c deleted file mode 100644 index 73a515e..0000000 --- a/mentos/src/ui/command/cmd_pwd.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_pwd.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include "commands.h" -#include "stdio.h" - -void cmd_pwd(int argc, char **argv) -{ - (void)argc; - (void)argv; - char cwd[PATH_MAX]; - getcwd(cwd, PATH_MAX); - printf("%s\n", cwd); -} diff --git a/mentos/src/ui/command/cmd_rm.c b/mentos/src/ui/command/cmd_rm.c deleted file mode 100644 index 4e80b27..0000000 --- a/mentos/src/ui/command/cmd_rm.c +++ /dev/null @@ -1,51 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_rm.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "fcntl.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" - -void cmd_rm(int argc, char **argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Remove (unlink) the FILE(s).\n"); - printf("Usage:\n"); - printf(" rm \n"); - - return; - } - - int fd = open(argv[1], O_RDONLY, 0); - if (fd < 0) - { - printf("%s: cannot remove '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); - - return; - } - - close(fd); - if (remove(argv[1]) != 0) - { - printf("rm: cannot remove '%s': Failed to remove file\n\n", - argv[1]); - - return; - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_rmdir.c b/mentos/src/ui/command/cmd_rmdir.c deleted file mode 100644 index e423560..0000000 --- a/mentos/src/ui/command/cmd_rmdir.c +++ /dev/null @@ -1,37 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_rmdir.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" - -void cmd_rmdir(int argc, char **argv) -{ - // Check the number of arguments. - if (argc != 2) { - printf("Bad usage.\n"); - printf("Try 'rmdir --help' for more information.\n"); - - return; - } - - if (strcmp(argv[1], "--help") == 0) { - printf("Removes a directory.\n"); - printf("Usage:\n"); - printf(" rmdir \n"); - - return; - } - - if (rmdir(argv[1]) != 0) { - printf("%s: failed to remove '%s': %s\n\n", argv[0], argv[1], - "unknown" /*strerror(errno)*/); - - return; - } -} diff --git a/mentos/src/ui/command/cmd_showpid.c b/mentos/src/ui/command/cmd_showpid.c deleted file mode 100644 index 30e2d87..0000000 --- a/mentos/src/ui/command/cmd_showpid.c +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_showpid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "unistd.h" -#include "scheduler.h" - -void cmd_showpid(int argc, char **argv) -{ - printf("pid %d\n", getpid()); -} diff --git a/mentos/src/ui/command/cmd_sleep.c b/mentos/src/ui/command/cmd_sleep.c deleted file mode 100644 index b588807..0000000 --- a/mentos/src/ui/command/cmd_sleep.c +++ /dev/null @@ -1,43 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file sleep.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "string.h" -#include "stdio.h" -#include "timer.h" -#include "clock.h" - -void cmd_sleep(int argc, char ** argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (!strcmp(argv[1], "--help")) - { - printf("Usage: %s \n\n", argv[0]); - - return; - } - - int seconds = atoi(argv[1]); - if (seconds <= 0) - { - printf("Error: You must provide a positive value (%d). \n\n", seconds); - - return; - } - - time_t t0 = get_hour() * 60 * 60 + get_minute() * 60 + get_second(); - printf("Start sleeping at '%d' for %ds...\n", t0, seconds); - sleep((time_t) seconds); - time_t t1 = get_hour() * 60 * 60 + get_minute() * 60 + get_second(); - printf("End sleeping at '%d' after %ds.\n", t1, t1 - t0); -} diff --git a/mentos/src/ui/command/cmd_tester.c b/mentos/src/ui/command/cmd_tester.c deleted file mode 100644 index 2921455..0000000 --- a/mentos/src/ui/command/cmd_tester.c +++ /dev/null @@ -1,353 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file testing.c -/// @brief Commands used to test OS functionalities. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "shm.h" -#include "stdio.h" -#include "timer.h" -#include "debug.h" -#include "shell.h" -#include "video.h" -#include "assert.h" -#include "stdlib.h" -#include "unistd.h" -#include "string.h" -#include "spinlock.h" - -//Function used to test vfork. -int task_test_function(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - printf("Hey there, here is %s with pid %d!\n", argv[0], getpid()); - sleep(12); - printf("Here is %s with pid %d, I'm leaving.\n", argv[0], getpid()); - - return 0; -} - -void try_process(int argc, char **argv) -{ - printf("I'm %d, testing task creation functions...\n", getpid()); - pid_t cpid = vfork(); - - if (cpid == 0) { - char *_cmd[] = { "task_test_function", (char *)NULL }; - char *_env[] = { (char *)NULL }; - execve((const char *)task_test_function, _cmd, _env); - assert(false && "I should not be here."); - } - printf("Executed process with pid %d...\n", cpid); -} - -void try_stress_heap(int argc, char **argv) -{ - uint32_t max_element = 1000; - if (argc >= 1) { - int val = atoi(argv[0]); - if (val > 0) { - max_element = (uint32_t)val; - } - } - - dbg_print("Starting allocation of matrix of %d...\n", max_element); - uint32_t **elements = malloc(max_element * sizeof(uint32_t *)); - dbg_print("Starting allocation of each vector...\n"); - for (uint32_t i = 0; i < max_element; ++i) { - elements[i] = malloc(100 * sizeof(uint32_t)); - (*elements[i]) = i; - } - - dbg_print("Starting de-allocation of each vector...\n"); - for (uint32_t i = 0; i < max_element; ++i) { - free(elements[i]); - } - free(elements); - dbg_print("Done\n"); -} - -// Share memory keys used in shm test. -#define SHMKEY1 81 -#define SHMKEY2 82 - -// Spinlock used in the following shm test. -static spinlock_t shmspinlock1; -static spinlock_t shmspinlock2; - -// @brief Function used to test the shm. -int shm_test_1(void *args) -{ - (void)args; - - printf("[T1] I am the first process to be executed.\n"); - - int shmid = shmget(SHMKEY1, 128 * sizeof(char), 0777); - if (shmid == -1) { - printf("[T1] Error: shmget() failed!\n"); - - return -1; - } - printf("[T1] I have got a share memory with ID %i.\n", shmid); - - char *myshm = shmat(shmid, NULL, 0); - if (myshm == (void *)-1) { - printf("[T1] Error: shmat() failed!\n"); - - return -1; - } - printf("[T1] I attached the share memory in my virtual address. \n"); - printf("[T1] SHM VIRTUAL ADDRESS %p\n", myshm); - printf("[T1] SHM PHYSICAL ADDRESS %p\n", - paging_virtual_to_physical(get_current_page_directory(), myshm)); - - printf("[T1] Writing something on share memory.\n"); - memcpy(myshm, "Bella questa Share Memory!", 27); - - int ret = shmdt(myshm); - if (ret == -1) { - printf("[T1] Error: shmdt() failed!\n"); - - return -1; - } - printf("[T1] Share memory detached\n"); - - printf("[T2] Passing the control to Task 2.\n"); - spinlock_unlock(&shmspinlock1); - - return 0; -} - -// Function used to test the shm. -int shm_test_2(void *args) -{ - (void)args; - printf("[T2] I'm waiting that T1 finishes...\n"); - spinlock_lock(&shmspinlock1); - printf("[T2] Now it's my turn!\n"); - - int shmid = shmget(SHMKEY1, 128 * sizeof(char), 0777); - if (shmid == -1) { - printf("[T2] Error: shmget() failed!\n"); - - return -1; - } - printf("[T2] I have got a share memory with ID %i.\n", shmid); - - char *myshm = shmat(shmid, NULL, 0); - if (myshm == (void *)-1) { - printf("[T2] Error: shmat() failed!\n"); - - return -1; - } - printf("[T2] I attached the share memory in my virtual address. \n"); - printf("[T2] SHM VIRTUAL ADDRESS %p\n", myshm); - printf("[T2] SHM PHYSICAL ADDRESS %p\n", - paging_virtual_to_physical(get_current_page_directory(), myshm)); - - printf("[T2] I'm going to see what's inside this share memory...\n"); - printf(" << "); - - char *c = myshm; - while (*c != '\0') { - printf("%c", *c++); - } - printf(" >>\n"); - - int ret = shmdt(myshm); - if (ret == -1) { - printf("[T2] Error: shmdt() failed!\n"); - - return -1; - } - printf("[T2] Share memory detached\n"); - - printf("[T2] Passing the control to my father\n"); - spinlock_unlock(&shmspinlock2); - - return 0; -} - -void try_shm(int argc, char **argv) -{ - printf("Testing shm functions...\n"); - // printf("[F] I am the father process.\n"); - // printf("[F] Creating shm: shmget()\n"); - // - // size_t size = 128 *sizeof(char); - // - // int shmid = shmget(SHMKEY1, size, IPC_CREAT | 0777); - // if (shmid == -1) - // { - // printf( - // "[F] Error: attempt to create a shared memory already created!\n"); - // - // return; - // } - // printf("[F] Share memory %i with ID %i and SIZE %i byte. \n", SHMKEY1, - // shmid, size); - // - // spinlock_init(&shmspinlock1); - // spinlock_init(&shmspinlock2); - // - // spinlock_lock(&shmspinlock1); - // spinlock_lock(&shmspinlock2); - // - // int process1_id = execvp(shm_test_1, - // "shm_test_1", - // "shm_test_1"); - // printf("[F] I have created Task 1 with pid: %d\n", process1_id); - // int process2_id = execvp(shm_test_2, - // "shm_test_2", - // "shm_test_2"); - // printf("[F] I have created Task 2 with pid: %d\n", process2_id); - // - // printf("[F] Now I have to wait child finished processing.\n"); - // spinlock_lock(&shmspinlock2); - // - // int ret = shmctl(shmid, IPC_RMID, NULL); - // if (ret == -1) - // { - // printf("[F] Error: shmctl() failed!\n"); - // return; - // } - // - // printf("[F] Share memory removed... Finished! :)\n"); -} - -void try_badshm() -{ - // Attempt to create a Shared Memory. - size_t size = sizeof(int); - mode_t mode = 0777; - - int shmid = shmget(SHMKEY2, size, IPC_CREAT | mode); - if (shmid == -1) { - printf("Error: attempt to create a shared memory already created!\n"); - - return; - } - - printf("I created a Shared Memory with: \n"); - printf(" -> KEY: %5i \n", SHMKEY2); - printf(" -> ID: %5i \n", shmid); - printf(" -> SIZE: %5i \n", size); - printf(" -> FLAGS: %5o \n", IPC_CREAT | mode); - - char mode_str[100]; - strmode(mode, mode_str); - printf(" -> PERMISSIONS: %5s \n", mode_str); - printf("but I don't want to free it! \n"); - printf("Other process/functions can get this share memory. \n"); - printf("Try ipcs. \n"); -} - -int run_to_2(void *args) -{ - (void)args; - for (int i = 1; i <= 2; i++) { - sleep(i); - } - - return 0; -} - -int run_to_3(void *args) -{ - (void)args; - (void)args; - - for (int i = 1; i <= 3; i++) { - sleep(i); - } - - return 0; -} - -void try_scheduler() -{ - unsigned int start = timer_get_ticks(); - // // Disable the IRQs. - // irq_disable(); - // struct task_struct * process1 = kernel_create_process(run_to_2, - // "run_to_2", - // "run_to_2"); - // printf("I have created Task 1 with pid: %d\n", process1->id); - // struct task_struct * process2 = kernel_create_process(run_to_3, - // "run_to_3", - // "run_to_3"); - // printf("I have created Task 2 with pid: %d\n", process2->id); - // // Re-Enable the IRQs. - // irq_enable(); - // wait(process1); - // wait(process2); - unsigned int end = timer_get_ticks(); - printf("Total time of execution: %d\n", end - start); -} - -// The maximum number of tests. -#define MAX_TEST 20 - -/// @brief Define testing functions. -struct { - /// The name of the test. - char cmd_testname[CMD_LEN]; - /// A description of the test. - char cmd_description[DESC_LEN]; - - /// A pointer to the function. - void (*func)(int, char **); -} _testing_functions[MAX_TEST] = { - { "try_process", "Test multiple processes creation", try_process }, - { "try_stress_heap", "Tries to stress the heap", try_stress_heap }, - { "try_shm", "Test shared memory", try_shm }, - { "try_badshm", "Test shared memory without free it", try_badshm }, - { "try_scheduler", "Test the performance of different schduler", - try_scheduler } -}; - -void cmd_tester(int argc, char **argv) -{ - if (argc <= 1) { - printf("Bad usage. Try '%s --help' for more info about the usage.\n", - argv[0]); - - return; - } - if (!strcmp(argv[1], "--help")) { - printf("Testing functions.. "); - video_set_color(RED); - printf("Warning: for developers only!\n"); - video_set_color(GREY); - for (size_t i = 0; i < MAX_TEST; i++) { - if (_testing_functions[i].func == NULL) { - break; - } - printf(" [%-2d] %-20s%-20s\n", i, - _testing_functions[i].cmd_testname, - _testing_functions[i].cmd_description); - } - video_set_color(WHITE); - - return; - } - int testid = atoi(argv[1]); - for (size_t i = 0; i < MAX_TEST; i++) { - if (testid != i) { - continue; - } - if (_testing_functions[i].func == NULL) { - break; - } - printf("Running test %d...\n", testid); - ++argv; - ++argv; - (_testing_functions[i].func)(argc - 2, argv); - printf("Done running test %d...\n", testid); - - return; - } - printf("Error: Test %s not found.\n", argv[1]); - printf(" You have to provide the test id.\n"); -} diff --git a/mentos/src/ui/command/cmd_touch.c b/mentos/src/ui/command/cmd_touch.c deleted file mode 100644 index 860b5aa..0000000 --- a/mentos/src/ui/command/cmd_touch.c +++ /dev/null @@ -1,43 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_touch.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "fcntl.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" - -void cmd_touch(int argc, char **argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Updates modification times of a given fine. If the does not" - "exists, it creates it.\n"); - printf("Usage:\n"); - printf(" touch \n"); - - return; - } - - int fd = open(argv[1], O_RDONLY, 0); - if (fd < 0) - { - fd = open(argv[1], O_CREAT, 0); - if (fd >= 0) - { - close(fd); - } - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_uname.c b/mentos/src/ui/command/cmd_uname.c deleted file mode 100644 index e0a7013..0000000 --- a/mentos/src/ui/command/cmd_uname.c +++ /dev/null @@ -1,97 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file uname.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "video.h" -#include "string.h" -#include "utsname.h" -#include "version.h" -#include "cmd_cpuid.h" - -void cmd_uname(int argc, char **argv) -{ - utsname_t utsname; - uname(&utsname); - if (argc != 2) { - printf("%s\n", utsname.sysname); - - return; - } - - if (!(strcmp(argv[1], "-a")) || !(strcmp(argv[1], "--all"))) { - printf("%s %s #1 CEST 2013 %s\n", utsname.sysname, utsname.version, - sinfo.cpu_vendor); - } else if (!(strcmp(argv[1], "-r")) || !(strcmp(argv[1], "--rev"))) { - printf("%s\n", utsname.version); - } else if (!(strcmp(argv[1], "-h")) || !(strcmp(argv[1], "--help"))) { - printf( - "Uname function allow you to see the kernel and system information.\n"); - printf("Function avaibles:\n"); - printf("1) -a - Kernel version and processor type\n" - "2) -r - Only the kernel version\n" - "3) -i - All info of system and kernel\n"); - } else if (!(strcmp(argv[1], "-i")) || !(strcmp(argv[1], "--info"))) { - printf("\n:==========: :System info: :==========:\n\n"); - printf("Version: %s\n", OS_VERSION); - printf("Major: %d\n", OS_MAJOR_VERSION); - printf("Minor: %d\n", OS_MINOR_VERSION); - printf("Micro: %d\n", OS_MICRO_VERSION); - - // CPU Info. - printf("\nCPU:"); - video_set_color(BRIGHT_RED); - video_move_cursor(61, video_get_line()); - printf(sinfo.cpu_vendor); - video_set_color(WHITE); - printf("\n"); - - // Memory RAM Info. - /* - * printf("Memory RAM: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d Kb\n", get_memsize()/1024); - * - * // Memory free RAM Info - * printf(LNG_FREERAM); - * video_move_cursor(60, video_get_line()); - * printf(" %d Kb\n", get_numpages()); - * - * printf("\n"); - * // Bitmap Info - * printf("Number bitmap's elements: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d", get_bmpelements()); - * video_move_cursor(60, video_get_line()); - */ - - // Mem_area Info. - /* - * printf("\nSize of mem_area: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d\n", sizeof(mem_area)); - */ - - // Page Dir Info. - /* - * printf("Page Dir Entry n.0 is: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d\n", get_pagedir_entry(0)); - */ - - // Page Table Info. - /* - * printf("Page Table Entry n.4 in Page dir 0 is: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d\n", get_pagetable_entry(0,4)); - */ - - printf("\n:==========: :===========: :==========:\n\n"); - } else { - printf("%s. For more info about this tool, please do 'uname --help'\n", - utsname.sysname); - } -} diff --git a/mentos/src/ui/command/cmd_whoami.c b/mentos/src/ui/command/cmd_whoami.c deleted file mode 100644 index 3be652f..0000000 --- a/mentos/src/ui/command/cmd_whoami.c +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_whoami.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" - -void cmd_whoami(int argc, char **argv) -{ - (void) argc; - (void) argv; - printf("%s\n", current_user.username); -} diff --git a/mentos/src/ui/init/init.c b/mentos/src/ui/init/init.c deleted file mode 100644 index d215ec0..0000000 --- a/mentos/src/ui/init/init.c +++ /dev/null @@ -1,40 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file init.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "init.h" -#include "wait.h" -#include "shell.h" -#include "errno.h" -#include "stdio.h" -#include "stdlib.h" -#include "unistd.h" -#include - -int main_init() -{ - pid_t cpid = vfork(); - - if (cpid == 0) - { - char *_argv[] = {"shell", "hello", (char *) NULL}; - char *_envp[] = {"/", (char *) NULL}; - - execve((const char *) shell, _argv, _envp); - - printf("This is bad, I should not be here! EXEC NOT WORKING\n"); - - return 1; - } - - int status; - while (true) - { - if ((cpid = wait(&status)) > 0) - dbg_print("Init has removed zombie children %d.\n", cpid); - } - - return 0; -} diff --git a/mentos/src/ui/shell/shell.c b/mentos/src/ui/shell/shell.c deleted file mode 100644 index 7a0d4c1..0000000 --- a/mentos/src/ui/shell/shell.c +++ /dev/null @@ -1,479 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell.c -/// @brief Implement shell functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "wait.h" -#include "video.h" -#include "types.h" -#include "stdio.h" -#include "debug.h" -#include "panic.h" -#include "stdlib.h" -#include "unistd.h" -#include "string.h" -#include "version.h" -#include "process.h" -#include "keyboard.h" -#include "commands.h" -#include "shell_login.h" - -#define HISTORY_MAX 10 - -/// The current user. -userenv_t current_user; - -/// The input command. -static char cmd[CMD_LEN]; - -/// The index of the cursor. -static uint32_t cmd_cursor_index; - -/// History of commands. -char history[HISTORY_MAX][CMD_LEN]; - -/// -static int history_write_index = 0; - -/// -static int history_read_index = 0; - -static bool_t history_full = false; - -#define MAX_NUM_COM 50 ///< Maximum number of saved commands. -struct { - /// The name of the command. - char cmdname[CMD_LEN]; - /// The function pointer to the command. - CommandFunction function; - /// The description of the command. - char cmddesc[DESC_LEN]; -} _shell_commands[] = { - { "logo", cmd_logo, "Show an ascii art logo" }, - { "clear", cmd_clear, "Clear the screen" }, - { "echo", cmd_echo, "Print some lines of text" }, - { "poweroff", cmd_poweroff, "Turn off the machine" }, - { "uname", cmd_uname, - "Print kernel version, try uname --help for more info" }, - { "credits", cmd_credits, "Show " OS_NAME " credits" }, - { "sleep", cmd_sleep, "Pause the OS for a particular number of seconds" }, - { "cpuid", cmd_cpuid, "Show cpu identification informations" }, - { "help", cmd_help, "See the 'help' list to learn commands now available" }, - { "ls", cmd_ls, "Tool for listing dir - not complete-" }, - { "cd", cmd_cd, "Change dir - not complete-" }, - { "mkdir", cmd_mkdir, "Creates a new directory." }, - { "rm", cmd_rm, "Removes a file." }, - { "rmdir", cmd_rmdir, "Removes a directory." }, - { "whoami", cmd_whoami, "Show the current user name" }, - { "pwd", cmd_pwd, "Print current working directory" }, - { "more", cmd_more, "Read content of a file" }, - { "touch", cmd_touch, "Create a new file" }, - { "newfile", cmd_newfile, "Create a new file" }, - { "ps", cmd_ps, "Show task list" }, - { "date", cmd_date, "Show date and time" }, - { "clear", cmd_clear, "Clears the screen" }, - { "showpid", cmd_showpid, "Shows the PID of the shell" }, - { "history", cmd_show_history, "Shows the shell history" }, - { "nice", cmd_nice, "Change the nice value of the process" } -}; - -/// @brief Completely delete the current command. -static void shell_command_clear() -{ - for (size_t it = 0; it < cmd_cursor_index; ++it) { - putchar('\b'); - } - cmd_cursor_index = 0; -} - -/// -/// @brief -/// @param _cmd -static void shell_command_set(char *_cmd) -{ - // Outputs the command. - printf(_cmd); - // Moves the cursore. - cmd_cursor_index += strlen(_cmd); - // Copies the command. - strcpy(cmd, _cmd); -} - -static void shell_command_erase_char() -{ - if (cmd_cursor_index > 0) { - cmd[--cmd_cursor_index] = '\0'; - } -} - -static bool_t shell_command_append_char(char c) -{ - if ((cmd_cursor_index + 1) < CMD_LEN) { - cmd[cmd_cursor_index++] = c; - cmd[cmd_cursor_index] = '\0'; - - return true; - } - - return false; -} - -static inline void history_debug_print() -{ -#if 1 - // Prints the history stack with current indexes values. - dbg_print("------------------------------\n"); - for (size_t index = 0; index < HISTORY_MAX; ++index) { - dbg_print("[%d]%c%c: %s\n", index, - (index == history_write_index) ? 'w' : ' ', - (index == history_read_index) ? 'r' : ' ', history[index]); - } -#endif -} - -/// @brief Push the command inside the history. -static void history_push(char *_cmd) -{ - // Reset the read index. - history_read_index = history_write_index; - // Check if it is a duplicated entry. - if (history_write_index > 0) { - if (strcmp(history[history_write_index - 1], _cmd) == 0) { - return; - } - } - // Insert the node. - strcpy(history[history_write_index], _cmd); - if (++history_write_index >= HISTORY_MAX) { - history_write_index = 0; - history_full = true; - } - // Reset the read index. - history_read_index = history_write_index; - history_debug_print(); -} - -/// @brief Give the key allows to navigate through the history. -static char *history_fetch(const int key) -{ - if ((history_write_index == 0) && (history_full == false)) { - return NULL; - } - // If the history is empty do nothing. - char *_cmd = NULL; - int next_index = history_read_index; - // Update the position inside the history. - if (key == KEY_DOWN) { - ++next_index; - } else if (key == KEY_UP) { - --next_index; - } - // Check the next index. - if (history_full) { - if (next_index < 0) { - next_index = HISTORY_MAX - 1; - } else if (next_index >= HISTORY_MAX) { - next_index = 0; - } - // Do no read where ne will have to write next. - if (next_index == history_write_index) { - next_index = history_read_index; - } - } else { - if (next_index < 0) { - next_index = 0; - } else if (next_index >= history_write_index) { - next_index = history_read_index; - } - } - history_read_index = next_index; - _cmd = history[history_read_index]; - history_debug_print(); - // Return the command. - return _cmd; -} - -void cmd_show_history(int argc, char **argv) -{ - (void)argc; - (void)argv; - // Prints the history stack with current indexes values - printf("------------------------------\n"); - printf(" Debug history \n"); - printf("------------------------------\n"); - for (size_t index = 0; index < HISTORY_MAX; ++index) { - printf("[%d]%c%c: %s\n", index, - (index == history_write_index) ? 'w' : ' ', - (index == history_read_index) ? 'r' : ' ', history[index]); - } -} - -/// @brief Prints the prompt. -static void shell_print_prompt() -{ - video_set_color(BRIGHT_BLUE); - printf(current_user.username); - video_set_color(WHITE); - char cwd[PATH_MAX]; - getcwd(cwd, PATH_MAX); - printf("~:%s# ", cwd); - // Update the lower-bounds for the video. - lower_bound_x = video_get_column(); - lower_bound_y = video_get_line(); -} - -/// @brief Gets the inserted command. -static void shell_get_command() -{ - // Re-Initialize the cursor index. - cmd_cursor_index = 0; - //Initializing the current command line buffer - memset(cmd, '\0', CMD_LEN); - do { - int c = getchar(); - // Return Key - if (c == '\n') { - if (strlen(cmd) == 0) { - printf("\n"); - } - // Break the while loop. - break; - } else if (c == '\033') { - getchar(); // Skip '[' - c = getchar(); // Get the char. - char *_cmd = NULL; - if ((c == KEY_UP) || (c == KEY_DOWN)) { - _cmd = history_fetch(c); - } - if (_cmd != NULL) { - // Clear the current command. - shell_command_clear(); - // Sets the command. - shell_command_set(_cmd); - } - } else if (keyboard_is_ctrl_pressed() && (c == 'c')) { - // However, the ISR of the keyboard has already put the char. - // Thus, delete it by using backspace. - putchar('\b'); - // Re-set the index to the beginning. - cmd_cursor_index = 0; - // Go to the new line. - printf("\n\n"); - // Sets the command. - shell_command_set("\0"); - - // Break the while loop. - break; - } else if (c == '\b') { - shell_command_erase_char(); - } else { - if (!shell_command_append_char(c)) { - putchar('\b'); - } - } - } while (cmd_cursor_index < CMD_LEN); - - // Cleans all blanks at the beginning of the command. - trim(cmd); -} - -/// @brief Gets the inserted command. -static CommandFunction shell_find_command(char *command) -{ - if (command == NULL) { - return NULL; - } - // Matching and executing the command. - for (size_t it = 0; it < MAX_NUM_COM; ++it) { - // Skip commands with undefined functions. - if (_shell_commands[it].function == NULL) { - continue; - } - if (strcmp(command, _shell_commands[it].cmdname) != 0) { - continue; - } - - return _shell_commands[it].function; - } - - return NULL; -} - -static inline bool_t shell_is_separator(char c) -{ - return ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || - (c == '\r')); -} - -static int shell_count_words(const char *sentence) -{ - int result = 0; - bool_t inword = false; - const char *it = sentence; - do - if (shell_is_separator(*it)) { - if (inword) { - inword = false; - result++; - } - } else { - inword = true; - } - while (*it++); - - return result; -} - -/// @brief Gets the options from the command. -/// @param command The executed command. -static void shell_get_options(char *command, int *argc, char ***argv) -{ - // Get the number of arguments, return if zero. - if (((*argc) = shell_count_words(command)) == 0) { - return; - } - (*argv) = (char **)malloc(sizeof(char *) * ((*argc) + 1)); - bool_t inword = false; - const char *cit = command; - size_t argcIt = 0, argIt = 0; - do { - if (shell_is_separator(*cit)) { - if (inword) { - inword = false; - (*argv)[argcIt++][argIt] = '\0'; - argIt = 0; - } - } else { - // Allocate string for argument. - if (!inword) { - (*argv)[argcIt] = (char *)malloc(sizeof(char) * CMD_LEN); - } - inword = true; - (*argv)[argcIt][argIt++] = (*cit); - } - } while (*cit++); - (*argv)[argcIt] = NULL; -} - -void cmd_help(int argc, char **argv) -{ - if (argc > 2) { - printf("Too many arguments.\n\n"); - return; - } - if (argc == 1) { - printf("Available commands:\n"); - for (int i = 0, j = 0; i < MAX_NUM_COM; ++i) { - if (_shell_commands[i].function != NULL) { - printf("%-10s ", _shell_commands[i].cmdname); - if ((j++) == 3) { - printf("\n"); - j = 0; - } - } - } - printf("\n\n"); - return; - } - if (argc == 2) { - for (int i = 0; i < MAX_NUM_COM; ++i) { - if (strcmp(_shell_commands[i].cmdname, argv[1]) == 0) { - printf("%s\n\n", _shell_commands[i].cmddesc); - - return; - } - } - printf("Cannot find command: '%s'\n\n", argv[1]); - } - printf("\n"); -} - -int shell(int argc, char **argv, char **envp) -{ - dbg_print("I'm shell, I am the knight here...\n"); - - video_set_color(BRIGHT_BLUE); - printf("\t\t.: Welcome to MentOS :.\n\n"); - video_set_color(WHITE); - - dbg_print("I'm shell, I'll let my pawn, login, handle any intruder...\n"); - shell_login(); - - chdir("/"); - current_user.uid = 1; - current_user.gid = 0; - - for (int i = 0; i < 50; ++i) { - putchar('\n'); - } - cmd_logo(1, NULL); - printf("\n\n\n\n"); - - dbg_print("I'm shell, let us begin...\n"); - - while (true) { - // First print the prompt. - shell_print_prompt(); - // Get the input command. - shell_get_command(); - // Check if the command is empty. - if (strlen(cmd) <= 0) { - continue; - } - // Retrieve the options from the command. - /// The current number of arguments. - int _argc = 1; - /// The vector of arguments. - char **_argv; - shell_get_options(cmd, &_argc, &_argv); - // Check if the command is empty. - if (_argc == 0) { - continue; - } - // Add the command to the history. - history_push(cmd); - // Find the command. - CommandFunction commandFunction = shell_find_command(_argv[0]); - if (commandFunction == NULL) { - printf("\nUnknown command: %s\n", _argv[0]); - } else if (strcmp(_argv[0], "cd") == 0) { - commandFunction(_argc, _argv); - } else { - int status; - pid_t cpid = vfork(); - if (cpid == 0) { - char *_envp[] = { (char *)NULL }; - execve((const char *)commandFunction, _argv, _envp); - kernel_panic("This is bad, I should not be here!\n"); - } - waitpid(cpid, &status, 0); - } - - // Free up the memory reserved for the arguments. - for (int it = 0; it < _argc; ++it) { - // Check if the argument is not empty. - if (_argv[it] != NULL) { - // Free up its memory. - free(_argv[it]); - } - } - free(_argv); - } - - return 0; -} - -void move_cursor_left(void) -{ - if (cmd_cursor_index > lower_bound_x) { - --cmd_cursor_index; - } -} - -void move_cursor_right(void) -{ - if (cmd_cursor_index < shell_lower_bound_x) { - ++cmd_cursor_index; - } -} diff --git a/mentos/src/ui/shell/shell_login.c b/mentos/src/ui/shell/shell_login.c deleted file mode 100644 index 7c1f124..0000000 --- a/mentos/src/ui/shell/shell_login.c +++ /dev/null @@ -1,152 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell_login.c -/// @brief Functions used to manage login. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "shell_login.h" -#include "vfs.h" -#include "stdio.h" -#include "fcntl.h" -#include "debug.h" -#include "video.h" -#include "string.h" -#include "keyboard.h" - -/// @brief Contains the credentials retrieved from the file. -typedef struct credentials_t { - /// The username. - char username[CREDENTIALS_LENGTH]; - /// The password. - char password[CREDENTIALS_LENGTH]; -} credentials_t; - -/// @brief Initialize the given credentials. -void init_credentials(credentials_t *credentials) -{ - if (credentials != NULL) { - memset(credentials->username, '\0', CREDENTIALS_LENGTH); - memset(credentials->password, '\0', CREDENTIALS_LENGTH); - } -} - -/// @brief -/// @param fd The FD of the file which contains the credentials. -/// @param credentials The structure which has to be filled with the -/// credentials. -/// @return If the credentials has been retrieved. -bool_t user_get(int fd, struct credentials_t *credentials) -{ - // Create a support array of char. - static char support[CREDENTIALS_LENGTH]; - // Cariable which will contain the number of bytes actually transferred. - ssize_t bytes_read = 0; - - // Get the USERNAME. - memset(support, '\0', CREDENTIALS_LENGTH); - for (int it = 0; it < CREDENTIALS_LENGTH; ++it) { - bytes_read = read(fd, &support[it], 1); - if (support[it] == ':') { - support[it] = '\0'; - - break; - } - } - replace_char(support, '\r', 0); - if ((bytes_read == 0) || (strlen(support) == 0)) { - return false; - } - strcpy(credentials->username, support); - - // Get the PASSWORD. - memset(support, '\0', CREDENTIALS_LENGTH); - for (int it = 0; it < CREDENTIALS_LENGTH; ++it) { - bytes_read = read(fd, &support[it], 1); - if ((support[it] == '\n') || (support[it] == EOF)) { - support[it] = '\0'; - - break; - } - } - - replace_char(support, '\r', 0); - if ((bytes_read == 0) || (strlen(support) == 0)) { - return false; - } - strcpy(credentials->password, support); - - return true; -} - -/// @brief Checks if the given credentials are correct. -bool_t check_credentials(credentials_t *credentials) -{ - // Initialize a variable for the return value. - bool_t status = false; - - /* Initialize the structure which will contain the username and the - * password. - */ - credentials_t existing; - init_credentials(&existing); - - // Open the file which contains the credentials. - // TODO: BUG: The first time the open is called, it fails. - int fd = open("/passwd", O_RDONLY, 0); - - // Check the file descriptor. - if (fd >= 0) { - // Get the next row inside the file containing the credentials. - while (user_get(fd, &existing) == true) { - if (!strcmp(credentials->username, existing.username) && - !strcmp(credentials->password, existing.password)) { - status = true; - - break; - } - } - // Close the file descriptor. - close(fd); - } else { - dbg_print("Can't open passwd file\n"); - } - return status; -} - -void shell_login() -{ - do { - // Initialize the credentials. - credentials_t credentials; - init_credentials(&credentials); - - // Ask the username. - printf("Username :"); - // Update the lower-bounds for the video. - lower_bound_x = video_get_column(); - lower_bound_y = video_get_line(); - // Get the username. - scanf("%50s", credentials.username); - - // Ask the password. - printf("Password :"); - // Update the lower-bounds for the video. - lower_bound_x = video_get_column(); - lower_bound_y = video_get_line(); - // Set the shadow option. - keyboard_set_shadow(true); - keyboard_set_shadow_character('*'); - // Get the password. - scanf("%50s", credentials.password); - // Disable the shadow option. - keyboard_set_shadow(false); - - // Check if the data are correct. - if (check_credentials(&credentials)) { - strcpy(current_user.username, credentials.username); - - break; - } - printf("Sorry, try again.\n"); - } while (true); -} diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt new file mode 100644 index 0000000..be81be8 --- /dev/null +++ b/programs/CMakeLists.txt @@ -0,0 +1,127 @@ +# ============================================================================= +# Author: Enrico Fraccaroli +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(programs C) + +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Set the directory where the compiled binaries will be placed. +set(MENTOS_BIN_DIR ${CMAKE_SOURCE_DIR}/files/bin) + +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -u_start") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -no-pie -Wl,-z,norelro") + +# ============================================================================= +# Add the executables (manually). +set(PROGRAMS + init.c + cpuid.c + clear.c + date.c + ls.c + logo.c + nice.c + poweroff.c + rm.c + shell.c + uname.c + cat.c + echo.c + login.c + mkdir.c + pwd.c + rmdir.c + showpid.c + touch.c + env.c + ps.c + kill.c + ipcrm.c + ipcs.c + sleep.c) +foreach(PROGRAM ${PROGRAMS}) + # Prepare the program name. + string(REPLACE ".c" "" PROGRAM_NAME ${PROGRAM}) + + # Set the name of the target. + set(TARGET_NAME prog_${PROGRAM_NAME}) + + # Log the entry. + message(VERBOSE "Program ${PROGRAM_NAME} (source: ${PROGRAM}, make target: ${TARGET_NAME})") + + # Randomize .text section address so when debugging symbols don't clash The allowed range is from 256MB to 2.75GB Minimum allowed address: 0x10000000 Max allowed address: + # 0xB0000000 + string(MD5 RAND_HASH ${PROGRAM}) + string(SUBSTRING ${RAND_HASH} 1 3 TEXADDR_INFIX) + string( + RANDOM + LENGTH 1 + ALPHABET 0123456789AB + RANDOM_SEED ${RAND_HASH} TEXADDR_FIRST) + + # Create the target. + add_executable(${TARGET_NAME} ${PROGRAM}) + + # Add the includes. + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/libc/inc) + + # Link the libc library. + target_link_libraries(${TARGET_NAME} ${CMAKE_BINARY_DIR}/libc/libc.a) + + # Add the dependency to libc. + add_dependencies(${TARGET_NAME} libc) + + # Add the linking properties. + set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "-Wl,-Ttext=0x${TEXADDR_FIRST}${TEXADDR_INFIX}0000") + + # Add the final target that strips the debugging symbols from the program. + add_custom_target( + ${PROGRAM_NAME} + BYPRODUCTS ${MENTOS_BIN_DIR}/${PROGRAM_NAME} + COMMAND mkdir -p ${MENTOS_BIN_DIR} + # COMMAND cp ${PROJECT_BINARY_DIR}/${TARGET_NAME} + # ${MENTOS_BIN_DIR}/${PROGRAM_NAME} + COMMAND strip --strip-debug ${PROJECT_BINARY_DIR}/${TARGET_NAME} -o ${MENTOS_BIN_DIR}/${PROGRAM_NAME} + DEPENDS ${TARGET_NAME}) + + # Append the program name to the list of all the executables. + list(APPEND ALL_EXECUTABLES ${PROGRAM_NAME}) +endforeach() + +# Add the overall target that builds all the programs. +add_custom_target(all_programs ALL DEPENDS ${ALL_EXECUTABLES}) diff --git a/programs/cat.c b/programs/cat.c new file mode 100644 index 0000000..862e4d6 --- /dev/null +++ b/programs/cat.c @@ -0,0 +1,40 @@ +/// MentOS, The Mentoring Operating system project +/// @file cat.c +/// @brief `cat` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Prints the content of the given file.\n"); + printf("Usage:\n"); + printf(" %s \n\n", argv[0]); + return 0; + } + int fd = open(argv[1], O_RDONLY, 42); + if (fd < 0) { + printf("%s: %s: %s\n\n", argv[0], strerror(errno), argv[1]); + return 1; + } + char buffer[BUFSIZ]; + // Put on the standard output the characters. + while (read(fd, buffer, BUFSIZ) > 0) { + puts(buffer); + } + putchar('\n'); + putchar('\n'); + close(fd); + return 0; +} diff --git a/programs/clear.c b/programs/clear.c new file mode 100644 index 0000000..910b13d --- /dev/null +++ b/programs/clear.c @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file clear.c +/// @brief `clear` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include + +int main(int argc, char **argv) +{ + puts("\033[J"); + return 0; +} diff --git a/programs/cpuid.c b/programs/cpuid.c new file mode 100644 index 0000000..2a5f6aa --- /dev/null +++ b/programs/cpuid.c @@ -0,0 +1,118 @@ +/// MentOS, The Mentoring Operating system project +/// @file cpuid.c +/// @brief `cpuid` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include + +int main(int argc, char **argv) +{ +#if 0 + (void)argc; + (void)argv; + + // List of features. + const char *ecx_features[ECX_FLAGS_SIZE] = { + "SSE3", + "Reserved", + "Reserved", + "Monitor/MWAIT", + "CPL Debug Store", + "Virtual Machine", + "Safer Mode", + "Enhanced Intel SpeedStep Technology", + "Thermal Monitor 2", + "SSSE3", + "L1 Context ID", + "Reserved", + "Reserved", + "CMPXCHG16B", + "xTPR Update Control", + "Perfmon and Debug Capability", + "Reserved", + "Reserved", + "DCA", + "SSE4.1", + "SSE4.2", + "Reserved", + "Reserved", + "POPCNT" + }; + + const char *edx_features[EDX_FLAGS_SIZE] = { + "x87 FPU", + "Virtual 8086 Mode", + "Debugging Extensions", + "Page Size Extensions", + "Time Stamp Counter", + "RDMSR and WRMSR", + "Physical Address Extensions", + "Machine Check Exception", + "CMPXCHG8B", + "APIC On-chip", + "Reserved", + "SYSENTER and SYSEXIT", + "Memory Type Range Registers", + "PTE Global Bit", + "Machine Check Architecture", + "Conditional Move Instructions", + "Page Attribute Table", + "36-bit Page Size", + "Processor Serial Number", + "Reserved", + "Debug Store", + "Thermal Monitor and Clock Facilities", + "Intel MMX", + "FXSAVE and FXRSTOR", + "SSE", + "SSE2", + "Self Snoop", + "Multi-Threading", + "TTC", + "Reserved", + "Pending Break Enable" + }; + + int i; + int verbose = 0; + + // Examine possible options. + if (argv[1] != NULL) { + if (strcmp(argv[1], "-v") == 0) { + verbose = 1; + } else { + printf("Unknown option %s\n", argv[1]); + printf("CPUID help message\n" + "-v : shows verbose CPUID information\n"); + + return; + } + } + + printf("----- CPU ID Information -----\n"); + if (strcmp(sinfo.brand_string, "Reserved") != 0) { + printf("%s\n", sinfo.brand_string); + } + printf("Vendor: %s\n", sinfo.cpu_vendor); + printf("Type: %s, Family: %x, Model: %x\n", sinfo.cpu_type, + sinfo.cpu_family, sinfo.cpu_model); + printf("Apic ID: %d\n", sinfo.apic_id); + + if (verbose == 1) { + printf("\n--- Supported features ---\n"); + for (i = 0; i < ECX_FLAGS_SIZE; i++) { + if (sinfo.cpuid_ecx_flags[i] == 1) { + printf("%s\n", ecx_features[i]); + } + } + for (i = 0; i < EDX_FLAGS_SIZE; i++) { + if (sinfo.cpuid_edx_flags[i] == 1) { + printf("%s\n", edx_features[i]); + } + } + printf("---------------------------\n"); + } +#endif + return 0; +} diff --git a/programs/date.c b/programs/date.c new file mode 100644 index 0000000..7368d58 --- /dev/null +++ b/programs/date.c @@ -0,0 +1,22 @@ +/// MentOS, The Mentoring Operating system project +/// @file date.c +/// @brief `date` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char** argv) +{ + time_t rawtime = time(NULL); + printf("Seconds since 01/01/1970 : %u\n", rawtime); + tm_t* timeinfo = localtime(&rawtime); + printf( + "It's %2d:%2d:%2d, %d weekday, %02d/%02d/%4d\n", + timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, + timeinfo->tm_wday, + timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year + ); + return 0; +} diff --git a/programs/echo.c b/programs/echo.c new file mode 100644 index 0000000..7d062fd --- /dev/null +++ b/programs/echo.c @@ -0,0 +1,117 @@ +/// MentOS, The Mentoring Operating system project +/// @file echo.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +#define ENV_NORM 1 +#define ENV_BRAK 2 +#define ENV_PROT 3 + +void expand_env(char *str, char *buf, size_t buf_len, int first_word, int last_word) +{ + // Buffer where we store the name of the variable. + char buffer[BUFSIZ] = { 0 }; + // Flags used to keep track of the special characters. + unsigned flags = 0; + // We keep track of where teh + char *env_start = NULL; + // Where we store the retrieved environmental variable value. + char *ENV = NULL; + // Get the length of the string. + size_t str_len = strlen(str); + // Position where we are writing on the buffer. + int b_pos = 0; + // Iterate the string. + for (int s_pos = 0; s_pos < str_len; ++s_pos) { + if (first_word && (s_pos == 0) && str[s_pos] == '"') + continue; + if (last_word && (s_pos == (str_len - 1)) && str[s_pos] == '"') + continue; + // If we find the backslash, we need to protect the next character. + if (str[s_pos] == '\\') { + if (bit_check(flags, ENV_PROT)) + buf[b_pos++] = '\\'; + else + bit_set_assign(flags, ENV_PROT); + continue; + } + // If we find the dollar, we need to catch the meaning. + if (str[s_pos] == '$') { + // If the previous character is a backslash, we just need to print the dollar. + if (bit_check(flags, ENV_PROT)) { + buf[b_pos++] = '$'; + } else if ((s_pos < (str_len - 2)) && ((str[s_pos + 1] == '{'))) { + // Toggle the open bracket method of accessing env variables. + bit_set_assign(flags, ENV_BRAK); + // We need to skip both the dollar and the open bracket `${`. + env_start = &str[s_pos + 2]; + } else { + // Toggle the normal method of accessing env variables. + bit_set_assign(flags, ENV_NORM); + // We need to skip the dollar `$`. + env_start = &str[s_pos + 1]; + } + continue; + } + if (bit_check(flags, ENV_BRAK)) { + if (str[s_pos] == '}') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_BRAK); + } + continue; + } + if (bit_check(flags, ENV_NORM)) { + if (str[s_pos] == ':') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Copy the `:`. + buf[b_pos++] = str[s_pos]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } + continue; + } + buf[b_pos++] = str[s_pos]; + } + if (bit_check(flags, ENV_NORM)) { + // Copy the environmental variable name. + strcpy(buffer, env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } +} + +int main(int argc, char **argv) +{ + char buffer[BUFSIZ]; + // Iterate all the words. + for (int i = 1; i < argc; ++i) { + memset(buffer, 0, BUFSIZ); + expand_env(argv[i], buffer, BUFSIZ, (i == 1), (i == (argc - 1))); + puts(buffer); + if (i < (argc - 1)) + putchar(' '); + } + printf("\n\n"); + return 0; +} diff --git a/programs/env.c b/programs/env.c new file mode 100644 index 0000000..8ebf935 --- /dev/null +++ b/programs/env.c @@ -0,0 +1,17 @@ +/// MentOS, The Mentoring Operating system project +/// @file env.c +/// @brief `env` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +extern char **environ; + +int main(int argc, char *argv[]) +{ + for (char **ep = environ; *ep; ++ep) + printf("%s\n", *ep); + return 0; +} diff --git a/programs/init.c b/programs/init.c new file mode 100644 index 0000000..45128a9 --- /dev/null +++ b/programs/init.c @@ -0,0 +1,28 @@ +/// MentOS, The Mentoring Operating system project +/// @file init.c +/// @brief `init` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[], char *envp[]) +{ + char *_argv[] = { "login", NULL }; + + if (fork() == 0) { + execv("/bin/login", _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + int status; +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" + while (1) { + wait(&status); + } +#pragma clang diagnostic pop + return 0; +} diff --git a/programs/ipcrm.c b/programs/ipcrm.c new file mode 100644 index 0000000..96aa5f2 --- /dev/null +++ b/programs/ipcrm.c @@ -0,0 +1,46 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipcrm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +int main(int argc, char **argv) +{ +#if 0 + if (argc != 2) { + printf("Bad arguments: you have to specify only IPC id, see ipcs.\n"); + + return; + } + + struct shmid_ds *shmid_ds = head; + struct shmid_ds *prev = NULL; + + while (shmid_ds != NULL) { + char strid[10]; + int_to_str(strid, (shmid_ds->shm_perm).seq, 10); + + if (strcmp(strid, argv[1]) == 0) { + break; + } + + prev = shmid_ds; + shmid_ds = shmid_ds->next; + } + + if (shmid_ds == NULL) { + printf("No shared memory find. \n"); + } else { + kfree(shmid_ds->shm_location); + + // shmid_ds = head. + if (prev == NULL) { + head = head->next; + } else { + prev->next = shmid_ds->next; + } + + kfree(shmid_ds); + } +#endif +} diff --git a/programs/ipcs.c b/programs/ipcs.c new file mode 100644 index 0000000..6e7d21f --- /dev/null +++ b/programs/ipcs.c @@ -0,0 +1,62 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipcs.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#if 0 +static inline void print_sem_stat() +{ + printf("Semaphores: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Semaphores not implemented", ""); +} + +static inline void print_msg_stat() +{ + printf("Message Queues: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Message Queues not implemented", ""); +} + +static inline void print_shm_stat() +{ + printf("Shared Memory: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Shared Memory not implemented", ""); +} +#endif + +int main(int argc, char **argv) +{ +#if 0 + if (argc > 2) { + printf("Too much arguments.\n"); + + return; + } + + char datehour[100] = ""; + strdatehour(datehour); + + printf("IPC status from " OS_NAME " as of %s\n", datehour); + + if (argc == 2) { + if (strcmp(argv[1], "-s") == 0) { + print_sem_stat(); + } else if (strcmp(argv[1], "-m") == 0) { + print_shm_stat(); + } else if (strcmp(argv[1], "-q") == 0) { + print_msg_stat(); + } else { + printf("Option not recognize.\n"); + } + } else { + print_sem_stat(); + print_shm_stat(); + print_msg_stat(); + } + + return; +#endif +} diff --git a/programs/kill.c b/programs/kill.c new file mode 100644 index 0000000..e75cf31 --- /dev/null +++ b/programs/kill.c @@ -0,0 +1,107 @@ +/// MentOS, The Mentoring Operating system project +/// @file kill.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +static inline int is_number(char *s) +{ + size_t len = strlen(s); + for (size_t i = 0; i < len; ++i) + if (!isdigit(s[i])) + return 0; + return 1; +} + +static inline void print_signal_list() +{ + for (int it = 1; it < (NSIG - 1); ++it) { + printf("%6s ", strsignal(it)); + if ((it % 8) == 0) + putchar('\n'); + } +} + +static inline int get_signal(char *s) +{ + int signr = 0; + if (is_number(s)) + signr = atoi(s); + else { + ++s; + for (int it = 1; it < (NSIG - 1); ++it) { + if (strcmp(s, strsignal(it)) == 0) { + signr = it; + break; + } + } + } + if ((signr <= 0) || (signr >= NSIG)) + signr = 0; + return signr; +} + +static inline int get_pid(char *s) +{ + int pid = atoi(s); + return ((pid > 0) && (pid < PID_MAX_LIMIT)) ? pid : 0; +} + +static inline int send_signal(int pid, int signr) +{ + int ret = kill(pid, signr); + if (ret == -1) { + printf("[%d] %5d failed sending signal %d (%s) : %s\n", + ret, pid, signr, strsignal(signr), strerror(errno)); + return 0; + } + printf("[%d] %5d sent signal %d (%s).\n", + ret, pid, signr, strsignal(signr)); + return 1; +} + +int main(int argc, char **argv) +{ + int pid, signr; + + if (argc == 1) { + printf("%s: not enough arguments.\n", argv[0]); + puts("Type kill -l for a list of signals\n"); + return 0; + } + if (argc == 2) { + if (strcmp(argv[1], "-l") == 0) { + print_signal_list(); + } else if (is_number(argv[1])) { + if ((pid = get_pid(argv[1]))) { + send_signal(pid, SIGTERM); + } else { + printf("%s: not a valid pid `%s`\n", argv[0], argv[1]); + return 1; + } + } else { + printf("%s: unrecognized option `%s`\n", argv[0], argv[1]); + puts("Type kill -l for a list of signals\n"); + return 1; + } + } else { + if (!(signr = get_signal(argv[1]))) { + printf("%s: unrecognized signal `%s`.\n", argv[0], argv[1]); + return 1; + } + + for (int i = 2; i < argc; ++i) + if ((pid = get_pid(argv[i]))) + send_signal(pid, signr); + } + putchar('\n'); + return 0; +} diff --git a/programs/login.c b/programs/login.c new file mode 100644 index 0000000..c1685e4 --- /dev/null +++ b/programs/login.c @@ -0,0 +1,193 @@ +///// MentOS, The Mentoring Operating system project +/// @file login.c +/// @brief Functions used to manage login. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Maximum length of credentials. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREDENTIALS_LENGTH 50 + +#define FG_BLACK "\033[30m" +#define FG_WHITE "\033[37m" +#define FG_RED "\033[31m" +#define BG_WHITE "\033[47m" +#define BG_BLACK "\033[40m" + +void set_echo(bool_t active) +{ + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + if (active) + _termios.c_lflag |= (ICANON | ECHO); + else + _termios.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, 0, &_termios); +} + +void set_erase(bool_t active) +{ + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + if (active) + _termios.c_lflag |= ECHOE; + else + _termios.c_lflag &= ~ECHOE; + tcsetattr(STDIN_FILENO, 0, &_termios); +} + +/// @brief Gets the inserted command. +static bool_t get_input(char *input, size_t max_len, bool_t hide) +{ + size_t index = 0; + int c; + bool_t result = false; + + set_erase(false); + if (hide) { + set_echo(false); + } + + memset(input, 0, max_len); + do { + c = getchar(); + // Return Key + if (c == '\n') { + input[index] = 0; + result = true; + break; + } else if (c == '\033') { + c = getchar(); + if (c == '[') { + c = getchar(); // Get the char, and ignore it. + } else if (c == '^') { + c = getchar(); // Get the char. + if (c == 'C') { + // However, the ISR of the keyboard has already put the char. + // Thus, delete it by using backspace. + if (!hide) { + putchar('\b'); + putchar('\b'); + putchar('\n'); + } + result = false; + break; + } else if (c == 'U') { + if (!hide) { + // However, the ISR of the keyboard has already put the char. + // Thus, delete it by using backspace. + putchar('\b'); + putchar('\b'); + // Clear the current command. + for (size_t it = 0; it < index; ++it) { + putchar('\b'); + } + } + index = 0; + } + } + } else if (c == '\b') { + if (index > 0) { + if (!hide) { + putchar('\b'); + } + --index; + } + } else { + input[index++] = c; + if (index == (max_len - 1)) { + input[index] = 0; + result = true; + break; + } + } + } while (index < max_len); + + set_erase(true); + if (hide) { + set_echo(true); + putchar('\n'); + } + + return result; +} + +static inline int setup_env(passwd_t *pwd) +{ + // Set the HOME. + char env_buffer[BUFSIZ]; + // Set the USER. + if (setenv("USER", pwd->pw_name, 0) == -1) { + printf( "Failed to set env: `USER`\n"); + return 0; + } + // Set the SHELL. + if (setenv("SHELL", pwd->pw_shell, 0) == -1) { + printf( "Failed to set env: `SHELL`\n"); + return 0; + } + sprintf(env_buffer, "/home/%s", pwd->pw_name); + if (setenv("HOME", env_buffer, 0) == -1) { + printf("Failed to set env: `HOME`\n"); + return 0; + } + return 1; +} + +int main(int argc, char **argv) +{ + passwd_t *pwd; + char username[50], password[50]; + do { + // Get the username. + do { + printf("Username :"); + } while (!get_input(username, 50, false)); + do { + printf("Password :"); + } while (!get_input(password, 50, true)); + if ((pwd = getpwnam(username)) == NULL) { + if (errno == ENOENT) { + printf("The given name was not found.\n"); + } else if (errno == 0) { + printf("Cannot access passwd file.\n"); + } else { + printf("Unknown error (%s).\n", strerror(errno)); + } + continue; + } + if (strcmp(pwd->pw_passwd, password) != 0) { + printf("Wrong password.\n"); + continue; + } + break; + } while (true); + if (pwd->pw_shell == NULL) { + printf("%s: There is no shell set for the user `%s`.\n", argv[0], pwd->pw_name); + return 1; + } + + if (!setup_env(pwd)) { + printf("%s: Failed to setup the environmental variables.\n", argv[0]); + return 1; + } + + char *_argv[] = { pwd->pw_shell, (char *)NULL }; + if (execv(pwd->pw_shell, _argv) == -1) { + printf("%s: Failed to execute the shell.\n", argv[0]); + printf("%s: %s.\n", argv[0], strerror(errno)); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/programs/logo.c b/programs/logo.c new file mode 100644 index 0000000..ff77725 --- /dev/null +++ b/programs/logo.c @@ -0,0 +1,18 @@ +/// MentOS, The Mentoring Operating system project +/// @file logo.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char **argv) +{ + printf(" __ __ _ ___ ____ \n"); + printf(" | \\/ | ___ _ __ | |_ / _ \\ / ___| \n"); + printf(" | |\\/| | / _ \\ | '_ \\ | __| | | | | \\___ \\ \n"); + printf(" | | | | | __/ | | | | | |_ | |_| | ___) |\n"); + printf(" |_| |_| \\___| |_| |_| \\__| \\___/ |____/ \n"); + return 0; +} diff --git a/programs/ls.c b/programs/ls.c new file mode 100644 index 0000000..0b233d3 --- /dev/null +++ b/programs/ls.c @@ -0,0 +1,152 @@ +/// MentOS, The Mentoring Operating system project +/// @file ls.c +/// @brief Command 'ls'. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLAG_L (1U << 0U) +#define FLAG_A (1U << 1U) + +#define FG_BRIGHT_GREEN "\033[92m" +#define FG_BRIGHT_CYAN "\033[96m" +#define FG_BRIGHT_WHITE "\033[97m" +#define FG_BRIGHT_YELLOW "\033[93m" + +static void print_ls(int fd, const char *path, unsigned int flags) +{ + char relative_path[PATH_MAX], hidden = 0; + dirent_t dent; + stat_t dstat; + size_t total_size = 0; + tm_t *timeinfo; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + // Check if the file starts with a dot (hidden), and we did not receive + // the `a` flag. + if ((dent.d_name[0] == '.') && !bitmask_check(flags, FLAG_A)) { + continue; + } + + // Prepare the relative path. + strcpy(relative_path, path); + if (strcmp(path, "/") != 0) + strcat(relative_path, "/"); + strcat(relative_path, dent.d_name); + + // Stat the file. + if (stat(relative_path, &dstat) == -1) { + continue; + } + + // Deal with the coloring. + if ((dent.d_type == DT_REG) && bitmask_check(dstat.st_mode, S_IXUSR)) { + puts(FG_BRIGHT_YELLOW); + } else if (dent.d_type == DT_DIR) { + puts(FG_BRIGHT_CYAN); + } else if (dent.d_type == DT_BLK) { + puts(FG_BRIGHT_GREEN); + } + + // Deal with the -l. + if (bitmask_check(flags, FLAG_L)) { + // Get the broken down time from the creation time of the file. + timeinfo = localtime(&dstat.st_ctime); + // Print the file type. + putchar(dt_char_array[dent.d_type]); + // Print the access permissions. + putchar(bitmask_check(dstat.st_mode, S_IRUSR) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWUSR) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXUSR) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IRGRP) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWGRP) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXGRP) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IROTH) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWOTH) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXOTH) ? 'x' : '-'); + // Add a space. + putchar(' '); + // Print the rest. + printf("%4d %4d %11s %2d/%2d %2d:%2d %s\n", + dstat.st_uid, + dstat.st_gid, + to_human_size(dstat.st_size), + timeinfo->tm_mon, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + dent.d_name); + total_size += dstat.st_size; + } else { + printf("%s ", dent.d_name); + } + + // Reset the color. + puts(FG_BRIGHT_WHITE); + } + printf("\n"); + if (bitmask_check(flags, FLAG_L)) { + printf("Total: %d byte\n", total_size); + } + printf("\n"); +} + +int main(int argc, char *argv[]) +{ + // Create a variable to store flags. + uint32_t flags = 0; + // Check the number of arguments. + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--help") == 0) { + printf("List information about files inside a given directory.\n"); + printf("Usage:\n"); + printf(" ls [options] [directory]\n\n"); + return 0; + } else if (argv[i][0] == '-') { + for (int j = 0; j < strlen(argv[i]); ++j) { + if (argv[i][j] == 'l') + bitmask_set_assign(flags, FLAG_L); + else if (argv[i][j] == 'a') + bitmask_set_assign(flags, FLAG_A); + } + } + } + + bool_t no_directory = true; + for (int i = 1; i < argc; ++i) { + if (argv[i][0] == '-') + continue; + no_directory = false; + int fd = open(argv[i], O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("%s: cannot access '%s': %s\n\n", argv[0], argv[i], strerror(errno)); + } else { + printf("%s:\n", argv[i]); + print_ls(fd, argv[i], flags); + close(fd); + } + } + if (no_directory) { + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + int fd = open(cwd, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("%s: cannot access '%s': %s\n\n", argv[0], cwd, strerror(errno)); + } else { + print_ls(fd, cwd, flags); + close(fd); + } + } + return 0; +} diff --git a/programs/mkdir.c b/programs/mkdir.c new file mode 100644 index 0000000..5f74eb3 --- /dev/null +++ b/programs/mkdir.c @@ -0,0 +1,30 @@ +/// MentOS, The Mentoring Operating system project +/// @file mkdir.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Check the number of arguments. + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Creates a new directory.\n"); + printf("Usage:\n"); + printf(" %s \n", argv[0]); + return 0; + } + if (mkdir(argv[1], 0)) { + printf("%s: cannot create directory '%s': %s\n", argv[0], argv[1], strerror(errno)); + } + return 0; +} diff --git a/programs/nice.c b/programs/nice.c new file mode 100644 index 0000000..63bc58b --- /dev/null +++ b/programs/nice.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file nice.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc == 1) { + int current = nice(0); + printf("%d \n\n", current); + return 0; + } + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (!strcmp(argv[1], "--help")) { + printf("Usage: %s \n\n", argv[0]); + return 0; + } + int increment = atoi(argv[1]); + if ((increment < -40) || (increment > 40)) { + printf("Error: You must provide a value between (-40,+40). \n\n", increment); + return 1; + } + int newNice = nice(increment); + printf("Your new nice value is %d.\n\n", newNice); + return 0; +} diff --git a/programs/poweroff.c b/programs/poweroff.c new file mode 100644 index 0000000..ef34fa4 --- /dev/null +++ b/programs/poweroff.c @@ -0,0 +1,20 @@ +/// MentOS, The Mentoring Operating system project +/// @file poweroff.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + printf("Executing power-off...\n"); + reboot( + LINUX_REBOOT_MAGIC1, + LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_POWER_OFF, + NULL); + return 0; +} diff --git a/programs/ps.c b/programs/ps.c new file mode 100644 index 0000000..4b90272 --- /dev/null +++ b/programs/ps.c @@ -0,0 +1,129 @@ +/// MentOS, The Mentoring Operating system project +/// @file ps.c +/// @brief Report a snapshot of the current processes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include + +#define FORMAT_S "%5s %5s %6s %s\n" +#define FORMAT "%5d %5d %6c %s\n" + +static inline void __iterate_proc_dirs(int fd) +{ + char absolute_path[PATH_MAX] = "/proc/"; + // Holds the file descriptor of the stat file. + int stat_fd; + // Buffer used to read the stat file. + char stat_buffer[BUFSIZ] = { 0 }; + // Holds the number of bytes read from stat file. + ssize_t ret; + // Variables used to read the stat file. + // (1) pid %d + // (2) comm %s + // (3) state %c + // (4) ppid %d + pid_t pid; + char comm[BUFSIZ] = { 0 }; + char state; + pid_t ppid; + // The directory entry. + dirent_t dent; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + // Skip non-directories. + if (dent.d_type != DT_DIR) + continue; + // Build the path to the stat file (i.e., `/proc//stat`). + strcpy(absolute_path + 6, dent.d_name); + strcat(absolute_path, "/stat"); + // Open the `/proc//stat` file. + if ((stat_fd = open(absolute_path, O_RDONLY, 0)) == -1) { + printf("Failed to open `%s`\n", absolute_path); + continue; + } + // Reset the stat buffer. + memset(stat_buffer, 0, BUFSIZ); + // Read the content of the stat file. + if ((ret = read(stat_fd, stat_buffer, BUFSIZ)) <= 0) { + printf("Cannot read `%s`\n", absolute_path); + close(stat_fd); + continue; + } + // Reset the comm buffer. + memset(comm, 0, BUFSIZ); + // Parse the content of the stat file. + sscanf(stat_buffer, "%d %s %c %d", &pid, comm, &state, &ppid); + // Print the stats concerning the process. + printf(FORMAT, pid, ppid, state, comm); + // Closing stat FD. + close(stat_fd); + } +} + +int main(int argc, char **argv) +{ + int fd = open("/proc", O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("ps: cannot access '/proc': %s\n\n", strerror(errno)); + } else { + printf(FORMAT_S, "PID", "PPID", "STATUS", "CMD"); + __iterate_proc_dirs(fd); + close(fd); + } + +#if 0 + + DIR *dir; + struct dirent_t *ent; + int i, fd_self, fd; + unsigned long time, stime; + char flag, *tty; + char cmd[256], tty_self[256], path[256], time_s[256]; + FILE *file; + + dir = opendir("/proc"); + fd_self = open("/proc/self/fd/0", O_RDONLY); + sprintf(tty_self, "%s", ttyname(fd_self)); + printf(FORMAT, "PID", "TTY", "TIME", "CMD"); + + while ((ent = readdir(dir)) != NULL) { + flag = 1; + for (i = 0; ent->d_name[i]; i++) + if (!isdigit(ent->d_name[i])) { + flag = 0; + break; + } + + if (flag) { + sprintf(path, "/proc/%s/fd/0", ent->d_name); + fd = open(path, O_RDONLY); + tty = ttyname(fd); + + if (tty && strcmp(tty, tty_self) == 0) { + sprintf(path, "/proc/%s/stat", ent->d_name); + file = fopen(path, "r"); + fscanf(file, "%d%s%c%c%c", &i, cmd, &flag, &flag, &flag); + cmd[strlen(cmd) - 1] = '\0'; + + for (i = 0; i < 11; i++) + fscanf(file, "%lu", &time); + fscanf(file, "%lu", &stime); + time = (int)((double)(time + stime) / sysconf(_SC_CLK_TCK)); + sprintf(time_s, "%02lu:%02lu:%02lu", + (time / 3600) % 3600, (time / 60) % 60, time % 60); + + printf(FORMAT, ent->d_name, tty + 5, time_s, cmd + 1); + fclose(file); + } + close(fd); + } + } + close(fd_self); +#endif + return 0; +} diff --git a/programs/pwd.c b/programs/pwd.c new file mode 100644 index 0000000..f57e020 --- /dev/null +++ b/programs/pwd.c @@ -0,0 +1,17 @@ +/// MentOS, The Mentoring Operating system project +/// @file pwd.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + printf("%s\n", cwd); + return 0; +} diff --git a/programs/rm.c b/programs/rm.c new file mode 100644 index 0000000..fe320b2 --- /dev/null +++ b/programs/rm.c @@ -0,0 +1,78 @@ +/// MentOS, The Mentoring Operating system project +/// @file rm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +bool_t has_option(int argc, char **argv, const char *first, ...) +{ + va_list ap; + const char *opt; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], first) == 0) + return true; + va_start(ap, first); + while ((opt = va_arg(ap, const char *)) != NULL) { + if (strcmp(argv[i], opt) == 0) + return true; + } + va_end(ap); + } + return false; +} + +int main(int argc, char **argv) +{ + if (argc <= 1) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Remove (unlink) the FILE(s).\n"); + printf("Usage:\n"); + printf(" rm \n"); + return 0; + } + if (strcmp(basename(argv[argc - 1]), "*") == 0) { + char directory[PATH_MAX], fullpath[PATH_MAX]; + int fd; + + if (strcmp(argv[argc - 1], "*") == 0) { + getcwd(directory, PATH_MAX); + } else { + strcpy(directory, dirname(argv[argc - 1])); + } + + if ((fd = open(directory, O_RDONLY | O_DIRECTORY, 0)) != -1) { + dirent_t dent; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + strcpy(fullpath, directory); + strcat(fullpath, dent.d_name); + if (dent.d_type == DT_REG) { + if (unlink(fullpath) == 0) { + if (lseek(fd, -1, SEEK_CUR) != -1) { + printf("Failed to move back the getdents...\n"); + } + } + } + } + close(fd); + } + } else { + if (unlink(argv[argc - 1]) < 0) { + printf("%s: cannot remove '%s': %s\n\n", argv[0], argv[argc - 1], strerror(errno)); + return 1; + } + } + printf("\n"); + return 0; +} diff --git a/programs/rmdir.c b/programs/rmdir.c new file mode 100644 index 0000000..3ff4665 --- /dev/null +++ b/programs/rmdir.c @@ -0,0 +1,31 @@ +/// MentOS, The Mentoring Operating system project +/// @file rmdir.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Check the number of arguments. + if (argc != 2) { + printf("Bad usage.\n"); + printf("Try 'rmdir --help' for more information.\n"); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Removes a directory.\n"); + printf("Usage:\n"); + printf(" rmdir \n"); + return 0; + } + if (rmdir(argv[1]) == -1) { + printf("%s: failed to remove '%s': %s\n\n", argv[0], argv[1], strerror(errno)); + return 1; + } + return 0; +} diff --git a/programs/shell.c b/programs/shell.c new file mode 100644 index 0000000..24c70ad --- /dev/null +++ b/programs/shell.c @@ -0,0 +1,772 @@ +/// MentOS, The Mentoring Operating system project +/// @file shell.c +/// @brief Implement shell functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include "stddef.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "strerror.h" +#include "termios.h" +#include "limits.h" +#include "sys/utsname.h" + +/// Maximum length of commands. +#define CMD_LEN 32 +/// Maximum lenght of the history. +#define HISTORY_MAX 10 + +/// @brief A set of colors. +#define FG_BLACK "\033[30m" +#define FG_RED "\033[31m" +#define FG_GREEN "\033[32m" +#define FG_YELLOW "\033[33m" +#define FG_BLUE "\033[34m" +#define FG_MAGENTA "\033[35m" +#define FG_CYAN "\033[36m" +#define FG_WHITE "\033[37m" +#define FG_BRIGHT_BLACK "\033[90m" +#define FG_BRIGHT_RED "\033[91m" +#define FG_BRIGHT_GREEN "\033[92m" +#define FG_BRIGHT_YELLOW "\033[93m" +#define FG_BRIGHT_BLUE "\033[94m" +#define FG_BRIGHT_MAGENTA "\033[95m" +#define FG_BRIGHT_CYAN "\033[96m" +#define FG_BRIGHT_WHITE "\033[97m" +#define BG_BLACK "\033[40m" +#define BG_RED "\033[41m" +#define BG_GREEN "\033[42m" +#define BG_YELLOW "\033[43m" +#define BG_BLUE "\033[44m" +#define BG_MAGENTA "\033[45m" +#define BG_CYAN "\033[46m" +#define BG_WHITE "\033[47m" +#define BG_BRIGHT_BLACK "\033[100m" +#define BG_BRIGHT_RED "\033[101m" +#define BG_BRIGHT_GREEN "\033[102m" +#define BG_BRIGHT_YELLOW "\033[103m" +#define BG_BRIGHT_BLUE "\033[104m" +#define BG_BRIGHT_MAGENTA "\033[105m" +#define BG_BRIGHT_CYAN "\033[106m" +#define BG_BRIGHT_WHITE "\033[107m" + +// Required by `export` +#define ENV_NORM 1 +#define ENV_BRAK 2 +#define ENV_PROT 3 + +// The input command. +static char cmd[CMD_LEN] = { 0 }; +// The index of the cursor. +static size_t cmd_cursor_index = 0; +// History of commands. +static char history[HISTORY_MAX][CMD_LEN] = { 0 }; +// The current write index inside the history. +static int history_write_index = 0; +// The current read index inside the history. +static int history_read_index = 0; +// Boolean used to check if the history is full. +static bool_t history_full = false; + +static inline int __is_separator(char c) +{ + return ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')); +} + +static inline int __count_words(const char *sentence) +{ + int result = 0; + bool_t inword = false; + const char *it = sentence; + do { + if (__is_separator(*it)) { + if (inword) { + inword = false; + result++; + } + } else { + inword = true; + } + } while (*it++); + return result; +} + +static inline void __set_echo(bool_t active) +{ + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + if (active) { + _termios.c_lflag |= (ICANON | ECHO); + } else { + _termios.c_lflag &= ~(ICANON | ECHO); + } + tcsetattr(STDIN_FILENO, 0, &_termios); +} + +static inline int __folder_contains( + const char *folder, + const char *entry, + int accepted_type, + dirent_t *result) +{ + int fd = open(folder, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) + return 0; + // Prepare the variables for the search. + dirent_t dent; + size_t entry_len = strlen(entry); + int found = 0; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + if (accepted_type && (accepted_type != dent.d_type)) + continue; + if (strncmp(entry, dent.d_name, entry_len) == 0) { + *result = dent; + found = 1; + break; + } + } + close(fd); + return found; +} + +static inline int __search_in_path(const char *entry, dirent_t *result) +{ + // Determine the search path. + char *PATH_VAR = getenv("PATH"); + if (PATH_VAR == NULL) + PATH_VAR = "/bin:/usr/bin"; + // Copy the path. + char *path = strdup(PATH_VAR); + // Iterate through the path entries. + char *token = strtok(path, ":"); + if (token == NULL) { + free(path); + return 0; + } + do { + if (__folder_contains(token, entry, DT_REG, result)) + return 1; + } while ((token = strtok(NULL, ":"))); + free(path); + return 0; +} + +/// @brief Prints the prompt. +static inline void __prompt_print() +{ + // Get the current working directory. + char CWD[PATH_MAX]; + getcwd(CWD, PATH_MAX); + // If the current working directory is equal to HOME, show ~. + char *HOME = getenv("HOME"); + if (HOME != NULL) + if (strcmp(CWD, HOME) == 0) + strcpy(CWD, "~\0"); + // Get the user. + char *USER = getenv("USER"); + if (USER == NULL) { + USER = "error"; + } + time_t rawtime = time(NULL); + tm_t *timeinfo = localtime(&rawtime); + // Get the hostname. + char *HOSTNAME; + utsname_t buffer; + if (uname(&buffer) < 0) { + HOSTNAME = "error"; + } else { + HOSTNAME = buffer.nodename; + } + printf(FG_GREEN "%s" FG_WHITE "@" FG_CYAN "%s " FG_BRIGHT_BLUE "[%02d:%02d:%02d]" FG_WHITE " [%s] " FG_BRIGHT_WHITE "%% ", + USER, HOSTNAME, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, CWD); +} + +static void __expand_env(char *str, char *buf, size_t buf_len) +{ + // Buffer where we store the name of the variable. + char buffer[BUFSIZ] = { 0 }; + // Flags used to keep track of the special characters. + unsigned flags = 0; + // We keep track of where teh + char *env_start = NULL; + // Where we store the retrieved environmental variable value. + char *ENV = NULL; + // Get the length of the string. + size_t str_len = strlen(str); + // Position where we are writing on the buffer. + int b_pos = 0; + // Iterate the string. + for (int s_pos = 0; s_pos < str_len; ++s_pos) { + if ((s_pos == 0) && str[s_pos] == '"') + continue; + if ((s_pos == (str_len - 1)) && str[s_pos] == '"') + continue; + // If we find the backslash, we need to protect the next character. + if (str[s_pos] == '\\') { + if (bit_check(flags, ENV_PROT)) + buf[b_pos++] = '\\'; + else + bit_set_assign(flags, ENV_PROT); + continue; + } + // If we find the dollar, we need to catch the meaning. + if (str[s_pos] == '$') { + // If the previous character is a backslash, we just need to print the dollar. + if (bit_check(flags, ENV_PROT)) { + buf[b_pos++] = '$'; + } else if ((s_pos < (str_len - 2)) && ((str[s_pos + 1] == '{'))) { + // Toggle the open bracket method of accessing env variables. + bit_set_assign(flags, ENV_BRAK); + // We need to skip both the dollar and the open bracket `${`. + env_start = &str[s_pos + 2]; + } else { + // Toggle the normal method of accessing env variables. + bit_set_assign(flags, ENV_NORM); + // We need to skip the dollar `$`. + env_start = &str[s_pos + 1]; + } + continue; + } + if (bit_check(flags, ENV_BRAK)) { + if (str[s_pos] == '}') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_BRAK); + } + continue; + } + if (bit_check(flags, ENV_NORM)) { + if (str[s_pos] == ':') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Copy the `:`. + buf[b_pos++] = str[s_pos]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } + continue; + } + buf[b_pos++] = str[s_pos]; + } + if (bit_check(flags, ENV_NORM)) { + // Copy the environmental variable name. + strcpy(buffer, env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } +} + +static int __export(int argc, char *argv[]) +{ + char name[BUFSIZ] = { 0 }, value[BUFSIZ] = { 0 }; + char *first, *last; + size_t name_len, value_start; + for (int i = 1; i < argc; ++i) { + // Get a pointer to the first and last occurrence of `=` inside the argument. + first = strchr(argv[i], '='), last = strrchr(argv[i], '='); + // Check validity of first and last, and check that they are the same. + if (!first || !last || (last < argv[i]) || (first != last)) + continue; + // Length of the name. + name_len = last - argv[i]; + // Set where the value starts. + value_start = (last + 1) - argv[i]; + // Copy the name. + strncpy(name, argv[i], name_len); + // Expand the environmental variables inside the argument. + __expand_env(&argv[i][value_start], value, BUFSIZ); + // Try to set the environmental variable. + if ((strlen(name) > 0) && (strlen(value) > 0)) { + if (setenv(name, value, 1) == -1) { + printf("Failed to set environmental variable.\n"); + return 1; + } + } + } + return 0; +} + +static int __cd(int argc, char *argv[]) +{ + if (argc > 2) { + printf("%s: too many arguments\n\n", argv[0]); + return 1; + } + const char *path = NULL; + if (argc == 2) { + path = argv[1]; + } else { + path = getenv("HOME"); + if (path == NULL) { + printf("cd: There is no home directory set.\n"); + return 1; + } + } + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("cd: %s: %s\n\n", argv[0], strerror(errno), path); + return 1; + } + // Set current working directory. + fchdir(fd); + close(fd); + // Get the updated working directory. + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + // Update the environmental variable. + if (setenv("PWD", cwd, 1) == -1) { + printf("cd: Failed to set current working directory.\n"); + return 1; + } + return 0; +} + +/// @brief Push the command inside the history. +static inline void __hst_push(char *_cmd) +{ + // Reset the read index. + history_read_index = history_write_index; + // Check if it is a duplicated entry. + if (history_write_index > 0) { + if (strcmp(history[history_write_index - 1], _cmd) == 0) { + return; + } + } + // Insert the node. + strcpy(history[history_write_index], _cmd); + if (++history_write_index >= HISTORY_MAX) { + history_write_index = 0; + history_full = true; + } + // Reset the read index. + history_read_index = history_write_index; +} + +/// @brief Give the key allows to navigate through the history. +static char *__hst_fetch(bool_t up) +{ + if ((history_write_index == 0) && (history_full == false)) { + return NULL; + } + // If the history is empty do nothing. + char *_cmd = NULL; + // Update the position inside the history. + int next_index = history_read_index + (up ? -1 : +1); + // Check the next index. + if (history_full) { + if (next_index < 0) { + next_index = HISTORY_MAX - 1; + } else if (next_index >= HISTORY_MAX) { + next_index = 0; + } + // Do no read where ne will have to write next. + if (next_index == history_write_index) { + next_index = history_read_index; + return NULL; + } + } else { + if (next_index < 0) { + next_index = 0; + } else if (next_index >= history_write_index) { + next_index = history_read_index; + return NULL; + } + } + history_read_index = next_index; + _cmd = history[history_read_index]; + // Return the command. + return _cmd; +} + +/// @brief Completely delete the current command. +static inline void __cmd_clr() +{ + // First we need to get back to the end of the line. + while (cmd[cmd_cursor_index] != 0) { + ++cmd_cursor_index; + puts("\033[1C"); + } + // Then we delete all the character. + for (size_t it = 0; it < cmd_cursor_index; ++it) { + putchar('\b'); + } + // Reset the index. + cmd_cursor_index = 0; +} + +/// @brief Sets the new command. +static inline void __cmd_set(char *_cmd) +{ + // Outputs the command. + printf(_cmd); + // Moves the cursore. + cmd_cursor_index += strlen(_cmd); + // Copies the command. + strcpy(cmd, _cmd); +} + +/// @brief Erases one character from the console. +static inline void __cmd_ers() +{ + if (cmd_cursor_index > 0) { + strcpy(cmd + cmd_cursor_index - 1, cmd + cmd_cursor_index); + putchar('\b'); + --cmd_cursor_index; + } +} + +/// @brief Appends the character `c` on the command. +static inline int __cmd_app(char c) +{ + if ((cmd_cursor_index + 1) < CMD_LEN) { + // If at the current index there is a character, shift the entire + // command ahead. + if (cmd[cmd_cursor_index] != 0) { + // Move forward the entire string. + for (unsigned long i = strlen(cmd); i > cmd_cursor_index; --i) { + cmd[i] = cmd[i - 1]; + } + } + // Place the new character. + cmd[cmd_cursor_index++] = c; + return 1; + } + return 0; +} + +static inline void __cmd_sug(dirent_t *suggestion, size_t starting_position) +{ + if (suggestion) { + for (size_t i = starting_position; i < strlen(suggestion->d_name); ++i) { + if (__cmd_app(suggestion->d_name[i])) { + putchar(suggestion->d_name[i]); + } + } + // If we suggested a directory, append a slash. + if (suggestion->d_type == DT_DIR) { + if (__cmd_app('/')) { + putchar('/'); + } + } + } +} + +/// @brief Gets the inserted command. +static void __cmd_get() +{ + // Re-Initialize the cursor index. + cmd_cursor_index = 0; + // Initializing the current command line buffer + memset(cmd, '\0', CMD_LEN); + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + __set_echo(false); + do { + int c = getchar(); + // Return Key + if (c == '\n') { + putchar('\n'); + // Break the while loop. + break; + } else if (c == '\033') { + c = getchar(); + if (c == '[') { + c = getchar(); // Get the char. + if ((c == 'A') || (c == 'B')) { + char *old_cmd = __hst_fetch(c == 'A'); + if (old_cmd != NULL) { + // Clear the current command. + __cmd_clr(); + // Sets the command. + __cmd_set(old_cmd); + } + } else if (c == 'D') { + if (cmd_cursor_index > 0) { + --cmd_cursor_index; + puts("\033[1D"); + } + } else if (c == 'C') { + if ((cmd_cursor_index + 1) < CMD_LEN && (cmd_cursor_index + 1) <= strlen(cmd)) { + ++cmd_cursor_index; + puts("\033[1C"); + } + } else if (c == 'H') { + // Move the cursor back to the beginning. + printf("\033[%dD", cmd_cursor_index); + // Reset the cursor position. + cmd_cursor_index = 0; + } else if (c == 'F') { + // Compute the offest to the end of the line, and move only if necessary. + size_t offset = strlen(cmd) - cmd_cursor_index; + if (offset > 0) { + printf("\033[%dC", offset); + // Reset the cursor position. + cmd_cursor_index += offset; + } + } + } else if (c == '^') { + c = getchar(); // Get the char. + if (c == 'C') { + // Re-set the index to the beginning. + cmd_cursor_index = 0; + // Go to the new line. + printf("\n\n"); + // Sets the command. + __cmd_set("\0"); + // Break the while loop. + break; + } else if (c == 'U') { + // Clear the current command. + __cmd_clr(); + // Re-set the index to the beginning. + cmd_cursor_index = 0; + // Sets the command. + __cmd_set("\0"); + } else if (c == 'D') { + // Go to the new line. + printf("\n"); + exit(0); + } + } + } else if (c == '\b') { + __cmd_ers(); + } else if (c == '\t') { + // Get the lenght of the command. + size_t cmd_len = strlen(cmd); + // Count the number of words. + int words = __count_words(cmd); + // If there are no words, skip. + if (words == 0) + continue; + // Determines if we are at the beginning of a new argument, last character is space. + if (__is_separator(cmd[cmd_len - 1])) + continue; + // If the last two characters are two dots `..` append a slash `/`, + // and continue. + if ((cmd_len >= 2) && ((cmd[cmd_len - 2] == '.') && (cmd[cmd_len - 1] == '.'))) { + if (__cmd_app('/')) { + putchar('/'); + continue; + } + } + // Determines if we are executing a command from current directory. + int is_run_cmd = (words == 1) && (cmd[0] == '.') && (cmd_len > 3) && (cmd[1] == '/'); + // Determines if we are entering an absolute path. + int is_abs_path = (words == 1) && (cmd[0] == '/'); + // Prepare the dirent variable. + dirent_t dent; + // If there is only one word, we are searching for a command. + if (is_run_cmd) { + if (__folder_contains(cwd, cmd + 2, 0, &dent)) + __cmd_sug(&dent, cmd_len - 2); + } else if (is_abs_path) { + const char *_dirname = dirname(cmd), *_basename = basename(cmd); + if (!_dirname || !_basename) + continue; + if ((*_dirname == 0) || (*_basename == 0)) + continue; + if (__folder_contains(_dirname, _basename, 0, &dent)) + __cmd_sug(&dent, strlen(_basename)); + } else if (words == 1) { + if (__search_in_path(cmd, &dent)) + __cmd_sug(&dent, cmd_len); + } else { + // Search the last occurrence of a space, from there on + // we will have the last argument. + char *last_argument = strrchr(cmd, ' '); + // We need to move ahead of one character if we found the space. + last_argument = last_argument ? last_argument + 1 : NULL; + // If there is no last argument. + if (last_argument == NULL) + continue; + const char *_dirname = dirname(last_argument), *_basename = basename(last_argument); + if (!_dirname || !_basename) + continue; + if ((*_dirname != 0) && (*_basename != 0)) { + if (__folder_contains(_dirname, _basename, 0, &dent)) + __cmd_sug(&dent, strlen(_basename)); + } else if (*_basename != 0) { + if (__folder_contains(cwd, _basename, 0, &dent)) + __cmd_sug(&dent, strlen(_basename)); + } + } + } else if (c == 127) { + if ((cmd_cursor_index + 1) <= strlen(cmd)) { + strcpy(cmd + cmd_cursor_index, cmd + cmd_cursor_index + 1); + putchar(127); + } + } else if ((c > 0) && (c != '\n')) { + if (__cmd_app(c)) { + putchar(c); + } + } + } while (cmd_cursor_index < CMD_LEN); + + // Cleans all blanks at the beginning of the command. + trim(cmd); + __set_echo(true); +} + +/// @brief Gets the options from the command. +/// @param command The executed command. +static void __cmd_opt(char *command, int *argc, char ***argv) +{ + // Get the number of arguments, return if zero. + if (((*argc) = __count_words(command)) == 0) { + return; + } + (*argv) = (char **)malloc(sizeof(char *) * ((*argc) + 1)); + bool_t inword = false; + const char *cit = command; + size_t argcIt = 0, argIt = 0; + do { + if (__is_separator(*cit)) { + if (inword) { + inword = false; + (*argv)[argcIt++][argIt] = '\0'; + argIt = 0; + } + } else { + // Allocate string for argument. + if (!inword) { + (*argv)[argcIt] = (char *)malloc(sizeof(char) * CMD_LEN); + } + inword = true; + (*argv)[argcIt][argIt++] = (*cit); + } + } while (*cit++); + (*argv)[argcIt] = NULL; +} + +void wait_for_child(int signum) +{ + wait(NULL); +} + +int main(int argc, char *argv[]) +{ + setsid(); + + char *USER = getenv("USER"); + if (USER == NULL) { + printf("shell: There is no user set.\n"); + return 1; + } + if (getenv("PATH") == NULL) { + if (setenv("PATH", "/bin:/usr/bin", 0) == -1) { + printf("shell: Failed to set PATH.\n"); + return 1; + } + } + // Set the signal handler to handle the termination of the child. + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = wait_for_child; + if (sigaction(SIGCHLD, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGCHLD, strerror(errno)); + return 1; + } + // Clear the screen. + puts("\033[J"); + // Welcome the user. + puts(BG_WHITE FG_BLACK); + printf("Welcome " FG_RED "%s" FG_BLACK "...\n\n", USER); + puts(BG_BLACK FG_BRIGHT_WHITE); + // Move inside the home directory. + __cd(0, NULL); + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" + while (true) { + // First print the prompt. + __prompt_print(); + + // Get the input command. + __cmd_get(); + + // Check if the command is empty. + if (strlen(cmd) <= 0) { + continue; + } + + // Retrieve the options from the command. + // The current number of arguments. + int _argc = 1; + // The vector of arguments. + char **_argv; + __cmd_opt(cmd, &_argc, &_argv); + // Check if the command is empty. + if (_argc == 0) { + continue; + } + + // Add the command to the history. + __hst_push(cmd); + if (!strcmp(_argv[0], "init")) { + } else if (!strcmp(_argv[0], "cd")) { + __cd(_argc, _argv); + } else if (!strcmp(_argv[0], "..")) { + const char *__argv[] = { "cd", "..", NULL }; + __cd(2, (char **)__argv); + } else if (!strcmp(_argv[0], "export")) { + __export(_argc, _argv); + } else { + bool_t blocking = true; + if (strcmp(_argv[_argc - 1], "&") == 0) { + free(_argv[_argc - 1]); + _argv[_argc - 1] = NULL; + blocking = false; + } + // Is a shell path, execute it! + int status; + pid_t cpid = fork(); + if (cpid == 0) { + // Makes the new process a group leader + pid_t pid = getpid(); + setgid(pid); + + if (execvp(_argv[0], _argv) == -1) { + printf("\nUnknown command: %s\n", _argv[0]); + exit(1); + } + } + if (blocking) { + waitpid(cpid, &status, 0); + } + } + // Free up the memory reserved for the arguments. + for (int it = 0; it < _argc; ++it) { + // Check if the argument is not empty. + if (_argv[it] != NULL) { + // Free up its memory. + free(_argv[it]); + } + } + } +#pragma clang diagnostic pop + return 0; +} diff --git a/programs/showpid.c b/programs/showpid.c new file mode 100644 index 0000000..a621164 --- /dev/null +++ b/programs/showpid.c @@ -0,0 +1,14 @@ +/// MentOS, The Mentoring Operating system project +/// @file showpid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char **argv) +{ + printf("pid %d\n", getppid()); + return 0; +} diff --git a/programs/sleep.c b/programs/sleep.c new file mode 100644 index 0000000..446c013 --- /dev/null +++ b/programs/sleep.c @@ -0,0 +1,19 @@ +/// MentOS, The Mentoring Operating system project +/// @file sleep.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char **argv) +{ + if (argc == 2) { + int amount = atoi(argv[1]); + if (amount > 0) { + sleep(amount); + } + } + return 0; +} diff --git a/programs/tests/CMakeLists.txt b/programs/tests/CMakeLists.txt new file mode 100644 index 0000000..16f00fb --- /dev/null +++ b/programs/tests/CMakeLists.txt @@ -0,0 +1,126 @@ +# ============================================================================= +# Author: Enrico Fraccaroli +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(tests C) + +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Set the directory where the compiled binaries will be placed. +set(MENTOS_TESTS_DIR ${CMAKE_SOURCE_DIR}/files/bin/tests) + +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -u_start") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -no-pie -Wl,-z,norelro") + +# ============================================================================= +# Add the executables (manually). +set(TESTS + t_mem.c + t_fork.c + # Real-time programs + t_periodic1.c + t_periodic2.c + t_periodic3.c + # Exec + t_exec.c + t_exec_callee.c + # Test environmental variables. + t_setenv.c + t_getenv.c + # Test: signals. + t_stopcont.c + t_sigusr.c + t_abort.c + t_sigfpe.c + t_siginfo.c + t_sleep.c + t_sigaction.c + t_sigmask.c + t_groups.c + t_alarm.c + t_kill.c + t_itimer.c + t_gid.c) + +foreach(TEST ${TESTS}) + # Prepare the program name. + string(REPLACE ".c" "" TEST_NAME ${TEST}) + # Set the name of the target. + set(TARGET_NAME test_${TEST_NAME}) + + # Log the entry. + message(VERBOSE "Test ${TEST_NAME} (source: ${TEST}, make target: ${TARGET_NAME})") + + # Randomize .text section address so when debugging symbols don't clash The allowed range is from 256MB to 2.75GB Minimum allowed address: 0x10000000 Max allowed address: + # 0xB0000000 + string(MD5 RAND_HASH ${TEST}) + string(SUBSTRING ${RAND_HASH} 1 3 TEXADDR_INFIX) + string( + RANDOM + LENGTH 1 + ALPHABET 0123456789AB + RANDOM_SEED ${RAND_HASH} TEXADDR_FIRST) + + # Create the target. + add_executable(${TARGET_NAME} ${TEST}) + + # Add the includes. + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/libc/inc) + + # Link the libc library. + target_link_libraries(${TARGET_NAME} ${CMAKE_BINARY_DIR}/libc/libc.a) + + # Add the dependency to libc. + add_dependencies(${TARGET_NAME} libc) + + # Add the linking properties. + set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "-Wl,-Ttext=0x${TEXADDR_FIRST}${TEXADDR_INFIX}0000,-e_start") + + # Add the final target that strips the debugging symbols from the program. + add_custom_target( + ${TEST_NAME} + BYPRODUCTS ${MENTOS_TESTS_DIR}/${TEST_NAME} + COMMAND mkdir -p ${MENTOS_TESTS_DIR} + COMMAND strip --strip-debug ${PROJECT_BINARY_DIR}/${TARGET_NAME} -o ${MENTOS_TESTS_DIR}/${TEST_NAME} + DEPENDS ${TARGET_NAME}) + + # Append the program name to the list of all the executables. + list(APPEND ALL_TESTS ${TEST_NAME}) +endforeach() + +# Add the overall target that builds all the programs. +add_custom_target(all_tests ALL DEPENDS ${ALL_TESTS}) diff --git a/programs/tests/t_abort.c b/programs/tests/t_abort.c new file mode 100644 index 0000000..518010b --- /dev/null +++ b/programs/tests/t_abort.c @@ -0,0 +1,46 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_abort.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void sig_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGABRT) { + + static int counter = 0; + counter += 1; + + printf("handler(%d) : Correct signal. ABRT (%d/3)\n", sig, counter); + if (counter < 3) + abort(); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sig_handler; + + if (sigaction(SIGABRT, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGABRT, strerror(errno)); + return 1; + } + + abort(); +} \ No newline at end of file diff --git a/programs/tests/t_alarm.c b/programs/tests/t_alarm.c new file mode 100644 index 0000000..3633a9d --- /dev/null +++ b/programs/tests/t_alarm.c @@ -0,0 +1,51 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_alarm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +void alarm_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGALRM) { + printf("handler(%d) : Correct signal.\n", sig); + + alarm(5); + int rest = alarm(5); + printf("handler(%d) : alarm(5) result: %d.\n", sig, rest); + + rest = alarm(0); + printf("handler(%d) : alarm(0) result: %d.\n", sig, rest); + + exit(0); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = alarm_handler; + if (sigaction(SIGALRM, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGALRM, strerror(errno)); + return 1; + } + + + alarm(5); + while(1) { } + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_exec.c b/programs/tests/t_exec.c new file mode 100644 index 0000000..821680a --- /dev/null +++ b/programs/tests/t_exec.c @@ -0,0 +1,65 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_exec.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +static inline void __print_usage(int argc, char *argv[]) +{ + if (argc > 0) { + printf("%s: Usage: %s \n", argv[0], argv[0]); + printf("exec_type: execl, execlp, execle, execlpe, execv, execvp, execve, execvpe\n"); + } +} + +int main(int argc, char *argv[]) +{ + int status; + if (argc != 2) { + __print_usage(argc, argv); + return 1; + } + + if (setenv("ENV_VAR", "pwd0", 0) == -1) { + printf("Failed to set env: `ENV_VAR`\n"); + return 1; + } + + char *file = "echo"; + char *path = "/bin/echo"; + char *exec_argv[] = { "echo", "ENV_VAR: ${ENV_VAR}", NULL }; + char *exec_envp[] = { "PATH=/bin", "ENV_VAR=pwd1", NULL }; + + if (fork() == 0) { + if (strcmp(argv[1], "execl") == 0) { + execl(path, "echo", "ENV_VAR: ${ENV_VAR}", NULL); + } else if (strcmp(argv[1], "execlp") == 0) { + execlp(file, "echo", "ENV_VAR: ${ENV_VAR}", NULL); + } else if (strcmp(argv[1], "execle") == 0) { + execle(path, "echo", "ENV_VAR: ${ENV_VAR}", exec_envp, NULL); + } else if (strcmp(argv[1], "execlpe") == 0) { + execlpe(file, "echo", "ENV_VAR: ${ENV_VAR}", exec_envp, NULL); + } else if (strcmp(argv[1], "execv") == 0) { + execv(path, exec_argv); + } else if (strcmp(argv[1], "execvp") == 0) { + execvp(file, exec_argv); + } else if (strcmp(argv[1], "execve") == 0) { + execve(path, exec_argv, exec_envp); + } else if (strcmp(argv[1], "execvpe") == 0) { + execvpe(file, exec_argv, exec_envp); + } else { + __print_usage(argc, argv); + return 1; + } + printf("Exec failed.\n"); + return 1; + } + wait(&status); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_exec_callee.c b/programs/tests/t_exec_callee.c new file mode 100644 index 0000000..1f31572 --- /dev/null +++ b/programs/tests/t_exec_callee.c @@ -0,0 +1,19 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_exec_callee.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char *argv[]) +{ + char *ENV_VAR = getenv("ENV_VAR"); + if (ENV_VAR == NULL) { + printf("Failed to get env: `ENV_VAR`\n"); + return 1; + } + printf("ENV_VAR = %s\n", ENV_VAR); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_fork.c b/programs/tests/t_fork.c new file mode 100644 index 0000000..744fc4e --- /dev/null +++ b/programs/tests/t_fork.c @@ -0,0 +1,66 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_fork.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + return 1; + } + char *ptr; + int N; + N = strtol(argv[1], &ptr, 10); + pid_t cpid, mypid; +#if 0 + char buffer[256]; + + N = strtol(argv[1], &ptr, 10); + + char *_argv[] = { + "/bin/tests/t_fork", + itoa(buffer, N - 1, 10), + NULL + }; + + printf("N = %d\n", N); + if (N > 1) { + + printf("Keep caling (N = %d)\n", N); + + if ((cpid = fork()) == 0) { + + printf("I'm the child (pid = %d, N = %d)!\n", getpid(), N); + + execv(_argv[0], _argv); + + } + printf("Will wait for %d\n", N, cpid); + while (wait(NULL) != -1) continue; + } +#else + while (1) { + mypid = getpid(); + if (N > 0) { + if ((cpid = fork()) == 0) { + N -= 1; + continue; + } + printf("I'm %d and I will wait for %d (N = %d)!\n", mypid, cpid, N); + while (wait(NULL) != -1) continue; + printf("I'm %d and I waited for %d (N = %d)!\n", mypid, cpid, N); + } else { + printf("I'm %d and I will not wait!\n", mypid); + } + break; + } +#endif + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_getenv.c b/programs/tests/t_getenv.c new file mode 100644 index 0000000..5a068ab --- /dev/null +++ b/programs/tests/t_getenv.c @@ -0,0 +1,21 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_getenv.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *ENV_VAR = getenv("ENV_VAR"); + if (ENV_VAR == NULL) { + printf("Failed to get env: `ENV_VAR`\n"); + return 1; + } + printf("ENV_VAR = %s\n", ENV_VAR); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_gid.c b/programs/tests/t_gid.c new file mode 100644 index 0000000..a03c6c8 --- /dev/null +++ b/programs/tests/t_gid.c @@ -0,0 +1,53 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_gid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "grp.h" +#include "stdlib.h" +#include "string.h" +#include + +static void list_groups() { + + group* iter; + while ((iter = getgrent()) != NULL) { + printf("Group\n\tname: %s\n\tpasswd: %s\n\tnames:\n",iter->gr_name, iter->gr_passwd); + + size_t count = 0; + while (iter->gr_mem[count] != NULL) { + printf("\t\t%s\n",iter->gr_mem[count]); + count += 1; + } + + printf("\n"); + } +} + +int main(int argc, char** argv) { + + printf("List of all groups:\n"); + + list_groups(); + setgrent(); + + printf("List all groups again:\n"); + + list_groups(); + endgrent(); + + group* root_group = getgrgid(0); + if (strcmp(root_group->gr_name, "root") != 0) { + printf("Error in getgrgid function."); + return 1; + } + + root_group = getgrnam("root"); + if (root_group->gr_gid != 0) { + printf("Error in getgrnam function."); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_groups.c b/programs/tests/t_groups.c new file mode 100644 index 0000000..eba4f20 --- /dev/null +++ b/programs/tests/t_groups.c @@ -0,0 +1,39 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_groups.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + pid_t gid = getgid(); + pid_t pid = getpid(); + pid_t sid = getsid(0); + + printf("pid: %d, gid: %d, sid: %d\n\n", pid, gid, sid); + for (int i = 0; i < 5; ++i) { + if (fork() == 0) { + pid_t gid_child = getgid(); + pid_t pid_child = getpid(); + + pid_t ppid_child = getppid(); + pid_t sid_child = getsid(ppid_child); + + printf("%d) pid_child: %d, gid_child: %d, ppid_child: %d, sid_child: %d\n", + i, pid_child, gid_child, ppid_child, sid_child); + sleep(5); + exit(0); + } + } + //int status; + //while (wait(&status) > 0) + // ; + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_itimer.c b/programs/tests/t_itimer.c new file mode 100644 index 0000000..6ca2b84 --- /dev/null +++ b/programs/tests/t_itimer.c @@ -0,0 +1,62 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_itimer.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void alarm_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGALRM) { + + itimerval val = { 0 }; + getitimer(ITIMER_REAL, &val); + printf("(sec: %d, usec: %d)\n", val.it_interval.tv_sec, val.it_interval.tv_usec); + + static int counter = 0; + counter += 1; + + printf("handler(%d) : Correct signal x%d\n", sig, counter); + if (counter == 4) + { + itimerval interval = { 0 }, prev = { 0 }; + interval.it_interval.tv_sec = 0; + + setitimer(ITIMER_REAL, &interval, &prev); + printf("prev: (sec: %d, usec: %d)", prev.it_interval.tv_sec, prev.it_interval.tv_usec); + + exit(0); + } + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = alarm_handler; + if (sigaction(SIGALRM, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGALRM, strerror(errno)); + return 1; + } + + itimerval interval = { 0 }; + interval.it_interval.tv_sec = 4; + setitimer(ITIMER_REAL, &interval, NULL); + + while(1) { } + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_kill.c b/programs/tests/t_kill.c new file mode 100644 index 0000000..98d08a8 --- /dev/null +++ b/programs/tests/t_kill.c @@ -0,0 +1,66 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_kill.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +#define cpu_relax() asm volatile("pause\n" \ + : \ + : \ + : "memory") + +static inline void fake_sleep(int times) +{ + for (int j = 0; j < times; ++j) + for (long i = 1; i < 1024000; ++i) + cpu_relax(); +} + +void child_sigusr1_handler(int sig) +{ + printf("handler(sig: %d) : Starting handler (pid: %d).\n", sig, getpid()); + printf("handler(sig: %d) : Ending handler (pid: %d).\n", sig, getpid()); +} + +void child_process() +{ + while (1) { + printf("I'm the child (%d): I'm playing around!\n", getpid()); + fake_sleep(1); + } +} + +int main(int argc, char *argv[]) +{ + printf("main : Creating child!\n"); + pid_t ppid; + if ((ppid = fork()) == 0) { + printf("I'm the child (%d)!\n", ppid); + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = child_sigusr1_handler; + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR1, strerror(errno)); + return 1; + } + child_process(); + } else { + printf("I'm the parent (%d)!\n", ppid); + } + fake_sleep(9); + kill(ppid, SIGUSR1); + fake_sleep(9); + kill(ppid, SIGTERM); + int status; + wait(&status); + printf("main : end (%d)\n", status); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_mem.c b/programs/tests/t_mem.c new file mode 100644 index 0000000..ab683ba --- /dev/null +++ b/programs/tests/t_mem.c @@ -0,0 +1,26 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_mem.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + return 1; + } + char *ptr; + int N = strtol(argv[1], &ptr, 10), *V; + for (int i = 0; i < N; ++i) { + for (int j = 1; j < N; ++j) { + V = (int *)malloc(j * sizeof(int)); + free(V); + } + } + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_periodic1.c b/programs/tests/t_periodic1.c new file mode 100644 index 0000000..07dfde3 --- /dev/null +++ b/programs/tests/t_periodic1.c @@ -0,0 +1,47 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_periodic1.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid = getpid(); + sched_param_t param; + // Get current parameters. + sched_getparam(cpid, ¶m); + // Change parameters. + param.period = 5000; + param.deadline = 5000; + param.is_periodic = true; + // Set modified parameters. + sched_setparam(cpid, ¶m); + int counter = 0; + if (fork() == 0) { + char *_argv[] = { "/bin/periodic2", NULL }; + execv(_argv[0], _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + if (fork() == 0) { + char *_argv[] = { "/bin/periodic3", NULL }; + execv(_argv[0], _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + + while (1) { + if (++counter == 10) { + counter = 0; + } + printf("[periodic1]counter %d\n",counter); + if (waitperiod() == -1) { + printf("[%s] Error in waitperiod: %s\n", argv[0], strerror(errno)); + break; + } + } + return 0; +} diff --git a/programs/tests/t_periodic2.c b/programs/tests/t_periodic2.c new file mode 100644 index 0000000..61d4bdb --- /dev/null +++ b/programs/tests/t_periodic2.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_periodic2.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid = getpid(); + sched_param_t param; + // Get current parameters. + sched_getparam(cpid, ¶m); + // Change parameters. + param.period = 4000; + param.deadline = 4000; + param.is_periodic = true; + // Set modified parameters. + sched_setparam(cpid, ¶m); + int counter = 0; + while (1) { + if (++counter == 10) + break; + printf("[priodic2] counter: %d\n", counter); + if (waitperiod() == -1) { + printf("[%s] Error in waitperiod: %s\n", argv[0], strerror(errno)); + break; + } + } + return 0; +} diff --git a/programs/tests/t_periodic3.c b/programs/tests/t_periodic3.c new file mode 100644 index 0000000..41e5cac --- /dev/null +++ b/programs/tests/t_periodic3.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_periodic3.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid = getpid(); + sched_param_t param; + // Get current parameters. + sched_getparam(cpid, ¶m); + // Change parameters. + param.period = 3000; + param.deadline = 3000; + param.is_periodic = true; + // Set modified parameters. + sched_setparam(cpid, ¶m); + int counter = 0; + while (1) { + if (++counter == 10) + break; + printf("[periodic3] counter: %d\n", counter); + if (waitperiod() == -1) { + printf("[%s] Error in waitperiod: %s\n", argv[0], strerror(errno)); + break; + } + } + return 0; +} diff --git a/programs/tests/t_setenv.c b/programs/tests/t_setenv.c new file mode 100644 index 0000000..3eb0dea --- /dev/null +++ b/programs/tests/t_setenv.c @@ -0,0 +1,29 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_setenv.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *_argv[] = { "/bin/tests/t_getenv", NULL }; + int status; + + if (setenv("ENV_VAR", "pwd0", 0) == -1) { + printf("Failed to set env: `PWD`\n"); + return 1; + } + + if (fork() == 0) { + execv(_argv[0], _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + + wait(&status); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sigaction.c b/programs/tests/t_sigaction.c new file mode 100644 index 0000000..c263bd3 --- /dev/null +++ b/programs/tests/t_sigaction.c @@ -0,0 +1,49 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigaction.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +static int *values = NULL; + +void sigusr1_handler(int sig) +{ + printf("handler(sig: %d) : Starting handler.\n", sig); + printf("handler(sig: %d) : value : %d\n", sig, values); + values = malloc(sizeof(int) * 4); + for (int i = 0; i < 4; ++i) { + values[i] = i; + printf("values[%d] : `%d`\n", i, values[i]); + } + printf("handler(sig: %d) : value : %d\n", sig, values); + printf("handler(sig: %d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sigusr1_handler; + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR1, strerror(errno)); + return 1; + } + + printf("main : Calling handler (%d).\n", SIGUSR1); + printf("main : value : %d\n", values); + int ret = kill(getpid(), SIGUSR1); + printf("main : Returning from handler (%d): %d.\n", SIGUSR1, ret); + printf("main : value : %d\n", values); + for (int i = 0; i < 4; ++i) + printf("values[%d] : `%d`\n", i, values[i]); + free(values); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sigfpe.c b/programs/tests/t_sigfpe.c new file mode 100644 index 0000000..26e4981 --- /dev/null +++ b/programs/tests/t_sigfpe.c @@ -0,0 +1,50 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigfpe.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + + +void sig_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGFPE) { + printf("handler(%d) : Correct signal. FPE\n", sig); + printf("handler(%d) : Exiting\n", sig); + exit(1); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sig_handler; + + if (sigaction(SIGFPE, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGFPE, strerror(errno)); + return 1; + } + + printf("Diving by zero (unrecoverable)...\n"); + + // Should trigger ALU error, fighting the compiler... + int d = 1, e = 1; + while (1) { + d /= e; + e -= 1; + } +} \ No newline at end of file diff --git a/programs/tests/t_siginfo.c b/programs/tests/t_siginfo.c new file mode 100644 index 0000000..9479835 --- /dev/null +++ b/programs/tests/t_siginfo.c @@ -0,0 +1,51 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_siginfo.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void sig_handler_info(int sig, siginfo_t *siginfo) +{ + printf("handler(%d, %p) : Starting handler.\n", sig, siginfo); + if (sig == SIGFPE) { + printf("handler(%d, %p) : Correct signal.\n", sig, siginfo); + printf("handler(%d, %p) : Code : %d\n", sig, siginfo, siginfo->si_code); + printf("handler(%d, %p) : Exiting\n", sig, siginfo); + exit(1); + } else { + printf("handler(%d, %p) : Wrong signal.\n", sig, siginfo); + } + printf("handler(%d, %p) : Ending handler.\n", sig, siginfo); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + + action.sa_handler = (sighandler_t)sig_handler_info; + action.sa_flags = SA_SIGINFO; + + if (sigaction(SIGFPE, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGFPE, strerror(errno)); + return 1; + } + + printf("Diving by zero (unrecoverable)...\n"); + + // Should trigger ALU error, fighting the compiler... + int d = 1, e = 1; + while (1) { + d /= e; + e -= 1; + } +} \ No newline at end of file diff --git a/programs/tests/t_sigmask.c b/programs/tests/t_sigmask.c new file mode 100644 index 0000000..24f37bb --- /dev/null +++ b/programs/tests/t_sigmask.c @@ -0,0 +1,50 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigmask.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +void sigusr1_handler(int sig) +{ + printf("handler(sig: %d) : Starting handler.\n", sig); + printf("handler(sig: %d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + int ret; + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sigusr1_handler; + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%d, %s).\n", SIGUSR1, strerror(errno)); + return 1; + } + + printf("main : Blocking signal (%d).\n", SIGUSR1); + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_BLOCK, &mask, NULL); + + printf("main : Calling handler (%d).\n", SIGUSR1); + ret = kill(getpid(), SIGUSR1); + printf("main : Returning from handler (%d): %d.\n", SIGUSR1, ret); + + printf("main : Unblocking signal (%d).\n", SIGUSR1); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + printf("main : Calling handler (%d).\n", SIGUSR1); + ret = kill(getpid(), SIGUSR1); + printf("main : Returning from handler (%d): %d.\n", SIGUSR1, ret); + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sigusr.c b/programs/tests/t_sigusr.c new file mode 100644 index 0000000..f9672b4 --- /dev/null +++ b/programs/tests/t_sigusr.c @@ -0,0 +1,56 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigusr.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void sig_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + + static int counter = 0; + if (sig == SIGUSR1 || sig == SIGUSR2) { + printf("handler(%d) : Correct signal. SIGUSER\n", sig); + counter += 1; + + if (counter == 2) + exit(1); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sig_handler; + + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR1, strerror(errno)); + return 1; + } + + if (sigaction(SIGUSR2, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR2, strerror(errno)); + return 1; + } + + kill(getpid(), SIGUSR1); + sleep(2); + kill(getpid(), SIGUSR2); + + while(1) { }; + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sleep.c b/programs/tests/t_sleep.c new file mode 100644 index 0000000..2193fc3 --- /dev/null +++ b/programs/tests/t_sleep.c @@ -0,0 +1,22 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sleep.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + printf("Sleeping for 4 seconds... "); + sleep(4); + printf("COMPLETED.\n"); + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_stopcont.c b/programs/tests/t_stopcont.c new file mode 100644 index 0000000..f666e07 --- /dev/null +++ b/programs/tests/t_stopcont.c @@ -0,0 +1,60 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_stopcont.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include "stdlib.h" +#include + +int child_pid; + +void wait_for_child(int signr) +{ + printf("Signal received: %s\n", strsignal(signr)); + sleep(10); + + printf("Sending continue sig to child\n"); + if (kill(child_pid, SIGCONT) == -1) + printf("Error sending signal\n"); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = wait_for_child; + if (sigaction(SIGCHLD, &action, NULL) == -1) { + printf("Failed to set signal handler. %d\n", SIGCHLD); + return 1; + } + + child_pid = fork(); + if (child_pid != 0) { + printf("Child PID: %d\n", child_pid); + + sleep(2); + printf("Sending stop sig to child\n"); + +#if 1 + if (kill(child_pid, SIGSTOP) == -1) + printf("Errore invio stop\n"); +#endif + + wait(NULL); + } else { + int c = 0; + for (;;) { + printf("c: %d\n", c++); + sleep(3); + } + } + + return 0; +} \ No newline at end of file diff --git a/programs/touch.c b/programs/touch.c new file mode 100644 index 0000000..8f56bb5 --- /dev/null +++ b/programs/touch.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file touch.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf( + "Updates modification times of a given fine. If the does not" + "exists, it creates it.\n" + ); + printf("Usage:\n"); + printf(" touch \n"); + return 0; + } + int fd = open(argv[1], O_RDONLY, 0); + if (fd < 0) { + fd = open(argv[1], O_CREAT, 0); + if (fd >= 0) { + close(fd); + } + } + printf("\n"); + return 0; +} diff --git a/programs/uname.c b/programs/uname.c new file mode 100644 index 0000000..580d9d9 --- /dev/null +++ b/programs/uname.c @@ -0,0 +1,40 @@ +/// MentOS, The Mentoring Operating system project +/// @file uname.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + utsname_t utsname; + uname(&utsname); + if (argc != 2) { + printf("%s\n", utsname.sysname); + return -1; + } + + if (!(strcmp(argv[1], "-a")) || !(strcmp(argv[1], "--all"))) { + printf("%s %s \n", utsname.sysname, utsname.version); + } else if (!(strcmp(argv[1], "-r")) || !(strcmp(argv[1], "--rev"))) { + printf("%s\n", utsname.version); + } else if (!(strcmp(argv[1], "-i")) || !(strcmp(argv[1], "--info"))) { + + } else if (!(strcmp(argv[1], "-h")) || !(strcmp(argv[1], "--help"))) { + printf("Uname function allow you to see the kernel and system information.\n"); + printf("Function avaibles:\n"); + printf( + "1) -a - Kernel version and processor type\n" + "2) -r - Only the kernel version\n" + "3) -i - All info of system and kernel\n" + ); + } else { + printf( + "%s. For more info about this tool, please do 'uname --help'\n", utsname.sysname + ); + } + return 0; +} diff --git a/scripts/get_text_address.sh b/scripts/get_text_address.sh new file mode 100644 index 0000000..68df330 --- /dev/null +++ b/scripts/get_text_address.sh @@ -0,0 +1,6 @@ +#!/bin/bash +for obj_file in "$@" +do + ADDR=$(readelf -WS $obj_file | grep .text | awk '{ print "0x"$5 }') + echo "add-symbol-file $obj_file ${ADDR}" +done \ No newline at end of file diff --git a/scripts/replace_links.sh b/scripts/replace_links.sh new file mode 100644 index 0000000..7a6d452 --- /dev/null +++ b/scripts/replace_links.sh @@ -0,0 +1,3 @@ +#!/bin/bash +find -type l -exec git update-index --assume-unchanged {} \; +find -type l -exec bash -c 'ln -f "$(readlink -m "$0")" "$0"' {} \; \ No newline at end of file From 5263b115bf0d7b3404484e0687783f7f675d1994 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Mon, 4 Oct 2021 11:55:18 +0200 Subject: [PATCH 16/21] Add help command. Remove compiled files. --- .gitignore | 3 +++ files/bin/cat | Bin 43644 -> 0 bytes files/bin/clear | Bin 37588 -> 0 bytes files/bin/cpuid | Bin 37564 -> 0 bytes files/bin/date | Bin 38084 -> 0 bytes files/bin/echo | Bin 37612 -> 0 bytes files/bin/env | Bin 37604 -> 0 bytes files/bin/init | Bin 37984 -> 0 bytes files/bin/ipcrm | Bin 37564 -> 0 bytes files/bin/ipcs | Bin 37564 -> 0 bytes files/bin/kill | Bin 43684 -> 0 bytes files/bin/login | Bin 52604 -> 0 bytes files/bin/logo | Bin 37588 -> 0 bytes files/bin/ls | Bin 52980 -> 0 bytes files/bin/mkdir | Bin 43588 -> 0 bytes files/bin/nice | Bin 37640 -> 0 bytes files/bin/poweroff | Bin 37640 -> 0 bytes files/bin/ps | Bin 47928 -> 0 bytes files/bin/pwd | Bin 37640 -> 0 bytes files/bin/rm | Bin 48096 -> 0 bytes files/bin/rmdir | Bin 43588 -> 0 bytes files/bin/shell | Bin 62076 -> 0 bytes files/bin/showpid | Bin 37644 -> 0 bytes files/bin/sleep | Bin 38084 -> 0 bytes files/bin/tests/t_abort | Bin 43620 -> 0 bytes files/bin/tests/t_alarm | Bin 43636 -> 0 bytes files/bin/tests/t_exec | Bin 42112 -> 0 bytes files/bin/tests/t_exec_callee | Bin 37588 -> 0 bytes files/bin/tests/t_fork | Bin 37700 -> 0 bytes files/bin/tests/t_getenv | Bin 37604 -> 0 bytes files/bin/tests/t_gid | Bin 38172 -> 0 bytes files/bin/tests/t_groups | Bin 38252 -> 0 bytes files/bin/tests/t_itimer | Bin 48180 -> 0 bytes files/bin/tests/t_kill | Bin 43760 -> 0 bytes files/bin/tests/t_mem | Bin 37604 -> 0 bytes files/bin/tests/t_periodic1 | Bin 48076 -> 0 bytes files/bin/tests/t_periodic2 | Bin 43656 -> 0 bytes files/bin/tests/t_periodic3 | Bin 43656 -> 0 bytes files/bin/tests/t_setenv | Bin 37984 -> 0 bytes files/bin/tests/t_sigaction | Bin 43624 -> 0 bytes files/bin/tests/t_sigfpe | Bin 43596 -> 0 bytes files/bin/tests/t_siginfo | Bin 43600 -> 0 bytes files/bin/tests/t_sigmask | Bin 43600 -> 0 bytes files/bin/tests/t_sigusr | Bin 48180 -> 0 bytes files/bin/tests/t_sleep | Bin 38100 -> 0 bytes files/bin/tests/t_stopcont | Bin 38252 -> 0 bytes files/bin/touch | Bin 37676 -> 0 bytes files/bin/uname | Bin 37608 -> 0 bytes programs/CMakeLists.txt | 3 ++- programs/help.c | 32 ++++++++++++++++++++++++++++++++ 50 files changed, 37 insertions(+), 1 deletion(-) delete mode 100644 files/bin/cat delete mode 100644 files/bin/clear delete mode 100644 files/bin/cpuid delete mode 100644 files/bin/date delete mode 100644 files/bin/echo delete mode 100644 files/bin/env delete mode 100644 files/bin/init delete mode 100644 files/bin/ipcrm delete mode 100644 files/bin/ipcs delete mode 100644 files/bin/kill delete mode 100644 files/bin/login delete mode 100644 files/bin/logo delete mode 100644 files/bin/ls delete mode 100644 files/bin/mkdir delete mode 100644 files/bin/nice delete mode 100644 files/bin/poweroff delete mode 100644 files/bin/ps delete mode 100644 files/bin/pwd delete mode 100644 files/bin/rm delete mode 100644 files/bin/rmdir delete mode 100644 files/bin/shell delete mode 100644 files/bin/showpid delete mode 100644 files/bin/sleep delete mode 100644 files/bin/tests/t_abort delete mode 100644 files/bin/tests/t_alarm delete mode 100644 files/bin/tests/t_exec delete mode 100644 files/bin/tests/t_exec_callee delete mode 100644 files/bin/tests/t_fork delete mode 100644 files/bin/tests/t_getenv delete mode 100644 files/bin/tests/t_gid delete mode 100644 files/bin/tests/t_groups delete mode 100644 files/bin/tests/t_itimer delete mode 100644 files/bin/tests/t_kill delete mode 100644 files/bin/tests/t_mem delete mode 100644 files/bin/tests/t_periodic1 delete mode 100644 files/bin/tests/t_periodic2 delete mode 100644 files/bin/tests/t_periodic3 delete mode 100644 files/bin/tests/t_setenv delete mode 100644 files/bin/tests/t_sigaction delete mode 100644 files/bin/tests/t_sigfpe delete mode 100644 files/bin/tests/t_siginfo delete mode 100644 files/bin/tests/t_sigmask delete mode 100644 files/bin/tests/t_sigusr delete mode 100644 files/bin/tests/t_sleep delete mode 100644 files/bin/tests/t_stopcont delete mode 100644 files/bin/touch delete mode 100644 files/bin/uname create mode 100644 programs/help.c diff --git a/.gitignore b/.gitignore index 54484f9..fa3360d 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,6 @@ src/initscp/initfscp # OS generated files .DS_Store + +# OS filesystem files. +files/bin/** \ No newline at end of file diff --git a/files/bin/cat b/files/bin/cat deleted file mode 100644 index c843bfe4c9b998f43db35695a111ed8b89657668..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43644 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 diff --git a/files/bin/clear b/files/bin/clear deleted file mode 100644 index a769a7dccbc20e3edd789214a7247da4759209de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37588 zcmeI53w%`7weZhO5*RQzqehK_GFq_UBLqbe!OClRD31tF z`~AM}%d}_ati9G=YwfkyUVHDgcW|R`&J2gcp;;fNmZAyNn%z)8j^D0r24g;%AusVrN})$H@ce38L%lo$e3~XtnjOCsAO}28Zic4GlT^PJd@XpM zMqUr|4B!#|Lf0h^vgH}UyF5}Z&&?kPiugIBOEqogygaRS%CZNqesWvY zd)bXWf9(AB-uer^8V;)e({F)(3-nu{-va#>=(j+>1^O+}Z-IUb^jo0c0{s^Ff5rk| zx-a~5-JB7rp|;N0T|If>j~CvokD_&H`bJF)b)?sA)0$$j1>xQL3`#?LJUgW6b;aGc zEnZSr(6c*|I%2o*keZ>6RJ|h>i={oG>ZY!A)aAJrtaMO0Gh0>O7Fp8sY7+e=Bn39@ zUT+AC1zq~~GZZDfasXF4^p-JwR?X0(?Bh4>o!iM;2@crQu|K3|V~dcl(_zDGZVX kg8Ir>yo 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 hO?>ljekI~klU~S+e^h~xO+bi+kub{9-}zXl?)i diff --git a/files/bin/echo b/files/bin/echo deleted file mode 100644 index c66825e7b9999ffbacf81895c0b7918512e39ed1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37612 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 T=qe!O5NZ`A-+1SjN%y}26yy0r diff --git a/files/bin/env b/files/bin/env deleted file mode 100644 index 9902273cdafb38ee41830a3d7f7a466c0ce14ede..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37604 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 L?8F%HN2mK=NzCqs diff --git a/files/bin/ipcrm b/files/bin/ipcrm deleted file mode 100644 index ae2982f9b4179b0a2c69207c4f6cf38c13de87c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37564 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 Bqa^?U diff --git a/files/bin/ipcs b/files/bin/ipcs deleted file mode 100644 index 9cedb3b23d5c5adf505138f0443564ada45cbec6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37564 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 diff --git a/files/bin/kill b/files/bin/kill deleted file mode 100644 index f82bb05c9fdbd82bbd94c5d53ebbdea7d87b4be6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43684 zcmeHw33!y%)&DzLV8GxE8ZamhgMtc5NFZ#2vJQ)EF$*q$AtaM9kYwV_ykYqWBn)V# zF>PF0ZM9ZgYSh|lU2sDTi-5RArAmZg)QE4KRHIExYE$R`J9nA669U@r|NWoudHxU4 zcji6!cka38o_p@O%X{Bpvtz+*lgXsXzZfl6BPxA{tu77U`yS;;nr7E}X;)|o+Bpno zsTFZ%4ObE}&=p6CR*mEM)-+okU8^!RjjnhVF9opzIIahVXc}En`D=mK0>@R4+YW%e z@J;fGuY)d>P1i8o(?#iYUH`c+A77{Z^+pP*MLdbPVsXt`l&$r9@up4r*ZlMRU%%L9 zn`^53Zs{kb6@bqEowdMO3!JsUSqq%Cz*!5NwZK^moVCDN3!JsUSqq%C!2dTE$PRp- z>2FIk#$!=jxwqqIn&xjy2)MCtp!I^XPc`X9i|4$4$ZQw9(?JB zs}JMaRg9BWX9qpcXUcjb+R=vFI^}Vu~cd+KJ+*^ZUAeGSI6jok`@zqo& z$=_y9ukmXQp-{T9a|%m>G=Fn%6wwc?tNR*1gA8a!1^LT6Y@2q~)<%`FhLtgbmqAL2 z8ubr_LddtR-gmx8IfP2HI(7u7`5k7{0Y^L2X%xfTjhiDnkx3pOz}TS)?9d)?bOihQ zk;7l!Zrk)0RM9r|!sn1py@kb~c$8vq$6ybn_NP*zz+PisSOJ#(+{*Hf&}L2B9FMHQ zcxF*4VF{YaYWxf$D)%mAGRz^JwN%kHvK9~$(&rctz;4x4>vUs+jbr;+jS`HiYZ(nI zFG=UwLwZMz-^}d2U^0Pr-jJh3jwLcjf|_9>%W#p%Kt`)^b}qeT`QE=-Z2ziRFf7jB z(jL0C3(dYH^Ll^VVB-VuRPMc}M$-as`>!?WY1OmiNJnbz8*Qoh-DAYOwIUz9+d z*YR8MQyrnJ*81B>X5M@wxW)lTv%kDq)2|R}M=5cbNv`FRnVjJA1CACriWZz)?P#e< z+=g4SgsDMz#TqG=SGYea9MYSq#&>hX?`T0MZkgO`<1V-X`rJj3Ae*Ab*>Mw^x+b@4 zZ*|8my48Ey@9b;Gj5aT3vrbt749Wm_Wb zZOmZV(N#2mxmnZuaz}fLs@fK}DunOVTFthG0v zo>{+<_nlHCn)Qnj7_?2p{ly3o#fVifq}h0nyHxO8gOZ?E6VCvrvfQ(R^%XqEV_H=BV0HI4?vom3xQ(1Pn^mP*V-u zh~HC2Az-7(5TXpZ9*P!0i|S*!r{1!o9-%*YMRj&Zpka4&jH$s?AJ|=;-H!Zn1TY=+ z8!4~_1wjSbTJN(dq#P=)kcq2AvkO!8gWP= zg9RnS_$7~I;1EDSUS^Ff(UZYL z7Fy@)EADR8FT|a{-oyt!Qp3>^bHHJx&1A~24`4ZazhFiJEZP@e0f*IJ-lF-~xLAQi z7<)6!jJp5wWVtnveN5g_tAp`;3O<`qrga1Yk)zFYa``dgWd|J1kVqjRq#MTHQBxkq zdijful;8xBZbp}9>6Wd3jc~sqQ=!QOBtw%M@HN6=R6C5x4y&bRFEJy$SgIamchF@4 zJaDq3Z6kb(zV%BP@3Bp=b2E7j*Q>%9GaduEb_t0s0K}W19c@eV2ILt57h1?=Y(9f5 zGL3O!Tv*nHJ;^db7V8JR+k{=B^>g@oEd_w`1kE>)YT~1Lir+8=W2tXsCj?@k!zq1a z>c0@EOt+I=S)e+Zflh@;RR%?v9XE?E(woJQ9Z@mF--zL1Z-Cvww3i4so zYTS)FK?wB@49Y$`6yRMN9jXeR(b3mup$w$-<~M?IQ~~gyk27{XVyql=B?{3GRbBj*om zxP@3}aaJz%=4h!A8{9ywgM>bxvG|M1_*iAONcDV}a+!m@@ZqnwM>M*K@-}VdhNz3q zdutDQ4JGf#oyhwz@}f3&9#X?f(Om9T!YhHJ(14>W80Rmx!XFTh5Iz%XW|#tw1VA-& zO@W1o+2>VR;qzPT^&^ZYDab$?Ld@_QnOFa^+JX7Dh^od?D&u0N1HzPr4^InduIqQe z`DB5^P!1FBL!?XKcj&K3!za-b_xKKR9?Omfo@Ci^FwijKV7N+4GL3IeQv80>4@Wf1 zGQ#pwM1HYy_!TpzHLzbu6Uly_JZZmgS2X|Xp7}FH@}5BZhz4a23g;&qFNH?&7egTk zWBre+$%c|He|gumO?~ls^X53eqYFJWfpJiE8AqqTypwTsGLBAr*j(o#7Yj07lO3JD z?XdWjj?ReDPy7RgKDq@~fM7EwQo^?V_#GN|VuSvI$9@+ag;RW7Z#I9vD^wYnl|}`M*Pt^_8NMqDVQvveaZZ zAo!UY$jA9&Ye-ozIE@!l-x-VPg=I%pJea6_nrt-pewpG@VE6l<1&(0O!T`?M=TH?` z#S)ckv?E-lnX*jO7E?CVm_xFVS@8{)T_}evz{^1q-vt`{%_hXgH=v~mzuiWf6yq-) zsNua8|64T_^};CD;6G@pax_qT{>L@+Q4LS2+IN3L)plad$~^Q(G~TBF(Z&mo{}0-T zVtgk#q-NeStP<9D=E~aOuhDi>yXIK!^bi@icz8M3$ zh^T;T(nEo4>@w)h!8DMVhL|u35jP3ZxzK%S5)hROeI?vKjQhWZBFmwO2&gh(NA>p4 z*?`bf3}IV4OCh6j?;u&5jvzvv>?^cKa2R6*O;z?FnQFrJZ+PeU0F_g1!{nXA+jQy!4DCA`3JFRV$*K(x@0^%f(bse-uQEFA+x}whAp6 zZZw+n^JuLf#a(!CBSkHg|J{udS3g}yj=@$L3wTwIlel3Ky9=9VYz${b!$Ux2pQN%e zhL}C?z%#?G&Z6SO?ICnH-&K_bcw?5FKo}fjVV4-~X_BH0@oav}S8&)J3cK?=Sea?-u$<}8EWg3z zqnf)mG+V7Z#1QFWKWBlC~rw?UVo}Viwl)(M_?GGG2^~ z)T{MD)wT6BGs-qIQfzp=f+Xl;uDMPuG%UN`3uJeq%>&t8!M^av$=MxxOz>EBb{FFz zhh#jow$=3G)P#Ef0~I8j{#d&PT@dO263dhJ)5$(n9u~)wvpe<6n8ZE@9JHUJ1^aMG z`(rfR1XvYlvV}^Z^p}DUg$>V!r=lPYcO%zQYqwE;*1co^RV)4}2f58LM4MY1kV0$R z(QF$uj<##)nMa$cfi}b7$8u~$t7F#%Tgn&<*<;KR{Q_!)M=4%FLfu%KB~(%O9A1GW#)8h>GmF1@8e>4jI~{}l z%!T-y=?P))7tc_H`G<<2?WHr+!`~e9Lq%Xk2??jGg)qu~%?<;hiEEeB16HHt1k9NN z^RX!$0*Tpg;33&H$Lss8Kqsa}@hKaGS5uy@TX`&aKD?tv=B}RBDcHj;Mq`ngavVYq zl3!0Pi_Y)@wN_@}{kJNX@-9Vw4=3xyW4Z$xDZuU{ZO=mW?$0ScZ054CYm9)(_e~Lb z+1TPsLLmg8)8M%dLXKg_t_4B>+*Nf`CeFjer9d1RfbY1$j{OjPjLE!e!E)*#VF-`< zSX^>JdkEzN*UciaQEV-VV3lB;lnL#;50gUe#?P>QfxJ->aUfSx2s(#LN0(DoCR^@o zfh{+ca_diG;>mZ=kI-~5Ee7GvDARTY`@^!)NwwkTF^WiDGUSo@YxkjCxQ5&dTufkp z0vY0ZuXxe^>6LpXrC@UWDa^B(=culreBo@cY@CsVtd)B|a5Kx6TW-G)PdK0>o(olP zP1<@S6fzyTHq=Z{r~GDwH9Q(^h73G4m#OIa0xgkfnTq}$n>A?9)^v$hspy9S9V5|SspyLWO_b;}Dtf;_ z|AA=@tM6VF#dbd{_lQK_QPCWMz9i8$6`d~72PFEvie4(vO%k=dr?gSLKvzg~sEU4p zoe;9d42h0X(bok!OrkSY^bZ23H0~LK)paUiPg^K=Kpq;%$eY;e2vp_$R zsQrDVjdBILQ=-?Z=uCk=DA5cR9WKxciMmv@w?J1)be)Q}e#5OXOQN@{=o*lCvr9h8K^cEG(6X-697O1F0 zpbtsZtD=_+bc;l*RJ4yk^CkL#iUzUwL4AL=L|;{K#xoGSrv5(v|gfzRdkL(e=E_%4-{LC5a?Ek zUag|%2(&<=nJW5MY?@Gw=1R0sMc)zVNQrJy(LW0`L88A>(Ypou6=tNY^`BKyU7!sT zZBfx11p2f@<33dCJ4vATNc2(_y-1*C5}l%=|Hf7c)%Qk;E?3cy1$wnaOI38YKnF{- zN=1Js(C;xPV)cDYMYjo*W=ss-t)hhj-6PSXDmqV~+a-EZMXwa-Z4ynuOf5V&>&MaH z8dhks3Vn+$6qq8I&Y-C(^r3*pF(^xgb_(c12KiLzVGh;uc_EtxW5L3YH12Gqx#b3D zcz2Etnkt8m@PHU=dhBt~oVG7tq?_9L$s-2wB+RwbjgO8q#j!28la|1dBZ(;O1}bia zv5*ya4lC|q7)Gmc{Qkvj@x6hiwvjZ2y2`uxxB$_X4Kev=k$GRgTa0 zJHF;1tz9S40%d(aq>VbcJb^ZJgO{B_;7W0i|LTqW`j|%ztVy#G(`!B zt{rmw_pMlZOHOz>MGw69BcR1st)zt*>5X01>g*){r}k~{!J{g;M|Fv<>I05WuxP3}@#>8@_OPLgHJM=IO@@8jFdJ=2He2XXUr&tmkOk$$ zZ@LR+V7nCh;O?~(kIE)FIyRn=%Hc3T*h*BIu#$LYgALD($Im1Nxx-6X>ZB>g2RLn6 zxw{}YyoW2S(OO9X7TdmL3GAT45^b==;bcS%X$e$OZYN$;eJ2^1SZ_21mbX#cWyK$E zoxr5D=UIOG6v2OHOv~F}8nP{Tec;H+FLC0rGk*8Kh6FysnUcY9YEhJB$N7}Mx;ty& zqut*P8PTw&AwO_r1Z@>ZL>3kl6iAUVXBHX78aTfD`ynIR1AD@!FGuVRe1xsUU@o>L z`)peJ|F8&Ywf369=>E}lr!8n)~%DoeF(3IJTI_AK(X6)ZnINa8Pn?OU=wqy9N*=7($ zCtVFRyH@cErOxs7;WrNb0s!y~?AtSb90cPTIbNveU@K%5y|ut4rPnx);cCV%Xj14o zDk=0F6>PkAKAdzPB0)3uivx}p9PjMfh#j@0$&QxI{q{5APJeNdDJX~nu#Rvf@p7D& zZ6Biy7Km_YxW^JRqRPvbF|7NwQQ>9xU!J6GQdyEw4nY4NDk>fO z(N^Q8&)6Uez(i+Q4GlYy4wwadvKTd?n2=9Y1uCUu_nf!= zdANarLbQ#}&B8YL*7pyKgSsb|cTk)N>wjhS!j6|%w>Zc4@A`e|-#sd-puM|2ZUZh`MFRDbNpzGyTtq^Vg?P$hFex3TtfZLNqUL>jKNRX&_G1 z%H93y$^OExdT3`fFU|&9wm!{T2+guNPcbAJM><8aJW7CJFr1je9|1IU z?Mw>e5n0kk%z5DKc!sGww_qE>%iqC3J7g(bm)>1M1uDKvwrs5=6A9Ak8zgTXJ{&T@ zaXN-0wM6on_T|rfrwu1lsHgxsg3|*rS4T=LPQ<8O5GdxQ|Tl-47aZ>1m22-5!5X%&Y zPCBre3VS|-2_P!Sxl(+15YPCNX<}%mfQ)ebv<%7OW?{d-3BF!TL-zvCq030P_7JG04Aiy-n%(~+|C z0E{BCxoLT=oCAYR%z;ma=fFr4bKsL~4ovZFJ%UaWHk~va^#ZB)t0=#tP{&JIN$6^L z^yj#PwPzAy_kD=n_k{FX<8hqxWg!~^Y4VH%{m5wnBQY6u2NrL zF-{#}BU0$lsOpty=2?dAO={+2cx3=6W;U(nc)Cp>M;q=N$eswQ51?B=#)gj=V8O;7 zD_@a2{AR>fT zdhHFYMbK1<%doNw*nWvL*-2h(sQy}2NiK+;P}3aS@yGywG3V zjOkmmw)snLj&RgQc+cpnveNL|6L+hb_O%oXwz22leQNxTXhil}%P}nb(Vg0iG*}az zCE$27tOZ*|MC4)$z=BfqB*&XHJv>4d30|ZO6sk-49Y^53M<#=pt+h@kr!^mTK;=UE zIdB;~bI{0tgwpIuC^W=or-6yXuQQqg@d$dzz? zjNkhL)>e(mP~d|R_#og+gPcZ*s#Xmw>xn!YiH{Orf zRz2h00A6?l)v@*WyrTBj2XKQ#D<#(Q#A&Ux_e0Haa2nNsJc=C0VKrl^?{76X(X=lE zs*p))@`rUc4IV~jIgcH*U}c?)vUpB(c-1{{u7KoWBjO9p-K~IzY8;bR90g^trh1O;9#RQZFFGzG;u@x1+rEUo;0c(8aDRx3 zqm^y+vt#~*VVLM88gK5Wi29QZ+IgarortSw11hCvTRXfFafTTm9SF~v6J+A+$cA18 z@v3Y(p0O54{3I?9kK36@dSK2KHyfkE;*dGK(8l6P)oMJ`s^fa5Rd*>;htJq3$rPhk z8*$1N8pcVz=`ctQ`5nDujhAuQbRPkYj>WV1O4GCIgz(#<#;(_(cm)+%(Tq_eyhpe`R zk&C>ZiL^vSqST&6-t@yF*$i_k1XS*MB^`&}hLf!2=vN7ufum6x4hXz&n0pe)TCzGZ zY!N0B+p?j`J*y!SqS#I$r1m88Ia+{sueRzOywc0|ksUAO^2m-ePgm*uo&@$}$39Ht zfyGQk5{a|rJ=?MW4~sOO-wQWb4`Fm_V-sdx<{_Icm`~&IVD;A8X%KzNsglS~#&j9+8;iI?zD2Yl>zm7iSxx$oZS|J9 zd$2zQ4XX!l!32m7IlhZU=I+*uOo#lR4jFN17eb~Mc=!Difp7b~A7}(;AYqGIT9juR zJNBTwb+Wwc{+lJ8d~fIvm6d7Cr=t4FqHxTNBD7K|8YEDyS`Pej_m>x$@O}&Swq9aY z+Wr2gfrg<+)DB%xtNvF@he@?n=`prCV+J~GU@tp%^j1aWHQzMj&~Ea`s)g_wno~BQ zFvNAXjIh&5OYA9I*-QyhlX&cyEMVp~Y0p}09@zaxOu#`Mor6id<*{Xqc-iD=5Gx4m zD*z#-DGkdA#Pm46S%dMS!Dygo^!uzx$FPL?OOvCSHZV4v%kx{dK7y>ln5H8f4i+>W z0ZWtr2qrhj|0!gN0n_kHVcBJK%40(GmX-F#K^mS#&7RA(!A6vwFq;0Jk;{(;>0Z|_m&zlUPZ?_e4TLFcIL7jt4$%@9?}QXMvb0`y)!l+X*VovxoD59m}!`9 z*aZo9b(er6WIaiEtBXc^MFJZd?u3ZSy^A4(LZv>W#y`3#6k45BbN3VYreCC(g70)A z4=2vx+|-T2OoJj!`HbmW$&?sjx-i02#h3F2Qu6a?eBa5SQsR#$zoiEI2SIEA=^ z;|VW6VR=6LM8(_av)L_;#VtpdcQqEDAcSvF;PC?;2F6PAD3+VCN28A2+l`i;P~(xg zXHvr!QRDouT&Bj=jmDXj2%dyx`LyH0vc-cO=Epy z*Rf9;Ux$g>8;#?;ac4CCyQ%T`>!(=QZ1f?iZ_E)@6K@HG^sbQJ*;G*vr;$O?gAo?K z5uR{TYrgq1GXIn^D=P8U{N$9ftj4d2JqPT>Vs8AT5v>qq=_F$Vu}tYv;&(kroKNii z!VgO_}!5D-f&cI^EH~*zFEgeL#f!Li8^8%Rqw+uLFO-h zj7)l8m5a0doZc-l9b~0g?j<5DlSKnRgjVjIE~Sc?#Fj1{v?RI=eGU!Kv>d;hVQ%st zRN6+INIh0=sPjc?p^>C%n^5*Dj zn2kKB6+fJ`qBtPDlzL7lb$m}eZi4gym#=TCXn<)N4}JV?Q==z=Mtm!frljCa0@;0h zH^$7S3+W?8 zX%WnC939wN>tOO*Bo>1$DXd@9jrw<)iUBO(X@jDjWCiu=yQz}U8EoMnCVKvL#>!UL zZPj;E2F_8FMzUyk$s?ro>L$QiT^(UJ9TY}t!w*9R4sxONk^>%4FpVeev257@?-6CN zG^WS_(oR#8p^S=%n#b^(SF=h5So_pQpEn`1+KuBos8t`BAI3idb6tFNKO^#iYpw)PD$Uckw{1KL37%G$G)9$a!| zP(puW==Z3bq8l@w*|>q5g6}NQvH01-HZ+RhT_<#3yz9iwZ%8-5?;^oaA^rngv@g<5 zy3GDJZ&ZFxe_bNYH7wg659>pxsDvlbe7!?@g-QM#6*C#Ch6bV^6JxUodQwWi!^FxP z7?B|Tj0=fJ-KI)0_}!ld(Ppe=;*qx}5a&{uIaH%a59$yuh#oh%dm8iL>$rj{CLPi+ zKEZK5Si}6t;+r z_A@pXtbLZ;Y}_!c7AwlVhLRsQZpL3V*L`z4#fOdD75PL1%`{k0lWO z%F`rATiJ!(IH4&)rUtPU(M_v3;~!6xR?p%f8TADEr2%WOSB-xzIyP4mlV&WL@Y{Cj zNJ=-F*@-y2(F>N3dKQQ}4l^EQ(h4B0+TV^K2;;Mx6B{YLxmSkemjY0gRB(F3tPiBtMvro|^ zZnc%lYC-E`e*vv851EmUE!a&=H)e1ZMEDoVwWP1^Wh@qD@mh7p!cF+Ctf`S1QLKU= zT9nGf>DF${2sC6ce`mZtxElnEt=??RCt>@n9l?J`wj>gbwUAr6=kHSxP7}e!&d1_%n$>^U{3qhou{X1)cvlcjOfwLAkYk{*CIBS8k7C38x zvlcjOf&Wzt3?J{clLAYJ=P$PpAD3@0$aNJt^X*<|NxrM3(C&2=mgE-Mhv(a`815Zm zzuG?B+c%0IjJ|GM$$hoq-mC2;ZlB#*;?@gS*>gRGdNCNifHW;@eW}yF##K~gA5~;8 zaC_{z_9BQY=jA!QUTu-v?$z^F*$ZF=J5=8ax#2jKREbL2bATzO1dZhpRp zN}28{2sy0I=$W#m)*>4|Q|v9#nOu?p?3VCLNvY(6U}_rpN8| zjuJVGTqSF|8HZ_-t5|!k8xG;}+VkCOOUU15^6Haf(ju-Bs!1U;=sYi+DAHE*O1R5N zztb3Al3R?n@{#wE{j^0+-&(h4jon2pS%B(Of#kKYb%Co;C!1xVfr@iW*2Be|B`lj4 z{G!2oLPbsTR^@tf(OmE#W*#lexz5L%En*zj`q0s-$?B5Z<|6M(QV*_3jbbly7NB!b zi;&^9c|}FeLi9F3>~!lsR#~1doyZGS;nyYMR)Gb4tKjv#)hGw7vj&<{Ex6yI z{YW*RQ(NNl_;eU4&*jO}UCfmV-6c+KdVZPH<8yi8nY@>IirqRQ7^h~si;MLVS03*` zJT2Yh_PO)iMS>-}WK9Wtl3b4^YYSmO*D80ZJ=cdAK;eM(P+>*pySXLgRk2i8u(Ol`x;4F6esIPH-ofMMLYEk8jPF95Ot(R~OsBW&}(jq5CcybCE zGWiGqlZaS=a9T>O2E1ru$gjQi7JpS)hAi9I3k!#E?!p6!_M{A25axpA7db z-mOH_;cB}N0GJd0{kmQZrY!Xje`?}J_z5`<%G+TySQq6AUs5vEuuLIaBz)Obn_ zsV8G3MqkcCFGcsDQ4-Z*zQ59G_vWo~=IfYSXtE3O{-tRPYD1x{U*U-uzN>IOf@>eH zPjP8?hC()6H{)7|>segy;cCZq?p>kKRk*IhwF=j5xVGckgKO~Jq0rU1?!ommt_ECR z;YzqC6q<{x0M}Mrzr|IL>o~41TuXi)3SD+zD0COTzq~gTGUI+0F4FHpq)o&{_y0H7 zQ$ohu_-@11_ZOki6}V>O%Ez?@*F(5=;W~yZ33^a@SsuTyNr%_@Q6& z`<>AL7qC|>WITxbk8phlxjzMN2%ZY0rHT78*vTh!%n)fSfj@!!Qe2(5r!s7~27+#e zy+^`!Q&7hDxR>S2?;9ac-plVv$lGlmrlq8g8Jjk4{Dg^^XDiT{nNh!bOYIug}QL%3gBAjW^w_O<9z^V8ON8 zosWh>zZ2KJ%DqfezpDklP2u}B?lb=YpTHqwd9?^ye#`X#&iDRqvU`%TOz?d8SSWN9 z_f)RDh@aBB=d&Q~C0q@UN3g5J{c5Ey?|`R!JN$?A-0XC1-l8n+`s{gG+Pnn|w5*Kj z>Du%e8CjZRVU{*K-J#8=w`k^EEjwe5mcAIdGqV<_Ygw6D>DtW2C@d>$-XeS~&eoP>F)`^Gi)U*$ z&P>nNZk(No*XY>auxT&Y@J{y&4^Hh~r_rn2#m>>r5|1m-Jvzf#>h`*5mOmP!KPCtu z){my)%&as`IbL6`$CsR^73X4Dz3ejkB?x$k+$=QPS5WE-dx01o zlP}RU%<#%i<)hRUSQ&Y8*MrGfQdW8@52Z$Ua&kxyUVIJ}gF3oyNUZMrYWy@=U;dpk z^S@a?uhWMzQ8#?LFk1Gg5&9~bkRz0>zke+^1a1auhJKJd|R;%IXdOxiqae2lKmw8ZMZ zUe;bQ`bKMPjJ_`Reba7$(^=M;$V*40t;kb`_>1SB?2pohA&tg3nPx}YM5HZ2S_*MF zBDTpF`V!AF@T>&S%oIZol$a16`t+jxf49cRh~I$hj%!W9q9+bGauv3 z4wZ-6>hTCq8+e+)Q-b%dRGnj2#xN$5V+D3O#w6AbO1;X2KI9W=;BkNlzXTqU6XPZG zGoQ%<{t)mhRXmTu4EF*51Mmqd?vCL10N(=~r}ZN8^$7kf@Q;CCt>T^tei-;a&rp5` z@HmXMr_(Pn7O%Dfk7^TNM1C6ZE6;#u0iOf>VpYHFNPZviS-@Gpk~T)ClJ>g?cs1}M zHGfP#!-pa5S>PW6zf;9$uzs*j_5*6CW8j;HxdFChB6f@^4VTje{664wRD5$p&%to` zSAj2A@fcnal0OA_7x3Goa4x`(v}M5a?#J9y#mOf>i1=g$c!oZJako2<;@jK7a|Ape zcISD6%OPD3f~We|q0k*FPt4YEUFkgAN#N5|d>yZ=9ckuxO}pShoF-9m(sfou*Q>xY z^9($sM>=@i;OVLD2rmWxGVs&+b}jHDz>8HqVw_A-JJOy4KKwVBccU*y_2DTdrYMzh z7(DYH3Wa9kKFsr9k#_9>UIhF`6^}^|+bR(UX|@BW;~nArF*Cz>8t`|3N7*W7ZWzx3 z9s-UdH~3 zSz&v`;oa94foH1qiJfmE2GW6kk+kg*G1kd>KoQH%;`uD#UBE}H@(Uw)KJatuu(nk3 zm>G=0jq!0h^-z4&kFE7qskzjx=20W1kcytp)*clo5##&pz;_$QqOh#lx5wTV=Z}xoV~F(to6rCz-5&*ipXWj$dsG`Gu{OE_IQ_D3pB-4k ztMXX6u@^IGyg4*Gcsq#q1+4246T@X=J`vk5j@5&Bl2AtzUQhTV?!!D}?}sDyo&uhq zfagh-2Mt2wm>p@$fDgh+$uCr$$zDez{iFgs$95r}AttHi#7>D1>#`j@+P-f7y9m0> zj_7g_JQKmw)0}{GF@R?P--P>cIV58hmtjS6!a17u1bE0S^hb8LBkcjC4Wa}&R$tGJ zN$oui_|Jhy`5bTWS+c!}Zx#6Je}K;pzFP1F!FRg8Kzcq4o`E=MDfOg$2a(nbX{XCa zI<$c&6+AzrYhnW0?g#kDHdDZNJNU-pK5QEvhMDb_0sj^7WEC&qwxj$Tfj@d0ob-JF z_zvKuYJNV?$yaT=4}9N)ujo|16_U>mtPOlKaPAgAViVDKI(#65`vCD+`yu9n=Q*_> zkw4mQ5c^B ze9RlCpUW%*z8LuF^xFvh7T}kv@|Q;Z;{o8gXUM-3c;8FFbcDjx?0@-L9c>2BBa~(;>3gCl*--mm(9b@i@$k+;=TJS`z-D3X4 z^duSEfnWAkD8x4GJM*%XD4{5j$vI_aDSqt|Lwm!R@>^H#dSJ8W)|7%=HQx-D;-xB zuA6Y(f@>wN0$giwxpDb$t;1E0YYVR1aovIIPF%mh^($Nt;o6StF8zB6}sC;pz6&l;p9=DWg(#My75Wlaid0HbNwyS_5h_O|#V% z;xk4wX&7p>LeTkwra9LeplJ_?aB8D`ldX>`cJN|b9p$6^RPH_nF9zKVT0;in$G>NzWsQ`!H)-oZFR({wnis5A zXg}yGg{}hqh(gzZ-lx!af&NsX=`9d#Xhi-4AZ-f$FzA~V`Vr9U6#5UKpH=9mK)vPk(kps!Hqe9*HM+66jap-Vt-QD`sdhZK4p z=v@k34*HlvZvma8)cT@yZNlJMXzc;Rq#P7?2+r#m>mo^miFohls zdOh;5Mm~z?6z4w!P3vQ#C*j*Q%2sDby@;L)I*@FuqxCM)4tzhCVymMyEzt`>znf;O z`=gL|1L#fTY;{ixIv4b)@wPfzn-aeh^zSA{{Ams7%cex=TS2c@=(V8lRQSt5->cAm z(A5eZ0KHA2e+K&33jK4?nUf>s-4D7&p&tglU!m(jFH`70g8pz)B>yv@k1F_!pe+jh zD(IIK`ZdrES4Z-{0s1P1ei!sR3jc?ooAEmq)J`}huSnVWeL(#_+i}EAj}xN5P5SuFzw_uc1Epgml{6pg&H> zB8C1sD&oxa9SaTE-wKISW{m%kN8homH{eFdixu*yKXDuzn)s^^(^QMdU#w=<=0RlX zwVeOF7?YL*dUhP2Kb;4C3PGPU&{ju#wnUeJetEI2j^_PDuLYeq*H$-L&>KNNr_hz4 z^})6}+CwD%0O*?YZFRIyP4q`_lx5f0>XwQ8^lyqJ!vAUChw%GB=Q#L$km%om{`u@k zd5?kqus`O-B0v3$C4ZV`t4kL2PS6)({zZFpl>ar**I&rv3DIwXJ_q^l5d41u{YX0I z%7Uh6CG+Rl>S!%Y{GWi{hw^V1^xr}M74|wV=r2J3E5lYt>k;Dr3UmwXajBp?K&SSx z)zLFD!oLF@e_f#2T~({SO|LlMOY6AJOlJy zN_}nwoq3L}j^^={e+B5i(`|L7f?f^!C)eBRXzfOLDd;UzBl2$pJsy16|q6 zR!7ewiT(xXYWUw`fjHY@b3qm27A-G zo8-R%x*7JNy)UBQ1wAtX>m@<|6ME3^@*r93k9gLIn{VN-v@Rh2Pe6}FePsD3Krg(& zR<}ste+B&}%A++r@qY{YWwfvKSNgY#?uGwN6ZnJh`*g%Nkof69lV$?lj`~V}`y=Sj zhS=(8FPh|y!}o1U`SiT1MyXFf&`-g>(mw`({t)d&dt#LTV$con?F>V;E#sjT z?fHAqe@V2}%@q7@giQKnH98|e`aS`CKkP02`)SY@EAi+h(0@jKWPjTOdJ6nqwtoZY zb76mJk0YQjL;sQVG0-2uzrCXT&p;1Cyp-{<9rS;}K4S&`9q6ykk$8AF+UEuMqwGH# z^uNhst2-+A>0btW8S$=G&=$~3mHsdQ^a=FWr2-!c`Y`-|ouEg8eggKF{xb%2#D2ZB zNuWE>Kfn%u?X6u4dRnHfj@I#HznP#nK)!6RH2BdQ@sa+p5cvJ@k4b_*3-sk^U)evF zf?j*Bt?pBS)4!qiFzo-Npnag{!G1FY{a>Ijfd9(&@Pl4}{xe75KLMR)wbgwn=(|9B z(Z14u?gf1X>@8`{<;@ZA0F-)gh&_kD=YUf}UY}Ozlr+1=%M&=OQ<5)uw0vI<9Z}C= z=a00aT(2)VMa#jdq9PpF&VgWdfIg?x=g~YkXr8aJ!Et_hT50_B)MIVMn!36x_3<(NP@CQyzElw$(r7*9FIQ;zYJV?5;; zPdUa@j`5UZJmnZqImT0tag<{mwN_Bb9QbQjS#0kxDsIDMu>hNTnR9lp~dLq*4ybK>Wl` z+)N=Y2dAQ)o}7GFp$qR|c-Oc}b8>xdmxfdO`5w*1KJyBSe0gO)4ey@d%w=AwPs{VT zOE)<2!iEM2vQnA#Qdvuk;e|76r!sS=GJB^ogQqfMr%q&Qv1U$XZJ){-Kb5t9Dr@;v z*7T{Y?NeFfr?S>hMWg4y)k@rOHN4C~?#kRbCx=ed<>W9g6^GN{ui_4D_y?rGh8Aa^B z6xMrDA_Rl-YDFc)Pg+4qujcc(iV+No+-vbVkk8G(b8<)&nGy#TsaI2(9%orkwas0p z;rL^&MpY|-B>plG^o5I%wz=#GdJksGM@H{D=3v|kMf6otgjbQMw7gXwjb4gE;Uco= zUOK|h+soy3l@w@2?!tB2T8|4aZ6J#)ueekz5LTpj#3&K(V`wmR0jo6Cnko;`fbhRY zFGxWR=IoSKLNZF-YxzCcQ3QGk*rXY#DG5cZ^<<8f>!_{pj!+TtQDG41^5zzmuFB;? zp%7Vzy#PUX9;Z+DusTy)=H#sO!gcAGDCK5_d2-}48Rl9!Il1giWDdP`$4Wpv z*PQE0sT3;I<18)0D?lt(QyPlG>v+rySbOGqOIfL2Cn;Ex3j&?pi$dwmFtP&rzCBVE?vWe;%YJ6SDBI5&!3$}J1Yr=BK)|xTW!4^3nSESxnFkSr{-W zZ?@pWw27~qYq6h9_u`NE=%V>N$s;~H*3WkEJ%oGtXBRiw_$D2QkLE@7;7gN*AyL}ol~GtrKZ^CbxTi14_kREbD5`4! diff --git a/files/bin/login b/files/bin/login deleted file mode 100644 index 2efd82b91cf19bb8f7edab161936a726729174ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52604 zcmeHwd0x>V*1Ts{*$?>+_43;f z1<#`e#lv`U~Iuz#cZS`wjIX($xpVl-*q-Xf{v2Tp?iAsr+k-C|tYmkVor4RL&WX>Zhf8#@Kmd;%yC-JDeUIT39O z6HV1em#=Tq^eadSi*XSU&`SW;2++%j695&V`57TCJE`*bcw+vEf~5f~8Dai90UWfq zfJzIVEl8e|B=(k=DtpTWdz<@HX`Y}1Dq8gZzV&UIcP5Kv+gOaBN}l4jnBXv!zB(($ zpWOya#MD(~8(XYZ_C%i%GtSkc+Y3vQJg$m;SDY(M#>X8&LebnWjplFGJJxH zCCapF-wiCFd1_cEAvcGU|1lw=Q!rsCTu2KylIqOVcwbX%=#~x?{JI76d@bvY7%q3t zBk;r5S-rI^<}o3LGhRNt+Wusg@sAER?&ezWJ4{%~gq0D(7$PjsGLA$O67MxvvsQk4 zlaG9Ineo<4=%xAEWA$VfR>Q)^3SmL7@f71LYbE+|MxQUy*~UGBy(KZr=y#E@LyHB* zdzI?lwy#b3D^(c)m` zI{YFL?n9}h?#~GUji+8C3J1r#R*|}hrS=k1joW6hEy);3{g}S$y7;O-G4wTeY+8`WtUvT%Fsouc}|F5=G;U>kvf~u6dZs7QZ~EI@>aTd5nLb@lRxg z7|=QD#v3m{+lqY`FawNXF?O8Il%xVcw9MM>S(A+lX8qtV2qQ5yB0KvD>JKIe4pf$b z_Ie^|cWu~@MYBzYZyQsX{eH0f)>|~amuQQ$unSp*WYy(bb>-)%*(~GHBvwj*etX+N zdn?%?INi6tC8Q^DII-x9SVXW4i5C4L-}+W{}{-e9HWMdh!so3}O zpFyA$tqRgWowP%nI8^Vs6Iq62Ge_1k+){ax?x{`MQR}z21_xGUxB2V#G{(l%#nk%u zRAsk>Q6g|tBBH|}}{I7`cNsrsxI%%D1Jb`9V zu`dZ)BNqq`+lcZbg$!;lhVfg`?LgKsbPhxH3lG|xI9i#VfKU(&!Tvx|O@VHR-X}`p zZkD*-s(E|()?2OmNYJF~X`mqst@iekPmA;m@Z_tF2}||a+hPyet+ZGVPEGI2oIQ71 zkN}JJ!p(2DQn>Q=B$p^hB8>&^hko$H z;$zaw4%!?QOy2bDao?;Aw3(XcXHl9Kjna23nl(4J|x|0gswjrM{OfJ(H{#PI(b{Nf$myM*!Cb;Z#bV;o-C~ zkC^R}d8SFNQ?`>h+v(BSMmWzS&NGF!KcZm^Dn6m{k8A@W^@eDv5gY6w(q}+Q+J6MV zw;zS^a$z=^>Uk4Tk-;AL@zvTQ8dVbS8p-=mH193l@EQv5EaJUNsAv;mHLR4c#cB*9 z{6)aC3v~qJeZ^Mz1VR!*Xkzu07{47uRZI2E82@aeE*{mMeU zgXGmetFlL?Hpa_{stLRPKxRUyN)jHG=|dd%+2Md@hN%$6!o7%giQ)-u7HRk;c=J&>#pDo*0(c7J5#jV!sa>)TpVaRCfM7(55VF>?ClY!$)ZfBAyZT-EG^1` zp0T%hLgkLF(?3=LcD_z+m(=e;lD@y+bdehHgi8TWK-gjN6YL$)!aqHU@UV^~{4f86 z@ZW}on-!(c2XpWy6v}UJMmWv@;Ox-xU#hn_&P#v)Xj&DJyE+ZOC)|aBwNI2J&)=`5j zyTrKQWvA>BInbP|S`p2Zdc`PP?)nxPJ^CBi5)&|)2gK4^QMl4*+%I}U{XINI^(AYF zzZm>Lu&d*@TXkcDgG9jFVn2ZgE=>nkp|}s{tl=SmvQJRn=tC^-H{hA!TIZ;ECgm3T zHRVPj_|#2GjdA~JGz2eeD69Nw4AZ!8qcW1EtW}uLv{P4RHk*GdAd)3TU!lK@CqMd> zt3}Gc$BvxaS+91~WALm(->&oIUjb)sACJXj*t29fBCAX@0e z_1qpj4+Z0~AG(PS9{Zu|#Mn=9QXSJrOGL#Fzk!^;-^;^|;$-xY$(r{P9^Q$;2O<#M zJZZ=L5RV|MvaJ|?{GG|Viy0jMNaQAn6spynvh^K@qG4@_JBzrdg}KRflp(;^ARodl zh(j+1Sz6L8#rsLZStKF$Bob(-gP|JQP$Hu2$Nz@7mk_ZMp86%`m}1pE%HZe=J7jN9 zgA}EU=kcwW#bJdg?9Ok{YV>MMXWF!+-(&DmD<=4%C>vx_mXnaB$g8srREw&$EJjJN zXrzA09zT2qX>z+}wAygs@rUM9v}V5C;9pI=o|OCzIf3xjge>qR6%A4~N$ za-xy6`%Qm#JL=q@-4W~sf1HrrrpE@4Rb_WD4LKy!(EL`@-=`|n&pudAvgwcOHRys! z-)C5rZ#|jpuJSNJo{-(HU(OQGIB2Ie6%AWCEa^b3h6g_vfd*SB1af~acz@XN+&vWr zsk=MybE<9LW?-9{0aPvcaR>)e*c@H7rMV6%G)o-DYqIgQWJ7x^EirYpAO=4+`$p6{ zCi_{271hk^1^P!&30oIlUh~5`qv`sA49iDJl14h{vrGH;AG{8%GJOleKb`K!zM8WElSd>4L3G|2^X~GSGwfh@wuz z6ay1t@#861cWeENN+U;VgL{F&da;WMnf~@ zG=X#C<=yi+GnB;4>4CWaD5u>RNO#W3*J3l?{{n9Q=G|PK)Lqf!F08i`jRHC1SMa7u6kyA80$aGJ6&NUHob@)qY3os2#~aH92@`$W!qx^5@P9iqvdJL z20(;DuX>9Lr>rQ`T37k!D2(?mDh!Q(w`8|$u^UOd?JcxMZ}edArm;}4A8ZV%*nX4P z4#U7=m2W>`xk{*C3|q>$f&^bkg6)<8bTk{wGCg;>QPN<-uA#VbphpNv+(ONBKsl0_ zWYx(?e2tTsVD-rfY^Tn|*GQ|YJ$IcV3(JpXLCZ_0D2J~x_Q$foj1m$~RtmXLaX_=d zK&axH#rv?eVH`!|OE^+PK7=700*S?EU>EAD_iKAEK_jL~ahCy5~^+ z;>Q#pE)=q`a*Tj_w@^X$y51CILI^Y(?Cnr&vSD0|5CC^o-IT?Zu(%^X69y0~Zm?r- z?4BDFgxuhIY9L`$Uk-Q{!v*ajRCHW7$i#ZNv?PO7qH)4ZXccXk6lyho_77GlDk7dM zl$3(b6w=Y;RF%n=mm?G>TmXg1`r}wU`3~9^NN%Ev za)TGHwLa;#3$V)p9kDl5wK*mEl~5?==ubk8v_s{)m$nVDMG6^N#$SYOn&Uyw*!xv% zmBfC6_a_+poQf@$*h41vu!@~4u@9Tr<0>{oVz-*u78QGr#Fm)Y9(xs?|AApSe4#nZ z#HOg&cO-VWi5;wBcS&rriJhuqe|}|( z&&1xLVlR`}4JP*YDmFo4mzdZmRP3i1iIX*^nAjIp>?;zRYGU6~v454=9ws)ZVyh+A z=qasnLdBL!>|qmo#y+Kv=1J@$CN@RIj*!?pOzc1v+gD=COzc<{dqNE8n&+F?g(|jQ zVn>?T0u}qb#GY$nSE<CNGcB90;Yht&n*j$Ny!Nk6<|_EsKi=K>{J!|Gl~5SCkD7J zWU1I&B=(4jEl{xwB=#{AtE<>i5?f@czStJwD>cC?9o zUd6sBu_-3@brt)2iT$32))Xh-QLz;gd(6aspj&S_I?u^hfTk51inII zH<{QJ6?=xn=9}0nRc!D};TzLU>@_O(HHjT)VzX83;}YA;#1^X9J0-S-hW})Zauw^5 z*w;;Ljf%~d*zG3vcPe&_#NK6MpH#8uORU$#?oqM-#Gswp!gVJ0O%>Z9v13i_$13(E ziM_zYwyD@{65A0g%iHgOQb#_Cec!}RQ?X8otu?XvDt3m%{=vjPpkfC}>}C^tSjC~gfBNh8O2IpjrnI`sxihV<32b6d!fXxG_gli?7vz>c^8@3&sFRP5>^S+r{VgS&o$`)&gK z>3**Ts~PdW;~j(QV)pnx?AX&B>wm}J`psXfd^Vz>)s23>Mw;xNQRPcjl4$N3?{oJ1 z0m~6FbR@)@xQr@m7nDV-U$P zk%L>BJ{89sdF7_*z_f&I>A`VTvoZZM#G=Pnm{nCL;*5_ZN!oEXws^Nyt&fnLKz=x* zrRfJjHNl>;0ZVF4emka?DTD0EtVtZ|;=-r{6}BpSGB2SdSJrO)w0!-SnimKC!aGy6 zauP1X1lBvUCQEBJS8%y539}%r>Ta?mrN|Q<^DmJ?P$l}6!6^A35h|iVQ!2~ia9J717Q@b0yUus4N(H3O9yV>{v|iwoD-h((YE~U?_s*Q zp&T05klt7euF6jF9k*4!36Cll9@QZm!$EsHNVH%fE!@)5C6=G)JT@xq*m%rgQR#ee zu-1s@Es+kcGD#$=46DmwGFmuoOrj0R?g(is2>B#zxC>_BwG_Qy=hDdyJ)Hku_lc<- zjx2<&M7c>T$(RARs#o@i4eO*h`gxzLI|H*E~BG)rqVS5P;A#c8qx)_Gxx z7FZ&XhKONW0!1{JS1+u(lMGCxi(>qXTd3|F34!J@ETuIs>5lD$eu9Y>w?5auGC0qF z^u%WfKJ5v6{@LIEE>6DmgHy|_l6IU!{8gPv{qOGizW<=Q6?OUkql0KcJ0fyfK|z5j zGWOIWqe%Vl@A;wspjQ9ha3CDC&;KqKJcGGdJUwH>jsFj`Fs;^F-4D$_y6m)2j|zxO zx?zE#xL`FtZj^-6Vh-3%d!g{nN?Fle-DTaAQhWIwZU(kcvT)QTHZ=^tAP; z%4|d(i@&lFy9^W#E1U4(uiH|24EO3vgCIJSYmh$WFH-6Z?-}C3uAc`4JOjH6OpoJg zf>Gd?={Z=iDQp9Ukd#$zKZdIjYqcq%XDFx8GnBD$<6t=Henf&stZIt`_8sf6?w2yb z-n6mz0T%A`6{o}mB~Sp;LG~1pk29(A1LVPh2*;bd&2fHIe!)R9zhemdw2sUT!+Jm) z8h)8n zIc^q*M5B!Lzlt_A=&0H(_V^ED={^gQ+v=;0MR4!Tv*)}1{=?KP+q$5BOkB)=k2qg# zI)z*-odL1NHP1%%in=aHs+bxuj~U)UeBmroM24a{3O>9s%2xIVn{NM=@e<^ zfd_Ph;ly;YQPFtfC^pT|?_n3Lg?GVPh=_VstL*qhM*0WMw;?A})Cd|YP&CGVOyjsZ zU9CjK=2MH>N5>ZwO~^$fo_jz$mBQG_OIe3851bu4t;+5Sx{+k#k^nl|zH8vR7*b+4 zvQPQ?Oq&(z@b0JY<^RJ&3y7+EmoUxi1`+r)5f3}u7n*K>T>v{ zs5b1~f|$aYwk4Lgeb7a1q#YS(AMA@e8;2lHqZcbwWoe+UzXe@g*q5NqP^ckyPt9X9 z6Vr4QGdxN{U3*J9QBvrG1{0lO9=lFD@JNMskuU%>B~MKE;kjs*a&21i&))Al%xwR@rT0F9zdb+YbzNGxGiN zfc*sG2}o(=JGH@HgCO!%!_k#}fEY?(W5ePa9`9bxWO59AB0L5Ls~iKL;4v`8w>1bl zDOeiQa3Bq!-n)GL)@6E6B%-O|6oP##*Jlc1_q~YS_k{Es<6#`O0VYF|lZj(2E5Km!&J6MqM<~K-)o=$ceHeb7S)ljj$Q3ly!dtn-;|^PB;!9W|y%UyPCe@MhxE?wT+(%bA+Qd!u$M=EmrD&yQ6Nl(CV9F!4}?~ zKcm{`LYDjj&M_=bpgFY|cVK$MLGnmg3!X(pQX-Y zQF!ms383Y<*2&~F=fe&tTu46?E`yyBjr>O{&HJ^`5U=`#^kX5tak&-Bwi+Msx}}Xg zdjQzW*MC}sg9J)J_roAV;QHvl_xr8Q8s(v&4@J<2fc}tr^Vx>2ISCIe?8;~b=v^bB z0B8RR`wwUTCt?+nrLvu9-{lPjrd4suN_^6@wSFAg?vz1Y6%KPNj)FY6s-EFBPRfDGMTdH1 zT*I)d@-x^Ao`6w^@Q0{4TG2uupz|fBVxX67T#Cisihbo}wDLsfPZ3v92b4>7Wh=Zf z%UCDQ;vgsLQDkC&u%T5!yegXxkxW1fOc*BQ;ZOjJq-}knxWyP676<0=L>rSQRjaL7 zRIk|gSt)DPS&FH{N8C%IljqY*8*mnv_Ulc(X{UWfZxoc?qQfggJlsb>qr>|gUuk$& zmB>qfJUpWflZt&Sktd%W>tF4A??@=L{Oszx6EV1;_j^ACoMo(~J#KnY9*dv8=un$qp~4%hnbAa;doU4}_rkJ3b$K+^xjZ~*Gh&6ssohxoS*Rp4 zd$4@-PE_tD%Nr;WDJ57?2?|SXG)q{QXpMh;5-Rj^iY)I*s8YnRlHSJkwWJ!)Vn4EC zU*8gr3uuB`^Mz>4cw6CviM{oNX3?^7ILW%MuMvQe#Y=}fnR0+h*~^R7!=_MDNvhnqM~>w@L50}leckLjM+;@!PuSOS+swELRgGU;db&3`J~GI z80BH$ZqUJqgVYa*citzYRk@N#8>tyGZ{C=PfGsO(yA40&UnDhd_;Pr2V_y2qa_|fp@XM))i7+xnPZh ztu!rNPA_ZwLfj0UeMm)`{9pknq+e)kL;oWTfwh8zU@#A;-h>5KXvZJcfHGD@BBPLp z&=fxD@Spc`;3+vz2!(eeij&`bAdWnWGgaqEs;dMQduEpc{0$!gsMt4*3J|UE)8du) zh{CNX|EUy)-SCMCsqnLWqOyy^Pnz%1n+m6Or|?!m6;W6;$z&symEDBu#&!Z63EiPX zx~52gfv^sbC{Lwov_;swz1BA{X!}mkTXS0kUe`8W^*DY0d6N8|r9!auj4Ec!+$vrv=uvS$? z$YTykBemeW4F3{?b6&^5G6ZWpZg0}2wTLz{4^Cr6gtMy2P)5bblE;dYW19y$`nket zqIZ?ZtTtoLZZhj5g{JvX1L(~bpch-7QGNGj*_+*jdzSGCjik|=(MiIK(Y1&n6vg-D zS5d>Ynsx2%Yg~;eaZo!K)x!1JOv{r(8RT%baS0-lsv8^6V!SF$A;u5Xrk<0wp+O7I zUAJOU7r~Ms!}LOYr-9;f^G}h3*%&RxbTRK0El69PjI9?fsq*2lKD47NJ;8{jfrqqP zOm%W)#F-!u74(SNXNeDv$o$TYNHG0O2uVQMt~SNsI|OyI&NznzsM;M8yH8wIs!>c2 zYLhOA7T2$H8oT9gp`eO09nvte-xls2c1Tkkw^Bqdw9>VxU(~EB06tGcHhRhOs&3Lz z2h?k!owuN#vW&4p0*8%gKjWU|)91;}>;Ep#AHVTVVx2Iq6THx)%uzeOJ>0uq+~+Ng~1D0uS3tMIKAdmCJj;#3ymMJpXv z#gr|c76&ObeZr^x!;$2{NQ{hpH7eQYc#evCgOj|GVxaLY`XGugbY>Kx7afqrcqlxw zr9Nei>^WzdhK>q4J52r-=m)!xv$w3gpc5rDB~(nEJe}LAtBE%XSgZZ{Q~`LK9I@9L zL`S`qOvY+r@PqyXAF{azNm<7Cd?L7Rn?>pdo|#+7tACo#en`%QqT`#8kAl;J?f>x_uF%6VAxP z*sDAptld(VCSex4u3p?7IC+~ zh)fT_X7)UWS~*U6u6;4zUL0%|&z zf38G2w@Eb8Hsi~1BSTGA`^FO!b?5uVWB_5I*$1V#1tVrA8pmi%vVu5r-eP^sZ6~VM^VptCf7)g~L zR4g~#7Nb^h8As6N$ne?O2QexmNx24m`0rcZJ}l{&&9lD>w>i`JkGq zz-%%0iLnB*wRMs!iHv99b|_5HYAlOZi@LhV5ecP;$j12kX)0w_jw~g=hr^#1()2#Y z#nFO-h2g?sN{(*TD(yI4W<9eT#e0Pn$N4JBW2zh*R{3f4mWm}i`-RQ86h^Ap^XHpk z6j~p`It(_NTPY3%Q!6lC#cKfidHz?86)Xrd5jm#&3{Ny_#he$NVhb;G!H7rDYf?9G z-2XPOl^Sv^v=dg>u&fqS_dGp4hf6#0&5By9e_zE%8nW>pAN20HfY&P5cebW>;4p<2 zZ1L~Ky9jt36fmD~-lX>cN*i#z=s!|HhdUe{Y~T?&&j~i=}eXwJsY2sM|7WI-?atJq zyZz=d3jg!27u_ueN|+MUn5k=b+Xt_P{YZDqgT0xctJoV_gfK$m@~YVwbiEoi=%O*- ze!R-mMOr6Mz%&I^6?>n8EF7zaRy(m!g^|E}UBv1Cmbgb*Tt10|tbU~3VrbV@+~xn4 zxSLsAUn=PUA&U*yRUEy0_2Y$2VsQr+3hmNQuHBX&6DO)R-i_y6s!PyAn> z@s__Hl>Wp`YHm>$H5#`gzd*JyfcKjJVooRCyyVC7T43mumCMWuE1;bU#d|7M{b~f_ zS9?CY5GyB*ICc3P7irI1$NhDe996fP3Tn*%YUo1@T47~ez`-BP^zY;O*=yyI6^D_= z?GKad7!J4~oph)}M%ZoS!!0)YN&!wfTY3MUx;4<)(CuOVKW^_TY~|Q|wEjg$=LJ$O zhvOhK<~$AdI(e=Ir`>=dg((e(TWIm8nvW0$LVBH1hrtbe5b4wc#Qk9~ zz5WXnkq`3>2=*ccP*%JUk3>_2hQ*{EiyEY7NlxAPTjV}(1$(o5pHbj zvEY859MBxVKGrwFgb`7~kVi2SVME@A$Sw>?S)9DA{K~&PP#|9U$Bt6VD(oiGX&Lu@ zvOLIMR5uhv_Q6$1=QXYttaF_M~qPfF^?qreO;T zcWhcxiE-isnf6)!yE{!WvxaM&vR^%l?amX0|I8k*Yi3_z+2PZNqCKipNa(TPv<>E4d-#lpfE`b z-y7j-%TucF?!cY4akt{0Wjxg)=KA!#M3dGqTBm zG;kATO+@nz)uHmSECCC!K#Lda@|{tBzTkR`1~ zTbFR#`p+BeMtw)a;`WB9kM7uvUlDmVkt5z>r<1<_g0^kc8}*I#uW%!=G02a7sp)X5 zaL3nw5u8X=!O?_Y|4Gub*`JiZjy9X!RA1cm-r|n>;!g-8j?LjvJbg$5=bPTcA*i_b zqCQz;Gy0GkJx-~{#St~m3Cm?Q#@8FCQX;JLN>&0B*OMqwjWC-~F*n+Dd~&dnnu5KF z0vby6Ref!J$FUFVUxA5Q>y7vK;K``}S3~{#uXM4n#TY|UhmcgVu>8VKNbd;g?G5GD zIvoBMXIl(6dRO>HctM3~^ZUPm`HMNxDv8?suuEQ6<5ePm3FJg#ssFGZwGd_L6ywiC za$mO+zwbuk8X_+aOZ>hYiPsUyHJv2FPLPK0KhQ^0>N4IOfWmFO3jd7bMtqx}Q?ZZ` z^?8b@a>tU)Z)ZoByIYy`tw+iPBaENZcbQ@iaV|-}Bp@u4qrucaQZ(3RN|iB**YY?D zU5+-#kr`i|wlw%sl)9l+G@y>HH#)bsMSq5-nWoIB_V!gLdqRnR#_m6m68%gDsRVl& znBirj_3?rAGs)Wwdh}jFkKB}2A$np<#xPc~zgUpkyf%@38;@o_Ehrim zW8TOt(?r6t^^MWRuo!nDQYKVpMtdo>oOWvX?gTsp=?5-f+fZ(UY3dJu@O4AIJCS;P zD=-Zy!RsYv&-;7OXEt0wKd~b1{bo+VH`pi*jYhx%LIdKS_9GfIw9ANrU}e2M9 z4JrH#-1{HCvJX{p`tP&_PFvu#1x{Pwv;|IE;Isu!Ti~<>PFvuAg$1rI%Nv|OcA_p6e=d=G(lbHjmS5bC#?eZ(Bl$CB3u-Gwqp~-4ZYc6vGNji=D#; zc(j>wXWP4_Bm@YvJx+H?Zn4ugUdzh$cvh9V^YQ2?blR4=Rys>;j9ZoKv6Ym1Z3U%z zNq$-{ZE9`_fw_5kPLIb{Mtu3U0;mgIcFBs8(p4okr`uiXwhbKM8HB`z?$VNFf)E)X z0e=HL<88>t?Xtrs@4KyTm026nrg8}KQv2&zz5w$r`PtPmlGTNYBzRT^* z^Om~TgfV6AQZj}%tHg_v>t$Y4oo9{5>nyhA=s@u994@tF*+-%;t99qN5b~L|9ykT974hdXPf?R;n*6 zw0X*M^PF0i(_QTHc!)jUS>lqpnQBidf=)nial&G!g^P!>mFjL3b*b)IqfLjK*qp0T zOpi9zUFz`+m7GPck`%d*|;bG)MQ$_K)T+$-05-Q0uHs~ymP34|d zvNE^Gm2b;+FVo3gG{%#MqnWKKb8_`+bDUnZf)zFw)noyRFZ|O>wk~ij)5&HIR8Vnl z$r@W}8QKVWz6bQO!n?!DlH?WUx^q!o@E|si=5Ve?vm%p3jKj4L9i5t4T)hOf4PN*) zTZk&fR^%-3+PFr@@Y<}RBIhzR0FTqTLYoig7+P9Vv_{Alju_F4OzqL8xJy?+`!ZLV zQ=6UZEsWS@zSE-@NjGFg$VcFzxy#_M1<>6?p2IfNX2B!8t^yazs)TH4kP8c+qQKpaKJ58T9JJ>Dv0Ky z1kQX48gLQZid;+Gxri^CHkm_^3-L1-E*Oq2oyoK`u%PzyxRt{hbJJkK2!}RVC_;kJ zr;rPf%W~6?%F_j3_7>)v-VDlOmzUa_(3gTJ)ml{kvWXUZVY7+w}cNP2r zeF)K+;hy8&Qk2Y85*|)!M0x|MJQpg;5(-DxK18%Z^yml)(y=vdZrA`(f++MzQ_Pm3 zAzXP%38^LL+o&zmm;!acLxw!zOG}+LPhO!jU&lZvdf)(m+kOl7-ER$rUdO!!S1)W8 z48%1ZS3a&yxOU?@hARaJM@Qpw;Bw=t!SxufBe*`pW$}hWm*XnO^*dbcxX#r>p^>=e z<0`}T2(H7p3|u`{hC->hrr=tF>p5JvuL*^|#eEL$MY#Tqi}c%owEJ+;^Z(72jHPYj zAC7w#t`b~ZaXpOd5Ux*f#jg&9F2ywo*E(E8|7&@V${T5K<7&gz2YRMUU5O@5>S&VpryuH`F=ic3n%ZgG3O z`pQ+S*Q}*@8ljs!W$HBh^cgc}U2|>b>^XC@=FMN=$i8mjqU&$aCeF#u%>0S=o7+R7 zN9DCmc{bD3`_Co*XA0dzcwSr$yWSDGzL2naS8@O8b6qFd-SHPlnh&c&p^x!Q`I;Be zQ(9-f^O3e6*SlLIxY6?2tCXc4G@a`~OevftZdYFEu=&oiQjd#9z{AipqVoi>W*CL~ zQcq5qzH}Hmqr71rciynFRrzUo8h&k;Uv9f}zBA7W@_dX^G1y1yC6{XBwP8*#2zeTz zX*oG%xo(d$2R#%bpeCm>|8c&UG+}0dtmXWGvsdREOC(|m%{kI8)dYj4&6%4sJsTT) z(`RMcX>QO9&{TfNoHd71@_Y4~?7`H8J6f8apv%Dsj;1x8ZT8;`XH9m@)P^l}m5d*z zd))Lu#)FOioAa5Oou$p1P#(r{tPW^F1Qys z=4NS*1&%Cj>Re>zaLk&6pSjuEbq*GjHGl4OZPC=MY;Do>1vuOp{!5*-^J07%Zs+f= z?p*HwqK}fZ=6JlhZZG_$I2XMy`D&E2P%0`~j&9&C8V1HIyV6nW5{#tXxobcK7c1*Z zL#Yv(9C*5!eGX-Va=I2KSM_=+VUj3vs=TL9rI%soFJ|q0EoPl1=EB7I_$@fTWJ4OA zPTaW>{T1$-{VYpm?3CUK(|mEUj+gg=jAPIBFGq}^V^NBnxYuJ?kPXRO zaD7sN*ROHcCd<5LAg@M|*H|P!g6mPdFL6;U9%d%SXtS&dvAQ-jDNgtHu=a@6*IDCY z_0@5)v3r1=yl?V>$uCcZqI8_gz;(xF^mn+^pAApy{se2tO0}w}3yHZx;gJ3VgAuN34?-wIOW_@K^mT6q<>48P%>QiWo_G zJOY~Bnowvep2IY^M(Xtl@SA~Oq~c?AfWY!y2*jK322 z^X|mBUgcjI$v+eLQNT}8@oOXaBH$MSk3(D${bOf@N#{D~8|kOgLG$I^IQ*!} zAb+|!;!h6HjJoIKG*p+XL31l;YE(I~Tf(~B3w#jx*Ht{_vp3QFBH%|nrLJ^-4Y_o5ssP3*Ot50zs-@Z*6$ zSzK-cehl#4^-DJJ$K!3`Il$-8E`r*pF6hNdq#~I1xn4f#z|Q29-{}YeU+-z8r@hsL-ASRqY*>k?g--hHvpm`oNQT|CZ_6SW2Xz1T5+tXRjGZ7kVBEC|vGZcEF zGtGk$nvtOCvkPl7M5B}|)-7nrrxt;x5Ht(b`V&4iGsbjg&=Kus(BA!AC{)ito?dx+Hla^ zqLu+P6m!M`ciT&$(D`Z}^F%|jA#D-x<-prjJb2d#@jS*}2byi5F=GJ9twGwINF%$1 zbzdUnQjFLR{I7tIiiILZOf$_&bVotA|3~O-pwrGme+Ihb&ixFMkrITEsnRpJ{4Ee=QiumdSpeY8;+|D#gKY9o>_x=b?3uyNJ2#poN z?2A*-P}xR;=KMpaE*sI^1iEQILPvhH8FZzf8=;m@4EwnMdI0!Mz^AGB0^wK0zaRLT zli*2@Cg6Vue3{BG=2iJ>n@KzyYY?C->Pol7q_Y7x5_E%K359M{=_aGjlz6-dG@C*5 zj2hF(kNzU&>7@5M(DXfW^6~P0z>hfzp7eMD`0IgxT-Aeo=Z_KJX#`CZXigS~NRN2D z(wgwv$;WxAz$XKLo0?Com=6;FG~jOqzA_5Go-2s(`M@^K@+;%e{lHHF{xTJRW5hq2fVZE5KmHtySx&*93j9Lg@#3JOe`Z+!X~0*V z1aAXNKJYIAA60&;r)xw#k&U*1<}1*2S4NWYXW%uw8g(yjQE_5xM8*-&ctI1jeiZwd zFa+_P{yS}f(-t^wfzuW^ZGqDkIBkK`7C3Ez(-!#u(gOW%4LGLZI$7a=wjtoS8`m#! z-G}RbTo2-U7}sOCp2YPmuIF*p;@Xev63|ynYhWPyIe>Q#F>M>8%?G^wk_fyI@aqbE6W|sF&IR1-(g=M4 z;DHLf0`PPNrge>c1=ay?QeaxA-L1g*M;EnY3cML`%4L!K{D4O*@Ew313Vb(Uw*u3g zv_^q{3-~bw{sZ763j7e@&lLC(z?RfV{!akDT!CpmTdu&*1OA-?*8*-=;FkfPJ0K$O zFyN62{089p3LF4jroi=pA5maBvvF8~KLTthumQNo<&pe82b`+FUjv?^z$XANQQ!{1 z&na*W9P;)-5qUiTf2-in1UyH5^8jC}z}Equq`)@AckUN_(mXyiB3L z3vjGL|4YC>QQ-Rk->m4f4e-|r{!f526?uOJ+@$D7XM*lk=$`@nuF@W!1AJ7e@0S4Y zQ0VsqKA^~Z1+Z1g?{&Zdh5jwTH!1K@z?&5M2Ec0-{Bgk36n#Dce23D0J_CHeV!tl{ zFIDKj2K<5o(?7NRTO~h$S|a)~Gu|UeYqoHF?xB4Tf>Z^z0Pnjp;8+emisyX*e-4=T zQ@MY@-7_rUuz`=@{($dJ4>)LllwkU2q+S{xaL^to!9xMRH#*>WRLUCzc*~f8<8cX3 z20UhLz(ISNL~jTD==g~LTnjk;ClPoaV6Or%1pFI?elg%}3Y-V{=L+ls{4)hE1^kc# z>wp(u70K^bz+WhE1>iabz724J0#^h6a6*LtZonTa_+J4&Ux9xQc)tQa2>9KJ5&pjd z9<9Jn0IpZ)sebQS7pdQ8f!{DG;Glg~vgdxliR%LnY3eFQ1GYUI!GBBifK7Y{;8c9K*&Gk_h(mv@zr`&|8*6=6g z#YuS-A1FBv`q5u)7^Yf^{KaV&%?*)j;ct}MCpJdg0{Bk}Vm#Of8rA?VJul#(b5R8U z67XMViSZ@D_XCd04LF8L{DXkA75HJm^%n&kbWVrpp9K6>a=<}n!3eg)1O-4c$E)?;S;6%WseFBcHlAg{B{^FW|;};UX5O5IrRZ7?fI3FqcEDkmFr7Et40~NB;qid)>=kg(*;V471o*+F zk@8Ii95^@NxK`q?0X$<~z%gFhGYjzBsJ{skpAC5M1p&t;5`P2W<}(A1Arik7@Sz0( z2ki+{elEaQCk7l7CH@w`!OVbz<`smebB$H~1CDDX>;t?R^;s<8+W{|uKQEE+J%AID zA3(0ZhXFs{4|6Gre;n{|rTotTeir%Dc_+&6MZk}t{%9UR@Ik<9uMIfp+ycR`0iFeW z0?qP20{l4gqw_9=Zvp&a&wzu@6chXn;4k6Nb0z+tfKQwia4eQ^9Q^L@D4&V%1^6bw zrhi`q_#x;^8Iruq00&S%G*2LSAmCqQ1spWaCpaDOA?Rn;|7hax9dO(!@jpN>`ffhd z(9TBu{t56~7e?}*2DszufMbKC&jkFQf}aQYiIjkY=4F)MBEUs?0SC=12wn`hKgw&i z2Pfcv!oMd;{KW_ntv3k!SwUX}ydx>%&qINqf%Zb@%Shg@0S{gj(a!^VT`Avcz{#jT z)4$3APe6HT-j7^8ra9IZwkR9vLo%wLiWBn(*FAZzE+7p z=L0t5wH5v_0Pr~IH&yC07$Ka#1WWG;P<~eeKN$66wwK|6UsU4bc)({P9+~ZV3gCtz z0mnQkFB9;UXiuj9+yEFg5^leFfZZs+Y2Ouq51{@`xD@c7X%T;43-~ASM~{?W0eC&) zzZu`}0Q~sWNdCVB+#mHZLeg&oJP-JrCHyepxrm2TB>V#WV=vl+*}k^}|DrYEcu(T# zquBisUuq=$65xL-?e%5A8uZ;G@pXW2hyULw;bVZuA|9-kF#Q`=S1Il7W8kf@hw0y+ z1CG>J53LRGNVI3L;~Z2^?K{A;(LU&057pNXfG(Akr zezavyZ;q>^O!v|YWqMf-UiQ(7ay{O(biCPCj88$7=H<9NIs7GpoHDOl!@F*H`<34+ z2TfY$a9m@@(a%`=8ACs#>1P!EjHI6t^uu%+^h8`^h+_iDM*jj3kbc#4(aMMiR$J;uuLBBZy-Jaf~325yUZqI7Sf12;vw) z93zNh1aS-}j^V^HoH&LP$8h2pP8`FDV>odPCywF7kwF|8#F0T98N`u692vxsK^z&x zkwF|8#6b*1Pvk_+3TZibz1-=}$#*St;bRt_6|S=N<$-rt^UAzhp1ZVctrH)>(13!-3^rm0SIBUo0~;oC>1R3lA8SDWW>;f6=0~zcD8L0cH*DP~#=*_mA9ClKVGuNG02*>r}`vc-- z`?L}C24~tx6U<<6IE2WTC?iif9@knYzyeoEzLb%ZlZUUFIK#Af(+;0!$brk3lyZ(t z>ng#EpWZ?ZAFF_jW%MBwEoVmN+$obYbLLK;zQFFtaZH|)Y0rW1)niAeQ9~=s%kdWK zB`ebMR%>W-)C@IzPa>~uji#5#7di`am!fiR!OQZ%6qn{Z>FzCEk>l1@ddl4R%tt|Y z57%aJp0TmI^Q@hs2Zd z@U9}o6Vk)&TuA{3M$N^C1G=$L?rIJ1>*i`?jsl1lUv@!h;Ga~MT>hRwH;nmU^sHtl z7nUocTS*bVD?|C_6}mP0!VT1r(M|Wz_ZWm9xIC_s0M)p{59-i=>5I0!x5;Wc{&Y?GoMSbQK zma+~AD#)sgT&rxA?#hSzqCBLh+gVnG55jOK6h;#g8kOW0i6neogZ+)2CC^jFT6&yi zkX@2n5~d-k2xlJXOCJ#;+j2FzJi-BTocX!lT(n&(#%g>Q1l0LXwi;z9jI5W5nnEUB zUv32%xek3e2Ob@+LaJRhc$wzSgB&&o!Umif9A!)0E3^W)ldVi2ktojftUx!8kArw~ zm*S3^<$Iy|1o*fAE(9fp%rrd4AI3{otc&L}z&`-_?-KlC2*E2Mv@X(y=Tyds zADTDO-$+~}55pnqza zkELh%M|5=2Jkg}HVV%VWy8Dr4{@LUM{%Hu-f#lJ6u@-b*Qz@j`aM9llxJX_)0r(?2 z8f%(=gflNLt3fNczVr#YqIUB-Lk5!EZ;6z)X>x(je(#DN#Cu@myLJ zZ0=QpGnVrS(4~UFymJN$OazIZ$?TIJu&sGfUg1`Pc2@X@C?F3 z{E4oE9>}IA74P&=IX&0D@5{%}X`jI;A+?Am2~QlJ1xs8~{O>=g*?w{6rkn2k%j%V)CHu>@matz<56B&i5=BroTitvvi`!AF zycm=_An>MJR_-UGA?~~ z+rGqx30Jd#X7HYL)Cwi+A$~jMQwQWv1#%XU8UD&dY11&2P3&Bm=r2yPgvN2rcp!(O z_!2G~&Sf@~*-+-BvY?z8kX0^sp!{secPN8^v4)1?-KNA7)j_!xQYC+-Dk*b=a%*+A zCE#j3FfZYu^iW1{u_Gv}FAzcJXF)k!ooF>k?7Pp}nUnBPurf;033<%uAgHFx$y*2E zHy|em<>c{l5^EBNwip#kL_@J4+X2ZLl#{BnBLP>^j@oS>R#ko~`RoSTzT5TbP6&X5 z$%g5y_n8LzfRrA(i0v9kAq zmP**L1sd0o-T_y4(3R{z>D*C|h^i71)va}ggK`&0q|jP_WtZfGCpy@V9cZwo?+0C7 zXmm>*dO)ojZ{J-Pl)JgfG$PStO(rt??pl9~J+QBLLciQ)ft-Y`cf$?-7II!nPwO0P zxFbXE*z$p{+<7K1ku=Rq#4adzO$X_*{@9@QYkDuGPMWU1Ye!DnoAU$er0rzC=8d-7 z(2x9;9qfsA_Cz~8aWoAXL-zzsL?886h9zZG@W)P64$AG-D=dMP?bLQT2}j$eGM8|k z{fAEz`VUOBGW^2m9ieLj4V@n$`E(`h{nzNgyW{JsQxMdeDEsaaR6p30H1O`;uSSor zTVIzSXc%8d8Ny(>rl6oeXKC%9WejQH*xqkOj}Hg-8HsTG{=mBdS2&al(}rxl>3^dL z-D~0M6mC}LZlN%IvD1=PV;#VyJxz%6U+P%dFUrm==w+%bM%su|%WJ^c`3P3tuP8M=J_8rF|gAX02VETe4u55`wAD^8;`3?oVEkA`5N9j3rmOerxf7+&>Nntl6iok=^b{$kFM(zatr zMbJJp62I&mg1AfySz?r>Y49lx{rlbcYr9Ptc z7r`6rOs5fg>Q_i*zgR3d&BNKhS*G`3*z8%zQsRAa^34fRtvwbDQNFB$3aW z8#W9FVj_Vp%`0nozB`V|v^j96F$YFfZ4TVYb70DEn~-#p>GUu(fK}_$2v5dUo(O~Mw@-vSdrX8o$@{2D)36I-=de6SHqBx6erO(H?+d!Z#t2KD z8+}DReT0p2`4Da0qYKSbul<~w`S7ij_Yp(kHXF`$(cBbhV|`}_UFhmzP7q;E5N*eC z-l`3-AoJHEhk%6Wv2OFLPJfqU<3z0Og4>h#V2NmHxH{6}U#wa~Z{s^Y^g6z6p;z(k z2>k`$meBJDCaH6v>yy)WzD>FA5P&4%N1vo-Dh%63T^Qtkf(MDj4v zTzs8pyN|yk45#W$`6Z_ z(Pl$mf%`5&=wtkTCE#e2NQNnr4n@(2fIdN^`C`@CX2%ONyEIk-3?!NYEdB$I9~S>R zswy~Z%J!gr7hO?Pbu(xY4P;~6Z-t@Gwg>QnLI)Mrh{9QIci@GZ;m|CyfFgEVnpePC`U=*b3V}(4y`(! zeO+h*vmY75-u@g`igi5&)CJ|b9x}M8p8Z&tKz5n(8m3)4K7zjx37Cb5c!E(O`$l(Lp>`t3uZ5JNN8c~ba8&A=~*y7b$>81)x6wysO=$gi3h@iixYz`#~zF! zQc$ri4}9@P)lF=;XM6lQOSQ|kY@G#DyY(mrqnLRa(=TP^4sivV2C9EN zi)|#!0j{&C>AhD?Qj@r9HL7C5C&bB}+3O_r?h7eb^$H$m_WUG&2#{}vN5FXQK1oM%nBMLUau)ek_D|k0D zRE9#Eaz5>mY#r+_4qJH6+Mc-1rq+w8Oenh3gsi;Np^k-(Nb{&T)HeccDrWmveFG)7 zK0wo1xdXwEexoTwj>@E{r;zPAtw>V8Y9rIi3qfEqEeLL)rn$vLMGK$=ZbDwQ^DY9} zW52gn?DB*zMlcvVB!Rt{@}NF%h{30pLt>Yh*EUfM64nK=gy?43>eV#DQRpg`rRP9oB=g1OA79 z9g>3SENTwkLjwkS<52trXl>t$wLOJImFS||cpz`Tzq8U2a5cdT{6?(~LQJNcq^*$Y z7%w;Z<#tQw$|fz!Fx-L(TAz*ohsab&^w@k>(?e6WavKeuukE7JD>!r|SDvF`W z_4pZ2JK5@V*%4k>8+kinX#x3_6ICa6XJ`@Xf+FlkJ;x5_9I`tm zhtR-KLk!LY1CG|Pzc|qnn#7IaQ11~r3qg!+n265_1w@xdhr%{RWyesTohp#h+g=aF zlL4R+0~}Hbg(Svajvk5Zh>k33GRgfZ_KMKACv6>u?+mC)&PU_oTQYXT)sHcvB&-*t zv*^ggcH`DWe6QkaoBOJ^o2pGgHA*v2vnf~_Ds}=l=pX{hB-0r9)J;^mGhb9*s8cH> zb{31BA1gLmb0*b%2{pf6!z3cTTaXH_H^y>}`k<`~q-!*NPI3OFbuJroyRM!u0gV{K zRiW-R6{A+k+_!dZvoHY!@CC#!~@b1 z(q|&?4q-ZIoVnmw1o>xstSKCL!@h&hlXAAkHM z*$;)ljQby%$wNuEzp{JQRvg@Q-?$AsLfz=0iA*7E!`RVVPOl)LU*^IKv0F{UghgoFX?4o`W`KjQN8cs&(}>d(-~?i9Fd;)k ztw1CokXwWL*>xTFbXa}AyOE%CQhz! zG=bwL5gjxliZ?_E%J5Av;^e4~Q6RV*dqi?Ks&zB5@+e23p$77By1Iz;IJinPQWL4q zuk6*XIwU_!9nF65i#gTk4WZC1A*8k|4)YNE?wkY=k$gb@mcf6fycF2`&Z$5H<}8d5 zltT{Ve5pxTh4n%wvsPngA>rHtbT59RH z$Gj$8U4y;6g!kS5w=C4`!YEefKV;d7CE0&sp^q#)&9q%#z_g+NhG|>>M;kA6rD%`u z)JBx!yC@*}*i-~$EeE98Eb1D&u@#Uo&2|oPev61`2~7btrXdzgLbR8}*jnhmGzkDUS_^%}=pX7tQsnWKMncsyP)u)s zp9cg}H#c%J&vQO$rmVaMOrR?DW~4g3uTaW8ig5_@9ooO3sR{RTt#kYhHO#(Yeh)Ig zbNb|0``!7Gudl%tO~Rp@bzj#&q>-uh92fdtzat_i8te*2y@+m#(*PK3^5uyz1i2xI z7GIhSu%%sKqoTgG55ez&$Z#Yn`ViG0tt*N=)mKWk;y8p8-cGp_FKi92ARr_2)dq;h z)doN=-hsv#V)MR*$TV7=v*P6>?GHDR4U~fQ+|*k>+!`|HxPLbrqAqKwHTg4`d-2!? z>$=EOjv&@DT{M*G-R8S>HmqdCSfRX%w*bZz979pbcdHJ`U949Z+A)NqBV9V0<~h>J z)fJ$=hY{E`OOAHM%bp6=>W z#xX}6Gtz#Fgewiq>424vl>=-7O}1b}horm^`laD`9-bn9qv5XNI%;jc+r%xPCs4ED zC&3&@;W`Y_wzfKy&>D9VpGRV=w;d9(Ph+XWeg?%c-=v`^!96b;I~{yxK$}{ppb~Ck z`FOt%?BVaHi z(5`uDn-EUk)awvaao$mftLSXt;(ztJwVR~+SklKiB#F0n&t+k-M3?lNxPMF1%h>sV zVlkV@{&uH22pfk9+aU+eqTz}bcalCVc+e>z5#M4Yrk0_-L3k4*E7Ghs&gkXsXq@SC zSQ*QA+z^G`S`>nHZvgC^iUj2o*{AVmW9Lv2<%A9B`qxV3Tn) zcAMah>Y9JmM-yT*XW^6NRYH=92yGJ$F!=d? zM9__henrssQa^Txn;ZR#;L99+TA?|G@A@X9iECEUb#GPr0Pfs+tw{DbG^C*)r?OWa zs~xrqoq-m`r)>}(c^`GIK^17>z|gry9wqLHui}WziY-P-`s@ zyu-&y)TJoy5oBFB>N+Tq0{k9rdrs9kZ&N0EBb#|)*BA*^?3>ad8)bK2GK3HUod)MR zIK)$(*b+esKunv%l-VUPyRBeHSzhe8p(Tc)yQ$NKX`xE$AO>nACp?>qa}r8;V&J+_ zD{R!Zmb7G*sCMcFbSej?q)=G>5w3sFTJq1g&@?prZA7zIqkXNh@=g15@|#sh5(@(Ik;dX9@2x!4So|jTPY&Uxp})A@ zLThE5+R=PKSL4S*4J8H{C@BD2ajOl5x(dxJNwG$i zh&DE3$_{Ulquc$Jsgi;@L5GE|4kpp@9B^QqQtk!~l0n-S!i7;*!jTQg*6hg6LAoPJ zMBldZKojQt8x{i%odsTRI4GkMH}dOwqY0(}$h-0PWq^x!>79mE{B zfg?|HM_2pTAgtFVSKrf$-(}g=ejk3b)iuaRl(6XFQYSh$O7s8|&C!XHqD1FKiE5eX zd_i=u_6Yu65!?+&V^Lxhe#KP57O8_Eo_n31t*?N+x9zLD2I~bHYu~66baW44WZ)fj zH?rYrR&yH%lZ@*P)Jj8-!_7OvhEtQu?cCn@akH#8b~mr=YK~c2I@R}%f&2y{r&S@G9Z|ZPtGW<4dJ^{XnCvjkE|S$5RQUy}Y*I^~#G;K>uB zj)RnAigm794&=*M9W-?rhknPSAnsMzFkT#uwhekVp_(eyo_Y1>??KaKoo7sc`>cmQ zp+t&W%1Ws7HPbXDILc5NQ6_pH|8c_^6m2TK`YxhZ?n)bxt#QXjRX1|H*ihQGIg!4b z8yqA!Ge zZF7|ku4z1c{PX5UZz9bw9KbXuhi=d?dynC2jHCHH`m&0$V{Kf5okl7{rxCD#(1Cbl zKSgttE-how+NxP6&=uZgwDobs_kr`BFJ9e1E)pI{t_eB z?_MSGqfO%N+b3YYcF=zm%Fsusg*=W^4lbl!1@R(x;Bs&W^KaF0R$QvZ6ggXc@C#Nk z;I9J{eWzOq-a|%0XZ}l7vI(R=!G;zMUX;|@gZEGcK~X)0c+vgXY>nHX9t<48V3gc) zNOMyAGK`402u4h48H~j`Yn-vqe*0$fgOI^xtjIx#giup4lrdv8%dNulAR86n_Nj?I zpeDCE)upeHTMy0E-G>&ysJs9Hw{CLvJr($+p*tJD+3FKwyM)GfToTZ>uIPrgZQWJ0 zfZMFMwQr=V;OPILguB8Lw`Uvu>6K6h63$Y`(D-W7jh$yxuMn<4BymBanyUFmIGk>I zVK<=+L9WQk}E| zg<3Fy9~$UvvCSs_){AcHvAnaR9P~I7OcG$*Oq~sGhSh0p#x0Y8y1gOsm$mE;vs3g4 z>d-=v*JfH+>SY|37YhZ=^m;(5>R)&d#1Sw8GFlbAl$@2Vwqh#;A!ufRKYc-G<)iPb zdMQU6pdqNABRQgH{7P`(HEe90aq>|2Qkdn=hP(TUo*Bnk2N*e0or+bhIeLD^EkgZ= zqvsU1{LGHhqNh{nKwVQyNF5~R$X$y@6f53aNMke_!8*9!~Z0gswWa@@l6pg&?xJp+G+m2YDW9+l34~wlt z3Upl4+@$SsF47$xlXRSVeJr`?9$<#tzTvzcl+ct=mb%_A0mQ3oSgV$q)Bw2SgWT%~ z4XXB6p<@d*F>%Hc4gW;IPVVa$V5WrY=;|Q&JZ8faHm0iQGPk?IE$D9$tujZvQQzbu zaHYSXRL|3r29m@eF7W6D@wnd#3S6-*vqCL|pQzYnQ7#j&$mIvQ3?d}89bc4d24$;D zsISwW1{p2n>6NXD>oiT`*EvX5D_S309;m3_$|N0Iu}PGzmJ1cM^pAKE71j+>t29}f za&ZU~@v2K!u{WtJ_AI?dEs0ljqjX{<8giJwD?v%=!3Q2&w@u9y96CZ@n^#wNU{twn z-=~jQ3t_>P z_;Z59<$?ENZfnyjsRo;`{fcFFhoRhU`#fL=HvEqxfMaj$W`7$yF%x*;CF)=!1wYJa z_F(YpeeD`X>N`z8G>_@8j0MX@%yQohR*_h4?Th6vzMZA|WgQ&t12f8XTX8$rHhP;K z{>FJ!aC^-xus&@gbU#&@tySU6Z(`8k6a#7+af~Ir`V*oa5lc-Jy;9?5oQLR7O&1FJ zme3}oZy{!4SRqztkH(eVh5PU)@z5}sayyo!bZ+)G{;qa!+bGLn|H;wg55J5)AO+rj z=YzoKL*5BA(xf%q8ue)cem_J3ej zjX>PL_oGo5wp#G#{4cOcd*3-3s2kIud6;uKEFJg^cwoQ-10ERgz<>t^JTTya0S^p# z;Qs*+I8Xn$-A*SxJwIBJTj216f}vz$PeP}2i_>}YB>n*>s^=CQw>Y;DGzsv{{Nr|S z>6^!vn+1^o}TCTXu+06p-_oJhy<&Mke}0vaZC zk}Rzbl>rprU?Xu-LuIsK}al?($ zj3utDtgEG8{}BJSl=eJode_U$zrWD%KQqxig?G=7Bavx$N1rb>SpPNS{@wS69=v<2 zU!u{p{U{Ro81E!ke~6ySde$3*vLkp-)&vxhmk4>*2uhS=W)}9OS zFTL$~aApr0sV6*XZE3M*lBdL5m{&S!nWwC@ywF$b-84xlDcs=k0@yUE*i+&wEq9kG zt0xr|uFjiO?#<&DceyXu>r2a%igOD~oa4qhFU(o%@p_zv<<62)=PD{)&@K+B2USNveRj(G)m)klO966o5aB4!t0ZQL;sR6i>xR6 z`A15h`ybj@?(sn;Y{Pey63x=;J**DC`feMHy0WE3OLC-ZU5j$0MOj%= z&a&Cr((F0QawK_ijx;}8mNKb$j%&HJTwZXUwETwUQuZPg&(2!5Sjv&{=Rrl!`%)IIFV7TCx|#v-i(_buQ{TK$eZ?9@N2oS^lKrJ0Fi9W$UP9_L;UF);Yrx zxG>GmR?QBOu9 zPTDrn>En_u#P4DhH4*;zk;tWHek?cceC9@kpmE}{fybz)Basv;(0MKJ$Hfhce|0~| zh~`qzjCdvzc@pmi%{+gc^&uusj&tk~A z7r0M$MBFlcDMboh&r$ju-z_$+}g^P{>nf#w>}eBVst6-GNz)(w0<@U*cQ z!(*-BLG9^;?_LHCxrIJdHU(wBHcWn8@xmt&(qu32^4Lsw}LL+Oa~w4 z3y~7h+zXm8Xt0A8EIX(Dn(DX?`c@b#d0nHE0G}dx% z5W?>QJ`IObmzeQ+qP+-DXPp_qPc`F9qxd7hX9JI2=%}7b6yFYfG4Q>`1MyEpBj4GN z{7ZqqyB~ZO@V@|lhFQKZD!&N$=lj9$0{*YSpKq@3iq?Mu_inDJIO6OjH#fIoL% zBvKTE&u4rp%G!bV0RLk%eh%lM9eRuspCt6VH$lh4x1I~EWrmz7z>nLHf5_9!XIoUy z9N-rNztW7i3PXs$5BR%)=e%n07Yv-h-wXU_z@IeNrf%6|g*$AOQ@F`J|KBf!4_JT4_i>suEX{M&)=2EIH7zuv$n z+9YY|;YbA2B~$(R2L4juD}m22<8O`1&jS9Zz<+MWTR&m~C(4R|U;BC_GNxx6jEs(b zHK6g}+9uBx^u9sybW1dzo&n9{{m@WbHh|_c(A31}veVF|3;5hOB9S-Ec#^X(+BZ^$ zNYWW^_84QWIfg%G0KWkE0GI{<%I@}EWW}F4_Sf|f>ilYA70z91+O)~4SCW?Om z`1^ss#*DYlF>H7d`2E1gv^Uu>H|nbv(0l@#Kbd(D&BIYU`Vpz zY2f?H%kzML68PT6B`4(O1OE!}&-NTY9~I*#`D!O31pcKL!4K=K41YACrDrz~A3b{UYEW>8JiK;2#G*$IRcF zW$6C|@W=bWJ5lEd@FVcIjxqLA+g%fl^)P5;(DY^_@o)^2q#WSy!@JQ2#A8>K#}v>U z0!_@E(fWJVlX&C+f6Lz^5#Dss`#tgS3LY1OrUEq2faZF0?htE}g%*7>0y?7o4QP)y zL?Ua={YTK=&~qJA2ij4%j~YU~82yh6*-jL9faWAxn zs5=8RTS0TH*#^*%j1|DW)7*C-p?KK>nhwyAjfSqPL_9fB^Z@YPz%Mo9Ne5GYs|C$P zxH;Yz4fXw2&@2N@e|?|m679%?-$F-vPXXN{pu60xml#HQ4VVM`pMg&^;|rqw#0UIq zec(xtdx8Hu@MY%uVxF3>hX&ET2)fb7d(4>x-71~V3A$F$<$>-dGr!sJ<1*2fR6Ot* z@W6lv20Sp}fdLN;cwoQ-10ERg!2bykB#xeHHUvd(bTXWH?Mz>|z81C9+>mOgQ^!r%`v$$1^-gqViM|O#3TA{7d5N@M}eli}3poz;qr)Fs&zk5Lch+1Y?4y0RFG|`b;|C zBAE8|4-SHlAb{XGfKLsr&wLWU1ZUy5=8XExr!<(>$ukq{GwCdf@HYZ}_DsDkEYd2# z(}qXk0>E2La1r2No9OAEDS6Zc)BgD{Ofa1@{mcY!2mF)?z6-GDtf)NNyZh1v(|WbR z1U~?{#038eaGO0^{}I3+oAAE}JjMh+1NevuegW`FN3?z|;HypW0l+6r^whph%cAW| zaJvaU0=U*_-@(#5fH$YrXVQKk`MV9jubA+(?|US@K9lyz2>&VILnb`&cPx*U=QP1x zz_$V(j$ewmI8>-J)gK1*O6b#36>p>8 zIB3kDY#oooE_{Be_2W2+dL2F*zc`Jb(|~b6fKROflhx5roMe-7A@YtS!GE~bB5efR z1(@1%IP}>D_@#06nbeL1(_jDn=<51R+KVChM}VhJtD{HVbpz@x83?$qEg;QJtt z_AjY^JoNqVk@cC!G?@OAq;Jhee%9cx0q=qRw4X@yaiBkSE^@L44*}dbEvkPa;0xhj z3J9Vf0r+C@pRU2lfGfshd;nj)y!uSa>s0?iz?Yv>pGoJ?1pgMlS6zg0PQyP5SiYb> zbEO7T9{Jv+DE>vjkHJ1Y{@w!o9`vRCR^sUBGw2UfMS!d<)=zz#ck3 zB{%_k)4y{;J?kuKu%rSX4|>Y;gbxFrF()ejAAlp!cZr7Y2K-CN+pWPCblGCGuO6R6 z053wk&eHHt0&da#?*M-X;9tRBJ>Fgf{1fC)I)@3}Z;evJn2g#8!Iz?=eh5Pll)e#EOzPyc-fFZ}-}jeb7h zpCF!S|DEWU0KVhm`b;`6BiIG_PAS^{>j9rI_0J7}509K5=LVw*K9|Hag{?_3T;9ZE9a!vl{fM35fn*Snz ztKgr@HT)3h_Ym^O91VUP?Qpx z4gB@?x)9}+XGiKw^k`xpO#l4 zl_@^*I=FcA$~H+#iFVhqAa^y~eyi6OT?hg{ zK;=?V3DJ`>(5+nZc?*lB!t$chjc9gXsrYrfi4}Pe_fyHmB+BdA(3_FD6%uZ}=1OEy z0XUVH<>tYf!ZipUQZjdSDXi?pJNc+sUcnJ4j4xt?iaaGOEpM$?$`iu1W>(5`R~LCi zlNFX1mJ~=urE4mrjov~ZyRR^>xJ)Y0oVUKPi23E=Kbi^u7O>G|H5m`qFbMybyohwD z!M%aS6OXdejp7~T5HUHO$aFVQ3lbKM)tgnTE6DZ&NGCcH28M;@xkY7ba|Kf zz4Yew_!KYOOl|3QuP#TZ(d}TW&BDC7Xn&6u4QLsTD7QP8ufn?N3M@-N-<3QSWh8}! zdOc-DXbSH2R0dHcxd;qmz%=D$EVbN23YO#oKsG3cP|5(PP{Ix^EEi4W_T=aKa?!C! zmx@9*BHzPS5_91crA&AhoKL@K3^Hn5U#a13ts%-*d)G?^UJr*&nYT2rIJbN~uGf`s zD)!~B#xK0hziah3pQi$er=-;9Nn2B*q^(v8i}EKT(9^u7tYw;Kt-HXRTkMh2)|C3v z$|y|<1iUMy<&_rG#cZU`@BA4BN(@nwSb1QaaNwRY4bIvJAb-Z-doUg=9$N2o;5QYI z{y|#i&lG$UKcbUp9kdGbs{{hXhZjnCrSVv&qxI1*J(#!Y!hqBH&DZEK%!+kW70yWM zUHcFnJv9F$endy>DQ6Yr;$8nZwHN&Rx>!XCIvOWytHeEC4QxLixGzBH;kA&1d#EU*>;bB`DlB6HjVE&@L J(~nN~zW^VIGDQFY diff --git a/files/bin/ls b/files/bin/ls deleted file mode 100644 index cffd9686c258066860862e3b616371ea5bc76bac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52980 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

=NEw;e7|9$XnPVh#jAV|H%rTNVMl#1p<`~Hw zBbZ|ZbBth)5zH}yIYuzY2<8~U93z-x1ak~$j^WHPoH>Rw$8hEt&K$#;V>ojRXO7{_ zk;xpH%#q0)naq*N9GT3K$sC!?k;xpH%)t!gCp)=?LR!8*hp!ssh_h{44vs;)y*c@w zLJwZL@GbL{=j8g!Jeqt{1gG`$y_!dS=M@zD^OpNHymNxHmU-oVEzet4ev=z7YG{B^ zK&CKhrf8nwrbeO(GDRC?iblv3t&k~NAoCKTt7!X7;Sia^BQk|cWD0l46#kGY93oSA zM5b_wOn5>L{I9f3_$W{G<>ZK4c{mP^EAMiAo}1ht3p}OyNb}?9d`?atUZQbZ92yP{ z;;jI@3jy9jzGtK5OrJG(@}yZgbEi$4?^=+vVAAASt{miBdCBPXLbpGsJTJ#zq?ayB z&s(XL>wcOPQoMQPt2Dh-p1Ui^T>_ilh_l#0O3Lz4g5T>eTbAS1mix-Rc#EV!D}YIH zYF+c^oo)XRDD=u4s*MR(G=5J08MbVTvG|8^cGQIBQoz*sXrG}%J zxf-ih07>RcK+qR1LTz)!(eqBsl#h(QmBP2}-v9!D*uf~_5P`C^?x{pWq z&9?L4jj#f(xU6ucw!-Vd%Noey$tx+>3Zxa6dGH<;67e2}216H!O0(9iJVXP+KaDR& zK@H*aOe>{~^0F1?J=jqMx(?VZGq5R%qSZPx$C8z7E4=zuOg;{;)Boe$SQM-!uZyk=9X@OmJzO%lanjXXXfx+rYHeDRCBK^ zXDKYy>n<qc98 z3VHUwB-gi0)6(%yhd*}-eqnR*TV&n&@jjH6ju#Ew>4l|w`Vt*)unmQ~r+dqUuIcWg zoB}W26w%TP%lzr(3`r&c8*Ax#cvA9W!b(KIf%XMfPmZ#c1xF{N=h9en5H~fgtriZc5X&PP7l*La^ TT%xXmLK)a#&y)B3vH1QMbD1~7 diff --git a/files/bin/nice b/files/bin/nice deleted file mode 100644 index c6a0bc1f587222b6520e706873384226eab206ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37640 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 H(&+vt5Y0&4 diff --git a/files/bin/poweroff b/files/bin/poweroff deleted file mode 100644 index b1f673d769650b755edc2e7d549c52d4efea1122..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37640 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 Stqftkwi|13?VWyfy8i`bsq1I} diff --git a/files/bin/ps b/files/bin/ps deleted file mode 100644 index d67e1a9536a2b98d6e7c08bc67ab52c89f52f76f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47928 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

w21u*msNb**;+d2X*5v?rGPZZ{>3Ps)Mnso( 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 diff --git a/files/bin/rm b/files/bin/rm deleted file mode 100644 index b43a78b86cf3d7c369b5462c0b934ea4ff76042c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48096 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 diff --git a/files/bin/rmdir b/files/bin/rmdir deleted file mode 100644 index 85ab8ec23cdfa3f9bcb18d6a962a75c94e2782f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43588 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 diff --git a/files/bin/shell b/files/bin/shell deleted file mode 100644 index ff6806178daa80ec5bbad7730afb330d81291234..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62076 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 diff --git a/files/bin/sleep b/files/bin/sleep deleted file mode 100644 index 336ead5c33526d678269aa18be98dd70c22c6fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38084 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 b_qA+#5nl2qDp0a1h}h-(5?`kNEV};$l!ipt diff --git a/files/bin/tests/t_abort b/files/bin/tests/t_abort deleted file mode 100644 index bfeaa334fb326c0d975f6a268bc7f140730db5b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43620 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 E0ro*~rvLx| diff --git a/files/bin/tests/t_gid b/files/bin/tests/t_gid deleted file mode 100644 index 63e903929f17cd3a336917776f3fed842bda82b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38172 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 Ie3*Ry3o-&gI{*Lx diff --git a/files/bin/tests/t_mem b/files/bin/tests/t_mem deleted file mode 100644 index d002510590c43faf6959e7c809ea929588ae9ad2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37604 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 diff --git a/files/bin/tests/t_periodic1 b/files/bin/tests/t_periodic1 deleted file mode 100644 index 490bb35312ff218ff01b23c29fd6604ca2ac2386..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48076 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 J!asDr{{=*T#1jAj diff --git a/files/bin/tests/t_periodic2 b/files/bin/tests/t_periodic2 deleted file mode 100644 index e82564c622ece6502d2ace2fd5825f920f878185..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43656 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

Qhr=^l-$Mi>Own)Z~~{7j;7f`TIF^H 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 diff --git a/files/bin/tests/t_setenv b/files/bin/tests/t_setenv deleted file mode 100644 index c18206125461ffda86c3041517fa7db3b3622262..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37984 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 diff --git a/files/bin/tests/t_sigaction b/files/bin/tests/t_sigaction deleted file mode 100644 index 954b5bbe299ffbd6e7663919712c12b20b93b234..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43624 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 IUnbxG0HXdD_y7O^ diff --git a/files/bin/tests/t_sigfpe b/files/bin/tests/t_sigfpe deleted file mode 100644 index 02879bad78c7399e30a58a699495f25e4542289a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43596 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 Oe-!f=c~4&^-~R%{inxFP diff --git a/files/bin/tests/t_siginfo b/files/bin/tests/t_siginfo deleted file mode 100644 index 544f5ff43d45f6c6e8a98ebe9d2631bfb2f59238..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43600 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 diff --git a/files/bin/tests/t_sigmask b/files/bin/tests/t_sigmask deleted file mode 100644 index caed7e91d984b0068b0a643e52c5a855bf870e27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43600 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

}L$*-ZdM~B!#EtVa8 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

_yWPOQDB4Po;X(@Rh^}|@? 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 diff --git a/files/bin/touch b/files/bin/touch deleted file mode 100644 index 42a40e242a9b7a798f0eb80eda66fccb6204dfb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37676 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 diff --git a/files/bin/uname b/files/bin/uname deleted file mode 100644 index 84d5369a62115d7e9edd66609342d198c1a9172d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37608 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 +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd = open("/bin", O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("%s: cannot access '/bin': %s\n\n", argv[0], strerror(errno)); + return 1; + } + dirent_t dent; + int per_line = 0; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + printf("%10s ", dent.d_name); + if (++per_line == 6) { + per_line = 0; + putchar('\n'); + } + } + putchar('\n'); + close(fd); + return 0; +} From 0c6a38e18909ec06076b7d231dfe5e41f776fbf5 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Tue, 5 Oct 2021 10:08:49 +0200 Subject: [PATCH 17/21] Update scheduler comments and improve code readability. --- mentos/src/process/scheduler_algorithm.c | 160 +++++++++++++++-------- 1 file changed, 108 insertions(+), 52 deletions(-) diff --git a/mentos/src/process/scheduler_algorithm.c b/mentos/src/process/scheduler_algorithm.c index 9cb3994..656389d 100644 --- a/mentos/src/process/scheduler_algorithm.c +++ b/mentos/src/process/scheduler_algorithm.c @@ -12,16 +12,27 @@ #include "wait.h" #include "scheduler.h" -static inline task_struct *scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) +/// @brief Updates task execution statistics. +/// @param task the task to update. +static void __update_task_statistics(task_struct *task); + +/// @brief Employs time-sharing, giving each job a timeslice, and is also +/// preemptive since the scheduler forces the task out of the CPU once +/// the timeslice expires. +/// @param runqueue list of all processes. +/// @param skip_periodic tells the algorithm if there are periodic processes +/// in the list, and in that case it needs to skip them. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) { - // If there is just one process, return it. + // If there is just one task, return it. if ((runqueue->curr->run_list.next == &runqueue->queue) && (runqueue->curr->run_list.prev == &runqueue->queue)) { return runqueue->curr; } - // By default, the next process is the current one. + // By default, the next task is the current one. task_struct *next = NULL, *entry = NULL; - // Search for the next process (BEWARE: We do not start from the head, so INSIDE skip the head). + // Search for the next task (BEWARE: We do not start from the head, so INSIDE skip the head). list_for_each_decl(it, &runqueue->curr->run_list) { // Check if we reached the head of list_head, and skip it. @@ -34,7 +45,7 @@ static inline task_struct *scheduler_rr(runqueue_t *runqueue, bool_t skip_period if (entry->state != TASK_RUNNING) continue; - // Skip the process if it is a periodic one, we are issued to skip + // Skip the task if it is a periodic one, we are issued to skip // periodic tasks, and the entry is not a periodic task under // analysis. if (entry->se.is_periodic && skip_periodic && !entry->se.is_under_analysis) @@ -47,79 +58,124 @@ static inline task_struct *scheduler_rr(runqueue_t *runqueue, bool_t skip_period return next; } -static inline task_struct *scheduler_priority(runqueue_t *runqueue, bool_t skip_periodic) +/// @brief Is a non-preemptive algorithm, where each task is assigned a +/// priority. Processes with highest priority are executed first, while +/// processes with same priority are executed on first-come/first-served +/// basis. Priority can be decided based on memory requirements, time +/// requirements or any other resource requirement. +/// @param runqueue list of all processes. +/// @param skip_periodic tells the algorithm if there are periodic processes +/// in the list, and in that case it needs to skip them. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_priority(runqueue_t *runqueue, bool_t skip_periodic) { - return scheduler_rr(runqueue, skip_periodic); + return __scheduler_rr(runqueue, skip_periodic); } -static inline task_struct *scheduler_cfs(runqueue_t *runqueue, bool_t skip_periodic) +/// @brief It aims at giving a fair share of CPU time to processes, and +/// achieves that by associating a virtual runtime to each of them. It always +/// tries to run the task with the smallest vruntime (i.e., the task which +/// executed least so far). It always tries to split up CPU time between +/// runnable tasks as close to "ideal multitasking hardware" as possible. +/// @param runqueue list of all processes. +/// @param skip_periodic tells the algorithm if there are periodic processes +/// in the list, and in that case it needs to skip them. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_cfs(runqueue_t *runqueue, bool_t skip_periodic) { - return scheduler_rr(runqueue, skip_periodic); + return __scheduler_rr(runqueue, skip_periodic); } -static inline task_struct *scheduler_aedf(runqueue_t *runqueue) +/// @brief Executes the task with the earliest absolute deadline among all +/// the ready tasks. +/// @param runqueue list of all processes. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_aedf(runqueue_t *runqueue) { - return scheduler_rr(runqueue, false); + return __scheduler_rr(runqueue, false); } -static inline task_struct *scheduler_edf(runqueue_t *runqueue) +/// @brief Executes the task with the earliest absolute DEADLINE among all +/// the ready tasks. When a task was executed, and its period is starting +/// again, it must be set as 'executable again', and its deadline and next_period +/// must be updated. +/// @param runqueue list of all processes. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_edf(runqueue_t *runqueue) { - return scheduler_rr(runqueue, false); + return __scheduler_rr(runqueue, false); } -static inline task_struct *scheduler_rm(runqueue_t *runqueue) +/// @brief Executes the task with the earliest next PERIOD among all the +/// ready tasks. +/// @details When a task was executed, and its period is starting again, it +/// must be set as 'executable again', and its deadline and next_period must +/// be updated. +/// @param runqueue list of all processes. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_rm(runqueue_t *runqueue) { - return scheduler_rr(runqueue, false); + return __scheduler_rr(runqueue, false); } task_struct *scheduler_pick_next_task(runqueue_t *runqueue) { + // Update task statistics. + __update_task_statistics(runqueue->curr); + + // Pointer to the next task to schedule. + task_struct *next = NULL; +#if defined(SCHEDULER_RR) + next = __scheduler_rr(runqueue, false); +#elif defined(SCHEDULER_PRIORITY) + next = __scheduler_priority(runqueue, false); +#elif defined(SCHEDULER_CFS) + next = __scheduler_cfs(runqueue, false); +#elif defined(SCHEDULER_EDF) + next = __scheduler_edf(runqueue); +#elif defined(SCHEDULER_RM) + next = __scheduler_rm(runqueue); +#elif defined(SCHEDULER_AEDF) + next = __scheduler_aedf(runqueue); +#else +#error "You should enable a scheduling algorithm!" +#endif + + assert(next && "No valid task selected by the scheduling algorithm."); + + // Update the last context switch time of the next task. + next->se.exec_start = timer_get_ticks(); + + return next; +} + +static void __update_task_statistics(task_struct *task) +{ + assert(task && "Current task is not valid."); + // While periodic task is under analysis is executed with aperiodic // scheduler and can be preempted by a "true" periodic task. // We need to sum all the execution spots to calculate the WCET even // if is a more pessimistic evaluation. // Update the delta exec. - runqueue->curr->se.exec_runtime = timer_get_ticks() - runqueue->curr->se.exec_start; - update_process_profiling_timer(runqueue->curr); + task->se.exec_runtime = timer_get_ticks() - task->se.exec_start; + update_process_profiling_timer(task); - // set the sum_exec_runtime. - runqueue->curr->se.sum_exec_runtime += runqueue->curr->se.exec_runtime; + // Set the sum_exec_runtime. + task->se.sum_exec_runtime += task->se.exec_runtime; // If the task is not a periodic task we have to update the virtual runtime. - if (!runqueue->curr->se.is_periodic) { - // Get the weight of the current process. - time_t weight = GET_WEIGHT(runqueue->curr->se.prio); + if (!task->se.is_periodic) { + // Get the weight of the current task. + time_t weight = GET_WEIGHT(task->se.prio); + // If the weight is different from the default load, compute it. if (weight != NICE_0_LOAD) { - // get the multiplicative factor for its delta_exec. + // Get the multiplicative factor for its delta_exec. double factor = ((double)NICE_0_LOAD) / ((double)weight); - // weight the delta_exec with the multiplicative factor. - runqueue->curr->se.exec_runtime = (int)(((double)runqueue->curr->se.exec_runtime) * factor); + // Weight the delta_exec with the multiplicative factor. + task->se.exec_runtime = (int)(((double)task->se.exec_runtime) * factor); } - // Update vruntime of the current process. - runqueue->curr->se.vruntime += runqueue->curr->se.exec_runtime; + // Update vruntime of the current task. + task->se.vruntime += task->se.exec_runtime; } - - // Pointer to the next task to schedule. - task_struct *next = NULL; -#if defined(SCHEDULER_RR) - next = scheduler_rr(runqueue, false); -#elif defined(SCHEDULER_PRIORITY) - next = scheduler_priority(runqueue, false); -#elif defined(SCHEDULER_CFS) - next = scheduler_cfs(runqueue, false); -#elif defined(SCHEDULER_EDF) - next = scheduler_edf(runqueue); -#elif defined(SCHEDULER_RM) - next = scheduler_rm(runqueue); -#elif defined(SCHEDULER_AEDF) - next = scheduler_aedf(runqueue); -#else -#error "You should enable a scheduling algorithm!" -#endif - assert(next && "No valid task selected by the scheduling algorithm."); - - // Update the last context switch time of the next process. - next->se.exec_start = timer_get_ticks(); - - return next; -} +} \ No newline at end of file From 18a2fd9baac58e3cefc83bcfd8bd5c2d82038366 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Tue, 5 Oct 2021 14:29:24 +0200 Subject: [PATCH 18/21] Force includes to use absolute paths. --- mentos/CMakeLists.txt | 27 +--- mentos/inc/bits/ioctls.h | 9 -- mentos/inc/bits/termios-struct.h | 45 ------ mentos/inc/elf/elf.h | 2 +- mentos/inc/fs/procfs.h | 2 +- mentos/inc/fs/vfs.h | 4 +- mentos/inc/fs/vfs_types.h | 2 +- mentos/inc/hardware/timer.h | 6 +- mentos/inc/io/proc_modules.h | 2 +- mentos/inc/klib/hashmap.h | 2 +- mentos/inc/klib/spinlock.h | 2 +- mentos/inc/klib/stdatomic.h | 2 +- mentos/inc/mem/kheap.h | 2 +- mentos/inc/mem/paging.h | 2 +- mentos/inc/mem/slab.h | 4 +- mentos/inc/mem/vmem_map.h | 6 +- mentos/inc/mem/zone_allocator.h | 10 +- mentos/inc/misc/debug.h | 2 +- mentos/inc/process/process.h | 6 +- mentos/inc/process/scheduler.h | 4 +- mentos/inc/process/wait.h | 4 +- mentos/inc/system/signal.h | 8 +- mentos/inc/system/syscall.h | 6 +- mentos/inc/system/syscall_types.h | 197 ----------------------- mentos/src/boot.c | 6 +- mentos/src/descriptor_tables/exception.c | 8 +- mentos/src/descriptor_tables/gdt.c | 6 +- mentos/src/descriptor_tables/idt.c | 6 +- mentos/src/descriptor_tables/interrupt.c | 12 +- mentos/src/descriptor_tables/tss.c | 6 +- mentos/src/devices/fpu.c | 12 +- mentos/src/devices/pci.c | 6 +- mentos/src/drivers/ata.c | 22 +-- mentos/src/drivers/fdc.c | 6 +- mentos/src/drivers/keyboard/keyboard.c | 16 +- mentos/src/drivers/keyboard/keymap.c | 2 +- mentos/src/drivers/mouse.c | 6 +- mentos/src/drivers/rtc.c | 8 +- mentos/src/elf/elf.c | 14 +- mentos/src/fs/initrd.c | 12 +- mentos/src/fs/ioctl.c | 10 +- mentos/src/fs/namei.c | 6 +- mentos/src/fs/open.c | 8 +- mentos/src/fs/procfs.c | 8 +- mentos/src/fs/read_write.c | 10 +- mentos/src/fs/readdir.c | 10 +- mentos/src/fs/stat.c | 10 +- mentos/src/fs/vfs.c | 16 +- mentos/src/hardware/cpuid.c | 2 +- mentos/src/hardware/pic8259.c | 4 +- mentos/src/hardware/timer.c | 26 +-- mentos/src/io/mm_io.c | 2 +- mentos/src/io/port_io.c | 2 +- mentos/src/io/proc_running.c | 10 +- mentos/src/io/proc_system.c | 10 +- mentos/src/io/proc_video.c | 16 +- mentos/src/io/stdio.c | 8 +- mentos/src/io/vga/vga.c | 12 +- mentos/src/io/video.c | 4 +- mentos/src/ipc/msg.c | 2 +- mentos/src/ipc/sem.c | 2 +- mentos/src/ipc/shm.c | 2 +- mentos/src/kernel.c | 48 +++--- mentos/src/kernel/sys.c | 8 +- mentos/src/klib/assert.c | 2 +- mentos/src/klib/hashmap.c | 4 +- mentos/src/klib/libgen.c | 6 +- mentos/src/klib/list.c | 4 +- mentos/src/klib/mutex.c | 4 +- mentos/src/klib/ndtree.c | 8 +- mentos/src/klib/rbtree.c | 6 +- mentos/src/klib/spinlock.c | 2 +- mentos/src/klib/string.c | 2 +- mentos/src/klib/time.c | 8 +- mentos/src/klib/vscanf.c | 4 +- mentos/src/klib/vsprintf.c | 2 +- mentos/src/mem/buddysystem.c | 8 +- mentos/src/mem/kheap.c | 8 +- mentos/src/mem/paging.c | 14 +- mentos/src/mem/slab.c | 8 +- mentos/src/mem/vmem_map.c | 4 +- mentos/src/mem/zone_allocator.c | 10 +- mentos/src/misc/debug.c | 8 +- mentos/src/multiboot.c | 4 +- mentos/src/process/process.c | 22 +-- mentos/src/process/scheduler.c | 26 +-- mentos/src/process/scheduler_algorithm.c | 12 +- mentos/src/process/wait.c | 2 +- mentos/src/sys/module.c | 6 +- mentos/src/sys/utsname.c | 8 +- mentos/src/system/errno.c | 4 +- mentos/src/system/panic.c | 4 +- mentos/src/system/printk.c | 4 +- mentos/src/system/signal.c | 14 +- mentos/src/system/syscall.c | 20 +-- 95 files changed, 351 insertions(+), 627 deletions(-) delete mode 100644 mentos/inc/bits/ioctls.h delete mode 100644 mentos/inc/bits/termios-struct.h delete mode 100644 mentos/inc/system/syscall_types.h diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index 78e3ec6..3dd47a5 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -134,32 +134,7 @@ endif(CMAKE_BUILD_TYPE STREQUAL "Debug") # ============================================================================= # Add the includes. -include_directories( - inc - inc/bits - inc/descriptor_tables - inc/devices - inc/drivers - inc/drivers/keyboard - inc/fs - inc/hardware - inc/io - inc/io/vga - inc/ipc - inc/kernel - inc/klib - inc/lng - inc/mem - inc/misc - inc/sys - inc/system - inc/process - inc/elf - inc/ui - inc/ui/shell - inc/ui/command - inc/ui/init - ../libc/inc) +include_directories(inc ../libc/inc) # ============================================================================= # Add kernel library. diff --git a/mentos/inc/bits/ioctls.h b/mentos/inc/bits/ioctls.h deleted file mode 100644 index c872a26..0000000 --- a/mentos/inc/bits/ioctls.h +++ /dev/null @@ -1,9 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ioctls.h -/// @brief Definitions of tty ioctl numbers. 0x54 is just a magic number to make these -/// relatively unique ('T') - -#pragma once - -#define TCGETS 0x5401U ///< Get the current serial port settings. -#define TCSETS 0x5402U ///< Set the current serial port settings. diff --git a/mentos/inc/bits/termios-struct.h b/mentos/inc/bits/termios-struct.h deleted file mode 100644 index a54adbc..0000000 --- a/mentos/inc/bits/termios-struct.h +++ /dev/null @@ -1,45 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file termios-struct.h -/// @brief -/// Copyright (c) 2014-2021 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// Type for control characters. -typedef unsigned char cc_t; -/// Type for speed. -typedef unsigned int speed_t; -/// Type for flags. -typedef unsigned int tcflag_t; -/// The number of control characters. -#define NCCS 32 - -/// @brief Stores information about a terminal IOs. -typedef struct termios { - tcflag_t c_iflag; ///< input mode flags - tcflag_t c_oflag; ///< output mode flags - tcflag_t c_cflag; ///< control mode flags - tcflag_t c_lflag; ///< local mode flags - cc_t c_line; ///< line discipline - cc_t c_cc[NCCS]; ///< control characters - speed_t c_ispeed; ///< input speed - speed_t c_ospeed; ///< output speed -} termios; - -/// @brief These flags generally control higher-level aspects of input processing than the input -/// modes flags described in Input Modes, such as echoing, signals, and the choice of canonical -/// or noncanonical input. -enum { - ISIG = 000001U, ///< Controls whether the INTR, QUIT, and SUSP characters are recognized. - ICANON = 000002U, ///< Enables canonical input processing mode. - ECHO = 000010U, ///< Echo input characters. - ECHOE = 000020U, ///< If ICANON is set, the ERASE character erases the preceding character. - ECHOK = 000040U, ///< If ICANON is set, the KILL character erases the current line. - ECHONL = 000100U, ///< If ICANON is set, echo the NL character even if ECHO is not set. - NOFLSH = 000200U, ///< Do not clear in/out queues when receiving INTR, QUIT, and SUSP. - TOSTOP = 000400U, ///< Allows SIGTTOU signals generated by background processes. - ECHOCTL = 001000U, ///< If this and ECHO are set, control characters with ‘^’ are echoed. - ECHOKE = 004000U, ///< If ICANON is set, KILL is echoed by erasing each character on the line. - IEXTEN = 100000U, ///< Enables implementation-defined input processing. -}; \ No newline at end of file diff --git a/mentos/inc/elf/elf.h b/mentos/inc/elf/elf.h index 44ba0b3..af303ea 100644 --- a/mentos/inc/elf/elf.h +++ b/mentos/inc/elf/elf.h @@ -6,7 +6,7 @@ #pragma once -#include "process.h" +#include "process/process.h" #include "stdint.h" /// @defgroup header_segment_types Program Header Segment Types diff --git a/mentos/inc/fs/procfs.h b/mentos/inc/fs/procfs.h index 68afcab..039e9d9 100644 --- a/mentos/inc/fs/procfs.h +++ b/mentos/inc/fs/procfs.h @@ -6,7 +6,7 @@ #pragma once -#include "process.h" +#include "process/process.h" /// @brief Stores information about a procfs directory entry. typedef struct proc_dir_entry_t { diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index c5ef6a1..1a2a5e1 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -6,8 +6,8 @@ #pragma once -#include "vfs_types.h" -#include "slab.h" +#include "fs/vfs_types.h" +#include "mem/slab.h" #define MAX_OPEN_FD 16 ///< Maximum number of opened file. diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index ba47810..4e9893e 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -6,7 +6,7 @@ #pragma once -#include "list_head.h" +#include "klib/list_head.h" #include "sys/dirent.h" #include "bits/stat.h" #include "stdint.h" diff --git a/mentos/inc/hardware/timer.h b/mentos/inc/hardware/timer.h index 459fe61..0faa231 100644 --- a/mentos/inc/hardware/timer.h +++ b/mentos/inc/hardware/timer.h @@ -8,9 +8,9 @@ #include "kernel.h" #include "stdint.h" -#include "list_head.h" -#include "spinlock.h" -#include "process.h" +#include "klib/list_head.h" +#include "klib/spinlock.h" +#include "process/process.h" #include "time.h" /// This enables the dynamic timer system use an hierarchical timing wheel, diff --git a/mentos/inc/io/proc_modules.h b/mentos/inc/io/proc_modules.h index 5725d6c..6dbd4a2 100644 --- a/mentos/inc/io/proc_modules.h +++ b/mentos/inc/io/proc_modules.h @@ -6,7 +6,7 @@ #pragma once -#include "vfs.h" +#include "fs/vfs.h" /// @brief Initialize the procfs video files. /// @return 0 on success, 1 on failure. diff --git a/mentos/inc/klib/hashmap.h b/mentos/inc/klib/hashmap.h index 37e2c44..392a0a3 100644 --- a/mentos/inc/klib/hashmap.h +++ b/mentos/inc/klib/hashmap.h @@ -6,7 +6,7 @@ #pragma once -#include "list.h" +#include "klib/list.h" // == OPAQUE TYPES ============================================================ /// @brief Stores information of an entry of the hashmap. diff --git a/mentos/inc/klib/spinlock.h b/mentos/inc/klib/spinlock.h index 792661c..f9f3c4d 100644 --- a/mentos/inc/klib/spinlock.h +++ b/mentos/inc/klib/spinlock.h @@ -6,7 +6,7 @@ #pragma once -#include "stdatomic.h" +#include "klib/stdatomic.h" /// Determines if the spinlock is free. #define SPINLOCK_FREE 0 diff --git a/mentos/inc/klib/stdatomic.h b/mentos/inc/klib/stdatomic.h index 78fca83..03234d4 100644 --- a/mentos/inc/klib/stdatomic.h +++ b/mentos/inc/klib/stdatomic.h @@ -8,7 +8,7 @@ #include "stdint.h" #include "stdbool.h" -#include "compiler.h" +#include "klib/compiler.h" /// @brief Standard structure for atomic operations (see below /// for volatile explanation). diff --git a/mentos/inc/mem/kheap.h b/mentos/inc/mem/kheap.h index c3f38d3..309b74f 100644 --- a/mentos/inc/mem/kheap.h +++ b/mentos/inc/mem/kheap.h @@ -8,7 +8,7 @@ #pragma once #include "kernel.h" -#include "scheduler.h" +#include "process/scheduler.h" /// @brief Initialize heap. /// @param initial_size Starting size. diff --git a/mentos/inc/mem/paging.h b/mentos/inc/mem/paging.h index f9e25f3..1298602 100644 --- a/mentos/inc/mem/paging.h +++ b/mentos/inc/mem/paging.h @@ -6,7 +6,7 @@ #pragma once -#include "zone_allocator.h" +#include "mem/zone_allocator.h" #include "proc_access.h" #include "kernel.h" #include "stddef.h" diff --git a/mentos/inc/mem/slab.h b/mentos/inc/mem/slab.h index 8894278..5d99ea1 100644 --- a/mentos/inc/mem/slab.h +++ b/mentos/inc/mem/slab.h @@ -6,9 +6,9 @@ #pragma once -#include "list_head.h" +#include "klib/list_head.h" #include "stddef.h" -#include "gfp.h" +#include "mem/gfp.h" //#define ENABLE_CACHE_TRACE //#define ENABLE_ALLOC_TRACE diff --git a/mentos/inc/mem/vmem_map.h b/mentos/inc/mem/vmem_map.h index 1bd11dc..b8e65b6 100644 --- a/mentos/inc/mem/vmem_map.h +++ b/mentos/inc/mem/vmem_map.h @@ -6,9 +6,9 @@ #pragma once -#include "buddysystem.h" -#include "zone_allocator.h" -#include "paging.h" +#include "mem/buddysystem.h" +#include "mem/zone_allocator.h" +#include "mem/paging.h" /// Size of the virtual memory. #define VIRTUAL_MEMORY_SIZE_MB 128 diff --git a/mentos/inc/mem/zone_allocator.h b/mentos/inc/mem/zone_allocator.h index 8febc9f..8fb57a5 100644 --- a/mentos/inc/mem/zone_allocator.h +++ b/mentos/inc/mem/zone_allocator.h @@ -6,15 +6,15 @@ #pragma once -#include "gfp.h" +#include "mem/gfp.h" #include "math.h" #include "stdint.h" -#include "list_head.h" +#include "klib/list_head.h" #include "sys/bitops.h" -#include "stdatomic.h" +#include "klib/stdatomic.h" #include "boot.h" -#include "buddysystem.h" -#include "slab.h" +#include "mem/buddysystem.h" +#include "mem/slab.h" #define page_count(p) atomic_read(&(p)->count) ///< Reads the page count. #define set_page_count(p, v) atomic_set(&(p)->count, v) ///< Sets the page count. diff --git a/mentos/inc/misc/debug.h b/mentos/inc/misc/debug.h index 85060d8..60ca043 100644 --- a/mentos/inc/misc/debug.h +++ b/mentos/inc/misc/debug.h @@ -6,7 +6,7 @@ #pragma once -#include "kernel_levels.h" +#include "sys/kernel_levels.h" struct pt_regs; diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index a647f8b..39a455d 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -6,9 +6,9 @@ #pragma once -#include "paging.h" -#include "signal.h" -#include "fpu.h" +#include "mem/paging.h" +#include "system/signal.h" +#include "devices/fpu.h" /// The maximum length of a name for a task_struct. #define TASK_NAME_MAX_LENGTH 100 diff --git a/mentos/inc/process/scheduler.h b/mentos/inc/process/scheduler.h index 5e7ded6..1fc6596 100644 --- a/mentos/inc/process/scheduler.h +++ b/mentos/inc/process/scheduler.h @@ -6,8 +6,8 @@ #pragma once -#include "list_head.h" -#include "process.h" +#include "klib/list_head.h" +#include "process/process.h" #include "stddef.h" /// @brief Structure that contains information about live processes. diff --git a/mentos/inc/process/wait.h b/mentos/inc/process/wait.h index 4a6430f..05afbba 100644 --- a/mentos/inc/process/wait.h +++ b/mentos/inc/process/wait.h @@ -6,8 +6,8 @@ #pragma once -#include "list_head.h" -#include "spinlock.h" +#include "klib/list_head.h" +#include "klib/spinlock.h" /// @brief Return immediately if no child is there to be waited for. #define WNOHANG 0x00000001 diff --git a/mentos/inc/system/signal.h b/mentos/inc/system/signal.h index 249c874..4e360b1 100644 --- a/mentos/inc/system/signal.h +++ b/mentos/inc/system/signal.h @@ -6,10 +6,10 @@ #pragma once -#include "stdatomic.h" -#include "spinlock.h" -#include "list_head.h" -#include "syscall.h" +#include "klib/stdatomic.h" +#include "klib/spinlock.h" +#include "klib/list_head.h" +#include "system/syscall.h" /// @brief Signal codes. typedef enum { diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index 98747e9..f52ccde 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -6,11 +6,11 @@ #pragma once -#include "syscall_types.h" -#include "vfs_types.h" +#include "system/syscall_types.h" +#include "fs/vfs_types.h" #include "kernel.h" #include "sys/dirent.h" -#include "types.h" +#include "sys/types.h" /// @brief Initialize the system calls. void syscall_init(); diff --git a/mentos/inc/system/syscall_types.h b/mentos/inc/system/syscall_types.h deleted file mode 100644 index cd2bf69..0000000 --- a/mentos/inc/system/syscall_types.h +++ /dev/null @@ -1,197 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file syscall_types.h -/// @brief System Call numbers. -/// @copyright (c) 2014-2021 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#define __NR_exit 1 ///< System-call number for `exit` -#define __NR_fork 2 ///< System-call number for `fork` -#define __NR_read 3 ///< System-call number for `read` -#define __NR_write 4 ///< System-call number for `write` -#define __NR_open 5 ///< System-call number for `open` -#define __NR_close 6 ///< System-call number for `close` -#define __NR_waitpid 7 ///< System-call number for `waitpid` -#define __NR_creat 8 ///< System-call number for `creat` -#define __NR_link 9 ///< System-call number for `link` -#define __NR_unlink 10 ///< System-call number for `unlink` -#define __NR_execve 11 ///< System-call number for `execve` -#define __NR_chdir 12 ///< System-call number for `chdir` -#define __NR_time 13 ///< System-call number for `time` -#define __NR_mknod 14 ///< System-call number for `mknod` -#define __NR_chmod 15 ///< System-call number for `chmod` -#define __NR_lchown 16 ///< System-call number for `lchown` -#define __NR_stat 18 ///< System-call number for `stat` -#define __NR_lseek 19 ///< System-call number for `lseek` -#define __NR_getpid 20 ///< System-call number for `getpid` -#define __NR_mount 21 ///< System-call number for `mount` -#define __NR_oldumount 22 ///< System-call number for `oldumount` -#define __NR_setuid 23 ///< System-call number for `setuid` -#define __NR_getuid 24 ///< System-call number for `getuid` -#define __NR_stime 25 ///< System-call number for `stime` -#define __NR_ptrace 26 ///< System-call number for `ptrace` -#define __NR_alarm 27 ///< System-call number for `alarm` -#define __NR_fstat 28 ///< System-call number for `fstat` -#define __NR_pause 29 ///< System-call number for `pause` -#define __NR_utime 30 ///< System-call number for `utime` -#define __NR_access 33 ///< System-call number for `access` -#define __NR_nice 34 ///< System-call number for `nice` -#define __NR_sync 36 ///< System-call number for `sync` -#define __NR_kill 37 ///< System-call number for `kill` -#define __NR_rename 38 ///< System-call number for `rename` -#define __NR_mkdir 39 ///< System-call number for `mkdir` -#define __NR_rmdir 40 ///< System-call number for `rmdir` -#define __NR_dup 41 ///< System-call number for `dup` -#define __NR_pipe 42 ///< System-call number for `pipe` -#define __NR_times 43 ///< System-call number for `times` -#define __NR_brk 45 ///< System-call number for `brk` -#define __NR_setgid 46 ///< System-call number for `setgid` -#define __NR_getgid 47 ///< System-call number for `getgid` -#define __NR_signal 48 ///< System-call number for `signal` -#define __NR_geteuid 49 ///< System-call number for `geteuid` -#define __NR_getegid 50 ///< System-call number for `getegid` -#define __NR_acct 51 ///< System-call number for `acct` -#define __NR_umount 52 ///< System-call number for `umount` -#define __NR_ioctl 54 ///< System-call number for `ioctl` -#define __NR_fcntl 55 ///< System-call number for `fcntl` -#define __NR_setpgid 57 ///< System-call number for `setpgid` -#define __NR_olduname 59 ///< System-call number for `olduname` -#define __NR_umask 60 ///< System-call number for `umask` -#define __NR_chroot 61 ///< System-call number for `chroot` -#define __NR_ustat 62 ///< System-call number for `ustat` -#define __NR_dup2 63 ///< System-call number for `dup2` -#define __NR_getppid 64 ///< System-call number for `getppid` -#define __NR_getpgrp 65 ///< System-call number for `getpgrp` -#define __NR_setsid 66 ///< System-call number for `setsid` -#define __NR_sigaction 67 ///< System-call number for `sigaction` -#define __NR_sgetmask 68 ///< System-call number for `sgetmask` -#define __NR_ssetmask 69 ///< System-call number for `ssetmask` -#define __NR_setreuid 70 ///< System-call number for `setreuid` -#define __NR_setregid 71 ///< System-call number for `setregid` -#define __NR_sigsuspend 72 ///< System-call number for `sigsuspend` -#define __NR_sigpending 73 ///< System-call number for `sigpending` -#define __NR_sethostname 74 ///< System-call number for `sethostname` -#define __NR_setrlimit 75 ///< System-call number for `setrlimit` -#define __NR_getrlimit 76 ///< System-call number for `getrlimit` -#define __NR_getrusage 77 ///< System-call number for `getrusage` -#define __NR_gettimeofday 78 ///< System-call number for `gettimeofday` -#define __NR_settimeofday 79 ///< System-call number for `settimeofday` -#define __NR_getgroups 80 ///< System-call number for `getgroups` -#define __NR_setgroups 81 ///< System-call number for `setgroups` -#define __NR_symlink 83 ///< System-call number for `symlink` -#define __NR_lstat 84 ///< System-call number for `lstat` -#define __NR_readlink 85 ///< System-call number for `readlink` -#define __NR_uselib 86 ///< System-call number for `uselib` -#define __NR_swapon 87 ///< System-call number for `swapon` -#define __NR_reboot 88 ///< System-call number for `reboot` -#define __NR_readdir 89 ///< System-call number for `readdir` -#define __NR_mmap 90 ///< System-call number for `mmap` -#define __NR_munmap 91 ///< System-call number for `munmap` -#define __NR_truncate 92 ///< System-call number for `truncate` -#define __NR_ftruncate 93 ///< System-call number for `ftruncate` -#define __NR_fchmod 94 ///< System-call number for `fchmod` -#define __NR_fchown 95 ///< System-call number for `fchown` -#define __NR_getpriority 96 ///< System-call number for `getpriority` -#define __NR_setpriority 97 ///< System-call number for `setpriority` -#define __NR_statfs 99 ///< System-call number for `statfs` -#define __NR_fstatfs 100 ///< System-call number for `fstatfs` -#define __NR_ioperm 101 ///< System-call number for `ioperm` -#define __NR_socketcall 102 ///< System-call number for `socketcall` -#define __NR_syslog 103 ///< System-call number for `syslog` -#define __NR_setitimer 104 ///< System-call number for `setitimer` -#define __NR_getitimer 105 ///< System-call number for `getitimer` -#define __NR_newstat 106 ///< System-call number for `newstat` -#define __NR_newlstat 107 ///< System-call number for `newlstat` -#define __NR_newfstat 108 ///< System-call number for `newfstat` -#define __NR_uname 109 ///< System-call number for `uname` -#define __NR_iopl 110 ///< System-call number for `iopl` -#define __NR_vhangup 111 ///< System-call number for `vhangup` -#define __NR_idle 112 ///< System-call number for `idle` -#define __NR_vm86old 113 ///< System-call number for `vm86old` -#define __NR_wait4 114 ///< System-call number for `wait4` -#define __NR_swapoff 115 ///< System-call number for `swapoff` -#define __NR_sysinfo 116 ///< System-call number for `sysinfo` -#define __NR_ipc 117 ///< System-call number for `ipc` -#define __NR_fsync 118 ///< System-call number for `fsync` -#define __NR_sigreturn 119 ///< System-call number for `sigreturn` -#define __NR_clone 120 ///< System-call number for `clone` -#define __NR_setdomainname 121 ///< System-call number for `setdomainname` -#define __NR_newuname 122 ///< System-call number for `newuname` -#define __NR_modify_ldt 123 ///< System-call number for `modify_ldt` -#define __NR_adjtimex 124 ///< System-call number for `adjtimex` -#define __NR_mprotect 125 ///< System-call number for `mprotect` -#define __NR_sigprocmask 126 ///< System-call number for `sigprocmask` -#define __NR_create_module 127 ///< System-call number for `create_module` -#define __NR_init_module 128 ///< System-call number for `init_module` -#define __NR_delete_module 129 ///< System-call number for `delete_module` -#define __NR_get_kernel_syms 130 ///< System-call number for `get_kernel_syms` -#define __NR_quotactl 131 ///< System-call number for `quotactl` -#define __NR_getpgid 132 ///< System-call number for `getpgid` -#define __NR_fchdir 133 ///< System-call number for `fchdir` -#define __NR_bdflush 134 ///< System-call number for `bdflush` -#define __NR_sysfs 135 ///< System-call number for `sysfs` -#define __NR_personality 136 ///< System-call number for `personality` -#define __NR_setfsuid 138 ///< System-call number for `setfsuid` -#define __NR_setfsgid 139 ///< System-call number for `setfsgid` -#define __NR_llseek 140 ///< System-call number for `llseek` -#define __NR_getdents 141 ///< System-call number for `getdents` -#define __NR_select 142 ///< System-call number for `select` -#define __NR_flock 143 ///< System-call number for `flock` -#define __NR_msync 144 ///< System-call number for `msync` -#define __NR_readv 145 ///< System-call number for `readv` -#define __NR_writev 146 ///< System-call number for `writev` -#define __NR_getsid 147 ///< System-call number for `getsid` -#define __NR_fdatasync 148 ///< System-call number for `fdatasync` -#define __NR_sysctl 149 ///< System-call number for `sysctl` -#define __NR_mlock 150 ///< System-call number for `mlock` -#define __NR_munlock 151 ///< System-call number for `munlock` -#define __NR_mlockall 152 ///< System-call number for `mlockall` -#define __NR_munlockall 153 ///< System-call number for `munlockall` -#define __NR_sched_setparam 154 ///< System-call number for `sched_setparam` -#define __NR_sched_getparam 155 ///< System-call number for `sched_getparam` -#define __NR_sched_setscheduler 156 ///< System-call number for `sched_setscheduler` -#define __NR_sched_getscheduler 157 ///< System-call number for `sched_getscheduler` -#define __NR_sched_yield 158 ///< System-call number for `sched_yield` -#define __NR_sched_get_priority_max 159 ///< System-call number for `sched_get_priority_max` -#define __NR_sched_get_priority_min 160 ///< System-call number for `sched_get_priority_min` -#define __NR_sched_rr_get_interval 161 ///< System-call number for `sched_rr_get_interval` -#define __NR_nanosleep 162 ///< System-call number for `nanosleep` -#define __NR_mremap 163 ///< System-call number for `mremap` -#define __NR_setresuid 164 ///< System-call number for `setresuid` -#define __NR_getresuid 165 ///< System-call number for `getresuid` -#define __NR_vm86 166 ///< System-call number for `vm86` -#define __NR_query_module 167 ///< System-call number for `query_module` -#define __NR_poll 168 ///< System-call number for `poll` -#define __NR_nfsservctl 169 ///< System-call number for `nfsservctl` -#define __NR_setresgid 170 ///< System-call number for `setresgid` -#define __NR_getresgid 171 ///< System-call number for `getresgid` -#define __NR_prctl 172 ///< System-call number for `prctl` -#define __NR_rt_sigreturn 173 ///< System-call number for `rt_sigreturn` -#define __NR_rt_sigaction 174 ///< System-call number for `rt_sigaction` -#define __NR_rt_sigprocmask 175 ///< System-call number for `rt_sigprocmask` -#define __NR_rt_sigpending 176 ///< System-call number for `rt_sigpending` -#define __NR_rt_sigtimedwait 177 ///< System-call number for `rt_sigtimedwait` -#define __NR_rt_sigqueueinfo 178 ///< System-call number for `rt_sigqueueinfo` -#define __NR_rt_sigsuspend 179 ///< System-call number for `rt_sigsuspend` -#define __NR_pread 180 ///< System-call number for `pread` -#define __NR_pwrite 181 ///< System-call number for `pwrite` -#define __NR_chown 182 ///< System-call number for `chown` -#define __NR_getcwd 183 ///< System-call number for `getcwd` -#define __NR_capget 184 ///< System-call number for `capget` -#define __NR_capset 185 ///< System-call number for `capset` -#define __NR_sigaltstack 186 ///< System-call number for `sigaltstack` -#define __NR_sendfile 187 ///< System-call number for `sendfile` -#define __NR_waitperiod 188 ///< System-call number for `waitperiod` -#define __NR_msgctl 189 ///< System-call number for `msgctl` -#define __NR_msgget 190 ///< System-call number for `msgget` -#define __NR_msgrcv 191 ///< System-call number for `msgrcv` -#define __NR_msgsnd 192 ///< System-call number for `msgsnd` -#define __NR_semctl 193 ///< System-call number for `semctl` -#define __NR_semget 194 ///< System-call number for `semget` -#define __NR_semop 195 ///< System-call number for `semop` -#define __NR_shmat 196 ///< System-call number for `shmat` -#define __NR_shmctl 197 ///< System-call number for `shmctl` -#define __NR_shmdt 198 ///< System-call number for `shmdt` -#define __NR_shmget 199 ///< System-call number for `shmget` -#define SYSCALL_NUMBER 200 ///< The total number of system-calls. diff --git a/mentos/src/boot.c b/mentos/src/boot.c index 1f7973e..aad9189 100644 --- a/mentos/src/boot.c +++ b/mentos/src/boot.c @@ -6,10 +6,10 @@ #include "link_access.h" #include "multiboot.h" -#include "paging.h" -#include "module.h" +#include "mem/paging.h" +#include "sys/module.h" #include "stdint.h" -#include "elf.h" +#include "elf/elf.h" /// @defgroup bootloader Bootloader /// @brief Set of functions and variables for booting the kernel. diff --git a/mentos/src/descriptor_tables/exception.c b/mentos/src/descriptor_tables/exception.c index 00e1c13..571790a 100644 --- a/mentos/src/descriptor_tables/exception.c +++ b/mentos/src/descriptor_tables/exception.c @@ -4,11 +4,11 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "panic.h" -#include "isr.h" -#include "idt.h" +#include "system/panic.h" +#include "descriptor_tables/isr.h" +#include "descriptor_tables/idt.h" #include "stdio.h" -#include "debug.h" +#include "misc/debug.h" // Default error messages for exceptions. static const char *exception_messages[32] = { diff --git a/mentos/src/descriptor_tables/gdt.c b/mentos/src/descriptor_tables/gdt.c index 8e359e2..039a0bc 100644 --- a/mentos/src/descriptor_tables/gdt.c +++ b/mentos/src/descriptor_tables/gdt.c @@ -4,9 +4,9 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "debug.h" -#include "gdt.h" -#include "tss.h" +#include "misc/debug.h" +#include "descriptor_tables/gdt.h" +#include "descriptor_tables/tss.h" /// The maximum dimension of the GDT. #define GDT_SIZE 10 diff --git a/mentos/src/descriptor_tables/idt.c b/mentos/src/descriptor_tables/idt.c index 207aa42..a4ae9de 100644 --- a/mentos/src/descriptor_tables/idt.c +++ b/mentos/src/descriptor_tables/idt.c @@ -4,9 +4,9 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "idt.h" -#include "gdt.h" -#include "isr.h" +#include "descriptor_tables/idt.h" +#include "descriptor_tables/gdt.h" +#include "descriptor_tables/isr.h" /// @brief This function is in idt.asm. /// @param idt_pointer Address of the idt. diff --git a/mentos/src/descriptor_tables/interrupt.c b/mentos/src/descriptor_tables/interrupt.c index 2788a68..8b67c91 100644 --- a/mentos/src/descriptor_tables/interrupt.c +++ b/mentos/src/descriptor_tables/interrupt.c @@ -4,15 +4,15 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "isr.h" +#include "descriptor_tables/isr.h" -#include "scheduler.h" -#include "pic8259.h" -#include "printk.h" +#include "process/scheduler.h" +#include "hardware/pic8259.h" +#include "system/printk.h" #include "assert.h" #include "stdio.h" -#include "debug.h" -#include "idt.h" +#include "misc/debug.h" +#include "descriptor_tables/idt.h" /// @brief Shared interrupt handlers, stored into a double-linked list. typedef struct irq_struct_t { diff --git a/mentos/src/descriptor_tables/tss.c b/mentos/src/descriptor_tables/tss.c index a6aefd3..d1c0484 100644 --- a/mentos/src/descriptor_tables/tss.c +++ b/mentos/src/descriptor_tables/tss.c @@ -4,11 +4,11 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "tss.h" +#include "descriptor_tables/tss.h" #include "string.h" -#include "debug.h" -#include "gdt.h" +#include "misc/debug.h" +#include "descriptor_tables/gdt.h" static tss_entry_t kernel_tss; diff --git a/mentos/src/devices/fpu.c b/mentos/src/devices/fpu.c index 44dc693..36b3f7a 100644 --- a/mentos/src/devices/fpu.c +++ b/mentos/src/devices/fpu.c @@ -4,15 +4,15 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "fpu.h" -#include "isr.h" -#include "debug.h" +#include "devices/fpu.h" +#include "descriptor_tables/isr.h" +#include "misc/debug.h" #include "string.h" #include "assert.h" -#include "scheduler.h" +#include "process/scheduler.h" #include "math.h" -#include "process.h" -#include "signal.h" +#include "process/process.h" +#include "system/signal.h" /// Pointerst to the current thread using the FPU. task_struct *thread_using_fpu = NULL; diff --git a/mentos/src/devices/pci.c b/mentos/src/devices/pci.c index cb077a9..578b3d7 100644 --- a/mentos/src/devices/pci.c +++ b/mentos/src/devices/pci.c @@ -5,10 +5,10 @@ /// See LICENSE.md for details. ///! @cond Doxygen_Suppress -#include "pci.h" -#include "debug.h" +#include "devices/pci.h" +#include "misc/debug.h" #include "string.h" -#include "port_io.h" +#include "io/port_io.h" void pci_write_field(uint32_t device, int field, int size, uint32_t value) { diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index cb6b209..647040b 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -5,22 +5,22 @@ /// See LICENSE.md for details. ///! @cond Doxygen_Suppress -#include "ata.h" -#include "spinlock.h" +#include "drivers/ata.h" +#include "klib/spinlock.h" #include "fcntl.h" -#include "vmem_map.h" -#include "list.h" +#include "mem/vmem_map.h" +#include "klib/list.h" #include "stdio.h" -#include "isr.h" -#include "vfs.h" -#include "pci.h" -#include "debug.h" -#include "kheap.h" +#include "descriptor_tables/isr.h" +#include "fs/vfs.h" +#include "devices/pci.h" +#include "misc/debug.h" +#include "mem/kheap.h" #include "assert.h" #include "string.h" #include "kernel.h" -#include "pic8259.h" -#include "port_io.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" #include "time.h" // #define COMPLETE_SCHEDULER diff --git a/mentos/src/drivers/fdc.c b/mentos/src/drivers/fdc.c index 420f60f..9eb0f3d 100644 --- a/mentos/src/drivers/fdc.c +++ b/mentos/src/drivers/fdc.c @@ -4,9 +4,9 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "fdc.h" -#include "video.h" -#include "port_io.h" +#include "drivers/fdc.h" +#include "io/video.h" +#include "io/port_io.h" void fdc_disable_motor() { diff --git a/mentos/src/drivers/keyboard/keyboard.c b/mentos/src/drivers/keyboard/keyboard.c index 0e2a392..18691f6 100644 --- a/mentos/src/drivers/keyboard/keyboard.c +++ b/mentos/src/drivers/keyboard/keyboard.c @@ -4,17 +4,17 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "keyboard.h" +#include "drivers/keyboard/keyboard.h" -#include "port_io.h" -#include "pic8259.h" -#include "keymap.h" +#include "io/port_io.h" +#include "hardware/pic8259.h" +#include "drivers/keyboard/keymap.h" #include "sys/bitops.h" -#include "video.h" -#include "debug.h" +#include "io/video.h" +#include "misc/debug.h" #include "ctype.h" -#include "isr.h" -#include "scheduler.h" +#include "descriptor_tables/isr.h" +#include "process/scheduler.h" /// A macro from Ivan to update buffer indexes. #define STEP(x) (((x) == BUFSIZE - 1) ? 0 : ((x) + 1)) diff --git a/mentos/src/drivers/keyboard/keymap.c b/mentos/src/drivers/keyboard/keymap.c index e24b688..9a2438b 100644 --- a/mentos/src/drivers/keyboard/keymap.c +++ b/mentos/src/drivers/keyboard/keymap.c @@ -4,7 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "keymap.h" +#include "drivers/keyboard/keymap.h" #include "string.h" /// Identifies the current keymap type. diff --git a/mentos/src/drivers/mouse.c b/mentos/src/drivers/mouse.c index 967a608..83acc45 100644 --- a/mentos/src/drivers/mouse.c +++ b/mentos/src/drivers/mouse.c @@ -6,9 +6,9 @@ ///// See LICENSE.md for details. /////! @cond Doxygen_Suppress -#include "mouse.h" -#include "pic8259.h" -#include "port_io.h" +#include "drivers/mouse.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" static uint8_t mouse_cycle = 0; diff --git a/mentos/src/drivers/rtc.c b/mentos/src/drivers/rtc.c index e7cc972..153043e 100644 --- a/mentos/src/drivers/rtc.c +++ b/mentos/src/drivers/rtc.c @@ -4,13 +4,13 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "rtc.h" +#include "drivers/rtc.h" -#include "pic8259.h" +#include "hardware/pic8259.h" #include "string.h" -#include "port_io.h" +#include "io/port_io.h" #include "kernel.h" -#include "isr.h" +#include "descriptor_tables/isr.h" #define CMOS_ADDR 0x70 ///< Addess where we need to write the Address. #define CMOS_DATA 0x71 ///< Addess where we need to write the Data. diff --git a/mentos/src/elf/elf.c b/mentos/src/elf/elf.c index 4478c6b..77024a2 100644 --- a/mentos/src/elf/elf.c +++ b/mentos/src/elf/elf.c @@ -7,17 +7,17 @@ /// Change the header. #define __DEBUG_HEADER__ "[ELF ]" -#include "elf.h" +#include "elf/elf.h" -#include "scheduler.h" -#include "vmem_map.h" -#include "process.h" +#include "process/scheduler.h" +#include "mem/vmem_map.h" +#include "process/process.h" #include "string.h" #include "stddef.h" -#include "debug.h" +#include "misc/debug.h" #include "stdio.h" -#include "slab.h" -#include "vfs.h" +#include "mem/slab.h" +#include "fs/vfs.h" /// @brief Reads the program header from file. /// @param file The file from which we extract the program header. diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index b7c9dab..3b6848d 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -5,16 +5,16 @@ /// See LICENSE.md for details. #include "assert.h" -#include "syscall.h" +#include "system/syscall.h" #include "sys/module.h" #include "system/panic.h" -#include "vfs.h" -#include "errno.h" -#include "debug.h" -#include "kheap.h" +#include "fs/vfs.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "mem/kheap.h" #include "fcntl.h" #include "sys/bitops.h" -#include "initrd.h" +#include "fs/initrd.h" #include "string.h" #include "stdio.h" #include "libgen.h" diff --git a/mentos/src/fs/ioctl.c b/mentos/src/fs/ioctl.c index 4cb2090..d2e6b87 100644 --- a/mentos/src/fs/ioctl.c +++ b/mentos/src/fs/ioctl.c @@ -4,12 +4,12 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "ioctl.h" -#include "scheduler.h" -#include "printk.h" +#include "fs/ioctl.h" +#include "process/scheduler.h" +#include "system/printk.h" #include "stdio.h" -#include "errno.h" -#include "vfs.h" +#include "sys/errno.h" +#include "fs/vfs.h" int sys_ioctl(int fd, int request, void *data) { diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c index 4234eea..8ffb0f9 100644 --- a/mentos/src/fs/namei.c +++ b/mentos/src/fs/namei.c @@ -4,9 +4,9 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "errno.h" -#include "vfs.h" -#include "scheduler.h" +#include "sys/errno.h" +#include "fs/vfs.h" +#include "process/scheduler.h" int sys_unlink(const char *path) { diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c index 346adbc..9200609 100644 --- a/mentos/src/fs/open.c +++ b/mentos/src/fs/open.c @@ -8,13 +8,13 @@ #include "process/process.h" #include "system/printk.h" #include "fcntl.h" -#include "syscall.h" +#include "system/syscall.h" #include "string.h" #include "limits.h" -#include "debug.h" -#include "errno.h" +#include "misc/debug.h" +#include "sys/errno.h" #include "stdio.h" -#include "vfs.h" +#include "fs/vfs.h" int sys_open(const char *pathname, int flags, mode_t mode) { diff --git a/mentos/src/fs/procfs.c b/mentos/src/fs/procfs.c index 7ed9833..2de01e6 100644 --- a/mentos/src/fs/procfs.c +++ b/mentos/src/fs/procfs.c @@ -7,11 +7,11 @@ /// Change the header. #define __DEBUG_HEADER__ "[PROCFS]" -#include "procfs.h" -#include "vfs.h" +#include "fs/procfs.h" +#include "fs/vfs.h" #include "string.h" -#include "errno.h" -#include "debug.h" +#include "sys/errno.h" +#include "misc/debug.h" #include "fcntl.h" #include "libgen.h" #include "assert.h" diff --git a/mentos/src/fs/read_write.c b/mentos/src/fs/read_write.c index 3c8e83d..23673f4 100644 --- a/mentos/src/fs/read_write.c +++ b/mentos/src/fs/read_write.c @@ -4,13 +4,13 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "scheduler.h" -#include "vfs_types.h" -#include "panic.h" -#include "errno.h" +#include "process/scheduler.h" +#include "fs/vfs_types.h" +#include "system/panic.h" +#include "sys/errno.h" #include "fcntl.h" #include "stdio.h" -#include "vfs.h" +#include "fs/vfs.h" ssize_t sys_read(int fd, void *buf, size_t nbytes) { diff --git a/mentos/src/fs/readdir.c b/mentos/src/fs/readdir.c index e879ae7..54f74f0 100644 --- a/mentos/src/fs/readdir.c +++ b/mentos/src/fs/readdir.c @@ -5,12 +5,12 @@ /// See LICENSE.md for details. #include "sys/dirent.h" -#include "scheduler.h" -#include "syscall.h" -#include "printk.h" -#include "errno.h" +#include "process/scheduler.h" +#include "system/syscall.h" +#include "system/printk.h" +#include "sys/errno.h" #include "stdio.h" -#include "vfs.h" +#include "fs/vfs.h" int sys_getdents(int fd, dirent_t *dirp, unsigned int count) { diff --git a/mentos/src/fs/stat.c b/mentos/src/fs/stat.c index b8531ee..becfdb1 100644 --- a/mentos/src/fs/stat.c +++ b/mentos/src/fs/stat.c @@ -4,13 +4,13 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "debug.h" -#include "errno.h" -#include "vfs.h" -#include "kheap.h" +#include "misc/debug.h" +#include "sys/errno.h" +#include "fs/vfs.h" +#include "mem/kheap.h" #include "stdio.h" #include "string.h" -#include "initrd.h" +#include "fs/initrd.h" #include "limits.h" int sys_stat(const char *path, stat_t *buf) diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 7b7d694..f6e310e 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -4,19 +4,19 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "vfs.h" +#include "fs/vfs.h" -#include "scheduler.h" -#include "spinlock.h" +#include "process/scheduler.h" +#include "klib/spinlock.h" #include "strerror.h" -#include "syscall.h" -#include "hashmap.h" +#include "system/syscall.h" +#include "klib/hashmap.h" #include "string.h" -#include "procfs.h" +#include "fs/procfs.h" #include "assert.h" #include "libgen.h" -#include "debug.h" -#include "panic.h" +#include "misc/debug.h" +#include "system/panic.h" #include "stdio.h" /// The hashmap that associates a type of Filesystem `name` to its `mount` function; diff --git a/mentos/src/hardware/cpuid.c b/mentos/src/hardware/cpuid.c index 1c255e8..a157016 100644 --- a/mentos/src/hardware/cpuid.c +++ b/mentos/src/hardware/cpuid.c @@ -4,7 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "cpuid.h" +#include "hardware/cpuid.h" #include "string.h" void get_cpuid(cpuinfo_t *cpuinfo) diff --git a/mentos/src/hardware/pic8259.c b/mentos/src/hardware/pic8259.c index 649cb7b..9dac083 100644 --- a/mentos/src/hardware/pic8259.c +++ b/mentos/src/hardware/pic8259.c @@ -4,8 +4,8 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "pic8259.h" -#include "port_io.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" #include "stddef.h" /// End-of-interrupt command code. diff --git a/mentos/src/hardware/timer.c b/mentos/src/hardware/timer.c index c38b7d4..117c02e 100644 --- a/mentos/src/hardware/timer.c +++ b/mentos/src/hardware/timer.c @@ -4,22 +4,22 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "timer.h" +#include "hardware/timer.h" -#include "irqflags.h" -#include "scheduler.h" -#include "pic8259.h" -#include "port_io.h" +#include "klib/irqflags.h" +#include "process/scheduler.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" #include "stdint.h" -#include "kheap.h" -#include "debug.h" -#include "wait.h" -#include "rtc.h" -#include "isr.h" -#include "fpu.h" -#include "signal.h" +#include "mem/kheap.h" +#include "misc/debug.h" +#include "process/wait.h" +#include "drivers/rtc.h" +#include "descriptor_tables/isr.h" +#include "devices/fpu.h" +#include "system/signal.h" #include "assert.h" -#include "errno.h" +#include "sys/errno.h" /// Number of ticks per seconds. #define TICKS_PER_SECOND 1193 diff --git a/mentos/src/io/mm_io.c b/mentos/src/io/mm_io.c index 0b215b5..219f776 100644 --- a/mentos/src/io/mm_io.c +++ b/mentos/src/io/mm_io.c @@ -4,7 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "mm_io.h" +#include "io/mm_io.h" uint8_t in_memb(uint32_t addr) { diff --git a/mentos/src/io/port_io.c b/mentos/src/io/port_io.c index 7da53bb..9dfe3ff 100644 --- a/mentos/src/io/port_io.c +++ b/mentos/src/io/port_io.c @@ -4,7 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "port_io.h" +#include "io/port_io.h" inline uint8_t inportb(uint16_t port) { diff --git a/mentos/src/io/proc_running.c b/mentos/src/io/proc_running.c index 02596c2..916fd70 100644 --- a/mentos/src/io/proc_running.c +++ b/mentos/src/io/proc_running.c @@ -2,15 +2,15 @@ // Created by andrea on 02/05/20. // -#include "procfs.h" +#include "fs/procfs.h" -#include "process.h" +#include "process/process.h" #include "string.h" #include "libgen.h" #include "stdio.h" -#include "errno.h" -#include "debug.h" -#include "prio.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "process/prio.h" /// @brief Returns the character identifying the process state. /// @details diff --git a/mentos/src/io/proc_system.c b/mentos/src/io/proc_system.c index 132f710..9e90927 100644 --- a/mentos/src/io/proc_system.c +++ b/mentos/src/io/proc_system.c @@ -4,14 +4,14 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "procfs.h" +#include "fs/procfs.h" #include "version.h" -#include "process.h" +#include "process/process.h" #include "string.h" #include "stdio.h" -#include "errno.h" -#include "debug.h" -#include "timer.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "hardware/timer.h" static ssize_t procs_do_uptime(char *buffer, size_t bufsize); diff --git a/mentos/src/io/proc_video.c b/mentos/src/io/proc_video.c index b9597a2..8937451 100644 --- a/mentos/src/io/proc_video.c +++ b/mentos/src/io/proc_video.c @@ -2,16 +2,16 @@ // Created by andrea on 02/05/20. // -#include "termios-struct.h" -#include "keyboard.h" -#include "procfs.h" -#include "ioctls.h" +#include "bits/termios-struct.h" +#include "drivers/keyboard/keyboard.h" +#include "fs/procfs.h" +#include "bits/ioctls.h" #include "sys/bitops.h" -#include "video.h" -#include "debug.h" -#include "errno.h" +#include "io/video.h" +#include "misc/debug.h" +#include "sys/errno.h" #include "fcntl.h" -#include "vfs.h" +#include "fs/vfs.h" static termios ktermios = { .c_cflag = 0, diff --git a/mentos/src/io/stdio.c b/mentos/src/io/stdio.c index 0e57909..d28f4d4 100644 --- a/mentos/src/io/stdio.c +++ b/mentos/src/io/stdio.c @@ -2,12 +2,12 @@ /// @brief Standard I/0 functions. /// @date Apr 2019 -#include "syscall.h" -#include "errno.h" +#include "system/syscall.h" +#include "sys/errno.h" #include "stdio.h" -#include "video.h" +#include "io/video.h" #include "ctype.h" -#include "keyboard.h" +#include "drivers/keyboard/keyboard.h" #include "string.h" void putchar(int character) diff --git a/mentos/src/io/vga/vga.c b/mentos/src/io/vga/vga.c index a3630ca..3d1ea71 100644 --- a/mentos/src/io/vga/vga.c +++ b/mentos/src/io/vga/vga.c @@ -1,13 +1,13 @@ -#include "vga.h" +#include "io/vga/vga.h" -#include "vga_palette.h" -#include "vga_mode.h" -#include "vga_font.h" +#include "io/vga/vga_palette.h" +#include "io/vga/vga_mode.h" +#include "io/vga/vga_font.h" -#include "port_io.h" +#include "io/port_io.h" #include "stdbool.h" #include "string.h" -#include "debug.h" +#include "misc/debug.h" #include "math.h" #define COUNT_OF(x) ((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x]))))) diff --git a/mentos/src/io/video.c b/mentos/src/io/video.c index 92f6ce7..d160806 100644 --- a/mentos/src/io/video.c +++ b/mentos/src/io/video.c @@ -4,8 +4,8 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "port_io.h" -#include "video.h" +#include "io/port_io.h" +#include "io/video.h" #include "stdbool.h" #include "ctype.h" #include "string.h" diff --git a/mentos/src/ipc/msg.c b/mentos/src/ipc/msg.c index 8f975dd..bbaa6d9 100644 --- a/mentos/src/ipc/msg.c +++ b/mentos/src/ipc/msg.c @@ -6,7 +6,7 @@ ///! @cond Doxygen_Suppress #include "ipc/msg.h" -#include "panic.h" +#include "system/panic.h" long sys_msgget(key_t key, int msgflg) { diff --git a/mentos/src/ipc/sem.c b/mentos/src/ipc/sem.c index 7e88a96..238b40f 100644 --- a/mentos/src/ipc/sem.c +++ b/mentos/src/ipc/sem.c @@ -6,7 +6,7 @@ ///! @cond Doxygen_Suppress #include "ipc/sem.h" -#include "panic.h" +#include "system/panic.h" long sys_semget(key_t key, int nsems, int semflg) { diff --git a/mentos/src/ipc/shm.c b/mentos/src/ipc/shm.c index ad55185..4bab57c 100644 --- a/mentos/src/ipc/shm.c +++ b/mentos/src/ipc/shm.c @@ -6,7 +6,7 @@ ///! @cond Doxygen_Suppress #include "ipc/shm.h" -#include "panic.h" +#include "system/panic.h" #if 0 struct shmid_ds *head = NULL; diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index cfb849a..6ec25bb 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -4,34 +4,34 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "proc_modules.h" -#include "vmem_map.h" -#include "procfs.h" -#include "pci.h" -#include "ata.h" -#include "idt.h" +#include "io/proc_modules.h" +#include "mem/vmem_map.h" +#include "fs/procfs.h" +#include "devices/pci.h" +#include "drivers/ata.h" +#include "descriptor_tables/idt.h" #include "kernel.h" -#include "zone_allocator.h" -#include "gdt.h" -#include "syscall.h" +#include "mem/zone_allocator.h" +#include "descriptor_tables/gdt.h" +#include "system/syscall.h" #include "version.h" -#include "video.h" -#include "pic8259.h" -#include "debug.h" -#include "fdc.h" -#include "initrd.h" -#include "irqflags.h" -#include "keyboard.h" -#include "scheduler.h" -#include "timer.h" -#include "vfs.h" -#include "fpu.h" -#include "printk.h" -#include "module.h" -#include "rtc.h" +#include "io/video.h" +#include "hardware/pic8259.h" +#include "misc/debug.h" +#include "drivers/fdc.h" +#include "fs/initrd.h" +#include "klib/irqflags.h" +#include "drivers/keyboard/keyboard.h" +#include "process/scheduler.h" +#include "hardware/timer.h" +#include "fs/vfs.h" +#include "devices/fpu.h" +#include "system/printk.h" +#include "sys/module.h" +#include "drivers/rtc.h" #include "stdio.h" #include "assert.h" -#include "vga.h" +#include "io/vga/vga.h" /// Describe start address of grub multiboot modules. char *module_start[MAX_MODULES]; diff --git a/mentos/src/kernel/sys.c b/mentos/src/kernel/sys.c index e968039..a9e5581 100644 --- a/mentos/src/kernel/sys.c +++ b/mentos/src/kernel/sys.c @@ -5,10 +5,10 @@ /// See LICENSE.md for details. #include "stdio.h" -#include "errno.h" -#include "reboot.h" -#include "stdatomic.h" -#include "mutex.h" +#include "sys/errno.h" +#include "sys/reboot.h" +#include "klib/stdatomic.h" +#include "klib/mutex.h" static void machine_power_off() { diff --git a/mentos/src/klib/assert.c b/mentos/src/klib/assert.c index 63960a1..9621daf 100644 --- a/mentos/src/klib/assert.c +++ b/mentos/src/klib/assert.c @@ -6,7 +6,7 @@ #include "assert.h" #include "stdio.h" -#include "panic.h" +#include "system/panic.h" void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) { diff --git a/mentos/src/klib/hashmap.c b/mentos/src/klib/hashmap.c index 27d7d8a..3033aba 100644 --- a/mentos/src/klib/hashmap.c +++ b/mentos/src/klib/hashmap.c @@ -4,10 +4,10 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "hashmap.h" +#include "klib/hashmap.h" #include "assert.h" #include "string.h" -#include "slab.h" +#include "mem/slab.h" /// @brief Stores information of an entry of the hashmap. struct hashmap_entry_t { diff --git a/mentos/src/klib/libgen.c b/mentos/src/klib/libgen.c index a3fa585..10283c6 100644 --- a/mentos/src/klib/libgen.c +++ b/mentos/src/klib/libgen.c @@ -4,13 +4,13 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "syscall.h" +#include "system/syscall.h" #include "libgen.h" #include "string.h" -#include "initrd.h" +#include "fs/initrd.h" #include "limits.h" #include "assert.h" -#include "paging.h" +#include "mem/paging.h" int parse_path(char *out, char **cur, char sep, size_t max) { diff --git a/mentos/src/klib/list.c b/mentos/src/klib/list.c index 4209f2a..836c6c6 100644 --- a/mentos/src/klib/list.c +++ b/mentos/src/klib/list.c @@ -4,10 +4,10 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "list.h" +#include "klib/list.h" #include "assert.h" #include "string.h" -#include "slab.h" +#include "mem/slab.h" static inline listnode_t *__node_alloc() { diff --git a/mentos/src/klib/mutex.c b/mentos/src/klib/mutex.c index 4d9e1f1..6a921ea 100644 --- a/mentos/src/klib/mutex.c +++ b/mentos/src/klib/mutex.c @@ -4,8 +4,8 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "mutex.h" -#include "debug.h" +#include "klib/mutex.h" +#include "misc/debug.h" void mutex_lock(mutex_t *mutex, uint32_t owner) { diff --git a/mentos/src/klib/ndtree.c b/mentos/src/klib/ndtree.c index 3564c5a..095d4d8 100644 --- a/mentos/src/klib/ndtree.c +++ b/mentos/src/klib/ndtree.c @@ -4,11 +4,11 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "debug.h" -#include "ndtree.h" +#include "misc/debug.h" +#include "klib/ndtree.h" #include "assert.h" -#include "list_head.h" -#include "slab.h" +#include "klib/list_head.h" +#include "mem/slab.h" // ============================================================================ // Tree types. diff --git a/mentos/src/klib/rbtree.c b/mentos/src/klib/rbtree.c index ca686c5..7f6abe8 100644 --- a/mentos/src/klib/rbtree.c +++ b/mentos/src/klib/rbtree.c @@ -4,11 +4,11 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "rbtree.h" +#include "klib/rbtree.h" #include "assert.h" -#include "debug.h" -#include "slab.h" +#include "misc/debug.h" +#include "mem/slab.h" /// @brief Stores information of a node. struct rbtree_node_t { diff --git a/mentos/src/klib/spinlock.c b/mentos/src/klib/spinlock.c index 6f15c43..5c59035 100644 --- a/mentos/src/klib/spinlock.c +++ b/mentos/src/klib/spinlock.c @@ -4,7 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "spinlock.h" +#include "klib/spinlock.h" void spinlock_init(spinlock_t *spinlock) { diff --git a/mentos/src/klib/string.c b/mentos/src/klib/string.c index b3e4b6c..9cbd601 100644 --- a/mentos/src/klib/string.c +++ b/mentos/src/klib/string.c @@ -4,7 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "kheap.h" +#include "mem/kheap.h" #include "stdio.h" #include "fcntl.h" #include "string.h" diff --git a/mentos/src/klib/time.c b/mentos/src/klib/time.c index 8898a35..2b97404 100644 --- a/mentos/src/klib/time.c +++ b/mentos/src/klib/time.c @@ -4,13 +4,13 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "debug.h" +#include "misc/debug.h" #include "time.h" #include "stdio.h" #include "stddef.h" -#include "port_io.h" -#include "timer.h" -#include "rtc.h" +#include "io/port_io.h" +#include "hardware/timer.h" +#include "drivers/rtc.h" static const char *str_weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; diff --git a/mentos/src/klib/vscanf.c b/mentos/src/klib/vscanf.c index 82bf75e..b811d6b 100644 --- a/mentos/src/klib/vscanf.c +++ b/mentos/src/klib/vscanf.c @@ -4,10 +4,10 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "vfs.h" +#include "fs/vfs.h" #include "ctype.h" #include "string.h" -#include "debug.h" +#include "misc/debug.h" #include "stdio.h" static int vsscanf(const char *buf, const char *s, va_list ap) diff --git a/mentos/src/klib/vsprintf.c b/mentos/src/klib/vsprintf.c index 47a123b..d44d4a7 100644 --- a/mentos/src/klib/vsprintf.c +++ b/mentos/src/klib/vsprintf.c @@ -11,7 +11,7 @@ #include "stdbool.h" #include "stdint.h" #include "stdio.h" -#include "video.h" +#include "io/video.h" #include "fcvt.h" /// Size of the buffer used to call cvt functions. diff --git a/mentos/src/mem/buddysystem.c b/mentos/src/mem/buddysystem.c index 900087c..d1cd2b1 100644 --- a/mentos/src/mem/buddysystem.c +++ b/mentos/src/mem/buddysystem.c @@ -7,11 +7,11 @@ /// Change the header. #define __DEBUG_HEADER__ "[BUDDY ]" -#include "buddysystem.h" -#include "paging.h" +#include "mem/buddysystem.h" +#include "mem/paging.h" #include "assert.h" -#include "debug.h" -#include "panic.h" +#include "misc/debug.h" +#include "system/panic.h" /// @brief Cache level low limit after which allocation starts. #define LOW_WATERMARK_LEVEL 10 diff --git a/mentos/src/mem/kheap.c b/mentos/src/mem/kheap.c index 12adba4..6f9ffe0 100644 --- a/mentos/src/mem/kheap.c +++ b/mentos/src/mem/kheap.c @@ -7,13 +7,13 @@ /// Change the header. #define __DEBUG_HEADER__ "[KHEAP ]" -#include "kheap.h" +#include "mem/kheap.h" #include "math.h" -#include "debug.h" +#include "misc/debug.h" #include "string.h" -#include "paging.h" +#include "mem/paging.h" #include "assert.h" -#include "list_head.h" +#include "klib/list_head.h" /// Overhead given by the block_t itself. #define OVERHEAD sizeof(block_t) diff --git a/mentos/src/mem/paging.c b/mentos/src/mem/paging.c index 34357f4..11601dd 100644 --- a/mentos/src/mem/paging.c +++ b/mentos/src/mem/paging.c @@ -7,15 +7,15 @@ /// Change the header. #define __DEBUG_HEADER__ "[PAGING]" -#include "paging.h" -#include "isr.h" -#include "vmem_map.h" -#include "zone_allocator.h" -#include "kheap.h" -#include "debug.h" +#include "mem/paging.h" +#include "descriptor_tables/isr.h" +#include "mem/vmem_map.h" +#include "mem/zone_allocator.h" +#include "mem/kheap.h" +#include "misc/debug.h" #include "assert.h" #include "string.h" -#include "panic.h" +#include "system/panic.h" /// Cache for storing mm_struct. kmem_cache_t *mm_cache; diff --git a/mentos/src/mem/slab.c b/mentos/src/mem/slab.c index 7417b68..744c374 100644 --- a/mentos/src/mem/slab.c +++ b/mentos/src/mem/slab.c @@ -7,11 +7,11 @@ /// Change the header. #define __DEBUG_HEADER__ "[SLAB ]" -#include "zone_allocator.h" -#include "paging.h" +#include "mem/zone_allocator.h" +#include "mem/paging.h" #include "assert.h" -#include "debug.h" -#include "slab.h" +#include "misc/debug.h" +#include "mem/slab.h" /// @brief Use it to manage cached pages. typedef struct kmem_obj { diff --git a/mentos/src/mem/vmem_map.c b/mentos/src/mem/vmem_map.c index 4d45cc7..2bed3cc 100644 --- a/mentos/src/mem/vmem_map.c +++ b/mentos/src/mem/vmem_map.c @@ -7,9 +7,9 @@ /// Change the header. #define __DEBUG_HEADER__ "[VMEM ]" -#include "vmem_map.h" +#include "mem/vmem_map.h" #include "string.h" -#include "panic.h" +#include "system/panic.h" /// Virtual addresses manager. static virt_map_page_manager_t virt_default_mapping; diff --git a/mentos/src/mem/zone_allocator.c b/mentos/src/mem/zone_allocator.c index 67ffa16..5c805d4 100644 --- a/mentos/src/mem/zone_allocator.c +++ b/mentos/src/mem/zone_allocator.c @@ -7,14 +7,14 @@ /// Change the header. #define __DEBUG_HEADER__ "[PMM ]" -#include "zone_allocator.h" -#include "buddysystem.h" -#include "list_head.h" +#include "mem/zone_allocator.h" +#include "mem/buddysystem.h" +#include "klib/list_head.h" #include "kernel.h" #include "assert.h" -#include "paging.h" +#include "mem/paging.h" #include "string.h" -#include "debug.h" +#include "misc/debug.h" /// TODO: Comment. #define MIN_PAGE_ALIGN(addr) ((addr) & (~(PAGE_SIZE - 1))) diff --git a/mentos/src/misc/debug.c b/mentos/src/misc/debug.c index 7f7ff75..c3e2be5 100644 --- a/mentos/src/misc/debug.c +++ b/mentos/src/misc/debug.c @@ -5,13 +5,13 @@ /// See LICENSE.md for details. #include "sys/bitops.h" -#include "debug.h" -#include "spinlock.h" -#include "port_io.h" +#include "misc/debug.h" +#include "klib/spinlock.h" +#include "io/port_io.h" #include "kernel.h" #include "string.h" #include "stdio.h" -#include "video.h" +#include "io/video.h" /// Serial port for QEMU. #define SERIAL_COM1 (0x03F8) diff --git a/mentos/src/multiboot.c b/mentos/src/multiboot.c index 6874ea1..e9801c5 100644 --- a/mentos/src/multiboot.c +++ b/mentos/src/multiboot.c @@ -10,8 +10,8 @@ #include "kernel.h" #include "sys/bitops.h" #include "stddef.h" -#include "debug.h" -#include "panic.h" +#include "misc/debug.h" +#include "system/panic.h" #include "stddef.h" diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index 528a831..b2eb124 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -5,26 +5,26 @@ /// See LICENSE.md for details. -#include "kernel_levels.h" +#include "sys/kernel_levels.h" //#ifndef __DEBUG_LEVEL__ //#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG //#endif -#include -#include "process.h" -#include "scheduler.h" +#include "process/process.h" +#include "process/scheduler.h" #include "assert.h" #include "libgen.h" #include "string.h" -#include "timer.h" +#include "hardware/timer.h" +#include "sys/errno.h" #include "fcntl.h" -#include "panic.h" -#include "debug.h" -#include "wait.h" -#include "prio.h" -#include "vfs.h" -#include "elf.h" +#include "system/panic.h" +#include "misc/debug.h" +#include "process/wait.h" +#include "process/prio.h" +#include "fs/vfs.h" +#include "elf/elf.h" /// Cache for creating the task structs. static kmem_cache_t *task_struct_cache; diff --git a/mentos/src/process/scheduler.c b/mentos/src/process/scheduler.c index e578c85..accfd33 100644 --- a/mentos/src/process/scheduler.c +++ b/mentos/src/process/scheduler.c @@ -9,20 +9,20 @@ #include "assert.h" #include "strerror.h" -#include "vfs.h" -#include "scheduler.h" -#include "tss.h" -#include "fpu.h" -#include "prio.h" -#include "wait.h" -#include "kheap.h" -#include "panic.h" -#include "debug.h" +#include "fs/vfs.h" +#include "process/scheduler.h" +#include "descriptor_tables/tss.h" +#include "devices/fpu.h" +#include "process/prio.h" +#include "process/wait.h" +#include "mem/kheap.h" +#include "system/panic.h" +#include "misc/debug.h" #include "time.h" -#include "errno.h" -#include "list_head.h" -#include "paging.h" -#include "timer.h" +#include "sys/errno.h" +#include "klib/list_head.h" +#include "mem/paging.h" +#include "hardware/timer.h" #include "math.h" #include "stdio.h" diff --git a/mentos/src/process/scheduler_algorithm.c b/mentos/src/process/scheduler_algorithm.c index 656389d..9eec80b 100644 --- a/mentos/src/process/scheduler_algorithm.c +++ b/mentos/src/process/scheduler_algorithm.c @@ -4,13 +4,13 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "timer.h" -#include "prio.h" -#include "debug.h" +#include "hardware/timer.h" +#include "process/prio.h" +#include "misc/debug.h" #include "assert.h" -#include "list_head.h" -#include "wait.h" -#include "scheduler.h" +#include "klib/list_head.h" +#include "process/wait.h" +#include "process/scheduler.h" /// @brief Updates task execution statistics. /// @param task the task to update. diff --git a/mentos/src/process/wait.c b/mentos/src/process/wait.c index 6680e27..9b37b82 100644 --- a/mentos/src/process/wait.c +++ b/mentos/src/process/wait.c @@ -7,7 +7,7 @@ /// Change the header. #define __DEBUG_HEADER__ "[WAIT ]" -#include "wait.h" +#include "process/wait.h" static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) { diff --git a/mentos/src/sys/module.c b/mentos/src/sys/module.c index 761103d..88d8b7b 100644 --- a/mentos/src/sys/module.c +++ b/mentos/src/sys/module.c @@ -4,11 +4,11 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "slab.h" -#include "module.h" +#include "mem/slab.h" +#include "sys/module.h" #include "string.h" #include "sys/bitops.h" -#include "debug.h" +#include "misc/debug.h" /// Defined in kernel.ld, points at the end of kernel's data segment. extern void *_kernel_end; diff --git a/mentos/src/sys/utsname.c b/mentos/src/sys/utsname.c index cd3e6eb..9a10dc2 100644 --- a/mentos/src/sys/utsname.c +++ b/mentos/src/sys/utsname.c @@ -5,12 +5,12 @@ /// See LICENSE.md for details. #include "string.h" -#include "utsname.h" +#include "sys/utsname.h" #include "version.h" -#include "debug.h" -#include "errno.h" +#include "misc/debug.h" +#include "sys/errno.h" #include "fcntl.h" -#include "vfs.h" +#include "fs/vfs.h" static inline int __gethostname(char *name, size_t len) { diff --git a/mentos/src/system/errno.c b/mentos/src/system/errno.c index c52a17f..c4bc9e1 100644 --- a/mentos/src/system/errno.c +++ b/mentos/src/system/errno.c @@ -4,8 +4,8 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "errno.h" -#include "scheduler.h" +#include "sys/errno.h" +#include "process/scheduler.h" /// @brief Returns the error number for the current process. /// @return Pointer to the error number. diff --git a/mentos/src/system/panic.c b/mentos/src/system/panic.c index c37b61e..6693357 100644 --- a/mentos/src/system/panic.c +++ b/mentos/src/system/panic.c @@ -4,8 +4,8 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "panic.h" -#include "debug.h" +#include "system/panic.h" +#include "misc/debug.h" void kernel_panic(const char *msg) { diff --git a/mentos/src/system/printk.c b/mentos/src/system/printk.c index 9d64247..00e475e 100644 --- a/mentos/src/system/printk.c +++ b/mentos/src/system/printk.c @@ -4,10 +4,10 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "printk.h" +#include "system/printk.h" #include "stdarg.h" #include "stdio.h" -#include "video.h" +#include "io/video.h" int sys_syslog(const char *format, ...) { diff --git a/mentos/src/system/signal.c b/mentos/src/system/signal.c index 17b8467..dfc8f7e 100644 --- a/mentos/src/system/signal.c +++ b/mentos/src/system/signal.c @@ -7,15 +7,15 @@ /// Change the header. #define __DEBUG_HEADER__ "[SIGNAL]" -#include "signal.h" -#include "wait.h" -#include "scheduler.h" -#include "process.h" -#include "errno.h" +#include "system/signal.h" +#include "process/wait.h" +#include "process/scheduler.h" +#include "process/process.h" +#include "sys/errno.h" #include "assert.h" -#include "debug.h" +#include "misc/debug.h" #include "string.h" -#include "irqflags.h" +#include "klib/irqflags.h" /// SLAB caches for signal bits. static kmem_cache_t *sigqueue_cachep; diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index c727119..b8931df 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -4,17 +4,17 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "fpu.h" -#include "kheap.h" -#include "syscall.h" -#include "isr.h" -#include "errno.h" +#include "devices/fpu.h" +#include "mem/kheap.h" +#include "system/syscall.h" +#include "descriptor_tables/isr.h" +#include "sys/errno.h" #include "kernel.h" -#include "process.h" -#include "scheduler.h" -#include "utsname.h" -#include "ioctl.h" -#include "timer.h" +#include "process/process.h" +#include "process/scheduler.h" +#include "sys/utsname.h" +#include "fs/ioctl.h" +#include "hardware/timer.h" #include "ipc/msg.h" #include "ipc/sem.h" From 064136703cc73433173a3f02a1a9443de66edd8a Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Wed, 13 Oct 2021 12:39:36 +0200 Subject: [PATCH 19/21] Add missing comments. --- doc/Doxygen | 3 +- doc/doxygen.css | 271 +++- doc/doxygen.scss | 2298 +++++++++++++++---------------- mentos/inc/io/vga/vga_font.h | 8 +- mentos/inc/io/vga/vga_mode.h | 3 +- mentos/inc/io/vga/vga_palette.h | 9 +- mentos/inc/misc/debug.h | 1 + mentos/src/io/vga/vga.c | 21 +- mentos/src/multiboot.c | 1 + 9 files changed, 1326 insertions(+), 1289 deletions(-) diff --git a/doc/Doxygen b/doc/Doxygen index 5cd223a..bc4c7bb 100644 --- a/doc/Doxygen +++ b/doc/Doxygen @@ -825,7 +825,8 @@ INPUT = @CMAKE_SOURCE_DIR@/README.md \ @CMAKE_SOURCE_DIR@/TODO.md \ @CMAKE_SOURCE_DIR@/doc/signal.md \ @CMAKE_SOURCE_DIR@/doc/syscall.md \ - @CMAKE_SOURCE_DIR@/mentos + @CMAKE_SOURCE_DIR@/mentos \ + @CMAKE_SOURCE_DIR@/libc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/doc/doxygen.css b/doc/doxygen.css index 9011869..1595128 100644 --- a/doc/doxygen.css +++ b/doc/doxygen.css @@ -4,11 +4,18 @@ .sm-dox { background: #4b5263; } -.sm-dox a, .sm-dox a:focus, .sm-dox a:active, .sm-dox a:hover, .sm-dox a.highlighted { +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:active, +.sm-dox a:hover, +.sm-dox a.highlighted { color: #A9B7C6; background: none; } -.sm-dox a, .sm-dox a:focus, .sm-dox a:hover, .sm-dox a:active { +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:hover, +.sm-dox a:active { text-shadow: none; outline: none; } @@ -16,7 +23,11 @@ background-color: #333842; } /* Stylesheet generated by Doxygen */ -body, table, div, p, dl { +body, +table, +div, +p, +dl { font: 400 14px/22px Roboto, sans-serif; display: block; margin-block-start: 0em; @@ -33,7 +44,8 @@ ul { margin-inline-end: 0px; padding-inline-start: 30px; } -p.reference, p.definition { +p.reference, +p.definition { font: 400 14px/22px Roboto, sans-serif; } /* @group Heading Levels */ @@ -56,7 +68,12 @@ h2.groupheader { h3.groupheader { font-size: 100%; } -h1, h2, h3, h4, h5, h6 { +h1, +h2, +h3, +h4, +h5, +h6 { margin-right: 15px; display: block; margin-block-start: 0.25em; @@ -66,7 +83,12 @@ h1, h2, h3, h4, h5, h6 { font-weight: bold; color: #d7dee4; } -h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { +h1.glow, +h2.glow, +h3.glow, +h4.glow, +h5.glow, +h6.glow { text-shadow: 0 0 15px cyan; } dt { @@ -80,7 +102,8 @@ ul.multicol { -webkit-column-count: 3; column-count: 3; } -p.startli, p.startdd { +p.startli, +p.startdd { margin-top: 2px; } p.starttd { @@ -103,15 +126,21 @@ span.legend { font-size: 70%; text-align: center; } +span.arrow { + /* width: 32px; */ + padding-left: 0px; } + h3.version { font-size: 90%; text-align: center; } -div.qindex, div.navtab { +div.qindex, +div.navtab { background-color: #333842; text-align: center; } -div.qindex, div.navpath { +div.qindex, +div.navpath { width: 100%; line-height: 140%; } @@ -146,10 +175,14 @@ a.qindexHL { a.el { font-weight: bold; } -a.line, a.line:visited { +a.line, +a.line:visited { color: #A9B7C6; } -a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { +a.codeRef, +a.codeRef:visited, +a.lineRef, +a.lineRef:visited { color: #4665A2; } /* @end */ @@ -236,7 +269,8 @@ span.lineno a:hover { -ms-user-select: none; user-select: none; } -div.ah, span.ah { +div.ah, +span.ah { background-color: #16181d; font-weight: bold; color: #FFFFFF; @@ -287,7 +321,8 @@ tr.memlist { p.formulaDsp { text-align: center; } -img.formulaInl, img.inline { +img.formulaInl, +img.inline { vertical-align: middle; } div.center { @@ -430,28 +465,36 @@ table.memberdecls { border-spacing: 0px; padding: 0px; } -.memberdecls td.glow, .fieldtable tr.glow { +.memberdecls td.glow, +.fieldtable tr.glow { background-color: cyan; box-shadow: 0 0 15px cyan; } -.memItemLeft, .memItemRight { +.memItemLeft, +.memItemRight { font-family: monaco, Consolas, "Lucida Console", monospace; } -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { +.mdescLeft, +.mdescRight, +.memItemLeft, +.memItemRight, +.memTemplItemLeft, +.memTemplItemRight, +.memTemplParams { border: none; margin: 4px; padding: 1px 0 0 8px; } -.mdescLeft, .mdescRight { +.mdescLeft, +.mdescRight { padding: 0px 8px 4px 8px; } .memSeparator { background-color: #282c34; line-height: 1px; } -.memItemLeft, .memTemplItemLeft { +.memItemLeft, +.memTemplItemLeft { white-space: nowrap; } .memItemRight { @@ -519,14 +562,16 @@ table.memberdecls { .memname td { vertical-align: bottom; } -.memproto, dl.reflist dt { +.memproto, +dl.reflist dt { padding: 0.5em 0; } .overload { font-family: "courier new", courier, monospace; font-size: 65%; } -.memdoc, dl.reflist dd { +.memdoc, +dl.reflist dd { padding: 1em; } dl.reflist dt { @@ -553,19 +598,27 @@ dl.reflist dd { .paramname code { line-height: 14px; } -.params, .retval, .exception, .tparams { +.params, +.retval, +.exception, +.tparams { margin-left: 0px; padding-left: 0px; } -.params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { +.params .paramname, +.retval .paramname, +.tparams .paramname, +.exception .paramname { font-weight: bold; vertical-align: top; } -.params .paramtype, .tparams .paramtype { +.params .paramtype, +.tparams .paramtype { font-style: italic; vertical-align: top; } -.params .paramdir, .tparams .paramdir { +.params .paramdir, +.tparams .paramdir { font-family: "courier new", courier, monospace; vertical-align: top; } @@ -730,7 +783,8 @@ table.doxtable { margin-top: 4px; margin-bottom: 4px; } -table.doxtable td, table.doxtable th { +table.doxtable td, +table.doxtable th { border: 1px solid #2D4068; padding: 3px 7px 2px; } @@ -753,10 +807,12 @@ table.fieldtable { -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } -.fieldtable td, .fieldtable th { +.fieldtable td, +.fieldtable th { padding: 3px 7px 2px; } -.fieldtable td.fieldtype, .fieldtable td.fieldname { +.fieldtable td.fieldtype, +.fieldtable td.fieldname { white-space: nowrap; border-right: 1px solid #A8B8D9; border-bottom: 1px solid #A8B8D9; @@ -921,13 +977,15 @@ dl.note.DocNodeRTL { border-right: 4px solid; border-color: #D0C000; } -dl.warning, dl.attention { +dl.warning, +dl.attention { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #FF0000; } -dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { +dl.warning.DocNodeRTL, +dl.attention.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; @@ -936,13 +994,17 @@ dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { border-right: 4px solid; border-color: #FF0000; } -dl.pre, dl.post, dl.invariant { +dl.pre, +dl.post, +dl.invariant { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #00D000; } -dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { +dl.pre.DocNodeRTL, +dl.post.DocNodeRTL, +dl.invariant.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; @@ -1047,7 +1109,8 @@ dl.section dd { margin: 0px; width: 100%; color: #A9B7C6; - background-color: #282c34; } + background-color: #282c34; + border-bottom: 3px solid #A9B7C6; } .image { text-align: center; @@ -1209,43 +1272,65 @@ div.toc li.level4 { padding: 0px; font: 12px/16px Roboto, sans-serif; } -#powerTip:before, #powerTip:after { +#powerTip:before, +#powerTip:after { content: ""; position: absolute; margin: 0px; } -#powerTip.n:after, #powerTip.n:before, -#powerTip.s:after, #powerTip.s:before, -#powerTip.w:after, #powerTip.w:before, -#powerTip.e:after, #powerTip.e:before, -#powerTip.ne:after, #powerTip.ne:before, -#powerTip.se:after, #powerTip.se:before, -#powerTip.nw:after, #powerTip.nw:before, -#powerTip.sw:after, #powerTip.sw:before { +#powerTip.n:after, +#powerTip.n:before, +#powerTip.s:after, +#powerTip.s:before, +#powerTip.w:after, +#powerTip.w:before, +#powerTip.e:after, +#powerTip.e:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.nw:after, +#powerTip.nw:before, +#powerTip.sw:after, +#powerTip.sw:before { border: solid transparent; content: " "; height: 0; width: 0; position: absolute; } -#powerTip.n:after, #powerTip.s:after, -#powerTip.w:after, #powerTip.e:after, -#powerTip.nw:after, #powerTip.ne:after, -#powerTip.sw:after, #powerTip.se:after { +#powerTip.n:after, +#powerTip.s:after, +#powerTip.w:after, +#powerTip.e:after, +#powerTip.nw:after, +#powerTip.ne:after, +#powerTip.sw:after, +#powerTip.se:after { border-color: rgba(255, 255, 255, 0); } -#powerTip.n:before, #powerTip.s:before, -#powerTip.w:before, #powerTip.e:before, -#powerTip.nw:before, #powerTip.ne:before, -#powerTip.sw:before, #powerTip.se:before { +#powerTip.n:before, +#powerTip.s:before, +#powerTip.w:before, +#powerTip.e:before, +#powerTip.nw:before, +#powerTip.ne:before, +#powerTip.sw:before, +#powerTip.se:before { border-color: rgba(128, 128, 128, 0); } -#powerTip.n:after, #powerTip.n:before, -#powerTip.ne:after, #powerTip.ne:before, -#powerTip.nw:after, #powerTip.nw:before { +#powerTip.n:after, +#powerTip.n:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.nw:after, +#powerTip.nw:before { top: 100%; } -#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { +#powerTip.n:after, +#powerTip.ne:after, +#powerTip.nw:after { border-top-color: #FFFFFF; border-width: 10px; margin: 0px -10px; } @@ -1255,40 +1340,54 @@ div.toc li.level4 { border-width: 11px; margin: 0px -11px; } -#powerTip.n:after, #powerTip.n:before { +#powerTip.n:after, +#powerTip.n:before { left: 50%; } -#powerTip.nw:after, #powerTip.nw:before { +#powerTip.nw:after, +#powerTip.nw:before { right: 14px; } -#powerTip.ne:after, #powerTip.ne:before { +#powerTip.ne:after, +#powerTip.ne:before { left: 14px; } -#powerTip.s:after, #powerTip.s:before, -#powerTip.se:after, #powerTip.se:before, -#powerTip.sw:after, #powerTip.sw:before { +#powerTip.s:after, +#powerTip.s:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.sw:after, +#powerTip.sw:before { bottom: 100%; } -#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { +#powerTip.s:after, +#powerTip.se:after, +#powerTip.sw:after { border-bottom-color: #FFFFFF; border-width: 10px; margin: 0px -10px; } -#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { +#powerTip.s:before, +#powerTip.se:before, +#powerTip.sw:before { border-bottom-color: #808080; border-width: 11px; margin: 0px -11px; } -#powerTip.s:after, #powerTip.s:before { +#powerTip.s:after, +#powerTip.s:before { left: 50%; } -#powerTip.sw:after, #powerTip.sw:before { +#powerTip.sw:after, +#powerTip.sw:before { right: 14px; } -#powerTip.se:after, #powerTip.se:before { +#powerTip.se:after, +#powerTip.se:before { left: 14px; } -#powerTip.e:after, #powerTip.e:before { +#powerTip.e:after, +#powerTip.e:before { left: 100%; } #powerTip.e:after { @@ -1303,7 +1402,8 @@ div.toc li.level4 { top: 50%; margin-top: -11px; } -#powerTip.w:after, #powerTip.w:before { +#powerTip.w:after, +#powerTip.w:before { right: 100%; } #powerTip.w:after { @@ -1327,7 +1427,12 @@ div.toc li.level4 { display: none; } body { overflow: visible; } - h1, h2, h3, h4, h5, h6 { + h1, + h2, + h3, + h4, + h5, + h6 { page-break-after: avoid; } .summary { display: none; } @@ -1469,3 +1574,31 @@ direction:ltr; /* @end */ u { text-decoration: underline; } + +/* +#nav-tree { + padding: 0px 0px; + font-size: 14px; + overflow: auto; +} +*/ +/* +.ui-resizable-e { + background: $white-color; + width: 3px; +} + +#nav-tree { + padding: 0px 0px; + background-color: $black-color; + background-image: none; + font-size: 14px; + overflow: auto; +} + +#nav-tree .selected { + background-color: $code-bg-color; + background-image: none; + color: $white-color; +} +*/ diff --git a/doc/doxygen.scss b/doc/doxygen.scss index 9ac2100..cb1235a 100644 --- a/doc/doxygen.scss +++ b/doc/doxygen.scss @@ -7,617 +7,575 @@ $cyan-color: #56b6c2; $yellow-color: #e5c07b; $blue-color: #61afef; $gutter-color: #4b5263; - // $code-font: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; -$code-font: monaco, Consolas, "Lucida Console", monospace; - +$code-font: monaco, +Consolas, +"Lucida Console", +monospace; $title-fg-color: $white-color; $title-bg-color: $black-color; - $menu-bg-color: $gutter-color; - $breadcrumb-fg-color: lighten($white-color, 0%); $breadcrumb-bg-color: lighten($black-color, 10%); $breadcrumb-arrow-color: $white-color; - $content-fg-color: $white-color; $content-bg-color: $black-color; - $headers-fg-color: lighten($white-color, 15%); - $header-bg-color: lighten($black-color, 15%); - $table-bg-color: lighten($black-color, 5%); - $image-bg-color: lighten($black-color, 25%); - $code-border-color: $gutter-color; $code-fg-color: $white-color; $code-bg-color: lighten($black-color, 5%); - $link-color: lighten(#4665A2, 15%); $link-visited-color: lighten(#4665A2, 10%); $link-hover-color: lighten(#4665A2, 25%); - $footer-fg-color: darken($white-color, 10%); $footer-bg-color: darken($black-color, 10%); - $ah-bg-color: darken($black-color, 8%); /* The following are overrides over hard-coded styles in Doxygen */ .sm-dox { - background: $menu-bg-color; - + background: $menu-bg-color; } -.sm-dox a, .sm-dox a:focus, .sm-dox a:active, .sm-dox a:hover, .sm-dox a.highlighted { - color: $white-color; - background: none; - +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:active, +.sm-dox a:hover, +.sm-dox a.highlighted { + color: $white-color; + background: none; } -.sm-dox a, .sm-dox a:focus, .sm-dox a:hover, .sm-dox a:active { - text-shadow: none; - outline: none; - +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:hover, +.sm-dox a:active { + text-shadow: none; + outline: none; } .memberdecls tr:not(:first-child) { - background-color: $table-bg-color; + background-color: $table-bg-color; } + /* Stylesheet generated by Doxygen */ -body, table, div, p, dl { - font: 400 14px/22px Roboto, sans-serif; - display: block; - margin-block-start: 0em; - margin-block-end: 0em; - margin-inline-start: 0px; - margin-inline-end: 0px; +body, +table, +div, +p, +dl { + font: 400 14px/22px Roboto, sans-serif; + display: block; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; } ul { - display: block; - list-style-type: disc; - margin-block-start: 0em; - margin-block-end: 0em; - margin-inline-start: 0px; - margin-inline-end: 0px; - padding-inline-start: 30px; + display: block; + list-style-type: disc; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 30px; } -p.reference, p.definition { - font: 400 14px/22px Roboto, sans-serif; - +p.reference, +p.definition { + font: 400 14px/22px Roboto, sans-serif; } + /* @group Heading Levels */ h1.groupheader { - font-size: 150%; - + font-size: 150%; } .title { - font: 400 14px/28px Roboto, sans-serif; - font-size: 150%; - font-weight: bold; - margin: 10px 2px; - + font: 400 14px/28px Roboto, sans-serif; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; } h2.groupheader { - // border-bottom: 1px solid $tabel-fg-color; - // color: #354C7B; - border-radius: 4px; - background-color: $header-bg-color; - font-size: 150%; - font-weight: normal; - //margin-top: 1.75em; - padding: 0.5em; - // width: 100%; + // border-bottom: 1px solid $tabel-fg-color; + // color: #354C7B; + border-radius: 4px; + background-color: $header-bg-color; + font-size: 150%; + font-weight: normal; + //margin-top: 1.75em; + padding: 0.5em; + // width: 100%; } h3.groupheader { - font-size: 100%; - + font-size: 100%; } -h1, h2, h3, h4, h5, h6 { - // -webkit-transition: text-shadow 0.5s linear; - // -moz-transition: text-shadow 0.5s linear; - // -ms-transition: text-shadow 0.5s linear; - // -o-transition: text-shadow 0.5s linear; - // transition: text-shadow 0.5s linear; - margin-right: 15px; - display: block; - margin-block-start: 0.25em; - margin-block-end: 0.25em; - margin-inline-start: 0px; - margin-inline-end: 0px; - font-weight: bold; - color: $headers-fg-color; +h1, +h2, +h3, +h4, +h5, +h6 { + // -webkit-transition: text-shadow 0.5s linear; + // -moz-transition: text-shadow 0.5s linear; + // -ms-transition: text-shadow 0.5s linear; + // -o-transition: text-shadow 0.5s linear; + // transition: text-shadow 0.5s linear; + margin-right: 15px; + display: block; + margin-block-start: 0.25em; + margin-block-end: 0.25em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + color: $headers-fg-color; } -h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { - text-shadow: 0 0 15px cyan; - +h1.glow, +h2.glow, +h3.glow, +h4.glow, +h5.glow, +h6.glow { + text-shadow: 0 0 15px cyan; } dt { - font-weight: bold; - + font-weight: bold; } ul.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; - column-count: 3; - + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; } -p.startli, p.startdd { - margin-top: 2px; - +p.startli, +p.startdd { + margin-top: 2px; } p.starttd { - margin-top: 0px; - + margin-top: 0px; } p.endli { - margin-bottom: 0px; - + margin-bottom: 0px; } p.enddd { - margin-bottom: 4px; - + margin-bottom: 4px; } p.endtd { - margin-bottom: 2px; - + margin-bottom: 2px; } -p.interli { +p.interli {} -} +p.interdd {} -p.interdd { +p.intertd {} -} - -p.intertd { - -} /* @end */ caption { - font-weight: bold; - + font-weight: bold; } span.legend { - font-size: 70%; - text-align: center; + font-size: 70%; + text-align: center; +} +span.arrow { + /* width: 32px; */ + padding-left: 0px; } h3.version { - font-size: 90%; - text-align: center; - + font-size: 90%; + text-align: center; } -div.qindex, div.navtab { - background-color: $table-bg-color; - // border: 1px solid #A3B4D7; - text-align: center; +div.qindex, +div.navtab { + background-color: $table-bg-color; + // border: 1px solid #A3B4D7; + text-align: center; } -div.qindex, div.navpath { - width: 100%; - line-height: 140%; - +div.qindex, +div.navpath { + width: 100%; + line-height: 140%; } div.navtab { - margin-right: 15px; - + margin-right: 15px; } + /* @group Link Styling */ a { - color: $link-color; - font-weight: normal; - text-decoration: none; - + color: $link-color; + font-weight: normal; + text-decoration: none; } .contents a:visited { - color: $link-visited-color; - + color: $link-visited-color; } a:hover { - color: $link-hover-color; - text-decoration: underline; - + color: $link-hover-color; + text-decoration: underline; } a.qindex { - font-weight: bold; - + font-weight: bold; } a.qindexHL { - font-weight: bold; - background-color: #9CAFD4; - color: #FFFFFF; - border: 1px double #869DCA; - + font-weight: bold; + background-color: #9CAFD4; + color: #FFFFFF; + border: 1px double #869DCA; } .contents a.qindexHL:visited { - color: #FFFFFF; - + color: #FFFFFF; } a.el { - font-weight: bold; - + font-weight: bold; } -a.elRef { - -} - -a.line, a.line:visited { - color: $white-color; +a.elRef {} +a.line, +a.line:visited { + color: $white-color; } // a.code { // color: $link-color; // } - -a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { - color: #4665A2; - +a.codeRef, +a.codeRef:visited, +a.lineRef, +a.lineRef:visited { + color: #4665A2; } + /* @end */ dl.el { - margin-left: -1cm; - + margin-left: -1cm; } ul { - overflow: hidden; - /*Fixed: list item bullets overlap floating elements*/ + overflow: hidden; + /*Fixed: list item bullets overlap floating elements*/ } #side-nav ul { - overflow: visible; - /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ + overflow: visible; + /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ } #main-nav ul { - overflow: visible; - /* reset ul rule for the navigation bar drop down lists */ + overflow: visible; + /* reset ul rule for the navigation bar drop down lists */ } #main-nav li { - overflow: visible; + overflow: visible; } .fragment { - text-align: left; - direction: ltr; - overflow-x: auto; - /*Fixed: fragment lines overlap floating elements*/ - overflow-y: hidden; - + text-align: left; + direction: ltr; + overflow-x: auto; + /*Fixed: fragment lines overlap floating elements*/ + overflow-y: hidden; } pre.fragment { - // border: 1px solid $code-border-color; - // background-color: $code-bg-color; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - line-height: 125%; - font-family: $code-font; - font-size: 105%; - color: $code-fg-color; + // border: 1px solid $code-border-color; + // background-color: $code-bg-color; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + line-height: 125%; + font-family: $code-font; + font-size: 105%; + color: $code-fg-color; } div.fragment { - // padding: 0 0 1px 0; [>Fixed: last line underline overlap border<] - // margin: 4px 8px 4px 2px; - padding: 0.5em; - background-color: $code-bg-color; - border: 1px solid $code-bg-color; - color: $code-fg-color; + // padding: 0 0 1px 0; [>Fixed: last line underline overlap border<] + // margin: 4px 8px 4px 2px; + padding: 0.5em; + background-color: $code-bg-color; + border: 1px solid $code-bg-color; + color: $code-fg-color; } div.line { - font-family: monospace, fixed; - font-size: 13px; - min-height: 13px; - line-height: 1.0; - text-wrap: unrestricted; - white-space: -moz-pre-wrap; - /* Moz */ - white-space: -pre-wrap; - /* Opera 4-6 */ - white-space: -o-pre-wrap; - /* Opera 7 */ - white-space: pre-wrap; - /* CSS3 */ - word-wrap: break-word; - /* IE 5.5+ */ - text-indent: -53px; - padding-left: 53px; - padding-bottom: 0px; - margin: 0px; - transition: 0.5s; - + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; + /* Moz */ + white-space: -pre-wrap; + /* Opera 4-6 */ + white-space: -o-pre-wrap; + /* Opera 7 */ + white-space: pre-wrap; + /* CSS3 */ + word-wrap: break-word; + /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + transition: 0.5s; } // div.line:hover { // background-color: lighten($code-bg-color, 10%); // } - div.line.glow { - background-color: cyan; - box-shadow: 0 0 10px cyan; - + background-color: cyan; + box-shadow: 0 0 10px cyan; } span.lineno { - padding-right: 4px; - text-align: right; - white-space: pre; - //background-color: $gutter-color; - //border-right: 2px solid darken($gutter-color, 10%); + padding-right: 4px; + text-align: right; + white-space: pre; + //background-color: $gutter-color; + //border-right: 2px solid darken($gutter-color, 10%); } span.lineno a { - //background-color: lighten($gutter-color, 5%); - + //background-color: lighten($gutter-color, 5%); } span.lineno a:hover { - background-color: lighten($gutter-color, 5%); + background-color: lighten($gutter-color, 5%); } .lineno { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } -div.ah, span.ah { - background-color: $ah-bg-color; - font-weight: bold; - color: #FFFFFF; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border-radius: 0.5em; - +div.ah, +span.ah { + background-color: $ah-bg-color; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border-radius: 0.5em; } div.classindex ul { - list-style: none; - padding-left: 0; - + list-style: none; + padding-left: 0; } div.classindex span.ai { - display: inline-block; - + display: inline-block; } div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; - + margin-left: 16px; + margin-top: 12px; + font-weight: bold; } div.groupText { - margin-left: 16px; - font-style: italic; - + margin-left: 16px; + font-style: italic; } body { - background-color: $content-bg-color; - color: $content-fg-color; - margin: 0; - + background-color: $content-bg-color; + color: $content-fg-color; + margin: 0; } div.contents { - //margin: 1em; - //margin-top: 2em; - //margin-left: 0.5em; - //margin-right: 0.5em; - //margin-bottom: 2em; - - // margin-top: 10px; - // margin-left: 12px; - // margin-right: 8px; - //margin-bottom: 1em; + //margin: 1em; + //margin-top: 2em; + //margin-left: 0.5em; + //margin-right: 0.5em; + //margin-bottom: 2em; + // margin-top: 10px; + // margin-left: 12px; + // margin-right: 8px; + //margin-bottom: 1em; } td.indexkey { - background-color: #EBEFF6; - font-weight: bold; - border: 1px solid #C4CFE5; - margin: 2px 0px 2px 0; - padding: 2px 10px; - white-space: nowrap; - vertical-align: top; - + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; } td.indexvalue { - background-color: #EBEFF6; - border: 1px solid #C4CFE5; - padding: 2px 10px; - margin: 2px 0px; - + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; } tr.memlist { - background-color: #EEF1F7; - + background-color: #EEF1F7; } p.formulaDsp { - text-align: center; - + text-align: center; } -img.formulaDsp { - -} - -img.formulaInl, img.inline { - vertical-align: middle; +img.formulaDsp {} +img.formulaInl, +img.inline { + vertical-align: middle; } div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; - + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; } div.center img { - border: 0px; - + border: 0px; } address.footer { - font-style: normal; - padding-right: 12px; - + font-style: normal; + padding-right: 12px; } hr.footer { - flex: 1 1 auto; - + flex: 1 1 auto; } img.footer { - height: 22px; - border: 0px; - vertical-align: middle; + height: 22px; + border: 0px; + vertical-align: middle; } div.footer ul { - list-style-type: none; - list-style-image: none; - margin: 0; - padding: 0; + list-style-type: none; + list-style-image: none; + margin: 0; + padding: 0; } + /* @group Code Colorization */ span.keyword { - color: #CC7832 //#008000 - + color: #CC7832 //#008000 } span.keywordtype { - color: #CC7832 //#aa6e32 - + color: #CC7832 //#aa6e32 } span.keywordflow { - color: #CC7832 - + color: #CC7832 } span.comment { - color: #808080 //#5c6370 - + color: #808080 //#5c6370 } span.preprocessor { - color: #BBB529 - + color: #BBB529 } span.stringliteral { - color: #6A8759 - + color: #6A8759 } span.charliteral { - color: #008080 - + color: #008080 } span.vhdldigit { - color: #ff00ff - + color: #ff00ff } span.vhdlchar { - color: #000000 - + color: #000000 } span.vhdlkeyword { - color: #700070 - + color: #700070 } span.vhdllogic { - color: #ff0000 - + color: #ff0000 } blockquote { - background-color: #F7F8FB; - border-left: 2px solid #9CAFD4; - margin: 0 24px 0 4px; - padding: 0 12px 0 16px; - + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; } blockquote.DocNodeRTL { - border-left: 0; - border-right: 2px solid #9CAFD4; - margin: 0 4px 0 24px; - padding: 0 16px 0 12px; - + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; } + /* @end */ + /* .search { color: #003399; @@ -682,1360 +640,1264 @@ div.footer { /* @group Member Descriptions */ table.memberdecls { - border-spacing: 0px; - padding: 0px; - + border-spacing: 0px; + padding: 0px; } -.memberdecls td.glow, .fieldtable tr.glow { - background-color: cyan; - box-shadow: 0 0 15px cyan; - +.memberdecls td.glow, +.fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; } -.memItemLeft, .memItemRight { - font-family: $code-font; - +.memItemLeft, +.memItemRight { + font-family: $code-font; } -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - border: none; - margin: 4px; - padding: 1px 0 0 8px; - +.mdescLeft, +.mdescRight, +.memItemLeft, +.memItemRight, +.memTemplItemLeft, +.memTemplItemRight, +.memTemplParams { + border: none; + margin: 4px; + padding: 1px 0 0 8px; } -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - // color: $table-fg-color; +.mdescLeft, +.mdescRight { + padding: 0px 8px 4px 8px; + // color: $table-fg-color; } .memSeparator { - // display: none; - background-color: $content-bg-color; - line-height: 1px; - // border-bottom: 1px solid transparent; - // line-height: 1px; - // margin: 0px; - // padding: 0px; + // display: none; + background-color: $content-bg-color; + line-height: 1px; + // border-bottom: 1px solid transparent; + // line-height: 1px; + // margin: 0px; + // padding: 0px; } -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; - +.memItemLeft, +.memTemplItemLeft { + white-space: nowrap; } .memItemRight { - width: 100%; - + width: 100%; } .memTemplParams { - color: #4665A2; - white-space: nowrap; - font-size: 80%; - + color: #4665A2; + white-space: nowrap; + font-size: 80%; } + /* @end */ + /* @group Member Details */ + /* Styles for detailed member documentation */ .memtitle { - // padding: 8px; - // border-top: 1px solid #A8B8D9; - // border-left: 1px solid #A8B8D9; - // border-right: 1px solid #A8B8D9; - // margin-bottom: -1px; - // background-image: url('nav_f.png'); - // background-repeat: repeat-x; - // background-color: #E2E8F2; - border-top-right-radius: 4px; - border-top-left-radius: 4px; - margin: 0; - background-color: $table-bg-color; - padding: 0.5em; - font-weight: 300; - display: flex; - align-items: center; - float: left; - clear: both; - display: block; + // padding: 8px; + // border-top: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // margin-bottom: -1px; + // background-image: url('nav_f.png'); + // background-repeat: repeat-x; + // background-color: #E2E8F2; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin: 0; + background-color: $table-bg-color; + padding: 0.5em; + font-weight: 300; + display: flex; + align-items: center; + float: left; + clear: both; + display: block; } .permalink { - font-size: 65%; - display: inline-block; - vertical-align: middle; + font-size: 65%; + display: inline-block; + vertical-align: middle; } .permalink a:hover { - text-decoration: none; - + text-decoration: none; } .memtemplate { - font-size: 80%; - color: #4665A2; - font-weight: normal; - margin-left: 9px; - + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; } .memnav { - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; - + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; } .mempage { - width: 100%; - + width: 100%; } .memitem { - background-color: $table-bg-color; - clear: both; - margin-bottom: 1em; - // padding: 0; - // margin-bottom: 10px; - // margin-right: 5px; - // -webkit-transition: box-shadow 0.5s linear; - // -moz-transition: box-shadow 0.5s linear; - // -ms-transition: box-shadow 0.5s linear; - // -o-transition: box-shadow 0.5s linear; - // transition: box-shadow 0.5s linear; - // display: table !important; - // width: 100%; + background-color: $table-bg-color; + clear: both; + margin-bottom: 1em; + // padding: 0; + // margin-bottom: 10px; + // margin-right: 5px; + // -webkit-transition: box-shadow 0.5s linear; + // -moz-transition: box-shadow 0.5s linear; + // -ms-transition: box-shadow 0.5s linear; + // -o-transition: box-shadow 0.5s linear; + // transition: box-shadow 0.5s linear; + // display: table !important; + // width: 100%; } .memitem.glow { - box-shadow: 0 0 15px cyan; - + box-shadow: 0 0 15px cyan; } .memname { - font-family: $code-font; - font-weight: 400; - margin-left: 6px; - + font-family: $code-font; + font-weight: 400; + margin-left: 6px; } .memname td { - vertical-align: bottom; - + vertical-align: bottom; } -.memproto, dl.reflist dt { - padding: 0.5em 0; - // background-color: darken(#d19a66, 40%); - // border-top: 1px solid #A8B8D9; - // border-left: 1px solid #A8B8D9; - // border-right: 1px solid #A8B8D9; - // padding: 6px 0px 6px 0px; - // color: #253555; - // font-weight: bold; - // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - // background-color: #DFE5F1; - // [> opera specific markup <] - // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - // border-top-right-radius: 4px; - // [> firefox specific markup <] - // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - // -moz-border-radius-topright: 4px; - // [> webkit specific markup <] - // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - // -webkit-border-top-right-radius: 4px; +.memproto, +dl.reflist dt { + padding: 0.5em 0; + // background-color: darken(#d19a66, 40%); + // border-top: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // padding: 6px 0px 6px 0px; + // color: #253555; + // font-weight: bold; + // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + // background-color: #DFE5F1; + // [> opera specific markup <] + // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // border-top-right-radius: 4px; + // [> firefox specific markup <] + // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + // -moz-border-radius-topright: 4px; + // [> webkit specific markup <] + // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // -webkit-border-top-right-radius: 4px; } .overload { - font-family: "courier new", courier, monospace; - font-size: 65%; - + font-family: "courier new", courier, monospace; + font-size: 65%; } -.memdoc, dl.reflist dd { - padding: 1em; - // border-bottom: 1px solid #A8B8D9; - // border-left: 1px solid #A8B8D9; - // border-right: 1px solid #A8B8D9; - // padding: 6px 10px 2px 10px; - // background-color: #FBFCFD; - // border-top-width: 0; - // background-image:url('nav_g.png'); - // background-repeat:repeat-x; - // background-color: #FFFFFF; - // [> opera specific markup <] - // border-bottom-left-radius: 4px; - // border-bottom-right-radius: 4px; - // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - // [> firefox specific markup <] - // -moz-border-radius-bottomleft: 4px; - // -moz-border-radius-bottomright: 4px; - // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - // [> webkit specific markup <] - // -webkit-border-bottom-left-radius: 4px; - // -webkit-border-bottom-right-radius: 4px; - // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +.memdoc, +dl.reflist dd { + padding: 1em; + // border-bottom: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // padding: 6px 10px 2px 10px; + // background-color: #FBFCFD; + // border-top-width: 0; + // background-image:url('nav_g.png'); + // background-repeat:repeat-x; + // background-color: #FFFFFF; + // [> opera specific markup <] + // border-bottom-left-radius: 4px; + // border-bottom-right-radius: 4px; + // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // [> firefox specific markup <] + // -moz-border-radius-bottomleft: 4px; + // -moz-border-radius-bottomright: 4px; + // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + // [> webkit specific markup <] + // -webkit-border-bottom-left-radius: 4px; + // -webkit-border-bottom-right-radius: 4px; + // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); } dl.reflist dt { - padding: 5px; - + padding: 5px; } dl.reflist dd { - margin: 0px 0px 10px 0px; - padding: 5px; - + margin: 0px 0px 10px 0px; + padding: 5px; } .paramkey { - text-align: right; - + text-align: right; } .paramtype { - white-space: nowrap; - + white-space: nowrap; } .paramname { - color: $yellow-color; - white-space: nowrap; - + color: $yellow-color; + white-space: nowrap; } .paramname em { - font-style: normal; - font-weight: bold; + font-style: normal; + font-weight: bold; } .paramname code { - line-height: 14px; - + line-height: 14px; } -.params, .retval, .exception, .tparams { - margin-left: 0px; - padding-left: 0px; - +.params, +.retval, +.exception, +.tparams { + margin-left: 0px; + padding-left: 0px; } -.params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { - font-weight: bold; - vertical-align: top; - +.params .paramname, +.retval .paramname, +.tparams .paramname, +.exception .paramname { + font-weight: bold; + vertical-align: top; } -.params .paramtype, .tparams .paramtype { - font-style: italic; - vertical-align: top; - +.params .paramtype, +.tparams .paramtype { + font-style: italic; + vertical-align: top; } -.params .paramdir, .tparams .paramdir { - font-family: "courier new", courier, monospace; - vertical-align: top; - +.params .paramdir, +.tparams .paramdir { + font-family: "courier new", courier, monospace; + vertical-align: top; } table.mlabels { - border-spacing: 0px; - + border-spacing: 0px; } td.mlabels-left { - width: 100%; - padding: 0px; - + width: 100%; + padding: 0px; } td.mlabels-right { - vertical-align: bottom; - padding: 0px; - white-space: nowrap; - + vertical-align: bottom; + padding: 0px; + white-space: nowrap; } span.mlabels { - margin-left: 8px; - + margin-left: 8px; } span.mlabel { - background-color: #728DC1; - - border-top: 1px solid #5373B4; - - border-left: 1px solid #5373B4; - - border-right: 1px solid #C4CFE5; - - border-bottom: 1px solid #C4CFE5; - text-shadow: none; - color: white; - margin-right: 4px; - padding: 2px 3px; - border-radius: 3px; - font-size: 7pt; - white-space: nowrap; - vertical-align: middle; - + background-color: #728DC1; + border-top: 1px solid #5373B4; + border-left: 1px solid #5373B4; + border-right: 1px solid #C4CFE5; + border-bottom: 1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; } /* @end */ + /* these are for tree view inside a (index) page */ div.directory { - //margin: 10px 0px; - // border-top: 1px solid #9CAFD4; - // border-bottom: 1px solid #9CAFD4; - width: 100%; + //margin: 10px 0px; + // border-top: 1px solid #9CAFD4; + // border-bottom: 1px solid #9CAFD4; + width: 100%; } .directory table { - - border-collapse: collapse; - + border-collapse: collapse; } .directory td { - margin: 0px; - padding: 0px; - vertical-align: top; - + margin: 0px; + padding: 0px; + vertical-align: top; } .directory td.entry { - white-space: nowrap; - padding-right: 6px; - padding-top: 3px; - padding-bottom: 3px; - + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 3px; } .directory td.entry a { - - outline: none; - + outline: none; } .directory td.entry a img { - border: none; - + border: none; } .directory td.desc { - width: 100%; - padding-left: 6px; - padding-right: 6px; - // border-left: 1px solid rgba(0,0,0,0.05); + width: 100%; + padding-left: 6px; + padding-right: 6px; + // border-left: 1px solid rgba(0,0,0,0.05); } .directory img { - vertical-align: -30%; - + vertical-align: -30%; } .directory .levels { - white-space: nowrap; - width: 100%; - text-align: right; - font-size: 9pt; - background-color: $table-bg-color; - + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; + background-color: $table-bg-color; } .directory .levels span { - cursor: pointer; - padding-left: 2px; - padding-right: 2px; - color: $link-color; - + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: $link-color; } .directory .levels span:hover { - color: $link-hover-color; - + color: $link-hover-color; } .arrow { - color: #9CAFD4; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - cursor: pointer; - font-size: 80%; - display: inline-block; - //width: 16px; - //height: 22px; - vertical-align: top; - + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + //width: 16px; + //height: 22px; + vertical-align: top; } .icon { - font-family: Arial, Helvetica; - font-weight: bold; - font-size: 12px; - height: 14px; - width: 16px; - display: inline-block; - background-color: #728DC1; - color: white; - text-align: center; - border-radius: 4px; - margin-left: 2px; - margin-right: 2px; - + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; } .icona { - width: 24px; - height: 22px; - display: inline-block; - + width: 24px; + height: 22px; + display: inline-block; } .iconfopen { - width: 24px; - height: 18px; - margin-bottom: 4px; - - background-image: url('folderopen.png'); - background-position: 0px -4px; - background-repeat: repeat-y; - - vertical-align: top; - display: inline-block; - + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url('folderopen.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } .iconfclosed { - width: 24px; - height: 18px; - margin-bottom: 4px; - - background-image: url('folderclosed.png'); - background-position: 0px -4px; - background-repeat: repeat-y; - - vertical-align: top; - display: inline-block; - + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url('folderclosed.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } .icondoc { - width: 24px; - height: 18px; - margin-bottom: 4px; - - background-image: url('doc.png'); - background-position: 0px -4px; - background-repeat: repeat-y; - - vertical-align: top; - display: inline-block; - + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url('doc.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } table.directory { - font: 400 14px Roboto, sans-serif; - + font: 400 14px Roboto, sans-serif; } + /* @end */ div.dynheader { - margin-top: 8px; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } - table.doxtable caption { - caption-side: top; - + caption-side: top; } table.doxtable { - - border-collapse: collapse; - margin-top: 4px; - margin-bottom: 4px; - + border-collapse: collapse; + margin-top: 4px; + margin-bottom: 4px; } -table.doxtable td, table.doxtable th { - border: 1px solid #2D4068; - padding: 3px 7px 2px; - +table.doxtable td, +table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; } table.doxtable th { - background-color: #374F7F; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; - + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; } table.fieldtable { - /*width: 100%;*/ - margin-bottom: 10px; - border: 1px solid #A8B8D9; - border-spacing: 0px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); - box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); - + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } -.fieldtable td, .fieldtable th { - padding: 3px 7px 2px; - +.fieldtable td, +.fieldtable th { + padding: 3px 7px 2px; } -.fieldtable td.fieldtype, .fieldtable td.fieldname { - white-space: nowrap; - border-right: 1px solid #A8B8D9; - border-bottom: 1px solid #A8B8D9; - vertical-align: top; - +.fieldtable td.fieldtype, +.fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; } .fieldtable td.fieldname { - padding-top: 3px; - + padding-top: 3px; } .fieldtable td.fielddoc { - border-bottom: 1px solid #A8B8D9; - /*width: 100%;*/ + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ } .fieldtable td.fielddoc p:first-child { - margin-top: 0px; - + margin-top: 0px; } .fieldtable td.fielddoc p:last-child { - margin-bottom: 2px; - + margin-bottom: 2px; } .fieldtable tr:last-child td { - border-bottom: none; - + border-bottom: none; } .fieldtable th { - - background-image: url('nav_f.png'); - - background-repeat: repeat-x; - background-color: #E2E8F2; - font-size: 90%; - color: #253555; - padding-bottom: 4px; - padding-top: 5px; - - text-align: left; - font-weight: 400; - -moz-border-radius-topleft: 4px; - -moz-border-radius-topright: 4px; - -webkit-border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom: 1px solid #A8B8D9; - + background-image: url('nav_f.png'); + background-repeat: repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align: left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; } - .tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; - + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; } .navpath ul { - font-size: 11px; - background-color: $breadcrumb-bg-color; - color: #8AA0CC; - // border:solid 1px #C2CDE4; - overflow: hidden; - margin: 0px; - padding: 0px; - display: flex; - align-items: center; + font-size: 11px; + background-color: $breadcrumb-bg-color; + color: #8AA0CC; + // border:solid 1px #C2CDE4; + overflow: hidden; + margin: 0px; + padding: 0px; + display: flex; + align-items: center; } .navpath li { - list-style-type: none; - // float:left; - // padding-left:10px; - padding-top: 0.25em; - padding-bottom: 0.25em; - padding-right: 0.25em; - display: flex; - padding: 0.25em; - color: $breadcrumb-fg-color; + list-style-type: none; + // float:left; + // padding-left:10px; + padding-top: 0.25em; + padding-bottom: 0.25em; + padding-right: 0.25em; + display: flex; + padding: 0.25em; + color: $breadcrumb-fg-color; } .navpath li:not(:first-child)::before { - content: "〉"; - display: block; - font-size: 1em; - font-weight: bold; - width: 1em; + content: "〉"; + display: block; + font-size: 1em; + font-weight: bold; + width: 1em; } .navpath li.navelem a { - display: block; - text-decoration: none; - outline: none; - color: $breadcrumb-fg-color; - font-family: 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; - // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - text-decoration: none; + display: block; + text-decoration: none; + outline: none; + color: $breadcrumb-fg-color; + font-family: 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; + // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; } .navpath li.navelem a:hover { - color: lighten($breadcrumb-fg-color, 15%); + color: lighten($breadcrumb-fg-color, 15%); } .navpath li.footer { - list-style-type: none; - - float: right; - - padding-left: 10px; - - padding-right: 15px; - - background-image: none; - - background-repeat: no-repeat; - - background-position: right; - - color: #364D7C; - font-size: 8pt; + list-style-type: none; + float: right; + padding-left: 10px; + padding-right: 15px; + background-image: none; + background-repeat: no-repeat; + background-position: right; + color: #364D7C; + font-size: 8pt; } - div.summary { - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; } div.summary a { - white-space: nowrap; + white-space: nowrap; } table.classindex { - margin: 10px; - white-space: nowrap; - margin-left: 3%; - margin-right: 3%; - width: 94%; - border: 0; - border-spacing: 0; - padding: 0; + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; } div.ingroups { - font-size: 8pt; - width: 50%; - text-align: left; + font-size: 8pt; + width: 50%; + text-align: left; } div.ingroups a { - white-space: nowrap; + white-space: nowrap; } div.header { - background-color: $content-bg-color; - margin: 0px; - // border-bottom: 1px solid #C4CFE5; - color: $content-fg-color; + background-color: $content-bg-color; + margin: 0px; + // border-bottom: 1px solid #C4CFE5; + color: $content-fg-color; } div.headertitle { - padding: 5px 5px 5px 10px; - border-bottom: 1px solid #C4CFE5; + padding: 5px 5px 5px 10px; + border-bottom: 1px solid #C4CFE5; } .PageDocRTL-title div.headertitle { - text-align: right; - direction: rtl; - + text-align: right; + direction: rtl; } dl { - padding: 0 0 0 0; - + padding: 0 0 0 0; } -/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ -dl.section { - margin-left: 0px; - padding-left: 0px; +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ + +dl.section { + margin-left: 0px; + padding-left: 0px; } dl.section.DocNodeRTL { - margin-right: 0px; - padding-right: 0px; - + margin-right: 0px; + padding-right: 0px; } dl.note { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #D0C000; - + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; } dl.note.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #D0C000; - + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; } -dl.warning, dl.attention { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #FF0000; - +dl.warning, +dl.attention { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; } -dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #FF0000; - +dl.warning.DocNodeRTL, +dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; } -dl.pre, dl.post, dl.invariant { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #00D000; - +dl.pre, +dl.post, +dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; } -dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #00D000; - +dl.pre.DocNodeRTL, +dl.post.DocNodeRTL, +dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; } dl.deprecated { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #505050; - + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; } dl.deprecated.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #505050; - + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; } dl.todo { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #00C0E0; - + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; } dl.todo.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #00C0E0; - + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; } dl.test { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #3030E0; - + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; } dl.test.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #3030E0; - + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; } dl.bug { - margin-left: -7px; - padding-left: 3px; - border-left: 4px solid; - border-color: #C08050; - + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; } dl.bug.DocNodeRTL { - margin-left: 0; - padding-left: 0; - border-left: 0; - margin-right: -7px; - padding-right: 3px; - border-right: 4px solid; - border-color: #C08050; - + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; } dl.section dd { - margin-bottom: 6px; - + margin-bottom: 6px; } - #projectlogo { - text-align: center; - vertical-align: bottom; - border-collapse: separate; + text-align: center; + vertical-align: bottom; + border-collapse: separate; } #projectlogo img { - border: 0px none; - height: 8em; - padding: 1em; + border: 0px none; + height: 8em; + padding: 1em; } #projectalign { - vertical-align: middle; + vertical-align: middle; } #projectname { - font: 300% Tahoma, Arial, sans-serif; - margin: 0px; - padding: 2px 0px; + font: 300% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 2px 0px; } #projectbrief { - font: 120% Tahoma, Arial, sans-serif; - margin: 0px; - padding: 0px; + font: 120% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; } #projectnumber { - font: 50% Tahoma, Arial, sans-serif; - margin: 0px; - padding: 0px; + font: 50% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; } #titlearea { - padding: 0px; - margin: 0px; - width: 100%; - color: $title-fg-color; - background-color: $title-bg-color; + padding: 0px; + margin: 0px; + width: 100%; + color: $title-fg-color; + background-color: $title-bg-color; + border-bottom: 3px solid $white-color; } .image { - text-align: center; - background-color: $image-bg-color; - margin-left: 5em; - margin-right: 5em; - padding: 0.5em; - border-radius: 4px; + text-align: center; + background-color: $image-bg-color; + margin-left: 5em; + margin-right: 5em; + padding: 0.5em; + border-radius: 4px; } .dotgraph { - text-align: center; + text-align: center; } .mscgraph { - text-align: center; + text-align: center; } .plantumlgraph { - text-align: center; + text-align: center; } .diagraph { - text-align: center; + text-align: center; } .caption { - font-weight: bold; + font-weight: bold; } div.zoom { - border: 1px solid #90A5CE; + border: 1px solid #90A5CE; } dl.citelist { - - margin-bottom: 50px; - + margin-bottom: 50px; } dl.citelist dt { - - color: #334975; - - float: left; - - font-weight: bold; - - margin-right: 10px; - - padding: 5px; - + color: #334975; + float: left; + font-weight: bold; + margin-right: 10px; + padding: 5px; } dl.citelist dd { - - margin: 2px 0; - - padding: 5px 0; - + margin: 2px 0; + padding: 5px 0; } div.toc { - padding: 14px 25px; - background-color: #F4F6FA; - border: 1px solid #D8DFEE; - border-radius: 7px 7px 7px 7px; - float: right; - height: auto; - margin: 0 8px 10px 10px; - width: 200px; - + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; } .PageDocRTL-title div.toc { - float: left !important; - text-align: right; - + float: left !important; + text-align: right; } div.toc li { - background: url("bdwn.png") no-repeat scroll 0 5px transparent; - font: 10px/1.2 Verdana, DejaVu Sans, Geneva, sans-serif; - margin-top: 5px; - padding-left: 10px; - padding-top: 2px; - + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana, DejaVu Sans, Geneva, sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; } .PageDocRTL-title div.toc li { - background-position-x: right !important; - padding-left: 0 !important; - padding-right: 10px; - + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; } div.toc h3 { - font: bold 12px/1.2 Arial, FreeSans, sans-serif; - color: #4665A2; - border-bottom: 0 none; - margin: 0; - + font: bold 12px/1.2 Arial, FreeSans, sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; } div.toc ul { - list-style: none outside none; - border: medium none; - padding: 0px; - + list-style: none outside none; + border: medium none; + padding: 0px; } div.toc li.level1 { - margin-left: 0px; - + margin-left: 0px; } div.toc li.level2 { - margin-left: 15px; - + margin-left: 15px; } div.toc li.level3 { - margin-left: 30px; - + margin-left: 30px; } div.toc li.level4 { - margin-left: 45px; - + margin-left: 45px; } .PageDocRTL-title div.toc li.level1 { - margin-left: 0 !important; - margin-right: 0; - + margin-left: 0 !important; + margin-right: 0; } .PageDocRTL-title div.toc li.level2 { - margin-left: 0 !important; - margin-right: 15px; - + margin-left: 0 !important; + margin-right: 15px; } .PageDocRTL-title div.toc li.level3 { - margin-left: 0 !important; - margin-right: 30px; - + margin-left: 0 !important; + margin-right: 30px; } .PageDocRTL-title div.toc li.level4 { - margin-left: 0 !important; - margin-right: 45px; - + margin-left: 0 !important; + margin-right: 45px; } .inherit_header { - font-weight: bold; - color: gray; - cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .inherit_header td { - padding: 6px 0px 2px 5px; - + padding: 6px 0px 2px 5px; } .inherit { - display: none; - + display: none; } tr.heading h2 { - //margin: 0.5em; - //margin: 0; - //margin-bottom: 0.5em; - + //margin: 0.5em; + //margin: 0; + //margin-bottom: 0.5em; } + /* tooltip related style info */ .ttc { - position: absolute; - display: none; - + position: absolute; + display: none; } #powerTip { - cursor: default; - white-space: nowrap; - background-color: white; - border: 1px solid gray; - border-radius: 4px 4px 4px 4px; - box-shadow: 1px 1px 7px gray; - display: none; - font-size: smaller; - max-width: 80%; - opacity: 0.9; - padding: 1ex 1em 1em; - position: absolute; - z-index: 2147483647; - + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; } #powerTip div.ttdoc { - color: grey; - font-style: italic; - + color: grey; + font-style: italic; } #powerTip div.ttname a { - font-weight: bold; - + font-weight: bold; } #powerTip div.ttname { - font-weight: bold; - + font-weight: bold; } #powerTip div.ttdeci { - color: #006318; - + color: #006318; } #powerTip div { - margin: 0px; - padding: 0px; - font: 12px/16px Roboto, sans-serif; - + margin: 0px; + padding: 0px; + font: 12px/16px Roboto, sans-serif; } -#powerTip:before, #powerTip:after { - content: ""; - position: absolute; - margin: 0px; - +#powerTip:before, +#powerTip:after { + content: ""; + position: absolute; + margin: 0px; } -#powerTip.n:after, #powerTip.n:before, -#powerTip.s:after, #powerTip.s:before, -#powerTip.w:after, #powerTip.w:before, -#powerTip.e:after, #powerTip.e:before, -#powerTip.ne:after, #powerTip.ne:before, -#powerTip.se:after, #powerTip.se:before, -#powerTip.nw:after, #powerTip.nw:before, -#powerTip.sw:after, #powerTip.sw:before { - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - +#powerTip.n:after, +#powerTip.n:before, +#powerTip.s:after, +#powerTip.s:before, +#powerTip.w:after, +#powerTip.w:before, +#powerTip.e:after, +#powerTip.e:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.nw:after, +#powerTip.nw:before, +#powerTip.sw:after, +#powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; } -#powerTip.n:after, #powerTip.s:after, -#powerTip.w:after, #powerTip.e:after, -#powerTip.nw:after, #powerTip.ne:after, -#powerTip.sw:after, #powerTip.se:after { - border-color: rgba(255, 255, 255, 0); - +#powerTip.n:after, +#powerTip.s:after, +#powerTip.w:after, +#powerTip.e:after, +#powerTip.nw:after, +#powerTip.ne:after, +#powerTip.sw:after, +#powerTip.se:after { + border-color: rgba(255, 255, 255, 0); } -#powerTip.n:before, #powerTip.s:before, -#powerTip.w:before, #powerTip.e:before, -#powerTip.nw:before, #powerTip.ne:before, -#powerTip.sw:before, #powerTip.se:before { - border-color: rgba(128, 128, 128, 0); - +#powerTip.n:before, +#powerTip.s:before, +#powerTip.w:before, +#powerTip.e:before, +#powerTip.nw:before, +#powerTip.ne:before, +#powerTip.sw:before, +#powerTip.se:before { + border-color: rgba(128, 128, 128, 0); } -#powerTip.n:after, #powerTip.n:before, -#powerTip.ne:after, #powerTip.ne:before, -#powerTip.nw:after, #powerTip.nw:before { - top: 100%; - +#powerTip.n:after, +#powerTip.n:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.nw:after, +#powerTip.nw:before { + top: 100%; } -#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { - border-top-color: #FFFFFF; - border-width: 10px; - margin: 0px -10px; - +#powerTip.n:after, +#powerTip.ne:after, +#powerTip.nw:after { + border-top-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; } #powerTip.n:before { - border-top-color: #808080; - border-width: 11px; - margin: 0px -11px; - + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; } -#powerTip.n:after, #powerTip.n:before { - left: 50%; - +#powerTip.n:after, +#powerTip.n:before { + left: 50%; } -#powerTip.nw:after, #powerTip.nw:before { - right: 14px; - +#powerTip.nw:after, +#powerTip.nw:before { + right: 14px; } -#powerTip.ne:after, #powerTip.ne:before { - left: 14px; - +#powerTip.ne:after, +#powerTip.ne:before { + left: 14px; } -#powerTip.s:after, #powerTip.s:before, -#powerTip.se:after, #powerTip.se:before, -#powerTip.sw:after, #powerTip.sw:before { - bottom: 100%; - +#powerTip.s:after, +#powerTip.s:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.sw:after, +#powerTip.sw:before { + bottom: 100%; } -#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { - border-bottom-color: #FFFFFF; - border-width: 10px; - margin: 0px -10px; - +#powerTip.s:after, +#powerTip.se:after, +#powerTip.sw:after { + border-bottom-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; } -#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { - border-bottom-color: #808080; - border-width: 11px; - margin: 0px -11px; - +#powerTip.s:before, +#powerTip.se:before, +#powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; } -#powerTip.s:after, #powerTip.s:before { - left: 50%; - +#powerTip.s:after, +#powerTip.s:before { + left: 50%; } -#powerTip.sw:after, #powerTip.sw:before { - right: 14px; - +#powerTip.sw:after, +#powerTip.sw:before { + right: 14px; } -#powerTip.se:after, #powerTip.se:before { - left: 14px; - +#powerTip.se:after, +#powerTip.se:before { + left: 14px; } -#powerTip.e:after, #powerTip.e:before { - left: 100%; - +#powerTip.e:after, +#powerTip.e:before { + left: 100%; } #powerTip.e:after { - border-left-color: #FFFFFF; - border-width: 10px; - top: 50%; - margin-top: -10px; - + border-left-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; } #powerTip.e:before { - border-left-color: #808080; - border-width: 11px; - top: 50%; - margin-top: -11px; - + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; } -#powerTip.w:after, #powerTip.w:before { - right: 100%; - +#powerTip.w:after, +#powerTip.w:before { + right: 100%; } #powerTip.w:after { - border-right-color: #FFFFFF; - border-width: 10px; - top: 50%; - margin-top: -10px; - + border-right-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; } #powerTip.w:before { - border-right-color: #808080; - border-width: 11px; - top: 50%; - margin-top: -11px; - + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; } @media print { - #top { - display: none; - } - - #side-nav { - display: none; - } - - #nav-path { - display: none; - } - - body { - overflow: visible; - } - - h1, h2, h3, h4, h5, h6 { - page-break-after: avoid; - } - - .summary { - display: none; - } - - .memitem { - page-break-inside: avoid; - } - - #doc-content { - margin-left: 0 !important; - - height: auto !important; - - width: auto !important; - - overflow: inherit; - - display: inline; - - } + #top { + display: none; + } + #side-nav { + display: none; + } + #nav-path { + display: none; + } + body { + overflow: visible; + } + h1, + h2, + h3, + h4, + h5, + h6 { + page-break-after: avoid; + } + .summary { + display: none; + } + .memitem { + page-break-inside: avoid; + } + #doc-content { + margin-left: 0 !important; + height: auto !important; + width: auto !important; + overflow: inherit; + display: inline; + } } + /* @group Markdown */ + /* table.markdownTable { @@ -2164,6 +2026,36 @@ direction:ltr; /* @end */ u { - text-decoration: underline; - + text-decoration: underline; } + + +/* +#nav-tree { + padding: 0px 0px; + font-size: 14px; + overflow: auto; +} +*/ + + +/* +.ui-resizable-e { + background: $white-color; + width: 3px; +} + +#nav-tree { + padding: 0px 0px; + background-color: $black-color; + background-image: none; + font-size: 14px; + overflow: auto; +} + +#nav-tree .selected { + background-color: $code-bg-color; + background-image: none; + color: $white-color; +} +*/ \ No newline at end of file diff --git a/mentos/inc/io/vga/vga_font.h b/mentos/inc/io/vga/vga_font.h index 3108658..33bbf68 100644 --- a/mentos/inc/io/vga/vga_font.h +++ b/mentos/inc/io/vga/vga_font.h @@ -6,6 +6,7 @@ #pragma once +/// Font 8x16. unsigned char arr_8x16_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, 0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00, @@ -265,6 +266,7 @@ unsigned char arr_8x16_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +/// Font 8x8. unsigned char arr_8x8_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, @@ -524,8 +526,7 @@ unsigned char arr_8x8_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -// Constant: font8x8_basic -// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) +/// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) unsigned char arr_8x8_basic_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0000 (nul) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0001 @@ -657,7 +658,8 @@ unsigned char arr_8x8_basic_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // U+007F }; -unsigned char vga_font[256 * 16] = { +/// Font 16x16. +unsigned char vga_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, diff --git a/mentos/inc/io/vga/vga_mode.h b/mentos/inc/io/vga/vga_mode.h index 8f7b54e..9213789 100644 --- a/mentos/inc/io/vga/vga_mode.h +++ b/mentos/inc/io/vga/vga_mode.h @@ -1,4 +1,4 @@ -/// @brief +/// Structure that holds the information about a VGA mode. typedef struct { unsigned char misc; ///< 00h -- /// @brief The Sequencer Registers. @@ -9,6 +9,7 @@ typedef struct { unsigned char character_map_select; ///< 03h -- unsigned char sequencer_memory_mode; ///< 04h -- } sc; + /// @brief CRT Controller (CRTC) Registers struct { unsigned char horizontal_total; ///< 00h -- unsigned char end_horizontal_display; ///< 01h -- diff --git a/mentos/inc/io/vga/vga_palette.h b/mentos/inc/io/vga/vga_palette.h index 79f804f..010c052 100644 --- a/mentos/inc/io/vga/vga_palette.h +++ b/mentos/inc/io/vga/vga_palette.h @@ -6,12 +6,14 @@ #pragma once +/// Structure that simplifies defining a palette. typedef struct { - unsigned char red; - unsigned char green; - unsigned char blue; + unsigned char red; ///< Red value. + unsigned char green; ///< Green value. + unsigned char blue; ///< Blue value. } palette_entry_t; +/// 16 color palette. palette_entry_t ansi_16_palette[17] = { { 0x00, 0x00, 0x00 }, // Black { 0xAA, 0x00, 0x00 }, // Red @@ -31,6 +33,7 @@ palette_entry_t ansi_16_palette[17] = { { 0xFF, 0xFF, 0xFF }, // White }; +/// 256 color palette. palette_entry_t ansi_256_palette[256] = { { 0, 0, 0 }, // Black { 128, 0, 0 }, // Maroon diff --git a/mentos/inc/misc/debug.h b/mentos/inc/misc/debug.h index 60ca043..6383626 100644 --- a/mentos/inc/misc/debug.h +++ b/mentos/inc/misc/debug.h @@ -11,6 +11,7 @@ struct pt_regs; #ifndef __DEBUG_LEVEL__ +/// Defines the debug level. #define __DEBUG_LEVEL__ LOGLEVEL_NOTICE #endif diff --git a/mentos/src/io/vga/vga.c b/mentos/src/io/vga/vga.c index 3d1ea71..0503d10 100644 --- a/mentos/src/io/vga/vga.c +++ b/mentos/src/io/vga/vga.c @@ -29,6 +29,7 @@ #define CRTC_DATA 0x03D5 #define INPUT_STATUS_READ 0x03DA +/// VGA pointers for drawing operations. typedef struct { /// Writes a pixel. void (*write_pixel)(unsigned int x, unsigned int y, unsigned char c); @@ -40,18 +41,20 @@ typedef struct { void (*fill_rect)(unsigned int x, unsigned int y, unsigned int wd, unsigned int ht, unsigned char c); } vga_ops_t; +/// VGA font details. typedef struct { - unsigned char *font; - unsigned width; - unsigned height; + unsigned char *font; ///< Pointer to the array holding the shape of each character. + unsigned width; ///< Width of the font. + unsigned height; ///< Height of the font. } vga_font_t; +/// VGA driver details. typedef struct { - int width; - int height; - int bpp; - char *address; - vga_ops_t *ops; + int width; ///< Screen's width. + int height; ///< Screen's height. + int bpp; ///< Bits per pixel (bpp). + char *address; ///< Starting address of the screen. + vga_ops_t *ops; ///< Writing operations. } vga_driver_t; static bool_t vga_enable = false; @@ -177,7 +180,7 @@ static void __set_mode(vga_mode_t *vga_mode) outportb(CRTC_DATA, *ptr); ++ptr; } - + // write GRAPHICS CONTROLLER regs. for (unsigned i = 0; i < MODE_NUM_GC_REGS; i++) { outportb(GC_INDEX, i); diff --git a/mentos/src/multiboot.c b/mentos/src/multiboot.c index e9801c5..59f18bc 100644 --- a/mentos/src/multiboot.c +++ b/mentos/src/multiboot.c @@ -4,6 +4,7 @@ /// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Set the debug level to 0. #define __DEBUG_LEVEL__ 0 #include "multiboot.h" From a968c1bd6d41033bc5d32ebfd6ec0491d6ea51b9 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 14 Oct 2021 17:41:26 +0200 Subject: [PATCH 20/21] Remove useless file. --- files/prog/more.c | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 files/prog/more.c diff --git a/files/prog/more.c b/files/prog/more.c deleted file mode 100644 index fa67ac4..0000000 --- a/files/prog/more.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -void main(int argc, char ** argv) -{ - if (argc == 2) - { - // Try to open the file. - int fd = open(argv[1], O_RDONLY, 42); - if (fd > -1) - { - char c; - // Put on the standard output the characters. - while (read(fd, &c, 1)) putchar(c); - putchar('\n'); - putchar('\n'); - close(fd); - } - else - { - printf("%s: cannot find the file.\n\n", argv[1]); - } - } - else - { - printf("Usage: more file\n\n"); - } -} \ No newline at end of file From f43bce08d6a848671bb2cf470c010d18c89f424f Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 14 Oct 2021 17:48:18 +0200 Subject: [PATCH 21/21] Update README. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3487958..1b9b584 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ seconds! If you are a beginner in Operating-System developing, perhaps MentOS is the right operating system to start with. +Parts of MentOS are inherited or inspired by a similar educational operating +system called [DreamOs](https://github.com/dreamos82/DreamOs) written by Ivan +Gualandri. + ## 2. Prerequisites MentOS is compatible with the main **unix-based** operating systems. It has been