From b7fe1385495ebfd5551d1a13c5606cb3dba486fb Mon Sep 17 00:00:00 2001 From: Jenny Curle Date: Mon, 1 Apr 2019 02:18:48 +0100 Subject: [PATCH] First upload. Libc incomplete. --- arch/i386/boot.s | 33 ++ arch/i386/crti.s | 16 + arch/i386/crtn.s | 7 + arch/i386/linker.ld | 30 ++ arch/i386/make.config | 8 + arch/i386/tty.c | 62 ++++ include/kernel/tty.h | 16 + include/kernel/vga.h | 34 ++ iso/boot/grub/menu.lst | 5 + iso/boot/grub/stage2_eltorito | Bin 0 -> 105522 bytes iso/boot/kernel.elf | Bin 0 -> 20844 bytes kernel/kernel.c | 19 ++ kernel/syscalls.c | 47 +++ libc/.gitignore | 3 + libc/Makefile | 91 +++++ libc/arch/i386/make.config | 8 + libc/include/errno.h | 0 libc/include/stdio.h | 19 ++ libc/include/stdlib.h | 601 ++++++++++++++++++++++++++++++++++ libc/include/string.h | 13 + libc/include/sys/cdefs.h | 5 + libc/include/sys/types.h | 0 libc/stdio/printf.c | 94 ++++++ libc/stdio/putchar.c | 15 + libc/stdio/puts.c | 5 + libc/stdlib/abort.c | 15 + libc/string/memcmp.c | 13 + libc/string/memmove.c | 14 + libc/string/memset.c | 8 + libc/string/strlen.c | 8 + makefile | 94 ++++++ 31 files changed, 1283 insertions(+) create mode 100755 arch/i386/boot.s create mode 100755 arch/i386/crti.s create mode 100755 arch/i386/crtn.s create mode 100755 arch/i386/linker.ld create mode 100755 arch/i386/make.config create mode 100755 arch/i386/tty.c create mode 100755 include/kernel/tty.h create mode 100755 include/kernel/vga.h create mode 100755 iso/boot/grub/menu.lst create mode 100755 iso/boot/grub/stage2_eltorito create mode 100755 iso/boot/kernel.elf create mode 100755 kernel/kernel.c create mode 100755 kernel/syscalls.c create mode 100755 libc/.gitignore create mode 100755 libc/Makefile create mode 100755 libc/arch/i386/make.config create mode 100755 libc/include/errno.h create mode 100755 libc/include/stdio.h create mode 100755 libc/include/stdlib.h create mode 100755 libc/include/string.h create mode 100755 libc/include/sys/cdefs.h create mode 100755 libc/include/sys/types.h create mode 100755 libc/stdio/printf.c create mode 100755 libc/stdio/putchar.c create mode 100755 libc/stdio/puts.c create mode 100755 libc/stdlib/abort.c create mode 100755 libc/string/memcmp.c create mode 100755 libc/string/memmove.c create mode 100755 libc/string/memset.c create mode 100755 libc/string/strlen.c create mode 100755 makefile diff --git a/arch/i386/boot.s b/arch/i386/boot.s new file mode 100755 index 0000000..7220deb --- /dev/null +++ b/arch/i386/boot.s @@ -0,0 +1,33 @@ +.set ALIGN, 1<<0 +.set MEMINFO, 1<<1 +.set FLAGS, ALIGN | MEMINFO +.set MAGIC, 0x1BADB002 +.set CHECKSUM, -(MAGIC + FLAGS) + +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +.section .bss +.align 16 +stack_bottom: + .skip 16384 +stack_top: + +.section .text +.global _start +.type _start, @function +_start: + movl $stack_top, %esp + + call _init + + call kernel_main + + cli +1: hlt + jmp 1b + +.size _start, . - _start \ No newline at end of file diff --git a/arch/i386/crti.s b/arch/i386/crti.s new file mode 100755 index 0000000..25c5732 --- /dev/null +++ b/arch/i386/crti.s @@ -0,0 +1,16 @@ +.section .init +.global _init + +.type _init, @function + +_init: + push %ebp + movl %esp, %ebp + +.section .fini +.global _fini +.type _fini, @function + +_fini: + push %ebp + movl %esp, %ebp \ No newline at end of file diff --git a/arch/i386/crtn.s b/arch/i386/crtn.s new file mode 100755 index 0000000..38dd18e --- /dev/null +++ b/arch/i386/crtn.s @@ -0,0 +1,7 @@ +.section .init + popl %ebp + ret + +.section .fini + popl %ebp + ret \ No newline at end of file diff --git a/arch/i386/linker.ld b/arch/i386/linker.ld new file mode 100755 index 0000000..1def46f --- /dev/null +++ b/arch/i386/linker.ld @@ -0,0 +1,30 @@ +ENTRY(_start) + +SECTIONS +{ + + . = 1M; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + .data BLOCK(4K : ALIGN(4K) + { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + +} \ No newline at end of file diff --git a/arch/i386/make.config b/arch/i386/make.config new file mode 100755 index 0000000..96463ac --- /dev/null +++ b/arch/i386/make.config @@ -0,0 +1,8 @@ +KERNEL_ARCH_CFLAGS= +KERNEL_ARCH_CPPFLAGS= +KERNEL_ARCH_LDFLAGS= +KERNEL_ARCH_LIBS= + +KERNEL_ARCH_OBKS=\ +$(ARCHDIR)/boot.o\ +$(ARCHDIR)/tty.o\ \ No newline at end of file diff --git a/arch/i386/tty.c b/arch/i386/tty.c new file mode 100755 index 0000000..df9989b --- /dev/null +++ b/arch/i386/tty.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +#include +#include "vga.h" + +static const size_t TERM_WIDTH=80; +static const size_t TERM_HEIGHT=25; + +static size_t terminal_row; +static size_t terminal_column; + +static uint8_t current_color; +static uint16_t* term_buffer; + +void screen_initialize(void) { + + static uint16_t* const vga_buffer = (uint16_t*) 0xB8000; + terminal_row = 0; + terminal_column = 0; + current_color = vga_color_set(LIGHT_GREY, BLACK); + term_buffer = vga_buffer; + + for (size_t y = 0; y < TERM_HEIGHT; y++) { + for(size_t x = 0; x < TERM_WIDTH; x++) { + const size_t offset = y * TERM_WIDTH + x; + term_buffer[offset] = vga_entry(' ', current_color); + } + } +} + +void term_setcolor(enum vga_colors color) { + current_color = color; +} + +void term_putentryat (unsigned char c, uint8_t color, size_t x, size_t y) { + const size_t offset = y * TERM_WIDTH + x; + term_buffer[offset] = vga_entry(c, color); +} + +void term_putchar(char c) { + unsigned char uc = c; + term_putentryat(uc, current_color, terminal_column, terminal_row); + + if(++terminal_column == TERM_WIDTH) + terminal_column = 0; + if(++terminal_row == TERM_HEIGHT){ + term_scroll(); + terminal_row = 0; + } +} + +void term_write(const char* data, size_t size) { + for(size_t i = 0; i < size; i++) + term_putchar(data[i]); +} + +void puts(const char* string) { + term_write(string + "\n", strlen(string)); +} \ No newline at end of file diff --git a/include/kernel/tty.h b/include/kernel/tty.h new file mode 100755 index 0000000..121c20b --- /dev/null +++ b/include/kernel/tty.h @@ -0,0 +1,16 @@ +#ifndef _KERNEL_TTY_H +#define _KERNEL_TTY_H + +#include + +void term_setcolor(enum vga_colors); +void screen_initialize(void); +void term_putentryat(char, uint8_t, size_t, size_t); +void term_putchar(char); +void term_write(char, size_t); +void puts(char); +void set_cursor(int, int); +void term_scroll(void); +void int_to_ascii(int, char*); +void int_to_hex(int, char*); +void empty_string(char*); diff --git a/include/kernel/vga.h b/include/kernel/vga.h new file mode 100755 index 0000000..6f82f38 --- /dev/null +++ b/include/kernel/vga.h @@ -0,0 +1,34 @@ +#ifndef ARCH_I386_VGA_H +#define ARCH_I386_VGA_H + +#include + + +enum vga_colors { + BLACK = 0, + BLUE = 1, + GREEN = 2, + CYAN = 3, + RED = 4, + MAGENTA = 5, + BROWN = 6, + LIGHT_GREY = 7, + DARK_GREY = 8, + LIGHT_BLUE = 9, + LIGHT_GREEN = 10, + LIGHT_CYAN = 11, + LIGHT_RED = 12, + LIGHT_MAGENTA = 13, + LIGHT_BROWN = 14, + WHITE = 15 +}; + +static inline uint8_t vga_color_set(enum vga_color fg, enum vga_color bg) { + return fg | bg << 4; +} + +static inline uint16_t vga_entry(unsigned char uc, uint8_t color) { + return (uint16_t) uc | (uint16_t) color << 8; +} + +#endif \ No newline at end of file diff --git a/iso/boot/grub/menu.lst b/iso/boot/grub/menu.lst new file mode 100755 index 0000000..387d721 --- /dev/null +++ b/iso/boot/grub/menu.lst @@ -0,0 +1,5 @@ +defualt=0 +timeout=0 + +title ProjectRED +kernel /boot/kernel.elf diff --git a/iso/boot/grub/stage2_eltorito b/iso/boot/grub/stage2_eltorito new file mode 100755 index 0000000000000000000000000000000000000000..9e1617cee78bb8f1ab6838837c9a1f36d729c9fd GIT binary patch literal 105522 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 I8%}5UKb>zEqW}N^ literal 0 HcmV?d00001 diff --git a/iso/boot/kernel.elf b/iso/boot/kernel.elf new file mode 100755 index 0000000000000000000000000000000000000000..6e87838f52d2e1f9067959dc1078f4eba0146433 GIT binary patch literal 20844 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 literal 0 HcmV?d00001 diff --git a/kernel/kernel.c b/kernel/kernel.c new file mode 100755 index 0000000..897096e --- /dev/null +++ b/kernel/kernel.c @@ -0,0 +1,19 @@ +#include +#include + +int kernel_main(void) { + //Prepare the screen, and blank it out. + screen_initialize(); + + //Print a copyright message. + puts("(c)"); + set_screen_text_color(GREEN); + puts(" Project"); + set_screen_text_color(RED); + puts("RED"); + set_screen_text_color(WHITE); + puts(", 2019\n"); + + for(;;) {} + return 0; +} \ No newline at end of file diff --git a/kernel/syscalls.c b/kernel/syscalls.c new file mode 100755 index 0000000..5f013a9 --- /dev/null +++ b/kernel/syscalls.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +#include + +//prototypes; these are the bare minimum for newlib to compile. + +void _exit(); + +int close(int file); + +char **environ; + +int execve(char *name, char **argv, char **env); + +int fork(); + +int fstat(int file, struct stat *st); + +int getpid(); + +int isatty(int file); + +int kill(int pid, int sig); + +int link(char *old, char *new); + +int lseek(int file, int ptr, int dir); + +int open(const char* name, int flags, ...); + +int read(int file, char* ptr, int len); + +caddr_t sbrk(int incr); + +int stat(const char* file, struct stat *st); + +clock_t times(struct tms* buf); + +int unlink(char* name); + +int wait(int file, char* ptr, int len); + +int gettimeofday(struct timeval* p, struct timezone* z); \ No newline at end of file diff --git a/libc/.gitignore b/libc/.gitignore new file mode 100755 index 0000000..5e3628b --- /dev/null +++ b/libc/.gitignore @@ -0,0 +1,3 @@ +*.a +*.d +*.o \ No newline at end of file diff --git a/libc/Makefile b/libc/Makefile new file mode 100755 index 0000000..bdabd0e --- /dev/null +++ b/libc/Makefile @@ -0,0 +1,91 @@ +DEFAULT_HOST!=../default-host.sh +HOST?=DEFAULT_HOST +HOSTARCH!==../target-to-arch.sh $(HOST) + +CFLAGS?=-O2 -g +CPPFLAGS?= +LDFLAGS?= +LIBS?= + +DESTDIR?= +PREFIX?=/usr/local +EXEC_PREFIX?=$(PREFIX) +INCLUDEDIR?=$(PREFIX)/include +LIBDIR?=$(EXEC_PREFIX)/lib + +CFLAGS:=$(CFLAGS) -ffreestanding -Wall -Wextra +CPPFLAGS:=$(CPPFLAGS) -D__is_libc -Iinclude +LIBK_CFLAGS:=$(CFLAGS) +LIBK_CPPFLAGS:=$(CPPFLAGS) -D__is_libk + +ARCHDIR=arch/$(HOSTARCH) + +include $(ARCHDIR)/make.config + +CFLAGS:=$(CFLAGS) $(ARCH_CFLAGS) +CPPFLAGS:=$(CPPFLAGS) $(ARCH_CPPFLAGS) +LIBK_CFLAGS:=$(LIBK_CFLAGS) $(KERNEL_ARCH_CFLAGS) +LIBK_CPPFLAGS:=$(LIBK_CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS) + +FREEOBJS=\ +$(ARCH_FREEOBJS)\ +stdio/printf.o\ +stdio/putchar.o\ +stdio/puts.o\ +stdlib/abort.o\ +string/memcmp.o\ +string/memcpy.o\ +string/memmove.o\ +string/memset.o\ +string/strlen.o\ + +HOSTEDOBJS=\ +$(ARCH_HOSTEDOBJS)\ + +OBJS=\ +$(FREEOBJS)\ +$(HOSTEDOBJS)\ + +LIBK_OBJS=$(FREEOBJS:.o=.libk.o) + +BINARIES=libk.a + +.PHONY: all clean install install-headers install-libs +.SUFFIXES: .o .libk.o .c .s + +all: $(BINARIES) + +libc.a: $(OBJS) + $(AR) rcs $@ $(OBJS) +libk.a: $(LIBK_OBJS) + $(AR) rcs $@ $(LIBK_OBJS) + +.c.o: + $(CC) -MD -c $< -o $@ -std=gnull $(CFLAGS) $(CPPFLAGS) + +.c.s: + $(CC) -MD -c $< -o $@ $(CFLAGS) $(CPPFLAGS) +.c.libk.o: + $(CC) -MD -c $< -o $@ -std=gnull $(LIBK_CFLAGS) $(LIBK_CPPFLAGS) +.s.libk.o: + $(CC) -MD -c $< -o $@ $(LIBK_CFLAGS) $(LIBK_CPPFLAGS) + +clean: + rm -f $(BINARIES) *.a + rm -f $(OBJS) $(LIBK_OBJS) *.o */*.o */*/*.o + rm -f $(OBJS:.o=.d) $(LIBK_OBJS:.o=.d) *.d */*.d */*/*.d + +install: install-headers install-libs + +install-headers: + mkdir -p $(DESTDIR)$(INCLUDEDIR) + cp -R --preserve-timestamps include/. $(DESTDIR)$(INCLUDEDIR)/. +install-libs: $(BINARIES) + mkdir -p $(DESTDIR)$(LIBDIR) + cp $(BINARIES) $(DESTDIR) $(LIBDIR) + +-include $(OBJS:.o=.d) +-include $(LIBK_OBJS:.o=.d) + + + diff --git a/libc/arch/i386/make.config b/libc/arch/i386/make.config new file mode 100755 index 0000000..acb23fa --- /dev/null +++ b/libc/arch/i386/make.config @@ -0,0 +1,8 @@ +ARCH_CFLAGS= +ARCH_CPPFLAGS= +KERNEL_ARCH_CFLAGS= +KERNEL_ARCH_CPPFLAGS= + +ARCH_FREEOBJS=\ + +ARCH_HOSTEDOBJS=\ diff --git a/libc/include/errno.h b/libc/include/errno.h new file mode 100755 index 0000000..e69de29 diff --git a/libc/include/stdio.h b/libc/include/stdio.h new file mode 100755 index 0000000..ba4721d --- /dev/null +++ b/libc/include/stdio.h @@ -0,0 +1,19 @@ +#ifndef _STDIO_H +#define _STDIO_H 1 + +#include + +#define EOF (-1) + +struct __sFile { + int unused; +}; + +typedef struct __sFile FILE; + +#define stderr (_impure_ptr->_stderr) + +int printf(const char* __restrict, ...); +int putchar(int); +int puts(const char*); +#endif \ No newline at end of file diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h new file mode 100755 index 0000000..5a0bc70 --- /dev/null +++ b/libc/include/stdlib.h @@ -0,0 +1,601 @@ +#ifndef _STDLIB_H +#define _STDLIB_H 1 + +#include + +__attribute__((__noreturn__)) +void abort(void); + +/* + Default header file for malloc-2.8.x, written by Doug Lea + and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ + + This header is for ANSI C/C++ only. You can set any of + the following #defines before including: + + * If USE_DL_PREFIX is defined, it is assumed that malloc.c + was also compiled with this option, so all routines + have names starting with "dl". + + * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this + file will be #included AFTER . This is needed only if + your system defines a struct mallinfo that is incompatible with the + standard one declared here. Otherwise, you can include this file + INSTEAD of your system system . At least on ANSI, all + declarations should be compatible with system versions + + * If MSPACES is defined, declarations for mspace versions are included. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* for size_t */ + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 /* define to a value */ +#elif ONLY_MSPACES != 0 +#define ONLY_MSPACES 1 +#endif /* ONLY_MSPACES */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ + +#ifndef MSPACES +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ + +#if !ONLY_MSPACES + +#if !NO_MALLINFO +#ifndef HAVE_USR_INCLUDE_MALLOC_H +#ifndef _MALLOC_H +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef STRUCT_MALLINFO_DECLARED +#define STRUCT_MALLINFO_DECLARED 1 +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* _MALLOC_H */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* !NO_MALLINFO */ + +/* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. +*/ +void* malloc(size_t); + +/* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cuase the current program to abort. +*/ +void free(void*); + +/* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. +*/ +void* calloc(size_t, size_t); + +/* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. +*/ +void* realloc(void*, size_t); + +/* + realloc_in_place(void* p, size_t n) + Resizes the space allocated for p to size n, only if this can be + done without moving p (i.e., only if there is adjacent space + available if n is greater than p's current allocated size, or n is + less than or equal to p's size). This may be used instead of plain + realloc if an alternative allocation strategy is needed upon failure + to expand space; for example, reallocation of a buffer that must be + memory-aligned or cleared. You can use realloc_in_place to trigger + these alternatives only when needed. + + Returns p if successful; otherwise null. +*/ +void* realloc_in_place(void*, size_t); + +/* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. +*/ +void* memalign(size_t, size_t); + +/* + int posix_memalign(void** pp, size_t alignment, size_t n); + Allocates a chunk of n bytes, aligned in accord with the alignment + argument. Differs from memalign only in that it (1) assigns the + allocated memory to *pp rather than returning it, (2) fails and + returns EINVAL if the alignment is not a power of two (3) fails and + returns ENOMEM if memory cannot be allocated. +*/ +int posix_memalign(void**, size_t, size_t); + +/* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. +*/ +void* valloc(size_t); + +/* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt: + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) +*/ +int mallopt(int, int); + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + + +/* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. +*/ +size_t malloc_footprint(void); + +/* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. +*/ +size_t malloc_max_footprint(void); + +/* + malloc_footprint_limit(); + Returns the number of bytes that the heap is allowed to obtain from + the system, returning the last value returned by + malloc_set_footprint_limit, or the maximum size_t value if + never set. The returned value reflects a permission. There is no + guarantee that this number of bytes can actually be obtained from + the system. +*/ +size_t malloc_footprint_limit(void); + +/* + malloc_set_footprint_limit(); + Sets the maximum number of bytes to obtain from the system, causing + failure returns from malloc and related functions upon attempts to + exceed this value. The argument value may be subject to page + rounding to an enforceable limit; this actual value is returned. + Using an argument of the maximum possible size_t effectively + disables checks. If the argument is less than or equal to the + current malloc_footprint, then all future allocations that require + additional system memory will fail. However, invocation cannot + retroactively deallocate existing used memory. +*/ +size_t malloc_set_footprint_limit(size_t bytes); + +/* + malloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg); + Traverses the heap and calls the given handler for each managed + region, skipping all bytes that are (or may be) used for bookkeeping + purposes. Traversal does not include include chunks that have been + directly memory mapped. Each reported region begins at the start + address, and continues up to but not including the end address. The + first used_bytes of the region contain allocated data. If + used_bytes is zero, the region is unallocated. The handler is + invoked with the given callback argument. If locks are defined, they + are held during the entire traversal. It is a bad idea to invoke + other malloc functions from within the handler. + + For example, to count the number of in-use chunks with size greater + than 1000, you could write: + static int count = 0; + void count_chunks(void* start, void* end, size_t used, void* arg) { + if (used >= 1000) ++count; + } + then: + malloc_inspect_all(count_chunks, NULL); + + malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. +*/ +void malloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg); + +#if !NO_MALLINFO +/* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. +*/ + +struct mallinfo mallinfo(void); +#endif /* NO_MALLINFO */ + +/* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } +*/ +void** independent_calloc(size_t, size_t, void**); + +/* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. +*/ +void** independent_comalloc(size_t, size_t*, void**); + +/* + bulk_free(void* array[], size_t n_elements) + Frees and clears (sets to null) each non-null pointer in the given + array. This is likely to be faster than freeing them one-by-one. + If footers are used, pointers that have been allocated in different + mspaces are not freed or cleared, and the count of all such pointers + is returned. For large arrays of pointers with poor locality, it + may be worthwhile to sort this array before calling bulk_free. +*/ +size_t bulk_free(void**, size_t n_elements); + +/* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ +void* pvalloc(size_t); + +/* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. +*/ +int malloc_trim(size_t); + +/* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. + + malloc_stats is not compiled if NO_MALLOC_STATS is defined. +*/ +void malloc_stats(void); + +#endif /* !ONLY_MSPACES */ + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); +*/ +size_t malloc_usable_size(const void*); + +#if MSPACES + +/* + mspace is an opaque type representing an independent + region of space that supports mspace_malloc, etc. +*/ +typedef void* mspace; + +/* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). +*/ +mspace create_mspace(size_t capacity, int locked); + +/* + destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. +*/ +size_t destroy_mspace(mspace msp); + +/* + create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. +*/ +mspace create_mspace_with_base(void* base, size_t capacity, int locked); + +/* + mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. +*/ +int mspace_track_large_chunks(mspace msp, int enable); + +#if !NO_MALLINFO +/* + mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. +*/ +struct mallinfo mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + +/* + An alias for mallopt. +*/ +int mspace_mallopt(int, int); + +/* + The following operate identically to their malloc counterparts + but operate only for the given mspace argument +*/ +void* mspace_malloc(mspace msp, size_t bytes); +void mspace_free(mspace msp, void* mem); +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); +void* mspace_realloc(mspace msp, void* mem, size_t newsize); +void* mspace_realloc_in_place(mspace msp, void* mem, size_t newsize); +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); +size_t mspace_bulk_free(mspace msp, void**, size_t n_elements); +size_t mspace_usable_size(const void* mem); +void mspace_malloc_stats(mspace msp); +int mspace_trim(mspace msp, size_t pad); +size_t mspace_footprint(mspace msp); +size_t mspace_max_footprint(mspace msp); +size_t mspace_footprint_limit(mspace msp); +size_t mspace_set_footprint_limit(mspace msp, size_t bytes); +void mspace_inspect_all(mspace msp, + void(*handler)(void *, void *, size_t, void*), + void* arg); +#endif /* MSPACES */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +#endif /* MALLOC_280_H */ diff --git a/libc/include/string.h b/libc/include/string.h new file mode 100755 index 0000000..e33fa44 --- /dev/null +++ b/libc/include/string.h @@ -0,0 +1,13 @@ +#ifndef _STRING_H +#define _STRING_H 1 + +#include +#include + +int memcmp(const void*, const void*, size_t); +void* memcpy(void* __restrict, const void* __restrict, size_t); +void* memmove(void*, const void*, size_t); +void* memset(void*, int, size_t); +size_t strlen(const char*); + +#endif \ No newline at end of file diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h new file mode 100755 index 0000000..2d557b4 --- /dev/null +++ b/libc/include/sys/cdefs.h @@ -0,0 +1,5 @@ +#ifndef _SYS_CDEFS_H +#define _SYS_CDEFS_H 1 + +#define __red_libc 1 +#endif \ No newline at end of file diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h new file mode 100755 index 0000000..e69de29 diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c new file mode 100755 index 0000000..caee4b3 --- /dev/null +++ b/libc/stdio/printf.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include + +static bool print(const char* data, size_t length) { + const unsigned char* bytes = (const unsigned char*) data; + for (size_t i = 0; i < length; i++) + if(putchar(bytes[i]) == EOF) + return false' + return true; +} + +int printf(const char* restrict format, ...) { + va_list parameters; + va_start(parameters, format); + + int written = 0; + + while(*format != '\0') { + size_t maxrem = INT_MAX - writen; + + if(format[0] != '%' || format[1] == '%') { + if(format[0] == '%') + format++; + size_t amount = 1; + + while (format[amount] && format[amount] != '%') + amount++; + + if(maxrem < amount) { + //TODO: Set an OVERFLOW error + return -1; + } + + if ((!print(format, amount)) + return -1; + + format += amount; + written += amount; + continue; + } + + const char* first_format = format++; + + switch(*format) { + case 'c': + format++; + char c = (char) va_arg(parameters, int); + if(!maxrem) { + //TODO: Set OVERFLOW + return -1; + } + + if(!print(&c, sizeof(c))) + return -1; + written++; + break; + case 's': + format++; + + const char* str = va_arg(parameters, const char*); + size_t len = strlen(str); + + if(maxrem < len) { + //TODO: Set OVERFLOW + return -1; + } + + if(!print(str, len)) + return -1; + + written += len; + break; + default: + format = first_format; + size_t len = strlen(format); + if(maxrem < len) { + //TODO: Set OVERFLOW + return -1; + } + + if(!print(format, len)) + return -1; + written += len; + format += len; + break; + } + } + + va_end(parameters); + return written; +} diff --git a/libc/stdio/putchar.c b/libc/stdio/putchar.c new file mode 100755 index 0000000..eba2d17 --- /dev/null +++ b/libc/stdio/putchar.c @@ -0,0 +1,15 @@ +#include + +#if defined(__is_libk) +#include +#endif + +int putchar(int ic) { +#if defined(__is_libk) + char c = (char) ic; + term_write(&c, sizeof(c)); +#else + //TODO: Implement stdio & the write call +#endif + return ic; +} \ No newline at end of file diff --git a/libc/stdio/puts.c b/libc/stdio/puts.c new file mode 100755 index 0000000..ea900cb --- /dev/null +++ b/libc/stdio/puts.c @@ -0,0 +1,5 @@ +#include + +int puts(const char* string) { + return printf("%s\n"), string); +} \ No newline at end of file diff --git a/libc/stdlib/abort.c b/libc/stdlib/abort.c new file mode 100755 index 0000000..8bf0f45 --- /dev/null +++ b/libc/stdlib/abort.c @@ -0,0 +1,15 @@ +#include +#include + +__attribute__((__noreturn__)) +void abort(void) { + +#if defined(__is_libk) + //TODO: Kernel panic. + printf(">>PANIC<<<\n abort() panicked!\n"); +#else + printf("abort() called\n"); +#endif + while(1) {} + __builtin_unreachable(); +} \ No newline at end of file diff --git a/libc/string/memcmp.c b/libc/string/memcmp.c new file mode 100755 index 0000000..b060501 --- /dev/null +++ b/libc/string/memcmp.c @@ -0,0 +1,13 @@ +#include + +int memcmp(const void* aptr, const void* bptr, size_t size_ { + const unsigned char* a = (const unsigned char*) aptr; + const unsigned char* b = (const unsigned char*) bptr; + + for (size_t i = 0; i < size; i++) { + if(a[i] < b[i] + return -1 + else if(b[i] < a[i]) + return 1; + } + return 0; \ No newline at end of file diff --git a/libc/string/memmove.c b/libc/string/memmove.c new file mode 100755 index 0000000..87c171f --- /dev/null +++ b/libc/string/memmove.c @@ -0,0 +1,14 @@ +#include + +void* memmove(void* dstptr, const void* srcptr, size_t size) { + unsigned char* dst = (unsigned char*) dstptr; + const unsigned char* stc = (const unsigned char*) srcptr; + if(dst < src) { + for(size_t i = o; i < size; i++) + dst[i] = src[i]; + } else { + for(size_t i = size; i != 0; i--) + dst[i-1] = src[i-1]; + } + return dstptr; +} \ No newline at end of file diff --git a/libc/string/memset.c b/libc/string/memset.c new file mode 100755 index 0000000..f16ce62 --- /dev/null +++ b/libc/string/memset.c @@ -0,0 +1,8 @@ +#include + +void* memset(void* bufptr, int value, size_t size) { + unsigned char* buf = (unsigned char*) bufptr; + for(size_t i = 0; i < size; i++) + buf[i] = (unsigned char) value; + return bufptr; +} \ No newline at end of file diff --git a/libc/string/strlen.c b/libc/string/strlen.c new file mode 100755 index 0000000..998b46d --- /dev/null +++ b/libc/string/strlen.c @@ -0,0 +1,8 @@ +#include + +size_t strlen(const char* str) { + size_t len = 0; + while (str[len]) + len++ + return len; +} \ No newline at end of file diff --git a/makefile b/makefile new file mode 100755 index 0000000..8796645 --- /dev/null +++ b/makefile @@ -0,0 +1,94 @@ +DEFAULT_HOST!=../default-host.sh +HOST?=DEFAULT_HOST +HOSTARCH!=../target_to_arch.sh $(HOST) + +CFLAGS?= -O2 -g +CPPFLAGS?= +LDFLAGS?= +LIBS?= + +DESTDIR?= +PREFIX?=/usr/local +EXEC_PREFIX?=$(PREFIX) +BOOTDIR?=$(EXEC_PREFIX)/boot +INCLUDEDIR?=$(PREFIX)/include + +CFLAGS:=$(CFLAGS) -ffreestanding -Wall -Wextra +CPPFLAGS:=$(CPPFLAGS) -D__is_kernel -Iinclude +LDFLAGS:=$(LDFLAGS) +LIBS:=$(LIBS) -nostdlib -lk -lgcc + +ARCHDIR=arch/$(HOSTARCH) + +include $(ARCHDIR)/make.config + +CFLAGS:=$(CFLAGS) $(KERNEL_ARCH_CFLAGS) +CPPFLAGS:=$(CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS) +LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS) +LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS) + +KERNEL_OBJS=\ +$(KERNEL_ARCH_OBJS)\ +kernel/kernel.o + +OBJS=\ +$(ARCHDIR)/crti.o \ +$(ARCHDIR)/crtbegin.o \ +$(KERNEL_OBJS) \ +$(ARCHDIR)/crtend.o \ +$(ARCHDIR)/crtn.o + +LINK_LIST=\ +$(LDFLAGS) \ +$(ARCHDIR)/crti.o \ +$(ARCHDIR)/crtbegin.o \ +$(KERNEL_OBJS) \ +$(LIBS) \ +$(ARCHDIR)/crtend.o \ +$(ARCHDIR)/crtn.o + +.PHONY: all clean install install-headers install-kernel +.SUFFIXES: .o .c .s + +all: red.kernel + +red.kernel: $(OBJS) $(ARCHDIR)/linker.ld + $(CC) -T $(ARCHDIR)/linker.ld -o $@ $(CFLAGS) $(LINK_LIST) + +$(ARCHDIR)/crtbegin.o $(ARCHDIR)/crtend.o: + OBJ=`$(CC) $(CFLAGS) $(LDFLAGS) -print-file-name=$(@F)` && cp "$$OBJ" $@ + +.c.o: + $(CC) -MD -c $< -o $@ -std=gnull $(CFLAGS) $(CPPFLAGS) +.s.o: + $(CC) -MD -c $< -o $@ $(CFLAGS) $(CPPFLAGS) + +clean: + rm -f red.kernel + rm -f $(OBJS) *.0 */*.o */*/*.o + rm -f $(OBJS:.o=.d) *.d */*.d */*/*.d + +install: install-headers install-kernel gen-iso + +install-headers: + mkdir -p $(DESTDIR)$(INCLUDEDIR) + cp -R --preserve=timestamps include/. $(DESTDIR)$(INCLUDEDIR)/. + +install-kernel: red.kernel + mkdir -p $(DESTDIR)$(BOOTDIR) + cp red.kernel $(DESTDIR)$(BOOTDIR) + +gen-iso: + rm -f red.iso \ + cp red.kernel iso/boot/kernel.elf \ + genisoimage -R\ + -b boot/grub/stage2_eltorito\ + -no-emul-boot\ + -A ProjectRED\ + -input-charset utf8\ + -quiet + -boot-info-table\ + -o red.iso\ + iso + +-include $(OBJS:.o=.d)