From 2216ec738892e06438c88ec9555373fb69e8e8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 17 Jun 2025 11:02:44 +0200 Subject: [PATCH 01/20] Implement basic DTB parser --- .github/workflows/kernel-build.yml | 1 + src/Kernel/Platform.h | 1 + src/Kernel/Platforms/RiscV/Platform.c | 79 ++++++++++++--------------- 3 files changed, 38 insertions(+), 43 deletions(-) diff --git a/.github/workflows/kernel-build.yml b/.github/workflows/kernel-build.yml index bb1a811..37acfca 100644 --- a/.github/workflows/kernel-build.yml +++ b/.github/workflows/kernel-build.yml @@ -26,6 +26,7 @@ jobs: - name: Install CMake uses: lukka/get-cmake@latest + # TODO: On some platforms ARMV7 for example, we should test with different board Virtaul, RPI2B, etc. - name: Install QEMU run: | sudo apt-get update diff --git a/src/Kernel/Platform.h b/src/Kernel/Platform.h index d340f34..4947c16 100644 --- a/src/Kernel/Platform.h +++ b/src/Kernel/Platform.h @@ -12,6 +12,7 @@ typedef struct ReadOnlySpanChar Name; uint32_t ArchitectureBits; uint8_t BootCpuId; + // TODO: Add Board Name / Computer name / Platform form name } PlatformInformation; // TODO: We should maybe put that in common and call it SystemDevice or SystemStaticDevice diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index a14ad09..219e334 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -8,6 +8,8 @@ uintptr_t globalBootHartId; uintptr_t globalDeviceTreeData; +// TODO: Merge get devices into one function. But maybe GetInformation is not great +// because we retrieve the whole device map also PlatformInformation PlatformGetInformation() { return (PlatformInformation) @@ -22,12 +24,13 @@ PlatformInformation PlatformGetInformation() // TODO: We should have a create binary reader that take the encoding and the pointer // TODO: It would be cool if it could work with streams but it seems too much // TODO: This function should take into account the endianness of the platform -uint32_t BinaryReadUint32Old(uintptr_t pointer) +uint32_t ConvertBytesToUint32(ReadOnlySpanUint8 data, ByteOrder byteOrder) { - // TODO: For now big endian -> little endian conversion - auto result = *(uint32_t*)pointer; + // TODO: Check length is at least 4 + // TODO: For now big endian -> little endian conversion + auto result = *(uint32_t*)data.Pointer; - if (PLATFORM_BYTE_ORDER == ByteOrder_LittleEndian) + if (PLATFORM_BYTE_ORDER != byteOrder) { result = __builtin_bswap32(result); } @@ -55,18 +58,22 @@ BinaryReader CreateBinaryReader(ReadOnlySpanUint8 data, ByteOrder byteOrder) uint32_t BinaryReadUint32(BinaryReader* reader) { auto span = SpanSliceFrom(reader->Data, reader->CurrentOffset); + reader->CurrentOffset += sizeof(uint32_t); + + return ConvertBytesToUint32(span, reader->ByteOrder); +} + +// TODO: When we have memoryarena we can maybe do better +void BinaryReadBytes(BinaryReader* reader, size_t length, SpanUint8* output) +{ + auto span = SpanSlice(reader->Data, reader->CurrentOffset, length); // TODO: For now big endian -> little endian conversion // TODO: Do the other conversions - auto result = *(uint32_t*)span.Pointer; - - if (PLATFORM_BYTE_ORDER == ByteOrder_LittleEndian) - { - result = __builtin_bswap32(result); - } - reader->CurrentOffset += sizeof(uint32_t); + MemoryCopy(*output, span); - return result; + output->Length = length; + reader->CurrentOffset += length; } // TODO: When we have memoryarena we can maybe do better @@ -93,7 +100,7 @@ void BinarySetOffset(BinaryReader* reader, size_t offset) reader->CurrentOffset = offset; } -void DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) +bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) { auto testNode = BinaryReadUint32(reader); @@ -114,12 +121,8 @@ void DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) auto length = BinaryReadUint32(reader); auto nameOffset = BinaryReadUint32(reader); - if (length != 4) - { - KernelConsolePrint(String(" Unknown length: %d\n"), length); - } - - auto value = BinaryReadUint32(reader); + auto value = StackAllocUint8(1024); + BinaryReadBytes(reader, length, &value); auto offset = reader->CurrentOffset; BinarySetOffset(reader, stringDataOffset + nameOffset); @@ -128,21 +131,26 @@ void DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) BinaryReadString(reader, &name); BinarySetOffset(reader, MemoryAlignUp(offset, 4)); - KernelConsolePrint(String(" Property: %s, %d\n"), name, value); + KernelConsolePrint(String(" Property: %s\n"), name); } - else + else if (testNode == 0x09) { - KernelConsolePrint(String("Unknown Node: %d\n"), testNode); + return false; } + + return true; } PlatformDevices PlatformGetDevices() { - auto dtbMagic = BinaryReadUint32Old(globalDeviceTreeData); - auto sizeInBytes = BinaryReadUint32Old(globalDeviceTreeData + sizeof(uint32_t)); + auto dtbHeaderData = CreateReadOnlySpanUint8((uint8_t*)globalDeviceTreeData, sizeof(uint32_t) * 2); + + auto dtbMagic = ConvertBytesToUint32(dtbHeaderData, ByteOrder_BigEndian); + auto sizeInBytes = ConvertBytesToUint32(SpanSliceFrom(dtbHeaderData, sizeof(uint32_t)), ByteOrder_BigEndian); + KernelConsolePrint(String("MagicDTB: %x\n"), dtbMagic); // TODO: Check magic - // TODO: Check boot_cpuid_phys + // TODO: Verify version auto dataSpan = CreateReadOnlySpanUint8((const uint8_t*)globalDeviceTreeData, sizeInBytes); auto reader = CreateBinaryReader(dataSpan, ByteOrder_BigEndian); @@ -155,24 +163,9 @@ PlatformDevices PlatformGetDevices() BinarySetOffset(&reader, structureOffset); - DeviceTreeReadNode(&reader, stringDataOffset); - DeviceTreeReadNode(&reader, stringDataOffset); - DeviceTreeReadNode(&reader, stringDataOffset); - - DeviceTreeReadNode(&reader, stringDataOffset); - DeviceTreeReadNode(&reader, stringDataOffset); - DeviceTreeReadNode(&reader, stringDataOffset); - - - /* This is a property node parsing - testNode = BinaryReadUint32(&reader); - length = BinaryReadUint32(&reader); - nameOffset = BinaryReadUint32(&reader); - KernelConsolePrint(String("TestNode: %d, %d, %d\n"), testNode, length, nameOffset); - auto test = SpanSliceFrom(dataSpan, stringDataOffset + nameOffset); - KernelConsolePrint(String("Test: %s\n"), test); - */ - + while (DeviceTreeReadNode(&reader, stringDataOffset)) + { + } // TODO: We parse DTB here for now but it will be moved in Kernel/Devices/DTB return (PlatformDevices) From bcbd714adca41db6b33728b951c2d42caad427cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 17 Jun 2025 11:57:57 +0200 Subject: [PATCH 02/20] Change CD --- .github/workflows/kanso-cd.yml | 3 --- .github/workflows/kanso-ci.yml | 3 +++ src/Kernel/KernelMain.c | 2 +- src/Kernel/Platforms/RiscV/Platform.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/kanso-cd.yml b/.github/workflows/kanso-cd.yml index 8c82922..d2656c8 100644 --- a/.github/workflows/kanso-cd.yml +++ b/.github/workflows/kanso-cd.yml @@ -1,8 +1,5 @@ name: Kanso-CD on: - push: - branches: - - main workflow_dispatch: jobs: diff --git a/.github/workflows/kanso-ci.yml b/.github/workflows/kanso-ci.yml index 5344fc3..b37aaa0 100644 --- a/.github/workflows/kanso-ci.yml +++ b/.github/workflows/kanso-ci.yml @@ -1,5 +1,8 @@ name: Kanso-CI on: + push: + branches: + - main pull_request: workflow_dispatch: diff --git a/src/Kernel/KernelMain.c b/src/Kernel/KernelMain.c index 64ffc7c..a0086a1 100644 --- a/src/Kernel/KernelMain.c +++ b/src/Kernel/KernelMain.c @@ -88,7 +88,7 @@ void KernelMain() KernelConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); - auto platformDevices = PlatformGetDevices(); + //auto platformDevices = PlatformGetDevices(); BiosSetTimer(CpuReadTime() + 10'000'000); diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index 219e334..464f677 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -114,7 +114,7 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) } else if (testNode == 0x02) { - KernelConsolePrint(String("EndNode\n")); + KernelConsolePrint(String("EndNode.\n")); } else if (testNode == 0x03) { From 1de505e1cf2030b4db5abc02b63b1167dd882661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 18 Jun 2025 14:25:23 +0200 Subject: [PATCH 03/20] Add SpanAt macro --- src/Common/Memory.h | 3 +- src/Common/String.c | 24 ++++++------- src/Kernel/KernelMain.c | 2 +- src/Kernel/Platform.h | 1 + src/Kernel/Platforms/RiscV/Platform.c | 50 +++++++++++++++++++++++++-- tests/Common/MemoryTests.c | 20 +++++------ tests/Common/StringTests.c | 2 +- 7 files changed, 73 insertions(+), 29 deletions(-) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 6205cef..9b9471b 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -55,8 +55,7 @@ DefineSpan(Uint64, uint64_t) ) #define SpanSliceFrom(span, offset) SpanSlice((span), (offset), (span).Length - (offset)) - -// TODO: SpanGetItem? +#define SpanAt(span, index) (span).Pointer[(index)] void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value); void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value); diff --git a/src/Common/String.c b/src/Common/String.c index 0fb4f1b..13bc8f1 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -14,7 +14,7 @@ bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) for (uint32_t i = 0; i < string1.Length; i++) { - if (string1.Pointer[i] != string2.Pointer[i]) + if (SpanAt(string1, i) != SpanAt(string2, i)) { return false; } @@ -53,7 +53,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list case '%': { - destination->Pointer[length++] = '%'; + SpanAt(*destination, length++) = '%'; break; } @@ -63,7 +63,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (*stringArgument) { - destination->Pointer[length++] = *stringArgument; + SpanAt(*destination, length++) = *stringArgument; stringArgument++; } break; @@ -77,7 +77,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list if (decimalArgument < 0) { - destination->Pointer[length++] = '-'; + SpanAt(*destination, length++) = '-'; magnitude = -magnitude; } @@ -90,7 +90,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (divisor > 0) { - destination->Pointer[length++] = '0' + magnitude / divisor; + SpanAt(*destination, length++) = '0' + magnitude / divisor; magnitude %= divisor; divisor /= 10; @@ -107,7 +107,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list if (decimalArgument < 0) { - destination->Pointer[length++] = '-'; + SpanAt(*destination, length++) = '-'; magnitude = -magnitude; } @@ -120,7 +120,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (divisor > 0) { - destination->Pointer[length++] = '0' + magnitude / divisor; + SpanAt(*destination, length++) = '0' + magnitude / divisor; magnitude %= divisor; divisor /= 10; @@ -131,13 +131,13 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list case 'x': { uintptr_t hexaArgument = va_arg(vargs, uintptr_t); - destination->Pointer[length++] = '0'; - destination->Pointer[length++] = 'x'; + SpanAt(*destination, length++) = '0'; + SpanAt(*destination, length++) = 'x'; for (int32_t i = (sizeof(uintptr_t) * 2) - 1; i >= 0; i--) { unsigned nibble = (hexaArgument >> (i * 4)) & 0xf; - destination->Pointer[length++] = "0123456789abcdef"[nibble]; + SpanAt(*destination, length++) = "0123456789abcdef"[nibble]; } break; } @@ -145,13 +145,13 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list } else { - destination->Pointer[length++] = *messagePointer; + SpanAt(*destination, length++) = *messagePointer; } messagePointer++; } destination->Length = length; - destination->Pointer[length] = '\0'; + SpanAt(*destination, length) = '\0'; } diff --git a/src/Kernel/KernelMain.c b/src/Kernel/KernelMain.c index a0086a1..64ffc7c 100644 --- a/src/Kernel/KernelMain.c +++ b/src/Kernel/KernelMain.c @@ -88,7 +88,7 @@ void KernelMain() KernelConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); - //auto platformDevices = PlatformGetDevices(); + auto platformDevices = PlatformGetDevices(); BiosSetTimer(CpuReadTime() + 10'000'000); diff --git a/src/Kernel/Platform.h b/src/Kernel/Platform.h index 4947c16..d4d94ed 100644 --- a/src/Kernel/Platform.h +++ b/src/Kernel/Platform.h @@ -12,6 +12,7 @@ typedef struct ReadOnlySpanChar Name; uint32_t ArchitectureBits; uint8_t BootCpuId; + uint32_t PageSize; // TODO: Add Board Name / Computer name / Platform form name } PlatformInformation; diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index 464f677..cc1a1ba 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -3,6 +3,8 @@ #include "String.h" #include "Types.h" +#define RISCV_MEMORY_PAGESIZE 4096 + // TODO: Add tests uintptr_t globalBootHartId; @@ -16,7 +18,8 @@ PlatformInformation PlatformGetInformation() { .Name = String("RISC-V"), .ArchitectureBits = PLATFORM_ARCHITECTURE_BITS, - .BootCpuId = globalBootHartId + .BootCpuId = globalBootHartId, + .PageSize = RISCV_MEMORY_PAGESIZE }; } @@ -38,6 +41,20 @@ uint32_t ConvertBytesToUint32(ReadOnlySpanUint8 data, ByteOrder byteOrder) return result; } +uint64_t ConvertBytesToUint64(ReadOnlySpanUint8 data, ByteOrder byteOrder) +{ + // TODO: Check length is at least 4 + // TODO: For now big endian -> little endian conversion + auto result = *(uint64_t*)data.Pointer; + + if (PLATFORM_BYTE_ORDER != byteOrder) + { + result = __builtin_bswap64(result); + } + + return result; +} + typedef struct { ReadOnlySpanUint8 Data; @@ -63,6 +80,14 @@ uint32_t BinaryReadUint32(BinaryReader* reader) return ConvertBytesToUint32(span, reader->ByteOrder); } +uint64_t BinaryReadUint64(BinaryReader* reader) +{ + auto span = SpanSliceFrom(reader->Data, reader->CurrentOffset); + reader->CurrentOffset += sizeof(uint64_t); + + return ConvertBytesToUint64(span, reader->ByteOrder); +} + // TODO: When we have memoryarena we can maybe do better void BinaryReadBytes(BinaryReader* reader, size_t length, SpanUint8* output) { @@ -85,9 +110,9 @@ void BinaryReadString(BinaryReader* reader, SpanChar* output) uint32_t length = 0; - while (span.Pointer[length] != '\0') + while (SpanAt(span, length) != '\0') { - output->Pointer[length] = span.Pointer[length]; + SpanAt(*output, length) = SpanAt(span, length); length++; } @@ -151,6 +176,8 @@ PlatformDevices PlatformGetDevices() KernelConsolePrint(String("MagicDTB: %x\n"), dtbMagic); // TODO: Check magic // TODO: Verify version + + // TODO: Parse reserved memory area? auto dataSpan = CreateReadOnlySpanUint8((const uint8_t*)globalDeviceTreeData, sizeInBytes); auto reader = CreateBinaryReader(dataSpan, ByteOrder_BigEndian); @@ -158,8 +185,25 @@ PlatformDevices PlatformGetDevices() auto structureOffset = BinaryReadUint32(&reader); auto stringDataOffset = BinaryReadUint32(&reader); + auto reservedMemoryDataOffset = BinaryReadUint32(&reader); // TODO: Parse the rest of the header + BinarySetOffset(&reader, reservedMemoryDataOffset); + + uint64_t reservedOffset = 1; + uint64_t reservedSize = 1; + + while (reservedOffset != 0 && reservedSize != 0) + { + reservedOffset = BinaryReadUint64(&reader); + reservedSize = BinaryReadUint64(&reader); + + if (reservedOffset != 0 && reservedSize != 0) + { + KernelConsolePrint(String("Reserved Memory: %x (size: %x)\n"), reservedOffset, reservedSize); + } + } + BinarySetOffset(&reader, structureOffset); diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index 8a505de..60e8fd6 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -14,7 +14,7 @@ Test(Memory, SpanSlice_WithSpan_HasCorrectValues) for (uint32_t i = 0; i < itemCount; i++) { - span.Pointer[i] = i; + SpanAt(span, i) = i; } // Act @@ -25,7 +25,7 @@ Test(Memory, SpanSlice_WithSpan_HasCorrectValues) for (uint32_t i = 0; i < result.Length; i++) { - TestAssertEquals(span.Pointer[i + sliceOffset], result.Pointer[i]); + TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); } } @@ -39,7 +39,7 @@ Test(Memory, SpanSliceFrom_WithSpan_HasCorrectValues) for (uint32_t i = 0; i < itemCount; i++) { - span.Pointer[i] = i; + SpanAt(span, i) = i; } // Act @@ -50,7 +50,7 @@ Test(Memory, SpanSliceFrom_WithSpan_HasCorrectValues) for (uint32_t i = 0; i < result.Length; i++) { - TestAssertEquals(span.Pointer[i + sliceOffset], result.Pointer[i]); + TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); } } @@ -68,7 +68,7 @@ Test(Memory, MemorySet_WithUint32_HasCorrectValues) // Assert for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(initialValue, destination.Pointer[i]); + TestAssertEquals(initialValue, SpanAt(destination, i)); } } @@ -86,7 +86,7 @@ Test(Memory, MemorySet_WithUint8_HasCorrectValues) // Assert for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(initialValue, destination.Pointer[i]); + TestAssertEquals(initialValue, SpanAt(destination, i)); } } @@ -98,7 +98,7 @@ Test(Memory, MemoryCopy_WithUint32_HasCorrectValues) for (uint32_t i = 0; i < itemCount; i++) { - source.Pointer[i] = i; + SpanAt(source, i) = i; } auto destination = StackAllocUint32(itemCount); @@ -110,7 +110,7 @@ Test(Memory, MemoryCopy_WithUint32_HasCorrectValues) // Assert for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(i, destination.Pointer[i]); + TestAssertEquals(i, SpanAt(destination, i)); } } @@ -122,7 +122,7 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) for (uint32_t i = 0; i < itemCount; i++) { - source.Pointer[i] = i; + SpanAt(source, i) = i; } auto destination = StackAllocUint8(itemCount); @@ -134,6 +134,6 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) // Assert for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(i, destination.Pointer[i]); + TestAssertEquals(i, SpanAt(destination, i)); } } diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index 67897ea..e57e56c 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -15,7 +15,7 @@ Test(String, String_HasCorrectValues) for (uint32_t i = 0; i < result.Length; i++) { - TestAssertEquals(testString[i], result.Pointer[i]); + TestAssertEquals(testString[i], SpanAt(result, i)); } } From a6798ccba9961597552a1d91bc2824aa046b447d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 23 Jun 2025 05:09:52 +0200 Subject: [PATCH 04/20] WIP memory management boot init --- src/Common/Memory.c | 9 ++ src/Common/Memory.h | 67 ++++++++++--- src/Common/String.c | 8 +- src/Common/String.h | 8 +- src/Common/System.h | 13 +++ src/Common/Test.c | 14 ++- src/Common/Test.h | 5 +- src/Kernel/KernelMain.c | 10 +- src/Kernel/KernelMemory.c | 56 +++++++++++ src/Kernel/KernelSystem.c | 9 ++ src/Kernel/KernelTest.c | 18 +++- src/Kernel/Platform.h | 18 ++-- src/Kernel/Platforms/RiscV/Boot.S | 14 ++- src/Kernel/Platforms/RiscV/Kernel.ld | 4 + src/Kernel/Platforms/RiscV/Platform.c | 25 +++-- src/Kernel/UnityBuild.c | 2 + tests/Common/MemoryTests.c | 129 +++++++++++++++++++++++++- tests/Common/StringTests.c | 49 ++++++---- 18 files changed, 392 insertions(+), 66 deletions(-) create mode 100644 src/Common/System.h create mode 100644 src/Kernel/KernelMemory.c create mode 100644 src/Kernel/KernelSystem.c diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 7f2a210..6f1699c 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -1,5 +1,9 @@ #include "Memory.h" +// -------------------------------------------------------------------------------------- +// Span +// -------------------------------------------------------------------------------------- + void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value) { (void)stride; @@ -54,6 +58,11 @@ void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLengt } } +// -------------------------------------------------------------------------------------- +// MemoryArena +// -------------------------------------------------------------------------------------- + + // TODO: Move that to the standard library size_t strlen(const char* string) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 9b9471b..581b0ee 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -2,10 +2,19 @@ #include "Types.h" +//--------------------------------------------------------------------------------------- +// General +//--------------------------------------------------------------------------------------- + #define MemoryAlignUp(value, align) __builtin_align_up(value, align) #define MemoryIsAligned(value, align) __builtin_is_aligned(value, align) #define MemoryOffsetOf(type, member) __builtin_offsetof(type, member) +#define ArraySize(a) (sizeof(a) / sizeof(*(a))) + +//--------------------------------------------------------------------------------------- +// Span +//--------------------------------------------------------------------------------------- #define DefineSpan(name, type) \ typedef struct Span##name { type* Pointer; size_t Length; } Span##name; \ @@ -45,6 +54,8 @@ DefineSpan(Uint32, uint32_t) DefineSpan(Uint64, uint64_t) #define StackAllocUint64(length) DefineSpanStackAlloc(Uint64, uint64_t, (length)) +// TODO: Span casting functions (do tests) + #define SpanSlice(span, offset, length) \ ( \ (typeof(span)) \ @@ -79,20 +90,48 @@ void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLengt )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, (source).Pointer, (source).Length) -#define _IS_READONLY_SPAN(span) \ - __builtin_types_compatible_p( \ - __typeof__((span).Pointer), \ - const __typeof__(*(span).Pointer) *) - -#define _ASSERT_READONLY_SPAN(src) \ - _Static_assert( _IS_READONLY_SPAN(src), \ - "MemoryCopy: source span must be read-only") - -#define MemoryCopy(destination, source) \ - do { \ - _ASSERT_READONLY_SPAN(source); \ - _MemoryCopyDispatch((destination), (source)); \ - } while (0) +#define _IS_READONLY_SPAN(span) \ + __builtin_types_compatible_p( \ + typeof((span).Pointer), \ + const typeof(*(span).Pointer) *) + +#define _ASSERT_READONLY_SPAN(source) \ + static_assert(_IS_READONLY_SPAN(source), "MemoryCopy: source span must be read-only") + +#define MemoryCopy(destination, source) \ + do { \ + _ASSERT_READONLY_SPAN(source); \ + _MemoryCopyDispatch((destination), (source)); \ + } while (false) + +//--------------------------------------------------------------------------------------- +// Memory Allocation +//--------------------------------------------------------------------------------------- + +typedef enum +{ + MemoryAccess_Read, + MemoryAccess_ReadWrite, + MemoryAccess_Execute, + MemoryAccess_ExecuteRead, + MemoryAccess_ExecuteReadWrite +} MemoryAccess; + +typedef struct +{ + void* BaseAddress; + size_t PageCount; +} MemoryReservation; + +MemoryReservation MemoryReservePages(size_t pageCount); +bool MemoryRelease(MemoryReservation* memoryReservation); + +bool MemoryCommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount, MemoryAccess access); +bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount); + +//--------------------------------------------------------------------------------------- +// Memory Arena +//--------------------------------------------------------------------------------------- diff --git a/src/Common/String.c b/src/Common/String.c index 13bc8f1..76600c1 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -23,6 +23,13 @@ bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) return true; } +// TODO: Replace that with a memory arena +void StringSplit(SpanString* result, ReadOnlySpanChar value, char separator) +{ + result->Length = 0; +} + +// TODO: Replace that with a memory arena void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...) { va_list vargs; @@ -154,4 +161,3 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list destination->Length = length; SpanAt(*destination, length) = '\0'; } - diff --git a/src/Common/String.h b/src/Common/String.h index 51c5756..d1e156c 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -3,13 +3,19 @@ #include "Types.h" #include "Memory.h" +DefineSpan(String, ReadOnlySpanChar) +#define StackAllocString(length) DefineSpanStackAlloc(String, ReadOnlySpanChar, (length)) + ReadOnlySpanChar String(const char* string); bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2); +// TODO: Replace that with a memory arena +void StringSplit(SpanString* result, ReadOnlySpanChar value, char separator); + +// TODO: Replace that with a memory arena void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...); void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list vargs); - // TODO: Move that to the standard library size_t strlen(const char* string); diff --git a/src/Common/System.h b/src/Common/System.h new file mode 100644 index 0000000..3174143 --- /dev/null +++ b/src/Common/System.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Memory.h" + +typedef struct +{ + ReadOnlySpanChar Name; + uint32_t ArchitectureBits; + uint32_t PageSize; + // TODO: Add Board Name / Computer name / Platform form name +} SystemInformation; + +SystemInformation SystemGetInformation(); diff --git a/src/Common/Test.c b/src/Common/Test.c index 3acaa60..054d8ae 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -1,9 +1,10 @@ #include "Test.h" +#include "Memory.h" TestEntry globalTests[MAX_TESTS]; uint32_t globalTestCount = 0; uint32_t globalCurrentTestIndex = 0; -SpanChar globalTestLastErrorMessage; +SpanChar globalTestLastErrorMessage = {}; void RegisterTest(const char* category, const char* name, TestFunction testFunction) { @@ -18,7 +19,7 @@ void RegisterTest(const char* category, const char* name, TestFunction testFunct }; } -void TestRun(TestLogHandler handler) +void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) { uint32_t failedCounter = 0; globalTestLastErrorMessage = StackAllocChar(TEST_ERROR_MESSAGE_LENGTH); @@ -30,6 +31,15 @@ void TestRun(TestLogHandler handler) auto test = &globalTests[i]; globalCurrentTestIndex = i; + if (categoryFilters.Length > 0) + { + // TODO: Split + for (uint32_t j = 0; j < categoryFilters.Length; j++) + { + handler(TestRunState_Start, String("Checking Category: %s.%s"), test->Category, test->Name); + } + } + handler(TestRunState_Start, String("%s.%s"), test->Category, test->Name); test->TestFunction(); diff --git a/src/Common/Test.h b/src/Common/Test.h index 556c54c..2d98788 100644 --- a/src/Common/Test.h +++ b/src/Common/Test.h @@ -63,11 +63,12 @@ extern SpanChar globalTestLastErrorMessage; #define TestAssertNotEquals(expected, actual) TestAssertCore((expected) != (actual), expected, actual, "!=") #define TestAssertGreaterThan(expected, actual) TestAssertCore((expected) > (actual), expected, actual, ">") #define TestAssertIsTrue(actual) TestAssertCore(true == (actual), true, actual, "==") +#define TestAssertIsFalse(actual) TestAssertCore(false == (actual), false, actual, "==") // TODO: Adapt the macro like the core one #define TestAssertStringEquals(expected, actual) \ do { \ - if (finalString.Length != destination.Length) \ + if (expected.Length != actual.Length) \ { \ TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ testEntry->HasError = true; \ @@ -83,4 +84,4 @@ extern SpanChar globalTestLastErrorMessage; } while (false) void RegisterTest(const char* category, const char* name, TestFunction testFunction); -void TestRun(TestLogHandler handler); +void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters); diff --git a/src/Kernel/KernelMain.c b/src/Kernel/KernelMain.c index 64ffc7c..05ad659 100644 --- a/src/Kernel/KernelMain.c +++ b/src/Kernel/KernelMain.c @@ -73,7 +73,7 @@ void KernelTrapHandler(CpuTrapFrame* trapFrame) KernelFailure(String("%s. (Code=%x, Extra=%x)"), errorName, trapCause.Code, trapCause.ExtraInformation); } -void KernelMain() +void KernelInit() { auto platformInformation = PlatformGetInformation(); @@ -83,14 +83,14 @@ void KernelMain() KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); KernelConsolePrint(String("Kanso OS %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.Name.Pointer, platformInformation.ArchitectureBits); + KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); KernelConsoleResetStyle(); KernelConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); +} - auto platformDevices = PlatformGetDevices(); - - +void KernelMain() +{ BiosSetTimer(CpuReadTime() + 10'000'000); CpuSetTrapHandler(KernelTrapHandler); diff --git a/src/Kernel/KernelMemory.c b/src/Kernel/KernelMemory.c new file mode 100644 index 0000000..610d115 --- /dev/null +++ b/src/Kernel/KernelMemory.c @@ -0,0 +1,56 @@ +#include "KernelConsole.h" +#include "Memory.h" +#include "Platform.h" + +SpanUint8 globalInitHeap; +bool globalMemoryTableInitialized = false; + +MemoryReservation KernelInitModeMemoryReservePages(size_t pageCount) +{ + if (globalInitHeap.Pointer == nullptr) + { + auto platformInformation = PlatformGetInformation(); + globalInitHeap = platformInformation.InitHeap; + KernelConsolePrint(String("HeapStart: %x Size:%d\n"), globalInitHeap.Pointer, globalInitHeap.Length); + } + + // TODO: + + return (MemoryReservation) + { + }; +} + +MemoryReservation KernelMemoryReservePages(size_t pageCount) +{ + // TODO: + + return (MemoryReservation) + { + }; +} + +MemoryReservation MemoryReservePages(size_t pageCount) +{ + if (!globalMemoryTableInitialized) + { + return KernelInitModeMemoryReservePages(pageCount); + } + + return KernelMemoryReservePages(pageCount); +} + +bool MemoryRelease(MemoryReservation* memoryReservation) +{ + return false; +} + +bool MemoryCommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount, MemoryAccess access) +{ + return false; +} + +bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount) +{ + return false; +} diff --git a/src/Kernel/KernelSystem.c b/src/Kernel/KernelSystem.c new file mode 100644 index 0000000..ec72a29 --- /dev/null +++ b/src/Kernel/KernelSystem.c @@ -0,0 +1,9 @@ +#include "Platform.h" +#include "System.h" + + +SystemInformation SystemGetInformation() +{ + auto platformInformation = PlatformGetInformation(); + return platformInformation.SystemInformation; +} diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index 8d7ef3d..7ce7c97 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -46,16 +46,28 @@ void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) va_end(vargs); } +void KernelInit() +{ + auto platformInformation = PlatformGetInformation(); + + KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); + KernelConsolePrint(String("\n\nKanso OS KernelInit Tests %s "), KANSO_VERSION_FULL); + KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + KernelConsoleResetStyle(); + + TestRun(KernelTestHandler, String("Memory|String")); +} + void KernelMain() { auto platformInformation = PlatformGetInformation(); KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("\n\nKanso OS Kernel Tests %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.Name.Pointer, platformInformation.ArchitectureBits); + KernelConsolePrint(String("\n\nKanso OS KernelMain Tests %s "), KANSO_VERSION_FULL); + KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); KernelConsoleResetStyle(); - TestRun(KernelTestHandler); + TestRun(KernelTestHandler, String("")); BiosReset(BiosResetType_Shutdown, BiosResetReason_None); } diff --git a/src/Kernel/Platform.h b/src/Kernel/Platform.h index d4d94ed..d8545be 100644 --- a/src/Kernel/Platform.h +++ b/src/Kernel/Platform.h @@ -1,28 +1,28 @@ #pragma once #include "Memory.h" +#include "System.h" #include "Types.h" // -------------------------------------------------------------------------------------- // General // -------------------------------------------------------------------------------------- +// TODO: We should maybe put that in common and call it SystemDevice or SystemStaticDevice typedef struct { - ReadOnlySpanChar Name; - uint32_t ArchitectureBits; - uint8_t BootCpuId; - uint32_t PageSize; - // TODO: Add Board Name / Computer name / Platform form name -} PlatformInformation; +} PlatformDevices; -// TODO: We should maybe put that in common and call it SystemDevice or SystemStaticDevice typedef struct { -} PlatformDevices; + SystemInformation SystemInformation; + uint8_t BootCpuId; + SpanUint8 InitHeap; + // TODO: Add Board Name / Computer name / Platform form name + PlatformDevices Devices; +} PlatformInformation; PlatformInformation PlatformGetInformation(); -PlatformDevices PlatformGetDevices(); // -------------------------------------------------------------------------------------- // Cpu diff --git a/src/Kernel/Platforms/RiscV/Boot.S b/src/Kernel/Platforms/RiscV/Boot.S index a714660..8ac5b24 100644 --- a/src/Kernel/Platforms/RiscV/Boot.S +++ b/src/Kernel/Platforms/RiscV/Boot.S @@ -11,13 +11,14 @@ boot: # Clear .bss la t0, __BSS la t1, __BSS_END - beq t0, t1, .Lenter_kernel_main + beq t0, t1, .Linit_parameters .Lclear_bss_loop: sb zero, (t0) addi t0, t0, 1 blt t0, t1, .Lclear_bss_loop +.Linit_parameters: # Save boot parameters la t0, globalBootHartId save_pointer a0, (t0) @@ -28,7 +29,7 @@ boot: # Run init array la t2, __INIT_ARRAY_START la t3, __INIT_ARRAY_END - beq t2, t3, .Lenter_kernel_main + beq t2, t3, .Lenter_kernel_init .Lrun_init_array_loop: load_pointer t4, (t2) @@ -36,6 +37,9 @@ boot: addi t2, t2, PTR_SIZE blt t2, t3, .Lrun_init_array_loop -.Lenter_kernel_main: - call KernelMain - j . +.Lenter_kernel_init: + call KernelInit + +# TODO: Setup real stack + # Run KernelMain + tail KernelMain diff --git a/src/Kernel/Platforms/RiscV/Kernel.ld b/src/Kernel/Platforms/RiscV/Kernel.ld index ee032a3..f2ad5ed 100644 --- a/src/Kernel/Platforms/RiscV/Kernel.ld +++ b/src/Kernel/Platforms/RiscV/Kernel.ld @@ -45,4 +45,8 @@ SECTIONS . = ALIGN(8); . += 128 * 1024; /* 128KB */ __STACK_PTR = .; + . = ALIGN(4096); + __INIT_HEAP_START = .; + . += 64 * 1024 * 1024; /* 64MB, TODO: Review this later, we don't need that much for init mode */ + __INIT_HEAP_END = .; } diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index cc1a1ba..f14c421 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -6,21 +6,34 @@ #define RISCV_MEMORY_PAGESIZE 4096 // TODO: Add tests +extern uint8_t __INIT_HEAP_START[]; +extern uint8_t __INIT_HEAP_END[]; uintptr_t globalBootHartId; uintptr_t globalDeviceTreeData; +PlatformInformation globalPlatformInformation = {}; + // TODO: Merge get devices into one function. But maybe GetInformation is not great // because we retrieve the whole device map also PlatformInformation PlatformGetInformation() { - return (PlatformInformation) + if (globalPlatformInformation.SystemInformation.ArchitectureBits == 0) { - .Name = String("RISC-V"), - .ArchitectureBits = PLATFORM_ARCHITECTURE_BITS, - .BootCpuId = globalBootHartId, - .PageSize = RISCV_MEMORY_PAGESIZE - }; + globalPlatformInformation = (PlatformInformation) + { + .SystemInformation = + { + .Name = String("RISC-V"), + .ArchitectureBits = PLATFORM_ARCHITECTURE_BITS, + .PageSize = RISCV_MEMORY_PAGESIZE + }, + .BootCpuId = globalBootHartId, + .InitHeap = CreateSpanUint8(__INIT_HEAP_START, __INIT_HEAP_END - __INIT_HEAP_START) + }; + } + + return globalPlatformInformation; } // TODO: Put that in a common binary reader or similar and do tests diff --git a/src/Kernel/UnityBuild.c b/src/Kernel/UnityBuild.c index 27f31b2..7403b4b 100644 --- a/src/Kernel/UnityBuild.c +++ b/src/Kernel/UnityBuild.c @@ -1,6 +1,8 @@ #include "../Common/UnityBuild.c" #include "Kernel.c" +#include "KernelSystem.c" +#include "KernelMemory.c" #include "KernelConsole.c" #ifdef BUILD_TESTS diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index 60e8fd6..11e2b14 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -1,6 +1,7 @@ +#include "Memory.h" #include "Test.h" -// TODO: SpanSlice: Test Length +// TODO: SpanSlice: Test Length and overflow // TODO: Memory set with a span that has less elements Test(Memory, SpanSlice_WithSpan_HasCorrectValues) @@ -137,3 +138,129 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) TestAssertEquals(i, SpanAt(destination, i)); } } + +Test(Memory, MemoryReserve_WithValidSize_ReturnsMemoryReservation) +{ + // Arrange + const size_t pageCount = 4; + + // Act + auto memoryReservation = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(pageCount, memoryReservation.PageCount); + TestAssertNotEquals(nullptr, memoryReservation.BaseAddress); +} + +Test(Memory, MemoryReserve_WithInvalidSize_ReturnsEmptyMemoryReservation) +{ + // Arrange + const size_t pageCount = 0; + + // Act + auto memoryReservation = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(0, memoryReservation.PageCount); + TestAssertEquals(nullptr, memoryReservation.BaseAddress); +} + +Test(Memory, MemoryCommit_WithValidRange_ReturnsTrue) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + + // Act + auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Assert + TestAssertIsTrue(result); +} + +Test(Memory, MemoryCommit_WithInvalidRange_ReturnsFalse) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 2; + const size_t committedPageCount = 3; + const size_t memoryAccess = MemoryAccess_ReadWrite; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + + // Act + auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Assert + TestAssertIsFalse(result); +} + +Test(Memory, MemoryDecommit_WithValidRange_ReturnsTrue) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + const size_t decommittedPageOffset = 1; + const size_t decommittedPageCount = 2; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Act + auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); + + // Assert + TestAssertIsTrue(result); +} + +Test(Memory, MemoryDecommit_WithInvalidRange_ReturnsFalse) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + const size_t decommittedPageOffset = 2; + const size_t decommittedPageCount = 3; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Act + auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); + + // Assert + TestAssertIsFalse(result); +} + +Test(Memory, MemoryReserve_WithValidMemoryReservation_ReturnsTrue) +{ + // Arrange + const size_t pageCount = 4; + auto memoryReservation = MemoryReservePages(pageCount); + + // Act + auto result = MemoryRelease(&memoryReservation); + + // Assert + TestAssertIsTrue(result); +} + +Test(Memory, MemoryReserve_WithInvalidMemoryReservation_ReturnsFalse) +{ + // Arrange + const size_t pageCount = 4; + auto memoryReservation = (MemoryReservation) {}; + + // Act + auto result = MemoryRelease(&memoryReservation); + + // Assert + TestAssertIsTrue(result); +} diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index e57e56c..1a68362 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -19,23 +19,6 @@ Test(String, String_HasCorrectValues) } } -Test(String, StringFormat_WithParameters_HasCorrectValues) -{ - // Arrange - const auto testString = String("Test: %d, %s"); - const auto intParameter = 28; - const auto stringParameter = String("TestParameter"); - - const auto finalString = String("Test: 28, TestParameter"); - auto destination = StackAllocChar(64); - - // Act - StringFormat(&destination, testString, intParameter, stringParameter); - - // Assert - TestAssertStringEquals(finalString, ToReadOnlySpanChar(destination)); -} - Test(String, StringEquals_WithSameStrings_ReturnsTrue) { // Arrange @@ -61,3 +44,35 @@ Test(String, StringEquals_WithDifferentStrings_ReturnsFalse) // Assert TestAssertEquals(false, result); } + +Test(String, StringSplit_WithParameters_HasCorrectValues) +{ + // Arrange + const auto testString = String("Test1|Test2|Test3"); + auto destination = StackAllocString(64); + + // Act + StringSplit(&destination, testString, '|'); + + // Assert + TestAssertEquals(3, destination.Length); + //TestAssertStringEquals(String("Test1"), SpanAt(destination, 0)); +} + +Test(String, StringFormat_WithParameters_HasCorrectValues) +{ + // Arrange + const auto testString = String("Test: %d, %s"); + const auto intParameter = 28; + const auto stringParameter = String("TestParameter"); + + const auto finalString = String("Test: 28, TestParameter"); + auto destination = StackAllocChar(64); + + // Act + StringFormat(&destination, testString, intParameter, stringParameter); + + // Assert + TestAssertStringEquals(finalString, ToReadOnlySpanChar(destination)); +} + From b25c170f64366693d300fe98896919a236ae25d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 23 Jun 2025 14:29:13 +0200 Subject: [PATCH 05/20] Fix tests --- src/Common/String.c | 18 +++++++++++- src/Common/Test.c | 58 +++++++++++++++++++++++++++----------- src/Common/Test.h | 34 +++++++++++----------- src/Kernel/KernelMain.c | 3 +- src/Kernel/KernelTest.c | 47 +++++++++++++++++++++++++++++- tests/Common/StringTests.c | 4 ++- tests/Kernel/CpuTests.c | 14 +++++++++ 7 files changed, 142 insertions(+), 36 deletions(-) diff --git a/src/Common/String.c b/src/Common/String.c index 76600c1..3127e2d 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -1,4 +1,5 @@ #include "String.h" +#include "Memory.h" ReadOnlySpanChar String(const char* string) { @@ -26,7 +27,21 @@ bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) // TODO: Replace that with a memory arena void StringSplit(SpanString* result, ReadOnlySpanChar value, char separator) { - result->Length = 0; + auto resultCount = 0; + auto currentStartIndex = 0; + + for (uint32_t i = 0; i < value.Length; i++) + { + if (SpanAt(value, i) == separator) + { + SpanAt(*result, resultCount++) = SpanSlice(value, currentStartIndex, i - currentStartIndex); + currentStartIndex = i + 1; + } + } + + SpanAt(*result, resultCount++) = SpanSlice(value, currentStartIndex, value.Length - currentStartIndex); + + result->Length = resultCount; } // TODO: Replace that with a memory arena @@ -39,6 +54,7 @@ void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...) } // TODO: Refactor this function +// TODO: It would be cool if we could handle ReadOnlySpan so we can take the length not until \0 void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list vargs) { uint32_t length = 0; diff --git a/src/Common/Test.c b/src/Common/Test.c index 054d8ae..60d922d 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -1,12 +1,16 @@ #include "Test.h" #include "Memory.h" +#include "String.h" TestEntry globalTests[MAX_TESTS]; uint32_t globalTestCount = 0; uint32_t globalCurrentTestIndex = 0; -SpanChar globalTestLastErrorMessage = {}; -void RegisterTest(const char* category, const char* name, TestFunction testFunction) +// TODO: Do someting better here? +char globalTestLastErrorMessageStorage[TEST_ERROR_MESSAGE_LENGTH]; +SpanChar globalTestLastErrorMessage = { .Pointer = globalTestLastErrorMessageStorage, .Length = TEST_ERROR_MESSAGE_LENGTH }; + +void RegisterTest(ReadOnlySpanChar category, ReadOnlySpanChar name, TestFunction testFunction) { // TODO: We don't have a way to assert for now in the kernel //assert(globalTestCount < MAX_TESTS, "Too many TEST()s: increase MAX_TESTS"); @@ -15,32 +19,54 @@ void RegisterTest(const char* category, const char* name, TestFunction testFunct { .Category = category, .Name = name, - .TestFunction = testFunction + .TestFunction = testFunction, }; } void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) { - uint32_t failedCounter = 0; - globalTestLastErrorMessage = StackAllocChar(TEST_ERROR_MESSAGE_LENGTH); - - handler(TestRunState_Separator, String("Running %d %s."), globalTestCount, globalTestCount == 1 ? "test" : "tests"); + uint32_t testRunCount = 0; for (uint32_t i = 0; i < globalTestCount; i++) { auto test = &globalTests[i]; - globalCurrentTestIndex = i; + test->CanRun = true; if (categoryFilters.Length > 0) { - // TODO: Split - for (uint32_t j = 0; j < categoryFilters.Length; j++) + auto splittedFilters = StackAllocString(64); + StringSplit(&splittedFilters, categoryFilters, '|'); + + auto testCanRun = false; + + for (uint32_t j = 0; j < splittedFilters.Length; j++) { - handler(TestRunState_Start, String("Checking Category: %s.%s"), test->Category, test->Name); + if (StringEquals(test->Category, SpanAt(splittedFilters, j))) + { + testCanRun = true; + } } + + test->CanRun = testCanRun; + } + + if (test->CanRun) + { + testRunCount++; } + } + + uint32_t failedCounter = 0; + + handler(TestRunState_Separator, String("Running %d %s."), testRunCount, testRunCount == 1 ? "test" : "tests"); + + for (uint32_t i = 0; i < globalTestCount; i++) + { + auto test = &globalTests[i]; + test->HasError = false; + globalCurrentTestIndex = i; - handler(TestRunState_Start, String("%s.%s"), test->Category, test->Name); + handler(TestRunState_Start, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); test->TestFunction(); @@ -51,12 +77,12 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) } else { - handler(TestRunState_OK, String("%s.%s"), test->Category, test->Name); + handler(TestRunState_OK, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); } } - handler(TestRunState_Separator, String("%d %s ran."), globalTestCount, globalTestCount == 1 ? "test" : "tests"); - handler(TestRunState_Passed, String("%d %s."), globalTestCount - failedCounter, (globalTestCount - failedCounter) == 1 ? "test" : "tests"); + handler(TestRunState_Separator, String("%d %s ran."), testRunCount, testRunCount == 1 ? "test" : "tests"); + handler(TestRunState_Passed, String("%d %s."), testRunCount - failedCounter, (testRunCount - failedCounter) == 1 ? "test" : "tests"); if (failedCounter > 0) { @@ -68,7 +94,7 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) if (test->HasError) { - handler(TestRunState_Failed, String("%s.%s"), test->Category, test->Name); + handler(TestRunState_Failed, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); } } } diff --git a/src/Common/Test.h b/src/Common/Test.h index 2d98788..15de28c 100644 --- a/src/Common/Test.h +++ b/src/Common/Test.h @@ -21,10 +21,11 @@ typedef void (*TestLogHandler)(TestRunState state, ReadOnlySpanChar message, ... typedef struct { - const char* Category; - const char* Name; + ReadOnlySpanChar Category; + ReadOnlySpanChar Name; TestFunction TestFunction; bool HasError; + bool CanRun; } TestEntry; @@ -40,7 +41,7 @@ extern SpanChar globalTestLastErrorMessage; [[gnu::constructor]] \ static void __register_##category##_##name() \ { \ - RegisterTest(#category, #name, test_##category##_##name); \ + RegisterTest(String(#category), String(#name), test_##category##_##name); \ } \ \ static void test_##category##_##name() \ @@ -58,7 +59,6 @@ extern SpanChar globalTestLastErrorMessage; } \ } while (false) -// BUG: There is a bug in the assert only in 32-bit version, when the assert fail maybe due to 64 bit values used in the comparison like with the time #define TestAssertEquals(expected, actual) TestAssertCore((expected) == (actual), expected, actual, "==") #define TestAssertNotEquals(expected, actual) TestAssertCore((expected) != (actual), expected, actual, "!=") #define TestAssertGreaterThan(expected, actual) TestAssertCore((expected) > (actual), expected, actual, ">") @@ -68,20 +68,22 @@ extern SpanChar globalTestLastErrorMessage; // TODO: Adapt the macro like the core one #define TestAssertStringEquals(expected, actual) \ do { \ - if (expected.Length != actual.Length) \ + TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ + if (!testEntry->HasError) \ { \ - TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ - testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s.Length) == (%s.Length)\n Actual: %d == %d"), __FILE__, #expected, #actual, expected.Length, actual.Length); \ - } \ - \ - if (!StringEquals(expected, actual)) \ - { \ - TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ - testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s) == (%s)\n Actual: \"%s\" == \"%s\""), __FILE__, #expected, #actual, expected.Pointer, actual.Pointer); \ + if (expected.Length != actual.Length) \ + { \ + testEntry->HasError = true; \ + StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s.Length) == (%s.Length)\n Actual: %d == %d"), __FILE__, #expected, #actual, expected.Length, actual.Length); \ + } \ + \ + if (!StringEquals(expected, actual)) \ + { \ + testEntry->HasError = true; \ + StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s) == (%s)\n Actual: \"%s\" == \"%s\""), __FILE__, #expected, #actual, expected.Pointer, actual.Pointer); \ + } \ } \ } while (false) -void RegisterTest(const char* category, const char* name, TestFunction testFunction); +void RegisterTest(ReadOnlySpanChar category, ReadOnlySpanChar name, TestFunction testFunction); void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters); diff --git a/src/Kernel/KernelMain.c b/src/Kernel/KernelMain.c index 05ad659..b953280 100644 --- a/src/Kernel/KernelMain.c +++ b/src/Kernel/KernelMain.c @@ -75,6 +75,8 @@ void KernelTrapHandler(CpuTrapFrame* trapFrame) void KernelInit() { + CpuSetTrapHandler(KernelTrapHandler); + auto platformInformation = PlatformGetInformation(); KernelConsoleSetForegroundColor(KernelConsoleColorAccent); @@ -92,7 +94,6 @@ void KernelInit() void KernelMain() { BiosSetTimer(CpuReadTime() + 10'000'000); - CpuSetTrapHandler(KernelTrapHandler); // TODO: Test Timer only when the hardware is running fine CpuEnableInterrupts(CpuInterruptType_Timer); diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index 7ce7c97..7210891 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -2,9 +2,52 @@ #include "Test.h" #include "String.h" #include "KernelConsole.h" +#include "Kernel.h" #include "Platform.h" #include "Version.h" +void KernelTrapHandler(CpuTrapFrame* trapFrame) +{ + auto trapCause = CpuTrapFrameGetCause(trapFrame); + auto errorName = String("Unknown kernel trap cause"); + + if (trapCause.Type == CpuTrapType_Synchronous) + { + switch (trapCause.SynchronousType) + { + case CpuTrapSynchronousType_InstructionError: + errorName = String("Instruction error"); + break; + + case CpuTrapSynchronousType_AddressError: + errorName = String("Address error"); + break; + + case CpuTrapSynchronousType_Debug: + errorName = String("Debug not implemented"); + break; + + case CpuTrapSynchronousType_PageError: + errorName = String("Page error"); + break; + + case CpuTrapSynchronousType_IntegrityError: + errorName = String("Integrity error"); + break; + + case CpuTrapSynchronousType_HardwareError: + errorName = String("Hardware error"); + break; + + default: + errorName = String("Unknown synchronous trap type"); + } + } + + CpuLogTrapFrame(trapFrame); + KernelFailure(String("%s. (Code=%x, Extra=%x)"), errorName, trapCause.Code, trapCause.ExtraInformation); +} + void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) { if (state == TestRunState_Start) @@ -48,6 +91,8 @@ void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) void KernelInit() { + CpuSetTrapHandler(KernelTrapHandler); + auto platformInformation = PlatformGetInformation(); KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); @@ -55,7 +100,7 @@ void KernelInit() KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); KernelConsoleResetStyle(); - TestRun(KernelTestHandler, String("Memory|String")); + TestRun(KernelTestHandler, String("Memory")); } void KernelMain() diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index 1a68362..96bbebc 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -56,7 +56,9 @@ Test(String, StringSplit_WithParameters_HasCorrectValues) // Assert TestAssertEquals(3, destination.Length); - //TestAssertStringEquals(String("Test1"), SpanAt(destination, 0)); + TestAssertStringEquals(String("Test1"), SpanAt(destination, 0)); + TestAssertStringEquals(String("Test2"), SpanAt(destination, 1)); + TestAssertStringEquals(String("Test3"), SpanAt(destination, 2)); } Test(String, StringFormat_WithParameters_HasCorrectValues) diff --git a/tests/Kernel/CpuTests.c b/tests/Kernel/CpuTests.c index 3b200c4..8527e72 100644 --- a/tests/Kernel/CpuTests.c +++ b/tests/Kernel/CpuTests.c @@ -7,11 +7,18 @@ Test(Cpu, CpuReadTime) { // Arrange + const uint32_t iterationCount = 50; uint64_t time1 = 0; uint64_t time2 = 0; // Act time1 = CpuReadTime(); + + for (uint32_t i = 0; i < iterationCount; i++) + { + CpuReadTime(); + } + time2 = CpuReadTime(); // Assert @@ -23,11 +30,18 @@ Test(Cpu, CpuReadTime) Test(Cpu, CpuReadCycle) { // Arrange + const uint32_t iterationCount = 50; uint64_t cycle1 = 0; uint64_t cycle2 = 0; // Act cycle1 = CpuReadCycle(); + + for (uint32_t i = 0; i < iterationCount; i++) + { + CpuReadCycle(); + } + cycle2 = CpuReadCycle(); // Assert From 24bab79ea6abe2b44334731b9de661a49f3857d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sun, 29 Jun 2025 19:29:25 +0200 Subject: [PATCH 06/20] Span and BitArray --- src/Common/Memory.c | 39 +++--- src/Common/Memory.h | 87 ++++-------- src/Common/String.c | 5 - src/Common/String.h | 6 +- src/Common/System.h | 2 +- src/Common/Test.c | 5 + src/Common/Types.c | 4 + src/Common/Types.h | 189 +++++++++++++++++++++++++- src/Common/UnityBuild.c | 1 + src/Kernel/KernelMemory.c | 62 ++++++++- src/Kernel/KernelTest.c | 4 +- src/Kernel/Platforms/RiscV/Cpu.c | 6 +- src/Kernel/Platforms/RiscV/Platform.c | 6 +- tests/Common/MemoryAllocationTests.c | 159 ++++++++++++++++++++++ tests/Common/MemoryTests.c | 177 ------------------------ tests/Common/TypesTests.c | 132 ++++++++++++++++++ tests/Common/UnityBuild.c | 2 + 17 files changed, 606 insertions(+), 280 deletions(-) create mode 100644 src/Common/Types.c create mode 100644 tests/Common/MemoryAllocationTests.c create mode 100644 tests/Common/TypesTests.c diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 6f1699c..171ff03 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -1,19 +1,17 @@ #include "Memory.h" -// -------------------------------------------------------------------------------------- +// TODO: This will need to be thread local +MemoryError globalMemoryError = MemoryError_None; + +//--------------------------------------------------------------------------------------- // Span -// -------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value) { (void)stride; - uint8_t* pointer = destination; uint8_t byteValue = *(uint8_t*)value; - - for (size_t i = 0; i < destinationLength; i++) - { - pointer[i] = byteValue; - } + __builtin_memset(destination, byteValue, destinationLength); } void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value) @@ -36,10 +34,7 @@ void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, // TODO: Check length (void)destinationLength; - for (size_t i = 0; i < sourceLength; i++) - { - ((uint8_t*)destination)[i] = ((uint8_t*)source)[i]; - } + __builtin_memcpy(destination, source, sourceLength); } void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength) @@ -58,10 +53,9 @@ void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLengt } } -// -------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- // MemoryArena -// -------------------------------------------------------------------------------------- - +//--------------------------------------------------------------------------------------- // TODO: Move that to the standard library @@ -79,5 +73,18 @@ size_t strlen(const char* string) void memset(uint8_t* destination, uint8_t value, size_t sizeInBytes) { - MemorySetByte(sizeof(uint8_t), destination, sizeInBytes, &value); + for (size_t i = 0; i < sizeInBytes; i++) + { + destination[i] = value; + } +} + +void* memcpy(uint8_t* destination, const uint8_t* source, size_t sizeInBytes) +{ + for (size_t i = 0; i < sizeInBytes; i++) + { + destination[i] = source[i]; + } + + return destination; } diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 581b0ee..709211c 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -3,71 +3,28 @@ #include "Types.h" //--------------------------------------------------------------------------------------- -// General +// Error Handling //--------------------------------------------------------------------------------------- -#define MemoryAlignUp(value, align) __builtin_align_up(value, align) -#define MemoryIsAligned(value, align) __builtin_is_aligned(value, align) -#define MemoryOffsetOf(type, member) __builtin_offsetof(type, member) +typedef enum +{ + MemoryError_None, + MemoryError_InvalidParameter, + MemoryError_OutOfMemory +} MemoryError; + +// TODO: This will need to be thread local +extern MemoryError globalMemoryError; -#define ArraySize(a) (sizeof(a) / sizeof(*(a))) +static inline MemoryError MemoryGetLastError() +{ + return globalMemoryError; +} //--------------------------------------------------------------------------------------- -// Span +// General //--------------------------------------------------------------------------------------- -#define DefineSpan(name, type) \ - typedef struct Span##name { type* Pointer; size_t Length; } Span##name; \ - typedef struct ReadOnlySpan##name { const type* Pointer; size_t Length; } ReadOnlySpan##name; \ - \ - static inline Span##name CreateSpan##name(type* pointer, size_t length) \ - { \ - return (Span##name) { .Pointer = pointer, .Length = length }; \ - } \ - \ - static inline ReadOnlySpan##name CreateReadOnlySpan##name(const type* pointer, size_t length) \ - { \ - return (ReadOnlySpan##name) { .Pointer = pointer, .Length = length }; \ - } \ - \ - static inline ReadOnlySpan##name ToReadOnlySpan##name(Span##name span) \ - { \ - return (ReadOnlySpan##name) { .Pointer = span.Pointer, .Length = span.Length }; \ - } - -#define DefineSpanStackAlloc(name, type, length) \ - (__extension__ ({ \ - static_assert((length) >= 0, "StackAlloc: length must be an integer-constant expression"); \ - type array[(length)]; \ - CreateSpan##name(array, (size_t)(length)); \ - })) - -DefineSpan(Char, char) -#define StackAllocChar(length) DefineSpanStackAlloc(Char, char, (length)) - -DefineSpan(Uint8, uint8_t) -#define StackAllocUint8(length) DefineSpanStackAlloc(Uint8, uint8_t, (length)) - -DefineSpan(Uint32, uint32_t) -#define StackAllocUint32(length) DefineSpanStackAlloc(Uint32, uint32_t, (length)) - -DefineSpan(Uint64, uint64_t) -#define StackAllocUint64(length) DefineSpanStackAlloc(Uint64, uint64_t, (length)) - -// TODO: Span casting functions (do tests) - -#define SpanSlice(span, offset, length) \ -( \ - (typeof(span)) \ - { \ - .Pointer = (span).Pointer + (offset), \ - .Length = (length) \ - } \ -) - -#define SpanSliceFrom(span, offset) SpanSlice((span), (offset), (span).Length - (offset)) -#define SpanAt(span, index) (span).Pointer[(index)] - void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value); void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value); @@ -79,6 +36,8 @@ void MemorySetDefault(size_t stride, void* destination, size_t destinationLength )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, &(typeof(*(destination).Pointer)){ (value) }) +// TODO: Add Errors + void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); @@ -123,6 +82,14 @@ typedef struct size_t PageCount; } MemoryReservation; +#define MEMORY_RESERVATION_EMPTY ((MemoryReservation){ .BaseAddress = nullptr, .PageCount = 0 }) + + +static inline bool MemoryReservationIsEmpty(MemoryReservation memoryReservation) +{ + return memoryReservation.BaseAddress == nullptr; +} + MemoryReservation MemoryReservePages(size_t pageCount); bool MemoryRelease(MemoryReservation* memoryReservation); @@ -133,7 +100,3 @@ bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t page // Memory Arena //--------------------------------------------------------------------------------------- - - -// TODO: Move that to the standard library -void memset(uint8_t* destination, uint8_t value, size_t sizeInBytes); diff --git a/src/Common/String.c b/src/Common/String.c index 3127e2d..3e2de87 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -1,11 +1,6 @@ #include "String.h" #include "Memory.h" -ReadOnlySpanChar String(const char* string) -{ - return CreateReadOnlySpanChar(string, __builtin_strlen(string)); -} - bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) { if (string1.Length != string2.Length) diff --git a/src/Common/String.h b/src/Common/String.h index d1e156c..91bbda2 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -6,7 +6,11 @@ DefineSpan(String, ReadOnlySpanChar) #define StackAllocString(length) DefineSpanStackAlloc(String, ReadOnlySpanChar, (length)) -ReadOnlySpanChar String(const char* string); +static inline ReadOnlySpanChar String(const char* string) +{ + return CreateReadOnlySpanChar(string, __builtin_strlen(string)); +} + bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2); // TODO: Replace that with a memory arena diff --git a/src/Common/System.h b/src/Common/System.h index 3174143..5a8296c 100644 --- a/src/Common/System.h +++ b/src/Common/System.h @@ -1,6 +1,6 @@ #pragma once -#include "Memory.h" +#include "Types.h" typedef struct { diff --git a/src/Common/Test.c b/src/Common/Test.c index 60d922d..1de82f4 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -66,6 +66,11 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) test->HasError = false; globalCurrentTestIndex = i; + if (!test->CanRun) + { + continue; + } + handler(TestRunState_Start, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); test->TestFunction(); diff --git a/src/Common/Types.c b/src/Common/Types.c new file mode 100644 index 0000000..e04ad29 --- /dev/null +++ b/src/Common/Types.c @@ -0,0 +1,4 @@ +#include "Types.h" + +// TODO: This will need to be thread local +TypeError globalTypeError = TypeError_None; diff --git a/src/Common/Types.h b/src/Common/Types.h index b0f9edd..e473632 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -1,5 +1,9 @@ #pragma once +//--------------------------------------------------------------------------------------- +// Primitive types +//--------------------------------------------------------------------------------------- + typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; @@ -22,6 +26,44 @@ static_assert(sizeof(uint64_t) == 8, "uint64_t must be 8 bytes."); static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not pointer-sized."); #define PLATFORM_ARCHITECTURE_BITS (__SIZEOF_POINTER__ * 8) +#define BITS_PER_BYTE 8u + +#define AlignUp(value, align) __builtin_align_up(value, align) +#define IsAligned(value, align) __builtin_is_aligned(value, align) +#define OffsetOf(type, member) __builtin_offsetof(type, member) + + +//--------------------------------------------------------------------------------------- +// Variable parameters +//--------------------------------------------------------------------------------------- + +#define va_list __builtin_va_list +#define va_start __builtin_va_start +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg + +//--------------------------------------------------------------------------------------- +// Error Handling +//--------------------------------------------------------------------------------------- + +typedef enum +{ + TypeError_None, + TypeError_InvalidParameter, + TypeError_Unaligned +} TypeError; + +// TODO: This will need to be thread local +extern TypeError globalTypeError; + +static inline TypeError TypeGetLastError() +{ + return globalTypeError; +} + +//--------------------------------------------------------------------------------------- +// Endianness conversion +//--------------------------------------------------------------------------------------- typedef enum { @@ -31,10 +73,149 @@ typedef enum #define PLATFORM_BYTE_ORDER __BYTE_ORDER__ -#define va_list __builtin_va_list -#define va_start __builtin_va_start -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg +// TODO: Read functions for endian + +//--------------------------------------------------------------------------------------- +// Span +//--------------------------------------------------------------------------------------- + +#define DefineSpan(name, type) \ + typedef struct Span##name { type* Pointer; size_t Length; } Span##name; \ + typedef struct ReadOnlySpan##name { const type* Pointer; size_t Length; } ReadOnlySpan##name; \ + \ + static inline Span##name CreateSpan##name(type* pointer, size_t length) \ + { \ + return (Span##name) { .Pointer = pointer, .Length = length }; \ + } \ + \ + static inline ReadOnlySpan##name CreateReadOnlySpan##name(const type* pointer, size_t length) \ + { \ + return (ReadOnlySpan##name) { .Pointer = pointer, .Length = length }; \ + } \ + \ + static inline ReadOnlySpan##name ToReadOnlySpan##name(Span##name span) \ + { \ + return (ReadOnlySpan##name) { .Pointer = span.Pointer, .Length = span.Length }; \ + } \ + \ + [[clang::overloadable]] \ + [[clang::always_inline]] \ + static inline Span##name _SpanCast(size_t sourceStride, void* sourcePointer, size_t sourceLength, const type* unused) \ + { \ + (void)unused; \ + size_t bytes = sourceStride * sourceLength; \ + return CreateSpan##name((type *)sourcePointer, bytes / sizeof(type)); \ + } + +#define DefineSpanStackAlloc(name, type, length) \ + (__extension__ ({ \ + static_assert((length) >= 0, "StackAlloc: length must be an integer-constant expression"); \ + type array[(length)]; \ + CreateSpan##name(array, (size_t)(length)); \ + })) + +DefineSpan(Char, char) +#define StackAllocChar(length) DefineSpanStackAlloc(Char, char, (length)) + +DefineSpan(Uint8, uint8_t) +#define StackAllocUint8(length) DefineSpanStackAlloc(Uint8, uint8_t, (length)) + +DefineSpan(Uint32, uint32_t) +#define StackAllocUint32(length) DefineSpanStackAlloc(Uint32, uint32_t, (length)) + +DefineSpan(Uint64, uint64_t) +#define StackAllocUint64(length) DefineSpanStackAlloc(Uint64, uint64_t, (length)) + +DefineSpan(Size, size_t) +#define StackAllocSize(length) DefineSpanStackAlloc(Size, size_t, (length)) + +#define SpanCast(type, sourceSpan) \ + _SpanCast(sizeof(*(sourceSpan).Pointer), \ + (sourceSpan).Pointer, \ + (sourceSpan).Length, \ + (type*)nullptr) + +#define SpanSlice(span, offset, length) \ +( \ + (typeof(span)) \ + { \ + .Pointer = (span).Pointer + (offset), \ + .Length = (length) \ + } \ +) + +#define SpanSliceFrom(span, offset) SpanSlice((span), (offset), (span).Length - (offset)) +#define SpanAt(span, index) (span).Pointer[(index)] + +//--------------------------------------------------------------------------------------- +// BitArray +//--------------------------------------------------------------------------------------- + +typedef struct +{ + SpanUint8 Data; + size_t BitCount; +} BitArray; + +#define BIT_ARRAY_EMPTY ((BitArray){ .Data = { .Pointer = nullptr, .Length = 0 }, .BitCount = 0 }) + +static inline bool BitArrayIsEmpty(BitArray bitArray) +{ + return bitArray.Data.Pointer == nullptr; +} + +static inline BitArray CreateBitArray(SpanUint8 data, size_t bitCount) +{ + if (bitCount > (data.Length * BITS_PER_BYTE)) + { + globalTypeError = TypeError_InvalidParameter; + return BIT_ARRAY_EMPTY; + } + + if (!IsAligned(data.Pointer, sizeof(size_t))) + { + globalTypeError = TypeError_Unaligned; + return BIT_ARRAY_EMPTY; + } + + globalTypeError = TypeError_None; + + return (BitArray) + { + .Data = data, + .BitCount = bitCount + }; +} + +static inline bool BitArraySet(BitArray bitArray, size_t index) +{ + if (index >= bitArray.BitCount) + { + globalTypeError = TypeError_InvalidParameter; + return false; + } + + // TODO: With aligned size_t + + globalTypeError = TypeError_None; + return true; +} + +static inline bool BitArrayReset(BitArray bitArray, size_t index) +{ + return false; +} + +static inline bool BitArrayIsSet(BitArray bitArray, size_t index) +{ + return false; +} + +size_t BitArrayFindFirstNotSet(BitArray bitArray, size_t index); + +//--------------------------------------------------------------------------------------- +// Standard types +//--------------------------------------------------------------------------------------- typedef struct { diff --git a/src/Common/UnityBuild.c b/src/Common/UnityBuild.c index 82f88c1..b2a1de7 100644 --- a/src/Common/UnityBuild.c +++ b/src/Common/UnityBuild.c @@ -1,3 +1,4 @@ +#include "Types.c" #include "Memory.c" #include "String.c" diff --git a/src/Kernel/KernelMemory.c b/src/Kernel/KernelMemory.c index 610d115..6f227f9 100644 --- a/src/Kernel/KernelMemory.c +++ b/src/Kernel/KernelMemory.c @@ -2,22 +2,70 @@ #include "Memory.h" #include "Platform.h" -SpanUint8 globalInitHeap; -bool globalMemoryTableInitialized = false; +typedef struct +{ + SpanUint8 InitHeap; + SpanUint8 Bitmap; +} KernelInitModeMemoryState; + +typedef struct +{ + bool MemoryTableInitialized; + size_t PageSize; + KernelInitModeMemoryState InitModeMemoryState; +} KernelMemoryState; + +KernelMemoryState globalKernelMemoryState = {}; + +static void KernelInitModeSetupBitmapAllocator(KernelInitModeMemoryState* memoryState) +{ + auto platformInformation = PlatformGetInformation(); + + globalKernelMemoryState.PageSize = platformInformation.SystemInformation.PageSize; + memoryState->InitHeap = platformInformation.InitHeap; + + auto maxPageCount = memoryState->InitHeap.Length / globalKernelMemoryState.PageSize; + auto bitmapStorageSizeInBytes = (maxPageCount + 7) / 8; + + memoryState->Bitmap = SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes); + auto bitmapStoragePageCount = (memoryState->Bitmap.Length + globalKernelMemoryState.PageSize - 1) / globalKernelMemoryState.PageSize; + + MemorySet(memoryState->Bitmap, 0xFF); + + for (uint32_t i = bitmapStoragePageCount; i < maxPageCount; i++) + { + // TODO: + } + + KernelConsolePrint(String("HeapStart: %x Size:%d\n"), memoryState->InitHeap.Pointer, memoryState->InitHeap.Length); + KernelConsolePrint(String("MaxPageCount: %d Bitmap Storage Page Count:%d\n"), maxPageCount, bitmapStoragePageCount); +} MemoryReservation KernelInitModeMemoryReservePages(size_t pageCount) { - if (globalInitHeap.Pointer == nullptr) + if (pageCount == 0) + { + globalMemoryError = MemoryError_InvalidParameter; + return MEMORY_RESERVATION_EMPTY; + } + + auto memoryState = &globalKernelMemoryState.InitModeMemoryState; + + if (memoryState->InitHeap.Pointer == nullptr) { - auto platformInformation = PlatformGetInformation(); - globalInitHeap = platformInformation.InitHeap; - KernelConsolePrint(String("HeapStart: %x Size:%d\n"), globalInitHeap.Pointer, globalInitHeap.Length); + KernelInitModeSetupBitmapAllocator(memoryState); } + auto baseAddress = memoryState->InitHeap.Pointer; + // TODO: + globalMemoryError = MemoryError_None; + return (MemoryReservation) { + .BaseAddress = baseAddress, + .PageCount = pageCount }; } @@ -32,7 +80,7 @@ MemoryReservation KernelMemoryReservePages(size_t pageCount) MemoryReservation MemoryReservePages(size_t pageCount) { - if (!globalMemoryTableInitialized) + if (!globalKernelMemoryState.MemoryTableInitialized) { return KernelInitModeMemoryReservePages(pageCount); } diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index 7210891..c01369e 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -100,7 +100,7 @@ void KernelInit() KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); KernelConsoleResetStyle(); - TestRun(KernelTestHandler, String("Memory")); + TestRun(KernelTestHandler, String("Types|Memory")); } void KernelMain() @@ -112,7 +112,7 @@ void KernelMain() KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); KernelConsoleResetStyle(); - TestRun(KernelTestHandler, String("")); + //TestRun(KernelTestHandler, String("")); BiosReset(BiosResetType_Shutdown, BiosResetReason_None); } diff --git a/src/Kernel/Platforms/RiscV/Cpu.c b/src/Kernel/Platforms/RiscV/Cpu.c index e2e558d..5469943 100644 --- a/src/Kernel/Platforms/RiscV/Cpu.c +++ b/src/Kernel/Platforms/RiscV/Cpu.c @@ -251,7 +251,7 @@ inline void CpuWaitForInterrupt() __asm__ __volatile__("wfi" ::: "memory"); } -void LogRegister(ReadOnlySpanChar name, uintptr_t value, uint8_t padding, bool insertTab) +static void LogRegister(ReadOnlySpanChar name, uintptr_t value, uint8_t padding, bool insertTab) { KernelConsoleSetForegroundColor(KernelConsoleColorKeyword); KernelConsolePrint(String("%s"), name); @@ -278,7 +278,7 @@ void LogRegister(ReadOnlySpanChar name, uintptr_t value, uint8_t padding, bool i } } -void LogGeneralPurposeRegisters(const GeneralPurposeRegisters* generalPurposeRegisters) +static void LogGeneralPurposeRegisters(const GeneralPurposeRegisters* generalPurposeRegisters) { LogRegister(String("ra"), generalPurposeRegisters->RA, 2, true); LogRegister(String("sp"), generalPurposeRegisters->SP, 2, true); @@ -323,7 +323,7 @@ void LogGeneralPurposeRegisters(const GeneralPurposeRegisters* generalPurposeReg LogRegister(String("t6"), generalPurposeRegisters->T6, 2, false); } -void LogSupervisorRegisters(const SupervisorRegisters* supervisorRegisters) +static void LogSupervisorRegisters(const SupervisorRegisters* supervisorRegisters) { LogRegister(String("sepc"), supervisorRegisters->Epc, 3, true); LogRegister(String("sstatus"), supervisorRegisters->Status, 1, true); diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index f14c421..4e4afaf 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -48,6 +48,7 @@ uint32_t ConvertBytesToUint32(ReadOnlySpanUint8 data, ByteOrder byteOrder) if (PLATFORM_BYTE_ORDER != byteOrder) { + // TODO: Put that in Types.h result = __builtin_bswap32(result); } @@ -62,6 +63,7 @@ uint64_t ConvertBytesToUint64(ReadOnlySpanUint8 data, ByteOrder byteOrder) if (PLATFORM_BYTE_ORDER != byteOrder) { + // TODO: Put that in Types.h result = __builtin_bswap64(result); } @@ -146,7 +148,7 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) { auto name = StackAllocChar(1024); BinaryReadString(reader, &name); - BinarySetOffset(reader, MemoryAlignUp(reader->CurrentOffset, 4)); + BinarySetOffset(reader, AlignUp(reader->CurrentOffset, 4)); KernelConsolePrint(String("BeginNode: '%s'\n"), name); } @@ -168,7 +170,7 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) auto name = StackAllocChar(1024); BinaryReadString(reader, &name); - BinarySetOffset(reader, MemoryAlignUp(offset, 4)); + BinarySetOffset(reader, AlignUp(offset, 4)); KernelConsolePrint(String(" Property: %s\n"), name); } else if (testNode == 0x09) diff --git a/tests/Common/MemoryAllocationTests.c b/tests/Common/MemoryAllocationTests.c new file mode 100644 index 0000000..e3c9d99 --- /dev/null +++ b/tests/Common/MemoryAllocationTests.c @@ -0,0 +1,159 @@ +#include "Memory.h" +#include "Test.h" + +// TODO: For user mode only, we will wipe the committed pages to zero. So +// we need to test that + +Test(Memory, MemoryReserve_WithValidSize_ReturnsMemoryReservation) +{ + // Arrange + const size_t pageCount = 4; + + // Act + auto memoryReservation = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + + TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation)); + TestAssertEquals(pageCount, memoryReservation.PageCount); +} + +Test(Memory, MemoryReserve_WithMultipleReservations_ReturnsDifferentMemoryReservations) +{ + // Arrange + const size_t pageCount = 4; + + // Act + auto memoryReservation1 = MemoryReservePages(pageCount); + auto memoryReservation2 = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + + TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation1)); + TestAssertEquals(pageCount, memoryReservation1.PageCount); + + TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation2)); + TestAssertEquals(pageCount, memoryReservation2.PageCount); + + TestAssertNotEquals(memoryReservation1.BaseAddress, memoryReservation2.BaseAddress); +} + +Test(Memory, MemoryReserve_WithInvalidSize_ReturnsEmptyMemoryReservation) +{ + // Arrange + const size_t pageCount = 0; + + // Act + auto memoryReservation = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsTrue(MemoryReservationIsEmpty(memoryReservation)); +} + +Test(Memory, MemoryCommit_WithValidRange_ReturnsTrue) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + + // Act + auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); +} + +Test(Memory, MemoryCommit_WithInvalidRange_ReturnsFalse) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 2; + const size_t committedPageCount = 3; + const size_t memoryAccess = MemoryAccess_ReadWrite; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + + // Act + auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} + +Test(Memory, MemoryDecommit_WithValidRange_ReturnsTrue) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + const size_t decommittedPageOffset = 1; + const size_t decommittedPageCount = 2; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Act + auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); +} + +Test(Memory, MemoryDecommit_WithInvalidRange_ReturnsFalse) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + const size_t decommittedPageOffset = 2; + const size_t decommittedPageCount = 3; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Act + auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} + +Test(Memory, MemoryRelease_WithValidMemoryReservation_ReturnsTrue) +{ + // Arrange + const size_t pageCount = 4; + auto memoryReservation = MemoryReservePages(pageCount); + + // Act + auto result = MemoryRelease(&memoryReservation); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); +} + +Test(Memory, MemoryRelease_WithInvalidMemoryReservation_ReturnsFalse) +{ + // Arrange + auto memoryReservation = (MemoryReservation) {}; + + // Act + auto result = MemoryRelease(&memoryReservation); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index 11e2b14..7c8be23 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -1,60 +1,8 @@ #include "Memory.h" #include "Test.h" -// TODO: SpanSlice: Test Length and overflow // TODO: Memory set with a span that has less elements -Test(Memory, SpanSlice_WithSpan_HasCorrectValues) -{ - // Arrange - const uint32_t itemCount = 10; - const uint32_t sliceOffset = 2; - const uint32_t sliceLength = 5; - - auto span = StackAllocUint32(itemCount); - - for (uint32_t i = 0; i < itemCount; i++) - { - SpanAt(span, i) = i; - } - - // Act - auto result = SpanSlice(span, sliceOffset, sliceLength); - - // Assert - TestAssertEquals(sliceLength, result.Length); - - for (uint32_t i = 0; i < result.Length; i++) - { - TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); - } -} - -Test(Memory, SpanSliceFrom_WithSpan_HasCorrectValues) -{ - // Arrange - const uint32_t itemCount = 10; - const uint32_t sliceOffset = 2; - - auto span = StackAllocUint32(itemCount); - - for (uint32_t i = 0; i < itemCount; i++) - { - SpanAt(span, i) = i; - } - - // Act - auto result = SpanSliceFrom(span, sliceOffset); - - // Assert - TestAssertEquals(itemCount - sliceOffset, result.Length); - - for (uint32_t i = 0; i < result.Length; i++) - { - TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); - } -} - Test(Memory, MemorySet_WithUint32_HasCorrectValues) { // Arrange @@ -139,128 +87,3 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) } } -Test(Memory, MemoryReserve_WithValidSize_ReturnsMemoryReservation) -{ - // Arrange - const size_t pageCount = 4; - - // Act - auto memoryReservation = MemoryReservePages(pageCount); - - // Assert - TestAssertEquals(pageCount, memoryReservation.PageCount); - TestAssertNotEquals(nullptr, memoryReservation.BaseAddress); -} - -Test(Memory, MemoryReserve_WithInvalidSize_ReturnsEmptyMemoryReservation) -{ - // Arrange - const size_t pageCount = 0; - - // Act - auto memoryReservation = MemoryReservePages(pageCount); - - // Assert - TestAssertEquals(0, memoryReservation.PageCount); - TestAssertEquals(nullptr, memoryReservation.BaseAddress); -} - -Test(Memory, MemoryCommit_WithValidRange_ReturnsTrue) -{ - // Arrange - const size_t reservedPageCount = 4; - const size_t committedPageOffset = 1; - const size_t committedPageCount = 2; - const size_t memoryAccess = MemoryAccess_ReadWrite; - - auto memoryReservation = MemoryReservePages(reservedPageCount); - - // Act - auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); - - // Assert - TestAssertIsTrue(result); -} - -Test(Memory, MemoryCommit_WithInvalidRange_ReturnsFalse) -{ - // Arrange - const size_t reservedPageCount = 4; - const size_t committedPageOffset = 2; - const size_t committedPageCount = 3; - const size_t memoryAccess = MemoryAccess_ReadWrite; - - auto memoryReservation = MemoryReservePages(reservedPageCount); - - // Act - auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); - - // Assert - TestAssertIsFalse(result); -} - -Test(Memory, MemoryDecommit_WithValidRange_ReturnsTrue) -{ - // Arrange - const size_t reservedPageCount = 4; - const size_t committedPageOffset = 1; - const size_t committedPageCount = 2; - const size_t memoryAccess = MemoryAccess_ReadWrite; - const size_t decommittedPageOffset = 1; - const size_t decommittedPageCount = 2; - - auto memoryReservation = MemoryReservePages(reservedPageCount); - MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); - - // Act - auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); - - // Assert - TestAssertIsTrue(result); -} - -Test(Memory, MemoryDecommit_WithInvalidRange_ReturnsFalse) -{ - // Arrange - const size_t reservedPageCount = 4; - const size_t committedPageOffset = 1; - const size_t committedPageCount = 2; - const size_t memoryAccess = MemoryAccess_ReadWrite; - const size_t decommittedPageOffset = 2; - const size_t decommittedPageCount = 3; - - auto memoryReservation = MemoryReservePages(reservedPageCount); - MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); - - // Act - auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); - - // Assert - TestAssertIsFalse(result); -} - -Test(Memory, MemoryReserve_WithValidMemoryReservation_ReturnsTrue) -{ - // Arrange - const size_t pageCount = 4; - auto memoryReservation = MemoryReservePages(pageCount); - - // Act - auto result = MemoryRelease(&memoryReservation); - - // Assert - TestAssertIsTrue(result); -} - -Test(Memory, MemoryReserve_WithInvalidMemoryReservation_ReturnsFalse) -{ - // Arrange - const size_t pageCount = 4; - auto memoryReservation = (MemoryReservation) {}; - - // Act - auto result = MemoryRelease(&memoryReservation); - - // Assert - TestAssertIsTrue(result); -} diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c new file mode 100644 index 0000000..993c79a --- /dev/null +++ b/tests/Common/TypesTests.c @@ -0,0 +1,132 @@ +#include "Memory.h" +#include "Types.h" +#include "Test.h" + +// TODO: SpanSlice: Test Length and overflow +// TODO: SpanCast: Test size compatibility + +Test(Types, SpanSlice_WithSpan_HasCorrectValues) +{ + // Arrange + const uint32_t itemCount = 10; + const uint32_t sliceOffset = 2; + const uint32_t sliceLength = 5; + + auto span = StackAllocUint32(itemCount); + + for (uint32_t i = 0; i < itemCount; i++) + { + SpanAt(span, i) = i; + } + + // Act + auto result = SpanSlice(span, sliceOffset, sliceLength); + + // Assert + TestAssertEquals(sliceLength, result.Length); + + for (uint32_t i = 0; i < result.Length; i++) + { + TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); + } +} + +Test(Types, SpanSliceFrom_WithSpan_HasCorrectValues) +{ + // Arrange + const uint32_t itemCount = 10; + const uint32_t sliceOffset = 2; + + auto span = StackAllocUint32(itemCount); + + for (uint32_t i = 0; i < itemCount; i++) + { + SpanAt(span, i) = i; + } + + // Act + auto result = SpanSliceFrom(span, sliceOffset); + + // Assert + TestAssertEquals(itemCount - sliceOffset, result.Length); + + for (uint32_t i = 0; i < result.Length; i++) + { + TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); + } +} + +Test(Types, SpanCast_WithSpanUint8ToUint32_HasCorrectValues) +{ + // Arrange + const uint32_t itemCount = 12; + + auto span = StackAllocUint8(itemCount); + + // Act + auto result = SpanCast(uint32_t, span); + + // Assert + TestAssertEquals(itemCount / sizeof(uint32_t), result.Length); + TestAssertEquals((uintptr_t)span.Pointer, (uintptr_t)result.Pointer); +} + +Test(Types, CreateBitArray_WithCorrectBitCount_ReturnsBitArray) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitCount = 13; + + auto span = StackAllocSize(spanLength); + + // Act + // TODO: Convert span + auto bitArray = CreateBitArray(SpanCast(uint8_t, span), bitCount); + + // Assert + TestAssertEquals(TypeError_None, TypeGetLastError()); + + TestAssertEquals(bitCount, bitArray.BitCount); + TestAssertEquals(spanLength * sizeof(size_t), bitArray.Data.Length); + TestAssertEquals((uint8_t*)span.Pointer, bitArray.Data.Pointer); +} + +Test(Types, CreateBitArray_WithIncorrectBitCount_ReturnsEmptyBitArray) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitCount = 45; + + auto span = StackAllocUint8(spanLength); + + // Act + auto bitArray = CreateBitArray(span, bitCount); + + // Assert + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); + TestAssertIsTrue(BitArrayIsEmpty(bitArray)); +} + +Test(Types, BitArraySet_WithCorrectIndex_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitCount = 13; + const uint32_t bitIndexToSet = 5; + + auto span = StackAllocUint8(spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span, bitCount); + + // Act + auto result = BitArraySet(bitArray, bitIndexToSet); + + // Assert + auto testResult = BitArrayIsSet(bitArray, bitIndexToSet); + + TestAssertIsTrue(result); + TestAssertEquals(TypeError_None, TypeGetLastError()); + + TestAssertIsTrue(testResult); +} diff --git a/tests/Common/UnityBuild.c b/tests/Common/UnityBuild.c index 3fdb46d..a32edc9 100644 --- a/tests/Common/UnityBuild.c +++ b/tests/Common/UnityBuild.c @@ -1,3 +1,5 @@ +#include "TypesTests.c" #include "MemoryTests.c" +#include "MemoryAllocationTests.c" #include "StringTests.c" From 72c74a490d3aab48ac63a0d760366db358dc46eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 30 Jun 2025 15:36:39 +0200 Subject: [PATCH 07/20] WIP BitArray operations --- src/Common/Types.c | 26 ++++++++++ src/Common/Types.h | 57 +++++++++++++--------- tests/Common/TypesTests.c | 100 ++++++++++++++++++++++++++++++-------- 3 files changed, 142 insertions(+), 41 deletions(-) diff --git a/src/Common/Types.c b/src/Common/Types.c index e04ad29..bed1dae 100644 --- a/src/Common/Types.c +++ b/src/Common/Types.c @@ -2,3 +2,29 @@ // TODO: This will need to be thread local TypeError globalTypeError = TypeError_None; + +size_t BitArrayFindFirstNotSet(BitArray bitArray) +{ + if (BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + // TODO: Return invalid size_t -> SIZE_MAX + return 0; + } + + for (uint32_t i = 0; i < bitArray.Data.Length; i++) + { + auto inverse = ~bitArray.Data.Pointer[i]; + + if (inverse == 0) + { + continue; + } + + // TODO: Rename + auto result = CoutTrailingZeros(inverse); + return i * BITS_PER_SIZE_TYPE + result; + } + + return 0; +} diff --git a/src/Common/Types.h b/src/Common/Types.h index e473632..871e485 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -27,11 +27,19 @@ static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not pointer-siz #define PLATFORM_ARCHITECTURE_BITS (__SIZEOF_POINTER__ * 8) #define BITS_PER_BYTE 8u +#define BITS_PER_SIZE_TYPE (sizeof(size_t) * BITS_PER_BYTE) +#define MASK_SIZE_TYPE (BITS_PER_SIZE_TYPE - 1) #define AlignUp(value, align) __builtin_align_up(value, align) #define IsAligned(value, align) __builtin_is_aligned(value, align) #define OffsetOf(type, member) __builtin_offsetof(type, member) +// TODO: Cleanup +#if __SIZEOF_SIZE_T__ == 8 +#define CoutTrailingZeros(x) __builtin_ctzll((unsigned long long)(x)) +#elif __SIZEOF_SIZE_T__ == 4 +#define CoutTrailingZeros(x) __builtin_ctz((unsigned int)(x)) +# endif //--------------------------------------------------------------------------------------- // Variable parameters @@ -49,8 +57,7 @@ static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not pointer-siz typedef enum { TypeError_None, - TypeError_InvalidParameter, - TypeError_Unaligned + TypeError_InvalidParameter } TypeError; // TODO: This will need to be thread local @@ -153,7 +160,7 @@ DefineSpan(Size, size_t) typedef struct { - SpanUint8 Data; + SpanSize Data; size_t BitCount; } BitArray; @@ -164,38 +171,26 @@ static inline bool BitArrayIsEmpty(BitArray bitArray) return bitArray.Data.Pointer == nullptr; } -static inline BitArray CreateBitArray(SpanUint8 data, size_t bitCount) +static inline BitArray CreateBitArray(SpanSize data) { - if (bitCount > (data.Length * BITS_PER_BYTE)) - { - globalTypeError = TypeError_InvalidParameter; - return BIT_ARRAY_EMPTY; - } - - if (!IsAligned(data.Pointer, sizeof(size_t))) - { - globalTypeError = TypeError_Unaligned; - return BIT_ARRAY_EMPTY; - } - globalTypeError = TypeError_None; return (BitArray) { .Data = data, - .BitCount = bitCount + .BitCount = data.Length * BITS_PER_SIZE_TYPE }; } static inline bool BitArraySet(BitArray bitArray, size_t index) { - if (index >= bitArray.BitCount) + if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) { globalTypeError = TypeError_InvalidParameter; return false; } - // TODO: With aligned size_t + bitArray.Data.Pointer[index / BITS_PER_SIZE_TYPE] |= (size_t)1 << (index & MASK_SIZE_TYPE); globalTypeError = TypeError_None; return true; @@ -203,15 +198,33 @@ static inline bool BitArraySet(BitArray bitArray, size_t index) static inline bool BitArrayReset(BitArray bitArray, size_t index) { - return false; + if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + return false; + } + + bitArray.Data.Pointer[index / BITS_PER_SIZE_TYPE] &= ~((size_t)1 << (index & MASK_SIZE_TYPE)); + + globalTypeError = TypeError_None; + return true; } static inline bool BitArrayIsSet(BitArray bitArray, size_t index) { - return false; + if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + return false; + } + + globalTypeError = TypeError_None; + + auto pointer = bitArray.Data.Pointer; + return ((pointer[index / BITS_PER_SIZE_TYPE] >> (index & MASK_SIZE_TYPE)) & 1) == 1; } -size_t BitArrayFindFirstNotSet(BitArray bitArray, size_t index); +size_t BitArrayFindFirstNotSet(BitArray bitArray); //--------------------------------------------------------------------------------------- // Standard types diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c index 993c79a..b909127 100644 --- a/tests/Common/TypesTests.c +++ b/tests/Common/TypesTests.c @@ -75,58 +75,120 @@ Test(Types, CreateBitArray_WithCorrectBitCount_ReturnsBitArray) { // Arrange const uint32_t spanLength = 2; - const uint32_t bitCount = 13; auto span = StackAllocSize(spanLength); // Act - // TODO: Convert span - auto bitArray = CreateBitArray(SpanCast(uint8_t, span), bitCount); + auto bitArray = CreateBitArray(span); // Assert TestAssertEquals(TypeError_None, TypeGetLastError()); - TestAssertEquals(bitCount, bitArray.BitCount); - TestAssertEquals(spanLength * sizeof(size_t), bitArray.Data.Length); - TestAssertEquals((uint8_t*)span.Pointer, bitArray.Data.Pointer); + TestAssertEquals(spanLength, bitArray.Data.Length); + TestAssertEquals(span.Pointer, bitArray.Data.Pointer); } -Test(Types, CreateBitArray_WithIncorrectBitCount_ReturnsEmptyBitArray) +Test(Types, BitArraySet_WithCorrectIndex_HasCorrectValue) { // Arrange const uint32_t spanLength = 2; - const uint32_t bitCount = 45; + const uint32_t bitIndexToSet = 5; - auto span = StackAllocUint8(spanLength); + auto span = StackAllocSize(spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); // Act - auto bitArray = CreateBitArray(span, bitCount); + auto result = BitArraySet(bitArray, bitIndexToSet); // Assert - TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); - TestAssertIsTrue(BitArrayIsEmpty(bitArray)); + TestAssertIsTrue(result); + TestAssertEquals(TypeError_None, TypeGetLastError()); + + auto testResult = BitArrayIsSet(bitArray, bitIndexToSet); + TestAssertIsTrue(testResult); } -Test(Types, BitArraySet_WithCorrectIndex_HasCorrectValue) +Test(Types, BitArraySet_WithIncorrectIndex_HasErrorSet) { // Arrange const uint32_t spanLength = 2; - const uint32_t bitCount = 13; - const uint32_t bitIndexToSet = 5; + const uint32_t bitIndexToSet = sizeof(size_t) * 8 * 3; - auto span = StackAllocUint8(spanLength); + auto span = StackAllocSize(spanLength); MemorySet(span, 0); - auto bitArray = CreateBitArray(span, bitCount); + auto bitArray = CreateBitArray(span); // Act auto result = BitArraySet(bitArray, bitIndexToSet); // Assert - auto testResult = BitArrayIsSet(bitArray, bitIndexToSet); + TestAssertIsFalse(result); + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); +} +Test(Types, BitArrayReset_WithCorrectIndex_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitIndexToSet = 5; + + auto span = StackAllocSize(spanLength); + MemorySet(span, 1); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArrayReset(bitArray, bitIndexToSet); + + // Assert TestAssertIsTrue(result); TestAssertEquals(TypeError_None, TypeGetLastError()); - TestAssertIsTrue(testResult); + auto testResult = BitArrayIsSet(bitArray, bitIndexToSet); + TestAssertIsFalse(testResult); +} + +Test(Types, BitArrayReset_WithIncorrectIndex_HasErrorSet) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitIndexToSet = sizeof(size_t) * 8 * 3; + + auto span = StackAllocSize(spanLength); + MemorySet(span, 1); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArrayReset(bitArray, bitIndexToSet); + + // Assert + TestAssertIsFalse(result); + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); +} + +Test(Types, BitArraySet_BitArrayFindFirstNotSet_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitMaxIndexToSet = sizeof(size_t) + 3; + + auto span = StackAllocSize(spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + for (uint32_t i = 0; i < bitMaxIndexToSet; i++) + { + BitArraySet(bitArray, i); + } + + // Act + auto result = BitArrayFindFirstNotSet(bitArray); + + // Assert + TestAssertEquals(bitMaxIndexToSet, result); } From 1baacadb5ae5018190f87a9a4b6fb056b49ca319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 4 Jul 2025 08:53:56 +0200 Subject: [PATCH 08/20] Change SpanCast implementation --- src/Common/Types.c | 10 ++++---- src/Common/Types.h | 50 ++++++++++++++++----------------------- tests/Common/TypesTests.c | 2 +- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/Common/Types.c b/src/Common/Types.c index bed1dae..32b94d1 100644 --- a/src/Common/Types.c +++ b/src/Common/Types.c @@ -8,8 +8,7 @@ size_t BitArrayFindFirstNotSet(BitArray bitArray) if (BitArrayIsEmpty(bitArray)) { globalTypeError = TypeError_InvalidParameter; - // TODO: Return invalid size_t -> SIZE_MAX - return 0; + return SIZE_MAX; } for (uint32_t i = 0; i < bitArray.Data.Length; i++) @@ -21,10 +20,9 @@ size_t BitArrayFindFirstNotSet(BitArray bitArray) continue; } - // TODO: Rename - auto result = CoutTrailingZeros(inverse); - return i * BITS_PER_SIZE_TYPE + result; + auto result = SizePrefixCountZeros(inverse); + return (i * BITS_PER_SIZE_TYPE) + result; } - return 0; + return SIZE_MAX; } diff --git a/src/Common/Types.h b/src/Common/Types.h index 871e485..88e3174 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -4,42 +4,35 @@ // Primitive types //--------------------------------------------------------------------------------------- -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -typedef long long int64_t; +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; typedef __UINTPTR_TYPE__ uintptr_t; typedef __INTPTR_TYPE__ intptr_t; typedef __SIZE_TYPE__ size_t; -static_assert(sizeof(uint8_t) == 1, "uint8_t must be 1 byte."); -static_assert(sizeof(uint16_t) == 2, "uint16_t must be 2 bytes."); -static_assert(sizeof(uint32_t) == 4, "uint32_t must be 4 bytes."); -static_assert(sizeof(uint64_t) == 8, "uint64_t must be 8 bytes."); - -static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not pointer-sized."); - #define PLATFORM_ARCHITECTURE_BITS (__SIZEOF_POINTER__ * 8) #define BITS_PER_BYTE 8u #define BITS_PER_SIZE_TYPE (sizeof(size_t) * BITS_PER_BYTE) #define MASK_SIZE_TYPE (BITS_PER_SIZE_TYPE - 1) +#define SIZE_MAX __SIZE_MAX__ #define AlignUp(value, align) __builtin_align_up(value, align) #define IsAligned(value, align) __builtin_is_aligned(value, align) #define OffsetOf(type, member) __builtin_offsetof(type, member) -// TODO: Cleanup -#if __SIZEOF_SIZE_T__ == 8 -#define CoutTrailingZeros(x) __builtin_ctzll((unsigned long long)(x)) +#if __SIZEOF_SIZE_T__ == 8 + #define SizePrefixCountZeros(value) __builtin_ctzll((uint64_t)(value)) #elif __SIZEOF_SIZE_T__ == 4 -#define CoutTrailingZeros(x) __builtin_ctz((unsigned int)(x)) -# endif + #define SizePrefixCountZeros(value) __builtin_ctz((uint32_t)(value)) +#endif //--------------------------------------------------------------------------------------- // Variable parameters @@ -105,9 +98,7 @@ typedef enum return (ReadOnlySpan##name) { .Pointer = span.Pointer, .Length = span.Length }; \ } \ \ - [[clang::overloadable]] \ - [[clang::always_inline]] \ - static inline Span##name _SpanCast(size_t sourceStride, void* sourcePointer, size_t sourceLength, const type* unused) \ + static inline Span##name _SPAN_CAST_##name(size_t sourceStride, void* sourcePointer, size_t sourceLength, const type* unused) \ { \ (void)unused; \ size_t bytes = sourceStride * sourceLength; \ @@ -121,6 +112,12 @@ typedef enum CreateSpan##name(array, (size_t)(length)); \ })) +#define DefineSpanCast(name, type, sourceSpan) \ + _SPAN_CAST_##name(sizeof(*(sourceSpan).Pointer), \ + (sourceSpan).Pointer, \ + (sourceSpan).Length, \ + (type*)nullptr) + DefineSpan(Char, char) #define StackAllocChar(length) DefineSpanStackAlloc(Char, char, (length)) @@ -129,6 +126,7 @@ DefineSpan(Uint8, uint8_t) DefineSpan(Uint32, uint32_t) #define StackAllocUint32(length) DefineSpanStackAlloc(Uint32, uint32_t, (length)) +#define SpanCastUint32(sourceSpan) DefineSpanCast(Uint32, uint32_t, (sourceSpan)) DefineSpan(Uint64, uint64_t) #define StackAllocUint64(length) DefineSpanStackAlloc(Uint64, uint64_t, (length)) @@ -136,12 +134,6 @@ DefineSpan(Uint64, uint64_t) DefineSpan(Size, size_t) #define StackAllocSize(length) DefineSpanStackAlloc(Size, size_t, (length)) -#define SpanCast(type, sourceSpan) \ - _SpanCast(sizeof(*(sourceSpan).Pointer), \ - (sourceSpan).Pointer, \ - (sourceSpan).Length, \ - (type*)nullptr) - #define SpanSlice(span, offset, length) \ ( \ (typeof(span)) \ diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c index b909127..8525aae 100644 --- a/tests/Common/TypesTests.c +++ b/tests/Common/TypesTests.c @@ -64,7 +64,7 @@ Test(Types, SpanCast_WithSpanUint8ToUint32_HasCorrectValues) auto span = StackAllocUint8(itemCount); // Act - auto result = SpanCast(uint32_t, span); + auto result = SpanCastUint32(span); // Assert TestAssertEquals(itemCount / sizeof(uint32_t), result.Length); From d72a45333a883a2741467156642ab682c57459ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sun, 13 Jul 2025 11:11:22 +0200 Subject: [PATCH 09/20] Implement bitmap memory allotor for kernel init --- src/Common/Memory.h | 9 +- src/Common/Types.c | 41 ++++++++ src/Common/Types.h | 17 +++- src/Kernel/KernelMemory.c | 140 +++++++++++++++++++++++---- src/Kernel/KernelTest.c | 3 + tests/Common/MemoryAllocationTests.c | 8 ++ tests/Common/TypesTests.c | 51 +++++++++- 7 files changed, 245 insertions(+), 24 deletions(-) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 709211c..4fe5049 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -82,14 +82,21 @@ typedef struct size_t PageCount; } MemoryReservation; -#define MEMORY_RESERVATION_EMPTY ((MemoryReservation){ .BaseAddress = nullptr, .PageCount = 0 }) +typedef struct +{ + size_t CommittedPages; + size_t ReservedPages; +} MemoryAllocationInfos; +#define MEMORY_RESERVATION_EMPTY ((MemoryReservation){ .BaseAddress = nullptr, .PageCount = 0 }) static inline bool MemoryReservationIsEmpty(MemoryReservation memoryReservation) { return memoryReservation.BaseAddress == nullptr; } +MemoryAllocationInfos MemoryGetAllocationInfos(); + MemoryReservation MemoryReservePages(size_t pageCount); bool MemoryRelease(MemoryReservation* memoryReservation); diff --git a/src/Common/Types.c b/src/Common/Types.c index 32b94d1..0164b14 100644 --- a/src/Common/Types.c +++ b/src/Common/Types.c @@ -20,9 +20,50 @@ size_t BitArrayFindFirstNotSet(BitArray bitArray) continue; } + globalTypeError = TypeError_None; auto result = SizePrefixCountZeros(inverse); + return (i * BITS_PER_SIZE_TYPE) + result; } + globalTypeError = TypeError_NotFound; + return SIZE_MAX; +} + +size_t BitArrayFindRangeNotSet(BitArray bitArray, size_t length) +{ + if (BitArrayIsEmpty(bitArray) || length == 0 || length > bitArray.BitCount) + { + globalTypeError = TypeError_InvalidParameter; + return SIZE_MAX; + } + + size_t result = 0; + size_t currentCount = 0; + + for (uint32_t i = 0; i < bitArray.BitCount; i++) + { + if (!BitArrayIsSet(bitArray, i)) + { + if (currentCount == 0) + { + result = i; + } + + currentCount++; + + if (currentCount == length) + { + globalTypeError = TypeError_None; + return result; + } + } + else + { + currentCount = 0; + } + } + + globalTypeError = TypeError_NotFound; return SIZE_MAX; } diff --git a/src/Common/Types.h b/src/Common/Types.h index 88e3174..5d76b7f 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -27,6 +27,7 @@ typedef __SIZE_TYPE__ size_t; #define AlignUp(value, align) __builtin_align_up(value, align) #define IsAligned(value, align) __builtin_is_aligned(value, align) #define OffsetOf(type, member) __builtin_offsetof(type, member) +#define DivRoundUp(value, divisor) (((value) + (divisor) - 1) / (divisor)) #if __SIZEOF_SIZE_T__ == 8 #define SizePrefixCountZeros(value) __builtin_ctzll((uint64_t)(value)) @@ -50,7 +51,8 @@ typedef __SIZE_TYPE__ size_t; typedef enum { TypeError_None, - TypeError_InvalidParameter + TypeError_InvalidParameter, + TypeError_NotFound } TypeError; // TODO: This will need to be thread local @@ -133,6 +135,7 @@ DefineSpan(Uint64, uint64_t) DefineSpan(Size, size_t) #define StackAllocSize(length) DefineSpanStackAlloc(Size, size_t, (length)) +#define SpanCastSize(sourceSpan) DefineSpanCast(Size, size_t, (sourceSpan)) #define SpanSlice(span, offset, length) \ ( \ @@ -174,6 +177,17 @@ static inline BitArray CreateBitArray(SpanSize data) }; } +static inline BitArray CreateBitArrayWithBitCount(SpanSize data, size_t bitCount) +{ + globalTypeError = TypeError_None; + + return (BitArray) + { + .Data = data, + .BitCount = bitCount + }; +} + static inline bool BitArraySet(BitArray bitArray, size_t index) { if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) @@ -217,6 +231,7 @@ static inline bool BitArrayIsSet(BitArray bitArray, size_t index) } size_t BitArrayFindFirstNotSet(BitArray bitArray); +size_t BitArrayFindRangeNotSet(BitArray bitArray, size_t length); //--------------------------------------------------------------------------------------- // Standard types diff --git a/src/Kernel/KernelMemory.c b/src/Kernel/KernelMemory.c index 6f227f9..fb897bd 100644 --- a/src/Kernel/KernelMemory.c +++ b/src/Kernel/KernelMemory.c @@ -1,67 +1,86 @@ #include "KernelConsole.h" #include "Memory.h" #include "Platform.h" +#include "Types.h" typedef struct { SpanUint8 InitHeap; - SpanUint8 Bitmap; + BitArray BitArray; } KernelInitModeMemoryState; typedef struct { bool MemoryTableInitialized; - size_t PageSize; KernelInitModeMemoryState InitModeMemoryState; + uint32_t PageSize; + size_t CommittedPages; + size_t ReservedPages; } KernelMemoryState; KernelMemoryState globalKernelMemoryState = {}; -static void KernelInitModeSetupBitmapAllocator(KernelInitModeMemoryState* memoryState) +static bool CheckMemoryReservationRange(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount) +{ + if ((pageOffset + pageCount) > memoryReservation->PageCount) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + return true; +} + +static void KernelInitModeSetupBitArrayAllocator() { auto platformInformation = PlatformGetInformation(); + auto memoryState = &globalKernelMemoryState.InitModeMemoryState; + globalKernelMemoryState.PageSize = platformInformation.SystemInformation.PageSize; memoryState->InitHeap = platformInformation.InitHeap; auto maxPageCount = memoryState->InitHeap.Length / globalKernelMemoryState.PageSize; - auto bitmapStorageSizeInBytes = (maxPageCount + 7) / 8; - memoryState->Bitmap = SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes); - auto bitmapStoragePageCount = (memoryState->Bitmap.Length + globalKernelMemoryState.PageSize - 1) / globalKernelMemoryState.PageSize; + auto bitmapStorageSizeInBytes = DivRoundUp(maxPageCount, 8); + auto bitmapStoragePageCount = DivRoundUp(bitmapStorageSizeInBytes, globalKernelMemoryState.PageSize); - MemorySet(memoryState->Bitmap, 0xFF); + memoryState->BitArray = CreateBitArrayWithBitCount(SpanCastSize(SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes)), maxPageCount); - for (uint32_t i = bitmapStoragePageCount; i < maxPageCount; i++) + for (uint32_t i = 0; i < maxPageCount; i++) { - // TODO: + if (i < bitmapStoragePageCount) + { + BitArraySet(memoryState->BitArray, i); + } + else + { + BitArrayReset(memoryState->BitArray, i); + } } - - KernelConsolePrint(String("HeapStart: %x Size:%d\n"), memoryState->InitHeap.Pointer, memoryState->InitHeap.Length); - KernelConsolePrint(String("MaxPageCount: %d Bitmap Storage Page Count:%d\n"), maxPageCount, bitmapStoragePageCount); } MemoryReservation KernelInitModeMemoryReservePages(size_t pageCount) { - if (pageCount == 0) - { - globalMemoryError = MemoryError_InvalidParameter; - return MEMORY_RESERVATION_EMPTY; - } - auto memoryState = &globalKernelMemoryState.InitModeMemoryState; if (memoryState->InitHeap.Pointer == nullptr) { - KernelInitModeSetupBitmapAllocator(memoryState); + KernelInitModeSetupBitArrayAllocator(); } - auto baseAddress = memoryState->InitHeap.Pointer; + auto freeIndex = BitArrayFindRangeNotSet(memoryState->BitArray, pageCount); - // TODO: + for (uint32_t i = 0; i < pageCount; i++) + { + BitArraySet(memoryState->BitArray, freeIndex + i); + } + auto baseAddress = memoryState->InitHeap.Pointer + (freeIndex * globalKernelMemoryState.PageSize); globalMemoryError = MemoryError_None; + globalKernelMemoryState.ReservedPages += pageCount; + return (MemoryReservation) { .BaseAddress = baseAddress, @@ -69,6 +88,31 @@ MemoryReservation KernelInitModeMemoryReservePages(size_t pageCount) }; } +bool KernelInitModeMemoryReleasePages(MemoryReservation* memoryReservation) +{ + auto memoryState = &globalKernelMemoryState.InitModeMemoryState; + + if (memoryState->InitHeap.Pointer == nullptr) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + auto startIndex = ((uint8_t*)memoryReservation->BaseAddress - memoryState->InitHeap.Pointer) / globalKernelMemoryState.PageSize; + + for (uint32_t i = 0; i < memoryReservation->PageCount; i++) + { + BitArrayReset(memoryState->BitArray, startIndex + i); + } + + globalKernelMemoryState.ReservedPages -= memoryReservation->PageCount; + + globalMemoryError = MemoryError_None; + *memoryReservation = (MemoryReservation){}; + + return true; +} + MemoryReservation KernelMemoryReservePages(size_t pageCount) { // TODO: @@ -78,8 +122,23 @@ MemoryReservation KernelMemoryReservePages(size_t pageCount) }; } +MemoryAllocationInfos MemoryGetAllocationInfos() +{ + return (MemoryAllocationInfos) + { + .CommittedPages = globalKernelMemoryState.CommittedPages, + .ReservedPages = globalKernelMemoryState.ReservedPages + }; +} + MemoryReservation MemoryReservePages(size_t pageCount) { + if (pageCount == 0) + { + globalMemoryError = MemoryError_InvalidParameter; + return MEMORY_RESERVATION_EMPTY; + } + if (!globalKernelMemoryState.MemoryTableInitialized) { return KernelInitModeMemoryReservePages(pageCount); @@ -90,15 +149,54 @@ MemoryReservation MemoryReservePages(size_t pageCount) bool MemoryRelease(MemoryReservation* memoryReservation) { + if (MemoryReservationIsEmpty(*memoryReservation)) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + return KernelInitModeMemoryReleasePages(memoryReservation); + } + + // TODO: Kernel Mode + return false; } bool MemoryCommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount, MemoryAccess access) { + if (!CheckMemoryReservationRange(memoryReservation, pageOffset, pageCount)) + { + return false; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + globalMemoryError = MemoryError_None; + return true; + } + + // TODO: Real MMU memory commit + return false; } bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount) { + if (!CheckMemoryReservationRange(memoryReservation, pageOffset, pageCount)) + { + return false; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + globalMemoryError = MemoryError_None; + return true; + } + + // TODO: Real MMU memory commit + return false; } diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index c01369e..a9716b8 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -101,6 +101,9 @@ void KernelInit() KernelConsoleResetStyle(); TestRun(KernelTestHandler, String("Types|Memory")); + + // TODO: Init the MMU + } void KernelMain() diff --git a/tests/Common/MemoryAllocationTests.c b/tests/Common/MemoryAllocationTests.c index e3c9d99..b67e183 100644 --- a/tests/Common/MemoryAllocationTests.c +++ b/tests/Common/MemoryAllocationTests.c @@ -8,15 +8,18 @@ Test(Memory, MemoryReserve_WithValidSize_ReturnsMemoryReservation) { // Arrange const size_t pageCount = 4; + auto beforeAllocationInfos = MemoryGetAllocationInfos(); // Act auto memoryReservation = MemoryReservePages(pageCount); // Assert + auto afterAllocationInfos = MemoryGetAllocationInfos(); TestAssertEquals(MemoryError_None, MemoryGetLastError()); TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation)); TestAssertEquals(pageCount, memoryReservation.PageCount); + TestAssertGreaterThan(afterAllocationInfos.ReservedPages, beforeAllocationInfos.ReservedPages); } Test(Memory, MemoryReserve_WithMultipleReservations_ReturnsDifferentMemoryReservations) @@ -136,13 +139,18 @@ Test(Memory, MemoryRelease_WithValidMemoryReservation_ReturnsTrue) // Arrange const size_t pageCount = 4; auto memoryReservation = MemoryReservePages(pageCount); + auto beforeAllocationInfos = MemoryGetAllocationInfos(); // Act auto result = MemoryRelease(&memoryReservation); // Assert + auto afterAllocationInfos = MemoryGetAllocationInfos(); + TestAssertEquals(MemoryError_None, MemoryGetLastError()); TestAssertIsTrue(result); + TestAssertIsTrue(MemoryReservationIsEmpty(memoryReservation)); + TestAssertGreaterThan(beforeAllocationInfos.ReservedPages, afterAllocationInfos.ReservedPages); } Test(Memory, MemoryRelease_WithInvalidMemoryReservation_ReturnsFalse) diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c index 8525aae..76fb654 100644 --- a/tests/Common/TypesTests.c +++ b/tests/Common/TypesTests.c @@ -170,7 +170,7 @@ Test(Types, BitArrayReset_WithIncorrectIndex_HasErrorSet) TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); } -Test(Types, BitArraySet_BitArrayFindFirstNotSet_HasCorrectValue) +Test(Types, BitArrayFindFirstNotSet_HasCorrectValue) { // Arrange const uint32_t spanLength = 2; @@ -192,3 +192,52 @@ Test(Types, BitArraySet_BitArrayFindFirstNotSet_HasCorrectValue) // Assert TestAssertEquals(bitMaxIndexToSet, result); } + +Test(Types, BitArrayFindRangeNotSet_WithCorrectLength_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t firstAvailableIndex = sizeof(size_t) + 3; + const uint32_t secondAvailableIndex = sizeof(size_t) + 10; + const uint32_t gap = 3; + + auto span = StackAllocSize(spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + for (uint32_t i = 0; i < firstAvailableIndex; i++) + { + BitArraySet(bitArray, i); + } + + for (uint32_t i = firstAvailableIndex + gap; i < secondAvailableIndex; i++) + { + BitArraySet(bitArray, i); + } + + // Act + auto result = BitArrayFindRangeNotSet(bitArray, gap + 2); + + // Assert + TestAssertEquals(secondAvailableIndex, result); +} + +Test(Types, BitArrayFindRangeNotSet_WithIncorrectLength_HasErrorSet) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t incorrectLength = (spanLength * sizeof(size_t) * BITS_PER_SIZE_TYPE) + 5; + + auto span = StackAllocSize(spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArrayFindRangeNotSet(bitArray, incorrectLength); + + // Assert + TestAssertEquals(SIZE_MAX, result); + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); +} From 7372b85247a9ab70a658993786c8d3448b32d288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 16 Jul 2025 05:49:59 +0200 Subject: [PATCH 10/20] WIP Memory Arena --- src/Common/Memory.c | 19 ++++++++++++++++ src/Common/Memory.h | 27 ++++++++++++++++++++++- tests/Common/MemoryAllocationTests.c | 1 + tests/Common/MemoryArenaTests.c | 33 ++++++++++++++++++++++++++++ tests/Common/UnityBuild.c | 1 + 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 tests/Common/MemoryArenaTests.c diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 171ff03..8e7a61f 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -57,6 +57,25 @@ void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLengt // MemoryArena //--------------------------------------------------------------------------------------- +MemoryArena CreateMemoryArena(size_t sizeInBytes) +{ + globalMemoryError = MemoryError_None; + return (MemoryArena) + { + }; +} + +void MemoryArenaRelease(MemoryArena memoryArena) +{ +} + +MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena) +{ + return (MemoryArenaAllocationInfos) + { + }; +} + // TODO: Move that to the standard library size_t strlen(const char* string) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 4fe5049..3889f74 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -88,7 +88,7 @@ typedef struct size_t ReservedPages; } MemoryAllocationInfos; -#define MEMORY_RESERVATION_EMPTY ((MemoryReservation){ .BaseAddress = nullptr, .PageCount = 0 }) +#define MEMORY_RESERVATION_EMPTY ((MemoryReservation) { .BaseAddress = nullptr, .PageCount = 0 }) static inline bool MemoryReservationIsEmpty(MemoryReservation memoryReservation) { @@ -107,3 +107,28 @@ bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t page // Memory Arena //--------------------------------------------------------------------------------------- +struct MemoryArenaStorage; + +typedef struct +{ + struct MemoryArenaStorage* Storage; +} MemoryArena; + +typedef struct +{ + size_t AllocatedBytes; + size_t CommittedBytes; + size_t MaximumSizeInBytes; +} MemoryArenaAllocationInfos; + +#define MEMORY_ARENA_EMPTY ((MemoryArena) { .Storage = nullptr }; + +static inline bool MemoryArenaIsEmpty(MemoryArena memoryArena) +{ + return memoryArena.Storage == nullptr; +} + +MemoryArena CreateMemoryArena(size_t sizeInBytes); +void MemoryArenaRelease(MemoryArena memoryArena); + +MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena); diff --git a/tests/Common/MemoryAllocationTests.c b/tests/Common/MemoryAllocationTests.c index b67e183..edfbb86 100644 --- a/tests/Common/MemoryAllocationTests.c +++ b/tests/Common/MemoryAllocationTests.c @@ -3,6 +3,7 @@ // TODO: For user mode only, we will wipe the committed pages to zero. So // we need to test that +// TODO: Check protection level for commit Test(Memory, MemoryReserve_WithValidSize_ReturnsMemoryReservation) { diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c new file mode 100644 index 0000000..7840146 --- /dev/null +++ b/tests/Common/MemoryArenaTests.c @@ -0,0 +1,33 @@ +#include "Memory.h" +#include "Test.h" + +Test(Memory, CreateMemoryArena_WithValidSize_ReturnsMemoryArena) +{ + // Arrange + const size_t memoryArenaSize = 1024; + + // Act + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsFalse(MemoryArenaIsEmpty(memoryArena)); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + TestAssertEquals(memoryArenaSize, allocationInfos.MaximumSizeInBytes); +} + +Test(Memory, CreateMemoryArena_WithInvalidSize_ReturnsEmptyMemoryArena) +{ + // Arrange + const size_t memoryArenaSize = 0; + + // Act + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsTrue(MemoryArenaIsEmpty(memoryArena)); +} + diff --git a/tests/Common/UnityBuild.c b/tests/Common/UnityBuild.c index a32edc9..0bbc2a9 100644 --- a/tests/Common/UnityBuild.c +++ b/tests/Common/UnityBuild.c @@ -1,5 +1,6 @@ #include "TypesTests.c" #include "MemoryTests.c" #include "MemoryAllocationTests.c" +#include "MemoryArenaTests.c" #include "StringTests.c" From bad6f1f6c62e2675720c55291ceb82bbe45f5d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 5 Aug 2025 15:53:15 +0200 Subject: [PATCH 11/20] WIP MemoryArena --- src/Common/Memory.c | 116 ++++++++++++++++++++++++++++++- src/Common/Memory.h | 11 ++- tests/Common/MemoryArenaTests.c | 117 ++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 3 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 8e7a61f..f301af2 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -1,6 +1,10 @@ #include "Memory.h" +#include "System.h" +#include "Types.h" // TODO: This will need to be thread local +// TODO: Implement memory arena multi-threading + MemoryError globalMemoryError = MemoryError_None; //--------------------------------------------------------------------------------------- @@ -57,25 +61,135 @@ void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLengt // MemoryArena //--------------------------------------------------------------------------------------- +typedef struct MemoryArenaStorage +{ + MemoryReservation MemoryReservation; + BitArray PageCommittedStatus; + SpanUint8 DataSpan; + uint8_t* CurrentPointer; + size_t CommittedBytes; +} MemoryArenaStorage; + MemoryArena CreateMemoryArena(size_t sizeInBytes) { + if (sizeInBytes == 0) + { + globalMemoryError = MemoryError_InvalidParameter; + return MEMORY_ARENA_EMPTY; + } + + auto systemInformation = SystemGetInformation(); + + auto dataPageCount = DivRoundUp(sizeInBytes, systemInformation.PageSize); + auto committedStatusBitArraySizeInBytes = DivRoundUp(dataPageCount, 8); + + auto headerSizeInBytes = sizeof(MemoryArenaStorage) + committedStatusBitArraySizeInBytes; + auto headerPageCount = DivRoundUp(headerSizeInBytes, systemInformation.PageSize); + + auto memoryReservation = MemoryReservePages(headerPageCount + dataPageCount); + + if (MemoryReservationIsEmpty(memoryReservation)) + { + return MEMORY_ARENA_EMPTY; + } + + MemoryCommitPages(&memoryReservation, 0, headerPageCount, MemoryAccess_ReadWrite); + + auto storage = (MemoryArenaStorage*)memoryReservation.BaseAddress; + *storage = (MemoryArenaStorage){}; + storage->MemoryReservation = memoryReservation; + storage->DataSpan = CreateSpanUint8((uint8_t*)memoryReservation.BaseAddress + headerSizeInBytes, sizeInBytes); + + auto committedStatusBitArrayData = SpanCastSize(CreateSpanUint8((uint8_t*)memoryReservation.BaseAddress + sizeof(MemoryArenaStorage), committedStatusBitArraySizeInBytes)); + storage->PageCommittedStatus = CreateBitArrayWithBitCount(committedStatusBitArrayData, dataPageCount); + storage->CurrentPointer = storage->DataSpan.Pointer; + globalMemoryError = MemoryError_None; + return (MemoryArena) { + .Storage = storage }; } -void MemoryArenaRelease(MemoryArena memoryArena) +bool MemoryArenaRelease(MemoryArena* memoryArena) { + if (MemoryArenaIsEmpty(*memoryArena)) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + MemoryRelease(&memoryArena->Storage->MemoryReservation); + + *memoryArena = MEMORY_ARENA_EMPTY; + globalMemoryError = MemoryError_None; + + return true; } MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena) { + auto storage = memoryArena.Storage; + return (MemoryArenaAllocationInfos) { + .AllocatedBytes = storage->CurrentPointer - storage->DataSpan.Pointer, + .CommittedBytes = storage->CommittedBytes, + .MaximumSizeInBytes = storage->DataSpan.Length }; } +SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes) +{ + auto span = MemoryArenaPushReserved(memoryArena, sizeInBytes); + MemoryArenaCommit(memoryArena, span); + + return span; +} + +SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) +{ + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + if (allocationInfos.AllocatedBytes + sizeInBytes > allocationInfos.MaximumSizeInBytes) + { + globalMemoryError = MemoryError_OutOfMemory; + return CreateSpanUint8(nullptr, 0); + } + + auto span = CreateSpanUint8(memoryArena.Storage->CurrentPointer, sizeInBytes); + + memoryArena.Storage->CurrentPointer += sizeInBytes; + globalMemoryError = MemoryError_None; + + return span; +} + +bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) +{ + auto storage = memoryArena.Storage; + auto systemInformation = SystemGetInformation(); + + // TODO: check calculations + + auto pageOffset = (range.Pointer - storage->DataSpan.Pointer) / systemInformation.PageSize; + auto pageCount = DivRoundUp(range.Length, systemInformation.PageSize); + + for (uint32_t i = 0; i < pageCount; i++) + { + // TODO: optimize + if (!BitArrayIsSet(storage->PageCommittedStatus, pageOffset + i)) + { + MemoryCommitPages(&storage->MemoryReservation, pageOffset, 1, MemoryAccess_ReadWrite); + BitArraySet(storage->PageCommittedStatus, pageOffset + i); + storage->CommittedBytes += systemInformation.PageSize; + } + } + + return false; +} + // TODO: Move that to the standard library size_t strlen(const char* string) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 3889f74..8e99892 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -121,7 +121,7 @@ typedef struct size_t MaximumSizeInBytes; } MemoryArenaAllocationInfos; -#define MEMORY_ARENA_EMPTY ((MemoryArena) { .Storage = nullptr }; +#define MEMORY_ARENA_EMPTY ((MemoryArena) { .Storage = nullptr }); static inline bool MemoryArenaIsEmpty(MemoryArena memoryArena) { @@ -129,6 +129,13 @@ static inline bool MemoryArenaIsEmpty(MemoryArena memoryArena) } MemoryArena CreateMemoryArena(size_t sizeInBytes); -void MemoryArenaRelease(MemoryArena memoryArena); +bool MemoryArenaRelease(MemoryArena* memoryArena); MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena); + +SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes); +SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes); +//bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); +//bool MemoryArenaClear(MemoryArena memoryArena); + +bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range); diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index 7840146..6bae04c 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -16,6 +16,7 @@ Test(Memory, CreateMemoryArena_WithValidSize_ReturnsMemoryArena) auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertEquals(memoryArenaSize, allocationInfos.MaximumSizeInBytes); + TestAssertEquals(0, allocationInfos.AllocatedBytes); } Test(Memory, CreateMemoryArena_WithInvalidSize_ReturnsEmptyMemoryArena) @@ -31,3 +32,119 @@ Test(Memory, CreateMemoryArena_WithInvalidSize_ReturnsEmptyMemoryArena) TestAssertIsTrue(MemoryArenaIsEmpty(memoryArena)); } +Test(Memory, MemoryArenaRelease_WithValidArena_ReturnsTrue) +{ + // Arrange + const size_t memoryArenaSize = 1024; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryGetAllocationInfos(); + + // Act + auto result = MemoryArenaRelease(&memoryArena); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + TestAssertIsTrue(MemoryArenaIsEmpty(memoryArena)); + + auto afterAllocationInfos = MemoryGetAllocationInfos(); + TestAssertGreaterThan(beforeAllocationInfos.ReservedPages, afterAllocationInfos.ReservedPages); +} + +Test(Memory, MemoryArenaRelease_WithInvalidArena_ReturnsFalse) +{ + // Arrange + auto memoryArena = (MemoryArena) {}; + + // Act + auto result = MemoryArenaRelease(&memoryArena); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} + +Test(Memory, MemoryArenaPush_WithValidSize_ReturnsValidSpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPush(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, span.Pointer); + TestAssertEquals(pushSize, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertEquals(pushSize, afterAllocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPush_WithInvalidSize_ReturnsEmptySpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 2048; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPush(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_OutOfMemory, MemoryGetLastError()); + TestAssertEquals(nullptr, span.Pointer); + TestAssertEquals(0, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(0, afterAllocationInfos.AllocatedBytes); + TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); +} + +Test(Memory, MemoryArenaPushReserved_WithValidSize_ReturnsValidSpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, span.Pointer); + TestAssertEquals(pushSize, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertEquals(pushSize, afterAllocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPushReserved_WithInvalidSize_ReturnsEmptySpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 2048; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_OutOfMemory, MemoryGetLastError()); + TestAssertEquals(nullptr, span.Pointer); + TestAssertEquals(0, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(0, afterAllocationInfos.AllocatedBytes); + TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); +} + From 69600f8671f233ad7a79f3efa41d5bfe6cdbd993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 3 Sep 2025 04:55:20 +0200 Subject: [PATCH 12/20] WIP memory --- src/Common/Memory.c | 14 +++++- src/Common/Memory.h | 23 ++++++++++ tests/Common/MemoryArenaTests.c | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index f301af2..e84b78a 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -143,7 +143,11 @@ MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes) { auto span = MemoryArenaPushReserved(memoryArena, sizeInBytes); - MemoryArenaCommit(memoryArena, span); + + if (span.Pointer) + { + MemoryArenaCommit(memoryArena, span); + } return span; } @@ -171,6 +175,11 @@ bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) auto storage = memoryArena.Storage; auto systemInformation = SystemGetInformation(); + if (range.Pointer > memoryArena.Storage->CurrentPointer || (range.Pointer + range.Length) > memoryArena.Storage->CurrentPointer) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } // TODO: check calculations auto pageOffset = (range.Pointer - storage->DataSpan.Pointer) / systemInformation.PageSize; @@ -187,7 +196,8 @@ bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) } } - return false; + globalMemoryError = MemoryError_None; + return true; } // TODO: Move that to the standard library diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 8e99892..94239ae 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -135,6 +135,29 @@ MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes); SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes); + +#define MemoryArenaPushStruct(memoryArena, structType) ((structType*)MemoryArenaPush((memoryArena), sizeof(structType)).Pointer) +//#define MemoryArenaPushArray(memoryArena, structType, count) CreateSpan##structType(((structType*)MemoryArenaPush((memoryArena), sizeof(structType) * (count)).Pointer), (count)) + + +/* ---------- choose the right CreateSpanXxx helper at compile-time ---------- */ +#define _SpanFactory(T) \ + _Generic(((T*)0), \ + uint8_t *: CreateSpanUint8, \ + uint32_t *: CreateSpanUint32, \ + uint64_t *: CreateSpanUint64, \ + /* add a line here the day you need uint128_t, size_t, etc. */ \ + default : CreateSpan##T) + +/* --------------------------- public-facing macro --------------------------- */ +#define MemoryArenaPushArray(arena, T, count) \ + ({ \ + size_t _n = (count); \ + void *_p = MemoryArenaPush((arena), sizeof(T)*_n).Pointer; \ + _SpanFactory(T)((T*)_p, _n); \ + }) + + //bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); //bool MemoryArenaClear(MemoryArena memoryArena); diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index 6bae04c..6a16033 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -1,5 +1,6 @@ #include "Memory.h" #include "Test.h" +#include "Types.h" Test(Memory, CreateMemoryArena_WithValidSize_ReturnsMemoryArena) { @@ -106,6 +107,46 @@ Test(Memory, MemoryArenaPush_WithInvalidSize_ReturnsEmptySpan) TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); } +Test(Memory, MemoryArenaPushStruct_WithValidStruct_ReturnsValidStruct) +{ + // Arrange + const size_t memoryArenaSize = 1024; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaPushStruct(memoryArena, uint32_t); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertEquals(sizeof(uint32_t), afterAllocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const uint32_t arrayCount = 5; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaPushArray(memoryArena, uint32_t, arrayCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(arrayCount, result.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertEquals(sizeof(uint32_t), afterAllocationInfos.AllocatedBytes); +} + Test(Memory, MemoryArenaPushReserved_WithValidSize_ReturnsValidSpan) { // Arrange @@ -148,3 +189,42 @@ Test(Memory, MemoryArenaPushReserved_WithInvalidSize_ReturnsEmptySpan) TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); } +Test(Memory, MemoryArenaCommit_WithValidRange_CommitMemory) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaCommit(memoryArena, SpanSlice(span, 64, 128)); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); +} + +Test(Memory, MemoryArenaCommit_WithInvalidRange_CommitMemory) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaCommit(memoryArena, SpanSlice(span, 512, 128)); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); +} From 53ec24d92493cbf7966dfd6e9bbff9396051e2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 3 Sep 2025 13:38:53 +0200 Subject: [PATCH 13/20] Fix build --- cmake/Toolchains/riscv-common.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/Toolchains/riscv-common.cmake b/cmake/Toolchains/riscv-common.cmake index bf23671..4b95197 100644 --- a/cmake/Toolchains/riscv-common.cmake +++ b/cmake/Toolchains/riscv-common.cmake @@ -19,3 +19,7 @@ endif() set(CMAKE_C_FLAGS "${_COMMON} -march=${_MARCH} -mabi=${_MABI} ${_MODEL}") set(CMAKE_ASM_FLAGS "--target=${COMPILE_TARGET} -march=${_MARCH} -mabi=${_MABI} -Wno-unused-command-line-argument -x assembler-with-cpp") +set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_C_LINK_FLAGS_INIT "-fuse-ld=lld") From a7ccdc1a0821d18d0abecb4b491174eb4d30cc28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 15 Sep 2025 12:03:39 +0200 Subject: [PATCH 14/20] Refactor Span types --- src/Common/Memory.c | 8 ++--- src/Common/Memory.h | 23 ++----------- src/Common/String.h | 3 +- src/Common/Test.c | 4 +-- src/Common/Types.h | 49 +++++++++++---------------- src/Kernel/Kernel.c | 2 +- src/Kernel/KernelConsole.c | 6 ++-- src/Kernel/KernelMemory.c | 2 +- src/Kernel/KernelTest.c | 2 +- src/Kernel/Platforms/RiscV/Platform.c | 12 +++---- tests/Common/MemoryArenaTests.c | 6 ++-- tests/Common/MemoryTests.c | 16 ++++----- tests/Common/StringTests.c | 6 ++-- tests/Common/TypesTests.c | 24 ++++++------- 14 files changed, 66 insertions(+), 97 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index e84b78a..1992f6d 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -98,9 +98,9 @@ MemoryArena CreateMemoryArena(size_t sizeInBytes) auto storage = (MemoryArenaStorage*)memoryReservation.BaseAddress; *storage = (MemoryArenaStorage){}; storage->MemoryReservation = memoryReservation; - storage->DataSpan = CreateSpanUint8((uint8_t*)memoryReservation.BaseAddress + headerSizeInBytes, sizeInBytes); + storage->DataSpan = CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + headerSizeInBytes, sizeInBytes); - auto committedStatusBitArrayData = SpanCastSize(CreateSpanUint8((uint8_t*)memoryReservation.BaseAddress + sizeof(MemoryArenaStorage), committedStatusBitArraySizeInBytes)); + auto committedStatusBitArrayData = SpanCast(size_t, CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + sizeof(MemoryArenaStorage), committedStatusBitArraySizeInBytes)); storage->PageCommittedStatus = CreateBitArrayWithBitCount(committedStatusBitArrayData, dataPageCount); storage->CurrentPointer = storage->DataSpan.Pointer; @@ -159,10 +159,10 @@ SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) if (allocationInfos.AllocatedBytes + sizeInBytes > allocationInfos.MaximumSizeInBytes) { globalMemoryError = MemoryError_OutOfMemory; - return CreateSpanUint8(nullptr, 0); + return CreateSpan(uint8_t, nullptr, 0); } - auto span = CreateSpanUint8(memoryArena.Storage->CurrentPointer, sizeInBytes); + auto span = CreateSpan(uint8_t, memoryArena.Storage->CurrentPointer, sizeInBytes); memoryArena.Storage->CurrentPointer += sizeInBytes; globalMemoryError = MemoryError_None; diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 94239ae..52f51fc 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -136,27 +136,8 @@ MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes); SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes); -#define MemoryArenaPushStruct(memoryArena, structType) ((structType*)MemoryArenaPush((memoryArena), sizeof(structType)).Pointer) -//#define MemoryArenaPushArray(memoryArena, structType, count) CreateSpan##structType(((structType*)MemoryArenaPush((memoryArena), sizeof(structType) * (count)).Pointer), (count)) - - -/* ---------- choose the right CreateSpanXxx helper at compile-time ---------- */ -#define _SpanFactory(T) \ - _Generic(((T*)0), \ - uint8_t *: CreateSpanUint8, \ - uint32_t *: CreateSpanUint32, \ - uint64_t *: CreateSpanUint64, \ - /* add a line here the day you need uint128_t, size_t, etc. */ \ - default : CreateSpan##T) - -/* --------------------------- public-facing macro --------------------------- */ -#define MemoryArenaPushArray(arena, T, count) \ - ({ \ - size_t _n = (count); \ - void *_p = MemoryArenaPush((arena), sizeof(T)*_n).Pointer; \ - _SpanFactory(T)((T*)_p, _n); \ - }) - +#define MemoryArenaPushStruct(type, memoryArena) ((type*)MemoryArenaPush((memoryArena), sizeof(type)).Pointer) +#define MemoryArenaPushArray(type, memoryArena, count) CreateSpan(type, ((type*)MemoryArenaPush((memoryArena), sizeof(type) * (count)).Pointer), (count)) //bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); //bool MemoryArenaClear(MemoryArena memoryArena); diff --git a/src/Common/String.h b/src/Common/String.h index 91bbda2..8033de3 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -4,11 +4,10 @@ #include "Memory.h" DefineSpan(String, ReadOnlySpanChar) -#define StackAllocString(length) DefineSpanStackAlloc(String, ReadOnlySpanChar, (length)) static inline ReadOnlySpanChar String(const char* string) { - return CreateReadOnlySpanChar(string, __builtin_strlen(string)); + return CreateReadOnlySpan(char, string, __builtin_strlen(string)); } bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2); diff --git a/src/Common/Test.c b/src/Common/Test.c index 1de82f4..3557cf8 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -34,7 +34,7 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) if (categoryFilters.Length > 0) { - auto splittedFilters = StackAllocString(64); + auto splittedFilters = StackAlloc(ReadOnlySpanChar, 64); StringSplit(&splittedFilters, categoryFilters, '|'); auto testCanRun = false; @@ -77,7 +77,7 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) if (test->HasError) { - handler(TestRunState_Failed, ToReadOnlySpanChar(globalTestLastErrorMessage)); + handler(TestRunState_Failed, ToReadOnlySpan(char, globalTestLastErrorMessage)); failedCounter++; } else diff --git a/src/Common/Types.h b/src/Common/Types.h index 5d76b7f..d88ab4b 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -85,58 +85,41 @@ typedef enum typedef struct Span##name { type* Pointer; size_t Length; } Span##name; \ typedef struct ReadOnlySpan##name { const type* Pointer; size_t Length; } ReadOnlySpan##name; \ \ - static inline Span##name CreateSpan##name(type* pointer, size_t length) \ + static inline Span##name _CREATE_SPAN_##type(type* pointer, size_t length) \ { \ return (Span##name) { .Pointer = pointer, .Length = length }; \ } \ \ - static inline ReadOnlySpan##name CreateReadOnlySpan##name(const type* pointer, size_t length) \ + static inline ReadOnlySpan##name _CREATE_READONLY_SPAN_##type(const type* pointer, size_t length) \ { \ return (ReadOnlySpan##name) { .Pointer = pointer, .Length = length }; \ } \ \ - static inline ReadOnlySpan##name ToReadOnlySpan##name(Span##name span) \ - { \ - return (ReadOnlySpan##name) { .Pointer = span.Pointer, .Length = span.Length }; \ - } \ - \ - static inline Span##name _SPAN_CAST_##name(size_t sourceStride, void* sourcePointer, size_t sourceLength, const type* unused) \ + static inline Span##name _SPAN_CAST_##type(size_t sourceStride, void* sourcePointer, size_t sourceLength, const type* unused) \ { \ (void)unused; \ size_t bytes = sourceStride * sourceLength; \ - return CreateSpan##name((type *)sourcePointer, bytes / sizeof(type)); \ + return CreateSpan(type, (type*)sourcePointer, bytes / sizeof(type)); \ } -#define DefineSpanStackAlloc(name, type, length) \ +#define CreateSpan(type, pointer, length) _CREATE_SPAN_##type(pointer, length) +#define CreateReadOnlySpan(type, pointer, length) _CREATE_READONLY_SPAN_##type(pointer, length) + +#define ToReadOnlySpan(type, span) _CREATE_READONLY_SPAN_##type((span).Pointer, (span).Length) + +#define StackAlloc(type, length) \ (__extension__ ({ \ static_assert((length) >= 0, "StackAlloc: length must be an integer-constant expression"); \ type array[(length)]; \ - CreateSpan##name(array, (size_t)(length)); \ + CreateSpan(type, array, (size_t)(length)); \ })) -#define DefineSpanCast(name, type, sourceSpan) \ - _SPAN_CAST_##name(sizeof(*(sourceSpan).Pointer), \ +#define SpanCast(type, sourceSpan) \ + _SPAN_CAST_##type(sizeof(*(sourceSpan).Pointer), \ (sourceSpan).Pointer, \ (sourceSpan).Length, \ (type*)nullptr) -DefineSpan(Char, char) -#define StackAllocChar(length) DefineSpanStackAlloc(Char, char, (length)) - -DefineSpan(Uint8, uint8_t) -#define StackAllocUint8(length) DefineSpanStackAlloc(Uint8, uint8_t, (length)) - -DefineSpan(Uint32, uint32_t) -#define StackAllocUint32(length) DefineSpanStackAlloc(Uint32, uint32_t, (length)) -#define SpanCastUint32(sourceSpan) DefineSpanCast(Uint32, uint32_t, (sourceSpan)) - -DefineSpan(Uint64, uint64_t) -#define StackAllocUint64(length) DefineSpanStackAlloc(Uint64, uint64_t, (length)) - -DefineSpan(Size, size_t) -#define StackAllocSize(length) DefineSpanStackAlloc(Size, size_t, (length)) -#define SpanCastSize(sourceSpan) DefineSpanCast(Size, size_t, (sourceSpan)) - #define SpanSlice(span, offset, length) \ ( \ (typeof(span)) \ @@ -149,6 +132,12 @@ DefineSpan(Size, size_t) #define SpanSliceFrom(span, offset) SpanSlice((span), (offset), (span).Length - (offset)) #define SpanAt(span, index) (span).Pointer[(index)] +DefineSpan(Char, char) +DefineSpan(Uint8, uint8_t) +DefineSpan(Uint32, uint32_t) +DefineSpan(Uint64, uint64_t) +DefineSpan(Size, size_t) + //--------------------------------------------------------------------------------------- // BitArray //--------------------------------------------------------------------------------------- diff --git a/src/Kernel/Kernel.c b/src/Kernel/Kernel.c index 8a1ae46..12807d2 100644 --- a/src/Kernel/Kernel.c +++ b/src/Kernel/Kernel.c @@ -12,7 +12,7 @@ void KernelFailureCore(ReadOnlySpanChar file, uint32_t line, ReadOnlySpanChar me va_list vargs; va_start(vargs, message); - auto tmp = StackAllocChar(256); + auto tmp = StackAlloc(char, 256); StringFormatVargs(&tmp, message, vargs); KernelConsolePrint(String("%s\n\n"), tmp); diff --git a/src/Kernel/KernelConsole.c b/src/Kernel/KernelConsole.c index 27eefb0..f5826c9 100644 --- a/src/Kernel/KernelConsole.c +++ b/src/Kernel/KernelConsole.c @@ -5,7 +5,7 @@ void KernelConsolePrint(ReadOnlySpanChar message, ...) { - auto output = StackAllocChar(2048); + auto output = StackAlloc(char, 2048); va_list vargs; va_start(vargs, message); @@ -14,7 +14,7 @@ void KernelConsolePrint(ReadOnlySpanChar message, ...) va_end(vargs); - BiosDebugConsoleWrite(ToReadOnlySpanChar(output)); + BiosDebugConsoleWrite(ToReadOnlySpan(char, output)); } void KernelConsoleSetForegroundColor(Color color) @@ -85,7 +85,7 @@ void FormatBoxedMessage(SpanChar destination, ReadOnlySpanChar message) void KernelConsolePrintBoxMessage(ReadOnlySpanChar message) { - auto boxedMessage = StackAllocChar(512); + auto boxedMessage = StackAlloc(char, 512); FormatBoxedMessage(boxedMessage, message); KernelConsolePrint(String("\n%s\n"), boxedMessage); } diff --git a/src/Kernel/KernelMemory.c b/src/Kernel/KernelMemory.c index fb897bd..f69754f 100644 --- a/src/Kernel/KernelMemory.c +++ b/src/Kernel/KernelMemory.c @@ -45,7 +45,7 @@ static void KernelInitModeSetupBitArrayAllocator() auto bitmapStorageSizeInBytes = DivRoundUp(maxPageCount, 8); auto bitmapStoragePageCount = DivRoundUp(bitmapStorageSizeInBytes, globalKernelMemoryState.PageSize); - memoryState->BitArray = CreateBitArrayWithBitCount(SpanCastSize(SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes)), maxPageCount); + memoryState->BitArray = CreateBitArrayWithBitCount(SpanCast(size_t, SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes)), maxPageCount); for (uint32_t i = 0; i < maxPageCount; i++) { diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index a9716b8..b6f8377 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -81,7 +81,7 @@ void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) va_list vargs; va_start(vargs, message); - auto tmp = StackAllocChar(256); + auto tmp = StackAlloc(char, 256); StringFormatVargs(&tmp, message, vargs); KernelConsolePrint(String(" %s\n"), tmp); diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index 4e4afaf..483c8e9 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -29,7 +29,7 @@ PlatformInformation PlatformGetInformation() .PageSize = RISCV_MEMORY_PAGESIZE }, .BootCpuId = globalBootHartId, - .InitHeap = CreateSpanUint8(__INIT_HEAP_START, __INIT_HEAP_END - __INIT_HEAP_START) + .InitHeap = CreateSpan(uint8_t, __INIT_HEAP_START, __INIT_HEAP_END - __INIT_HEAP_START) }; } @@ -146,7 +146,7 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) if (testNode == 0x01) { - auto name = StackAllocChar(1024); + auto name = StackAlloc(char, 1024); BinaryReadString(reader, &name); BinarySetOffset(reader, AlignUp(reader->CurrentOffset, 4)); @@ -161,13 +161,13 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) auto length = BinaryReadUint32(reader); auto nameOffset = BinaryReadUint32(reader); - auto value = StackAllocUint8(1024); + auto value = StackAlloc(uint8_t, 1024); BinaryReadBytes(reader, length, &value); auto offset = reader->CurrentOffset; BinarySetOffset(reader, stringDataOffset + nameOffset); - auto name = StackAllocChar(1024); + auto name = StackAlloc(char, 1024); BinaryReadString(reader, &name); BinarySetOffset(reader, AlignUp(offset, 4)); @@ -183,7 +183,7 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) PlatformDevices PlatformGetDevices() { - auto dtbHeaderData = CreateReadOnlySpanUint8((uint8_t*)globalDeviceTreeData, sizeof(uint32_t) * 2); + auto dtbHeaderData = CreateReadOnlySpan(uint8_t, (uint8_t*)globalDeviceTreeData, sizeof(uint32_t) * 2); auto dtbMagic = ConvertBytesToUint32(dtbHeaderData, ByteOrder_BigEndian); auto sizeInBytes = ConvertBytesToUint32(SpanSliceFrom(dtbHeaderData, sizeof(uint32_t)), ByteOrder_BigEndian); @@ -194,7 +194,7 @@ PlatformDevices PlatformGetDevices() // TODO: Parse reserved memory area? - auto dataSpan = CreateReadOnlySpanUint8((const uint8_t*)globalDeviceTreeData, sizeInBytes); + auto dataSpan = CreateReadOnlySpan(uint8_t, (const uint8_t*)globalDeviceTreeData, sizeInBytes); auto reader = CreateBinaryReader(dataSpan, ByteOrder_BigEndian); BinarySetOffset(&reader, sizeof(uint32_t) * 2); diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index 6a16033..600af6f 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -115,7 +115,7 @@ Test(Memory, MemoryArenaPushStruct_WithValidStruct_ReturnsValidStruct) auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); // Act - auto result = MemoryArenaPushStruct(memoryArena, uint32_t); + auto result = MemoryArenaPushStruct(uint32_t, memoryArena); // Assert TestAssertEquals(MemoryError_None, MemoryGetLastError()); @@ -135,7 +135,7 @@ Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); // Act - auto result = MemoryArenaPushArray(memoryArena, uint32_t, arrayCount); + auto result = MemoryArenaPushArray(uint32_t, memoryArena, arrayCount); // Assert TestAssertEquals(MemoryError_None, MemoryGetLastError()); @@ -144,7 +144,7 @@ Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); - TestAssertEquals(sizeof(uint32_t), afterAllocationInfos.AllocatedBytes); + TestAssertEquals(sizeof(uint32_t) * arrayCount, afterAllocationInfos.AllocatedBytes); } Test(Memory, MemoryArenaPushReserved_WithValidSize_ReturnsValidSpan) diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index 7c8be23..e733fea 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -9,7 +9,7 @@ Test(Memory, MemorySet_WithUint32_HasCorrectValues) const uint32_t itemCount = 10; const uint32_t initialValue = 28; - auto destination = StackAllocUint32(itemCount); + auto destination = StackAlloc(uint32_t, itemCount); // Act MemorySet(destination, initialValue); @@ -27,7 +27,7 @@ Test(Memory, MemorySet_WithUint8_HasCorrectValues) const uint8_t itemCount = 10; const uint8_t initialValue = 28; - auto destination = StackAllocUint8(itemCount); + auto destination = StackAlloc(uint8_t, itemCount); // Act MemorySet(destination, initialValue); @@ -43,18 +43,18 @@ Test(Memory, MemoryCopy_WithUint32_HasCorrectValues) { // Arrange const uint8_t itemCount = 10; - auto source = StackAllocUint32(itemCount); + auto source = StackAlloc(uint32_t, itemCount); for (uint32_t i = 0; i < itemCount; i++) { SpanAt(source, i) = i; } - auto destination = StackAllocUint32(itemCount); + auto destination = StackAlloc(uint32_t, itemCount); MemorySet(destination, 0); // Act - MemoryCopy(destination, ToReadOnlySpanUint32(source)); + MemoryCopy(destination, ToReadOnlySpan(uint32_t, source)); // Assert for (uint32_t i = 0; i < itemCount; i++) @@ -67,18 +67,18 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) { // Arrange const uint8_t itemCount = 10; - auto source = StackAllocUint8(itemCount); + auto source = StackAlloc(uint8_t, itemCount); for (uint32_t i = 0; i < itemCount; i++) { SpanAt(source, i) = i; } - auto destination = StackAllocUint8(itemCount); + auto destination = StackAlloc(uint8_t, itemCount); MemorySet(destination, 0); // Act - MemoryCopy(destination, ToReadOnlySpanUint8(source)); + MemoryCopy(destination, ToReadOnlySpan(uint8_t, source)); // Assert for (uint32_t i = 0; i < itemCount; i++) diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index 96bbebc..450488c 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -49,7 +49,7 @@ Test(String, StringSplit_WithParameters_HasCorrectValues) { // Arrange const auto testString = String("Test1|Test2|Test3"); - auto destination = StackAllocString(64); + auto destination = StackAlloc(ReadOnlySpanChar, 64); // Act StringSplit(&destination, testString, '|'); @@ -69,12 +69,12 @@ Test(String, StringFormat_WithParameters_HasCorrectValues) const auto stringParameter = String("TestParameter"); const auto finalString = String("Test: 28, TestParameter"); - auto destination = StackAllocChar(64); + auto destination = StackAlloc(char, 64); // Act StringFormat(&destination, testString, intParameter, stringParameter); // Assert - TestAssertStringEquals(finalString, ToReadOnlySpanChar(destination)); + TestAssertStringEquals(finalString, ToReadOnlySpan(char, destination)); } diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c index 76fb654..4aec1b9 100644 --- a/tests/Common/TypesTests.c +++ b/tests/Common/TypesTests.c @@ -12,7 +12,7 @@ Test(Types, SpanSlice_WithSpan_HasCorrectValues) const uint32_t sliceOffset = 2; const uint32_t sliceLength = 5; - auto span = StackAllocUint32(itemCount); + auto span = StackAlloc(uint32_t, itemCount); for (uint32_t i = 0; i < itemCount; i++) { @@ -37,7 +37,7 @@ Test(Types, SpanSliceFrom_WithSpan_HasCorrectValues) const uint32_t itemCount = 10; const uint32_t sliceOffset = 2; - auto span = StackAllocUint32(itemCount); + auto span = StackAlloc(uint32_t, itemCount); for (uint32_t i = 0; i < itemCount; i++) { @@ -61,10 +61,10 @@ Test(Types, SpanCast_WithSpanUint8ToUint32_HasCorrectValues) // Arrange const uint32_t itemCount = 12; - auto span = StackAllocUint8(itemCount); + auto span = StackAlloc(uint8_t, itemCount); // Act - auto result = SpanCastUint32(span); + auto result = SpanCast(uint32_t, span); // Assert TestAssertEquals(itemCount / sizeof(uint32_t), result.Length); @@ -76,7 +76,7 @@ Test(Types, CreateBitArray_WithCorrectBitCount_ReturnsBitArray) // Arrange const uint32_t spanLength = 2; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); // Act auto bitArray = CreateBitArray(span); @@ -94,7 +94,7 @@ Test(Types, BitArraySet_WithCorrectIndex_HasCorrectValue) const uint32_t spanLength = 2; const uint32_t bitIndexToSet = 5; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); auto bitArray = CreateBitArray(span); @@ -116,7 +116,7 @@ Test(Types, BitArraySet_WithIncorrectIndex_HasErrorSet) const uint32_t spanLength = 2; const uint32_t bitIndexToSet = sizeof(size_t) * 8 * 3; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); auto bitArray = CreateBitArray(span); @@ -135,7 +135,7 @@ Test(Types, BitArrayReset_WithCorrectIndex_HasCorrectValue) const uint32_t spanLength = 2; const uint32_t bitIndexToSet = 5; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 1); auto bitArray = CreateBitArray(span); @@ -157,7 +157,7 @@ Test(Types, BitArrayReset_WithIncorrectIndex_HasErrorSet) const uint32_t spanLength = 2; const uint32_t bitIndexToSet = sizeof(size_t) * 8 * 3; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 1); auto bitArray = CreateBitArray(span); @@ -176,7 +176,7 @@ Test(Types, BitArrayFindFirstNotSet_HasCorrectValue) const uint32_t spanLength = 2; const uint32_t bitMaxIndexToSet = sizeof(size_t) + 3; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); auto bitArray = CreateBitArray(span); @@ -201,7 +201,7 @@ Test(Types, BitArrayFindRangeNotSet_WithCorrectLength_HasCorrectValue) const uint32_t secondAvailableIndex = sizeof(size_t) + 10; const uint32_t gap = 3; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); auto bitArray = CreateBitArray(span); @@ -229,7 +229,7 @@ Test(Types, BitArrayFindRangeNotSet_WithIncorrectLength_HasErrorSet) const uint32_t spanLength = 2; const uint32_t incorrectLength = (spanLength * sizeof(size_t) * BITS_PER_SIZE_TYPE) + 5; - auto span = StackAllocSize(spanLength); + auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); auto bitArray = CreateBitArray(span); From fbad334f8402b57207028bb10c2fe4a258065ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 15 Sep 2025 14:40:11 +0200 Subject: [PATCH 15/20] Implement MemoryArena Pop and Clear. --- src/Common/Memory.c | 20 ++++++++++++ src/Common/Memory.h | 4 +-- src/Common/Types.h | 14 +-------- tests/Common/MemoryArenaTests.c | 56 +++++++++++++++++++++++++++++++++ tests/Common/TypesTests.c | 10 +++--- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 1992f6d..a93519e 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -170,6 +170,26 @@ SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) return span; } +bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes) +{ + if (memoryArena.Storage->CurrentPointer - memoryArena.Storage->DataSpan.Pointer < sizeInBytes) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + memoryArena.Storage->CurrentPointer -= sizeInBytes; + globalMemoryError = MemoryError_None; + + return true; +} + +void MemoryArenaClear(MemoryArena memoryArena) +{ + memoryArena.Storage->CurrentPointer = memoryArena.Storage->DataSpan.Pointer; + globalMemoryError = MemoryError_None; +} + bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) { auto storage = memoryArena.Storage; diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 52f51fc..558f129 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -139,7 +139,7 @@ SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes); #define MemoryArenaPushStruct(type, memoryArena) ((type*)MemoryArenaPush((memoryArena), sizeof(type)).Pointer) #define MemoryArenaPushArray(type, memoryArena, count) CreateSpan(type, ((type*)MemoryArenaPush((memoryArena), sizeof(type) * (count)).Pointer), (count)) -//bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); -//bool MemoryArenaClear(MemoryArena memoryArena); +bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); +void MemoryArenaClear(MemoryArena memoryArena); bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range); diff --git a/src/Common/Types.h b/src/Common/Types.h index d88ab4b..05c8662 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -94,18 +94,12 @@ typedef enum { \ return (ReadOnlySpan##name) { .Pointer = pointer, .Length = length }; \ } \ - \ - static inline Span##name _SPAN_CAST_##type(size_t sourceStride, void* sourcePointer, size_t sourceLength, const type* unused) \ - { \ - (void)unused; \ - size_t bytes = sourceStride * sourceLength; \ - return CreateSpan(type, (type*)sourcePointer, bytes / sizeof(type)); \ - } #define CreateSpan(type, pointer, length) _CREATE_SPAN_##type(pointer, length) #define CreateReadOnlySpan(type, pointer, length) _CREATE_READONLY_SPAN_##type(pointer, length) #define ToReadOnlySpan(type, span) _CREATE_READONLY_SPAN_##type((span).Pointer, (span).Length) +#define SpanCast(type, span) CreateSpan(type, (type*)(span).Pointer, (sizeof(*(span).Pointer) * (span).Length) / sizeof(type)) #define StackAlloc(type, length) \ (__extension__ ({ \ @@ -114,12 +108,6 @@ typedef enum CreateSpan(type, array, (size_t)(length)); \ })) -#define SpanCast(type, sourceSpan) \ - _SPAN_CAST_##type(sizeof(*(sourceSpan).Pointer), \ - (sourceSpan).Pointer, \ - (sourceSpan).Length, \ - (type*)nullptr) - #define SpanSlice(span, offset, length) \ ( \ (typeof(span)) \ diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index 600af6f..14532cf 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -189,6 +189,62 @@ Test(Memory, MemoryArenaPushReserved_WithInvalidSize_ReturnsEmptySpan) TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); } +Test(Memory, MemoryArenaPop_WithValidSize_ReturnsTrue) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + MemoryArenaPush(memoryArena, pushSize); + + // Act + auto result = MemoryArenaPop(memoryArena, pushSize / 2); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(pushSize / 2, allocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPop_WithInvalidSize_ReturnsFalse) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + MemoryArenaPush(memoryArena, pushSize); + + // Act + auto result = MemoryArenaPop(memoryArena, pushSize * 2); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(pushSize, allocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaClear_ResetAllocatedMemory) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + MemoryArenaPush(memoryArena, pushSize); + + // Act + MemoryArenaClear(memoryArena); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(0, allocationInfos.AllocatedBytes); +} + Test(Memory, MemoryArenaCommit_WithValidRange_CommitMemory) { // Arrange diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c index 4aec1b9..551467b 100644 --- a/tests/Common/TypesTests.c +++ b/tests/Common/TypesTests.c @@ -155,7 +155,7 @@ Test(Types, BitArrayReset_WithIncorrectIndex_HasErrorSet) { // Arrange const uint32_t spanLength = 2; - const uint32_t bitIndexToSet = sizeof(size_t) * 8 * 3; + const uint32_t bitIndexToSet = BITS_PER_SIZE_TYPE * 24; auto span = StackAlloc(size_t, spanLength); MemorySet(span, 1); @@ -174,7 +174,7 @@ Test(Types, BitArrayFindFirstNotSet_HasCorrectValue) { // Arrange const uint32_t spanLength = 2; - const uint32_t bitMaxIndexToSet = sizeof(size_t) + 3; + const uint32_t bitMaxIndexToSet = BITS_PER_SIZE_TYPE+ 3; auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); @@ -197,8 +197,8 @@ Test(Types, BitArrayFindRangeNotSet_WithCorrectLength_HasCorrectValue) { // Arrange const uint32_t spanLength = 2; - const uint32_t firstAvailableIndex = sizeof(size_t) + 3; - const uint32_t secondAvailableIndex = sizeof(size_t) + 10; + const uint32_t firstAvailableIndex = BITS_PER_SIZE_TYPE + 3; + const uint32_t secondAvailableIndex = BITS_PER_SIZE_TYPE + 10; const uint32_t gap = 3; auto span = StackAlloc(size_t, spanLength); @@ -227,7 +227,7 @@ Test(Types, BitArrayFindRangeNotSet_WithIncorrectLength_HasErrorSet) { // Arrange const uint32_t spanLength = 2; - const uint32_t incorrectLength = (spanLength * sizeof(size_t) * BITS_PER_SIZE_TYPE) + 5; + const uint32_t incorrectLength = (spanLength * BITS_PER_SIZE_TYPE) + 5; auto span = StackAlloc(size_t, spanLength); MemorySet(span, 0); From 37f5b80ec2d88787aa8c4dc28e9af4d45b031489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 17 Sep 2025 15:40:33 +0200 Subject: [PATCH 16/20] WIP StackMemoryArena --- src/Common/Memory.c | 177 ++++++++++++++++++++++---------- src/Common/Memory.h | 101 ++++++++++-------- src/Common/Types.h | 2 + src/Kernel/KernelConsole.c | 1 + tests/Common/MemoryArenaTests.c | 27 +++++ tests/Common/MemoryTests.c | 71 ++++++++++++- 6 files changed, 281 insertions(+), 98 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index a93519e..d8d0d37 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -7,56 +7,6 @@ MemoryError globalMemoryError = MemoryError_None; -//--------------------------------------------------------------------------------------- -// Span -//--------------------------------------------------------------------------------------- - -void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value) -{ - (void)stride; - uint8_t byteValue = *(uint8_t*)value; - __builtin_memset(destination, byteValue, destinationLength); -} - -void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value) -{ - uint8_t* pointer = destination; - - for (size_t i = 0; i < destinationLength; i++) - { - for (size_t j = 0; j < stride; j++) - { - pointer[(i * stride) + j] = ((uint8_t*)value)[j]; - } - } -} - -void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength) -{ - (void)stride; - - // TODO: Check length - (void)destinationLength; - - __builtin_memcpy(destination, source, sourceLength); -} - -void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength) -{ - uint8_t* pointer = destination; - - // TODO: Check length - (void)destinationLength; - - for (size_t i = 0; i < sourceLength; i++) - { - for (size_t j = 0; j < stride; j++) - { - pointer[(i * stride) + j] = ((uint8_t*)source)[(i * stride) + j]; - } - } -} - //--------------------------------------------------------------------------------------- // MemoryArena //--------------------------------------------------------------------------------------- @@ -70,12 +20,15 @@ typedef struct MemoryArenaStorage size_t CommittedBytes; } MemoryArenaStorage; -MemoryArena CreateMemoryArena(size_t sizeInBytes) +// TODO: This will need to be thread local +MemoryArenaStorage* globalStackMemoryArenaStorage; + +MemoryArenaStorage* CreateMemoryArenaStorage(size_t sizeInBytes) { if (sizeInBytes == 0) { globalMemoryError = MemoryError_InvalidParameter; - return MEMORY_ARENA_EMPTY; + return nullptr; } auto systemInformation = SystemGetInformation(); @@ -90,7 +43,7 @@ MemoryArena CreateMemoryArena(size_t sizeInBytes) if (MemoryReservationIsEmpty(memoryReservation)) { - return MEMORY_ARENA_EMPTY; + return nullptr; } MemoryCommitPages(&memoryReservation, 0, headerPageCount, MemoryAccess_ReadWrite); @@ -106,6 +59,18 @@ MemoryArena CreateMemoryArena(size_t sizeInBytes) globalMemoryError = MemoryError_None; + return storage; +} + +MemoryArena CreateMemoryArena(size_t sizeInBytes) +{ + auto storage = CreateMemoryArenaStorage(sizeInBytes); + + if (!storage) + { + return MEMORY_ARENA_EMPTY; + } + return (MemoryArena) { .Storage = storage @@ -172,7 +137,7 @@ SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes) { - if (memoryArena.Storage->CurrentPointer - memoryArena.Storage->DataSpan.Pointer < sizeInBytes) + if ((size_t)(memoryArena.Storage->CurrentPointer - memoryArena.Storage->DataSpan.Pointer) < sizeInBytes) { globalMemoryError = MemoryError_InvalidParameter; return false; @@ -220,6 +185,110 @@ bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) return true; } +MemoryArena CreateStackMemoryArena() +{ + if (!globalStackMemoryArenaStorage) + { + globalStackMemoryArenaStorage = CreateMemoryArenaStorage(1024); + } + + return (MemoryArena) + { + .Storage = globalStackMemoryArenaStorage, + .StackStartPointer = globalStackMemoryArenaStorage->CurrentPointer + }; +} + +void ReleaseStackMemoryArena(void* pointer) +{ + auto stackMemoryArena = (MemoryArena*)pointer; + //MemoryArenaPop(*stackMemoryArena, (stackMemoryArena->Storage->CurrentPointer - stackMemoryArena->StackStartPointer)); + MemoryArenaPop(*stackMemoryArena, 10); +} + +//--------------------------------------------------------------------------------------- +// General +//--------------------------------------------------------------------------------------- + +void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value) +{ + (void)stride; + uint8_t byteValue = *(uint8_t*)value; + __builtin_memset(destination, byteValue, destinationLength); +} + +void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value) +{ + uint8_t* pointer = destination; + + for (size_t i = 0; i < destinationLength; i++) + { + for (size_t j = 0; j < stride; j++) + { + pointer[(i * stride) + j] = ((uint8_t*)value)[j]; + } + } +} + +void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength) +{ + (void)stride; + + // TODO: Check length + (void)destinationLength; + + __builtin_memcpy(destination, source, sourceLength); +} + +void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength) +{ + uint8_t* pointer = destination; + + // TODO: Check length + (void)destinationLength; + + for (size_t i = 0; i < sourceLength; i++) + { + for (size_t j = 0; j < stride; j++) + { + pointer[(i * stride) + j] = ((uint8_t*)source)[(i * stride) + j]; + } + } +} + +void* MemoryConcatChar(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length) +{ + auto destination = MemoryArenaPush(memoryArena, source1Length + source2Length + 1); + + MemoryCopyByte(stride, destination.Pointer, source1Length, source1, source1Length); + MemoryCopyByte(stride, destination.Pointer + (source1Length * stride), source2Length, source2, source2Length); + + SpanAt(destination, source1Length + source2Length) = 0; + + return destination.Pointer; +} + +void* MemoryConcatByte(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length) +{ + auto destination = MemoryArenaPush(memoryArena, source1Length + source2Length); + + MemoryCopyByte(stride, destination.Pointer, source1Length, source1, source1Length); + MemoryCopyByte(stride, destination.Pointer + (source1Length * stride), source2Length, source2, source2Length); + + return destination.Pointer; +} + +void* MemoryConcatDefault(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length) +{ + auto destination = MemoryArenaPush(memoryArena, stride * (source1Length + source2Length)); + + MemoryCopyDefault(stride, destination.Pointer, source1Length, source1, source1Length); + MemoryCopyDefault(stride, destination.Pointer + (source1Length * stride), source2Length, source2, source2Length); + + return destination.Pointer; +} + + // TODO: Move that to the standard library size_t strlen(const char* string) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 558f129..55ea1a0 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -21,48 +21,6 @@ static inline MemoryError MemoryGetLastError() return globalMemoryError; } -//--------------------------------------------------------------------------------------- -// General -//--------------------------------------------------------------------------------------- - -void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value); -void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value); - -#define MemorySet(destination, value) \ - _Generic((destination).Pointer, \ - char*: MemorySetByte, \ - uint8_t*: MemorySetByte, \ - default: MemorySetDefault \ - )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, &(typeof(*(destination).Pointer)){ (value) }) - - -// TODO: Add Errors - -void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); -void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); - -#define _MemoryCopyDispatch(destination, source) \ - _Generic((destination).Pointer, \ - char*: MemoryCopyByte, \ - uint8_t*: MemoryCopyByte, \ - default: MemoryCopyDefault \ - )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, (source).Pointer, (source).Length) - - -#define _IS_READONLY_SPAN(span) \ - __builtin_types_compatible_p( \ - typeof((span).Pointer), \ - const typeof(*(span).Pointer) *) - -#define _ASSERT_READONLY_SPAN(source) \ - static_assert(_IS_READONLY_SPAN(source), "MemoryCopy: source span must be read-only") - -#define MemoryCopy(destination, source) \ - do { \ - _ASSERT_READONLY_SPAN(source); \ - _MemoryCopyDispatch((destination), (source)); \ - } while (false) - //--------------------------------------------------------------------------------------- // Memory Allocation //--------------------------------------------------------------------------------------- @@ -112,6 +70,7 @@ struct MemoryArenaStorage; typedef struct { struct MemoryArenaStorage* Storage; + uint8_t* StackStartPointer; } MemoryArena; typedef struct @@ -143,3 +102,61 @@ bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); void MemoryArenaClear(MemoryArena memoryArena); bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range); + + +MemoryArena CreateStackMemoryArena(); +void ReleaseStackMemoryArena(void* pointer); + +#define GetStackMemoryArena() \ + (__extension__ ({ \ + [[gnu::cleanup(ReleaseStackMemoryArena)]] auto stackMemoryArena = CreateStackMemoryArena(); \ + stackMemoryArena; \ + })) + +//--------------------------------------------------------------------------------------- +// General +//--------------------------------------------------------------------------------------- + +void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value); +void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value); + +#define MemorySet(destination, value) \ + _Generic((destination).Pointer, \ + char*: MemorySetByte, \ + uint8_t*: MemorySetByte, \ + default: MemorySetDefault \ + )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, &(typeof(*(destination).Pointer)){ (value) }) + + +// TODO: Add Errors + +void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); +void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); + +#define MemoryCopy(destination, source) \ + _Generic((destination).Pointer, \ + char*: MemoryCopyByte, \ + uint8_t*: MemoryCopyByte, \ + default: MemoryCopyDefault \ + )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, (source).Pointer, (source).Length) + +void* MemoryConcatChar(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length); +void* MemoryConcatByte(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length); +void* MemoryConcatDefault(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length); + +// TODO: It works but the only drawback now is that if source1 is a ReadOnlySpan, it will create a ReadOnlySpan as a result +#define MemoryConcat(memoryArena, source1, source2) \ + (__extension__ ({ \ + auto result = _Generic((source1).Pointer, \ + char*: MemoryConcatChar, \ + const char*: MemoryConcatChar, \ + uint8_t*: MemoryConcatByte, \ + const uint8_t*: MemoryConcatByte, \ + default: MemoryConcatDefault \ + )(memoryArena, sizeof(*(source1).Pointer), (source1).Pointer, (source1).Length, (source2).Pointer, (source2).Length); \ + (typeof(source1)) \ + { \ + .Pointer = result, \ + .Length = (source1).Length + (source2.Length) \ + };\ + })) diff --git a/src/Common/Types.h b/src/Common/Types.h index 05c8662..2165e60 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -98,7 +98,9 @@ typedef enum #define CreateSpan(type, pointer, length) _CREATE_SPAN_##type(pointer, length) #define CreateReadOnlySpan(type, pointer, length) _CREATE_READONLY_SPAN_##type(pointer, length) +// TODO: It would be nice if we could ommit the type for this function because we know it already #define ToReadOnlySpan(type, span) _CREATE_READONLY_SPAN_##type((span).Pointer, (span).Length) + #define SpanCast(type, span) CreateSpan(type, (type*)(span).Pointer, (sizeof(*(span).Pointer) * (span).Length) / sizeof(type)) #define StackAlloc(type, length) \ diff --git a/src/Kernel/KernelConsole.c b/src/Kernel/KernelConsole.c index f5826c9..7fadd24 100644 --- a/src/Kernel/KernelConsole.c +++ b/src/Kernel/KernelConsole.c @@ -85,6 +85,7 @@ void FormatBoxedMessage(SpanChar destination, ReadOnlySpanChar message) void KernelConsolePrintBoxMessage(ReadOnlySpanChar message) { + // TODO: Use the stack memory arena here auto boxedMessage = StackAlloc(char, 512); FormatBoxedMessage(boxedMessage, message); KernelConsolePrint(String("\n%s\n"), boxedMessage); diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index 14532cf..ddaa9b8 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -284,3 +284,30 @@ Test(Memory, MemoryArenaCommit_WithInvalidRange_CommitMemory) auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertEquals(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); } + +Test(Memory, GetStackMemoryArena_WithDifferentScopes_UsesSeparateMemoryArena) +{ + // Arrange + auto stackMemoryArena1 = GetStackMemoryArena(); + auto string1 = MemoryConcat(stackMemoryArena1, String("Test1"), String("Stack1")); + + MemoryArenaAllocationInfos beforeAllocationInfos; + ReadOnlySpanChar string2; + ReadOnlySpanChar string5; + + // Act + { + auto stackMemoryArena2 = GetStackMemoryArena(); + string2 = MemoryConcat(stackMemoryArena1, String("Test2"), String("Stack1")); + MemoryConcat(stackMemoryArena2, String("Test1"), String("Stack2")); + + beforeAllocationInfos = MemoryArenaGetAllocationInfos(stackMemoryArena1); + } + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(stackMemoryArena1); + + // Assert + TestAssertStringEquals(String("Test1Stack1"), string1); + TestAssertStringEquals(String("Test2Stack1"), string2); + TestAssertGreaterThan(beforeAllocationInfos.AllocatedBytes, afterAllocationInfos.AllocatedBytes); +} diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index e733fea..ae46d9a 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -54,7 +54,7 @@ Test(Memory, MemoryCopy_WithUint32_HasCorrectValues) MemorySet(destination, 0); // Act - MemoryCopy(destination, ToReadOnlySpan(uint32_t, source)); + MemoryCopy(destination, source); // Assert for (uint32_t i = 0; i < itemCount; i++) @@ -78,7 +78,7 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) MemorySet(destination, 0); // Act - MemoryCopy(destination, ToReadOnlySpan(uint8_t, source)); + MemoryCopy(destination, source); // Assert for (uint32_t i = 0; i < itemCount; i++) @@ -87,3 +87,70 @@ Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) } } +Test(Memory, MemoryConcat_WithUint8_HasCorrectValues) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const uint8_t itemCount = 10; + const uint8_t source1Value = 5; + const uint8_t source2Value = 28; + + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + auto source1 = StackAlloc(uint8_t, itemCount); + MemorySet(source1, source1Value); + + auto source2 = StackAlloc(uint8_t, itemCount); + MemorySet(source2, source2Value); + + // Act + auto result = MemoryConcat(memoryArena, ToReadOnlySpan(uint8_t, source1), source2); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(itemCount * 2, result.Length); + + for (uint32_t i = 0; i < itemCount; i++) + { + TestAssertEquals(source1Value, SpanAt(result, i)); + TestAssertEquals(source2Value, SpanAt(result, itemCount + i)); + } +} + +Test(Memory, MemoryConcat_WithChar_HasCorrectValues) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const uint8_t itemCount = 10; + const uint8_t defaultValue = 255; + const uint8_t source1Value = 5; + const uint8_t source2Value = 28; + + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto data = MemoryArenaPush(memoryArena, memoryArenaSize); + MemorySet(data, defaultValue); + MemoryArenaClear(memoryArena); + + auto source1 = StackAlloc(char, itemCount); + MemorySet(source1, source1Value); + + auto source2 = StackAlloc(char, itemCount); + MemorySet(source2, source2Value); + + // Act + auto result = MemoryConcat(memoryArena, source1, source2); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(itemCount * 2, result.Length); + + for (uint32_t i = 0; i < itemCount; i++) + { + TestAssertEquals(source1Value, SpanAt(result, i)); + TestAssertEquals(source2Value, SpanAt(result, itemCount + i)); + } + + TestAssertEquals(0, SpanAt(result, result.Length)); +} From 82197306de3e0bfba8a639c444bb8f2ae843ed71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 20 Sep 2025 11:35:59 +0200 Subject: [PATCH 17/20] Implement StackMemoryArena --- src/Common/Console.c | 77 +++++++++++++++++ src/Common/Console.h | 23 +++++ src/Common/Memory.c | 116 +++++++++++++++++++++----- src/Common/Memory.h | 34 ++++---- src/Common/String.h | 2 + src/Common/Types.h | 18 ++-- src/Common/UnityBuild.c | 1 + src/Kernel/Kernel.c | 12 +-- src/Kernel/KernelConsole.c | 78 +---------------- src/Kernel/KernelConsole.h | 23 ----- src/Kernel/KernelMain.c | 22 ++--- src/Kernel/KernelMemory.c | 4 +- src/Kernel/KernelTest.c | 43 +++++----- src/Kernel/Platforms/RiscV/Cpu.c | 40 ++++----- src/Kernel/Platforms/RiscV/Kernel.ld | 24 +++--- src/Kernel/Platforms/RiscV/Platform.c | 10 +-- tests/Common/MemoryArenaTests.c | 47 +++++++++-- tests/Common/MemoryTests.c | 31 +++++++ 18 files changed, 375 insertions(+), 230 deletions(-) create mode 100644 src/Common/Console.c create mode 100644 src/Common/Console.h delete mode 100644 src/Kernel/KernelConsole.h diff --git a/src/Common/Console.c b/src/Common/Console.c new file mode 100644 index 0000000..057eccd --- /dev/null +++ b/src/Common/Console.c @@ -0,0 +1,77 @@ +#include "Console.h" + +void FormatBoxedMessage(SpanChar destination, ReadOnlySpanChar message) +{ + auto upLeftCorner = String("┌"); + auto upRightCorner = String("┐"); + auto downLeftCorner = String("└"); + auto downRightCorner = String("┘"); + auto horizontalLine = String("─"); + auto verticalLine = String("│"); + + MemoryCopy(destination, upLeftCorner); + destination = SpanSliceFrom(destination, upLeftCorner.Length); + + for (uint32_t i = 0; i < message.Length + 2; i++) + { + MemoryCopy(destination, horizontalLine); + destination = SpanSliceFrom(destination, horizontalLine.Length); + } + + MemoryCopy(destination, upRightCorner); + destination = SpanSliceFrom(destination, upRightCorner.Length); + + MemoryCopy(destination, String("\n")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, verticalLine); + destination = SpanSliceFrom(destination, verticalLine.Length); + + MemoryCopy(destination, String(" ")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, message); + destination = SpanSliceFrom(destination, message.Length); + + MemoryCopy(destination, String(" ")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, verticalLine); + destination = SpanSliceFrom(destination, verticalLine.Length); + + MemoryCopy(destination, String("\n")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, downLeftCorner); + destination = SpanSliceFrom(destination, downLeftCorner.Length); + + for (uint32_t i = 0; i < message.Length + 2; i++) + { + MemoryCopy(destination, horizontalLine); + destination = SpanSliceFrom(destination, horizontalLine.Length); + } + + MemoryCopy(destination, downRightCorner); + destination = SpanSliceFrom(destination, downRightCorner.Length); + + // TODO: There is a problem here with null terminator not present +} + +void ConsoleSetForegroundColor(Color color) +{ + ConsolePrint(String("\x1b[38;2;%d;%d;%dm"), (int32_t)color.Red, (int32_t)color.Green, (int32_t)color.Blue); +} + +void ConsoleResetStyle() +{ + ConsolePrint(String("\x1b[0m")); + ConsoleSetForegroundColor(ConsoleColorNormal); +} + +void ConsolePrintBoxMessage(ReadOnlySpanChar message) +{ + // TODO: Use the stack memory arena here + auto boxedMessage = StackAlloc(char, 512); + FormatBoxedMessage(boxedMessage, message); + ConsolePrint(String("\n%s\n"), boxedMessage); +} diff --git a/src/Common/Console.h b/src/Common/Console.h new file mode 100644 index 0000000..70b77d5 --- /dev/null +++ b/src/Common/Console.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Memory.h" +#include "String.h" +#include "Types.h" + +const Color ConsoleColorNormal = { 212, 212, 212, 255 }; +const Color ConsoleColorHighlight = { 250, 250, 250, 255 }; +const Color ConsoleColorAccent = { 79, 193, 255, 255 }; +const Color ConsoleColorSuccess = { 106, 153, 85, 255 }; +const Color ConsoleColorWarning = { 255, 135, 100, 255 }; +const Color ConsoleColorError = { 255, 105, 105, 255 }; +const Color ConsoleColorInfo = { 220, 220, 170, 255 }; +const Color ConsoleColorAction = { 197, 134, 192, 255 }; +const Color ConsoleColorKeyword = { 86, 156, 214, 255 }; +const Color ConsoleColorNumeric = { 181, 206, 168, 255 }; + +void ConsolePrint(ReadOnlySpanChar message, ...); + +void ConsoleSetForegroundColor(Color color); +void ConsoleResetStyle(); + +void ConsolePrintBoxMessage(ReadOnlySpanChar message); diff --git a/src/Common/Memory.c b/src/Common/Memory.c index d8d0d37..c217458 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -1,6 +1,8 @@ #include "Memory.h" +#include "Console.h" #include "System.h" #include "Types.h" +#include "String.h" // TODO: This will need to be thread local // TODO: Implement memory arena multi-threading @@ -18,10 +20,13 @@ typedef struct MemoryArenaStorage SpanUint8 DataSpan; uint8_t* CurrentPointer; size_t CommittedBytes; + uint8_t StackLevel; + uint8_t StackMinAllocatedLevel; } MemoryArenaStorage; // TODO: This will need to be thread local -MemoryArenaStorage* globalStackMemoryArenaStorage; +MemoryArenaStorage* globalStackMemoryArenaStorage = nullptr; +MemoryArenaStorage* globalStackMemoryArenaExtraStorage = nullptr; MemoryArenaStorage* CreateMemoryArenaStorage(size_t sizeInBytes) { @@ -34,9 +39,10 @@ MemoryArenaStorage* CreateMemoryArenaStorage(size_t sizeInBytes) auto systemInformation = SystemGetInformation(); auto dataPageCount = DivRoundUp(sizeInBytes, systemInformation.PageSize); - auto committedStatusBitArraySizeInBytes = DivRoundUp(dataPageCount, 8); + auto committedStatusBitArraySizeInBytes = AlignUp(DivRoundUp(dataPageCount, 8), alignof(size_t)); - auto headerSizeInBytes = sizeof(MemoryArenaStorage) + committedStatusBitArraySizeInBytes; + auto storageSize = AlignUp(sizeof(MemoryArenaStorage), alignof(size_t)); + auto headerSizeInBytes = storageSize + committedStatusBitArraySizeInBytes; auto headerPageCount = DivRoundUp(headerSizeInBytes, systemInformation.PageSize); auto memoryReservation = MemoryReservePages(headerPageCount + dataPageCount); @@ -53,15 +59,38 @@ MemoryArenaStorage* CreateMemoryArenaStorage(size_t sizeInBytes) storage->MemoryReservation = memoryReservation; storage->DataSpan = CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + headerSizeInBytes, sizeInBytes); - auto committedStatusBitArrayData = SpanCast(size_t, CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + sizeof(MemoryArenaStorage), committedStatusBitArraySizeInBytes)); + auto committedStatusBitArrayData = SpanCast(size_t, CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + storageSize, committedStatusBitArraySizeInBytes)); storage->PageCommittedStatus = CreateBitArrayWithBitCount(committedStatusBitArrayData, dataPageCount); storage->CurrentPointer = storage->DataSpan.Pointer; + storage->StackLevel = 0; + storage->StackMinAllocatedLevel = UINT8_MAX; globalMemoryError = MemoryError_None; return storage; } +MemoryArenaStorage* GetMemoryArenaWorkingStorage(MemoryArena memoryArena) +{ + auto storage = memoryArena.Storage; + + if (memoryArena.StackLevel != storage->StackLevel) + { + if (!globalStackMemoryArenaExtraStorage) + { + // TODO: Replace the size by a constant depending on the boot phase + // TODO: Do an util method for bytes + globalStackMemoryArenaExtraStorage = CreateMemoryArenaStorage(1024); + } + + // TODO: Replace by a min function + storage->StackMinAllocatedLevel = memoryArena.StackLevel < storage->StackMinAllocatedLevel ? memoryArena.StackLevel : storage->StackMinAllocatedLevel; + return globalStackMemoryArenaExtraStorage; + } + + return storage; +} + MemoryArena CreateMemoryArena(size_t sizeInBytes) { auto storage = CreateMemoryArenaStorage(sizeInBytes); @@ -119,7 +148,10 @@ SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes) SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) { - auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + sizeInBytes = AlignUp(sizeInBytes, sizeof(uintptr_t)); + + auto storage = GetMemoryArenaWorkingStorage(memoryArena); + auto allocationInfos = MemoryArenaGetAllocationInfos((MemoryArena){ .Storage = storage }); if (allocationInfos.AllocatedBytes + sizeInBytes > allocationInfos.MaximumSizeInBytes) { @@ -127,9 +159,9 @@ SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) return CreateSpan(uint8_t, nullptr, 0); } - auto span = CreateSpan(uint8_t, memoryArena.Storage->CurrentPointer, sizeInBytes); + auto span = CreateSpan(uint8_t, storage->CurrentPointer, sizeInBytes); - memoryArena.Storage->CurrentPointer += sizeInBytes; + storage->CurrentPointer += sizeInBytes; globalMemoryError = MemoryError_None; return span; @@ -151,32 +183,36 @@ bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes) void MemoryArenaClear(MemoryArena memoryArena) { + // TODO: Do nothing for stack + memoryArena.Storage->CurrentPointer = memoryArena.Storage->DataSpan.Pointer; globalMemoryError = MemoryError_None; } bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) { - auto storage = memoryArena.Storage; + auto storage = GetMemoryArenaWorkingStorage(memoryArena); auto systemInformation = SystemGetInformation(); - if (range.Pointer > memoryArena.Storage->CurrentPointer || (range.Pointer + range.Length) > memoryArena.Storage->CurrentPointer) + if (range.Pointer > storage->CurrentPointer || (range.Pointer + range.Length) > storage->CurrentPointer) { globalMemoryError = MemoryError_InvalidParameter; return false; } - // TODO: check calculations - auto pageOffset = (range.Pointer - storage->DataSpan.Pointer) / systemInformation.PageSize; + auto headerPageCount = ((uintptr_t)storage->DataSpan.Pointer - (uintptr_t)storage->MemoryReservation.BaseAddress) / systemInformation.PageSize; + auto dataPageOffset = ((uintptr_t)range.Pointer - (uintptr_t)storage->DataSpan.Pointer) / systemInformation.PageSize; + + auto pageOffset = headerPageCount + dataPageOffset; auto pageCount = DivRoundUp(range.Length, systemInformation.PageSize); for (uint32_t i = 0; i < pageCount; i++) { // TODO: optimize - if (!BitArrayIsSet(storage->PageCommittedStatus, pageOffset + i)) + if (!BitArrayIsSet(storage->PageCommittedStatus, dataPageOffset + i)) { - MemoryCommitPages(&storage->MemoryReservation, pageOffset, 1, MemoryAccess_ReadWrite); - BitArraySet(storage->PageCommittedStatus, pageOffset + i); + MemoryCommitPages(&storage->MemoryReservation, pageOffset + i, 1, MemoryAccess_ReadWrite); + BitArraySet(storage->PageCommittedStatus, dataPageOffset + i); storage->CommittedBytes += systemInformation.PageSize; } } @@ -185,25 +221,59 @@ bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) return true; } -MemoryArena CreateStackMemoryArena() +MemoryArena GetStackMemoryArena() { if (!globalStackMemoryArenaStorage) { + // TODO: Replace the size by a constant depending on the boot phase + // TODO: Do an util method for bytes globalStackMemoryArenaStorage = CreateMemoryArenaStorage(1024); } + globalStackMemoryArenaStorage->StackLevel++; + return (MemoryArena) { .Storage = globalStackMemoryArenaStorage, - .StackStartPointer = globalStackMemoryArenaStorage->CurrentPointer + .StackStartPointer = globalStackMemoryArenaStorage->CurrentPointer, + .StackExtraStartPointer = globalStackMemoryArenaExtraStorage ? globalStackMemoryArenaExtraStorage->CurrentPointer : nullptr, + .StackLevel = globalStackMemoryArenaStorage->StackLevel }; } -void ReleaseStackMemoryArena(void* pointer) +void ReleaseStackMemoryArena(MemoryArena* stackMemoryArena) { - auto stackMemoryArena = (MemoryArena*)pointer; - //MemoryArenaPop(*stackMemoryArena, (stackMemoryArena->Storage->CurrentPointer - stackMemoryArena->StackStartPointer)); - MemoryArenaPop(*stackMemoryArena, 10); + if (!stackMemoryArena || !stackMemoryArena->Storage) + { + return; + } + + auto storage = stackMemoryArena->Storage; + + if (globalStackMemoryArenaExtraStorage && storage->StackMinAllocatedLevel >= stackMemoryArena->StackLevel) + { + size_t extraBytesToPop = (size_t)(globalStackMemoryArenaExtraStorage->CurrentPointer - stackMemoryArena->StackExtraStartPointer); + + if (extraBytesToPop) + { + // TODO: Do something cleaner? + MemoryArenaPop((MemoryArena){ .Storage = globalStackMemoryArenaExtraStorage }, extraBytesToPop); + } + + storage->StackMinAllocatedLevel = UINT8_MAX; + } + + storage->StackLevel--; + + auto bytesToPop = (size_t)(storage->CurrentPointer - stackMemoryArena->StackStartPointer); + + if (bytesToPop) + { + MemoryArenaPop(*stackMemoryArena, bytesToPop); + } + + stackMemoryArena->StackStartPointer = storage->CurrentPointer; + stackMemoryArena->StackExtraStartPointer = globalStackMemoryArenaExtraStorage ? globalStackMemoryArenaExtraStorage->CurrentPointer : nullptr; } //--------------------------------------------------------------------------------------- @@ -273,7 +343,7 @@ void* MemoryConcatByte(MemoryArena memoryArena, size_t stride, const void* sourc auto destination = MemoryArenaPush(memoryArena, source1Length + source2Length); MemoryCopyByte(stride, destination.Pointer, source1Length, source1, source1Length); - MemoryCopyByte(stride, destination.Pointer + (source1Length * stride), source2Length, source2, source2Length); + MemoryCopyByte(stride, destination.Pointer + source1Length, source2Length, source2, source2Length); return destination.Pointer; } @@ -303,12 +373,14 @@ size_t strlen(const char* string) return (size_t)(pointer - string); } -void memset(uint8_t* destination, uint8_t value, size_t sizeInBytes) +void* memset(uint8_t* destination, uint8_t value, size_t sizeInBytes) { for (size_t i = 0; i < sizeInBytes; i++) { destination[i] = value; } + + return destination; } void* memcpy(uint8_t* destination, const uint8_t* source, size_t sizeInBytes) diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 55ea1a0..64b7bf2 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -71,6 +71,8 @@ typedef struct { struct MemoryArenaStorage* Storage; uint8_t* StackStartPointer; + uint8_t* StackExtraStartPointer; + uint8_t StackLevel; } MemoryArena; typedef struct @@ -103,15 +105,10 @@ void MemoryArenaClear(MemoryArena memoryArena); bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range); +MemoryArena GetStackMemoryArena(); +void ReleaseStackMemoryArena(MemoryArena* stackMemoryArena); -MemoryArena CreateStackMemoryArena(); -void ReleaseStackMemoryArena(void* pointer); - -#define GetStackMemoryArena() \ - (__extension__ ({ \ - [[gnu::cleanup(ReleaseStackMemoryArena)]] auto stackMemoryArena = CreateStackMemoryArena(); \ - stackMemoryArena; \ - })) +#define StackMemoryArena(name) [[gnu::cleanup(ReleaseStackMemoryArena)]] MemoryArena name = GetStackMemoryArena(); //--------------------------------------------------------------------------------------- // General @@ -146,17 +143,14 @@ void* MemoryConcatDefault(MemoryArena memoryArena, size_t stride, const void* so // TODO: It works but the only drawback now is that if source1 is a ReadOnlySpan, it will create a ReadOnlySpan as a result #define MemoryConcat(memoryArena, source1, source2) \ - (__extension__ ({ \ - auto result = _Generic((source1).Pointer, \ - char*: MemoryConcatChar, \ - const char*: MemoryConcatChar, \ - uint8_t*: MemoryConcatByte, \ - const uint8_t*: MemoryConcatByte, \ - default: MemoryConcatDefault \ - )(memoryArena, sizeof(*(source1).Pointer), (source1).Pointer, (source1).Length, (source2).Pointer, (source2).Length); \ (typeof(source1)) \ { \ - .Pointer = result, \ - .Length = (source1).Length + (source2.Length) \ - };\ - })) + .Pointer = _Generic((source1).Pointer, \ + char*: MemoryConcatChar, \ + const char*: MemoryConcatChar, \ + uint8_t*: MemoryConcatByte, \ + const uint8_t*: MemoryConcatByte, \ + default: MemoryConcatDefault \ + )(memoryArena, sizeof(*(source1).Pointer), (source1).Pointer, (source1).Length, (source2).Pointer, (source2).Length), \ + .Length = (source1).Length + (source2).Length \ + }; diff --git a/src/Common/String.h b/src/Common/String.h index 8033de3..2816c33 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -3,6 +3,8 @@ #include "Types.h" #include "Memory.h" +// TODO: Move this header to a standard one? Not a header just for strings? + DefineSpan(String, ReadOnlySpanChar) static inline ReadOnlySpanChar String(const char* string) diff --git a/src/Common/Types.h b/src/Common/Types.h index 2165e60..b021ad7 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -22,6 +22,17 @@ typedef __SIZE_TYPE__ size_t; #define BITS_PER_BYTE 8u #define BITS_PER_SIZE_TYPE (sizeof(size_t) * BITS_PER_BYTE) #define MASK_SIZE_TYPE (BITS_PER_SIZE_TYPE - 1) + +#define UINT8_MAX __UINT8_MAX__ +#define UINT16_MAX __UINT16_MAX__ +#define UINT32_MAX __UINT32_MAX__ +#define UINT64_MAX __UINT64_MAX__ + +#define INT8_MAX __INT8_MAX__ +#define INT16_MAX __INT16_MAX__ +#define INT32_MAX __INT32_MAX__ +#define INT64_MAX __INT64_MAX__ + #define SIZE_MAX __SIZE_MAX__ #define AlignUp(value, align) __builtin_align_up(value, align) @@ -103,12 +114,7 @@ typedef enum #define SpanCast(type, span) CreateSpan(type, (type*)(span).Pointer, (sizeof(*(span).Pointer) * (span).Length) / sizeof(type)) -#define StackAlloc(type, length) \ - (__extension__ ({ \ - static_assert((length) >= 0, "StackAlloc: length must be an integer-constant expression"); \ - type array[(length)]; \ - CreateSpan(type, array, (size_t)(length)); \ - })) +#define StackAlloc(type, length) CreateSpan(type, (type*)__builtin_alloca(sizeof(type) * length), length); #define SpanSlice(span, offset, length) \ ( \ diff --git a/src/Common/UnityBuild.c b/src/Common/UnityBuild.c index b2a1de7..8acb673 100644 --- a/src/Common/UnityBuild.c +++ b/src/Common/UnityBuild.c @@ -1,6 +1,7 @@ #include "Types.c" #include "Memory.c" #include "String.c" +#include "Console.c" #ifdef BUILD_TESTS #include "Test.c" diff --git a/src/Kernel/Kernel.c b/src/Kernel/Kernel.c index 12807d2..0c74aad 100644 --- a/src/Kernel/Kernel.c +++ b/src/Kernel/Kernel.c @@ -1,13 +1,13 @@ #include "Kernel.h" -#include "KernelConsole.h" +#include "Console.h" #include "Memory.h" #include "Platform.h" void KernelFailureCore(ReadOnlySpanChar file, uint32_t line, ReadOnlySpanChar message, ...) { - KernelConsoleSetForegroundColor(KernelConsoleColorError); - KernelConsolePrintBoxMessage(String("Kernel Failure")); - KernelConsolePrint(String("%s:%d\n"), file, line); + ConsoleSetForegroundColor(ConsoleColorError); + ConsolePrintBoxMessage(String("Kernel Failure")); + ConsolePrint(String("%s:%d\n"), file, line); va_list vargs; va_start(vargs, message); @@ -15,8 +15,8 @@ void KernelFailureCore(ReadOnlySpanChar file, uint32_t line, ReadOnlySpanChar me auto tmp = StackAlloc(char, 256); StringFormatVargs(&tmp, message, vargs); - KernelConsolePrint(String("%s\n\n"), tmp); - KernelConsoleResetStyle(); + ConsolePrint(String("%s\n\n"), tmp); + ConsoleResetStyle(); va_end(vargs); diff --git a/src/Kernel/KernelConsole.c b/src/Kernel/KernelConsole.c index 7fadd24..c82a518 100644 --- a/src/Kernel/KernelConsole.c +++ b/src/Kernel/KernelConsole.c @@ -1,9 +1,9 @@ -#include "KernelConsole.h" +#include "Console.h" #include "Memory.h" #include "String.h" #include "Platform.h" -void KernelConsolePrint(ReadOnlySpanChar message, ...) +void ConsolePrint(ReadOnlySpanChar message, ...) { auto output = StackAlloc(char, 2048); @@ -16,77 +16,3 @@ void KernelConsolePrint(ReadOnlySpanChar message, ...) BiosDebugConsoleWrite(ToReadOnlySpan(char, output)); } - -void KernelConsoleSetForegroundColor(Color color) -{ - KernelConsolePrint(String("\x1b[38;2;%d;%d;%dm"), (int32_t)color.Red, (int32_t)color.Green, (int32_t)color.Blue); -} - -void KernelConsoleResetStyle() -{ - KernelConsolePrint(String("\x1b[0m")); - KernelConsoleSetForegroundColor(KernelConsoleColorNormal); -} - -void FormatBoxedMessage(SpanChar destination, ReadOnlySpanChar message) -{ - auto upLeftCorner = String("┌"); - auto upRightCorner = String("┐"); - auto downLeftCorner = String("└"); - auto downRightCorner = String("┘"); - auto horizontalLine = String("─"); - auto verticalLine = String("│"); - - MemoryCopy(destination, upLeftCorner); - destination = SpanSliceFrom(destination, upLeftCorner.Length); - - for (uint32_t i = 0; i < message.Length + 2; i++) - { - MemoryCopy(destination, horizontalLine); - destination = SpanSliceFrom(destination, horizontalLine.Length); - } - - MemoryCopy(destination, upRightCorner); - destination = SpanSliceFrom(destination, upRightCorner.Length); - - MemoryCopy(destination, String("\n")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, verticalLine); - destination = SpanSliceFrom(destination, verticalLine.Length); - - MemoryCopy(destination, String(" ")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, message); - destination = SpanSliceFrom(destination, message.Length); - - MemoryCopy(destination, String(" ")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, verticalLine); - destination = SpanSliceFrom(destination, verticalLine.Length); - - MemoryCopy(destination, String("\n")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, downLeftCorner); - destination = SpanSliceFrom(destination, downLeftCorner.Length); - - for (uint32_t i = 0; i < message.Length + 2; i++) - { - MemoryCopy(destination, horizontalLine); - destination = SpanSliceFrom(destination, horizontalLine.Length); - } - - MemoryCopy(destination, downRightCorner); - destination = SpanSliceFrom(destination, downRightCorner.Length); -} - -void KernelConsolePrintBoxMessage(ReadOnlySpanChar message) -{ - // TODO: Use the stack memory arena here - auto boxedMessage = StackAlloc(char, 512); - FormatBoxedMessage(boxedMessage, message); - KernelConsolePrint(String("\n%s\n"), boxedMessage); -} diff --git a/src/Kernel/KernelConsole.h b/src/Kernel/KernelConsole.h deleted file mode 100644 index d8ca80a..0000000 --- a/src/Kernel/KernelConsole.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Memory.h" -#include "String.h" -#include "Types.h" - -const Color KernelConsoleColorNormal = { 212, 212, 212, 255 }; -const Color KernelConsoleColorHighlight = { 250, 250, 250, 255 }; -const Color KernelConsoleColorAccent = { 79, 193, 255, 255 }; -const Color KernelConsoleColorSuccess = { 106, 153, 85, 255 }; -const Color KernelConsoleColorWarning = { 255, 135, 100, 255 }; -const Color KernelConsoleColorError = { 255, 105, 105, 255 }; -const Color KernelConsoleColorInfo = { 220, 220, 170, 255 }; -const Color KernelConsoleColorAction = { 197, 134, 192, 255 }; -const Color KernelConsoleColorKeyword = { 86, 156, 214, 255 }; -const Color KernelConsoleColorNumeric = { 181, 206, 168, 255 }; - -void KernelConsolePrint(ReadOnlySpanChar message, ...); - -void KernelConsoleSetForegroundColor(Color color); -void KernelConsoleResetStyle(); - -void KernelConsolePrintBoxMessage(ReadOnlySpanChar message); diff --git a/src/Kernel/KernelMain.c b/src/Kernel/KernelMain.c index b953280..f3bfb3e 100644 --- a/src/Kernel/KernelMain.c +++ b/src/Kernel/KernelMain.c @@ -1,9 +1,9 @@ #include "Types.h" #include "String.h" #include "Memory.h" +#include "Console.h" #include "Version.h" #include "Platform.h" -#include "KernelConsole.h" #include "Kernel.h" const char KernelLogo[] = @@ -28,7 +28,7 @@ void KernelTrapHandler(CpuTrapFrame* trapFrame) BiosSetTimer(CpuReadTime() + 10'000'000); auto programCounter = CpuTrapFrameGetProgramCounter(trapFrame); - KernelConsolePrint(String("Kernel trap handler: %l (PC=%x).\n"), CpuReadTime(), programCounter); + ConsolePrint(String("Kernel trap handler: %l (PC=%x).\n"), CpuReadTime(), programCounter); return; @@ -79,16 +79,16 @@ void KernelInit() auto platformInformation = PlatformGetInformation(); - KernelConsoleSetForegroundColor(KernelConsoleColorAccent); - KernelConsolePrint(String("\n\n%s\n"), KernelLogo); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorAccent); + ConsolePrint(String("\n\n%s\n"), KernelLogo); + ConsoleResetStyle(); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("Kanso OS %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("Kanso OS %s "), KANSO_VERSION_FULL); + ConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + ConsoleResetStyle(); - KernelConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); + ConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); } void KernelMain() @@ -100,7 +100,7 @@ void KernelMain() while (true) { - KernelConsolePrint(String("WFI\n")); + ConsolePrint(String("WFI\n")); //CpuGenerateInvalidInstruction(); CpuWaitForInterrupt(); diff --git a/src/Kernel/KernelMemory.c b/src/Kernel/KernelMemory.c index f69754f..f5ddf04 100644 --- a/src/Kernel/KernelMemory.c +++ b/src/Kernel/KernelMemory.c @@ -1,4 +1,4 @@ -#include "KernelConsole.h" +#include "Console.h" #include "Memory.h" #include "Platform.h" #include "Types.h" @@ -42,7 +42,7 @@ static void KernelInitModeSetupBitArrayAllocator() auto maxPageCount = memoryState->InitHeap.Length / globalKernelMemoryState.PageSize; - auto bitmapStorageSizeInBytes = DivRoundUp(maxPageCount, 8); + auto bitmapStorageSizeInBytes = AlignUp(DivRoundUp(maxPageCount, 8), sizeof(size_t)); auto bitmapStoragePageCount = DivRoundUp(bitmapStorageSizeInBytes, globalKernelMemoryState.PageSize); memoryState->BitArray = CreateBitArrayWithBitCount(SpanCast(size_t, SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes)), maxPageCount); diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index b6f8377..e2186fd 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -1,7 +1,7 @@ #include "Memory.h" #include "Test.h" #include "String.h" -#include "KernelConsole.h" +#include "Console.h" #include "Kernel.h" #include "Platform.h" #include "Version.h" @@ -48,35 +48,36 @@ void KernelTrapHandler(CpuTrapFrame* trapFrame) KernelFailure(String("%s. (Code=%x, Extra=%x)"), errorName, trapCause.Code, trapCause.ExtraInformation); } +// TODO: Move that out of the way from the kernel. It will be the same code in user mode. void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) { if (state == TestRunState_Start) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[ RUN ]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[ RUN ]")); } else if (state == TestRunState_OK) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[ OK ]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[ OK ]")); } else if (state == TestRunState_Passed) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[ PASSED ]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[ PASSED ]")); } else if (state == TestRunState_Failed) { - KernelConsoleSetForegroundColor(KernelConsoleColorError); - KernelConsolePrint(String("[ FAILED ]")); + ConsoleSetForegroundColor(ConsoleColorError); + ConsolePrint(String("[ FAILED ]")); } else if (state == TestRunState_Separator) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[==========]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[==========]")); } - KernelConsoleResetStyle(); + ConsoleResetStyle(); va_list vargs; va_start(vargs, message); @@ -84,7 +85,7 @@ void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) auto tmp = StackAlloc(char, 256); StringFormatVargs(&tmp, message, vargs); - KernelConsolePrint(String(" %s\n"), tmp); + ConsolePrint(String(" %s\n"), tmp); va_end(vargs); } @@ -95,10 +96,10 @@ void KernelInit() auto platformInformation = PlatformGetInformation(); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("\n\nKanso OS KernelInit Tests %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("\n\nKanso OS KernelInit Tests %s "), KANSO_VERSION_FULL); + ConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + ConsoleResetStyle(); TestRun(KernelTestHandler, String("Types|Memory")); @@ -110,10 +111,10 @@ void KernelMain() { auto platformInformation = PlatformGetInformation(); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("\n\nKanso OS KernelMain Tests %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("\n\nKanso OS KernelMain Tests %s "), KANSO_VERSION_FULL); + ConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + ConsoleResetStyle(); //TestRun(KernelTestHandler, String("")); BiosReset(BiosResetType_Shutdown, BiosResetReason_None); diff --git a/src/Kernel/Platforms/RiscV/Cpu.c b/src/Kernel/Platforms/RiscV/Cpu.c index 5469943..efbf639 100644 --- a/src/Kernel/Platforms/RiscV/Cpu.c +++ b/src/Kernel/Platforms/RiscV/Cpu.c @@ -1,6 +1,6 @@ #include "Memory.h" #include "Types.h" -#include "../../KernelConsole.h" +#include "Console.h" #include "../../Platform.h" #define RISCV_INTERRUPT_SOFTWARE 1 @@ -253,28 +253,28 @@ inline void CpuWaitForInterrupt() static void LogRegister(ReadOnlySpanChar name, uintptr_t value, uint8_t padding, bool insertTab) { - KernelConsoleSetForegroundColor(KernelConsoleColorKeyword); - KernelConsolePrint(String("%s"), name); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorKeyword); + ConsolePrint(String("%s"), name); + ConsoleResetStyle(); - KernelConsolePrint(String(":")); + ConsolePrint(String(":")); for (uint32_t i = 0; i < padding; i++) { - KernelConsolePrint(String(" ")); + ConsolePrint(String(" ")); } - KernelConsoleSetForegroundColor(KernelConsoleColorNumeric); - KernelConsolePrint(String("%x"), value); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorNumeric); + ConsolePrint(String("%x"), value); + ConsoleResetStyle(); if (insertTab) { - KernelConsolePrint(String(" ")); + ConsolePrint(String(" ")); } else { - KernelConsolePrint(String("\n")); + ConsolePrint(String("\n")); } } @@ -335,18 +335,18 @@ static void LogSupervisorRegisters(const SupervisorRegisters* supervisorRegister void CpuLogTrapFrame(const CpuTrapFrame* trapFrame) { - KernelConsoleSetForegroundColor(KernelConsoleColorInfo); - KernelConsolePrintBoxMessage(String("Trap Frame")); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorInfo); + ConsolePrintBoxMessage(String("Trap Frame")); + ConsoleResetStyle(); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("General Purpose Registers:\n")); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("General Purpose Registers:\n")); + ConsoleResetStyle(); LogGeneralPurposeRegisters(&trapFrame->GeneralPurposeRegisters); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("\nSupervisor Registers:\n")); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("\nSupervisor Registers:\n")); + ConsoleResetStyle(); LogSupervisorRegisters(&trapFrame->SupervisorRegisters); } diff --git a/src/Kernel/Platforms/RiscV/Kernel.ld b/src/Kernel/Platforms/RiscV/Kernel.ld index f2ad5ed..d39c519 100644 --- a/src/Kernel/Platforms/RiscV/Kernel.ld +++ b/src/Kernel/Platforms/RiscV/Kernel.ld @@ -1,31 +1,29 @@ ENTRY(boot) -MEMORY -{ - kernel_space (rwx) : ORIGIN = 0x80200000, LENGTH = 64 * 1024 * 1024 -} - SECTIONS { + . = 0x80200000; + .text : { *(.text.kernel) *(.text); - } > kernel_space + } .text.interrupt : ALIGN(4) { + *(.text.interrupt .text.interrupt.*) } .data : ALIGN(8) { *(.data); - } > kernel_space + } .rodata : ALIGN(8) { *(.rodata); - } > kernel_space + } .init_array ALIGN(8) : { @@ -33,18 +31,20 @@ SECTIONS KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*))) KEEP(*(.init_array)) __INIT_ARRAY_END = .; - } > kernel_space + } - .bss : ALIGN(8) + .bss (NOLOAD) : ALIGN(8) { __BSS = .; - *(.bss .sbss); + *(.bss .bss.* .sbss .sbss.*) + *(COMMON) __BSS_END = .; - } > kernel_space + } . = ALIGN(8); . += 128 * 1024; /* 128KB */ __STACK_PTR = .; + . = ALIGN(4096); __INIT_HEAP_START = .; . += 64 * 1024 * 1024; /* 64MB, TODO: Review this later, we don't need that much for init mode */ diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index 483c8e9..2bd72f6 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -150,11 +150,11 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) BinaryReadString(reader, &name); BinarySetOffset(reader, AlignUp(reader->CurrentOffset, 4)); - KernelConsolePrint(String("BeginNode: '%s'\n"), name); + ConsolePrint(String("BeginNode: '%s'\n"), name); } else if (testNode == 0x02) { - KernelConsolePrint(String("EndNode.\n")); + ConsolePrint(String("EndNode.\n")); } else if (testNode == 0x03) { @@ -171,7 +171,7 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) BinaryReadString(reader, &name); BinarySetOffset(reader, AlignUp(offset, 4)); - KernelConsolePrint(String(" Property: %s\n"), name); + ConsolePrint(String(" Property: %s\n"), name); } else if (testNode == 0x09) { @@ -188,7 +188,7 @@ PlatformDevices PlatformGetDevices() auto dtbMagic = ConvertBytesToUint32(dtbHeaderData, ByteOrder_BigEndian); auto sizeInBytes = ConvertBytesToUint32(SpanSliceFrom(dtbHeaderData, sizeof(uint32_t)), ByteOrder_BigEndian); - KernelConsolePrint(String("MagicDTB: %x\n"), dtbMagic); + ConsolePrint(String("MagicDTB: %x\n"), dtbMagic); // TODO: Check magic // TODO: Verify version @@ -215,7 +215,7 @@ PlatformDevices PlatformGetDevices() if (reservedOffset != 0 && reservedSize != 0) { - KernelConsolePrint(String("Reserved Memory: %x (size: %x)\n"), reservedOffset, reservedSize); + ConsolePrint(String("Reserved Memory: %x (size: %x)\n"), reservedOffset, reservedSize); } } diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index ddaa9b8..82eae32 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -1,3 +1,4 @@ +#include "Console.h" #include "Memory.h" #include "Test.h" #include "Types.h" @@ -123,7 +124,7 @@ Test(Memory, MemoryArenaPushStruct_WithValidStruct_ReturnsValidStruct) auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); - TestAssertEquals(sizeof(uint32_t), afterAllocationInfos.AllocatedBytes); + TestAssertGreaterThan(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t)); } Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) @@ -144,7 +145,7 @@ Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); - TestAssertEquals(sizeof(uint32_t) * arrayCount, afterAllocationInfos.AllocatedBytes); + TestAssertGreaterThan(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t) * arrayCount); } Test(Memory, MemoryArenaPushReserved_WithValidSize_ReturnsValidSpan) @@ -288,26 +289,60 @@ Test(Memory, MemoryArenaCommit_WithInvalidRange_CommitMemory) Test(Memory, GetStackMemoryArena_WithDifferentScopes_UsesSeparateMemoryArena) { // Arrange - auto stackMemoryArena1 = GetStackMemoryArena(); + StackMemoryArena(stackMemoryArena1); auto string1 = MemoryConcat(stackMemoryArena1, String("Test1"), String("Stack1")); MemoryArenaAllocationInfos beforeAllocationInfos; ReadOnlySpanChar string2; + ReadOnlySpanChar string3; + ReadOnlySpanChar string4; ReadOnlySpanChar string5; // Act { - auto stackMemoryArena2 = GetStackMemoryArena(); + StackMemoryArena(stackMemoryArena2); string2 = MemoryConcat(stackMemoryArena1, String("Test2"), String("Stack1")); - MemoryConcat(stackMemoryArena2, String("Test1"), String("Stack2")); - + + { + StackMemoryArena(stackMemoryArena3); + MemoryConcat(stackMemoryArena2, String("Test1"), String("Stack2")); + } + + { + StackMemoryArena(stackMemoryArena3); + + { + StackMemoryArena(stackMemoryArena4); + MemoryConcat(stackMemoryArena4, String("Test1"), String("Stack4")); + MemoryConcat(stackMemoryArena4, String("Test2"), String("Stack4")); + MemoryConcat(stackMemoryArena4, String("Test3"), String("Stack4")); + } + + MemoryConcat(stackMemoryArena3, String("Test1"), String("Stack3")); + string3 = MemoryConcat(stackMemoryArena1, String("Test3"), String("Stack1")); + } + + MemoryConcat(stackMemoryArena2, String("Test2"), String("Stack2")); + MemoryConcat(stackMemoryArena2, String("Test3"), String("Stack2")); + MemoryConcat(stackMemoryArena2, String("Test4"), String("Stack2")); + MemoryConcat(stackMemoryArena2, String("Test5"), String("Stack2")); + string4 = MemoryConcat(stackMemoryArena1, String("Test4"), String("Stack1")); + beforeAllocationInfos = MemoryArenaGetAllocationInfos(stackMemoryArena1); } + + { + StackMemoryArena(stackMemoryArena2); + string5 = MemoryConcat(stackMemoryArena1, String("Test5"), String("Stack1")); + } auto afterAllocationInfos = MemoryArenaGetAllocationInfos(stackMemoryArena1); // Assert TestAssertStringEquals(String("Test1Stack1"), string1); TestAssertStringEquals(String("Test2Stack1"), string2); + TestAssertStringEquals(String("Test3Stack1"), string3); + TestAssertStringEquals(String("Test4Stack1"), string4); + TestAssertStringEquals(String("Test5Stack1"), string5); TestAssertGreaterThan(beforeAllocationInfos.AllocatedBytes, afterAllocationInfos.AllocatedBytes); } diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index ae46d9a..7fb7cf0 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -154,3 +154,34 @@ Test(Memory, MemoryConcat_WithChar_HasCorrectValues) TestAssertEquals(0, SpanAt(result, result.Length)); } + +Test(Memory, MemoryConcat_WithUint32_HasCorrectValues) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const uint8_t itemCount = 10; + const uint32_t source1Value = 5; + const uint32_t source2Value = 28; + + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + auto source1 = StackAlloc(uint32_t, itemCount); + MemorySet(source1, source1Value); + + auto source2 = StackAlloc(uint32_t, itemCount); + MemorySet(source2, source2Value); + + // Act + auto result = MemoryConcat(memoryArena, source1, source2); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(itemCount * 2, result.Length); + + for (uint32_t i = 0; i < itemCount; i++) + { + TestAssertEquals(source1Value, SpanAt(result, i)); + TestAssertEquals(source2Value, SpanAt(result, itemCount + i)); + } +} From f034fa4ce837d816c90d50a26d958fcb060c0798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 29 Sep 2025 10:59:45 +0200 Subject: [PATCH 18/20] Update StringSplit function --- src/Common/Memory.c | 6 ++++-- src/Common/Memory.h | 8 ++++++++ src/Common/String.c | 14 +++++++++----- src/Common/String.h | 2 +- src/Common/Test.c | 5 +++-- tests/Common/StringTests.c | 4 ++-- tests/Kernel/CpuTests.c | 6 +++--- 7 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index c217458..0519afb 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -13,6 +13,8 @@ MemoryError globalMemoryError = MemoryError_None; // MemoryArena //--------------------------------------------------------------------------------------- +#define STACK_MEMORY_ARENA_DEFAULT_SIZE KiloBytesToBytes(128) + typedef struct MemoryArenaStorage { MemoryReservation MemoryReservation; @@ -80,7 +82,7 @@ MemoryArenaStorage* GetMemoryArenaWorkingStorage(MemoryArena memoryArena) { // TODO: Replace the size by a constant depending on the boot phase // TODO: Do an util method for bytes - globalStackMemoryArenaExtraStorage = CreateMemoryArenaStorage(1024); + globalStackMemoryArenaExtraStorage = CreateMemoryArenaStorage(STACK_MEMORY_ARENA_DEFAULT_SIZE); } // TODO: Replace by a min function @@ -227,7 +229,7 @@ MemoryArena GetStackMemoryArena() { // TODO: Replace the size by a constant depending on the boot phase // TODO: Do an util method for bytes - globalStackMemoryArenaStorage = CreateMemoryArenaStorage(1024); + globalStackMemoryArenaStorage = CreateMemoryArenaStorage(STACK_MEMORY_ARENA_DEFAULT_SIZE); } globalStackMemoryArenaStorage->StackLevel++; diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 64b7bf2..ea02729 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -21,6 +21,14 @@ static inline MemoryError MemoryGetLastError() return globalMemoryError; } +//--------------------------------------------------------------------------------------- +// Utilities +//--------------------------------------------------------------------------------------- + +#define KiloBytesToBytes(value) (value) * 1024 +#define MegaBytesToBytes(value) KiloBytesToBytes((value)) * 1024 +#define GigaBytesToBytes(value) MegaBytesToBytes((value)) * 1024 + //--------------------------------------------------------------------------------------- // Memory Allocation //--------------------------------------------------------------------------------------- diff --git a/src/Common/String.c b/src/Common/String.c index 3e2de87..1ac1d8a 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -19,24 +19,28 @@ bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) return true; } -// TODO: Replace that with a memory arena -void StringSplit(SpanString* result, ReadOnlySpanChar value, char separator) +ReadOnlySpanString StringSplit(MemoryArena memoryArena, ReadOnlySpanChar value, char separator) { auto resultCount = 0; auto currentStartIndex = 0; + // TODO: Compute the needed length first? + auto result = MemoryArenaPushArray(ReadOnlySpanChar, memoryArena, 32); + for (uint32_t i = 0; i < value.Length; i++) { if (SpanAt(value, i) == separator) { - SpanAt(*result, resultCount++) = SpanSlice(value, currentStartIndex, i - currentStartIndex); + SpanAt(result, resultCount++) = SpanSlice(value, currentStartIndex, i - currentStartIndex); currentStartIndex = i + 1; } } - SpanAt(*result, resultCount++) = SpanSlice(value, currentStartIndex, value.Length - currentStartIndex); + SpanAt(result, resultCount++) = SpanSlice(value, currentStartIndex, value.Length - currentStartIndex); + + result.Length = resultCount; - result->Length = resultCount; + return ToReadOnlySpan(ReadOnlySpanChar, result); } // TODO: Replace that with a memory arena diff --git a/src/Common/String.h b/src/Common/String.h index 2816c33..9b01b53 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -15,7 +15,7 @@ static inline ReadOnlySpanChar String(const char* string) bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2); // TODO: Replace that with a memory arena -void StringSplit(SpanString* result, ReadOnlySpanChar value, char separator); +ReadOnlySpanString StringSplit(MemoryArena memoryArena, ReadOnlySpanChar value, char separator); // TODO: Replace that with a memory arena void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...); diff --git a/src/Common/Test.c b/src/Common/Test.c index 3557cf8..66b8007 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -25,7 +25,9 @@ void RegisterTest(ReadOnlySpanChar category, ReadOnlySpanChar name, TestFunction void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) { + StackMemoryArena(stackMemoryArena); uint32_t testRunCount = 0; + auto splittedFilters = StringSplit(stackMemoryArena, categoryFilters, '|'); for (uint32_t i = 0; i < globalTestCount; i++) { @@ -34,8 +36,7 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) if (categoryFilters.Length > 0) { - auto splittedFilters = StackAlloc(ReadOnlySpanChar, 64); - StringSplit(&splittedFilters, categoryFilters, '|'); + // TODO: This is really bad. Move that away from the loop auto testCanRun = false; diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index 450488c..a5e71da 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -49,10 +49,10 @@ Test(String, StringSplit_WithParameters_HasCorrectValues) { // Arrange const auto testString = String("Test1|Test2|Test3"); - auto destination = StackAlloc(ReadOnlySpanChar, 64); + StackMemoryArena(stackMemoryArena); // Act - StringSplit(&destination, testString, '|'); + auto destination = StringSplit(stackMemoryArena, testString, '|'); // Assert TestAssertEquals(3, destination.Length); diff --git a/tests/Kernel/CpuTests.c b/tests/Kernel/CpuTests.c index 8527e72..950481f 100644 --- a/tests/Kernel/CpuTests.c +++ b/tests/Kernel/CpuTests.c @@ -70,11 +70,11 @@ Test(Cpu, CpuTrapHandler_WithTimerInterrupt_HasCorrectCause) { // Arrange CpuSetTrapHandler(TestTrapHandler_WithTimerInterrupt); - CpuEnableInterrupts(CpuInterruptType_Timer); - auto timerDeadline = CpuReadTime(); // Act - BiosSetTimer(timerDeadline); + CpuEnableInterrupts(CpuInterruptType_Timer); + + // TODO: SetTimer doesn't work here // Assert const uint32_t maxIterations = 10; From a26486598ea10676211d7abdee176e7ded0e37f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 29 Sep 2025 11:47:34 +0200 Subject: [PATCH 19/20] Update StringFormat --- src/Common/Memory.c | 2 +- src/Common/String.c | 38 ++++++++++++++++++++++---------------- src/Common/String.h | 6 ++---- src/Common/Test.c | 7 ++++--- src/Common/Test.h | 9 +++++---- src/Kernel/Kernel.c | 5 +++-- src/Kernel/KernelConsole.c | 6 +++--- src/Kernel/KernelTest.c | 5 +++-- tests/Common/StringTests.c | 6 +++--- 9 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 0519afb..724456f 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -13,7 +13,7 @@ MemoryError globalMemoryError = MemoryError_None; // MemoryArena //--------------------------------------------------------------------------------------- -#define STACK_MEMORY_ARENA_DEFAULT_SIZE KiloBytesToBytes(128) +#define STACK_MEMORY_ARENA_DEFAULT_SIZE MegaBytesToBytes(4) typedef struct MemoryArenaStorage { diff --git a/src/Common/String.c b/src/Common/String.c index 1ac1d8a..e6b51c0 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -43,19 +43,23 @@ ReadOnlySpanString StringSplit(MemoryArena memoryArena, ReadOnlySpanChar value, return ToReadOnlySpan(ReadOnlySpanChar, result); } -// TODO: Replace that with a memory arena -void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...) +ReadOnlySpanChar StringFormat(MemoryArena memoryArena, ReadOnlySpanChar message, ...) { va_list vargs; va_start(vargs, message); - StringFormatVargs(destination, message, vargs); + auto result = StringFormatVargs(memoryArena, message, vargs); va_end(vargs); + + return result; } // TODO: Refactor this function // TODO: It would be cool if we could handle ReadOnlySpan so we can take the length not until \0 -void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list vargs) +ReadOnlySpanChar StringFormatVargs(MemoryArena memoryArena, ReadOnlySpanChar message, va_list vargs) { + // TODO: Don't hardcode the size + auto destination = MemoryArenaPushArray(char, memoryArena, 1024); + uint32_t length = 0; char* messagePointer = (char*)message.Pointer; @@ -75,7 +79,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list case '%': { - SpanAt(*destination, length++) = '%'; + SpanAt(destination, length++) = '%'; break; } @@ -85,7 +89,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (*stringArgument) { - SpanAt(*destination, length++) = *stringArgument; + SpanAt(destination, length++) = *stringArgument; stringArgument++; } break; @@ -99,7 +103,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list if (decimalArgument < 0) { - SpanAt(*destination, length++) = '-'; + SpanAt(destination, length++) = '-'; magnitude = -magnitude; } @@ -112,7 +116,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (divisor > 0) { - SpanAt(*destination, length++) = '0' + magnitude / divisor; + SpanAt(destination, length++) = '0' + magnitude / divisor; magnitude %= divisor; divisor /= 10; @@ -129,7 +133,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list if (decimalArgument < 0) { - SpanAt(*destination, length++) = '-'; + SpanAt(destination, length++) = '-'; magnitude = -magnitude; } @@ -142,7 +146,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (divisor > 0) { - SpanAt(*destination, length++) = '0' + magnitude / divisor; + SpanAt(destination, length++) = '0' + magnitude / divisor; magnitude %= divisor; divisor /= 10; @@ -153,13 +157,13 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list case 'x': { uintptr_t hexaArgument = va_arg(vargs, uintptr_t); - SpanAt(*destination, length++) = '0'; - SpanAt(*destination, length++) = 'x'; + SpanAt(destination, length++) = '0'; + SpanAt(destination, length++) = 'x'; for (int32_t i = (sizeof(uintptr_t) * 2) - 1; i >= 0; i--) { unsigned nibble = (hexaArgument >> (i * 4)) & 0xf; - SpanAt(*destination, length++) = "0123456789abcdef"[nibble]; + SpanAt(destination, length++) = "0123456789abcdef"[nibble]; } break; } @@ -167,12 +171,14 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list } else { - SpanAt(*destination, length++) = *messagePointer; + SpanAt(destination, length++) = *messagePointer; } messagePointer++; } - destination->Length = length; - SpanAt(*destination, length) = '\0'; + destination.Length = length; + SpanAt(destination, length) = '\0'; + + return ToReadOnlySpan(char, destination); } diff --git a/src/Common/String.h b/src/Common/String.h index 9b01b53..54cbf30 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -14,12 +14,10 @@ static inline ReadOnlySpanChar String(const char* string) bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2); -// TODO: Replace that with a memory arena ReadOnlySpanString StringSplit(MemoryArena memoryArena, ReadOnlySpanChar value, char separator); -// TODO: Replace that with a memory arena -void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...); -void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list vargs); +ReadOnlySpanChar StringFormat(MemoryArena memoryArena, ReadOnlySpanChar message, ...); +ReadOnlySpanChar StringFormatVargs(MemoryArena memoryArena, ReadOnlySpanChar message, va_list vargs); // TODO: Move that to the standard library diff --git a/src/Common/Test.c b/src/Common/Test.c index 66b8007..53e291c 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -6,9 +6,8 @@ TestEntry globalTests[MAX_TESTS]; uint32_t globalTestCount = 0; uint32_t globalCurrentTestIndex = 0; -// TODO: Do someting better here? -char globalTestLastErrorMessageStorage[TEST_ERROR_MESSAGE_LENGTH]; -SpanChar globalTestLastErrorMessage = { .Pointer = globalTestLastErrorMessageStorage, .Length = TEST_ERROR_MESSAGE_LENGTH }; +ReadOnlySpanChar globalTestLastErrorMessage = {}; +MemoryArena globalTestMemoryArena = {}; void RegisterTest(ReadOnlySpanChar category, ReadOnlySpanChar name, TestFunction testFunction) { @@ -28,9 +27,11 @@ void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) StackMemoryArena(stackMemoryArena); uint32_t testRunCount = 0; auto splittedFilters = StringSplit(stackMemoryArena, categoryFilters, '|'); + globalTestMemoryArena = CreateMemoryArena(KiloBytesToBytes(128)); for (uint32_t i = 0; i < globalTestCount; i++) { + MemoryArenaClear(globalTestMemoryArena); auto test = &globalTests[i]; test->CanRun = true; diff --git a/src/Common/Test.h b/src/Common/Test.h index 15de28c..95821b0 100644 --- a/src/Common/Test.h +++ b/src/Common/Test.h @@ -32,7 +32,8 @@ typedef struct extern TestEntry globalTests[MAX_TESTS]; extern uint32_t globalTestCount; extern uint32_t globalCurrentTestIndex; -extern SpanChar globalTestLastErrorMessage; +extern ReadOnlySpanChar globalTestLastErrorMessage; +extern MemoryArena globalTestMemoryArena; #define Test(category, name) \ @@ -54,7 +55,7 @@ extern SpanChar globalTestLastErrorMessage; if (!testEntry->HasError) \ { \ testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: %s\n Actual: %d %s %d"), __FILE__, #expr, expected, operator, actual); \ + globalTestLastErrorMessage = StringFormat(globalTestMemoryArena, String("%s\n Expected: %s\n Actual: %d %s %d"), __FILE__, #expr, expected, operator, actual); \ } \ } \ } while (false) @@ -74,13 +75,13 @@ extern SpanChar globalTestLastErrorMessage; if (expected.Length != actual.Length) \ { \ testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s.Length) == (%s.Length)\n Actual: %d == %d"), __FILE__, #expected, #actual, expected.Length, actual.Length); \ + globalTestLastErrorMessage = StringFormat(globalTestMemoryArena, String("%s\n Expected: (%s.Length) == (%s.Length)\n Actual: %d == %d"), __FILE__, #expected, #actual, expected.Length, actual.Length); \ } \ \ if (!StringEquals(expected, actual)) \ { \ testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s) == (%s)\n Actual: \"%s\" == \"%s\""), __FILE__, #expected, #actual, expected.Pointer, actual.Pointer); \ + globalTestLastErrorMessage = StringFormat(globalTestMemoryArena, String("%s\n Expected: (%s) == (%s)\n Actual: \"%s\" == \"%s\""), __FILE__, #expected, #actual, expected.Pointer, actual.Pointer); \ } \ } \ } while (false) diff --git a/src/Kernel/Kernel.c b/src/Kernel/Kernel.c index 0c74aad..46da952 100644 --- a/src/Kernel/Kernel.c +++ b/src/Kernel/Kernel.c @@ -5,6 +5,8 @@ void KernelFailureCore(ReadOnlySpanChar file, uint32_t line, ReadOnlySpanChar message, ...) { + StackMemoryArena(stackMemoryArena); + ConsoleSetForegroundColor(ConsoleColorError); ConsolePrintBoxMessage(String("Kernel Failure")); ConsolePrint(String("%s:%d\n"), file, line); @@ -12,8 +14,7 @@ void KernelFailureCore(ReadOnlySpanChar file, uint32_t line, ReadOnlySpanChar me va_list vargs; va_start(vargs, message); - auto tmp = StackAlloc(char, 256); - StringFormatVargs(&tmp, message, vargs); + auto tmp = StringFormatVargs(stackMemoryArena, message, vargs); ConsolePrint(String("%s\n\n"), tmp); ConsoleResetStyle(); diff --git a/src/Kernel/KernelConsole.c b/src/Kernel/KernelConsole.c index c82a518..12ec10e 100644 --- a/src/Kernel/KernelConsole.c +++ b/src/Kernel/KernelConsole.c @@ -5,14 +5,14 @@ void ConsolePrint(ReadOnlySpanChar message, ...) { - auto output = StackAlloc(char, 2048); + StackMemoryArena(stackMemoryArena); va_list vargs; va_start(vargs, message); - StringFormatVargs(&output, message, vargs); + auto output = StringFormatVargs(stackMemoryArena, message, vargs); va_end(vargs); - BiosDebugConsoleWrite(ToReadOnlySpan(char, output)); + BiosDebugConsoleWrite(output); } diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index e2186fd..2555b03 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -82,8 +82,8 @@ void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) va_list vargs; va_start(vargs, message); - auto tmp = StackAlloc(char, 256); - StringFormatVargs(&tmp, message, vargs); + StackMemoryArena(stackMemoryArena); + auto tmp = StringFormatVargs(stackMemoryArena, message, vargs); ConsolePrint(String(" %s\n"), tmp); @@ -117,6 +117,7 @@ void KernelMain() ConsoleResetStyle(); //TestRun(KernelTestHandler, String("")); + BiosReset(BiosResetType_Shutdown, BiosResetReason_None); } diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index a5e71da..8fc92fd 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -69,12 +69,12 @@ Test(String, StringFormat_WithParameters_HasCorrectValues) const auto stringParameter = String("TestParameter"); const auto finalString = String("Test: 28, TestParameter"); - auto destination = StackAlloc(char, 64); + StackMemoryArena(stackMemoryArena); // Act - StringFormat(&destination, testString, intParameter, stringParameter); + auto destination = StringFormat(stackMemoryArena, testString, intParameter, stringParameter); // Assert - TestAssertStringEquals(finalString, ToReadOnlySpan(char, destination)); + TestAssertStringEquals(finalString, destination); } From 5d6004b2a01bb517688785c5ac0d9538847c8a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 29 Sep 2025 12:08:52 +0200 Subject: [PATCH 20/20] Fix test --- src/Common/Test.h | 1 + tests/Common/MemoryArenaTests.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Common/Test.h b/src/Common/Test.h index 95821b0..ec75adf 100644 --- a/src/Common/Test.h +++ b/src/Common/Test.h @@ -63,6 +63,7 @@ extern MemoryArena globalTestMemoryArena; #define TestAssertEquals(expected, actual) TestAssertCore((expected) == (actual), expected, actual, "==") #define TestAssertNotEquals(expected, actual) TestAssertCore((expected) != (actual), expected, actual, "!=") #define TestAssertGreaterThan(expected, actual) TestAssertCore((expected) > (actual), expected, actual, ">") +#define TestAssertGreaterThanOrEquals(expected, actual) TestAssertCore((expected) >= (actual), expected, actual, ">=") #define TestAssertIsTrue(actual) TestAssertCore(true == (actual), true, actual, "==") #define TestAssertIsFalse(actual) TestAssertCore(false == (actual), false, actual, "==") diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c index 82eae32..6cd22ae 100644 --- a/tests/Common/MemoryArenaTests.c +++ b/tests/Common/MemoryArenaTests.c @@ -124,7 +124,7 @@ Test(Memory, MemoryArenaPushStruct_WithValidStruct_ReturnsValidStruct) auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); - TestAssertGreaterThan(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t)); + TestAssertGreaterThanOrEquals(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t)); } Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) @@ -145,7 +145,7 @@ Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); - TestAssertGreaterThan(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t) * arrayCount); + TestAssertGreaterThanOrEquals(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t) * arrayCount); } Test(Memory, MemoryArenaPushReserved_WithValidSize_ReturnsValidSpan)