Merge branch 'main' of gitlab.ibr.cs.tu-bs.de:vss/teaching/ws25/v_bsb2/stubsmi

This commit is contained in:
2026-02-06 23:09:43 +01:00
31 changed files with 402 additions and 3388 deletions

View File

@@ -1,6 +1,30 @@
FormatStyle: google
HeaderFilterRegex: '.*'
WarningsAsErrors: 'readability*'
Checks: 'readability*,google-readability-casting,google-explicit-constructor,bugprone*,-bugprone-narrowing-conversions,-bugprone-reserved-identifier,-readability-else-after-return,-readability-magic-numbers,-readability-identifier-length,-readability-braces-around-statements,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-pro-*,-cppcoreguidelines-avoid-do-while,-cppcoreguidelines-owning-memory'
#WarningsAsErrors: 'readability*'
Checks: >
google-readability-casting,
google-explicit-constructor,
readability*,
-readability-braces-around-statements,
-readability-else-after-return,
-readability-function-cognitive-complexity
-readability-identifier-length,
-readability-implicit-bool-conversion,
-readability-magic-numbers,
bugprone*,
-bugprone-easily-swappable-parameters,
-bugprone-narrowing-conversions,
-bugprone-reserved-identifier,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-*,
CheckOptions:
readability-simplify-boolean-expr.IgnoreMacros: 'true'
readability-identifier-length.IgnoredVariableNames: '^(g|id|me)$'
readability-identifier-length.IgnoredParameterNames: '^(id|ms|p[1-5])$'
readability-identifier-length.IgnoredLoopCounterNames: '.*'

View File

@@ -8,7 +8,6 @@ Diese Vorlage dient als Grundlage für Lehrveranstaltungen und darf nicht ohne v
Es ist erlaubt und wünschenswert, diese Vorlage als Inspiration für eigene Projekte zu verwenden, es wird allerdings erbeten, dass die Vorgabe nicht mit deiner Lösung veröffentlicht wird.
Wir, als Lehrende, möchten alle teilnehmenden Studierenden dazu ermutigen eine eigene Lösung zu erstellen; eine veröffentlichte Lösung ist ein Anreiz zum Abschreiben, den wir gerne vermeiden möchten.
This skeleton is provided as a foundation for educational purposes and therefore MUST NOT BE DISTRIBUTED OR PUBLISHED without prior, written consent of the copyright holders.
You are free to use this skeleton as inspiration for your projects, but, please, do not publish it along with your solution.
We, as lecturers, want to encourage every participating student to write a solution themself; a public solution is an allurement to copying we want to avoid.
We, as lecturers, want to encourage every participating student to write a solution on their own; a public solution is an allurement to copying we want to avoid.

View File

@@ -10,13 +10,9 @@ constinit struct InterruptDescriptor idt[256] = {};
// Struct used for loading (the address of) the Interrupt Descriptor Table into
// the IDT-Register
struct Register {
uint16_t limit; // Address of the last valid byte (relative to base)
struct InterruptDescriptor* base;
explicit Register(uint8_t max = 255) {
limit =
(max + static_cast<uint16_t>(1)) * sizeof(InterruptDescriptor) - 1;
base = idt;
}
uint16_t limit =
sizeof(idt) - 1; // Address of the last valid byte (relative to base)
struct InterruptDescriptor* base = idt;
} __attribute__((packed));
static_assert(sizeof(InterruptDescriptor) == 16,
@@ -26,11 +22,11 @@ static_assert(alignof(decltype(idt)) % 8 == 0, "IDT must be 8 byte aligned!");
void load() {
// Create structure required for writing to idtr and load via lidt
Register idtr(Core::Interrupt::VECTORS - 1);
Register idtr;
asm volatile("lidt %0\n\t" ::"m"(idtr));
}
void set(Core::Interrupt::Vector vector, InterruptDescriptor descriptor) {
idt[(uint8_t)vector] = descriptor;
idt[static_cast<uint8_t>(vector)] = descriptor;
}
} // namespace IDT

File diff suppressed because it is too large Load Diff

View File

