diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d1e5908 --- /dev/null +++ b/Makefile @@ -0,0 +1,373 @@ +#!/usr/bin/make -f +# --- +# Project: vxKernek - Vhex project kernel +# Author: yann.magnin@protonmail.com +# +# TODO: +# <> proper clean rule +# --- + + + + +#--- +# environment check +#--- + +ifeq ($(VXSDK_PREFIX_BUILD),) +$(error "The vxKernel sould be build using the vxSDK") +endif + + + + +#--- +# Build rules +#--- + +# Make selects the first rule set when you type "make" but, in our case, we are +# going to generate most of the rules. So, we set a placeholder to force the +# "all" rule to be the "first" rule +first: all + +# display the library version +version: + @echo "$(VXSDK_PKG_VERSION)" + +# Display helper +help: + @ echo 'Rules listing:' + @ echo '... all the default, if no target is provided' + @ echo '... clean remove build object' + @ echo '... fclean remove all generated object' + @ echo '... re same as `make fclean all`' + @ echo '... version display version' + @ echo '... install install the library' + @ echo '... uninstall uninstall the library' + +.PHONY: help version first + + + + +#--- +# Check build validity +#--- + +CONFIG := $(VXSDK_PREFIX_BUILD)/kernel.cfg +ifeq "$(wildcard $(CONFIG))" "" +$(error "config file $(CONFIG) does not exist (you should use `../configure`") +endif +include $(CONFIG) + + + + +#--- +# Variables definition +#--- + +# Many variables will be provided by the "kernel.cfg" file (generated during +# the configuration part). It will defines: +# <> BOARD - indicate the target board list +# <> {BOARD}-SRC-MODULE-PATH - list of all sources path for a specific board +# <> {BOARD}-TOOLCHAIN-PREFIX - toolchain prefix for a board (sh-elf, ...) +# <> FORMAT - format for the kernel (static, dynamic) + +# color definition, for swagg :D +red := \033[1;31m +green := \033[1;32m +blue := \033[1;34m +white := \033[1;37m +nocolor := \033[1;0m + + + +#--- +# Rules definition +# +# The objectif is to generate a building this build tree: +# +# /: +# |-- kernel.cfg +# |-- /: +# | |-- /: +# | | |-- obj/: +# | | | |-- main.o +# ... +# | | | `-- initialize.o +# | | `-- map +# | |-- .ld +# | `-- libvxkernel-.[so/a] +# `-- Makefile +#--- + +# Function that will generate a rule for one object file +# @param +# *1 - GCC name (with specific prefix if needed) +# *2 - CFLAGS list +# *3 - source file path +# *4 - build root directory path +# *5 - object file compilation rule list +# *6 - dependencies file compilation rule list +# *7 - unique id (used to generate unique variable name (workaround)) +define generate-compile-file-rule + +# generate the object file path +# @note +# <> This is alse the generated rule name +# <> step: +# 1) ../board/path/file.c -> ../board/path/file +# 2) .._board_path_file -> board_path_file +# 3) board_path_file.o -> {build path}/board_path_file.o +tname := $(strip $7) +obj-$(tname)-name := $$(basename $3) +obj-$(tname)-name := $$(subst /,_,$$(obj-$(tname)-name)) +obj-$(tname)-name := $$(patsubst %,$4/%.o,$$(obj-$(tname)-name)) + +# generate the building rule + +$$(obj-$(tname)-name): $3 + @ mkdir -p $$(dir $$@) +ifeq ($(CONFIG.VERBOSE),false) + @ printf "$(green)>$(nocolor) $(white)$$@$(nocolor)\n" + @ $1 $2 -o $$@ -c $$< -MMD -MT $$@ -MF $$@.d -MP +else + $1 $2 -o $$@ -c $$< -MMD -MT $$@ -MF $$@.d -MP +endif + +# register the build rules and dependencies file name + +$5 += $$(obj-$(tname)-name) +$6 += $$(obj-$(tname)-name).d + +endef + + + +# Function that will generate all rules for a specific board and format +# @params +# *1 - board name +# *2 - format name (static or dynamic) +# *3 - rules list variable name +define generate-target-lib + +# generate common information +# @note: +# Because we generate rule "on-the-fly" we need to create unique variable +# definition because make is not a "scoped" language. (therefore, when a +# variable is created, it will never be destroyed until the end of the script) +t-$1-$2-build := $(VXSDK_PREFIX_BUILD)/$1/$2 + +# generate compilation flags + +t-$1-$2-ar := $$(CONFIG.$1.TOOLCHAIN.PREFIX)ar +t-$1-$2-ld := $$(CONFIG.$1.TOOLCHAIN.PREFIX)ld +t-$1-$2-gcc := $$(CONFIG.$1.TOOLCHAIN.PREFIX)gcc +t-$1-$2-ldflags := $$(CONFIG.$1.TOOLCHAIN.LDFLAGS) +t-$1-$2-cflags := $$(CONFIG.$1.TOOLCHAIN.CFLAGS) +t-$1-$2-cflags += -fpic -ffreestanding -nostdlib -fstrict-volatile-bitfields -O1 +t-$1-$2-cflags += -Wa,--dsp + +# generate compiler information (used to find some library like libgcc.a) +t-$1-$2-gcc-base := $$(shell $$(CONFIG.$1.TOOLCHAIN.PREFIX)gcc --print-search-dirs | grep install | sed 's/install: //') +t-$1-$2-gcc-header := -I$$(t-$1-$2-gcc-base)/include -I$$(t-$1-$2-gcc-base)/include/openlibm +t-$1-$2-cflags += -Iinclude -I. -Isrc $$(t-$1-$2-gcc-header) +t-$1-$2-cflags += -Llib -L. -L$$(t-$1-$2-gcc-base) + +# generate file's sources list, based on the configuration step + +t-$1-$2-src := $$(foreach path,$$(CONFIG.$1.SRC-MODULE-PATH),\ + $$(wildcard $$(path)/*.c) \ + $$(wildcard $$(path)/*.S) \ + $$(wildcard $$(path)/*.s)) + +# generate format-specific flags + +t-$1-$2-exec := +ifeq ($2,static) +t-$1-$2-exec := $(VXSDK_PREFIX_BUILD)/libvhex-$1.a +endif +ifeq ($2,dynamic) +t-$1-$2-ldflags += -shared +t-$1-$2-ldflags += -soname=libvhex-$1-$(VXSDK_PKG_VERSION).so +t-$1-$2-ldflags += -Map=$$(t-$1-$2-build)/map +t-$1-$2-exec := $(VXSDK_PREFIX_BUILD)/libvhex-$1.so +t-$1-$2-src := $(wildcard fake/*.c) +endif + +# generate file's compilation rules and all object filename into an object +# list variable, this will be used by the `main` rule + +t-$1-$2-obj := +t-$1-$2-dep := +$$(foreach source,$$(t-$1-$2-src),$$(eval \ + $$(call generate-compile-file-rule,\ + $$(t-$1-$2-gcc),\ + $$(t-$1-$2-cflags),\ + $$(source),\ + $$(t-$1-$2-build),\ + t-$1-$2-obj,\ + t-$1-$2-dep,\ + $1-$2\ + ))\ +) + +# asset generation +t-$1-$2-asset := $(patsubst \ + $(VXSDK_ASSETS_SRC)/%,\ + $(VXSDK_ASSETS_BUILD)/$1/$2/%.o,\ + $(wildcard $(VXSDK_ASSETS_SRC)/*.c) \ +) + +$(VXSDK_ASSETS_BUILD)/$1/$2/%.o: $(VXSDK_ASSETS_SRC)/% +ifeq ($(CONFIG.VERBOSE),true) + @ mkdir -p $$(dir $$@) + sh-elf-vhex-gcc $$(t-$1-$2-cflags) -o $$@ -c $$< +else + @ mkdir -p $$(dir $$@) + @ printf "$(green)>$(nocolor) $(white)$@$(nocolor)\n" + @ sh-elf-vhex-gcc $$(t-$1-$2-cflags) -o $$@ -c $$< +endif + +# generate the "main" rule for this lib + +$$(t-$1-$2-exec): $$(t-$1-$2-obj) $$(t-$1-$2-asset) + @ mkdir -p $$(dir $$@) + @ echo "dep : $$(t-$1-$2-dep)" + @ printf "$(blue)Create the library $(red)$$@$(nocolor)\n" +ifeq ($2,dynamic) + $$(t-$1-$2-gcc) -shared $$(t-$1-$2-cflags) $$(t-$1-$2-gcc-libs) -o $$@ $$^ +else + $$(t-$1-$2-ar) crs $$@ $$^ +endif + +# register the "main" building rule for the lib + +$3 += $$(t-$1-$2-exec) + + +# import dependencies rules +-include $$(t-$1-$2-dep) +$$(t-$1-$2-dep): ; +.PRECIOUS: $$(t-$1-$2-dep) + +endef + + +# Generate the "main" rules list +target-lib-list := +$(foreach board,$(CONFIG.BOARD-LIST),\ + $(foreach format,$(CONFIG.FORMAT-LIST),$(eval\ + $(call generate-target-lib,$(board),$(format),target-lib-list)\ + ))\ +) + + + +#--- +# Build rules +#--- +all: $(target-lib-list) + +.PHONY: all + + + +#--- +# Generate installation rules +#--- +# Common rules generated for the installation of each libraries. +# Basically, it will generate -install and -uninstall rules +# @note: +# *1 - library pathname +# *2 - variable name (installation rules list) +# *3 - variable name (uninstallation rules list) +define generate-install-rule + +# Generate the installation rule +$(basename $(notdir $1))-install: + install -d $(VXSDK_PREFIX_LIB) + install $1 -m 644 $(VXSDK_PREFIX_LIB) + +# Generate the uninstallation rule +$(basename $(notdir $1))-uninstall: + rm -f $(VXSDK_PREFIX_LIB)/$(notdir $1) + +# Register generated rules into their appropriate list +$2 += $(basename $(notdir $1))-install +$3 += $(basename $(notdir $1))-uninstall +endef + +# Common rules generated for the installation of board-specific linker script +# @note: +# $1 - board name +# $2 - variable name (installation rules list) +# $3 - variable name (uninstallation rules list) +define generate-board-install-rule + +$1-install: + install -d $(VXSDK_PREFIX_LIB) + install board/$(strip $1)/*.ld -m 644 $(VXSDK_PREFIX_LIB) + +$1-uninstall: + rm -f $(VXSDK_PREFIX_LIB)/$(strip $1).ld + rm -f $(VXSDK_PREFIX_LIB)/$(strip $1)-dynamic.ld + +$2 += $1-install +$3 += $1-uninstall + +endef + +# Generate all installation/uninstallation rules + +target-install-rules := +target-uninstall-rules := +$(foreach target,$(target-lib-list),$(eval \ + $(call generate-install-rule, \ + $(target), \ + target-install-rules, \ + target-uninstall-rules \ + ) \ +)) +$(foreach board,$(CONFIG.BOARD-LIST),$(eval \ + $(call generate-board-install-rule, \ + $(board), \ + target-install-rules, \ + target-uninstall-rules \ + ) \ +)) + +# Generate the path where include directory will be installed. +target-install-hdr-dir := $(VXSDK_PREFIX_LIB)/include/ +ifeq ($(wildcard $(target-install-header-dir)vhex/.*),) + target-install-hdr-dir := $(target-install-hdr-dir)vhex +endif + + + +#--- +# Installation rules +#--- +install: $(target-install-rules) + mkdir -p $(dir $(target-install-hdr-dir)) + cp -r include/vhex $(target-install-hdr-dir) + +unsintall: $(target-uninstall_rules) + rm -rf $(VXSDK_PREFIX_LIB)/include/vhex + +.PHONY: install uninstall + + + +#--- +# cleaning rules +#--- +#clean: TODO: generate clean rule list + + +fclean: clean + rm -rf $(target-lib-list) +re: fclean all + +.PHONY: clean fclean re all diff --git a/configure b/configure new file mode 100755 index 0000000..a18408b --- /dev/null +++ b/configure @@ -0,0 +1,290 @@ +#! /usr/bin/python3 +import sys +import os.path +import os +import shutil +import toml + + +help_string = f""" +usage: config [options...] + +Configuration script for the Vhex unikernel. You should build out-of-tree by +creating a build directory and configuring from there. + +Options: + -h,--help + display this message + + --verbose + display more information during the compilation step + + -- + select the format of the library generation. You can use two format: + <> static - generate a static library + <> dynamic - generate a dynamic library + By default, only "dynamic" is used. + + --board[=BOARD,board[,...]] + select boards. If no board name is given, a list of all available board + will be printed. + + --prefix=PATH + installation path for all generated libraries +""".strip() + +#--- +# Internal functions +#--- + +def __list_dir(path): + ldir = [] + for dirent in os.listdir(path): + dpath = f'{path}/{dirent}' + if os.path.isdir(dpath): + ldir.append(dpath) + ldir += __list_dir(dpath) + return ldir + +def error(string, exitcode=84): + print(string, file=sys.stderr) + exit(exitcode) + +def warn(string): + print(string, file=sys.stderr) + +#--- +# part handlers +#--- + +def _board_check(file, board_list): + r"""Check board and architecture. + + This function will try to load the /board//board.toml file + which describe the required module to be build for the kernel image + generation. + + TODO: board.toml documentation + TODO: function documentation + + Args: + > [out] file (stream) - the 'kernel.cfg' files stream + > [in] board_list (list of str) - list of all board to support + """ + + # list available board and architecture + try: + boards = list(os.walk('board'))[0][1] + archs = list(os.walk('src/driver/mpu'))[0][1] + except Exception: + error(f"{prefix}: project not found, fix VXSDK_PREFIX_BUILD") + + # list all available board if no board target is provided + if not board_list: + print('board available:') + for board in boards: + path = f'board/{board}/board.toml' + if not os.path.exists(path): + warn(f'missing \'{board}\' board description') + continue + conf = None + with open(path) as f: + conf = toml.loads(f.read()) + if not ('meta' in conf) or not ('description' in conf['meta']): + print(f'<> {board}'.ljust(16) + 'No description available') + continue + print(f'<> {board}'.ljust(16) + f"{conf['meta']['description']}") + exit(0) + + # generate board list information + valid_board_list = [] + for board in board_list: + + # try to read the board description + if not (board in boards): + warn(f"board '{board}' does not exist") + continue + path = f'board/{board}/board.toml' + if not os.path.exists(path): + warn(f"board '{board}' does not have descriptor file") + continue + conf = None + with open(path) as f: + conf = toml.loads(f.read()) + + # check fundamental information + if not ('drivers' in conf) or not ('mpu' in conf['drivers']): + warn(f"board '{board}': missing MPU information") + continue + + # gues the architecture + arch = None + mpu = conf['drivers']['mpu'] + for _arch in archs: + if not os.path.exists(f'src/driver/mpu/{_arch}/{mpu}'): + continue + arch = _arch + break + if not arch: + warn(f"board '{board}': unreconized MPU '{mpu}'") + continue + + # Now generate the complete directory list that will be used by the + # Makefile as to find all source files + pathlist = [ + f'src', + f'board/{board}', + f'src/driver/mpu/{_arch}/{mpu}' + ] + pathlist += __list_dir(f'board/{board}') + pathlist += __list_dir(f'src/driver/mpu/{_arch}/{mpu}') + + # generate driver path list + for driver in conf['drivers']: + if driver == 'mpu': + continue + dpath = f'src/driver/{driver}' + if not os.path.exists(dpath): + warn(f"board '{board}': unreconized driver \'{driver}\', skipped") + continue + for t in conf['drivers'][driver].split(','): + tpath = dpath + '/' + t + if not os.path.exists(tpath): + warn(f"board '{board}': unreconized driver '{t}'") + continue + pathlist.append(tpath) + pathlist += __list_dir(tpath) + + # generate module path list + modules = [] + if ('config' in conf) and ('modules' in conf['config']): + modules = conf['config']['modules'] + for dirent in os.listdir(f'src'): + if (dirent in ['driver']): + continue + if modules and not (dirent in modules): + continue + pathlist.append(f'src/{dirent}') + pathlist += __list_dir(f'src/{dirent}') + + # fetch toolchain information + # TODO: polymorphisme + ldflags,cflags,prefix,libs = [],[],[],[] + if 'toolchain' in conf: + if 'prefix' in conf['toolchain']: + prefix = conf['toolchain']['prefix'] + if 'cflags' in conf['toolchain']: + cflags = conf['toolchain']['cflags'] + if 'libs' in conf['toolchain']: + libs = conf['toolchain']['libs'] + + # add board information in the make configuration file + confstr = 'CONFIG.' + board + file.write( + confstr + '.SRC-MODULE-PATH := ' + ' '.join(map(str, pathlist)) + '\n' + + confstr + '.TOOLCHAIN.LDFLAGS := ' + ' '.join(map(str, ldflags)) + '\n' + + confstr + '.TOOLCHAIN.CFLAGS := ' + ' '.join(map(str, cflags)) + '\n' + + confstr + '.TOOLCHAIN.PREFIX := ' + prefix + '\n' + + confstr + '.LIBS :=' + ' '.join(map(str, libs)) + '\n' + ) + + # append board list + valid_board_list.append(board) + + # write board list + file.write('CONFIG.BOARD-LIST := ' + ' '.join(valid_board_list) + '\n') + + + + +def _format_check(file, format_list): + file.write('CONFIG.FORMAT-LIST :=') + for _format in format_list: + if not (_format in ["static", "dynamic", "all"]): + print("%s: unreconized format '%s'" % (sys.argv[0], _format)) + exit(84) + if _format == 'all': + file.write(' dynamic static\n') + return + file.write(' ' + _format) + if not format_list: + file.write(' dynamic') + file.write('\n') + +def _prefix_check(file, prefix): + file.write('CONFIG.INSTALL-PREFIX :=' + prefix + '\n') + +def _verbose_check(file, verbose): + file.write(f"CONFIG.VERBOSE := {'true' if verbose else 'false'}\n") + +#--- +# Public part +#--- + +def parse_arguments(): + """ + The objectif of this function is to generate the "arguments object" with + all arguments passed throuth this script correctly isolated. + """ + args = { + 'board' : [], + 'format' : set(), + 'verbose': False, + 'prefix': '' + } + arg_name = [ + "-h", + "--help", + "--static", + "--dynamic", + "--verbose", + "--board", + "--prefix" + ] + for arg in sys.argv[1:]: + info = arg.split("=") + if (info[0] in arg_name) == False: + print("%s: unreconized option '%s'" % (sys.argv[0],info[0])) + print("Try '%s --help' for more information" % sys.argv[0]) + exit(84) + if info[0] == "--verbose": + args["verbose"] = True + continue + if info[0] == "--prefix": + args["prefix"] = info[1] + continue + if info[0] in ['--static', '--dynamic']: + args['format'].add(info[0][2:]) + continue + if len(info) > 1: + args[info[0][2:]] = info[1].split(",") + return args + + + +def main(): + """ main entry of the script """ + # early check + if ('-h' in sys.argv[1:]) or ('--help' in sys.argv[1:]): + print(help_string) + exit(0) + + # check if we are used by the vxSDK or not + try: + bprefix = os.environ['VXSDK_PREFIX_BUILD'] + except Exception: + error('The vxKernel cannot be built without using the vxSDK.') + + # check user arguments + args = parse_arguments() + + # creare the configuration file + file = open(f'{bprefix}/kernel.cfg', 'w') + + # handle all part of the configuration steps + _board_check(file, args['board']) + _format_check(file, args['format']) + _prefix_check(file, args['prefix']) + _verbose_check(file, args['verbose']) + +main() diff --git a/fake/display.c b/fake/display.c new file mode 100644 index 0000000..4b2a265 --- /dev/null +++ b/fake/display.c @@ -0,0 +1,140 @@ + +/* dpixel(): Change a pixel's color */ +void dpixel(int x, int y, int color) +{ + (void)x; + (void)y; + (void)color; +} + +/* dupdate(): Push the video RAM to the display driver */ +void dupdate(void) +{ + ; +} + +/* dclear(): Fill the screen with a single color */ +void dclear(int color) +{ + (void)color; +} + +/* dascii() : display one ASCII character */ +extern void dascii(int x, int y, int fg, int bg, int n) +{ + (void)x; + (void)y; + (void)fg; + (void)bg; + (void)n; +} + +/* dline(): Render a straight line */ +void dline(int x1, int y1, int x2, int y2, int color) +{ + (void)x1; + (void)y1; + (void)x2; + (void)y2; + (void)color; +} + +/* dtext() : display raw text */ +void dtext(int x, int y, int fg, char const * restrict const text) +{ + (void)x; + (void)y; + (void)fg; + (void)text; +} + +/* dprint() : display formated text */ +void dprint(int x, int y, int fg, char const * const text, ...) +{ + (void)x; + (void)y; + (void)fg; + (void)text; +} + +/* dsize(): Get the width and height of rendered text */ +void dsize(char const *str, int *w, int *h) +{ + (void)str; + (void)w; + (void)h; +} + +/* dnsize(): Get the width and height of rendered text for the n first char */ +void dnsize(char const *str, int n, int *w, int *h) +{ + (void)str; + (void)n; + (void)w; + (void)h; +} + +/* drect(): Fill a rectangle of the screen */ +void drect(int x1, int y1, int x2, int y2, int color) +{ + (void)x1; + (void)y1; + (void)x2; + (void)y2; + (void)color; +} + +/* dhline(): Draw horizontal line */ +void dhline(int x1, int x2, int y, int color) +{ + (void)x1; + (void)x2; + (void)y; + (void)color; +} + +/* dvline(): Draw vertical line */ +void dvline(int y1, int y2, int x, int color) +{ + (void)y1; + (void)y2; + (void)x; + (void)color; +} + +void dsubimage(int x, int y, void *image, int left, int top, + int width, int height, int flags) +{ + (void)x; + (void)y; + (void)image; + (void)left; + (void)top; + (void)width; + (void)height; + (void)flags; +} + +void *dfont(void const *font) +{ + (void)font; +} + +void dimage(int x, int y, void const *image) +{ + (void)x; + (void)y; + (void)image; +} + +void dprint_opt(int x, int y, int fg, int bg, int halign, int valign, + char const *format, ...) +{ + (void)x; + (void)y; + (void)fg; + (void)bg; + (void)halign; + (void)valign; + (void)format; +} diff --git a/fake/kmalloc.c b/fake/kmalloc.c new file mode 100644 index 0000000..70cfd86 --- /dev/null +++ b/fake/kmalloc.c @@ -0,0 +1,35 @@ +#include + +/* kmalloc(): Allocate memory in one of the available arenas + This function acts like malloc(). The second parameter specifies which arena + to allocate from; when NULL, all default arenas are considered. + + @size Size of requested block + @arena_name Name of arena to allocate in (can be NULL) + Returns address of allocated block, NULL on error. */ +void *kmalloc(size_t size, char const *arena_name) +{ + (void)size; + (void)arena_name; +} + +/* krealloc(): Reallocate memory + This function acts like realloc(). It only tries to reallocate the block in + the arena where it was previously allocated. Note that if NULL is returned, + the user needs to have a copy of the original address or the memory will + become unreachable. + + @ptr Existing allocated block + @size New requested size for the block + Returns address of reallocated block, NULL on error. */ +void *krealloc(void *ptr, size_t size) +{ + (void)ptr; + (void)size; +} + +/* kfree(): Free memory allocated with kalloc() */ +void kfree(void *ptr) +{ + (void)ptr; +} diff --git a/vxsdk.toml b/vxsdk.toml index b30f2ea..6f2f36c 100644 --- a/vxsdk.toml +++ b/vxsdk.toml @@ -1,6 +1,6 @@ [project] name = 'vxkernel' -version = '0.5.0' +type = 'lib' [build] configure = 'python3 ./configure --board=fxcg50'