diff --git a/targets/_any/SConscript b/targets/_any/SConscript index bb83a35..c0c4c32 100644 --- a/targets/_any/SConscript +++ b/targets/_any/SConscript @@ -49,6 +49,7 @@ any_target_sources = Split(''' src/os/draw.cpp src/os/memory.cpp src/os/panic.cpp + src/os/syscall.cpp src/os/tty.cpp src/os/resources/lat9-08.psf.s diff --git a/targets/_any/include/os/cpu.hpp b/targets/_any/include/os/cpu.hpp new file mode 100644 index 0000000..f376e09 --- /dev/null +++ b/targets/_any/include/os/cpu.hpp @@ -0,0 +1,57 @@ + +#pragma once + +#if !defined(BAD_APPLE_OS_CPU_HPP_INCLUDED) +#define BAD_APPLE_OS_CPU_HPP_INCLUDED + +#include + +namespace baos::cpu +{ +enum class MSR : std::uint32_t +{ + IA32_EFER = 0xC000'0080, + STAR = 0xC000'0081, // segments for syscall + LSTAR = 0xC000'0082, // instruction pointer for syscall +}; +inline constexpr std::uint64_t IA32_EFER_SYSTEM_CALL_EXTENSIONS_BIT = (1 << 0); +inline constexpr std::uint64_t IA32_EFER_LONG_MODE_ENABLE_BIT = (1 << 8); +inline constexpr std::uint64_t IA32_EFER_LONG_MODE_ACTIVE_BIT = (1 << 10); +inline constexpr std::uint64_t IA32_EFER_NO_EXECUTE_ENABLE_BIT = (1 << 11); +// ... + +inline std::uint64_t readMSR(MSR msr) noexcept +{ + std::uint32_t lowHalf = 0; + std::uint32_t highHalf = 0; + __asm__ __volatile__( + "rdmsr" + : "=a"(lowHalf), "=d"(highHalf) + : "c"(msr) + ); + return static_cast(highHalf) << 32 | lowHalf; +} + +inline void writeMSR(MSR msr, std::uint64_t value) noexcept +{ + std::uint32_t lowHalf = static_cast(value & 0xFFFFFFFF); + std::uint32_t highHalf = static_cast(value >> 32); + __asm__ __volatile__( + "wrmsr" + : + : "a"(lowHalf), "d"(highHalf), "c"(msr) + ); +} + +inline void setMSRBits(MSR msr, std::uint64_t bits) noexcept +{ + writeMSR(msr, readMSR(msr) | bits); +} + +inline void unsetMSRBits(MSR msr, std::uint64_t bits) noexcept +{ + writeMSR(msr, readMSR(msr) & ~bits); +} +} + +#endif // !defined(BAD_APPLE_OS_CPU_HPP_INCLUDED) diff --git a/targets/_any/include/os/segments.hpp b/targets/_any/include/os/segments.hpp new file mode 100644 index 0000000..1d5e93b --- /dev/null +++ b/targets/_any/include/os/segments.hpp @@ -0,0 +1,18 @@ + +#pragma once + +#if !defined(BAD_APPLE_OS_SEGMENTS_HPP_INCLUDED) +#define BAD_APPLE_OS_SEGMENTS_HPP_INCLUDED + +#include + +namespace baos +{ +inline constexpr std::uint16_t SEGIDX_KERNEL_CODE = 1 << 3; +inline constexpr std::uint16_t SEGIDX_KERNEL_DATA = 2 << 3; +inline constexpr std::uint16_t SEGIDX_USER_CODE = 4 << 3; +inline constexpr std::uint16_t SEGIDX_USER_DATA = 3 << 3; +inline constexpr std::uint16_t SEGIDX_TSS = 5 << 3; +} + +#endif // !defined(BAD_APPLE_OS_SEGMENTS_HPP_INCLUDED) diff --git a/targets/_any/include/os/syscall.hpp b/targets/_any/include/os/syscall.hpp new file mode 100644 index 0000000..540ac10 --- /dev/null +++ b/targets/_any/include/os/syscall.hpp @@ -0,0 +1,33 @@ + +#pragma once + +#if !defined(BAD_APPLE_OS_SYSCALL_HPP_INCLUDED) +#define BAD_APPLE_OS_SYSCALL_HPP_INCLUDED + +#include +#include + +namespace baos +{ +enum class Syscall : std::uint64_t +{ + FILE_READ = 0, + FILE_WRITE = 1 +}; + +void setupSyscall() noexcept; + +template +inline void doSyscall(Syscall cmd, TParam0 param0 = 0, TParam1 param1 = 0, TParam2 param2 = 0) noexcept +{ + register std::uint64_t r8 asm("r8") = param2; + __asm__ __volatile__( + "syscall" + : + : "D"(cmd), "S"(param0), "d"(param1) + : "%rcx" + ); +} +} + +#endif // !defined(BAD_APPLE_OS_SYSCALL_HPP_INCLUDED) diff --git a/targets/_any/src/app/main.cpp b/targets/_any/src/app/main.cpp index 8c043c1..84b6127 100644 --- a/targets/_any/src/app/main.cpp +++ b/targets/_any/src/app/main.cpp @@ -110,8 +110,6 @@ void cmdDumpPageTable() noexcept extern "C" void main() { - - // __asm__ __volatile__("cli"); std::string cmd; while(true) { diff --git a/targets/_any/src/cstdlib/stdio.cpp b/targets/_any/src/cstdlib/stdio.cpp index 8741911..56351ad 100644 --- a/targets/_any/src/cstdlib/stdio.cpp +++ b/targets/_any/src/cstdlib/stdio.cpp @@ -3,7 +3,7 @@ #include #include -#include "os/serial.hpp" +#include "os/syscall.hpp" #include "os/tty.hpp" #include "os/tools/printf_helper.hpp" #include "os/tools/ringbuffer.hpp" @@ -113,6 +113,7 @@ FILE* __stdin = &gStdin; int putchar(int chr) noexcept { +#if 0 tty::putChar(static_cast(chr)); if (chr == '\n') { @@ -124,10 +125,17 @@ int putchar(int chr) noexcept serialWrite(PORT_COM1, static_cast(chr)); } return 0; +#endif + char asChar = static_cast(chr); + baos::doSyscall(baos::Syscall::FILE_WRITE, 0, &asChar, 1); + return 0; } int puts(const char* str) noexcept { + baos::doSyscall(baos::Syscall::FILE_WRITE, 0, str, std::strlen(str)); + return 0; +#if 0 while (*str) { putchar(*str); @@ -135,6 +143,7 @@ int puts(const char* str) noexcept } putchar('\n'); return 0; +#endif } int printf(const char* format, ...) noexcept diff --git a/targets/_any/src/drivers/pic.cpp b/targets/_any/src/drivers/pic.cpp index 2b49610..7d64c8b 100644 --- a/targets/_any/src/drivers/pic.cpp +++ b/targets/_any/src/drivers/pic.cpp @@ -78,6 +78,7 @@ bool initPICs(std::uint8_t masterOffset, std::uint8_t slaveOffset) noexcept return true; } +__attribute__((no_caller_saved_registers)) void sendEndOfInterrupt(std::uint8_t irq) noexcept { if (irq >= 8) { diff --git a/targets/_any/src/os/memory.cpp b/targets/_any/src/os/memory.cpp index 287c4ab..2ef377c 100644 --- a/targets/_any/src/os/memory.cpp +++ b/targets/_any/src/os/memory.cpp @@ -24,10 +24,9 @@ struct MemoryRangeAllocationInfo }; static_assert(sizeof(MemoryRangeAllocationInfo) == 4096); -inline constexpr unsigned MAX_USABLE_RANGES = 32; +inline constexpr unsigned MAX_USABLE_RANGES = 512; std::array gUsableMemoryRanges; -std::array gRangeAllocationInfo; -std::uint8_t gNumUsableRanges = 0; +unsigned gNumUsableRanges = 0; // initial 2MB page for setting up the page table // std::array gInitPage alignas(2ul << 20); @@ -75,6 +74,7 @@ PageTableEntry& getOrCreatePageTableEntry(PageTable& pageTable, std::uint16_t in entry.address = std::bit_cast(&childTable) >> 12; entry.writable = true; entry.present = true; + entry.user = true; } return entry; } @@ -111,60 +111,16 @@ void addUsableRange(const MemoryRange& range) noexcept return; } gUsableMemoryRanges[gNumUsableRanges] = range; - gRangeAllocationInfo[gNumUsableRanges].numFreePages = range.numPages; ++gNumUsableRanges; } -std::uint64_t findFreePages(unsigned numConsecutive) noexcept -{ - if (numConsecutive == 0 || numConsecutive > 4086) { - return 0; - } - if (numConsecutive != 1) { - return 0; // TODO!!! - } - - for (std::uint8_t rangeIdx = 0; rangeIdx < gNumUsableRanges; ++rangeIdx) - { - std::uint64_t page = gUsableMemoryRanges[rangeIdx].pageBase; - for (MemoryRangeAllocationInfo* allocInfo = &gRangeAllocationInfo[rangeIdx]; allocInfo != nullptr; page += allocInfo->usedPages.size(), allocInfo = allocInfo->next) - { - if (allocInfo->numFreePages < numConsecutive) { - continue; - } - for (unsigned pagesIdx = 0; pagesIdx < allocInfo->usedPages.size(); ++pagesIdx) - { - if (allocInfo->usedPages[pagesIdx] != 0xFF) - { - for (unsigned bit = 0; bit < 8; ++bit) - { - if ((allocInfo->usedPages[pagesIdx] & (1 << bit)) == 0) - { - allocInfo->usedPages[pagesIdx] |= (1 << bit); - return page + (8 * pagesIdx) + bit; - } - } - } - } - } - } - return 0; -} - -PageTable& setupPageTableEntry(PageTableEntry& entry, std::uint64_t page) noexcept -{ - entry.address = page; - entry.present = true; - entry.writable = true; - return *(::new(std::bit_cast(entry.address << 12)) PageTable); -} - /** Finds a free memory range using the provided constraints (size and page alignment). */ bool findFreeMemory(std::size_t numPages, std::size_t pageAlignment, std::uint64_t& outPage, MemoryRange*& outRange) noexcept { // first try to find a range that already is already aligned properly - for (MemoryRange& range : gUsableMemoryRanges) + for (unsigned rangeIdx = 0; rangeIdx < gNumUsableRanges; ++rangeIdx) { + MemoryRange& range = gUsableMemoryRanges[rangeIdx]; if (range.numPages >= numPages && range.pageBase % pageAlignment == 0) { outPage = range.pageBase; @@ -176,8 +132,9 @@ bool findFreeMemory(std::size_t numPages, std::size_t pageAlignment, std::uint64 return false; // no need to search further, everything is full! } // now just try to find a range that could be used (needs to be split then) - for (MemoryRange& range : gUsableMemoryRanges) + for (unsigned rangeIdx = 0; rangeIdx < gNumUsableRanges; ++rangeIdx) { + MemoryRange& range = gUsableMemoryRanges[rangeIdx]; const std::uint64_t possibleBase = std::alignUp(range.pageBase, pageAlignment); // needs to be 2MB aligned if (range.numPages - (possibleBase - range.pageBase) >= numPages) { @@ -239,6 +196,12 @@ bool allocateIdentityMappedPages(std::size_t numPages, std::size_t pageAlignment bool setupPaging(std::span memoryRanges) noexcept { + std::uint32_t eax, ebx, ecx, edx; + + // TODO: apparently it's not supported in Qemu (or on the host CPU, who knows?) so don't waste time on it + // __cpuid(0x8000'0001, eax, ebx, ecx, edx); + // g1GBPagesSupported = (edx & (1 << 26)) != 0; + std::uint64_t firstKernelPage = std::bit_cast(&gKernelStart) / 4096; std::uint64_t lastKernelPage = std::bit_cast(&gKernelEnd) / 4096 + 1; for (MemoryRange range : memoryRanges) @@ -309,18 +272,6 @@ const char* memoryTypeName(MemoryType type) noexcept return ""; } -void* allocatePages(unsigned numConsecutive) noexcept -{ - const std::uint64_t firstPage = findFreePages(numConsecutive); - if (firstPage == 0) - { - return nullptr; - } - setupIdentityPaging(firstPage, firstPage + numConsecutive); - return std::bit_cast(firstPage << 12); -} - - void setupIdentityPaging(std::uint64_t page) noexcept { PageTableEntry& pageTableEntry = getOrCreatePage(page); diff --git a/targets/_any/src/os/syscall.cpp b/targets/_any/src/os/syscall.cpp new file mode 100644 index 0000000..e5f07e5 --- /dev/null +++ b/targets/_any/src/os/syscall.cpp @@ -0,0 +1,50 @@ + +#include "os/syscall.hpp" + +#include +#include +#include "os/cpu.hpp" +#include "os/segments.hpp" +#include "os/serial.hpp" +#include "os/tty.hpp" + +extern "C" void __handleSyscall(); + +namespace baos +{ +void setupSyscall() noexcept +{ + // enable syscall/sysexit + cpu::setMSRBits(cpu::MSR::IA32_EFER, cpu::IA32_EFER_SYSTEM_CALL_EXTENSIONS_BIT); + cpu::writeMSR(cpu::MSR::LSTAR, std::bit_cast(&__handleSyscall)); + cpu::writeMSR(cpu::MSR::STAR, static_cast(SEGIDX_KERNEL_CODE) << 32 | static_cast(SEGIDX_KERNEL_DATA) << 48); +} + +void sysFileRead(unsigned fileDescriptor, char* buffer, std::size_t count) noexcept +{ + // TODO +} + +void sysFileWrite(unsigned fileDescriptor, const char* buffer, std::size_t count) noexcept +{ + for (std::size_t index = 0; index < count; ++index) { + serialWrite(PORT_COM1, buffer[index]); + } + tty::write(buffer, count); +} +} + +extern "C" void __baosSyscall(baos::Syscall cmd, std::uint64_t param0, std::uint64_t param1, std::uint64_t param2) noexcept +{ + using namespace baos; + + switch (cmd) + { + case Syscall::FILE_READ: + sysFileRead(static_cast(param0), std::bit_cast(param1), param2); + break; + case Syscall::FILE_WRITE: + sysFileWrite(static_cast(param0), std::bit_cast(param1), param2); + break; + } +} diff --git a/targets/x86_64/src/kernel/boot.s b/targets/x86_64/src/kernel/boot.s index 248d6a6..b2e4dab 100644 --- a/targets/x86_64/src/kernel/boot.s +++ b/targets/x86_64/src/kernel/boot.s @@ -94,7 +94,7 @@ __flushTSS: .global __enterUsermode .type __enterUsermode @function -// void __enterUsermode(uint16_t codeSegment [%di], uint16_t dataSegment [%si], void* rsp [%rdx], void* function [%rcx]) +// void __enterUsermode(uint16_t codeSegment [%di], uint16_t dataSegment [%si], void* rsp [%rdx], void* function [%rcx], void* stackPtr [r8]) __enterUsermode: orw $0x3, %di // we are switching to ring 3, therefore set the bottom 2 bits to 3 orw $0x3, %si // same for the data segment @@ -104,15 +104,25 @@ __enterUsermode: movw %si, %gs movq %rsp, (%rdx) - movq %rsp, %rax pushq %rsi // data segment - pushq %rax + pushq %r8 pushf // flags pushq %rdi // code segment pushq %rcx iretq - +.global __handleSyscall +.type __handleSyscall @function +// void __handleSyscall() +__handleSyscall: + pushq %rcx // store the original instruction pointer + pushq %rsp // also store the original stack pointer + movq %r8, %rcx + // movq (__kernel_rsp), %rsp + call __baosSyscall + popq %rsp + popq %rcx + sysretq /* Set the size of the _start symbol to the current location '.' minus its start. diff --git a/targets/x86_64/src/kernel/startup.cpp b/targets/x86_64/src/kernel/startup.cpp index 0e33463..3497814 100644 --- a/targets/x86_64/src/kernel/startup.cpp +++ b/targets/x86_64/src/kernel/startup.cpp @@ -5,11 +5,12 @@ #include #include #include -#include #include "os/draw.hpp" #include "os/memory.hpp" +#include "os/segments.hpp" #include "os/serial.hpp" +#include "os/syscall.hpp" #include "os/tty.hpp" #include "drivers/pci.hpp" #include "drivers/pic.hpp" @@ -56,18 +57,12 @@ constinit std::array GDT alignas(16) = { SEGMENT_NULL, SEGMENT_KERNEL_CODE, SEGMENT_KERNEL_DATA, - SEGMENT_USER_CODE, SEGMENT_USER_DATA, + SEGMENT_USER_CODE, SEGMENT_NULL, // TSS lower SEGMENT_NULL // TSS higher }; -inline constexpr std::uint16_t SEGIDX_KERNEL_CODE = 1 << 3; -inline constexpr std::uint16_t SEGIDX_KERNEL_DATA = 2 << 3; -inline constexpr std::uint16_t SEGIDX_USER_CODE = 3 << 3; -inline constexpr std::uint16_t SEGIDX_USER_DATA = 4 << 3; -inline constexpr std::uint16_t SEGIDX_TSS = 5 << 3; - constinit std::array IDT alignas(16); @@ -153,7 +148,7 @@ extern "C" void __setGDT(uint16_t limit, void* base); extern "C" void __setIDT(uint16_t limit, void* base); extern "C" void __reloadSegments(uint64_t code, uint64_t data); extern "C" void __flushTSS(uint16_t segment); -extern "C" void __enterUsermode(uint16_t codeSegment, uint16_t dataSegment, void* rsp, void (*function)()); +extern "C" void __enterUsermode(uint16_t codeSegment, uint16_t dataSegment, void* rsp, void (*function)(), void* stackPtr); template void setupInterrupt(std::uint8_t index, THandler handler) noexcept @@ -222,6 +217,8 @@ void kernel_main() __reloadSegments(SEGIDX_KERNEL_CODE, SEGIDX_KERNEL_DATA); __flushTSS(SEGIDX_TSS); + setupSyscall(); + const bool picsInited = initPICs(MASTER_PIC_OFFSET, SLAVE_PIC_OFFSET); unmaskIRQ(1); @@ -236,7 +233,10 @@ void kernel_main() // init the heap (required for the double buffer) const std::vector ranges = detectMemoryRangesFromEfi(gBootInfo->memoryMap); - setupPaging({ranges.begin(), ranges.end()}); + if (!setupPaging({ranges.begin(), ranges.end()})) + { + panic("Error setting up paging."); + } // make sure boot info and the frame buffer are accessible identityMapRegion(gBootInfo, sizeof(*gBootInfo)); @@ -244,8 +244,6 @@ void kernel_main() initGlobals(); - // initHeapFromEfiMemoryMap(gBootInfo->memoryMap); - // initialize the framebuffer draw::initializeDefaultFramebuffer({ /* base = */ static_cast(gBootInfo->displayInfo.frameBufferBase), @@ -257,11 +255,6 @@ void kernel_main() // initialize terminal interface tty::initialize(); - for (const MemoryRange& range : ranges) - { - std::printf("Start: 0x%lX, Pages: %ld, Type: %s\n", range.pageBase << 12, range.numPages, memoryTypeName(range.type)); - } - if (!initSerialPort(PORT_COM1)) { tty::write("Error initializing serial port.\n"); @@ -269,6 +262,11 @@ void kernel_main() } serialWriteString(PORT_COM1, "\r\n\r\n"); // write newlines to separate from UEFI output + for (const MemoryRange& range : ranges) + { + // std::printf("Start: 0x%lX, Pages: %ld, Type: %s\n", range.pageBase << 12, range.numPages, memoryTypeName(range.type)); + } + if (!picsInited) { std::puts("Error initializing the PICs!\n"); @@ -277,7 +275,7 @@ void kernel_main() if (!ps2Inited) { - std::puts("Error initializing the PS2 controller!!\n"); + std::puts("Error initializing the PS2 controller!\n"); return; } @@ -286,11 +284,7 @@ void kernel_main() initializePCIDevice(header); } - std::puts("This is BadAppleOS and everything is fine!\n"); - - // __enterUsermode(SEGIDX_USER_CODE, SEGIDX_USER_DATA, &tss.rsp0, &main); - main(); // for now run it in kernel mode, user mode doesn't work yet - - + static std::array usermodeStack; + __enterUsermode(SEGIDX_USER_CODE, SEGIDX_USER_DATA, &tss.rsp0, &main, &usermodeStack.back()); } } // extern "C"