diff --git a/test_programs/.gitignore b/test_programs/.gitignore index 0a0178a..f7e49da 100644 --- a/test_programs/.gitignore +++ b/test_programs/.gitignore @@ -1,4 +1,4 @@ # Ignoring dump files *.dump *.o -./target \ No newline at end of file +target \ No newline at end of file diff --git a/test_programs/Makefile b/test_programs/Makefile index 662a888..ad2c039 100644 --- a/test_programs/Makefile +++ b/test_programs/Makefile @@ -6,8 +6,16 @@ include $(TOPDIR)/Makefile.config # dumps: $(MAKE) dumps -C riscv_instructions/ - mkdir -p ${TOPDIR}/target - find . -name '*.dump' -exec mv {} ${TOPDIR}/target \; + mkdir -p ${TOPDIR}/target/dumps/ + find . -name '*.dump' -exec mv {} ${TOPDIR}/target/dumps/ \; + +user_lib: + $(MAKE) -C userlib/ + +tests: user_lib + $(MAKE) tests -C riscv_instructions/ + mkdir -p ${TOPDIR}/target/guac/ + find . -name '*.guac' -exec mv {} ${TOPDIR}/target/guac/ \; clean: rm -rf $(TOPDIR)/target \ No newline at end of file diff --git a/test_programs/Makefile.tests b/test_programs/Makefile.tests new file mode 100644 index 0000000..681d31f --- /dev/null +++ b/test_programs/Makefile.tests @@ -0,0 +1,38 @@ +include $(TOPDIR)/Makefile.config +USERLIB = $(TOPDIR)/userlib +INCPATH += -I$(TOPDIR) -I$(USERLIB) +LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds +ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH) +CFLAGS = $(RISCV_CFLAGS) $(INCPATH) + +# Rules +%.o: %.s + $(RISCV_AS) $(ASFLAGS) -c $< + +%.o: %.c + $(RISCV_GCC) $(CFLAGS) -c $< + +%.dump: %.o + $(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@ + +%.guac: %.o + $(RISCV_LD) $(LDFLAGS) $+ -o $@ + +# Dependencies +.%.d: %.s + @echo Generating dependencies for $< + @$(SHELL) -ec '$(GCC) -x assembler-with-cpp -M $(ASFLAGS) $< \ + | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \ + [ -s $@ ] || rm -f $@' + +.%.d: %.c + @echo Generating dependencies for $< + @$(SHELL) -ec '$(GCC) -M $(CFLAGS) $< \ + | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \ + [ -s $@ ] || rm -f $@' + +# Targets +#clean: +# rm -rf *.o 2> /dev/null +# rm -rf *.dump 2> /dev/null +# rm -rf *.guac 2> /dev/null \ No newline at end of file diff --git a/test_programs/riscv_instructions/Makefile b/test_programs/riscv_instructions/Makefile index c134d5f..22a1303 100644 --- a/test_programs/riscv_instructions/Makefile +++ b/test_programs/riscv_instructions/Makefile @@ -1,4 +1,9 @@ dumps: make dumps -C boolean_logic/ make dumps -C jump_instructions/ - make dumps -C simple_arithmetics/ \ No newline at end of file + make dumps -C simple_arithmetics/ + +tests: + make tests -C boolean_logic/ + make tests -C jump_instructions/ + make tests -C simple_arithmetics/ \ No newline at end of file diff --git a/test_programs/riscv_instructions/boolean_logic/Makefile b/test_programs/riscv_instructions/boolean_logic/Makefile index 2150f14..5f738e9 100644 --- a/test_programs/riscv_instructions/boolean_logic/Makefile +++ b/test_programs/riscv_instructions/boolean_logic/Makefile @@ -1,4 +1,9 @@ TOPDIR = ../.. -include $(TOPDIR)/Makefile.dumps +include $(TOPDIR)/Makefile.tests -dumps: comparisons.dump if.dump switch.dump \ No newline at end of file +dumps: comparisons.dump if.dump switch.dump + +tests: comparisons.guac if.guac switch.guac + +# Dependances +$(PROGRAMS): % : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o \ No newline at end of file diff --git a/test_programs/riscv_instructions/jump_instructions/Makefile b/test_programs/riscv_instructions/jump_instructions/Makefile index 75b887b..ce69447 100644 --- a/test_programs/riscv_instructions/jump_instructions/Makefile +++ b/test_programs/riscv_instructions/jump_instructions/Makefile @@ -1,4 +1,6 @@ TOPDIR = ../.. -include $(TOPDIR)/Makefile.dumps +include $(TOPDIR)/Makefile.tests -dumps: jump.dump ret.dump \ No newline at end of file +dumps: jump.dump ret.dump + +tests: jump.guac ret.guac \ No newline at end of file diff --git a/test_programs/riscv_instructions/simple_arithmetics/Makefile b/test_programs/riscv_instructions/simple_arithmetics/Makefile index d775b97..623b297 100644 --- a/test_programs/riscv_instructions/simple_arithmetics/Makefile +++ b/test_programs/riscv_instructions/simple_arithmetics/Makefile @@ -1,4 +1,6 @@ TOPDIR = ../.. -include $(TOPDIR)/Makefile.dumps +include $(TOPDIR)/Makefile.tests -dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump \ No newline at end of file +dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump + +tests: unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac \ No newline at end of file diff --git a/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c b/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c index 05a7829..f1c005a 100644 --- a/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c +++ b/test_programs/riscv_instructions/simple_arithmetics/unsigned_addition.c @@ -1,3 +1,6 @@ +#include "userlib/syscall.h" +#include "userlib/libnachos.h" + // EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 1 int main() { unsigned int x = 0; diff --git a/test_programs/userlib/Makefile b/test_programs/userlib/Makefile new file mode 100644 index 0000000..903f3b5 --- /dev/null +++ b/test_programs/userlib/Makefile @@ -0,0 +1,4 @@ +TOPDIR = ../ +include $(TOPDIR)/Makefile.tests + +default: sys.o libnachos.o \ No newline at end of file diff --git a/test_programs/userlib/ldscript.lds b/test_programs/userlib/ldscript.lds new file mode 100644 index 0000000..7bc9ebf --- /dev/null +++ b/test_programs/userlib/ldscript.lds @@ -0,0 +1,61 @@ +/* + ldscript for running user programs under Nachos + + Sections should be aligned on page boundaries. Here an alignement of + at least 0x2000 is selected, thus supporting pages up to 8KB + large. See addrspace.cc for details. +*/ + +ENTRY(__start) +SECTIONS +{ + + /* Skip an area of about 8k, so that NULL pointer dereferences can + be detected */ + . += 0x2000; + + .sys ALIGN(0x4000) : { + *(.init) + *(.sys) + } + + /* Code is aligned on a 16k boundary + Due to the size of the .sys section, the code start address will + presumably be at address 0x4000 */ + .text ALIGN(0x400000) : { + _ftext = .; + eprol = .; + *(.text) + *(.fini) + } + etext = .; + _etext = .; + + /* Initialized data is aligned on a 16k boundary */ + .data ALIGN(0x4000) : { + _fdata = .; + *(.data) + *(.sdata) + } + .rodata ALIGN(0x4000) : + { + *(.rdata) + *(.srodata) + *(.rodata) + } + edata = .; + _edata = .; + + /* Non-initialized data is aligned on a 16k boundary */ + /* Bss = Block Started by Symbol */ + .bss ALIGN(0x4000) : { + *(.bss) + *(.sbss) + *(.scommon) + *(COMMON) + } + + end = .; + _end = .; + +} diff --git a/test_programs/userlib/libnachos.c b/test_programs/userlib/libnachos.c new file mode 100644 index 0000000..32bb273 --- /dev/null +++ b/test_programs/userlib/libnachos.c @@ -0,0 +1,630 @@ +/*! \file libnachos.c + * \brief Functions of our library, for user programs. + * + * This library only provides some usefull functions for + * programming. + * + * ----------------------------------------------------- + * This file is part of the Nachos-RiscV distribution + * Copyright (c) 2022 University of Rennes 1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details + * (see see ). + * ----------------------------------------------------- + */ + +#include "libnachos.h" +#include +#include + +//---------------------------------------------------------------------- +// threadStart() +/*! Makes a thread execute a function or program. This function +// is static, it is called internally by library function threadCreate +// and should not be called directly by user programs. The interest +// of this function is to be able to terminate threads correctly, +// even when the thread to be terminated does not explicitly call +// Exit. threadStart provides the mechanism by which Exit +// is called automatically +// +// \param func is the identificator of the function to execute. +*/ +//---------------------------------------------------------------------- + +static void threadStart(uint64_t func) +{ + VoidNoArgFunctionPtr func2; + func2=(VoidNoArgFunctionPtr)func; + // Call the function that actually contains the thread code + (*func2)(); + // Call exit, such that there is no return using an empty stack + Exit(0); +} + +//---------------------------------------------------------------------- +// threadCreate() +/*! Creates a thread and makes it execute a function. +// +// NB : instead of directly executing the required function, +// function threadStart is called so as to ensure +// that the thread will properly exit +// This function must be called instead of calling directly +// the system call newThread +// +// \param name the name of the thread (for debugging purpose) +// \param func is the address of the function to execute. +*/ +//---------------------------------------------------------------------- +ThreadId threadCreate(char *debug_name, VoidNoArgFunctionPtr func) +{ + return newThread(debug_name, (uint64_t)threadStart,(uint64_t)func); +} + +//---------------------------------------------------------------------- +// n_strcmp() +/*! String comparison +// +// \param s1 is the first string, +// \param s2 is the second one. +// \return an integer greater than, equal to, or less than 0, +// if the first string is greater than, equal to, or less than +// the the second string. +*/ +//---------------------------------------------------------------------- +int n_strcmp(const char *s1, const char *s2) +{ + int comparaison; + int fini=0; + int i=0; + while(!fini) { + if ((s1[i]==0)&&(s2[i]==0)) { + fini=1; + comparaison=0; + } + if (s1[i]s2[i]) { + fini=1; + comparaison=1; + } + i++; + } + return comparaison; +} + +//---------------------------------------------------------------------- +// n_strcpy() +/*! String copy +// +// \param dst is where the string is to be copied, +// \param src is where the string to copy is. +// \return dst, if the copy successes, 0 otherwise +*/ +//---------------------------------------------------------------------- +char *n_strcpy(char *dst, const char *src) +{ + int i=0; + int fini=0; + if ((dst!=0)&&(src!=0)) { + while(fini==0) { + if(src[i]=='\0') fini=1; + dst[i]=src[i]; + i++; + } + return dst; + } + else return 0; +} + +//---------------------------------------------------------------------- +// n_strlen() +/*! Gives the number of bytes in a string, not including the +// terminating null character. +// +// \param c is a pointer onto a string. +// \return the length of the string. +*/ +//---------------------------------------------------------------------- +size_t n_strlen(const char *s) +{ + size_t i=0; + while (s[i] != 0) i++; + return i; +} + + +//---------------------------------------------------------------------- +// n_strcat() +/*! Appends a copy of a string, including null character, to the end +// of another string. Enough memory has to be available in the +// destination string. +// +// \param dst is a pointer onto the string where the other string +// will be appended. +// \param src is the string to append. +// \return the pointer string dst. +*/ +//---------------------------------------------------------------------- +char *n_strcat(char *dst, const char *src) +{ + int i,j,k; + i=(int)n_strlen(dst); + j=(int)n_strlen(src); + for(k=i;k<=j+i;k++) { + dst[k]=src[k-i]; + } + return dst; +} + + +//---------------------------------------------------------------------- +// n_toupper() +/*! Gives the upper-case letter corresponding to the lower-case +// letter passed as parameter. +// +// \param c is the ASCII code of the letter to transform. +// \return the corresponding upper-case letter +*/ +//---------------------------------------------------------------------- +int n_toupper(int c) +{ + if((c>='a')&&(c<='z')) + return c+('A'-'a'); + else return c; +} + +//---------------------------------------------------------------------- +// n_tolower() +/*! Gives the lower-case letter corresponding to the upper-case +// letter passed as parameter +// +// \param c is the ASCII code of the letter to transform. +// \return the corresponding lower-case letter +*/ +//---------------------------------------------------------------------- +int n_tolower(int c) +{ + if((c<='Z')&&(c>='A')) + return c+('a'-'A'); + else return c; +} + +//---------------------------------------------------------------------- +// n_atoi() +/*! String to integer conversion. +// +// \param c is a pointer onto a string. +// \return the corresponding value +*/ +//---------------------------------------------------------------------- +int n_atoi(const char *str) +{ + int i=0; + int fini=0; + int val=0; + int negative = 0; + if (str[i] == '-') { + negative = 1; i=1; + } + while(!fini) + { + if(str[i]==0 || str[i]<'0' || str[i]>'9') + fini=1; + else + { + val*=10; + val+=str[i]-'0'; + i++; + } + } + if (negative) return(-val); else return val; +} + +//---------------------------------------------------------------------- +// n_memcmp() +/*! Memory comparison. +// +// \param s1 is the first memory area, +// \param s2 is the second memory area. +// \param n size in bytes of the area to be compared. +// \return an integer less than, equal to, or greater than 0, +// according as s1 is lexicographically less than, equal to, +// or greater than s2 when taken to be unsigned characters. +// +*/ +//---------------------------------------------------------------------- +int n_memcmp(const void *s1, const void *s2, size_t n) +{ + unsigned char* c1=(unsigned char*)s1; + unsigned char* c2=(unsigned char*)s2; + + int comparaison=0; + int fini=0; + int i=0; + while ((!fini)&&(ic2[i]) { + fini=1; + comparaison=1; + } + i++; + } + return comparaison; +} + +//---------------------------------------------------------------------- +// n_memcpy() +/*! Memory copy. +// +// \param s1 is where the elements are to be copied, +// \param s2 is the memory area to copy. +// \param n size in bytes of the area to be copied. +// \return the memory area where the copy has been done. +*/ +//---------------------------------------------------------------------- +void *n_memcpy(void *s1, const void *s2, size_t n) +{ + + unsigned char* c1=(unsigned char*)s1; + unsigned char* c2=(unsigned char*)s2; + + int i=0; + if ((c1!=0)&&(c2!=0)) { + while(i> 4) & 0xf); + s[1] = TOHEX(addr[i] & 0xf); + s[2] = '\0'; + n_printf("%s ", s); + if ((((i+1)%16) == 0) || (i == len-1)) + n_printf("\n"); + } +} + +#define PUTCHAR(carac) \ + do { \ + if (result < len-1) *buff++ = carac;\ + result++; \ + } while (0) + + +//---------------------------------------------------------------------- +// n_vsnprintf() +/*! Build a string according to a specified format (internal function) +// +// Nachos vsnprintf accepts: +// %c to print a character, +// %s, to print a string, +// %d, to print an integer, +// %x, to print an integer in hexa +// %lx %ld same for 64-bit values +// %f, to print a floating point value +// +// \param buff the destination buffer to generate the string to +// \param len the size of buff, determines the number max of +// characters copied to buff (taking the final \0 into account) +// \param format the string to parse +// \param ap parameters to print +// +// \return the number of characters formatted (NOT including \0), +// that is, the number of characters that would have been written +// to the buffer if it were large enough. -1 on error. +*/ +//---------------------------------------------------------------------- +static int n_vsnprintf(char *buff, int len, const char *format, va_list ap) +{ + int i, result; + + if (!buff || !format || (len < 0)) { + return -1; + } + result = 0; + + for (i=0 ; format[i] != '\0' ; i++) { + switch (format[i]) { + case '%': + i++; + switch(format[i]) { + case '%': { + PUTCHAR('%'); + break; + } + case 'i': + case'd': { + int integer = (int) va_arg(ap,int); + int cpt2 = 0; + char buff_int[11]; + if (integer<0) {PUTCHAR('-'); + } + do { + int m10 = integer%10; + m10 = (m10 < 0)? -m10:m10; + buff_int[cpt2++]=(char)('0'+ m10); + integer=integer/10; + } while(integer!=0); + for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) { + PUTCHAR(buff_int[cpt2]); + } + break; + } + case 'l': { + i++; + switch(format[i]) { + case 'd': { + long longer = va_arg(ap,long); + int cpt2 = 0; + char buff_long[20]; + if (longer<0) { + PUTCHAR('-'); + } + do { + int m10 = longer%10; + m10 = (m10 < 0)? -m10:m10; + buff_long[cpt2++]=(char)('0'+ m10); + longer=longer/10; + } while(longer!=0); + for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) { + PUTCHAR(buff_long[cpt2]); + } + break; + } + case 'x': { + uint64_t hexa = va_arg(ap,long); + uint64_t nb; + uint32_t i, had_nonzero = 0; + for (i=0 ; i < 16 ; i++) { + nb = (hexa << (i*4)); + nb = (nb >> 60); + nb = nb & 0x000000000000000f; + // Skip the leading zeros + if (nb == 0) { + if (had_nonzero) { + PUTCHAR((uint8_t)'0'); + } + } + else { + had_nonzero = 1; + if (nb < 10) + PUTCHAR((uint8_t)'0'+(uint8_t)nb); + else + PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10)); + } + } + if (! had_nonzero) + PUTCHAR((uint8_t)'0'); + break; + } + default: { + PUTCHAR('%'); + PUTCHAR('l'); + PUTCHAR(format[i]); + break; + } + } + + break; + } + case 'c': { + int value = va_arg(ap,int); + PUTCHAR((char)value); + break; + } + case 's': { + char *string = va_arg(ap,char *); + if (! string) + string = "(null)"; + for( ; *string != '\0' ; string++) + PUTCHAR(*string); + break; + } + case 'x': { + uint32_t hexa = va_arg(ap,int); + uint32_t nb; + uint32_t i, had_nonzero = 0; + for (i=0 ; i < 8 ; i++) { + nb = (hexa << (i*4)); + nb = (nb >> 28); + nb = nb & 0x0000000f; + // Skip the leading zeros + if (nb == 0) { + if (had_nonzero) + PUTCHAR((uint8_t)'0'); + } + else { + had_nonzero = 1; + if (nb < 10) + PUTCHAR((uint8_t)'0'+(uint8_t)nb); + else + PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10)); + } + } + if (! had_nonzero) + PUTCHAR((uint8_t)'0'); + break; + } + /*case 'f': { + // Very simple routine to print floats as xxxx.yyyyy + // Not very good (unable to print large numbers) + // If anyone wants to re-write it, feel free ... + double f = (double) va_arg(ap,double); + int cpt2, j; + char buff_float[200]; + long ient,idec; + if (f<0) { + PUTCHAR('-'); + f = -f; + } + ient = (int)f; + // 100000 = print 5 digits max + idec = (int)((f - ((double)ient))*100000); + // Round up + if ( f - ((double)ient) - ((double)idec)/100000.0 >= 0.5E-5) + idec ++; + cpt2 = 0; + // Print digits after the '.' + for (j=0 ; j<5 ; j++) { + buff_float[cpt2++]=(char)('0'+(idec%10)); + idec=idec/10; + } + buff_float[cpt2++] = '.'; + // Print digits before the '.' + do { + buff_float[cpt2++]=(char)('0'+ (ient%10)); + ient=ient/10; + } while (ient!=0); + for(j = cpt2 - 1 ; j >= 0 ; j--) + PUTCHAR(buff_float[j]); + break; + } + */ + default: + PUTCHAR('%'); + PUTCHAR(format[i]); + } + break; + default: + PUTCHAR(format[i]); + } + } + *buff = '\0'; + return result; +} + +//---------------------------------------------------------------------- +// n_snprintf() +/*! Build a string according to a specified format +// +// Nachos snprintf accepts: +// %c to print a character, +// %s, to print a string, +// %d, to print an integer, +// %x, to print a string in hexa +// %f, to print a floating point value +// +// \param buff the destination buffer to generate the string to +// \param len the size of buff, determines the number max of +// characters copied to buff (taking the final \0 into account) +// \param format the string to parse +// \param ... the (variable number of) arguments +// +// \return the number of characters formatted (NOT including \0), +// that is, the number of characters that would have been written +// to the buffer if it were large enough. -1 on error. +*/ +//---------------------------------------------------------------------- +int n_snprintf(char * buff, int len, const char *format, ...){ + va_list ap; + va_start(ap, format); + len = n_vsnprintf(buff, len, format, ap); + va_end(ap); + return len; +} + +//---------------------------------------------------------------------- +// n_printf() +/*! Print to the standard output parameters. +// +// Nachos printf accepts: +// %c to print a character, +// %s, to print a string, +// %d, to print an integer, +// %x, to print a string in hexa +// %ld, %lx, same for 64-bit values +// %f, to print a floating point value +// +// \param parameters to print, +// \param type of print. +*/ +//---------------------------------------------------------------------- +void n_printf(const char *format, ...){ + + va_list ap; + char buff[200]; + int len; + + va_start(ap, format); + len = n_vsnprintf(buff, sizeof(buff), format, ap); + va_end(ap); + + if (len >= sizeof(buff)) { + len = sizeof(buff) - 1; + } + if (len > 0) { + Write(buff,len,CONSOLE_OUTPUT); + } +} + +//---------------------------------------------------------------------- +// n_read_int() +/*! +// Very basic minimalist read integer function, no error +// checking... +*/ +//---------------------------------------------------------------------- +int n_read_int(void) { + char buff[200]; + Read(buff,200,CONSOLE_INPUT); + return n_atoi(buff); +} diff --git a/test_programs/userlib/libnachos.h b/test_programs/userlib/libnachos.h new file mode 100644 index 0000000..f033ed5 --- /dev/null +++ b/test_programs/userlib/libnachos.h @@ -0,0 +1,87 @@ +/*! \file libnachos.h + \brief Function structures for programs + + Libnachos proposes several 'libc-like' functions + for: + Input-Output operations, + String operations, + Memory operations, + System calls are defined in kernel/syscalls.h + + Nachos-libc functions are prefixed by 'n' to avoid + any confusion with standard libc functions. + + * ----------------------------------------------------- + * This file is part of the Nachos-RiscV distribution + * Copyright (c) 2022 University of Rennes 1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details + * (see see ). + * ----------------------------------------------------- +*/ + +#include "userlib/syscall.h" + +typedef void (*VoidNoArgFunctionPtr)(); +typedef unsigned int size_t; + +// Thread management +// ---------------------------- +ThreadId threadCreate(char * debug_name, VoidNoArgFunctionPtr func); + +// Input/Output operations : +// ------------------------------------ + +// Print on the standard output specified parameters. +void n_printf(const char *format, ...); + +// Format (of max length ) according to the format +int n_snprintf(char * buff, int len, const char *format, ...); + +// Read an integer on the standard input +int n_read_int(void); + +// String operations : +// ------------------- + +// Compare two strings byte by byte. +int n_strcmp(const char *s1, const char *s2); + +// Copy a string. +char* n_strcpy(char *dst, const char *src); + +// Return the number of bytes in a string. +size_t n_strlen(const char *s); + +// appends a copy of a string, to the end of another string. +char* n_strcat(char *dst, const char *src); + +// Return a upper-case letter, +// equivalent to the lower-case letter given. +int n_toupper(int c); + +// Return a lower-case letter, +// equivalent to the upper-case letter given. +int n_tolower(int c); + +// Convert a string in integer. +int n_atoi(const char *str); + +// Concerning memory area operations : +// ----------------------------------- + +// Compare two memory area, looking at the first n bytes . +int n_memcmp(const void *s1, const void *s2, size_t n); + +// Copy n byte from an memory area to another. +void* n_memcpy(void *s1, const void *s2, size_t n); + +// Set the first n bytes in a memory area to a specified value. +void* n_memset(void *s, int c, size_t n); diff --git a/test_programs/userlib/sys.s b/test_programs/userlib/sys.s new file mode 100644 index 0000000..64c3069 --- /dev/null +++ b/test_programs/userlib/sys.s @@ -0,0 +1,343 @@ +/* Start.s + * Assembly language assist for user programs running on top of Nachos. + * + * Since we don't want to pull in the entire C library, we define + * what we need for a user program here, namely Start and the system + * calls. + * + * ----------------------------------------------------- + * This file is part of the BurritOS distribution + * Copyright (c) 2022 University of Rennes 1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details + * (see see ). + * ----------------------------------------------------- + */ + +#define IN_ASM +#include "userlib/syscall.h" + + // Equivalent to ".text", but with a different name, in order + // to be correctly handled by the ldscript + .section .sys,"ax",@progbits + + .align 2 + +/* ------------------------------------------------------------- + * __start + * Initialize running a C program, by calling "main". + * + * NOTE: This has to be first, so that it gets loaded at location 0. + * The Nachos kernel always starts a program by jumping to location 0. + * ------------------------------------------------------------- + */ + + .globl __start + .type __start, @function +__start: + +/* Call the program entry point */ + call main + li a0, 0 + call Exit + jr ra /* if we return from main, exit(0) */ + + +/* ------------------------------------------------------------- + * System call stubs: + * Assembly language assist to make system calls to the Nachos kernel. + * There is one stub per system call, that places the code for the + * system call into register r10, and leaves the arguments to the + * system call alone (in other words, arg1 is in r12, arg2 is + * in r13, arg3 is in r14, arg4 is in r15) + * + * The return value is in r10. This follows the standard C calling + * convention on the RISC-V. + * ------------------------------------------------------------- + */ + + .globl Halt + .type __Halt, @function +Halt: + addi a7,zero,SC_HALT + ecall + jr ra + + + .globl Exit + .type __Exit, @function +Exit: + addi a7,zero,SC_EXIT + ecall + jr ra + + + .globl Exec + .type __Exec, @function +Exec: + addi a7,zero,SC_EXEC + ecall + jr ra + + + .globl Join + .type __Join, @function +Join: + addi a7,zero,SC_JOIN + ecall + jr ra + + + .globl Create + .type __Create, @function +Create: + addi a7,zero,SC_CREATE + ecall + jr ra + + + .globl Open + .type __Open, @function +Open: + addi a7,zero,SC_OPEN + ecall + jr ra + + + .globl Read + .type __Read, @function +Read: + addi a7,zero,SC_READ + ecall + jr ra + + + + .globl Write + .type __Write, @function +Write: + addi a7,zero,SC_WRITE + ecall + jr ra + + + .globl Seek + .type __Seek, @function +Seek: + addi a7,zero,SC_SEEK + ecall + jr ra + + + .globl Close + .type __Close, @function +Close: + addi a7,zero,SC_CLOSE + ecall + jr ra + + + .globl FSList + .type __FSList, @function +FSList: + addi a7,zero,SC_FSLIST + ecall + jr ra + + + .globl newThread + .type __newThread, @function +newThread: + addi a7,zero,SC_NEW_THREAD + ecall + jr ra + + + .globl Remove + .type __Remove, @function +Remove: + addi a7,zero,SC_REMOVE + ecall + jr ra + + + .globl Yield + .type __Yield, @function +Yield: + addi a7,zero,SC_YIELD + ecall + jr ra + + + .globl PError + .type __PError, @function +PError: + addi a7,zero,SC_PERROR + ecall + jr ra + + + .globl P + .type __P, @function +P: + addi a7,zero,SC_P + ecall + jr ra + + + .globl V + .type __V, @function +V: + addi a7,zero,SC_V + ecall + jr ra + + .globl SemCreate + .type __SemCreate, @function +SemCreate: + addi a7,zero,SC_SEM_CREATE + ecall + jr ra + + + .globl SemDestroy + .type __SemDestroy, @function +SemDestroy: + addi a7,zero,SC_SEM_DESTROY + ecall + jr ra + + + .globl SysTime + .type __SysTime, @function +SysTime: + addi a7,zero,SC_SYS_TIME + ecall + jr ra + + + .globl LockCreate + .type __LockCreate, @function +LockCreate: + addi a7,zero,SC_LOCK_CREATE + ecall + jr ra + + .globl LockDestroy + .type __LockDestroy, @function +LockDestroy: + addi a7,zero,SC_LOCK_DESTROY + ecall + jr ra + + + .globl LockAcquire + .type __LockAquire, @function +LockAcquire: + addi a7,zero,SC_LOCK_ACQUIRE + ecall + jr ra + + + .globl LockRelease + .type __LockRelease, @function +LockRelease: + addi a7,zero,SC_LOCK_RELEASE + ecall + jr ra + + + .globl CondCreate + .type __CondCreate, @function +CondCreate: + addi a7,zero,SC_COND_CREATE + ecall + jr ra + + + .globl CondDestroy + .type __CondDestroy, @function +CondDestroy: + addi a7,zero,SC_COND_DESTROY + ecall + jr ra + + + .globl CondWait + .type __CondWait, @function +CondWait: + addi a7,zero,SC_COND_WAIT + ecall + jr ra + + + .globl CondSignal + .type __CondSignal, @function +CondSignal: + addi a7,zero,SC_COND_SIGNAL + ecall + jr ra + + + .globl CondBroadcast + .type __CondBroadcast, @function +CondBroadcast: + addi a7,zero,SC_COND_BROADCAST + ecall + jr ra + + + .globl TtySend + .type __TtySend, @function +TtySend: + addi a7,zero,SC_TTY_SEND + ecall + jr ra + + + .globl TtyReceive + .type __TtyReceive, @function +TtyReceive: + addi a7,zero,SC_TTY_RECEIVE + ecall + jr ra + + + .globl Mkdir + .type __Mkdir, @function +Mkdir: + addi a7,zero,SC_MKDIR + ecall + jr ra + + + .globl Rmdir + .type __Rmdir, @function +Rmdir: + addi a7,zero,SC_RMDIR + ecall + jr ra + + + .globl Mmap + .type __Mmap, @function +Mmap: + addi a7,zero,SC_MMAP + ecall + jr ra + + .globl Debug + .type __Debug, @function +Debug: + addi a7,zero,SC_DEBUG + ecall + jr ra + + diff --git a/test_programs/userlib/syscall.h b/test_programs/userlib/syscall.h new file mode 100644 index 0000000..e650db1 --- /dev/null +++ b/test_programs/userlib/syscall.h @@ -0,0 +1,287 @@ +/*! \file syscall.h + \brief Nachos system call interface. + + These are Nachos kernel operations + that can be invoked from user programs, by trapping to the kernel + via the "syscall" instruction. + + This file is included by user programs and by the Nachos kernel. + + Each of these is invoked by a user program by simply calling the + procedure; an assembly language stub stuffs the system call code + into a register, and traps to the kernel. The kernel procedures + are then invoked in the Nachos kernel, after appropriate error checking, + from the system call entry point in exception.cc. + + * ----------------------------------------------------- + * This file is part of the Nachos-RiscV distribution + * Copyright (c) 2022 University of Rennes 1. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details + * (see see ). + * ----------------------------------------------------- + */ + + +#ifndef SYSCALLS_H +#define SYSCALLS_H + +//#include "kernel/copyright.h" + +/* system call codes -- used by the stubs to tell the kernel which system call + * is being asked for + */ +#define SC_HALT 0 +#define SC_EXIT 1 +#define SC_EXEC 2 +#define SC_JOIN 3 +#define SC_CREATE 4 +#define SC_OPEN 5 +#define SC_READ 6 +#define SC_WRITE 7 +#define SC_SEEK 8 +#define SC_CLOSE 9 +#define SC_NEW_THREAD 10 +#define SC_YIELD 11 +#define SC_PERROR 12 +#define SC_P 13 +#define SC_V 14 +#define SC_SEM_CREATE 15 +#define SC_SEM_DESTROY 16 +#define SC_LOCK_CREATE 17 +#define SC_LOCK_DESTROY 18 +#define SC_LOCK_ACQUIRE 19 +#define SC_LOCK_RELEASE 20 +#define SC_COND_CREATE 21 +#define SC_COND_DESTROY 22 +#define SC_COND_WAIT 23 +#define SC_COND_SIGNAL 24 +#define SC_COND_BROADCAST 25 +#define SC_TTY_SEND 26 +#define SC_TTY_RECEIVE 27 +#define SC_MKDIR 28 +#define SC_RMDIR 29 +#define SC_REMOVE 30 +#define SC_FSLIST 31 +#define SC_SYS_TIME 32 +#define SC_MMAP 33 +#define SC_DEBUG 34 + +#ifndef IN_ASM + +/* The system call interface. These are the operations the Nachos + * kernel needs to support, to be able to run user programs. + * + */ + +typedef int t_error; + +/* Stop Nachos, and print out performance stats */ +void Halt(); + + +/* Return the time spent running Nachos */ + +/*! \brief Defines the Nachos basic time unit */ +typedef struct { + long seconds; + long nanos; +} Nachos_Time; +void SysTime(Nachos_Time *t); + +/* Address space control operations: Exit, Exec, and Join */ + +/* This user program is done (status = 0 means exited normally). */ +void Exit(int status); + +/* A unique identifier for a thread executed within a user program */ +typedef unsigned long ThreadId; + +/* Run the executable, stored in the Nachos file "name", and return the + * master thread identifier + */ +ThreadId Exec(char *name); + +/* Create a new thread in the current process + * Return thread identifier + */ +ThreadId newThread(char * debug_name, int func, int arg); + +/* Only return once the the thread "id" has finished. + */ +t_error Join(ThreadId id); + +/* Yield the CPU to another runnable thread, whether in this address space + * or not. + */ +void Yield(); + +/*! Print the last error message with the personalized one "mess" */ +void PError(char *mess); + +/* File system operations: Create, Open, Read, Write, Seek, Close + * These functions are patterned after UNIX -- files represent + * both files *and* hardware I/O devices. + * + * If this assignment is done before doing the file system assignment, + * note that the Nachos file system has a stub implementation, which + * will work for the purposes of testing out these routines. + */ + +/* A unique identifier for an open Nachos file. */ +typedef unsigned long OpenFileId; + +/* when an address space starts up, it has two open files, representing + * keyboard input and display output (in UNIX terms, stdin and stdout). + * Read and Write can be used directly on these, without first opening + * the console device. + */ +#define CONSOLE_INPUT 0 +#define CONSOLE_OUTPUT 1 + +/* Create a Nachos file, with "name" */ +t_error Create(char *name,int size); + +/* Open the Nachos file "name", and return an "OpenFileId" that can + * be used to read and write to the file. + */ +OpenFileId Open(char *name); + +/* Write "size" bytes from "buffer" to the open file. */ +t_error Write(char *buffer, int size, OpenFileId id); + +/* Read "size" bytes from the open file into "buffer". + * Return the number of bytes actually read -- if the open file isn't + * long enough, or if it is an I/O device, and there aren't enough + * characters to read, return whatever is available (for I/O devices, + * you should always wait until you can return at least one character). + */ +t_error Read(char *buffer, int size, OpenFileId id); + +/* Seek to a specified offset into an opened file */ +t_error Seek(int offset, OpenFileId id); + +#ifndef SYSDEP_H +/* Close the file, we're done reading and writing to it. */ +t_error Close(OpenFileId id); +#endif // SYSDEP_H + +/* Remove the file */ +t_error Remove(char* name); + +/******************************************************************/ +/* system calls concerning directory management */ + +/* Create a new repertory + Return a negative number if an error ocurred. +*/ +t_error Mkdir(char* name); + +/* Destroy a repertory, which must be empty. + Return a negative number if an error ocurred. +*/ +t_error Rmdir(char* name); + +/* List the content of NachOS FileSystem */ +t_error FSList(); + +/******************************************************************/ +/* User-level synchronization operations : */ + +/* System calls concerning semaphores management */ + +typedef unsigned long SemId; + +/* Create a semaphore, initialising it at count. + Return a Semid, which will enable to do operations on this + semaphore */ +SemId SemCreate(char * debug_name, int count); + +/* Destroy a semaphore identified by sema. + Return a negative number if an error occured during the destruction */ +t_error SemDestroy(SemId sema); + +/* Do the operation P() on the semaphore sema */ +t_error P(SemId sema); + +/* Do the operation V() on the semaphore sema */ +t_error V(SemId sema); + +/* System calls concerning locks management */ +typedef unsigned long LockId; + +/* Create a lock. + Return an identifier */ +LockId LockCreate(char * debug_name); + +/* Destroy a lock. + Return a negative number if an error ocurred + during the destruction. */ +t_error LockDestroy(LockId id); + +/* Do the operation Acquire on the lock id. + Return a negative number if an error ocurred. */ +t_error LockAcquire(LockId id); + +/* Do the operation Release on the lock id. + Return a negative number if an error ocurred. +*/ +t_error LockRelease(LockId id); + +/* System calls concerning conditions variables. */ +typedef unsigned long CondId; + +/* Create a new condition variable */ +CondId CondCreate(char * debug_name); + +/* Destroy a condition variable. + Return a negative number if an error ocurred. +*/ +t_error CondDestroy(CondId id); + +/* Do the operation Wait on a condition variable. + Returns a negative number if an error ocurred. +*/ +t_error CondWait(CondId cond); + +/* Do the operation Signal on a condition variable (wake up only one thread). + Return a negative number if an error ocurred. +*/ +t_error CondSignal(CondId cond); + +/* Do the operation Signal on a condition variable (wake up all threads). + Return a negative number if an error ocurred. +*/ +t_error CondBroadcast(CondId cond); + +/******************************************************************/ +/* System calls concerning serial port and console */ + +/* Send the message on the serial communication link. + Returns the number of bytes successfully sent. +*/ +int TtySend(char *mess); + +/* Wait for a message comming from the serial communication link. + The length of the buffer where the bytes will be copied is given as a parameter. + Returns the number of characters actually received. +*/ +int TtyReceive(char *mess,int length); + +/* Map an opened file in memory. Size is the size to be mapped in bytes. +*/ +void *Mmap(OpenFileId f, int size); + +/* For debug purpose +*/ +void Debug(int param); + +#endif // IN_ASM +#endif // SYSCALL_H