Implemented syscalls and jump to usermode.

This commit is contained in:
Patrick 2024-01-27 01:56:51 +01:00
parent 9c70cfa846
commit edd9ee85c7
11 changed files with 215 additions and 93 deletions

View File

@ -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

View 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)

View 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)

View 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)

View File

@ -110,8 +110,6 @@ void cmdDumpPageTable() noexcept
extern "C" void main()
{
// __asm__ __volatile__("cli");
std::string cmd;
while(true)
{

View File

@ -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

View File

@ -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) {

View File

@ -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);

View 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;
}
}

View File

@ -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.

View File

@ -5,11 +5,12 @@
#include <bit>
#include <cassert>
#include <cstdio>
#include <memory>
#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<InterruptDescriptor, 256> 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<typename THandler>
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<MemoryRange> 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<draw::Pixel*>(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<std::uint8_t, 16384> usermodeStack;
__enterUsermode(SEGIDX_USER_CODE, SEGIDX_USER_DATA, &tss.rsp0, &main, &usermodeStack.back());
}
} // extern "C"