diff --git a/.gitignore b/.gitignore index 377b8fa..04b9ca0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .build +build .vscode .DS_Store compile_commands.json +ide-swift-toolchain.txt .cache \ No newline at end of file diff --git a/Kernel/.clangd b/Kernel/.clangd new file mode 100644 index 0000000..9992ca2 --- /dev/null +++ b/Kernel/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: ../.build/temp/Kernel diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index c44dd52..be84ad4 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -1,150 +1,45 @@ cmake_minimum_required(VERSION 3.20) project(ksOSKernel LANGUAGES ASM C) -set(KERNEL_MODULE_NAME "Kernel") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# --- Locate Swift toolchain with Embedded Swift stdlib --- -# Priority: cmake var > env var > auto-detect -if(NOT SWIFT_TOOLCHAIN AND DEFINED ENV{SWIFT_TOOLCHAIN}) - set(SWIFT_TOOLCHAIN "$ENV{SWIFT_TOOLCHAIN}") -endif() +file(GLOB_RECURSE KERNEL_SOURCES CMAKE_CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/Source/KernelMain.c + ${CMAKE_CURRENT_SOURCE_DIR}/Source/Arch/entry.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/Arch/vectors.S + ${CMAKE_CURRENT_SOURCE_DIR}/Source/**/*.c +) -if(SWIFT_TOOLCHAIN) - set(SWIFTC "${SWIFT_TOOLCHAIN}/usr/bin/swiftc") - set(SWIFT_RESOURCE_DIR "${SWIFT_TOOLCHAIN}/usr/lib/swift") -elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") - # Scan for a swift.org toolchain that ships the embedded stdlib. - # APPLE is false here (target is Generic), so we check the HOST OS. - file(GLOB _tc_candidates - "$ENV{HOME}/Library/Developer/Toolchains/*.xctoolchain" - "/Library/Developer/Toolchains/*.xctoolchain" - ) - foreach(_tc ${_tc_candidates}) - if(EXISTS "${_tc}/usr/lib/swift/embedded") - set(SWIFTC "${_tc}/usr/bin/swiftc") - set(SWIFT_RESOURCE_DIR "${_tc}/usr/lib/swift") - break() - endif() - endforeach() -else() - # Linux: find swiftc in PATH - find_program(_SWIFTC_EXE swiftc) - if(_SWIFTC_EXE) - get_filename_component(_SWIFTC_BIN "${_SWIFTC_EXE}" DIRECTORY) - get_filename_component(_SWIFTC_USR "${_SWIFTC_BIN}" DIRECTORY) - - if(EXISTS "${_SWIFTC_USR}/lib/swift/embedded") - set(SWIFTC "${_SWIFTC_EXE}") - set(SWIFT_RESOURCE_DIR "${_SWIFTC_USR}/lib/swift") - elseif(EXISTS "${_SWIFTC_USR}/lib/swift/lib/swift/embedded") - set(SWIFTC "${_SWIFTC_EXE}") - set(SWIFT_RESOURCE_DIR "${_SWIFTC_USR}/lib/swift/lib/swift") - endif() - endif() -endif() +add_executable(Kernel ${KERNEL_SOURCES}) +target_include_directories(Kernel PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/Include/ + ${CMAKE_CURRENT_SOURCE_DIR}/../Common +) -if(NOT SWIFTC OR NOT EXISTS "${SWIFT_RESOURCE_DIR}/embedded") - message(FATAL_ERROR - "Swift toolchain with Embedded Swift not found.\n" - "Install a swift.org toolchain and pass -DSWIFT_TOOLCHAIN= " - "or set the SWIFT_TOOLCHAIN env var.") -endif() +target_compile_options(Kernel PRIVATE + $<$: + -ffreestanding + -fno-stack-protector + -fno-builtin + -Wall -Wextra + -g + -mgeneral-regs-only + > +) -message(STATUS "Swift: ${SWIFTC}") -message(STATUS "Swift resource dir: ${SWIFT_RESOURCE_DIR}") - -# --- Build --- -add_compile_options(-ffreestanding -nostdlib -O0 -g) - -set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld) -add_link_options( - -fuse-ld=lld +target_link_options(Kernel PRIVATE -nostdlib -static - -Wl,-T,${LINKER_SCRIPT} + -no-pie + -T "${CMAKE_CURRENT_SOURCE_DIR}/linker.ld" + -z max-page-size=0x1000 ) -set(SWIFT_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/Source/kernel.swift - ${CMAKE_CURRENT_SOURCE_DIR}/Source/Arch/dtb.swift - ${CMAKE_CURRENT_SOURCE_DIR}/Source/IO/uart.swift -) -set(SWIFT_OBJ ${CMAKE_CURRENT_BINARY_DIR}/kernel_swift.o) - -add_custom_command( - OUTPUT ${SWIFT_OBJ} - COMMAND ${SWIFTC} - -target aarch64-none-none-elf - -enable-experimental-feature Embedded - -parse-as-library - -wmo - -O - -Xcc -fno-stack-protector - -Xcc -I${CMAKE_CURRENT_SOURCE_DIR}/../Common - -import-bridging-header ${CMAKE_CURRENT_SOURCE_DIR}/Source/Support/BridgingHeader.h - -resource-dir ${SWIFT_RESOURCE_DIR} - -c ${SWIFT_SOURCES} - -o ${SWIFT_OBJ} - COMMAND ${LLVM_OBJCOPY} --remove-section=.swift_modhash ${SWIFT_OBJ} - DEPENDS ${SWIFT_SOURCES} - COMMENT "Compiling Swift kernel" +set_target_properties(Kernel PROPERTIES + OUTPUT_NAME "Kernel.elf" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) -set_source_files_properties(${SWIFT_OBJ} PROPERTIES - EXTERNAL_OBJECT TRUE - GENERATED TRUE -) - -add_executable(kernel.elf Source/Arch/entry.S Source/Support/stubs.c ${SWIFT_OBJ}) - -add_custom_command(TARGET kernel.elf POST_BUILD - COMMAND ${LLVM_OBJCOPY} -O binary kernel.elf kernel.bin -) - -# --- SourceKit-LSP: generate compile_commands.json for Swift (Dynamic) --- -set(_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/Source/Support/BridgingHeader.h") - -set(SWIFT_ARGS - "\"${SWIFTC}\"" - "\"-target\"" "\"aarch64-none-none-elf\"" - "\"-enable-experimental-feature\"" "\"Embedded\"" - "\"-module-name\"" "\"${KERNEL_MODULE_NAME}\"" - "\"-parse-as-library\"" - "\"-import-bridging-header\"" "\"${_BRIDGING_HEADER}\"" - "\"-Xcc\"" "\"-I${CMAKE_CURRENT_SOURCE_DIR}/../Common\"" - "\"-resource-dir\"" "\"${SWIFT_RESOURCE_DIR}\"" -) -foreach(_src IN LISTS SWIFT_SOURCES) - list(APPEND SWIFT_ARGS "\"${_src}\"") -endforeach() - -string(JOIN ", " SWIFT_ARGS_JSON ${SWIFT_ARGS}) - -set(COMPDB_ENTRIES "") -list(LENGTH SWIFT_SOURCES _src_count) -math(EXPR _last_idx "${_src_count} - 1") -set(_idx 0) - -foreach(_src IN LISTS SWIFT_SOURCES) - set(_entry " {\n \"file\": \"${_src}\",\n \"directory\": \"${CMAKE_CURRENT_BINARY_DIR}\",\n \"arguments\": [${SWIFT_ARGS_JSON}]\n }") - - if(_idx LESS _last_idx) - string(APPEND _entry ",\n") - else() - string(APPEND _entry "\n") - endif() - - string(APPEND COMPDB_ENTRIES "${_entry}") - math(EXPR _idx "${_idx} + 1") -endforeach() - -if(DEFINED ENV{TEMP_DIR}) - set(COMPDB_OUTPUT_DIR "$ENV{TEMP_DIR}/Kernel") -elseif(DEFINED ENV{BUILD_DIR}) - set(COMPDB_OUTPUT_DIR "$ENV{BUILD_DIR}/temp/Kernel") -else() - set(COMPDB_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") -endif() - -file(GENERATE OUTPUT "${COMPDB_OUTPUT_DIR}/compile_commands.json" - CONTENT "[\n${COMPDB_ENTRIES}]\n" +add_custom_command(TARGET Kernel POST_BUILD + COMMAND ${LLVM_OBJCOPY} -O binary ${CMAKE_BINARY_DIR}/Kernel.elf ${CMAKE_BINARY_DIR}/Kernel.bin + COMMENT "Generating ksOSKernel.bin from Kernel.elf" ) \ No newline at end of file diff --git a/Kernel/Include/Arch/CPU.h b/Kernel/Include/Arch/CPU.h new file mode 100644 index 0000000..d142bc9 --- /dev/null +++ b/Kernel/Include/Arch/CPU.h @@ -0,0 +1,24 @@ +#pragma once +#include + +static inline void CPUYield() { + __asm__ volatile ("yield" ::: "memory"); +} + +static inline void CPUWaitForInterrupt() { + __asm__ volatile ("wfi" ::: "memory"); +} + +static inline void CPUDisableInterrupts() { + __asm__ volatile ("msr daifset, #3" ::: "memory"); +} + +static inline void CPUEnableInterrupts() { + __asm__ volatile ("msr daifclr, #3" ::: "memory"); +} + +static inline UInt64 CPUGetFAR() { + UInt64 far; + __asm__ volatile ("mrs %0, far_el1" : "=r" (far)); + return far; +} \ No newline at end of file diff --git a/Kernel/Include/Arch/Exceptions.h b/Kernel/Include/Arch/Exceptions.h new file mode 100644 index 0000000..df48045 --- /dev/null +++ b/Kernel/Include/Arch/Exceptions.h @@ -0,0 +1,67 @@ +#pragma once +#include + +typedef struct ExceptionsContext { + UInt64 x0; + UInt64 x1; + UInt64 x2; + UInt64 x3; + UInt64 x4; + UInt64 x5; + UInt64 x6; + UInt64 x7; + UInt64 x8; + UInt64 x9; + UInt64 x10; + UInt64 x11; + UInt64 x12; + UInt64 x13; + UInt64 x14; + UInt64 x15; + UInt64 x16; + UInt64 x17; + UInt64 x18; + UInt64 x19; + UInt64 x20; + UInt64 x21; + UInt64 x22; + UInt64 x23; + UInt64 x24; + UInt64 x25; + UInt64 x26; + UInt64 x27; + UInt64 x28; + UInt64 x29; // fp + UInt64 x30; // lr + UInt64 elr_el1; // pc + UInt64 spsr_el1; // cpu status + UInt64 esr_el1; // error reason +} ExceptionsContext; + +typedef enum ExceptionsType { + // curr el with sp0 (EL1t) + // usually dont happen cuz we switch to sp_el1, but just in case + ExceptionsSyncEl1t, + ExceptionsIRQEl1t, + ExceptionsFIQEl1t, + ExceptionsSErrorEl1t, + + // curr el with sp1 (EL1h) + // exception in kernel space + ExceptionsSyncEl1h, + ExceptionsIRQEl1h, + ExceptionsFIQEl1h, + ExceptionsSErrorEl1h, + + // lower EL 64-bit from userspace + ExceptionsSyncEl064, + ExceptionsIRQEl064, + ExceptionsFIQEl064, + ExceptionsSErrorEl064, + + // lower EL 32-bit from userspace + ExceptionsSyncEl032, + ExceptionsIRQEl032, + ExceptionsFIQEl032, + ExceptionsSErrorEl032, +} ExceptionsType; \ No newline at end of file diff --git a/Kernel/Include/Arch/IO.h b/Kernel/Include/Arch/IO.h new file mode 100644 index 0000000..91f494b --- /dev/null +++ b/Kernel/Include/Arch/IO.h @@ -0,0 +1,14 @@ +#pragma once +#include + +static inline void IOAddressWrite32(Address address, UInt32 value) { + __asm__ volatile ("dsb sy" ::: "memory"); // wait till all previous writes are finished physically + *(volatile UInt32*)address = value; + __asm__ volatile ("dsb sy" ::: "memory"); // wait till my write is finished physically +} + +static inline UInt32 IOAddressRead32(Address address) { + UInt32 value = *(volatile UInt32*)address; + __asm__ volatile ("dsb ld" ::: "memory"); // wait till my read is finished physically + return value; +} \ No newline at end of file diff --git a/Kernel/Include/IO/Serial.h b/Kernel/Include/IO/Serial.h new file mode 100644 index 0000000..f2d7787 --- /dev/null +++ b/Kernel/Include/IO/Serial.h @@ -0,0 +1,9 @@ +#pragma once +#include + +enum { + kUARTBaseAddress = 0x09000000, // TODO: make it dynamic by parsing DTB +}; + +Int32 IOSerialPutCharacter(ASCII character); +Int32 IOSerialPutString(const ASCII* string); \ No newline at end of file diff --git a/Kernel/Include/Lib/String.h b/Kernel/Include/Lib/String.h new file mode 100644 index 0000000..201334f --- /dev/null +++ b/Kernel/Include/Lib/String.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +void* StringSet(BytePointer destination, ASCII value, Size count); +void* MemoryCopy(void* destination, const void* source, Size count); + +Int32 StringCompare(const ASCII* firstString, const ASCII* secondString); +Int32 StringCompareWithLimit(const ASCII* firstString, const ASCII* secondString, Size limit); + +ASCII* StringCopy(ASCII* destination, const ASCII* source); +ASCII* StringCopyWithLimit(ASCII* destination, const ASCII* source, Size limit); + +Size StringGetLength(const ASCII* string); +const ASCII* StringFindLastOccurrenceOfCharacter(const ASCII* string, ASCII separator); + +Int32 StringFormatVariadic(ASCII* string, Size size, const ASCII* format, va_list args); +Int32 StringFormat(ASCII* destination, UInt64 size, const ASCII* format, ...); \ No newline at end of file diff --git a/Kernel/Include/Lib/VAArgs.h b/Kernel/Include/Lib/VAArgs.h new file mode 100644 index 0000000..da688ef --- /dev/null +++ b/Kernel/Include/Lib/VAArgs.h @@ -0,0 +1,8 @@ +#pragma once + +typedef __builtin_va_list va_list; + +#define va_start(v, l) __builtin_va_start(v, l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v, l) __builtin_va_arg(v, l) +#define va_copy(d, s) __builtin_va_copy(d, s) \ No newline at end of file diff --git a/Kernel/Include/OS/Log.h b/Kernel/Include/OS/Log.h new file mode 100644 index 0000000..5b9d1d2 --- /dev/null +++ b/Kernel/Include/OS/Log.h @@ -0,0 +1,8 @@ +#pragma once +#include + +enum { + kOSLogBufferSize = 1024, +}; + +void OSLog(const ASCII* format, ...); \ No newline at end of file diff --git a/Kernel/Include/OS/Panic.h b/Kernel/Include/OS/Panic.h new file mode 100644 index 0000000..c8d69d5 --- /dev/null +++ b/Kernel/Include/OS/Panic.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +__attribute__((noreturn)) void OSPanicException(ExceptionsContext* frame); \ No newline at end of file diff --git a/Kernel/Include/types.h b/Kernel/Include/types.h new file mode 100644 index 0000000..3e4261f --- /dev/null +++ b/Kernel/Include/types.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once + +typedef unsigned char UInt8; +typedef unsigned short UInt16; +typedef unsigned int UInt32; +typedef unsigned long long UInt64; +typedef unsigned long long UInt; + +typedef UInt Address; +typedef UInt8* BytePointer; +typedef UInt8* MemoryPointer; + +typedef signed char Int8; +typedef signed short Int16; +typedef signed int Int32; +typedef signed long long Int64; +typedef int Int; + +typedef UInt64 Size; +typedef char ASCII; + +typedef _Bool Boolean; diff --git a/Kernel/Source/Arch/Exceptions.c b/Kernel/Source/Arch/Exceptions.c new file mode 100644 index 0000000..532bbb2 --- /dev/null +++ b/Kernel/Source/Arch/Exceptions.c @@ -0,0 +1,8 @@ +#include +#include +#include +#include + +void ExceptionsHandler(ExceptionsContext* frame, ExceptionsType type) { + OSPanicException(frame); +} \ No newline at end of file diff --git a/Kernel/Source/Arch/dtb.swift b/Kernel/Source/Arch/dtb.swift deleted file mode 100644 index 04f8b8b..0000000 --- a/Kernel/Source/Arch/dtb.swift +++ /dev/null @@ -1,81 +0,0 @@ -struct FDTHeader { - let magic: UInt32 // 0xd00dfeed - let totalsize: UInt32 - let off_dt_struct: UInt32 - let off_dt_strings: UInt32 - let off_mem_rsvmap: UInt32 - let version: UInt32 - let last_comp_version: UInt32 - let boot_cpuid_phys: UInt32 - let size_dt_strings: UInt32 - let size_dt_struct: UInt32 -} - -struct FDTProperty { - let len: UInt32 - let nameoff: UInt32 -} - -enum FDTToken: UInt32 { - case beginNode = 0x1 - case endNode = 0x2 - case prop = 0x3 - case nop = 0x4 - case end = 0x9 -} - -extension UInt32 { - var fromBe: UInt32 { - return self.byteSwapped - } -} - -func alignUp(_ value: Int, to: Int) -> Int { - return (value + to - 1) & ~(to - 1) -} - -let kFDTHeaderMagic = 0xd00dfeed - -func parseDTB(at pointer: UnsafeRawPointer) { - let header = pointer.bindMemory(to: FDTHeader.self, capacity: 1).pointee - guard header.magic.byteSwapped == kFDTHeaderMagic else { - return - } - - let structOffset = Int(header.off_dt_struct.byteSwapped) - let stringOffset = Int(header.off_dt_strings.byteSwapped) - - var structBase = pointer.advanced(by: structOffset) - let stringsBase = pointer.advanced(by: stringOffset) - - var finished = false - while !finished { - let rawToken = structBase.load(as: UInt32.self).byteSwapped - structBase = structBase.advanced(by: 4) - - if let token = FDTToken(rawValue: rawToken) { - switch token { - case .beginNode: - let namePointer = structBase.assumingMemoryBound(to: UInt8.self) - let name = String(cString: namePointer) - let nameLength = name.utf8.count + 1 - structBase = structBase.advanced(by: alignUp(nameLength, to: 4)) - case .prop: - let propertyHeader = structBase.load(as: FDTProperty.self) - let dataLength = Int(propertyHeader.len.byteSwapped) - let nameOffset = Int(propertyHeader.nameoff.byteSwapped) - - let namePointer = stringsBase.advanced(by: nameOffset).assumingMemoryBound(to: UInt8.self) - - structBase = structBase.advanced(by: 8) // skip header - structBase = structBase.advanced(by: alignUp(dataLength, to: 4)) // skip data for now - case .endNode: - continue - case .nop: - continue - case .end: - finished = true - } - } - } -} \ No newline at end of file diff --git a/Kernel/Source/Arch/entry.S b/Kernel/Source/Arch/entry.S index 2638f10..af21614 100644 --- a/Kernel/Source/Arch/entry.S +++ b/Kernel/Source/Arch/entry.S @@ -1,7 +1,6 @@ .section .text.boot, "ax" .global _start - _start: - bl kmain -.hang: - b .hang \ No newline at end of file + bl ExceptionsVectorsInit + bl KernelMain + b . \ No newline at end of file diff --git a/Kernel/Source/Arch/vectors.S b/Kernel/Source/Arch/vectors.S new file mode 100644 index 0000000..05e183e --- /dev/null +++ b/Kernel/Source/Arch/vectors.S @@ -0,0 +1,90 @@ +.macro ventry type + .align 7 + sub sp, sp, #272 // save 272 bytes of stack + stp x0, x1, [sp, #0] // move stack + mov x1, #\type // move type to x1 + b ExceptionsTrapEntry +.endm + +.section .text.vectors +.align 11 +.global vectors +ExceptionsVectorsTable: + // EL1t (curr EL with SP0) + ventry 0 // Sync + ventry 1 // IRQ + ventry 2 // FIQ + ventry 3 // SError + + // EL1h (curr EL with SP1) + ventry 4 // Sync + ventry 5 // IRQ + ventry 6 // FIQ + ventry 7 // SError + + // EL0 (lower EL 64-bit from userspace) + ventry 8 // Sync + ventry 9 // IRQ + ventry 10 // FIQ + ventry 11 // SError + + // EL0 (lower EL 32-bit from userspace) + ventry 12; ventry 13; ventry 14; ventry 15 + +ExceptionsTrapEntry: + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + + mrs x21, elr_el1 + mrs x22, spsr_el1 + mrs x23, esr_el1 + + stp x30, x21, [sp, #16 * 15] + stp x22, x23, [sp, #16 * 16] + + mov x0, sp + bl ExceptionsHandler + + ldp x22, x23, [sp, #16 * 16] + msr spsr_el1, x22 + + ldp x30, x21, [sp, #16 * 15] + msr elr_el1, x21 + + ldp x28, x29, [sp, #16 * 14] + ldp x26, x27, [sp, #16 * 13] + ldp x24, x25, [sp, #16 * 12] + ldp x22, x23, [sp, #16 * 11] + ldp x20, x21, [sp, #16 * 10] + ldp x18, x19, [sp, #16 * 9] + ldp x16, x17, [sp, #16 * 8] + ldp x14, x15, [sp, #16 * 7] + ldp x12, x13, [sp, #16 * 6] + ldp x10, x11, [sp, #16 * 5] + ldp x8, x9, [sp, #16 * 4] + ldp x6, x7, [sp, #16 * 3] + ldp x4, x5, [sp, #16 * 2] + ldp x2, x3, [sp, #16 * 1] + ldp x0, x1, [sp, #0] + + add sp, sp, #272 + eret + +.global ExceptionsVectorsInit +ExceptionsVectorsInit: + adr x0, ExceptionsVectorsTable + msr vbar_el1, x0 + isb + ret \ No newline at end of file diff --git a/Kernel/Source/IO/Serial.c b/Kernel/Source/IO/Serial.c new file mode 100644 index 0000000..68f7ef9 --- /dev/null +++ b/Kernel/Source/IO/Serial.c @@ -0,0 +1,25 @@ +#include +#include +#include + +Int32 IOSerialPutCharacter(ASCII character) { + // TXFF -- TRansmit FIFO Full for PL011 is 5 bit of FR reg (0x18) + UInt64 uartFR = kUARTBaseAddress + 0x18; + + while ((IOAddressRead32(uartFR) & (1 << 5)) != 0) { + CPUYield(); + } + + IOAddressWrite32(kUARTBaseAddress, character); + return character; +} + +Int32 IOSerialPutString(const ASCII* string) { + Int i = 0; + while (string[i] != '\0') { + IOSerialPutCharacter(string[i]); + i++; + } + + return i; +} diff --git a/Kernel/Source/IO/uart.swift b/Kernel/Source/IO/uart.swift deleted file mode 100644 index 3d9107f..0000000 --- a/Kernel/Source/IO/uart.swift +++ /dev/null @@ -1,20 +0,0 @@ -let kUARTBaseAddress: UInt = 0x09000000 // TODO: Make it dynamic by parsion DTB - -@_cdecl("putchar") -@discardableResult -public func _serialPutchar(_ char: Int32) -> Int32 { - let uartFR: UInt = kUARTBaseAddress + 0x18// TXFF -- TRansmit FIFO Full for PL011 is 5 bit of FR reg (0x18) - - while (mmio_read32(uartFR) & (1 << 5)) != 0 { - // i love pizza btw - } - - mmio_write32(UInt(kUARTBaseAddress), UInt32(char)) - return char -} - -public func kprint(_ message: StaticString) { - for i in 0.. + +void KernelMain(void) { + OSLog("Hi meow! ;3\n"); +} \ No newline at end of file diff --git a/Kernel/Source/Lib/String.c b/Kernel/Source/Lib/String.c new file mode 100644 index 0000000..5a23995 --- /dev/null +++ b/Kernel/Source/Lib/String.c @@ -0,0 +1,179 @@ +#include +#include + +static void BufferAdd(ASCII* buffer, Size bufferSize, Size* written, ASCII character) { + if (*written + 1 < bufferSize) { + buffer[*written] = character; + } + (*written)++; +} + +void* StringSet(BytePointer destination, ASCII value, Size count) { + BytePointer savedDestination = destination; + while (count--) { + *destination++ = (UInt8) value; + } + return savedDestination; +} + +void* MemoryCopy(void* destination, const void* source, Size count) { + BytePointer destinationBuffer = (BytePointer) destination; + const UInt8* sourceBuffer = (const UInt8*) source; + + while (count >= 8) { + *(UInt64*) destinationBuffer = *(const UInt64*) sourceBuffer; + destinationBuffer += 8; + sourceBuffer += 8; + count -= 8; + } + + while (count > 0) { + *destinationBuffer++ = *sourceBuffer++; + count--; + } + + return destination; +} + +Int32 StringCompare(const ASCII* firstString, const ASCII* secondString) { + while (*firstString && (*firstString == *secondString)) { + firstString++; + secondString++; + } + return *(const UInt8*) firstString - *(const UInt8*) secondString; +} + +Int32 StringCompareWithLimit(const ASCII* firstString, const ASCII* secondString, Size limit) { + while (limit > 0) { + if (*firstString != *secondString) return *(const UInt8*) firstString - *(const UInt8*) secondString; + if (*firstString == '\0') return 0; + firstString++; + secondString++; + limit--; + } + + return 0; +} + +ASCII* StringCopy(ASCII* destination, const ASCII* source) { + ASCII* saved = destination; + while (*source) *destination++ = *source++; + *destination = 0; + return saved; +} + +ASCII* StringCopyWithLimit(ASCII* destination, const ASCII* source, Size limit) { + ASCII* saved = destination; + while (*source && limit > 0) { + *destination++ = *source++; + limit--; + } + while (limit > 0) { + *destination++ = 0; + limit--; + } + return saved; +} + +Size StringGetLength(const ASCII* string) { + Size result = 0; + for (result = 0; string[result]; result++); + return result; +} + +const ASCII* StringFindLastOccurrenceOfCharacter(const ASCII* string, ASCII separator) { + const ASCII* lastSeparator = 0; + do { + if (*string == separator) lastSeparator = string; + } while (*string++); + + return lastSeparator; +} + +Int32 StringFormatVariadic(ASCII* string, Size size, const ASCII* format, va_list args) { + Size written = 0; + for (Size i = 0; format[i] != '\0'; i++) { + if (format[i] == '%') { + i++; + if (format[i] == '\0') break; + switch (format[i]) { + case 's': { + const ASCII* vaArgString = va_arg(args, const ASCII*); + if (!vaArgString) vaArgString = "(null)"; + while (*vaArgString) BufferAdd(string, size, &written, *vaArgString++); + break; + } + case 'c': { + ASCII character = (ASCII)va_arg(args, Int); + BufferAdd(string, size, &written, character); + break; + } + case 'd': { + Int64 number = va_arg(args, Int); + if (number < 0) { + BufferAdd(string, size, &written, '-'); + number = -number; + } + + UInt64 unsignedNumber = (UInt64)number; + ASCII tempBuffer[32]; + Size position = 0; + + if (unsignedNumber == 0) tempBuffer[position++] = '0'; + while (unsignedNumber > 0) { + tempBuffer[position++] = (ASCII)((unsignedNumber % 10) + '0'); + unsignedNumber /= 10; + } + + while (position > 0) BufferAdd(string, size, &written, tempBuffer[--position]); + break; + } + case 'x': + case 'X': { + UInt64 unsignedNumber = va_arg(args, UInt64); + UInt8 padding = (format[i] == 'X') ? 16 : 0; + + ASCII tempBuffer[32]; + Size position = 0; + static const ASCII kHexDigits[] = "0123456789ABCDEF"; + + if (unsignedNumber == 0 && padding == 0) tempBuffer[position++] = '0'; + while (unsignedNumber > 0) { + tempBuffer[position++] = kHexDigits[unsignedNumber % 16]; + unsignedNumber /= 16; + } + + while (position < (Size)padding) tempBuffer[position++] = '0'; + while (position > 0) BufferAdd(string, size, &written, tempBuffer[--position]); + break; + } + case '%': { + BufferAdd(string, size, &written, '%'); + break; + } + default: { + BufferAdd(string, size, &written, '%'); + BufferAdd(string, size, &written, format[i]); + break; + } + } + } else { + BufferAdd(string, size, &written, format[i]); + } + } + + if (size > 0) { + if (written < size) string[written] = '\0'; + else string[size - 1] = '\0'; + } + + return (Int32)written; +} + +Int32 StringFormat(ASCII* destination, UInt64 size, const ASCII* format, ...) { + va_list args; + va_start(args, format); + Int32 returnValue = StringFormatVariadic(destination, size, format, args); + va_end(args); + return returnValue; +} \ No newline at end of file diff --git a/Kernel/Source/OS/Log.c b/Kernel/Source/OS/Log.c new file mode 100644 index 0000000..d69be46 --- /dev/null +++ b/Kernel/Source/OS/Log.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +void OSLog(const ASCII* format, ...) { + ASCII buffer[kOSLogBufferSize]; + + va_list args; + va_start(args, format); + StringFormatVariadic(buffer, kOSLogBufferSize, format, args); + va_end(args); + + IOSerialPutString(buffer); +} \ No newline at end of file diff --git a/Kernel/Source/OS/Panic.c b/Kernel/Source/OS/Panic.c new file mode 100644 index 0000000..13883ac --- /dev/null +++ b/Kernel/Source/OS/Panic.c @@ -0,0 +1,95 @@ +#include +#include +#include + +static const ASCII* GetExceptionClassString(UInt32 class) { + switch (class) { + case 0x00: return "Unknown reason"; + case 0x01: return "Trapped WFI | WFE instruction"; + case 0x07: return "Trapped SIMD | FP instruction"; + case 0x11: return "SVC from EL1"; + case 0x15: return "SVC from EL0"; + case 0x20: return "Instruction abort (LoEL)"; // User Execute Fault + case 0x21: return "Instruction abort (CurrEL)"; // Kernel Execute Fault + case 0x22: return "PC Alignment Fault"; // Jumped to a misaligned address + case 0x24: return "Data abort (LoEL)"; // User Memory Fault + case 0x25: return "Data abort (CurrEL)"; // Kernel Memory Fault + case 0x26: return "SP Alignment Fault"; // SP must be 16-byte aligned + case 0x3C: return "Breakpoint"; + default: return "Reserved"; + } +} + +static void PrintSeparator() { + OSLog("--------------------------------\n"); +} + + +__attribute__((noreturn)) void OSPanicException(ExceptionsContext* frame) { + UInt32 esr = frame->esr_el1; + UInt32 class = (esr >> 26) & 0x3F; // Exception class (Bits 31:26) + UInt32 length = (esr >> 25) & 0x1; // Instruction length (Bit 25) + UInt32 syndrome = esr & 0x1FFFFFF; // Syndrome (Bits 24:0) + + PrintSeparator(); + OSLog("Kernel Panic! :(\n"); + PrintSeparator(); + OSLog("CPU Exception: %s (%d)\n", GetExceptionClassString(class), class); + OSLog("ESR_EL1 (Syndrome): 0x%x (%d)\n", esr, syndrome); + OSLog("Instruction pointer: 0x%x (%s)\n", frame->elr_el1, length ? "32-bit" : "64-bit"); + OSLog("CPU status: 0x%x\n", frame->spsr_el1); + + if (class == 0x25 || class == 0x24 || class == 0x20 || class == 0x21 ) { + PrintSeparator(); + OSLog("Memory abort helper:\n"); + PrintSeparator(); + + UInt64 far = CPUGetFAR(); + OSLog("Faulting address: 0x%x\n", far); + + UInt32 wnr = (syndrome >> 6) & 0x1; + if (class == 0x24 || class == 0x25) { + OSLog("[W] Caused by %s\n", wnr ? "WRITE" : "READ"); + } else { + OSLog("[T] Tried to execute code from NX/Invalid memory\n"); + } + + + UInt32 dfsc = syndrome & 0x3F; + switch (dfsc & 0b111100) { + case 0b000000: OSLog("[P] Reason: Address size fault (Bad pointer format)\n"); break; + case 0b000100: OSLog("[P] Reason: Translation fault\n"); break; + case 0b001100: OSLog("[P] Reason: Permission fault (Page protection violation)\n"); break; + case 0b010000: OSLog("[P] Reason: Synchronous external abort\n"); break; + case 0b100000: OSLog("[P] Reason: Alignment fault\n"); break; + default: OSLog("[P] Reason: Unknown fault code (0x%X)\n", dfsc); break; + } + } + PrintSeparator(); + OSLog("Registers:\n"); + PrintSeparator(); + OSLog("x0 = 0x%X; x1 = 0x%X\n", frame->x0, frame->x1); + OSLog("x2 = 0x%X; x3 = 0x%X\n", frame->x2, frame->x3); + OSLog("x4 = 0x%X; x5 = 0x%X\n", frame->x4, frame->x5); + OSLog("x6 = 0x%X; x7 = 0x%X\n", frame->x6, frame->x7); + OSLog("x8 = 0x%X; x9 = 0x%X\n", frame->x8, frame->x9); + OSLog("x10 = 0x%X; x11 = 0x%X\n", frame->x10, frame->x11); + OSLog("x12 = 0x%X; x13 = 0x%X\n", frame->x12, frame->x13); + OSLog("x14 = 0x%X; x15 = 0x%X\n", frame->x14, frame->x15); + OSLog("x16 = 0x%X; x17 = 0x%X\n", frame->x16, frame->x17); + OSLog("x18 = 0x%X; x19 = 0x%X\n", frame->x18, frame->x19); + OSLog("x20 = 0x%X; x21 = 0x%X\n", frame->x20, frame->x21); + OSLog("x22 = 0x%X; x23 = 0x%X\n", frame->x22, frame->x23); + OSLog("x24 = 0x%X; x25 = 0x%X\n", frame->x24, frame->x25); + OSLog("x26 = 0x%X; x27 = 0x%X\n", frame->x26, frame->x27); + OSLog("\t\tx28 = 0x%X\n", frame->x28); + OSLog("FP = 0x%X; LR = 0x%X\n", frame->x29, frame->x30); + PrintSeparator(); + + OSLog("System halted.\n"); + + while (1) { + CPUDisableInterrupts(); + CPUWaitForInterrupt(); + } +} \ No newline at end of file diff --git a/Kernel/Source/Support/BridgingHeader.h b/Kernel/Source/Support/BridgingHeader.h deleted file mode 100644 index 9208982..0000000 --- a/Kernel/Source/Support/BridgingHeader.h +++ /dev/null @@ -1,10 +0,0 @@ -#include "bootinfo.h" -#include - -static inline void mmio_write32(uintptr_t addr, uint32_t val) { - *(volatile uint32_t *)addr = val; -} - -static inline uint32_t mmio_read32(uintptr_t addr) { - return *(volatile uint32_t *)addr; -} \ No newline at end of file diff --git a/Kernel/Source/Support/stubs.c b/Kernel/Source/Support/stubs.c deleted file mode 100644 index ca96b13..0000000 --- a/Kernel/Source/Support/stubs.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include - -void* memmove(void* dest, const void* src, unsigned long n) { - unsigned char* d = (unsigned char*)dest; - const unsigned char* s = (const unsigned char*)src; - if (d < s) { - while (n--) *d++ = *s++; - } else { - d += n; s += n; - while (n--) *--d = *--s; - } - return dest; -} - -void* memcpy(void* dest, const void* src, unsigned long n) { - return memmove(dest, src, n); -} - -void* memset(void* dest, int c, unsigned long n) { - unsigned char* d = (unsigned char*)dest; - while (n--) *d++ = (unsigned char)c; - return dest; -} - -// Stack protection -long __stack_chk_guard = (long)0xDEADBEEFCAFEBABEULL; -void __stack_chk_fail(void) { while (1); } - -// Swift runtime allocator stubs — should never be called in embedded mode -int posix_memalign(void** ptr, unsigned long align, unsigned long size) { - (void)ptr; (void)align; (void)size; - while (1); -} - -void free(void* ptr) { (void)ptr; } - -// Swift stdlib uses arc4random_buf for Hasher seed — stub with zeroes in bare-metal -void arc4random_buf(void* buf, unsigned long nbytes) { - unsigned char* b = (unsigned char*)buf; - while (nbytes--) *b++ = 0; -} diff --git a/Kernel/Source/kernel.swift b/Kernel/Source/kernel.swift deleted file mode 100644 index a438c9e..0000000 --- a/Kernel/Source/kernel.swift +++ /dev/null @@ -1,4 +0,0 @@ -@_cdecl("kmain") -public func kernelMain(_ bootInfo: UnsafeMutablePointer) { - kprint("Test nya") -} diff --git a/Kernel/cmake/aarch64-bare.cmake b/Kernel/cmake/aarch64-bare.cmake index 3b90c1b..461e28a 100644 --- a/Kernel/cmake/aarch64-bare.cmake +++ b/Kernel/cmake/aarch64-bare.cmake @@ -3,26 +3,53 @@ set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) if(NOT LLVM_BIN) - if(APPLE) + find_program(_CLANG + NAMES clang + HINTS + /opt/homebrew/opt/llvm/bin + /usr/local/opt/llvm/bin + ) + + if(NOT _CLANG AND APPLE) execute_process( COMMAND brew --prefix llvm OUTPUT_VARIABLE LLVM_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE BREW_PREFIX_RESULT ) - set(LLVM_BIN "${LLVM_PREFIX}/bin") - else() - find_program(_CLANG clang) - if(NOT _CLANG) - message(FATAL_ERROR "clang not found.") + if(BREW_PREFIX_RESULT EQUAL 0 AND EXISTS "${LLVM_PREFIX}/bin/clang") + set(_CLANG "${LLVM_PREFIX}/bin/clang") endif() - get_filename_component(LLVM_BIN "${_CLANG}" DIRECTORY) endif() + + if(NOT _CLANG) + message(FATAL_ERROR "clang not found. Set LLVM_BIN or add clang to PATH.") + endif() + + get_filename_component(LLVM_BIN "${_CLANG}" DIRECTORY) endif() set(CMAKE_C_COMPILER "${LLVM_BIN}/clang") set(CMAKE_ASM_COMPILER "${LLVM_BIN}/clang") -set(LLVM_OBJCOPY "${LLVM_BIN}/llvm-objcopy") set(TARGET_TRIPLE aarch64-none-elf) set(CMAKE_C_COMPILER_TARGET ${TARGET_TRIPLE}) set(CMAKE_ASM_COMPILER_TARGET ${TARGET_TRIPLE}) + +if(APPLE) + find_program(TERMOS_LD_LLD NAMES ld.lld HINTS /usr/local/bin /opt/homebrew/bin REQUIRED) + set(CMAKE_C_LINK_FLAGS "") + set(CMAKE_C_LINK_EXECUTABLE + "${TERMOS_LD_LLD} -o ") +endif() + +find_program(LLVM_OBJCOPY NAMES llvm-objcopy objcopy + HINTS + "${LLVM_BIN}" + /usr/local/opt/llvm/bin + /opt/homebrew/opt/llvm/bin + /usr/local/bin + /opt/homebrew/bin + REQUIRED +) diff --git a/Kernel/justfile b/Kernel/justfile index 417ef27..d621268 100644 --- a/Kernel/justfile +++ b/Kernel/justfile @@ -6,8 +6,9 @@ build: cmake --build {{TEMP_DIR}}/Kernel - cp {{TEMP_DIR}}/Kernel/kernel.bin {{BUILD_DIR}}/Kernel/ksOSKernel.bin + cp {{TEMP_DIR}}/Kernel/Kernel.bin {{BUILD_DIR}}/Kernel/ksOSKernel.bin @echo "✅ Kernel ready at: {{BUILD_DIR}}/Kernel/ksOSKernel.bin" clean: - rm -rf {{TEMP_DIR}}/Kernel \ No newline at end of file + rm -rf {{TEMP_DIR}}/Kernel + rm -f compile_commands.json \ No newline at end of file diff --git a/justfile b/justfile index 0566df1..d843608 100644 --- a/justfile +++ b/justfile @@ -86,4 +86,8 @@ _prep: just run @clean: + just Bootloader clean + just Kernel clean rm -rf {{BUILD_DIR}} + rm -f compile_commands.json + rm -f ide-swift-toolchain.txt \ No newline at end of file