Skip to content

Commit

Permalink
turned everything upside down chasing down and fixing critical core b…
Browse files Browse the repository at this point in the history
…ugs in the kernel vector class and the kernel heap allocation routine, finally got multicore unit test workinggit status!
  • Loading branch information
FlareCoding committed Sep 11, 2024
1 parent 9ba3efd commit 0f28785
Show file tree
Hide file tree
Showing 15 changed files with 335 additions and 313 deletions.
Binary file modified efi/OVMF_VARS.fd
Binary file not shown.
4 changes: 2 additions & 2 deletions kernel/src/arch/x86/ap_startup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void initializeApCores() {
auto& acpiController = AcpiController::get();
Madt* apicTable = acpiController.getApicTable();

auto& sched = RRScheduler::get();
auto& sched = Scheduler::get();

RUN_ELEVATED({
// Copy the necessary resources and data to the lower physical address
Expand All @@ -112,7 +112,7 @@ void initializeApCores() {
// *Note* starting at 1 because BSP_ID is 0
for (size_t cpu = 1; cpu < apicTable->getCpuCount(); cpu++) {
// Create a scheduler run queue for each detected core
sched.registerCpuCore(cpu);
sched.registerCoreForScheduling(cpu);

// Get the APIC ID of the core
uint8_t apicid = apicTable->getLocalApicDescriptor(cpu).apicId;
Expand Down
8 changes: 8 additions & 0 deletions kernel/src/core/kstring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ uint64_t strlen(const char *str) {
return 0;
}

int strcmp(const char *s1, const char *s2) {
while (*s1 && (*s1 == *s2)) {
s1++;
s2++;
}
return *(unsigned char *)s1 - *(unsigned char *)s2;
}

namespace kstl {
string::string() : m_isUsingSSOBuffer(true) {
m_ssoBuffer[0] = '\0';
Expand Down
1 change: 1 addition & 0 deletions kernel/src/core/kstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int ftoa(
);

uint64_t strlen(const char *str);
int strcmp(const char *s1, const char *s2);

namespace kstl {
class string {
Expand Down
6 changes: 6 additions & 0 deletions kernel/src/core/kvector.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class vector {

size_t find(const T& value) const;

T* data() const;
size_t size() const;
size_t capacity() const;
bool empty() const;
Expand Down Expand Up @@ -293,6 +294,11 @@ size_t vector<T>::find(const T& value) const {
return vector<T>::npos;
}

template <typename T>
T* vector<T>::data() const {
return m_data;
}

template <typename T>
size_t vector<T>::size() const {
return m_size;
Expand Down
2 changes: 1 addition & 1 deletion kernel/src/entry/kernel_entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void _kuser_entry() {
}

// Initialize the scheduler
auto& sched = RRScheduler::get();
auto& sched = Scheduler::get();
sched.init();

// Bring up all available processor cores
Expand Down
195 changes: 195 additions & 0 deletions kernel/src/entry/tests/kernel_stl.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include "kernel_unit_tests.h"
#include <core/kvector.h>

struct TestStruct {
int id;
const char* name;

bool operator==(const TestStruct& other) const {
return id == other.id && strcmp(name, other.name) == 0;
}
};

DECLARE_UNIT_TEST("Vector Initialization", kvectorInitUnitTest) {
kstl::vector<int> vec;

ASSERT_EQ(vec.size(), 0, "Vector size should be 0 after initialization");
ASSERT_EQ(vec.capacity(), 0, "Vector capacity should be 0 after initialization");
ASSERT_TRUE(vec.empty(), "Vector should be empty after initialization");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector PushBack", kvectorPushBackUnitTest) {
kstl::vector<int> vec;
vec.pushBack(10);

ASSERT_TRUE(vec.data() != nullptr, "Vector size should be 1 after pushBack");
ASSERT_EQ(vec.size(), 1, "Vector size should be 1 after pushBack");
ASSERT_EQ(vec[0], 10, "Element at index 0 should be 10");

vec.pushBack(20);
ASSERT_EQ(vec.size(), 2, "Vector size should be 2 after pushBack");
ASSERT_EQ(vec[1], 20, "Element at index 1 should be 20");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Capacity Growth", kvectorCapacityGrowthUnitTest) {
kstl::vector<int> vec;
size_t initialCapacity = vec.capacity();

for (int i = 0; i < 100; ++i) {
vec.pushBack(i);
}

ASSERT_EQ(vec.size(), 100, "Vector size should be 100 after 100 pushBack operations");
ASSERT_TRUE(vec.capacity() > initialCapacity, "Vector capacity should grow after multiple insertions");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Insert", kvectorInsertUnitTest) {
kstl::vector<int> vec;
vec.pushBack(10);
vec.pushBack(30);

vec.insert(1, 20);

ASSERT_EQ(vec.size(), 3, "Vector size should be 3 after insert");
ASSERT_EQ(vec[1], 20, "Element at index 1 should be 20 after insert");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector PopBack", kvectorPopBackUnitTest) {
kstl::vector<int> vec;
vec.pushBack(10);
vec.pushBack(20);

vec.popBack();
ASSERT_EQ(vec.size(), 1, "Vector size should be 1 after popBack");
ASSERT_EQ(vec[0], 10, "Element at index 0 should still be 10 after popBack");

vec.popBack();
ASSERT_EQ(vec.size(), 0, "Vector size should be 0 after second popBack");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Erase", kvectorEraseUnitTest) {
kstl::vector<int> vec;
vec.pushBack(10);
vec.pushBack(20);
vec.pushBack(30);

vec.erase(1); // Remove the element at index 1 (20)

ASSERT_EQ(vec.size(), 2, "Vector size should be 2 after erase");
ASSERT_EQ(vec[0], 10, "Element at index 0 should still be 10");
ASSERT_EQ(vec[1], 30, "Element at index 1 should now be 30");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Clear", kvectorClearUnitTest) {
kstl::vector<int> vec;
vec.pushBack(10);
vec.pushBack(20);

vec.clear();

ASSERT_EQ(vec.size(), 0, "Vector size should be 0 after clear");
ASSERT_TRUE(vec.empty(), "Vector should be empty after clear");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Find", kvectorFindUnitTest) {
kstl::vector<int> vec;
vec.pushBack(10);
vec.pushBack(20);
vec.pushBack(30);

size_t index = vec.find(20);
ASSERT_TRUE(index != kstl::vector<int>::npos, "Find should return a valid index");
ASSERT_EQ(index, 1, "Element 20 should be at index 1");

size_t notFoundIndex = vec.find(40);
ASSERT_EQ(notFoundIndex, kstl::vector<int>::npos, "Find should return npos when element is not found");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Copy Semantics", kvectorCopySemanticsUnitTest) {
kstl::vector<int> vec1;
vec1.pushBack(10);
vec1.pushBack(20);

kstl::vector<int> vec2 = vec1;

ASSERT_EQ(vec2.size(), vec1.size(), "Copied vector should have the same size");
ASSERT_TRUE(vec2[0] == vec1[0] && vec2[1] == vec1[1], "Copied vector should have the same values");

vec2.pushBack(30);
ASSERT_TRUE(vec1.size() != vec2.size(), "Original vector size should not be affected by copy");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector Size and Capacity Consistency", kvectorSizeCapacityConsistencyUnitTest) {
kstl::vector<int> vec;

for (int i = 0; i < 50; ++i) {
vec.pushBack(i);
ASSERT_TRUE(vec.size() <= vec.capacity(), "Vector size should never be greater than its capacity");
}

vec.insert(25, 100);
ASSERT_TRUE(vec.size() <= vec.capacity(), "After insertion, size should never exceed capacity");

vec.erase(10);
ASSERT_TRUE(vec.size() <= vec.capacity(), "After erase, size should never exceed capacity");

vec.clear();
ASSERT_TRUE(vec.size() == 0, "After clear, vector size should be 0");
ASSERT_TRUE(vec.capacity() > 0, "Capacity should not be 0 after clearing");

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Vector with Custom Struct", kvectorCustomStructUnitTest) {
kstl::vector<TestStruct> vec;

TestStruct obj1 = {1, "Object 1"};
TestStruct obj2 = {2, "Object 2"};
TestStruct obj3 = {3, "Object 3"};

// Test pushBack
vec.pushBack(obj1);
vec.pushBack(obj2);
vec.pushBack(obj3);

ASSERT_EQ(vec.size(), 3, "Vector size should be 3 after adding three elements");
ASSERT_EQ(vec[0].id, obj1.id, "First element ID should match obj1");
ASSERT_EQ(vec[1].id, obj2.id, "Second element ID should match obj2");
ASSERT_EQ(vec[2].id, obj3.id, "Third element ID should match obj3");

// Test erase
vec.erase(1); // Erase obj2 (index 1)
ASSERT_EQ(vec.size(), 2, "Vector size should be 2 after erase");
ASSERT_EQ(vec[0].id, obj1.id, "First element should still be obj1");
ASSERT_EQ(vec[1].id, obj3.id, "Second element should now be obj3 after erase");

// Test popBack
vec.popBack();
ASSERT_EQ(vec.size(), 1, "Vector size should be 1 after popBack");
ASSERT_EQ(vec[0].id, obj1.id, "First element should still be obj1 after popBack");

// Test clear
vec.clear();
ASSERT_EQ(vec.size(), 0, "Vector size should be 0 after clear");
ASSERT_TRUE(vec.capacity() > 0, "Capacity should be greater than 0 after clearing");

return UNIT_TEST_SUCCESS;
}
1 change: 1 addition & 0 deletions kernel/src/entry/tests/kernel_unit_tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define KERNEL_UNIT_TESTS_H
#include <ktypes.h>
#include <kprint.h>
#include <core/kstring.h>

// Define return values for test outcomes
#define UNIT_TEST_SUCCESS 0
Expand Down
20 changes: 20 additions & 0 deletions kernel/src/entry/tests/memory_allocation.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,26 @@ DECLARE_UNIT_TEST("Heap Allocate - Heavy", kheapHeavyAllocateUnitTest) {
return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Heap Reallocate Test", kheapReallocateUnitTest) {
// Allocate an initial block of memory
void* ptr = kmalloc(ALLOC_SIZE);
ASSERT_TRUE_CRITICAL(ptr, "Initial allocated memory pointer was null");

// Reallocate the memory to a larger size
void* new_ptr = krealloc(ptr, ALLOC_SIZE * 2);
ASSERT_TRUE_CRITICAL(new_ptr, "Reallocated memory pointer was null");

// Ensure the new pointer is valid and that the data wasn't corrupted (if possible to check)
ASSERT_TRUE(ptr != new_ptr, "Reallocated memory pointer didn't change as expected when resizing");

kuPrint(UNIT_TEST "Reallocated memory from %llu bytes to %llu bytes: Success\n", (uint64_t)ALLOC_SIZE, (uint64_t)(ALLOC_SIZE * 2));

// Free the reallocated memory
kfree(new_ptr);

return UNIT_TEST_SUCCESS;
}

DECLARE_UNIT_TEST("Paging - Allocate Single Page", pagingAllocateUnitTest) {
auto& allocator = paging::getGlobalPageFrameAllocator();
size_t usedMemoryBefore = allocator.getUsedSystemMemory(); // KB
Expand Down
20 changes: 11 additions & 9 deletions kernel/src/entry/tests/multithreading.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ DECLARE_UNIT_TEST("Multithreading Test - Kernel Task Creation", mtTaskCreationUn
DECLARE_UNIT_TEST("Multithreading Test - Single Core", mtSingleCoreUnitTest) {
const size_t taskCount = 1000;
const int targetCpu = BSP_CPU_ID;
const int taskExecutionTimeout = 400;
auto& sched = RRScheduler::get();
const int taskExecutionTimeout = 1200;
auto& sched = Scheduler::get();

// Allocate a buffer to store the tasks
Task** taskArray = (Task**)kmalloc(sizeof(Task*) * taskCount);
Expand All @@ -69,8 +69,8 @@ DECLARE_UNIT_TEST("Multithreading Test - Single Core", mtSingleCoreUnitTest) {

// Schedule all the tasks
for (size_t i = 0; i < taskCount; i++) {
bool ret = sched.addTask(taskArray[i], targetCpu);
ASSERT_TRUE(ret, "Failed to schedule a task on a single CPU core");
sched.addTaskToCpu(taskArray[i], targetCpu);
// ASSERT_TRUE(ret, "Failed to schedule a task on a single CPU core");
}

// Wait for all tasks to finish
Expand All @@ -93,9 +93,9 @@ DECLARE_UNIT_TEST("Multithreading Test - Single Core", mtSingleCoreUnitTest) {

DECLARE_UNIT_TEST("Multithreading Test - Multi Core", mtMultiCoreUnitTest) {
const size_t systemCpus = AcpiController::get().getApicTable()->getCpuCount();
const size_t taskCount = 200 * (systemCpus - 1);
const uint32_t taskExecutionTimeout = 400;
auto& sched = RRScheduler::get();
const size_t taskCount = 1000 * systemCpus;
const uint32_t taskExecutionTimeout = 1200;
auto& sched = Scheduler::get();

// Allocate a buffer to store the tasks
Task** taskArray = (Task**)kmalloc(sizeof(Task*) * taskCount);
Expand All @@ -115,8 +115,10 @@ DECLARE_UNIT_TEST("Multithreading Test - Multi Core", mtMultiCoreUnitTest) {

// Schedule all the tasks
for (size_t i = 0; i < taskCount; i++) {
bool ret = sched.addTask(taskArray[i]);
ASSERT_TRUE(ret, "Failed to schedule a task");
size_t targetCpu = i % systemCpus;

sched.addTaskToCpu(taskArray[i], targetCpu);
// ASSERT_TRUE(ret, "Failed to schedule a task");
}

// Wait for all tasks to finish
Expand Down
2 changes: 1 addition & 1 deletion kernel/src/interrupts/interrupts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ DEFINE_INT_HANDLER(_exc_handler_pf) {
DEFINE_INT_HANDLER(_irq_handler_timer) {
Apic::__irqGetLocalApic()->completeIrq();

auto& sched = RRScheduler::get();
auto& sched = Scheduler::get();
size_t cpu = current->cpu;

PCB* prevTask = sched.getCurrentTask(cpu);
Expand Down
2 changes: 2 additions & 0 deletions kernel/src/interrupts/panic.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "panic.h"
#include <sched/sched.h>
#include <kprint.h>
#include <sync.h>

Expand Down Expand Up @@ -78,6 +79,7 @@ void kpanic(PtRegs* frame) {

kprintChar('\n');
kprintError("====== PANIC: CPU EXCEPTION %s ======\n", g_cpuExceptionMessages[frame->intno]);
kprintInfo("CPU: %i\n", current->cpu);
kprintInfo("Error Code: %llx\n", frame->error);

printBacktrace(frame);
Expand Down
6 changes: 2 additions & 4 deletions kernel/src/memory/kheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ void* DynamicMemoryAllocator::allocate(size_t size) {
return nullptr;
}

if (!_splitSegment(segment, newSegmentSize)) {
releaseSpinlock(&__kheap_lock);
return nullptr;
}
// Attempt to split the segment if it's large enough
_splitSegment(segment, newSegmentSize);

// Mark segment as used
segment->flags.free = false;
Expand Down
Loading

0 comments on commit 0f28785

Please sign in to comment.