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 FormatStyle: google
HeaderFilterRegex: '.*' HeaderFilterRegex: '.*'
WarningsAsErrors: 'readability*' #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' 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: CheckOptions:
readability-simplify-boolean-expr.IgnoreMacros: 'true' 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. 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. 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. 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. 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 // Struct used for loading (the address of) the Interrupt Descriptor Table into
// the IDT-Register // the IDT-Register
struct Register { struct Register {
uint16_t limit; // Address of the last valid byte (relative to base) uint16_t limit =
struct InterruptDescriptor* base; sizeof(idt) - 1; // Address of the last valid byte (relative to base)
explicit Register(uint8_t max = 255) { struct InterruptDescriptor* base = idt;
limit =
(max + static_cast<uint16_t>(1)) * sizeof(InterruptDescriptor) - 1;
base = idt;
}
} __attribute__((packed)); } __attribute__((packed));
static_assert(sizeof(InterruptDescriptor) == 16, static_assert(sizeof(InterruptDescriptor) == 16,
@@ -26,11 +22,11 @@ static_assert(alignof(decltype(idt)) % 8 == 0, "IDT must be 8 byte aligned!");
void load() { void load() {
// Create structure required for writing to idtr and load via lidt // 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)); asm volatile("lidt %0\n\t" ::"m"(idtr));
} }
void set(Core::Interrupt::Vector vector, InterruptDescriptor descriptor) { void set(Core::Interrupt::Vector vector, InterruptDescriptor descriptor) {
idt[(uint8_t)vector] = descriptor; idt[static_cast<uint8_t>(vector)] = descriptor;
} }
} // namespace IDT } // 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; } unsigned getModuleCount() { return multiboot_addr->mods.size; }
void *Memory::getStartAddress() const { void *Memory::getStartAddress() const { return reinterpret_cast<void *>(addr); }
return reinterpret_cast<void *>(static_cast<uintptr_t>(addr));
}
void *Memory::getEndAddress() const { void *Memory::getEndAddress() const {
uint64_t end = addr + len; 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; } bool Memory::isAvailable() const { return type == AVAILABLE; }

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,42 +3,13 @@
#include "../types.h" #include "../types.h"
struct Message { struct Message {
const size_t pid; ///< Sender Thread ID of message int sender; ///< Sender Thread ID of message
const uintptr_t sbuffer; ///< Send buffer uintptr_t sbuffer; ///< Send buffer
const size_t ssize; ///< Send buffer size size_t ssize; ///< Send buffer size
const uintptr_t rbuffer; ///< Receive buffer uintptr_t rbuffer; ///< Receive buffer
const size_t rsize; ///< Receive buffer size size_t rsize; ///< Receive buffer size
Message *queue_link = nullptr; ///< Next message in the message queue 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 { class Semaphore {
// Prevent copies and assignments // Prevent copies and assignments
Semaphore(const Semaphore&) = delete; Semaphore(const Semaphore&) = delete;
Semaphore(Semaphore&&) = delete;
Semaphore& operator=(const Semaphore&) = delete; Semaphore& operator=(const Semaphore&) = delete;
Queue<Thread> waiting; Queue<Thread> waiting;
public: public:
/*! \brief Constructor; initialized the counter with provided value `c` /*! \param init Initial counter value
* \param c Initial counter value
*/ */
explicit Semaphore(unsigned c = 0) : counter(c) {} explicit Semaphore(unsigned c = 0) : counter(c) {}
unsigned counter; unsigned counter;
bool used; bool used;
/*! \brief Wait for access to the critical area. /*! \brief Wait for access to the critical area.
* *
* Enter/decrement/wait operation: If the counter is greater than 0, then * Enter/decrement/wait operation: If the counter is greater than 0, then

View File

@@ -1,14 +1,12 @@
#include "syscall/handler.h" #include "syscall/handler.h"
#include "../arch/core.h" #include "arch/core_interrupt.h"
#include "../arch/core_interrupt.h" #include "arch/idt.h"
#include "../arch/gdt.h" #include "debug/kernelpanic.h"
#include "../arch/idt.h" #include "debug/output.h"
#include "../debug/kernelpanic.h" #include "interrupt/guard.h"
#include "../debug/output.h" #include "syscall/skeleton.h"
#include "../interrupt/guard.h" #include "syscall/stub.h"
#include "../syscall/skeleton.h"
#include "../syscall/stub.h"
#include "types.h" #include "types.h"
/*! \brief Interrupt based system call entry function /*! \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 * by the system call number -- allowing up to five parameters for the system
* call. * call.
* *
* \param sysnum identifier for the system call
* \param p1 first parameter * \param p1 first parameter
* \param p2 second parameter * \param p2 second parameter
* \param p3 third parameter * \param p3 third parameter
* \param p4 fourth parameter * \param p4 fourth parameter
* \param p5 fifth parameter * \param p5 fifth parameter
* \param sysnum identifier for the system call
* \param user pointer to the interrupt \ref context (for example to determine * \param user pointer to the interrupt \ref context (for example to determine
* instruction pointer) * instruction pointer)
* \return system call return value * \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_signal(Vault &vault, size_t id);
bool sem_wait(Vault &vault, size_t id); bool sem_wait(Vault &vault, size_t id);
void exit(Vault &vault); void exit(Vault &vault);
void kill(Vault &vault, size_t pid); void kill(Vault &vault, int pid);
} // namespace Skeleton } // namespace Skeleton
} // namespace Syscall } // namespace Syscall

View File

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

View File

@@ -7,8 +7,7 @@
#include "../interrupt/guard.h" #include "../interrupt/guard.h"
#include "debug/output.h" #include "debug/output.h"
// counter for ID static int idCounter = 1; // counter for task IDs
static size_t idCounter = 1;
void Thread::kickoff(uintptr_t param1, uintptr_t param2, uintptr_t param3) { void Thread::kickoff(uintptr_t param1, uintptr_t param2, uintptr_t param3) {
Thread *thread = reinterpret_cast<Thread *>(param1); 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_user[STACK_SIZE];
alignas(16) char reserved_stack_space_isr[STACK_SIZE]; alignas(16) char reserved_stack_space_isr[STACK_SIZE];
protected:
/*! \brief Context of the thread, used for saving and restoring the register /*! \brief Context of the thread, used for saving and restoring the register
* values when context switching. * values when context switching.
*/ */
@@ -59,6 +58,7 @@ class Thread {
* \param param1 Thread to be started * \param param1 Thread to be started
* \param param2 Second parameter (will be used later) * \param param2 Second parameter (will be used later)
* \param param3 Third 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); 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; void* operator new ( size_t count )noexcept;
/*! \brief Unique thread id */ /*! \brief Unique thread id */
const size_t id; const int id;
@@ -81,6 +81,13 @@ class Thread {
*/ */
volatile bool kill_flag; 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 /*! \brief Constructor
* Initializes the context using \ref prepareContext with the thread's * Initializes the context using \ref prepareContext with the thread's
* stack space. * stack space.

View File

@@ -4,7 +4,6 @@
void Application::action() { // NOLINT void Application::action() { // NOLINT
// Thread 1 may be an auxiliary thread // Thread 1 may be an auxiliary thread
//sys_test(1,2,3,4,5); //sys_test(1,2,3,4,5);
unsigned id = sys_getpid(); 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. # The standard target 'clean' removes the whole generated system, the object files, and the dependency files.
clean:: clean::
rm -rf "$(BUILDDIR)" rm -rf "$(BUILDDIR)"
rm -rf doc
# Target issuing a nested call to make generating a fully optimized systems without assertions. # Target issuing a nested call to make generating a fully optimized systems without assertions.
%-$(OPTTAG): %-$(OPTTAG):
@@ -120,6 +120,9 @@ clean::
%-$(VERBOSETAG): %-$(VERBOSETAG):
$(VERBOSE) $(MAKE) BUILDDIR="$(BUILDDIR)/$(VERBOSETAG)" ISODIR="$(ISODIR)/$(VERBOSETAG)" CXXFLAGS_OPT="-DVERBOSE" $* $(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 # Documentation
help:: help::
@@ -134,7 +137,8 @@ help::
"To get a verbose make output, clear VERBOSE, e.g. \e[3mmake VERBOSE=\e[0m.\n" \ "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" \ "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" \ "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 # Print warnings, if appropriate
@@ -150,4 +154,4 @@ endif
# Phony targets # 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) all: $(INITRD)
# recipe for building the final oostubs image # Rezepte fuer Apps (Unterverzeichnisse)
$(INITRD): $(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 $@" @echo "IMGBUILD $@"
@mkdir -p $(@D) @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; with pkgs;
[ [
# for all tasks and maintenance # for all tasks and maintenance
gnumake
gdb gdb
qemu_kvm qemu_kvm
nasm nasm
@@ -49,6 +50,8 @@
python3 python3
bear # make compile_commands.json bear # make compile_commands.json
clang-tools # for clangd and clang-format clang-tools # for clangd and clang-format
doxygen
graphviz # make doc
] ]
++ (lib.filter ( ++ (lib.filter (
pkg: lib.isDerivation pkg && pkg.pname or "" != "glibc" pkg: lib.isDerivation pkg && pkg.pname or "" != "glibc"