Implemented syscalls and jump to usermode.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
57
targets/_any/include/os/cpu.hpp
Normal file
57
targets/_any/include/os/cpu.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_CPU_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_CPU_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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<std::uint64_t>(highHalf) << 32 | lowHalf;
|
||||
}
|
||||
|
||||
inline void writeMSR(MSR msr, std::uint64_t value) noexcept
|
||||
{
|
||||
std::uint32_t lowHalf = static_cast<std::uint32_t>(value & 0xFFFFFFFF);
|
||||
std::uint32_t highHalf = static_cast<std::uint32_t>(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)
|
||||
18
targets/_any/include/os/segments.hpp
Normal file
18
targets/_any/include/os/segments.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_SEGMENTS_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_SEGMENTS_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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)
|
||||
33
targets/_any/include/os/syscall.hpp
Normal file
33
targets/_any/include/os/syscall.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_SYSCALL_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_SYSCALL_HPP_INCLUDED
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
|
||||
namespace baos
|
||||
{
|
||||
enum class Syscall : std::uint64_t
|
||||
{
|
||||
FILE_READ = 0,
|
||||
FILE_WRITE = 1
|
||||
};
|
||||
|
||||
void setupSyscall() noexcept;
|
||||
|
||||
template<typename TParam0, typename TParam1, typename TParam2>
|
||||
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)
|
||||
@@ -110,8 +110,6 @@ void cmdDumpPageTable() noexcept
|
||||
|
||||
extern "C" void main()
|
||||
{
|
||||
|
||||
// __asm__ __volatile__("cli");
|
||||
std::string cmd;
|
||||
while(true)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdarg.h>
|
||||
#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<char>(chr));
|
||||
if (chr == '\n')
|
||||
{
|
||||
@@ -124,10 +125,17 @@ int putchar(int chr) noexcept
|
||||
serialWrite(PORT_COM1, static_cast<std::uint8_t>(chr));
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
char asChar = static_cast<char>(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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<MemoryRange, MAX_USABLE_RANGES> gUsableMemoryRanges;
|
||||
std::array<MemoryRangeAllocationInfo, MAX_USABLE_RANGES> gRangeAllocationInfo;
|
||||
std::uint8_t gNumUsableRanges = 0;
|
||||
unsigned gNumUsableRanges = 0;
|
||||
|
||||
// initial 2MB page for setting up the page table
|
||||
// std::array<std::uint8_t, 2ul << 20> gInitPage alignas(2ul << 20);
|
||||
@@ -75,6 +74,7 @@ PageTableEntry& getOrCreatePageTableEntry(PageTable& pageTable, std::uint16_t in
|
||||
entry.address = std::bit_cast<std::uint64_t>(&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<void*>(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<const MemoryRange> 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<std::uint64_t>(&gKernelStart) / 4096;
|
||||
std::uint64_t lastKernelPage = std::bit_cast<std::uint64_t>(&gKernelEnd) / 4096 + 1;
|
||||
for (MemoryRange range : memoryRanges)
|
||||
@@ -309,18 +272,6 @@ const char* memoryTypeName(MemoryType type) noexcept
|
||||
return "<INVALID>";
|
||||
}
|
||||
|
||||
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<void*>(firstPage << 12);
|
||||
}
|
||||
|
||||
|
||||
void setupIdentityPaging(std::uint64_t page) noexcept
|
||||
{
|
||||
PageTableEntry& pageTableEntry = getOrCreatePage(page);
|
||||
|
||||
50
targets/_any/src/os/syscall.cpp
Normal file
50
targets/_any/src/os/syscall.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
#include "os/syscall.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#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<std::uint64_t>(&__handleSyscall));
|
||||
cpu::writeMSR(cpu::MSR::STAR, static_cast<std::uint64_t>(SEGIDX_KERNEL_CODE) << 32 | static_cast<std::uint64_t>(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<unsigned>(param0), std::bit_cast<char*>(param1), param2);
|
||||
break;
|
||||
case Syscall::FILE_WRITE:
|
||||
sysFileWrite(static_cast<unsigned>(param0), std::bit_cast<const char*>(param1), param2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user