commit b7fe1385495ebfd5551d1a13c5606cb3dba486fb Author: Jenny Curle Date: Mon Apr 1 02:18:48 2019 +0100 First upload. Libc incomplete. 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 0000000..9e1617c Binary files /dev/null and b/iso/boot/grub/stage2_eltorito differ diff --git a/iso/boot/kernel.elf b/iso/boot/kernel.elf new file mode 100755 index 0000000..6e87838 Binary files /dev/null and b/iso/boot/kernel.elf differ 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)