@@ -96,13 +96,11 @@ Module *getModule(unsigned i) {
unsigned getModuleCount() { return multiboot_addr->mods.size; }
void *Memory::getStartAddress() const {
return reinterpret_cast<void *>(static_cast<uintptr_t>(addr));
}
void *Memory::getStartAddress() const { return reinterpret_cast<void *>(addr); }
void *Memory::getEndAddress() const {
uint64_t end = addr + len;
return reinterpret_cast<void *>(static_cast<uintptr_t>(end));
return reinterpret_cast<void *>(end);
}
bool Memory::isAvailable() const { return type == AVAILABLE; }

View File

@@ -1,7 +1,7 @@
#include "assert.h"
#include "../arch/core.h"
#include "output.h"
#include "./output.h"
[[noreturn]] void assertion_failed(const char* exp, const char* func,
const char* file, int line) {

View File

@@ -15,7 +15,7 @@
*/
#pragma once
#include "../types.h"
#include "types.h"
#ifndef STRINGIFY
/*! \def STRINGIFY(S)

View File

@@ -33,7 +33,7 @@ OutputStream* copyout = &copystream;
#include "./interrupt/guard.h"
#include "./boot/multiboot/data.h"
#include "./sync/semaphore.h"
#include "./thread/thread.h"
//Semaphore koutsem(1);
//TextStream kout(0, 80, 1, 17, true);

View File

@@ -29,7 +29,7 @@ assert_size(ErrorCode, 4);
uint64_t err) {
PageFault::ErrorCode error(err);
// Get the faulting address
uintptr_t virt;
uintptr_t virt = 0;
asm volatile("mov %%cr2, %0" : "=r"(virt));
DBG << "Page fault at " << hex << virt << dec << endl;

View File

@@ -25,18 +25,22 @@ class Queue {
/// Function pointer to the get_link function, called whenever the
/// next pointer array is referenced
const NextFunc get_link = default_get_link;
/// head points to the first element (the one returned on first dequeue).
/// Can be nullptr if the queue is empty.
/// `head` points to the first element (the one returned on first dequeue).
/// Can be `nullptr` if the queue is empty.
T* head = nullptr;
/// tail points to the last element (the one last added).
/// Is only valid if head != nullptr
/// `tail` points to the last element (the one last added).
/// Is only valid if `head != nullptr`.
T* tail = nullptr;
// Prevent copies and assignments
Queue(const Queue&) = delete;
Queue(Queue&&) = delete;
Queue& operator=(const Queue&) = delete;
Queue& operator=(Queue&&) = delete;
public:
~Queue() = default;
/*! \brief Minimal forward iterator
* You can use this iterator to iterate the queue like a normal STL
* container. It only supports forward iteration, since the queue is single
@@ -44,11 +48,11 @@ class Queue {
*/
class Iterator {
private:
Queue<T>& queue;
Queue<T>* queue;
T* current;
friend class Queue<T>;
Iterator(Queue<T>& queue, T* current)
: queue(queue), current(current) {}
: queue(&queue), current(current) {}
public:
Iterator operator+(unsigned num) {
@@ -57,21 +61,21 @@ class Queue {
}
T* temp = current;
while (num--) {
temp = queue.next(*temp);
temp = queue->next(*temp);
}
return Iterator(queue, temp);
return Iterator(*queue, temp);
}
// pre increment
Iterator& operator++() {
current = queue.next(*current);
current = queue->next(*current);
return *this;
}
// post increment
Iterator operator++(int) {
auto temp = Iterator(queue, current);
current = queue.next(*current);
current = queue->next(*current);
return temp;
}
@@ -84,8 +88,6 @@ class Queue {
bool operator!=(const Iterator& other) { return !(*this == other); }
};
constexpr Queue(Queue&&) = default;
/*! \brief Constructor
* \param[in] get_link A function pointer to the get_link, i.e. a function
* which returns a pointer to the next-pointer of an
@@ -97,11 +99,10 @@ class Queue {
"get_link function pointer must not be nullptr!");
}
/*! \brief Enqueues the provided item at the end of the queue. If the
*element is already contained in the queue, false will be returned
* \param[in] item element to be appended (enqueued).
* \return false if the element already was enqueued (and nothing was done)
* or not (and it is now enqueued, then true)
/*! \brief Enqueues the provided item at the end of the queue.
* \param[in] item Element to be appended (enqueued).
* \return `false` if the element already was already in this (or a
* different) queue (and nothing was done), `true` otherwise.
*/
bool enqueue(T& item) {
T** nextptr = get_link(item);
@@ -214,7 +215,7 @@ class Queue {
T* remove(T* that) {
if (!that) return nullptr;
T* cur = head;
T** next_link;
T** next_link = nullptr;
if (head == that) {
head = *get_link(*head);
@@ -258,14 +259,14 @@ class Queue {
* the pointer.
*/
template <class T>
OutputStream& operator<<(OutputStream& os, Queue<T>& queue) {
os << "{";
OutputStream& operator<<(OutputStream& out, Queue<T>& queue) {
out << "{";
for (typename Queue<T>::Iterator it = queue.begin(); it != queue.end();
++it) {
os << *it;
out << *it;
if (it + 1 != queue.end()) {
os << ", ";
out << ", ";
}
}
return os << "}";
return out << "}";
}

View File

@@ -3,42 +3,13 @@
#include "../types.h"
struct Message {
const size_t pid; ///< Sender Thread ID of message
int sender; ///< Sender Thread ID of message
const uintptr_t sbuffer; ///< Send buffer
const size_t ssize; ///< Send buffer size
uintptr_t sbuffer; ///< Send buffer
size_t ssize; ///< Send buffer size
const uintptr_t rbuffer; ///< Receive buffer
const size_t rsize; ///< Receive buffer size
uintptr_t rbuffer; ///< Receive buffer
size_t rsize; ///< Receive buffer size
Message *queue_link = nullptr; ///< Next message in the message queue
/*! \brief Constructor
* \param pid Sender Thread ID of message
* \param sbuffer Send buffer
* \param ssize Send buffer size
* \param rbuffer Receive buffer
* \param rsize Receive buffer size
*/
explicit Message(int pid, uintptr_t sbuffer = 0, size_t ssize = 0,
uintptr_t rbuffer = 0, size_t rsize = 0)
: pid(pid),
sbuffer(sbuffer),
ssize(ssize),
rbuffer(rbuffer),
rsize(rsize) {}
/*! \brief Helper to retrieve (and remove) a message from the queue
* \param pid Thread id of message
* \param queue Queue with message
* \return Pointer to message or nullptr
*/
static Message *dequeueByPID(size_t pid, Queue<Message> &queue) {
for (Message *m : queue) {
if (m->pid == pid) {
return queue.remove(m);
}
}
return nullptr;
}
};

View File

@@ -24,16 +24,16 @@ class Thread;
class Semaphore {
// Prevent copies and assignments
Semaphore(const Semaphore&) = delete;
Semaphore(Semaphore&&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
Queue<Thread> waiting;
public:
/*! \brief Constructor; initialized the counter with provided value `c`
* \param c Initial counter value
/*! \param init Initial counter value
*/
explicit Semaphore(unsigned c = 0) : counter(c) {}
unsigned counter;
bool used;
bool used;
/*! \brief Wait for access to the critical area.
*
* Enter/decrement/wait operation: If the counter is greater than 0, then

View File

@@ -1,14 +1,12 @@
#include "syscall/handler.h"
#include "../arch/core.h"
#include "../arch/core_interrupt.h"
#include "../arch/gdt.h"
#include "../arch/idt.h"
#include "../debug/kernelpanic.h"
#include "../debug/output.h"
#include "../interrupt/guard.h"
#include "../syscall/skeleton.h"
#include "../syscall/stub.h"
#include "arch/core_interrupt.h"
#include "arch/idt.h"
#include "debug/kernelpanic.h"
#include "debug/output.h"
#include "interrupt/guard.h"
#include "syscall/skeleton.h"
#include "syscall/stub.h"
#include "types.h"
/*! \brief Interrupt based system call entry function
@@ -26,12 +24,12 @@ namespace Syscall {
* by the system call number -- allowing up to five parameters for the system
* call.
*
* \param sysnum identifier for the system call
* \param p1 first parameter
* \param p2 second parameter
* \param p3 third parameter
* \param p4 fourth parameter
* \param p5 fifth parameter
* \param sysnum identifier for the system call
* \param user pointer to the interrupt \ref context (for example to determine
* instruction pointer)
* \return system call return value

View File

@@ -22,7 +22,7 @@ bool sem_destroy(Vault &vault, size_t id);
bool sem_signal(Vault &vault, size_t id);
bool sem_wait(Vault &vault, size_t id);
void exit(Vault &vault);
void kill(Vault &vault, size_t pid);
void kill(Vault &vault, int pid);
} // namespace Skeleton
} // namespace Syscall

View File

@@ -32,15 +32,13 @@ void Scheduler::resume(bool ready) {
Thread *me = dispatcher.active();
assert(me != nullptr && "Pointer to active thread should never be nullptr");
if (true) {
// Be careful, never put the idle thread into the ready list
bool is_idle_thread = static_cast<Thread *>(&idleThread) == me;
// Be careful, never put the idle thread into the ready list
bool is_idle_thread = static_cast<Thread *>(&idleThread) == me;
if (ready && readylist.is_empty()) {
return;
} else if (!is_idle_thread) {
if (ready) readylist.enqueue(*me);
}
if (ready && readylist.is_empty()) {
return;
} else if (!is_idle_thread) {
if (ready) readylist.enqueue(*me);
}
dispatcher.dispatch(getNext());
@@ -56,6 +54,8 @@ void Scheduler::kill(Thread *that) {
if (dispatcher.active() == that) {
exit();
}
readylist.remove(that);
}
bool Scheduler::isEmpty() const { return readylist.is_empty(); }

View File

@@ -7,8 +7,7 @@
#include "../interrupt/guard.h"
#include "debug/output.h"
// counter for ID
static size_t idCounter = 1;
static int idCounter = 1; // counter for task IDs
void Thread::kickoff(uintptr_t param1, uintptr_t param2, uintptr_t param3) {
Thread *thread = reinterpret_cast<Thread *>(param1);

View File

@@ -38,7 +38,6 @@ class Thread {
// alignas(16) char reserved_stack_space_user[STACK_SIZE];
alignas(16) char reserved_stack_space_isr[STACK_SIZE];
protected:
/*! \brief Context of the thread, used for saving and restoring the register
* values when context switching.
*/
@@ -59,6 +58,7 @@ class Thread {
* \param param1 Thread to be started
* \param param2 Second parameter (will be used later)
* \param param3 Third parameter (will be used later)
*
*/
static void kickoff(uintptr_t param1, uintptr_t param2, uintptr_t param3);
@@ -72,7 +72,7 @@ class Thread {
void* operator new ( size_t count )noexcept;
/*! \brief Unique thread id */
const size_t id;
const int id;
@@ -81,6 +81,13 @@ class Thread {
*/
volatile bool kill_flag;
// Naively moving or copying esp. the (user) stack of a thread would be a
// bad idea:
Thread(const Thread&) = delete;
Thread(Thread&&) = delete;
Thread& operator=(const Thread&) = delete;
Thread& operator=(Thread&&) = delete;
/*! \brief Constructor
* Initializes the context using \ref prepareContext with the thread's
* stack space.

View File

@@ -4,7 +4,6 @@
void Application::action() { // NOLINT
// Thread 1 may be an auxiliary thread
//sys_test(1,2,3,4,5);
unsigned id = sys_getpid();

View File

@@ -1 +0,0 @@
#include "device/textstream.h"

74
libsys/assert.h Normal file
View File

@@ -0,0 +1,74 @@
// vim: set noet ts=4 sw=4:
/*! \file
* \brief Contains several macros usable for making assertions
*
* Depending on the type of assertion (either static or at runtime), a failing
* assertion will trigger an error. For static assertion, this error will be
* shown at compile time and abort compilation. Runtime assertions will trigger
* a message containing details about the error occurred and will make the CPU
* die.
*/
/*!
* \defgroup debug Debugging functions
*/
#pragma once
#include "types.h"
#ifndef STRINGIFY
/*! \def STRINGIFY(S)
* \brief Converts a macro parameter into a string
* \ingroup debug
* \param S Expression to be converted
* \return stringified version of S
*/
#define STRINGIFY(S) #S
#endif
/*! \def assert_size(TYPE, SIZE)
* \brief Statically ensure (at compile time) that a data type (or variable)
* has the expected size.
*
* \ingroup debug
* \param TYPE The type to be checked
* \param SIZE Expected size in bytes
*/
#define assert_size(TYPE, SIZE) \
static_assert(sizeof(TYPE) == (SIZE), "Wrong size for " STRINGIFY(TYPE))
/*! \def assert(EXP)
* \brief Ensure (at execution time) an expression evaluates to `true`, print
* an error message and stop the CPU otherwise.
*
* \ingroup debug
* \param EXP The expression to be checked
*/
#ifdef NDEBUG
#define assert(EXP) ((void)0)
#else
#define assert(EXP) \
do { \
if (__builtin_expect(!(EXP), 0)) { \
assertion_failed(STRINGIFY(EXP), __func__, __FILE__, __LINE__); \
} \
} while (false)
/*! \brief Handles a failed assertion
*
* This function will print a message containing further information about the
* failed assertion and stops the current CPU permanently.
*
* \note This function should never be called directly, but only via the macro
* `assert`.
*
*
* \param exp Expression that did not hold
* \param func Name of the function in which the assertion failed
* \param file Name of the file in which the assertion failed
* \param line Line in which the assertion failed
*/
[[noreturn]] void assertion_failed(const char* exp, const char* func,
const char* file, int line);
#endif

View File

@@ -1,24 +0,0 @@
#pragma once
#include "outputstream.h"
#include "stub.h"
class IOStream : public OutputStream {
private:
IOStream(IOStream &copy); // no copy
int fd;
public:
explicit IOStream(int sysfd = 0) : fd(sysfd) {}
~IOStream() {
if (pos > 0) {
sys_write(fd, buffer, pos);
}
}
void flush() override {
sys_write(fd, buffer, pos);
pos = 0;
}
};

View File

@@ -1,68 +0,0 @@
#include "rand.h"
// Mersenne Twister (32 bit pseudorandom number generator)
// https://en.wikipedia.org/wiki/Mersenne_Twister
static const uint32_t N = 624;
static const uint32_t M = 397;
static const uint32_t R = 31;
static const uint32_t A = 0x9908B0DF;
// initialization multiplier
static const uint32_t F = 1812433253;
static const uint32_t U = 11;
static const uint32_t S = 7;
static const uint32_t B = 0x9D2C5680;
static const uint32_t T = 15;
static const uint32_t C = 0xEFC60000;
static const uint32_t L = 18;
static const uint32_t MASK_LOWER = (1ULL << R) - 1;
static const uint32_t MASK_UPPER = (1ULL << R);
static uint32_t mt[N];
static uint16_t index;
void srand(const uint32_t seed) {
mt[0] = seed;
for (uint32_t i = 1; i < N; i++) {
mt[i] = (F * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
}
index = N;
}
uint32_t rand() {
uint16_t v = index;
if (index >= N) {
// Twist
for (uint32_t i = 0; i < N; i++) {
uint32_t x = (mt[i] & MASK_UPPER) + (mt[(i + 1) % N] & MASK_LOWER);
uint32_t xA = x >> 1;
if ((x & 0x1) != 0) {
xA ^= A;
}
mt[i] = mt[(i + M) % N] ^ xA;
}
v = index = 0;
}
uint32_t y = mt[v];
index = v + 1;
y ^= (y >> U);
y ^= (y << S) & B;
y ^= (y << T) & C;
y ^= (y >> L);
return y;
}

View File

@@ -1,9 +0,0 @@
#pragma once
#include "types.h"
const uint32_t RAND_MAX = UINT32_MAX;
void srand(uint32_t seed);
uint32_t rand();

0
nix-develop.sh Normal file → Executable file
View File

View File

@@ -99,7 +99,7 @@ $(BUILDDIR)/%.asm.o : %.asm $(MAKEFILE_LIST)
# The standard target 'clean' removes the whole generated system, the object files, and the dependency files.
clean::
rm -rf "$(BUILDDIR)"
rm -rf doc
# Target issuing a nested call to make generating a fully optimized systems without assertions.
%-$(OPTTAG):
@@ -120,6 +120,9 @@ clean::
%-$(VERBOSETAG):
$(VERBOSE) $(MAKE) BUILDDIR="$(BUILDDIR)/$(VERBOSETAG)" ISODIR="$(ISODIR)/$(VERBOSETAG)" CXXFLAGS_OPT="-DVERBOSE" $*
# Build the doxygen docs
doc:
PROJECT_NAME=StuBS doxygen ../doxygen/doxy-sra.cfg
# Documentation
help::
@@ -134,7 +137,8 @@ help::
"To get a verbose make output, clear VERBOSE, e.g. \e[3mmake VERBOSE=\e[0m.\n" \
"The following targets are available (each target can be suffixed by \e[3m-noopt\e[0m\n" \
"and \e[3m-verbose\e[0m):\n\n" \
" \e[3mall\e[0m Builds $(PROJECT), generating an ELF binary\n\n"
" \e[3mall\e[0m Builds $(PROJECT), generating an ELF binary\n\n" \
" \e[3mdoc\e[0m Builds the documention (doc/html/index.html)\n" \
# Print warnings, if appropriate
@@ -150,4 +154,4 @@ endif
# Phony targets
.PHONY: clean help
.PHONY: clean doc help

0
tools/cpplint.py vendored Normal file → Executable file
View File

View File

@@ -13,8 +13,33 @@ INITRD = $(BUILDDIR)/initrd.img
all: $(INITRD)
# recipe for building the final oostubs image
$(INITRD):
# Rezepte fuer Apps (Unterverzeichnisse)
$(APPS): $(BUILDDIR)/init.o
$(VERBOSE) $(MAKE) -C $@ -f ../Makefile.app
# recipe for compiling imgbuilder
$(IMGBUILDER): imgbuilder.cc $(MAKEFILE_LIST)
@echo "CXX $@"
@mkdir -p $(@D)
$(VERBOSE) $(CXX) -std=c++23 -o $@ $<
.ONESHELL:
# recipe for building the initrd image
$(INITRD): $(APPS) $(IMGBUILDER)
@echo "IMGBUILD $@"
@mkdir -p $(@D)
@touch $@
if [ -z "$(APPS)" ] ; then touch $@ ; exit 0 ; fi
$(VERBOSE) $(IMGBUILDER) $(addsuffix $(BUILDDIR)/app.img, $(APPS)) > $@
lint::
$(VERBOSE) $(foreach APP,$(APPS),$(MAKE) -C $(APP) -f ../Makefile.app lint && ) true
tidy::
$(VERBOSE) $(foreach APP,$(APPS),$(MAKE) -C $(APP) -f ../Makefile.app tidy && ) true
# clean all apps additionally to default clean recipe from common.mk
clean::
$(VERBOSE) $(foreach APP,$(APPS),$(MAKE) -C $(APP) -f ../Makefile.app clean && ) true
.PHONY: all clean $(APPS)

53
user/Makefile.app Normal file
View File

@@ -0,0 +1,53 @@
# --------------------------------------------------------------------------
# Linker Script
LINKER_SCRIPT = ../sections.ld
# Files to be compiled (important: defined prior to importing common.mk!)
CC_SOURCES = $(shell find * -name "*.cc" -a ! -name '.*' )
ASM_SOURCES = $(shell find * -name "*.asm" -a ! -name '.*' )
CXXFLAGS_INCLUDE = -I../../libsys -I../../kernel
CXXFLAGS_DEFAULT += $(CXXFLAGS_INCLUDE)
# -----------------------------------------------------------------------------
# Globale Variablen und Standard-Rezepte einbinden
include ../../tools/common.mk
# --------------------------------------------------------------------------
# Default target
.DEFAULT_GOAL = all
all: $(BUILDDIR)/app.img
# -----------------------------------------------------------------------------
# Parameters & flags:
CXXFLAGS += $(CXXFLAGS_INCLUDE)
# Enable 16-byte compare-and-exchange instruction for debugging purposes (stack alignment)
CXXFLAGS += -mcx16
LDLIBS += -L../../libsys/$(BUILDDIR) -lsys
INITOBJ = ../$(BUILDDIR)/init.o
# --------------------------------------------------------------------------
# Linked applications
$(BUILDDIR)/app: $(INITOBJ) $(ASM_OBJECTS) $(CC_OBJECTS) $(LINKER_SCRIPT) $(MAKEFILE_LIST)
@echo "LD $@"
@mkdir -p $(@D)
$(VERBOSE) $(CXX) $(CXXFLAGS) -Wl,-T $(LINKER_SCRIPT) -o $@ $(LDFLAGS) $(INITOBJ) $(ASM_OBJECTS) $(CC_OBJECTS) $(LDLIBS)
# --------------------------------------------------------------------------
# 'app.img' contains all section required for loading an application (flat binary)
$(BUILDDIR)/app.img: $(BUILDDIR)/app
@echo "OBJCOPY $@"
@if ! nm $< | grep "4000000 T start" >/dev/null 2>&1 ; then echo "Symbol 'start' is not first address" ; exit 1 ; fi
@mkdir -p $(@D)
$(VERBOSE) objcopy -O binary --set-section-flags .bss=alloc,load,contents $< $@
# --------------------------------------------------------------------------
# Include dependency files (generated gcc -MM)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEP_FILES)
endif

67
user/imgbuilder.cc Normal file
View File

@@ -0,0 +1,67 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <cstdint>
#include <format>
#include <fstream>
#include <iostream>
#include <print>
#include <vector>
constexpr std::streamsize block_size = 4096;
const uint8_t zeros[block_size] = {};
template <class... Args>
static void die(std::format_string<Args...> fmt, Args &&...args) {
auto msg = std::format(fmt, std::forward<Args>(args)...);
perror(msg.c_str());
exit(EXIT_FAILURE);
}
static void writeBuffer(const void *data, std::streamsize size) {
std::cout.write(reinterpret_cast<const char *>(data), size);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::println(stderr, "usage: {} <list of flat binaries>\n", argv[0]);
return EXIT_FAILURE;
} else if (argc > 1000) {
std::println(stderr, "Only a maximum of 1000 apps are supported\n");
return EXIT_FAILURE;
}
// HEADER: Number of apps + size of each app in bytes
std::array<uint32_t, block_size / sizeof(uint32_t)> sizes{0};
sizes[0] = static_cast<uint32_t>(argc - 1);
for (size_t i = 1; i < argc; ++i) {
struct stat file;
if (stat(argv[i], &file) != 0) die("stat");
if (file.st_size >= UINT32_MAX) { // 4 GB Limit
errno = EFBIG;
die("stat");
}
sizes[i] = file.st_size;
}
writeBuffer(sizes.data(), block_size);
// DATA: Each App
for (size_t i = 1; i < argc; ++i) {
std::vector<char> buf(sizes[i]);
std::ifstream input(argv[i], std::ios::binary);
if (!input) die("fopen");
input.read(buf.data(), sizes[i]);
writeBuffer(buf.data(), sizes[i]);
// Fill to block size, if required
if (sizes[i] % block_size != 0)
writeBuffer(zeros, block_size - (sizes[i] % block_size));
}
std::cout.flush();
return EXIT_SUCCESS;
}

73
user/init.cc Normal file
View File

@@ -0,0 +1,73 @@
#include <../libsys/stub.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void _init();
extern void _fini();
extern void main();
extern void (*__preinit_array_start[])();
extern void (*__preinit_array_end[])();
extern void (*__init_array_start[])();
extern void (*__init_array_end[])();
extern void (*__fini_array_start[])();
extern void (*__fini_array_end[])();
[[gnu::used]] [[noreturn]] void start() {
const unsigned int preinit_size =
__preinit_array_end - __preinit_array_start;
for (unsigned int i = 0; i != preinit_size; ++i)
(*__preinit_array_start[i])();
const unsigned int init_size = __init_array_end - __init_array_start;
for (unsigned int i = 0; i != init_size; ++i) (*__init_array_start[i])();
main();
const unsigned int fini_size = __fini_array_end - __fini_array_start;
for (unsigned int i = 0; i != fini_size; ++i) (*__fini_array_start[i])();
sys_exit();
while (true) {
}
}
// additional C++ stuff
void __cxa_pure_virtual() {
// pure virtual error
}
// For libraries
int __cxa_atexit(void (*func)(void *), void *arg, void *dso_handle) {
(void)func;
(void)arg;
(void)dso_handle;
return 0;
}
#ifdef __cplusplus
}
#endif
void *operator new([[maybe_unused]] __SIZE_TYPE__ n) {
return reinterpret_cast<void *>(0xdeadbeef);
}
void *operator new[]([[maybe_unused]] __SIZE_TYPE__ n) {
return reinterpret_cast<void *>(0xdeadbeef);
}
void operator delete[](void *ptr) { operator delete(ptr); }
void operator delete[](void *ptr, [[maybe_unused]] __SIZE_TYPE__ size) {
operator delete(ptr);
}
void operator delete([[maybe_unused]] void *ptr) {}
void operator delete(void *ptr, [[maybe_unused]] __SIZE_TYPE__ size) {
operator delete(ptr);
}

View File

@@ -38,6 +38,7 @@
with pkgs;
[
# for all tasks and maintenance
gnumake
gdb
qemu_kvm
nasm
@@ -49,6 +50,8 @@
python3
bear # make compile_commands.json
clang-tools # for clangd and clang-format
doxygen
graphviz # make doc
]
++ (lib.filter (
pkg: lib.isDerivation pkg && pkg.pname or "" != "glibc"