From: Clyne Sullivan Date: Mon, 30 Sep 2024 15:08:46 +0000 (-0400) Subject: reorganize files X-Git-Url: https://code.bitgloo.com/?a=commitdiff_plain;h=19d9a04e36e7fb96eebe89e24311408460c29a70;p=clyne%2Fosdev.git reorganize files --- diff --git a/.gitignore b/.gitignore index 540202a..afda4de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -*.bin *.iso -*.o *.sw* iso/boot/*.bin +out diff --git a/Makefile b/Makefile index 728ead6..2484913 100644 --- a/Makefile +++ b/Makefile @@ -2,43 +2,35 @@ CXXFLAGS := -m32 -ggdb -g3 -O1 -fno-pic -fno-rtti -fno-exceptions -std=c++23 \ -Wall -Wextra -pedantic -Werror LDFLAGS := $(CXXFLAGS) -T link.ld -static -nostdlib -fno-use-cxa-atexit -CXXFILES := acpi.cpp \ - ata.cpp \ - boot.cpp \ - gdt.cpp \ - idt.cpp \ - keyboard.cpp \ - memmove.cpp \ - memory.cpp \ - multiboot.cpp \ - pic.cpp \ - pit.cpp \ - tasking.cpp \ - vgaterminal.cpp \ - kernel.cpp - -OBJS := $(subst .cpp,.o,$(CXXFILES)) - -all: myos.iso - -myos.iso: myos.bin iso/boot/grub/grub.cfg +CXXFILES := $(wildcard src/*.cpp) + +PROJECT := myos +PRJISO := $(PROJECT).iso +PRJBIN := $(PROJECT).bin +OBJS := $(patsubst src/%.cpp,out/%.o,$(CXXFILES)) + +all: $(PRJISO) + +$(PRJISO): iso/boot/$(PRJBIN) iso/boot/grub/grub.cfg @echo " ISO " $@ - @cp myos.bin iso/boot/ - @grub-mkrescue -o myos.iso iso/ + @grub-mkrescue -o $@ iso/ -myos.bin: $(OBJS) link.ld +iso/boot/$(PRJBIN): $(OBJS) link.ld @echo " LD " $@ @g++ $(LDFLAGS) -o $@ $(OBJS) -%.o: %.cpp +out/: + @mkdir out + +out/%.o: src/%.cpp @echo " CXX " $< @g++ $(CXXFLAGS) -c $< -o $@ clean: @echo " CLEAN" - @rm -f $(OBJS) myos.bin myos.iso + @rm -f $(OBJS) $(PRJBIN) $(PRJISO) run: myos.iso @echo " QEMU" - @qemu-system-i386 -drive file=$<,index=2,media=cdrom -monitor stdio -no-reboot -s -S #-d int + @qemu-system-i386 -drive file=$<,index=2,media=cdrom -monitor stdio -no-reboot #-s -S #-d int diff --git a/acpi.cpp b/acpi.cpp deleted file mode 100644 index b74a7dc..0000000 --- a/acpi.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "textoutput.hpp" - -#include - -extern TextOutput& term; -extern std::uint32_t *acpiRsdp; -extern std::uint32_t *acpiRsdpV2; - -struct XSDP { - char Signature[8]; - std::uint8_t Checksum; - char OEMID[6]; - std::uint8_t Revision; - std::uint32_t RsdtAddress; // deprecated since version 2.0 - - // v2 only! - std::uint32_t Length; - std::uint64_t XsdtAddress; - std::uint8_t ExtendedChecksum; - std::uint8_t reserved[3]; -} __attribute__ ((packed)); - -struct SDTHeader { - char Signature[4]; - std::uint32_t Length; - std::uint8_t Revision; - std::uint8_t Checksum; - char OEMID[6]; - char OEMTableID[8]; - std::uint32_t OEMRevision; - std::uint32_t CreatorID; - std::uint32_t CreatorRevision; -}; - -static XSDP *rsdp = nullptr; - -void acpi_initialize() -{ - if (acpiRsdp) { - term.write("ACPI v1 detected.\n"); - rsdp = reinterpret_cast(acpiRsdp); - } else if (acpiRsdpV2) { - term.write("ACPI v2 detected, treating as v1.\n"); - rsdp = reinterpret_cast(acpiRsdpV2); - } - - //if (rsdp) { - // auto sdt = reinterpret_cast(rsdp->RsdtAddress); - //} -} - diff --git a/acpi.hpp b/acpi.hpp deleted file mode 100644 index 8154b81..0000000 --- a/acpi.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef ACPI_HPP -#define ACPI_HPP - -void acpi_initialize(); - -#endif // ACPI_HPP - diff --git a/ata.cpp b/ata.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/ata.hpp b/ata.hpp deleted file mode 100644 index 5394c31..0000000 --- a/ata.hpp +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef ATA_HPP -#define ATA_HPP - -#include "portio.hpp" - -#include -#include - -namespace ATA -{ - enum class Type { - None, - ATA, - ATAPI - }; - - enum class Base : std::uint16_t { - Primary = 0x01F0, - Secondary = 0x0170 - }; - - enum class Drive : std::uint8_t { - Master = 0xA0, - Slave = 0xB0 - }; - - enum class Command : std::uint8_t { - Identify = 0xEC, - IdentifyPacketDevice = 0xA1 - }; - - namespace Status { - static constexpr std::uint8_t Busy = 0x80; - static constexpr std::uint8_t Ready = 0x40; - static constexpr std::uint8_t DF = 0x20; - static constexpr std::uint8_t DSC = 0x10; - static constexpr std::uint8_t DRQ = 0x08; - static constexpr std::uint8_t CORR = 0x04; - static constexpr std::uint8_t Index = 0x02; - static constexpr std::uint8_t Error = 0x01; - } - - static constexpr std::uint8_t ATAPIIdentify0 = 0x14; - static constexpr std::uint8_t ATAPIIdentify1 = 0xEB; - - using enum Base; - using enum Drive; - - template - struct Bus - { - template - using BPort = Port; - - [[no_unique_address]] BPort data; - [[no_unique_address]] BPort errFeats; - [[no_unique_address]] BPort count; - [[no_unique_address]] BPort lba0; - [[no_unique_address]] BPort lba1; - [[no_unique_address]] BPort lba2; - [[no_unique_address]] BPort select; - [[no_unique_address]] BPort cmdStat; - - Type identify(Drive drv) { - auto type = Type::None; - - data = std::to_underlying(drv); - count = '\0'; - lba0 = '\0'; - lba1 = '\0'; - lba2 = '\0'; - cmdStat = std::to_underlying(Command::Identify); - - if (cmdStat == '\0') - return type; - - type = Type::ATA; - while (cmdStat & Status::Busy); - - std::uint8_t stat; - do { - stat = cmdStat; - - if (stat & Status::Error) { - if (lba1 == ATAPIIdentify0 && lba2 == ATAPIIdentify1) { - type = identifyAtapi(drv); - break; - } else { - return type; - } - } - } while (!(stat & Status::DRQ)); - - if (type != Type::None) { - for (int i = 0; i < 256; ++i) { - volatile std::uint16_t w = data; - (void)w; - } - } - - return type; - } - - Type identifyAtapi(Drive drv) { - data = std::to_underlying(drv); - count = '\0'; - lba0 = '\0'; - lba1 = '\0'; - lba2 = '\0'; - cmdStat = std::to_underlying(Command::IdentifyPacketDevice); - - if (cmdStat == '\0') - return Type::None; - - while (cmdStat & Status::Busy); - - std::uint8_t stat; - do { - stat = cmdStat; - - if (stat & Status::Error) - return Type::None; - } while (!(stat & Status::DRQ)); - - return Type::ATAPI; - } - }; -} // ATA - -#endif // ATA_HPP - diff --git a/boot.cpp b/boot.cpp deleted file mode 100644 index 6b1da6a..0000000 --- a/boot.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -extern void (*__init_array_start)(); -extern void (*__init_array_end)(); -extern void kernel_main(); - -alignas(16) -static std::array stack; - -static void init_array() -{ - std::span initArray (&__init_array_start, &__init_array_end); - for (auto& fn : initArray) - fn(); -} - -extern "C" -__attribute__((naked)) -void _start() -{ - asm volatile(R"( - mov %%eax, multiboot_magic - mov %%ebx, multiboot_ptr - mov %0, %%esp - )" :: "i" (stack.data() + stack.size())); - - init_array(); - kernel_main(); - - asm volatile("cli"); - for (;;) - asm volatile("hlt"); -} - diff --git a/circularbuffer.hpp b/circularbuffer.hpp deleted file mode 100644 index 64a9e48..0000000 --- a/circularbuffer.hpp +++ /dev/null @@ -1,573 +0,0 @@ -/* -MIT License - -Copyright (c) 2020 Vinit James - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#ifndef CIRCULAR_BUFFER_H -#define CIRCULAR_BUFFER_H - -#include -#include -#include -#include -#include -#include - - -template -class CircularBuffer { -private: - - typedef T* pointer; - typedef const T* const_pointer; - typedef T& reference; - typedef const T& const_reference; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - template struct BufferIterator; - - -public: - typedef T value_type; - - // clyne - constexpr CircularBuffer() - :_buff{}, _max_size{0} {} - - explicit CircularBuffer(size_t size) - :_buff{std::unique_ptr(new value_type[size])}, _max_size{size}{} - - CircularBuffer(const CircularBuffer& other) - :_buff{std::unique_ptr(new value_type[other._max_size])}, - _max_size{other._max_size}, - _size{other._size}, - _head{other._head}, - _tail{other._tail}{ - std::copy(other.data(), other.data() + _max_size, _buff.get()); - } - - - CircularBuffer& operator=(const CircularBuffer& other){ - if ( this != &other){ - _buff.reset(new value_type[other._max_size]); - _max_size = other._max_size; - _size = other._size; - _head = other._head; - _tail = other._tail; - std::copy(other.data(), other.data() + _max_size, _buff.get()); - } - return *this; - } - - CircularBuffer(CircularBuffer&& other) noexcept - :_buff{std::move(other._buff)}, - _max_size{other._max_size}, - _size{other._size}, - _head{other._head}, - _tail{other._tail}{ - - other._buff = nullptr; - other._max_size = 0; - other._size = 0; - other._head = 0; - other._tail = 0; - } - - - CircularBuffer& operator=(CircularBuffer&& other) noexcept{ - if ( this != &other){ - _buff = std::move(other._buff); - _max_size = other._max_size; - _size = other._size; - _head = other._head; - _tail = other._tail; - - other._buff = nullptr; - other._max_size = 0; - other._size = 0; - other._head = 0; - other._tail = 0; - } - return *this; - } - - void push_back(const value_type& data); - void push_back(value_type&& data) noexcept; - void pop_front(); - reference front(); - reference back(); - const_reference front() const; - const_reference back() const; - void clear(); - bool empty() const ; - bool full() const ; - size_type capacity() const ; - size_type size() const; - size_type buffer_size() const {return sizeof(value_type)*_max_size;}; - const_pointer data() const { return _buff.get(); } - - const_reference operator[](size_type index) const; - reference operator[](size_type index); - const_reference at(size_type index) const; - reference at(size_type index); - - typedef BufferIterator iterator; - typedef BufferIterator const_iterator; - - iterator begin(); - const_iterator begin() const; - iterator end(); - const_iterator end() const; - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; - iterator rbegin() noexcept; - const_iterator rbegin() const noexcept; - iterator rend() noexcept; - const_iterator rend() const noexcept; - - -private: - void _increment_bufferstate(); - void _decrement_bufferstate(); - // clyne - //mutable std::mutex _mtx; - std::unique_ptr _buff; - size_type _head = 0; - size_type _tail = 0; - size_type _size = 0; - size_type _max_size = 0; - - template - struct BufferIterator{ - public: - friend class CircularBuffer; - typedef std::random_access_iterator_tag iterator_category; - typedef ptrdiff_t difference_type; - typedef T value_type; - typedef typename std::conditional::type reference; - typedef typename std::conditional::type pointer; - typedef typename std::conditional*, - CircularBuffer*>::type cbuf_pointer; - private: - cbuf_pointer _ptrToBuffer; - size_type _offset; - size_type _index; - bool _reverse; - - bool _comparable(const BufferIterator& other) const{ - return (_ptrToBuffer == other._ptrToBuffer)&&(_reverse == other._reverse); - } - - public: - BufferIterator() - :_ptrToBuffer{nullptr}, _offset{0}, _index{0}, _reverse{false}{} - - BufferIterator(const BufferIterator& it) - :_ptrToBuffer{it._ptrToBuffer}, - _offset{it._offset}, - _index{it._index}, - _reverse{it._reverse}{} - - reference operator*(){ - if(_reverse) - return (*_ptrToBuffer)[(_ptrToBuffer->size() - _index - 1)]; - return (*_ptrToBuffer)[_index]; - } - - pointer operator->() { return &(operator*()); } - - reference operator[](size_type index){ - BufferIterator iter = *this; - iter._index += index; - return *iter; - } - - BufferIterator& operator++(){ - ++_index; - return *this; - } - - BufferIterator operator++(int){ - BufferIterator iter = *this; - ++_index; - return iter; - } - - BufferIterator& operator--(){ - --_index; - return *this; - } - - BufferIterator operator--(int){ - BufferIterator iter = *this; - --_index; - return iter; - } - - friend BufferIterator operator+(BufferIterator lhsiter, difference_type n){ - lhsiter._index += n; - return lhsiter; - } - - friend BufferIterator operator+(difference_type n, BufferIterator rhsiter){ - rhsiter._index += n; - return rhsiter; - } - - - BufferIterator& operator+=(difference_type n){ - _index += n; - return *this; - } - - friend BufferIterator operator-(BufferIterator lhsiter, difference_type n){ - lhsiter._index -= n; - return lhsiter; - } - - friend difference_type operator-(const BufferIterator& lhsiter, const BufferIterator& rhsiter){ - - return lhsiter._index - rhsiter._index; - } - - BufferIterator& operator-=(difference_type n){ - _index -= n; - return *this; - } - - bool operator==(const BufferIterator& other) const{ - if (!_comparable(other)) - return false; - return ((_index == other._index)&&(_offset == other._offset)); - } - - bool operator!=(const BufferIterator& other) const{ - if (!_comparable(other)) - return true; - return ((_index != other._index)||(_offset != other._offset)); - } - - bool operator<(const BufferIterator& other) const { - if (!_comparable(other)) - return false; - return ((_index + _offset)<(other._index+other._offset)); - } - - bool operator>(const BufferIterator& other) const{ - if (!_comparable(other)) - return false; - return ((_index + _offset)>(other._index+other._offset)); - } - - bool operator<=(const BufferIterator& other) const { - if (!_comparable(other)) - return false; - return ((_index + _offset)<=(other._index+other._offset)); - } - - bool operator>=(const BufferIterator& other) const { - if (!_comparable(other)) - return false; - return ((_index + _offset)>=(other._index+other._offset)); - } - }; -}; - -template -inline -bool CircularBuffer::full() const{ - return _size == _max_size; -} - -template -inline -bool CircularBuffer::empty() const{ - return _size == 0; -} - -template -inline -typename CircularBuffer::size_type CircularBuffer::capacity() const{ - return _max_size; -} - -template -inline -void CircularBuffer::clear(){ - //std::lock_guard _lck(_mtx); - _head = _tail = _size = 0; -} - -template -inline -typename CircularBuffer::size_type CircularBuffer::size() const{ - //std::lock_guard _lck(_mtx); - return _size; - } - -template -inline -typename CircularBuffer::reference CircularBuffer::front() { - //std::lock_guard _lck(_mtx); - // clyne - if(empty()) - abort(); //throw std::length_error("front function called on empty buffer"); - return _buff[_tail]; -} - -template -inline -typename CircularBuffer::reference CircularBuffer::back() { - //std::lock_guard _lck(_mtx); - if(empty()) - abort(); //throw std::length_error("back function called on empty buffer"); - return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1]; -} - -template -inline -typename CircularBuffer::const_reference CircularBuffer::front() const{ - //std::lock_guard _lck(_mtx); - if(empty()) - abort(); //throw std::length_error("front function called on empty buffer"); - return _buff[_tail]; -} - -template -inline -typename CircularBuffer::const_reference CircularBuffer::back() const{ - //std::lock_guard _lck(_mtx); - if(empty()) - abort(); //throw std::length_error("back function called on empty buffer"); - return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1]; -} - -template -inline -void CircularBuffer::push_back(const T& data){ - //std::lock_guard _lck(_mtx); - //if(full()) - // _buff[_tail].~T(); - _buff[_head] = data; - _increment_bufferstate(); -} - -template -inline -void CircularBuffer::push_back(T&& data) noexcept{ - //std::lock_guard _lck(_mtx); - _buff[_head] = std::move(data); - _increment_bufferstate(); -} - - -template -inline -void CircularBuffer::_increment_bufferstate(){ - if(full()) - _tail = (_tail + 1)%_max_size; - else - ++_size; - _head = (_head + 1)%_max_size; -} - -template -inline -void CircularBuffer::pop_front(){ - //std::lock_guard _lck(_mtx); - if(empty()) - abort(); //throw std::length_error("pop_front called on empty buffer"); - _decrement_bufferstate(); -} - -template -inline -void CircularBuffer::_decrement_bufferstate(){ - --_size; - _tail = (_tail + 1)%_max_size; -} - -template -inline -typename CircularBuffer::reference CircularBuffer::operator[](size_t index) { - //std::lock_guard _lck(_mtx); - if((index<0)||(index>=_size)) - abort(); //throw std::out_of_range("Index is out of Range of buffer size"); - index += _tail; - index %= _max_size; - return _buff[index]; -} - -template -inline -typename CircularBuffer::const_reference CircularBuffer::operator[](size_t index) const { - //std::lock_guard _lck(_mtx); - if((index<0)||(index>=_size)) - abort(); //throw std::out_of_range("Index is out of Range of buffer size"); - index += _tail; - index %= _max_size; - return _buff[index]; -} - -template -inline -typename CircularBuffer::reference CircularBuffer::at(size_t index) { - //std::lock_guard _lck(_mtx); - if((index<0)||(index>=_size)) - abort(); //throw std::out_of_range("Index is out of Range of buffer size"); - index += _tail; - index %= _max_size; - return _buff[index]; -} - -template -inline -typename CircularBuffer::const_reference CircularBuffer::at(size_t index) const { - //std::lock_guard _lck(_mtx); - if((index<0)||(index>=_size)) - abort(); //throw std::out_of_range("Index is out of Range of buffer size"); - index += _tail; - index %= _max_size; - return _buff[index]; -} - -template -inline -typename CircularBuffer::iterator CircularBuffer::begin() { - //std::lock_guard _lck(_mtx); - iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = 0; - iter._reverse = false; - return iter; -} - -template -inline -typename CircularBuffer::const_iterator CircularBuffer::begin() const{ - //std::lock_guard _lck(_mtx); - const_iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = 0; - iter._reverse = false; - return iter; -} - -template -inline -typename CircularBuffer::iterator CircularBuffer::end() { - //std::lock_guard _lck(_mtx); - iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = _size; - iter._reverse = false; - return iter; -} - -template -inline -typename CircularBuffer::const_iterator CircularBuffer::end() const{ - //std::lock_guard _lck(_mtx); - const_iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = _size; - iter._reverse = false; - return iter; -} - -template -inline -typename CircularBuffer::const_iterator CircularBuffer::cbegin() const noexcept{ - //std::lock_guard _lck(_mtx); - const_iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = 0; - iter._reverse = false; - return iter; -} - -template -inline -typename CircularBuffer::const_iterator CircularBuffer::cend() const noexcept{ - //std::lock_guard _lck(_mtx); - const_iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = _size; - iter._reverse = false; - return iter; -} - -template -inline -typename CircularBuffer::iterator CircularBuffer::rbegin() noexcept{ - //std::lock_guard _lck(_mtx); - iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = 0; - iter._reverse = true; - return iter; -} - -template -inline -typename CircularBuffer::const_iterator CircularBuffer::rbegin() const noexcept{ - //std::lock_guard _lck(_mtx); - const_iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = 0; - iter._reverse = true; - return iter; -} - -template -inline -typename CircularBuffer::iterator CircularBuffer::rend() noexcept{ - //std::lock_guard _lck(_mtx); - iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = _size; - iter._reverse = true; - return iter; -} - -template -inline -typename CircularBuffer::const_iterator CircularBuffer::rend() const noexcept{ - //std::lock_guard _lck(_mtx); - const_iterator iter; - iter._ptrToBuffer = this; - iter._offset = _tail; - iter._index = _size; - iter._reverse = true; - return iter; -} - -#endif /* CIRCULAR_BUFFER_H */ diff --git a/gdt.cpp b/gdt.cpp deleted file mode 100644 index 24d974b..0000000 --- a/gdt.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include - -struct gdt_entry_bits { - std::uint32_t limit_low : 16; - std::uint32_t base_low : 24; - std::uint32_t accessed : 1; - std::uint32_t read_write : 1; // readable for code, writable for data - std::uint32_t conforming_expand_down : 1; // conforming for code, expand down for data - std::uint32_t code : 1; // 1 for code, 0 for data - std::uint32_t code_data_segment : 1; // should be 1 for everything but TSS and LDT - std::uint32_t DPL : 2; // privilege level - std::uint32_t present : 1; - std::uint32_t limit_high : 4; - std::uint32_t available : 1; // only used in software; has no effect on hardware - std::uint32_t long_mode : 1; - std::uint32_t big : 1; // 32-bit opcodes for code, uint32_t stack for data - std::uint32_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing - std::uint32_t base_high : 8; -} __attribute__((packed)); - -struct TSSEntry -{ - std::uint32_t prevTSS; - std::uint32_t esp0; - std::uint32_t ss0; - std::uint32_t unused[23] = {}; -} __attribute__((packed)); - -static TSSEntry tss = { - .prevTSS = 0, - .esp0 = 0, - .ss0 = 0x10 -}; - -static const std::array gdt {{ - {}, - /* kernel_code = */ { - .limit_low = 0xFFFF, - .base_low = 0x0000, - .accessed = 0, - .read_write = 1, - .conforming_expand_down = 0, - .code = 1, - .code_data_segment = 1, - .DPL = 0, - .present = 1, - .limit_high = 0xF, - .available = 0, - .long_mode = 0, - .big = 1, - .gran = 1, - .base_high = 0x00 - }, - /* kernel_data = */ { - .limit_low = 0xFFFF, - .base_low = 0x0000, - .accessed = 0, - .read_write = 1, - .conforming_expand_down = 0, - .code = 0, - .code_data_segment = 1, - .DPL = 0, - .present = 1, - .limit_high = 0xF, - .available = 0, - .long_mode = 0, - .big = 1, - .gran = 1, - .base_high = 0x00 - }, - /* user_code = */ { - .limit_low = 0xFFFF, - .base_low = 0x0000, - .accessed = 0, - .read_write = 1, - .conforming_expand_down = 0, - .code = 1, - .code_data_segment = 1, - .DPL = 3, - .present = 1, - .limit_high = 0xF, - .available = 0, - .long_mode = 0, - .big = 1, - .gran = 1, - .base_high = 0x00 - }, - /* user_data = */ { - .limit_low = 0xFFFF, - .base_low = 0x0000, - .accessed = 0, - .read_write = 1, - .conforming_expand_down = 0, - .code = 0, - .code_data_segment = 1, - .DPL = 3, - .present = 1, - .limit_high = 0xF, - .available = 0, - .long_mode = 0, - .big = 1, - .gran = 1, - .base_high = 0x00 - }, - /* tss = */ { - .limit_low = sizeof(TSSEntry), - .base_low = (std::uint32_t)&tss & 0xFFFFFF, - .accessed = 1, - .read_write = 0, - .conforming_expand_down = 0, - .code = 1, - .code_data_segment = 0, - .DPL = 0, - .present = 1, - .limit_high = 0, - .available = 0, - .long_mode = 0, - .big = 0, - .gran = 0, - .base_high = (std::uint32_t)&tss >> 24 - } -}}; - -void gdt_initialize() -{ - auto gdtr = reinterpret_cast(gdt.data()); - gdtr <<= 16; - gdtr |= gdt.size() * sizeof(gdt[0]); - - asm volatile(R"( - lgdt %0 - pushl $0x8 - push $.setcs - ljmp *(%%esp) - .setcs: - add $8, %%esp - mov $0x10, %%eax - mov %%eax, %%ds - mov %%eax, %%es - mov %%eax, %%fs - mov %%eax, %%gs - mov %%eax, %%ss - - mov $0x28, %%ax - ltr %%ax - )" :: "m"(gdtr)); -} - -void enter_user_mode(void (*func)()) -{ - asm volatile("mov %%esp, %0" : "=r" (tss.esp0)); - - asm volatile(R"( - mov $0x23, %%ax - mov %%ax, %%ds - mov %%ax, %%es - mov %%ax, %%fs - mov %%ax, %%gs - mov %%esp, %%eax - push $0x23 - push %%esp - pushf - push $0x1b - push %0 - iret - )" :: "b"(func)); -} - diff --git a/gdt.hpp b/gdt.hpp deleted file mode 100644 index de5e8fc..0000000 --- a/gdt.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef GDT_HPP -#define GDT_HPP - -void gdt_initialize(); -void enter_user_mode(void (*func)()); - -#endif // GDT_HPP - diff --git a/idt.cpp b/idt.cpp deleted file mode 100644 index 5380c9e..0000000 --- a/idt.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "idt.hpp" -#include "portio.hpp" -#include "textoutput.hpp" - -#include -#include -#include - -extern TextOutput& term; - -static constexpr unsigned InterruptCount = 49; - -static constexpr std::uint8_t TaskGate = 0x5; -static constexpr std::uint8_t IntrGate16 = 0x6; -static constexpr std::uint8_t TrapGate16 = 0x7; -static constexpr std::uint8_t IntrGate32 = 0xE; -static constexpr std::uint8_t TrapGate32 = 0xF; - -struct idt_entry_bits { - std::uint32_t offset_low : 16; - std::uint32_t segment_selector : 16; - std::uint32_t rsvd : 8 = 0; - std::uint32_t gate_type : 4; - std::uint32_t rsvd2 : 1 = 0; - std::uint32_t dpl : 2; - std::uint32_t present : 1; - std::uint32_t offset_high : 16; -} __attribute__((packed)); - -static std::array callbacks; - -extern "C" -void interruptGeneralHandler(Registers regs) -{ - const auto& inum = regs.inum; - - if (inum >= 32) { - if (inum >= 40) - outb(0xA0, 0x20); - - outb(0x20, 0x20); - } - - if (inum < callbacks.size()) { - if (auto cb = callbacks[inum]; cb) { - asm volatile("cli"); - cb(regs); - asm volatile("sti"); - } - } -} - -template -struct StubEntry -{ - static constexpr bool HasError = N == 8 || (N >= 10 && N <= 14) || N == 17 || N == 30; - - __attribute__((naked)) - static void stub() { - if constexpr (!HasError) - asm volatile("push $0x0"); - - asm volatile(R"( - pusha - mov %%ds, %%eax - push %%eax - mov $0x10, %%ax - mov %%ax, %%ds - mov %%ax, %%es - mov %%ax, %%fs - mov %%ax, %%gs - push %0 - cld - call interruptGeneralHandler - pop %%eax - pop %%eax - mov %%ax, %%ds - mov %%ax, %%es - mov %%ax, %%fs - mov %%ax, %%gs - popa - add $0x4, %%esp - iret - )" :: "i"(N)); - } - - static constexpr std::uint32_t segment(std::uint16_t gdt_idx, bool useLdt, std::uint16_t rpl) { - return gdt_idx | (useLdt ? 0x4 : 0x0) | (rpl & 0x3); - } - - idt_entry_bits entry = { - .offset_low = (uint32_t)stub & 0xFFFF, - .segment_selector = segment(0x8, false, 0), - .gate_type = IntrGate32, - .dpl = 0, - .present = 1, - .offset_high = (uint32_t)stub >> 16 - }; - - operator idt_entry_bits() const noexcept { - return entry; - } -}; - -static auto idt = - [](std::index_sequence) { - return std::array { StubEntry()... }; - }(std::make_index_sequence{}); - -void idt_initialize() -{ - idt[0x28].dpl = 3; - - auto idtr = reinterpret_cast(idt.data()); - idtr <<= 16; - idtr |= idt.size() * sizeof(idt[0]); - - asm volatile("lidt %0" :: "m"(idtr)); -} - -void idt_register_callback(std::size_t num, Callback cb) -{ - if (num < callbacks.size()) - callbacks[num] = cb; -} - diff --git a/idt.hpp b/idt.hpp deleted file mode 100644 index c5e7e47..0000000 --- a/idt.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef IDT_HPP -#define IDT_HPP - -#include -#include - -struct Registers -{ - std::uint32_t inum; - std::uint32_t ds; - std::uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; - std::uint32_t error; - std::uint32_t eip, cs, eflags; -} __attribute__((packed)); - -using Callback = void (*)(const Registers&); - -void idt_initialize(); -void idt_register_callback(std::size_t num, Callback cb); - -#endif // IDT_HPP - diff --git a/kernel.cpp b/kernel.cpp deleted file mode 100644 index ae3f153..0000000 --- a/kernel.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "acpi.hpp" -#include "ata.hpp" -#include "gdt.hpp" -#include "idt.hpp" -#include "keyboard.hpp" -#include "memory.hpp" -#include "multiboot.hpp" -#include "pic.hpp" -#include "pit.hpp" -#include "portio.hpp" -#include "tasking.hpp" -#include "vgaterminal.hpp" - -static VGATerminal vga; -TextOutput& term = vga; - -static volatile bool termBusy = false; - -void ata_probe(auto bus, ATA::Drive drv, const char *name) -{ - switch (bus.identify(drv)) { - case ATA::Type::ATA: - term.write("ata:"); - term.write(name); - term.write(": ATA drive detected.\n"); - break; - case ATA::Type::ATAPI: - term.write("ata:"); - term.write(name); - term.write(": ATAPI drive detected.\n"); - break; - default: - break; - } -} - -void kernel_main(void) -{ - term.write("Clyne's kernel, v2024\n\n"); - - if (!multiboot_initialize()) - for (;;); - - idt_register_callback(14, [](auto& regs) { - term.write("Page fault! eip="); - term.write(regs.eip); - term.write('\n'); - for (;;); - }); - - acpi_initialize(); - memory_initialize(); - gdt_initialize(); - pic_initialize(); - idt_initialize(); - pit_initialize(); - keyboard_initialize(); - asm volatile("sti"); - tasking_initialize(); - term.write("Tasking enabled.\n"); - - ATA::Bus bus0; - ATA::Bus bus1; - - ata_probe(bus0, ATA::Master, "0:0"); - ata_probe(bus0, ATA::Slave, "0:1"); - ata_probe(bus1, ATA::Master, "1:0"); - ata_probe(bus1, ATA::Slave, "1:1"); - - idt_register_callback(0x28, [](auto& regs) { - term.write(static_cast(regs.eax)); - }); - - term.write("Entering user mode...\n"); - enter_user_mode([] { - asm volatile("int $0x28" :: "a" ('Z')); - for (;;); - }); - - for (;;) { - const auto ch = keyboard_read(); - if (ch) - term.write(*ch); - - pit_delay_ms(10); - } -} - -extern "C" -void abort() -{ - term.write("!!! abort() called !!!"); - asm volatile("cli"); - for (;;); -} - -extern "C" -int __cxa_atexit(void (*)(void *), void *, void *) -{ - return 0; -} - -int __dso_handle = 0; - diff --git a/keyboard.cpp b/keyboard.cpp deleted file mode 100644 index 48dad02..0000000 --- a/keyboard.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "circularbuffer.hpp" -#include "idt.hpp" -#include "keyboard.hpp" -#include "portio.hpp" -#include "vgaterminal.hpp" - -#include -#include - -extern TextOutput& term; - -static CircularBuffer keyboardBuffer; -static Port<0x60> keyboardPort; - -static const std::array ScanCodeSet1 {{ - 0, K_ESCAPE, - '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', - '-', '=', '\b', '\t', - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', '\n', K_CONTROL_L, - 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', '`', K_SHIFT_L, '\\', - 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', - K_SHIFT_R, '*', K_ALT_L, ' ', K_CAPS, - K_F1, K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, - K_NUM, K_SCROLL, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', - 0, 0, 0, // non-existant - K_F11, K_F12 -}}; - -static inline bool isReleased(auto ch) { - return ch & 0x80; -} - -static inline auto keycode(auto ch) { - return ch & 0x7F; -} - -void keyboard_initialize() -{ - keyboardBuffer = CircularBuffer(128); - - idt_register_callback(33, [](auto&) { - const std::uint8_t kc = keyboardPort; - - if (!isReleased(kc)) { - const auto ch = ScanCodeSet1[keycode(kc)]; - //if (ch > 0) - // term.write(ch); - - keyboardBuffer.push_back(ch); - } - }); -} - -std::optional keyboard_read() -{ - if (keyboardBuffer.empty()) { - return {}; - } else { - const auto ch = keyboardBuffer.front(); - keyboardBuffer.pop_front(); - return ch; - } -} - diff --git a/keyboard.hpp b/keyboard.hpp deleted file mode 100644 index 694ff89..0000000 --- a/keyboard.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef KEYBOARD_HPP -#define KEYBOARD_HPP - -#include - -#define K_CONTROL_L -1 -#define K_SHIFT_L -2 -#define K_ALT_L -3 -#define K_CAPS -4 -#define K_NUM -5 -#define K_SCROLL -6 -#define K_SHIFT_R -7 -#define K_ESCAPE -8 -#define K_F1 -10 -#define K_F2 -11 -#define K_F3 -12 -#define K_F4 -13 -#define K_F5 -14 -#define K_F6 -15 -#define K_F7 -16 -#define K_F8 -17 -#define K_F9 -18 -#define K_F10 -19 -#define K_F11 -20 -#define K_F12 -21 - -void keyboard_initialize(); - -std::optional keyboard_read(); - -#endif // KEYBOARD_HPP - diff --git a/memmove.cpp b/memmove.cpp deleted file mode 100644 index 06e37df..0000000 --- a/memmove.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* Taken from newlib... */ - -#include -#include -#include - -/* Nonzero if either X or Y is not aligned on a "long" boundary. */ -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -/* How many bytes are copied each iteration of the 4X unrolled loop. */ -#define BIGBLOCKSIZE (sizeof (long) << 2) - -/* How many bytes are copied each iteration of the word copy loop. */ -#define LITTLEBLOCKSIZE (sizeof (long)) - -/* Threshhold for punting to the byte copier. */ -#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) - -extern "C" -void *memmove (void *dst_void, const void *src_void, size_t length) -{ - char *dst = reinterpret_cast(dst_void); - const char *src = reinterpret_cast(src_void); - long *aligned_dst; - const long *aligned_src; - - if (src < dst && dst < src + length) - { - /* Destructive overlap...have to copy backwards */ - src += length; - dst += length; - while (length--) - { - *--dst = *--src; - } - } - else - { - /* Use optimizing algorithm for a non-destructive copy to closely - match memcpy. If the size is small or either SRC or DST is unaligned, - then punt into the byte copy loop. This should be rare. */ - if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) - { - aligned_dst = (long*)dst; - aligned_src = (long*)src; - - /* Copy 4X long words at a time if possible. */ - while (length >= BIGBLOCKSIZE) - { - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - length -= BIGBLOCKSIZE; - } - - /* Copy one long word at a time if possible. */ - while (length >= LITTLEBLOCKSIZE) - { - *aligned_dst++ = *aligned_src++; - length -= LITTLEBLOCKSIZE; - } - - /* Pick up any residual with a byte copier. */ - dst = (char*)aligned_dst; - src = (char*)aligned_src; - } - - while (length--) - { - *dst++ = *src++; - } - } - - return dst_void; -} - diff --git a/memory.cpp b/memory.cpp deleted file mode 100644 index 108c240..0000000 --- a/memory.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "textoutput.hpp" - -#include -#include - -struct PageDirectory -{ - static constexpr std::uint32_t NotPresent = 0x2; - - PageDirectory(): value(NotPresent) {} - PageDirectory(void *addr): value(reinterpret_cast(addr) | 7) {} - - std::uint32_t value; -}; -static_assert(sizeof(PageDirectory) == sizeof(std::uint32_t)); - -extern std::uint32_t lowerMem; -extern std::uint32_t upperMem; -extern TextOutput& term; - -static std::uintptr_t lowerFree = 0x400; -static std::uintptr_t upperFree = 0x100000; - -alignas(4096) -static std::array pageDirectory; - -alignas(4096) -static std::array pageTable; - -void memory_initialize() -{ - lowerMem -= 1024; - - const auto totalKb = (lowerMem + upperMem) / 1024u; - - term.write("Claiming "); - term.write(totalKb); - term.write(" kB for allocations...\n"); - - std::uint32_t addr = 0; - for (auto& p : pageTable) { - p = addr | 7; // supervisor, r/w, present - addr += 0x1000; - } - - pageDirectory[0] = PageDirectory(pageTable.data()); - - asm volatile(R"( - mov %%eax, %%cr3 - mov %%cr0, %%eax - or $0x80000000, %%eax - mov %%eax, %%cr0 - )" :: "a"(pageDirectory.data())); - - term.write("Paging enabled.\n"); -} - -static void *memory_alloc(std::size_t size) -{ - void *ret = nullptr; - - if (lowerMem > size) { - ret = reinterpret_cast(lowerFree); - lowerFree += size; - lowerMem -= size; - } else if (upperMem > size) { - ret = reinterpret_cast(upperFree); - upperFree += size; - upperMem -= size; - } else { - // Uh oh! - term.write("!!! Kernel allocation failed !!!"); - } - - return ret; -} - -void *operator new(std::size_t size) -{ - return memory_alloc(size); -} - -void *operator new[](std::size_t size) -{ - return memory_alloc(size); -} - -void operator delete(void *) -{ - -} - -void operator delete[](void *) -{ - -} - -void operator delete(void *, std::size_t) -{ - -} - -void operator delete[](void *, std::size_t) -{ - -} - diff --git a/memory.hpp b/memory.hpp deleted file mode 100644 index 0467bf6..0000000 --- a/memory.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef MEMORY_HPP -#define MEMORY_HPP - -#include -#include - -template -struct kallocator -{ - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using propagate_on_container_move_assignment = std::true_type; - using is_always_equal = std::true_type; - - template - struct rebind { - typedef kallocator other; - }; - - kallocator() = default; - - template - kallocator(const kallocator&) noexcept {} - - T* allocate(std::size_t n) { - return new T[n]; - } - - void deallocate([[maybe_unused]] T* p, [[maybe_unused]] std::size_t n) { - - } -}; - -void memory_initialize(); - -#endif // MEMORY_HPP - diff --git a/multiboot.cpp b/multiboot.cpp deleted file mode 100644 index 46c505b..0000000 --- a/multiboot.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "textoutput.hpp" - -#include - -extern TextOutput& term; - -struct multiboot2_tag -{ - alignas(8) - std::uint16_t id; - std::uint16_t flags; - std::uint32_t length; - std::uint32_t data[1]; -} __attribute__((packed)); - -template -struct multiboot2 -{ - static constexpr std::uint32_t MAGIC = 0xE85250D6; - static constexpr std::uint32_t FLAGS = 0; - static constexpr std::uint32_t LENGTH = 16; - static constexpr std::uint32_t CHECKSUM = -(MAGIC + FLAGS + LENGTH); - - alignas(8) - std::uint32_t magic = MAGIC; - std::uint32_t flags = FLAGS; - std::uint32_t length = LENGTH; - std::uint32_t checksum = CHECKSUM; - - multiboot2_tag tags[N]; -} __attribute__((packed)); - -__attribute__((section(".multiboot2"))) -multiboot2 multibootHeader = { - .tags = { - { - 1, 0, sizeof(multiboot2_tag) + sizeof(std::uint32_t), - {4} - }, - { - 0, 0, 8, {} - } - } -}; - -std::uint32_t multiboot_magic; -std::uint32_t *multiboot_ptr; - -std::uint32_t lowerMem = 0; -std::uint32_t upperMem = 0; -std::uint32_t *acpiRsdp = nullptr; -std::uint32_t *acpiRsdpV2 = nullptr; - -bool multiboot_initialize() -{ - if (multiboot_magic != 0x36d76289) { - term.write("Not multiboot!"); - return false; - } - - term.write("Found multiboot headers: "); - - auto ptr = multiboot_ptr + 2; - while (ptr[0] != 0 && ptr[1] != 8) { - term.write(ptr[0]); - term.write(", "); - - switch (ptr[0]) { - case 4: - lowerMem = ptr[2] * 1024; - upperMem = ptr[3] * 1024; - break; - case 14: - acpiRsdp = ptr + 2; - break; - case 15: - acpiRsdpV2 = ptr + 2; - break; - default: - break; - } - - auto next = reinterpret_cast(ptr); - next += ptr[1]; - next = (next + 7) & ~7; - ptr = reinterpret_cast(next); - } - - term.write('\n'); - return true; -} - diff --git a/multiboot.hpp b/multiboot.hpp deleted file mode 100644 index 9916850..0000000 --- a/multiboot.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef MULTIBOOT_HPP -#define MULTIBOOT_HPP - -bool multiboot_initialize(); - -#endif // MULTIBOOT_HPP - diff --git a/pic.cpp b/pic.cpp deleted file mode 100644 index 437f384..0000000 --- a/pic.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "pic.hpp" - -#include "portio.hpp" - -#define PIC1 0x20 /* IO base address for master PIC */ -#define PIC2 0xA0 /* IO base address for slave PIC */ -#define PIC1_COMMAND PIC1 -#define PIC1_DATA (PIC1+1) -#define PIC2_COMMAND PIC2 -#define PIC2_DATA (PIC2+1) - -#define PIC_EOI 0x20 /* End-of-interrupt command code */ - -#define ICW1_ICW4 0x01 /* Indicates that ICW4 will be present */ -#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ -#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ -#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ -#define ICW1_INIT 0x10 /* Initialization - required! */ - -#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ -#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ -#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ -#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ -#define ICW4_SFNM 0x10 /* Special fully nested (not) */ - -void pic_initialize() -{ - constexpr int offset1 = 0x20, offset2 = 0x28; - std::uint8_t a1, a2; - - a1 = inb(PIC1_DATA); // save masks - a2 = inb(PIC2_DATA); - - outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) - io_wait(); - outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); - io_wait(); - outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset - io_wait(); - outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset - io_wait(); - outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) - io_wait(); - outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) - io_wait(); - - outb(PIC1_DATA, ICW4_8086); // ICW4: have the PICs use 8086 mode (and not 8080 mode) - io_wait(); - outb(PIC2_DATA, ICW4_8086); - io_wait(); - - outb(PIC1_DATA, a1); // restore saved masks. - outb(PIC2_DATA, a2); -} - -void pic_eoi(std::uint8_t irq) -{ - if (irq >= 8) - outb(PIC2_COMMAND, PIC_EOI); - - outb(PIC1_COMMAND, PIC_EOI); -} - diff --git a/pic.hpp b/pic.hpp deleted file mode 100644 index 17abd55..0000000 --- a/pic.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef PIC_HPP -#define PIC_HPP - -#include - -/* reinitialize the PIC controllers, giving them specified vector offsets - rather than 8h and 70h, as configured by default */ -void pic_initialize(); -void pic_eoi(std::uint8_t irq); - -#endif // PIC_HPP - diff --git a/pit.cpp b/pit.cpp deleted file mode 100644 index 964522b..0000000 --- a/pit.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "pit.hpp" -#include "idt.hpp" -#include "portio.hpp" -#include "tasking.hpp" - -constexpr std::uint32_t Frequency = 1000; - -static volatile std::uint32_t ticks = 0; - -static void timer_callback(const Registers& regs) -{ - ticks = ticks + 1; - - schedule(regs); -} - -void pit_initialize() -{ - // Firstly, register our timer callback. - idt_register_callback(32, timer_callback); - - // The value we send to the PIT is the value to divide it's input clock - // (1193180 Hz) by, to get our required frequency. Important to note is - // that the divisor must be small enough to fit into 16-bits. - const auto divisor = 1193180ul / Frequency; - - // Send the command byte. - outb(0x43, 0x36); - - // Send the frequency divisor. - outb(0x40, divisor & 0xFF); - outb(0x40, (divisor >> 8) & 0xFF); -} - -void pit_delay_ms(std::int32_t ms) -{ - const auto end = ticks + ms; - while (static_cast(end - ticks) > 0) - asm volatile("nop"); -} - diff --git a/pit.hpp b/pit.hpp deleted file mode 100644 index 55bcde9..0000000 --- a/pit.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef PIT_HPP -#define PIT_HPP - -#include - -void pit_initialize(); -void pit_delay_ms(std::int32_t ms); - -#endif // PIT_HPP - diff --git a/portio.hpp b/portio.hpp deleted file mode 100644 index 7636c14..0000000 --- a/portio.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef PORTIO_HPP -#define PORTIO_HPP - -#include - -inline void outb(std::uint16_t port, std::uint8_t val) -{ - asm volatile("out %%al, %%dx" :: "a"(val), "Nd"(port) : "memory"); -} - -inline std::uint8_t inb(std::uint16_t port) -{ - std::uint8_t val; - asm volatile("inb %%dx" : "=a"(val) : "Nd"(port) : "memory"); - return val; -} - -inline void outw(std::uint16_t port, std::uint16_t val) -{ - asm volatile("out %%ax, %%dx" :: "a"(val), "Nd"(port) : "memory"); -} - -inline std::uint16_t inw(std::uint16_t port) -{ - std::uint16_t val; - asm volatile("inw %%dx" : "=a"(val) : "Nd"(port) : "memory"); - return val; -} - -inline void io_wait() -{ - outb(0x80, 0); -} - -template -struct Port -{ - Port() = default; - - template - auto operator=(T val) noexcept { - if constexpr (sizeof(T) == 1) - outb(Addr, val); - else if constexpr (sizeof(T) == 2) - outw(Addr, val); - - return *this; - } - - template - operator T() const noexcept { - if constexpr (sizeof(T) == 1) - return inb(Addr); - else if constexpr (sizeof(T) == 2) - return inw(Addr); - } - - template - bool operator==(T val) const noexcept { - T dat = *this; - return dat == val; - } - - template - auto operator&(T val) const noexcept { - T dat = *this; - return dat & val; - } -}; - -#endif // PORTIO_HPP - diff --git a/src/acpi.cpp b/src/acpi.cpp new file mode 100644 index 0000000..b74a7dc --- /dev/null +++ b/src/acpi.cpp @@ -0,0 +1,51 @@ +#include "textoutput.hpp" + +#include + +extern TextOutput& term; +extern std::uint32_t *acpiRsdp; +extern std::uint32_t *acpiRsdpV2; + +struct XSDP { + char Signature[8]; + std::uint8_t Checksum; + char OEMID[6]; + std::uint8_t Revision; + std::uint32_t RsdtAddress; // deprecated since version 2.0 + + // v2 only! + std::uint32_t Length; + std::uint64_t XsdtAddress; + std::uint8_t ExtendedChecksum; + std::uint8_t reserved[3]; +} __attribute__ ((packed)); + +struct SDTHeader { + char Signature[4]; + std::uint32_t Length; + std::uint8_t Revision; + std::uint8_t Checksum; + char OEMID[6]; + char OEMTableID[8]; + std::uint32_t OEMRevision; + std::uint32_t CreatorID; + std::uint32_t CreatorRevision; +}; + +static XSDP *rsdp = nullptr; + +void acpi_initialize() +{ + if (acpiRsdp) { + term.write("ACPI v1 detected.\n"); + rsdp = reinterpret_cast(acpiRsdp); + } else if (acpiRsdpV2) { + term.write("ACPI v2 detected, treating as v1.\n"); + rsdp = reinterpret_cast(acpiRsdpV2); + } + + //if (rsdp) { + // auto sdt = reinterpret_cast(rsdp->RsdtAddress); + //} +} + diff --git a/src/acpi.hpp b/src/acpi.hpp new file mode 100644 index 0000000..8154b81 --- /dev/null +++ b/src/acpi.hpp @@ -0,0 +1,7 @@ +#ifndef ACPI_HPP +#define ACPI_HPP + +void acpi_initialize(); + +#endif // ACPI_HPP + diff --git a/src/ata.cpp b/src/ata.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/ata.hpp b/src/ata.hpp new file mode 100644 index 0000000..5394c31 --- /dev/null +++ b/src/ata.hpp @@ -0,0 +1,131 @@ +#ifndef ATA_HPP +#define ATA_HPP + +#include "portio.hpp" + +#include +#include + +namespace ATA +{ + enum class Type { + None, + ATA, + ATAPI + }; + + enum class Base : std::uint16_t { + Primary = 0x01F0, + Secondary = 0x0170 + }; + + enum class Drive : std::uint8_t { + Master = 0xA0, + Slave = 0xB0 + }; + + enum class Command : std::uint8_t { + Identify = 0xEC, + IdentifyPacketDevice = 0xA1 + }; + + namespace Status { + static constexpr std::uint8_t Busy = 0x80; + static constexpr std::uint8_t Ready = 0x40; + static constexpr std::uint8_t DF = 0x20; + static constexpr std::uint8_t DSC = 0x10; + static constexpr std::uint8_t DRQ = 0x08; + static constexpr std::uint8_t CORR = 0x04; + static constexpr std::uint8_t Index = 0x02; + static constexpr std::uint8_t Error = 0x01; + } + + static constexpr std::uint8_t ATAPIIdentify0 = 0x14; + static constexpr std::uint8_t ATAPIIdentify1 = 0xEB; + + using enum Base; + using enum Drive; + + template + struct Bus + { + template + using BPort = Port; + + [[no_unique_address]] BPort data; + [[no_unique_address]] BPort errFeats; + [[no_unique_address]] BPort count; + [[no_unique_address]] BPort lba0; + [[no_unique_address]] BPort lba1; + [[no_unique_address]] BPort lba2; + [[no_unique_address]] BPort select; + [[no_unique_address]] BPort cmdStat; + + Type identify(Drive drv) { + auto type = Type::None; + + data = std::to_underlying(drv); + count = '\0'; + lba0 = '\0'; + lba1 = '\0'; + lba2 = '\0'; + cmdStat = std::to_underlying(Command::Identify); + + if (cmdStat == '\0') + return type; + + type = Type::ATA; + while (cmdStat & Status::Busy); + + std::uint8_t stat; + do { + stat = cmdStat; + + if (stat & Status::Error) { + if (lba1 == ATAPIIdentify0 && lba2 == ATAPIIdentify1) { + type = identifyAtapi(drv); + break; + } else { + return type; + } + } + } while (!(stat & Status::DRQ)); + + if (type != Type::None) { + for (int i = 0; i < 256; ++i) { + volatile std::uint16_t w = data; + (void)w; + } + } + + return type; + } + + Type identifyAtapi(Drive drv) { + data = std::to_underlying(drv); + count = '\0'; + lba0 = '\0'; + lba1 = '\0'; + lba2 = '\0'; + cmdStat = std::to_underlying(Command::IdentifyPacketDevice); + + if (cmdStat == '\0') + return Type::None; + + while (cmdStat & Status::Busy); + + std::uint8_t stat; + do { + stat = cmdStat; + + if (stat & Status::Error) + return Type::None; + } while (!(stat & Status::DRQ)); + + return Type::ATAPI; + } + }; +} // ATA + +#endif // ATA_HPP + diff --git a/src/boot.cpp b/src/boot.cpp new file mode 100644 index 0000000..6b1da6a --- /dev/null +++ b/src/boot.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +extern void (*__init_array_start)(); +extern void (*__init_array_end)(); +extern void kernel_main(); + +alignas(16) +static std::array stack; + +static void init_array() +{ + std::span initArray (&__init_array_start, &__init_array_end); + for (auto& fn : initArray) + fn(); +} + +extern "C" +__attribute__((naked)) +void _start() +{ + asm volatile(R"( + mov %%eax, multiboot_magic + mov %%ebx, multiboot_ptr + mov %0, %%esp + )" :: "i" (stack.data() + stack.size())); + + init_array(); + kernel_main(); + + asm volatile("cli"); + for (;;) + asm volatile("hlt"); +} + diff --git a/src/circularbuffer.hpp b/src/circularbuffer.hpp new file mode 100644 index 0000000..64a9e48 --- /dev/null +++ b/src/circularbuffer.hpp @@ -0,0 +1,573 @@ +/* +MIT License + +Copyright (c) 2020 Vinit James + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef CIRCULAR_BUFFER_H +#define CIRCULAR_BUFFER_H + +#include +#include +#include +#include +#include +#include + + +template +class CircularBuffer { +private: + + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + template struct BufferIterator; + + +public: + typedef T value_type; + + // clyne + constexpr CircularBuffer() + :_buff{}, _max_size{0} {} + + explicit CircularBuffer(size_t size) + :_buff{std::unique_ptr(new value_type[size])}, _max_size{size}{} + + CircularBuffer(const CircularBuffer& other) + :_buff{std::unique_ptr(new value_type[other._max_size])}, + _max_size{other._max_size}, + _size{other._size}, + _head{other._head}, + _tail{other._tail}{ + std::copy(other.data(), other.data() + _max_size, _buff.get()); + } + + + CircularBuffer& operator=(const CircularBuffer& other){ + if ( this != &other){ + _buff.reset(new value_type[other._max_size]); + _max_size = other._max_size; + _size = other._size; + _head = other._head; + _tail = other._tail; + std::copy(other.data(), other.data() + _max_size, _buff.get()); + } + return *this; + } + + CircularBuffer(CircularBuffer&& other) noexcept + :_buff{std::move(other._buff)}, + _max_size{other._max_size}, + _size{other._size}, + _head{other._head}, + _tail{other._tail}{ + + other._buff = nullptr; + other._max_size = 0; + other._size = 0; + other._head = 0; + other._tail = 0; + } + + + CircularBuffer& operator=(CircularBuffer&& other) noexcept{ + if ( this != &other){ + _buff = std::move(other._buff); + _max_size = other._max_size; + _size = other._size; + _head = other._head; + _tail = other._tail; + + other._buff = nullptr; + other._max_size = 0; + other._size = 0; + other._head = 0; + other._tail = 0; + } + return *this; + } + + void push_back(const value_type& data); + void push_back(value_type&& data) noexcept; + void pop_front(); + reference front(); + reference back(); + const_reference front() const; + const_reference back() const; + void clear(); + bool empty() const ; + bool full() const ; + size_type capacity() const ; + size_type size() const; + size_type buffer_size() const {return sizeof(value_type)*_max_size;}; + const_pointer data() const { return _buff.get(); } + + const_reference operator[](size_type index) const; + reference operator[](size_type index); + const_reference at(size_type index) const; + reference at(size_type index); + + typedef BufferIterator iterator; + typedef BufferIterator const_iterator; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + iterator rbegin() noexcept; + const_iterator rbegin() const noexcept; + iterator rend() noexcept; + const_iterator rend() const noexcept; + + +private: + void _increment_bufferstate(); + void _decrement_bufferstate(); + // clyne + //mutable std::mutex _mtx; + std::unique_ptr _buff; + size_type _head = 0; + size_type _tail = 0; + size_type _size = 0; + size_type _max_size = 0; + + template + struct BufferIterator{ + public: + friend class CircularBuffer; + typedef std::random_access_iterator_tag iterator_category; + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef typename std::conditional::type reference; + typedef typename std::conditional::type pointer; + typedef typename std::conditional*, + CircularBuffer*>::type cbuf_pointer; + private: + cbuf_pointer _ptrToBuffer; + size_type _offset; + size_type _index; + bool _reverse; + + bool _comparable(const BufferIterator& other) const{ + return (_ptrToBuffer == other._ptrToBuffer)&&(_reverse == other._reverse); + } + + public: + BufferIterator() + :_ptrToBuffer{nullptr}, _offset{0}, _index{0}, _reverse{false}{} + + BufferIterator(const BufferIterator& it) + :_ptrToBuffer{it._ptrToBuffer}, + _offset{it._offset}, + _index{it._index}, + _reverse{it._reverse}{} + + reference operator*(){ + if(_reverse) + return (*_ptrToBuffer)[(_ptrToBuffer->size() - _index - 1)]; + return (*_ptrToBuffer)[_index]; + } + + pointer operator->() { return &(operator*()); } + + reference operator[](size_type index){ + BufferIterator iter = *this; + iter._index += index; + return *iter; + } + + BufferIterator& operator++(){ + ++_index; + return *this; + } + + BufferIterator operator++(int){ + BufferIterator iter = *this; + ++_index; + return iter; + } + + BufferIterator& operator--(){ + --_index; + return *this; + } + + BufferIterator operator--(int){ + BufferIterator iter = *this; + --_index; + return iter; + } + + friend BufferIterator operator+(BufferIterator lhsiter, difference_type n){ + lhsiter._index += n; + return lhsiter; + } + + friend BufferIterator operator+(difference_type n, BufferIterator rhsiter){ + rhsiter._index += n; + return rhsiter; + } + + + BufferIterator& operator+=(difference_type n){ + _index += n; + return *this; + } + + friend BufferIterator operator-(BufferIterator lhsiter, difference_type n){ + lhsiter._index -= n; + return lhsiter; + } + + friend difference_type operator-(const BufferIterator& lhsiter, const BufferIterator& rhsiter){ + + return lhsiter._index - rhsiter._index; + } + + BufferIterator& operator-=(difference_type n){ + _index -= n; + return *this; + } + + bool operator==(const BufferIterator& other) const{ + if (!_comparable(other)) + return false; + return ((_index == other._index)&&(_offset == other._offset)); + } + + bool operator!=(const BufferIterator& other) const{ + if (!_comparable(other)) + return true; + return ((_index != other._index)||(_offset != other._offset)); + } + + bool operator<(const BufferIterator& other) const { + if (!_comparable(other)) + return false; + return ((_index + _offset)<(other._index+other._offset)); + } + + bool operator>(const BufferIterator& other) const{ + if (!_comparable(other)) + return false; + return ((_index + _offset)>(other._index+other._offset)); + } + + bool operator<=(const BufferIterator& other) const { + if (!_comparable(other)) + return false; + return ((_index + _offset)<=(other._index+other._offset)); + } + + bool operator>=(const BufferIterator& other) const { + if (!_comparable(other)) + return false; + return ((_index + _offset)>=(other._index+other._offset)); + } + }; +}; + +template +inline +bool CircularBuffer::full() const{ + return _size == _max_size; +} + +template +inline +bool CircularBuffer::empty() const{ + return _size == 0; +} + +template +inline +typename CircularBuffer::size_type CircularBuffer::capacity() const{ + return _max_size; +} + +template +inline +void CircularBuffer::clear(){ + //std::lock_guard _lck(_mtx); + _head = _tail = _size = 0; +} + +template +inline +typename CircularBuffer::size_type CircularBuffer::size() const{ + //std::lock_guard _lck(_mtx); + return _size; + } + +template +inline +typename CircularBuffer::reference CircularBuffer::front() { + //std::lock_guard _lck(_mtx); + // clyne + if(empty()) + abort(); //throw std::length_error("front function called on empty buffer"); + return _buff[_tail]; +} + +template +inline +typename CircularBuffer::reference CircularBuffer::back() { + //std::lock_guard _lck(_mtx); + if(empty()) + abort(); //throw std::length_error("back function called on empty buffer"); + return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1]; +} + +template +inline +typename CircularBuffer::const_reference CircularBuffer::front() const{ + //std::lock_guard _lck(_mtx); + if(empty()) + abort(); //throw std::length_error("front function called on empty buffer"); + return _buff[_tail]; +} + +template +inline +typename CircularBuffer::const_reference CircularBuffer::back() const{ + //std::lock_guard _lck(_mtx); + if(empty()) + abort(); //throw std::length_error("back function called on empty buffer"); + return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1]; +} + +template +inline +void CircularBuffer::push_back(const T& data){ + //std::lock_guard _lck(_mtx); + //if(full()) + // _buff[_tail].~T(); + _buff[_head] = data; + _increment_bufferstate(); +} + +template +inline +void CircularBuffer::push_back(T&& data) noexcept{ + //std::lock_guard _lck(_mtx); + _buff[_head] = std::move(data); + _increment_bufferstate(); +} + + +template +inline +void CircularBuffer::_increment_bufferstate(){ + if(full()) + _tail = (_tail + 1)%_max_size; + else + ++_size; + _head = (_head + 1)%_max_size; +} + +template +inline +void CircularBuffer::pop_front(){ + //std::lock_guard _lck(_mtx); + if(empty()) + abort(); //throw std::length_error("pop_front called on empty buffer"); + _decrement_bufferstate(); +} + +template +inline +void CircularBuffer::_decrement_bufferstate(){ + --_size; + _tail = (_tail + 1)%_max_size; +} + +template +inline +typename CircularBuffer::reference CircularBuffer::operator[](size_t index) { + //std::lock_guard _lck(_mtx); + if((index<0)||(index>=_size)) + abort(); //throw std::out_of_range("Index is out of Range of buffer size"); + index += _tail; + index %= _max_size; + return _buff[index]; +} + +template +inline +typename CircularBuffer::const_reference CircularBuffer::operator[](size_t index) const { + //std::lock_guard _lck(_mtx); + if((index<0)||(index>=_size)) + abort(); //throw std::out_of_range("Index is out of Range of buffer size"); + index += _tail; + index %= _max_size; + return _buff[index]; +} + +template +inline +typename CircularBuffer::reference CircularBuffer::at(size_t index) { + //std::lock_guard _lck(_mtx); + if((index<0)||(index>=_size)) + abort(); //throw std::out_of_range("Index is out of Range of buffer size"); + index += _tail; + index %= _max_size; + return _buff[index]; +} + +template +inline +typename CircularBuffer::const_reference CircularBuffer::at(size_t index) const { + //std::lock_guard _lck(_mtx); + if((index<0)||(index>=_size)) + abort(); //throw std::out_of_range("Index is out of Range of buffer size"); + index += _tail; + index %= _max_size; + return _buff[index]; +} + +template +inline +typename CircularBuffer::iterator CircularBuffer::begin() { + //std::lock_guard _lck(_mtx); + iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = 0; + iter._reverse = false; + return iter; +} + +template +inline +typename CircularBuffer::const_iterator CircularBuffer::begin() const{ + //std::lock_guard _lck(_mtx); + const_iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = 0; + iter._reverse = false; + return iter; +} + +template +inline +typename CircularBuffer::iterator CircularBuffer::end() { + //std::lock_guard _lck(_mtx); + iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = _size; + iter._reverse = false; + return iter; +} + +template +inline +typename CircularBuffer::const_iterator CircularBuffer::end() const{ + //std::lock_guard _lck(_mtx); + const_iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = _size; + iter._reverse = false; + return iter; +} + +template +inline +typename CircularBuffer::const_iterator CircularBuffer::cbegin() const noexcept{ + //std::lock_guard _lck(_mtx); + const_iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = 0; + iter._reverse = false; + return iter; +} + +template +inline +typename CircularBuffer::const_iterator CircularBuffer::cend() const noexcept{ + //std::lock_guard _lck(_mtx); + const_iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = _size; + iter._reverse = false; + return iter; +} + +template +inline +typename CircularBuffer::iterator CircularBuffer::rbegin() noexcept{ + //std::lock_guard _lck(_mtx); + iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = 0; + iter._reverse = true; + return iter; +} + +template +inline +typename CircularBuffer::const_iterator CircularBuffer::rbegin() const noexcept{ + //std::lock_guard _lck(_mtx); + const_iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = 0; + iter._reverse = true; + return iter; +} + +template +inline +typename CircularBuffer::iterator CircularBuffer::rend() noexcept{ + //std::lock_guard _lck(_mtx); + iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = _size; + iter._reverse = true; + return iter; +} + +template +inline +typename CircularBuffer::const_iterator CircularBuffer::rend() const noexcept{ + //std::lock_guard _lck(_mtx); + const_iterator iter; + iter._ptrToBuffer = this; + iter._offset = _tail; + iter._index = _size; + iter._reverse = true; + return iter; +} + +#endif /* CIRCULAR_BUFFER_H */ diff --git a/src/gdt.cpp b/src/gdt.cpp new file mode 100644 index 0000000..24d974b --- /dev/null +++ b/src/gdt.cpp @@ -0,0 +1,169 @@ +#include +#include + +struct gdt_entry_bits { + std::uint32_t limit_low : 16; + std::uint32_t base_low : 24; + std::uint32_t accessed : 1; + std::uint32_t read_write : 1; // readable for code, writable for data + std::uint32_t conforming_expand_down : 1; // conforming for code, expand down for data + std::uint32_t code : 1; // 1 for code, 0 for data + std::uint32_t code_data_segment : 1; // should be 1 for everything but TSS and LDT + std::uint32_t DPL : 2; // privilege level + std::uint32_t present : 1; + std::uint32_t limit_high : 4; + std::uint32_t available : 1; // only used in software; has no effect on hardware + std::uint32_t long_mode : 1; + std::uint32_t big : 1; // 32-bit opcodes for code, uint32_t stack for data + std::uint32_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing + std::uint32_t base_high : 8; +} __attribute__((packed)); + +struct TSSEntry +{ + std::uint32_t prevTSS; + std::uint32_t esp0; + std::uint32_t ss0; + std::uint32_t unused[23] = {}; +} __attribute__((packed)); + +static TSSEntry tss = { + .prevTSS = 0, + .esp0 = 0, + .ss0 = 0x10 +}; + +static const std::array gdt {{ + {}, + /* kernel_code = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 1, + .code_data_segment = 1, + .DPL = 0, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* kernel_data = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 0, + .code_data_segment = 1, + .DPL = 0, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* user_code = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 1, + .code_data_segment = 1, + .DPL = 3, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* user_data = */ { + .limit_low = 0xFFFF, + .base_low = 0x0000, + .accessed = 0, + .read_write = 1, + .conforming_expand_down = 0, + .code = 0, + .code_data_segment = 1, + .DPL = 3, + .present = 1, + .limit_high = 0xF, + .available = 0, + .long_mode = 0, + .big = 1, + .gran = 1, + .base_high = 0x00 + }, + /* tss = */ { + .limit_low = sizeof(TSSEntry), + .base_low = (std::uint32_t)&tss & 0xFFFFFF, + .accessed = 1, + .read_write = 0, + .conforming_expand_down = 0, + .code = 1, + .code_data_segment = 0, + .DPL = 0, + .present = 1, + .limit_high = 0, + .available = 0, + .long_mode = 0, + .big = 0, + .gran = 0, + .base_high = (std::uint32_t)&tss >> 24 + } +}}; + +void gdt_initialize() +{ + auto gdtr = reinterpret_cast(gdt.data()); + gdtr <<= 16; + gdtr |= gdt.size() * sizeof(gdt[0]); + + asm volatile(R"( + lgdt %0 + pushl $0x8 + push $.setcs + ljmp *(%%esp) + .setcs: + add $8, %%esp + mov $0x10, %%eax + mov %%eax, %%ds + mov %%eax, %%es + mov %%eax, %%fs + mov %%eax, %%gs + mov %%eax, %%ss + + mov $0x28, %%ax + ltr %%ax + )" :: "m"(gdtr)); +} + +void enter_user_mode(void (*func)()) +{ + asm volatile("mov %%esp, %0" : "=r" (tss.esp0)); + + asm volatile(R"( + mov $0x23, %%ax + mov %%ax, %%ds + mov %%ax, %%es + mov %%ax, %%fs + mov %%ax, %%gs + mov %%esp, %%eax + push $0x23 + push %%esp + pushf + push $0x1b + push %0 + iret + )" :: "b"(func)); +} + diff --git a/src/gdt.hpp b/src/gdt.hpp new file mode 100644 index 0000000..de5e8fc --- /dev/null +++ b/src/gdt.hpp @@ -0,0 +1,8 @@ +#ifndef GDT_HPP +#define GDT_HPP + +void gdt_initialize(); +void enter_user_mode(void (*func)()); + +#endif // GDT_HPP + diff --git a/src/idt.cpp b/src/idt.cpp new file mode 100644 index 0000000..5380c9e --- /dev/null +++ b/src/idt.cpp @@ -0,0 +1,126 @@ +#include "idt.hpp" +#include "portio.hpp" +#include "textoutput.hpp" + +#include +#include +#include + +extern TextOutput& term; + +static constexpr unsigned InterruptCount = 49; + +static constexpr std::uint8_t TaskGate = 0x5; +static constexpr std::uint8_t IntrGate16 = 0x6; +static constexpr std::uint8_t TrapGate16 = 0x7; +static constexpr std::uint8_t IntrGate32 = 0xE; +static constexpr std::uint8_t TrapGate32 = 0xF; + +struct idt_entry_bits { + std::uint32_t offset_low : 16; + std::uint32_t segment_selector : 16; + std::uint32_t rsvd : 8 = 0; + std::uint32_t gate_type : 4; + std::uint32_t rsvd2 : 1 = 0; + std::uint32_t dpl : 2; + std::uint32_t present : 1; + std::uint32_t offset_high : 16; +} __attribute__((packed)); + +static std::array callbacks; + +extern "C" +void interruptGeneralHandler(Registers regs) +{ + const auto& inum = regs.inum; + + if (inum >= 32) { + if (inum >= 40) + outb(0xA0, 0x20); + + outb(0x20, 0x20); + } + + if (inum < callbacks.size()) { + if (auto cb = callbacks[inum]; cb) { + asm volatile("cli"); + cb(regs); + asm volatile("sti"); + } + } +} + +template +struct StubEntry +{ + static constexpr bool HasError = N == 8 || (N >= 10 && N <= 14) || N == 17 || N == 30; + + __attribute__((naked)) + static void stub() { + if constexpr (!HasError) + asm volatile("push $0x0"); + + asm volatile(R"( + pusha + mov %%ds, %%eax + push %%eax + mov $0x10, %%ax + mov %%ax, %%ds + mov %%ax, %%es + mov %%ax, %%fs + mov %%ax, %%gs + push %0 + cld + call interruptGeneralHandler + pop %%eax + pop %%eax + mov %%ax, %%ds + mov %%ax, %%es + mov %%ax, %%fs + mov %%ax, %%gs + popa + add $0x4, %%esp + iret + )" :: "i"(N)); + } + + static constexpr std::uint32_t segment(std::uint16_t gdt_idx, bool useLdt, std::uint16_t rpl) { + return gdt_idx | (useLdt ? 0x4 : 0x0) | (rpl & 0x3); + } + + idt_entry_bits entry = { + .offset_low = (uint32_t)stub & 0xFFFF, + .segment_selector = segment(0x8, false, 0), + .gate_type = IntrGate32, + .dpl = 0, + .present = 1, + .offset_high = (uint32_t)stub >> 16 + }; + + operator idt_entry_bits() const noexcept { + return entry; + } +}; + +static auto idt = + [](std::index_sequence) { + return std::array { StubEntry()... }; + }(std::make_index_sequence{}); + +void idt_initialize() +{ + idt[0x28].dpl = 3; + + auto idtr = reinterpret_cast(idt.data()); + idtr <<= 16; + idtr |= idt.size() * sizeof(idt[0]); + + asm volatile("lidt %0" :: "m"(idtr)); +} + +void idt_register_callback(std::size_t num, Callback cb) +{ + if (num < callbacks.size()) + callbacks[num] = cb; +} + diff --git a/src/idt.hpp b/src/idt.hpp new file mode 100644 index 0000000..c5e7e47 --- /dev/null +++ b/src/idt.hpp @@ -0,0 +1,22 @@ +#ifndef IDT_HPP +#define IDT_HPP + +#include +#include + +struct Registers +{ + std::uint32_t inum; + std::uint32_t ds; + std::uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; + std::uint32_t error; + std::uint32_t eip, cs, eflags; +} __attribute__((packed)); + +using Callback = void (*)(const Registers&); + +void idt_initialize(); +void idt_register_callback(std::size_t num, Callback cb); + +#endif // IDT_HPP + diff --git a/src/kernel.cpp b/src/kernel.cpp new file mode 100644 index 0000000..ae3f153 --- /dev/null +++ b/src/kernel.cpp @@ -0,0 +1,104 @@ +#include "acpi.hpp" +#include "ata.hpp" +#include "gdt.hpp" +#include "idt.hpp" +#include "keyboard.hpp" +#include "memory.hpp" +#include "multiboot.hpp" +#include "pic.hpp" +#include "pit.hpp" +#include "portio.hpp" +#include "tasking.hpp" +#include "vgaterminal.hpp" + +static VGATerminal vga; +TextOutput& term = vga; + +static volatile bool termBusy = false; + +void ata_probe(auto bus, ATA::Drive drv, const char *name) +{ + switch (bus.identify(drv)) { + case ATA::Type::ATA: + term.write("ata:"); + term.write(name); + term.write(": ATA drive detected.\n"); + break; + case ATA::Type::ATAPI: + term.write("ata:"); + term.write(name); + term.write(": ATAPI drive detected.\n"); + break; + default: + break; + } +} + +void kernel_main(void) +{ + term.write("Clyne's kernel, v2024\n\n"); + + if (!multiboot_initialize()) + for (;;); + + idt_register_callback(14, [](auto& regs) { + term.write("Page fault! eip="); + term.write(regs.eip); + term.write('\n'); + for (;;); + }); + + acpi_initialize(); + memory_initialize(); + gdt_initialize(); + pic_initialize(); + idt_initialize(); + pit_initialize(); + keyboard_initialize(); + asm volatile("sti"); + tasking_initialize(); + term.write("Tasking enabled.\n"); + + ATA::Bus bus0; + ATA::Bus bus1; + + ata_probe(bus0, ATA::Master, "0:0"); + ata_probe(bus0, ATA::Slave, "0:1"); + ata_probe(bus1, ATA::Master, "1:0"); + ata_probe(bus1, ATA::Slave, "1:1"); + + idt_register_callback(0x28, [](auto& regs) { + term.write(static_cast(regs.eax)); + }); + + term.write("Entering user mode...\n"); + enter_user_mode([] { + asm volatile("int $0x28" :: "a" ('Z')); + for (;;); + }); + + for (;;) { + const auto ch = keyboard_read(); + if (ch) + term.write(*ch); + + pit_delay_ms(10); + } +} + +extern "C" +void abort() +{ + term.write("!!! abort() called !!!"); + asm volatile("cli"); + for (;;); +} + +extern "C" +int __cxa_atexit(void (*)(void *), void *, void *) +{ + return 0; +} + +int __dso_handle = 0; + diff --git a/src/keyboard.cpp b/src/keyboard.cpp new file mode 100644 index 0000000..48dad02 --- /dev/null +++ b/src/keyboard.cpp @@ -0,0 +1,64 @@ +#include "circularbuffer.hpp" +#include "idt.hpp" +#include "keyboard.hpp" +#include "portio.hpp" +#include "vgaterminal.hpp" + +#include +#include + +extern TextOutput& term; + +static CircularBuffer keyboardBuffer; +static Port<0x60> keyboardPort; + +static const std::array ScanCodeSet1 {{ + 0, K_ESCAPE, + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '-', '=', '\b', '\t', + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', '\n', K_CONTROL_L, + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', '`', K_SHIFT_L, '\\', + 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', + K_SHIFT_R, '*', K_ALT_L, ' ', K_CAPS, + K_F1, K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, + K_NUM, K_SCROLL, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', + 0, 0, 0, // non-existant + K_F11, K_F12 +}}; + +static inline bool isReleased(auto ch) { + return ch & 0x80; +} + +static inline auto keycode(auto ch) { + return ch & 0x7F; +} + +void keyboard_initialize() +{ + keyboardBuffer = CircularBuffer(128); + + idt_register_callback(33, [](auto&) { + const std::uint8_t kc = keyboardPort; + + if (!isReleased(kc)) { + const auto ch = ScanCodeSet1[keycode(kc)]; + //if (ch > 0) + // term.write(ch); + + keyboardBuffer.push_back(ch); + } + }); +} + +std::optional keyboard_read() +{ + if (keyboardBuffer.empty()) { + return {}; + } else { + const auto ch = keyboardBuffer.front(); + keyboardBuffer.pop_front(); + return ch; + } +} + diff --git a/src/keyboard.hpp b/src/keyboard.hpp new file mode 100644 index 0000000..694ff89 --- /dev/null +++ b/src/keyboard.hpp @@ -0,0 +1,32 @@ +#ifndef KEYBOARD_HPP +#define KEYBOARD_HPP + +#include + +#define K_CONTROL_L -1 +#define K_SHIFT_L -2 +#define K_ALT_L -3 +#define K_CAPS -4 +#define K_NUM -5 +#define K_SCROLL -6 +#define K_SHIFT_R -7 +#define K_ESCAPE -8 +#define K_F1 -10 +#define K_F2 -11 +#define K_F3 -12 +#define K_F4 -13 +#define K_F5 -14 +#define K_F6 -15 +#define K_F7 -16 +#define K_F8 -17 +#define K_F9 -18 +#define K_F10 -19 +#define K_F11 -20 +#define K_F12 -21 + +void keyboard_initialize(); + +std::optional keyboard_read(); + +#endif // KEYBOARD_HPP + diff --git a/src/memmove.cpp b/src/memmove.cpp new file mode 100644 index 0000000..06e37df --- /dev/null +++ b/src/memmove.cpp @@ -0,0 +1,78 @@ +/* Taken from newlib... */ + +#include +#include +#include + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +extern "C" +void *memmove (void *dst_void, const void *src_void, size_t length) +{ + char *dst = reinterpret_cast(dst_void); + const char *src = reinterpret_cast(src_void); + long *aligned_dst; + const long *aligned_src; + + if (src < dst && dst < src + length) + { + /* Destructive overlap...have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + /* Use optimizing algorithm for a non-destructive copy to closely + match memcpy. If the size is small or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (length >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + length -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (length >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + length -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +} + diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000..108c240 --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,107 @@ +#include "textoutput.hpp" + +#include +#include + +struct PageDirectory +{ + static constexpr std::uint32_t NotPresent = 0x2; + + PageDirectory(): value(NotPresent) {} + PageDirectory(void *addr): value(reinterpret_cast(addr) | 7) {} + + std::uint32_t value; +}; +static_assert(sizeof(PageDirectory) == sizeof(std::uint32_t)); + +extern std::uint32_t lowerMem; +extern std::uint32_t upperMem; +extern TextOutput& term; + +static std::uintptr_t lowerFree = 0x400; +static std::uintptr_t upperFree = 0x100000; + +alignas(4096) +static std::array pageDirectory; + +alignas(4096) +static std::array pageTable; + +void memory_initialize() +{ + lowerMem -= 1024; + + const auto totalKb = (lowerMem + upperMem) / 1024u; + + term.write("Claiming "); + term.write(totalKb); + term.write(" kB for allocations...\n"); + + std::uint32_t addr = 0; + for (auto& p : pageTable) { + p = addr | 7; // supervisor, r/w, present + addr += 0x1000; + } + + pageDirectory[0] = PageDirectory(pageTable.data()); + + asm volatile(R"( + mov %%eax, %%cr3 + mov %%cr0, %%eax + or $0x80000000, %%eax + mov %%eax, %%cr0 + )" :: "a"(pageDirectory.data())); + + term.write("Paging enabled.\n"); +} + +static void *memory_alloc(std::size_t size) +{ + void *ret = nullptr; + + if (lowerMem > size) { + ret = reinterpret_cast(lowerFree); + lowerFree += size; + lowerMem -= size; + } else if (upperMem > size) { + ret = reinterpret_cast(upperFree); + upperFree += size; + upperMem -= size; + } else { + // Uh oh! + term.write("!!! Kernel allocation failed !!!"); + } + + return ret; +} + +void *operator new(std::size_t size) +{ + return memory_alloc(size); +} + +void *operator new[](std::size_t size) +{ + return memory_alloc(size); +} + +void operator delete(void *) +{ + +} + +void operator delete[](void *) +{ + +} + +void operator delete(void *, std::size_t) +{ + +} + +void operator delete[](void *, std::size_t) +{ + +} + diff --git a/src/memory.hpp b/src/memory.hpp new file mode 100644 index 0000000..0467bf6 --- /dev/null +++ b/src/memory.hpp @@ -0,0 +1,38 @@ +#ifndef MEMORY_HPP +#define MEMORY_HPP + +#include +#include + +template +struct kallocator +{ + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template + struct rebind { + typedef kallocator other; + }; + + kallocator() = default; + + template + kallocator(const kallocator&) noexcept {} + + T* allocate(std::size_t n) { + return new T[n]; + } + + void deallocate([[maybe_unused]] T* p, [[maybe_unused]] std::size_t n) { + + } +}; + +void memory_initialize(); + +#endif // MEMORY_HPP + diff --git a/src/multiboot.cpp b/src/multiboot.cpp new file mode 100644 index 0000000..46c505b --- /dev/null +++ b/src/multiboot.cpp @@ -0,0 +1,92 @@ +#include "textoutput.hpp" + +#include + +extern TextOutput& term; + +struct multiboot2_tag +{ + alignas(8) + std::uint16_t id; + std::uint16_t flags; + std::uint32_t length; + std::uint32_t data[1]; +} __attribute__((packed)); + +template +struct multiboot2 +{ + static constexpr std::uint32_t MAGIC = 0xE85250D6; + static constexpr std::uint32_t FLAGS = 0; + static constexpr std::uint32_t LENGTH = 16; + static constexpr std::uint32_t CHECKSUM = -(MAGIC + FLAGS + LENGTH); + + alignas(8) + std::uint32_t magic = MAGIC; + std::uint32_t flags = FLAGS; + std::uint32_t length = LENGTH; + std::uint32_t checksum = CHECKSUM; + + multiboot2_tag tags[N]; +} __attribute__((packed)); + +__attribute__((section(".multiboot2"))) +multiboot2 multibootHeader = { + .tags = { + { + 1, 0, sizeof(multiboot2_tag) + sizeof(std::uint32_t), + {4} + }, + { + 0, 0, 8, {} + } + } +}; + +std::uint32_t multiboot_magic; +std::uint32_t *multiboot_ptr; + +std::uint32_t lowerMem = 0; +std::uint32_t upperMem = 0; +std::uint32_t *acpiRsdp = nullptr; +std::uint32_t *acpiRsdpV2 = nullptr; + +bool multiboot_initialize() +{ + if (multiboot_magic != 0x36d76289) { + term.write("Not multiboot!"); + return false; + } + + term.write("Found multiboot headers: "); + + auto ptr = multiboot_ptr + 2; + while (ptr[0] != 0 && ptr[1] != 8) { + term.write(ptr[0]); + term.write(", "); + + switch (ptr[0]) { + case 4: + lowerMem = ptr[2] * 1024; + upperMem = ptr[3] * 1024; + break; + case 14: + acpiRsdp = ptr + 2; + break; + case 15: + acpiRsdpV2 = ptr + 2; + break; + default: + break; + } + + auto next = reinterpret_cast(ptr); + next += ptr[1]; + next = (next + 7) & ~7; + ptr = reinterpret_cast(next); + } + + term.write('\n'); + return true; +} + diff --git a/src/multiboot.hpp b/src/multiboot.hpp new file mode 100644 index 0000000..9916850 --- /dev/null +++ b/src/multiboot.hpp @@ -0,0 +1,7 @@ +#ifndef MULTIBOOT_HPP +#define MULTIBOOT_HPP + +bool multiboot_initialize(); + +#endif // MULTIBOOT_HPP + diff --git a/src/pic.cpp b/src/pic.cpp new file mode 100644 index 0000000..437f384 --- /dev/null +++ b/src/pic.cpp @@ -0,0 +1,63 @@ +#include "pic.hpp" + +#include "portio.hpp" + +#define PIC1 0x20 /* IO base address for master PIC */ +#define PIC2 0xA0 /* IO base address for slave PIC */ +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) + +#define PIC_EOI 0x20 /* End-of-interrupt command code */ + +#define ICW1_ICW4 0x01 /* Indicates that ICW4 will be present */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + +void pic_initialize() +{ + constexpr int offset1 = 0x20, offset2 = 0x28; + std::uint8_t a1, a2; + + a1 = inb(PIC1_DATA); // save masks + a2 = inb(PIC2_DATA); + + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) + io_wait(); + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset + io_wait(); + outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset + io_wait(); + outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) + io_wait(); + outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) + io_wait(); + + outb(PIC1_DATA, ICW4_8086); // ICW4: have the PICs use 8086 mode (and not 8080 mode) + io_wait(); + outb(PIC2_DATA, ICW4_8086); + io_wait(); + + outb(PIC1_DATA, a1); // restore saved masks. + outb(PIC2_DATA, a2); +} + +void pic_eoi(std::uint8_t irq) +{ + if (irq >= 8) + outb(PIC2_COMMAND, PIC_EOI); + + outb(PIC1_COMMAND, PIC_EOI); +} + diff --git a/src/pic.hpp b/src/pic.hpp new file mode 100644 index 0000000..17abd55 --- /dev/null +++ b/src/pic.hpp @@ -0,0 +1,12 @@ +#ifndef PIC_HPP +#define PIC_HPP + +#include + +/* reinitialize the PIC controllers, giving them specified vector offsets + rather than 8h and 70h, as configured by default */ +void pic_initialize(); +void pic_eoi(std::uint8_t irq); + +#endif // PIC_HPP + diff --git a/src/pit.cpp b/src/pit.cpp new file mode 100644 index 0000000..964522b --- /dev/null +++ b/src/pit.cpp @@ -0,0 +1,41 @@ +#include "pit.hpp" +#include "idt.hpp" +#include "portio.hpp" +#include "tasking.hpp" + +constexpr std::uint32_t Frequency = 1000; + +static volatile std::uint32_t ticks = 0; + +static void timer_callback(const Registers& regs) +{ + ticks = ticks + 1; + + schedule(regs); +} + +void pit_initialize() +{ + // Firstly, register our timer callback. + idt_register_callback(32, timer_callback); + + // The value we send to the PIT is the value to divide it's input clock + // (1193180 Hz) by, to get our required frequency. Important to note is + // that the divisor must be small enough to fit into 16-bits. + const auto divisor = 1193180ul / Frequency; + + // Send the command byte. + outb(0x43, 0x36); + + // Send the frequency divisor. + outb(0x40, divisor & 0xFF); + outb(0x40, (divisor >> 8) & 0xFF); +} + +void pit_delay_ms(std::int32_t ms) +{ + const auto end = ticks + ms; + while (static_cast(end - ticks) > 0) + asm volatile("nop"); +} + diff --git a/src/pit.hpp b/src/pit.hpp new file mode 100644 index 0000000..55bcde9 --- /dev/null +++ b/src/pit.hpp @@ -0,0 +1,10 @@ +#ifndef PIT_HPP +#define PIT_HPP + +#include + +void pit_initialize(); +void pit_delay_ms(std::int32_t ms); + +#endif // PIT_HPP + diff --git a/src/portio.hpp b/src/portio.hpp new file mode 100644 index 0000000..7636c14 --- /dev/null +++ b/src/portio.hpp @@ -0,0 +1,72 @@ +#ifndef PORTIO_HPP +#define PORTIO_HPP + +#include + +inline void outb(std::uint16_t port, std::uint8_t val) +{ + asm volatile("out %%al, %%dx" :: "a"(val), "Nd"(port) : "memory"); +} + +inline std::uint8_t inb(std::uint16_t port) +{ + std::uint8_t val; + asm volatile("inb %%dx" : "=a"(val) : "Nd"(port) : "memory"); + return val; +} + +inline void outw(std::uint16_t port, std::uint16_t val) +{ + asm volatile("out %%ax, %%dx" :: "a"(val), "Nd"(port) : "memory"); +} + +inline std::uint16_t inw(std::uint16_t port) +{ + std::uint16_t val; + asm volatile("inw %%dx" : "=a"(val) : "Nd"(port) : "memory"); + return val; +} + +inline void io_wait() +{ + outb(0x80, 0); +} + +template +struct Port +{ + Port() = default; + + template + auto operator=(T val) noexcept { + if constexpr (sizeof(T) == 1) + outb(Addr, val); + else if constexpr (sizeof(T) == 2) + outw(Addr, val); + + return *this; + } + + template + operator T() const noexcept { + if constexpr (sizeof(T) == 1) + return inb(Addr); + else if constexpr (sizeof(T) == 2) + return inw(Addr); + } + + template + bool operator==(T val) const noexcept { + T dat = *this; + return dat == val; + } + + template + auto operator&(T val) const noexcept { + T dat = *this; + return dat & val; + } +}; + +#endif // PORTIO_HPP + diff --git a/src/tasking.cpp b/src/tasking.cpp new file mode 100644 index 0000000..1b78011 --- /dev/null +++ b/src/tasking.cpp @@ -0,0 +1,92 @@ +#include "tasking.hpp" + +#include + +struct Task +{ + enum class State { + Invalid, + Staging, + Staged, + Running + }; + + using enum State; + + std::uint32_t esp; + std::uint32_t ebp; + State state = State::Invalid; +}; + +static std::array tasks; +static int current = -1; + +void schedule(const Registers&) +{ + if (current < 0) + return; + + asm volatile(R"( + mov %%esp, %0 + mov %%ebp, %1 + )" : "=m" (tasks[current].esp), "=m" (tasks[current].ebp)); + + do { + if (++current >= static_cast(tasks.size())) + current = 0; + } while (tasks[current].state == Task::Invalid || tasks[current].state == Task::Staging); + + asm volatile(R"( + mov %0, %%esp + mov %1, %%ebp + )" :: "m" (tasks[current].esp), "m" (tasks[current].ebp)); + + if (tasks[current].state == Task::Staged) { + tasks[current].state = Task::Running; + asm volatile(R"( + pop %eax + popa + add $0x4, %esp + iret + )"); + } +} + +void tasking_initialize() +{ + tasks[0].state = Task::Running; + current = 0; + asm volatile("int $0x20"); +} + +bool tasking_spawn(void (*entry)(), unsigned ssize) +{ + unsigned i; + for (i = 0; i < tasks.size(); ++i) { + if (tasks[i].state == Task::Invalid) + break; + } + + if (i >= tasks.size()) + return false; + + tasks[i].state = Task::Staging; + + auto stack = reinterpret_cast(new std::uint8_t[ssize]); + const auto stackend = stack + ssize; + const auto regbase = stackend - sizeof(Registers); + auto r = reinterpret_cast(regbase); + r->ebp = stackend; + r->esp = stackend; + r->eip = reinterpret_cast(entry); + r->cs = 0x8; + asm volatile("pushfl; pop %%eax" : "=a"(r->eflags)); + + tasks[i] = Task { + .esp = regbase, + .ebp = stackend, + .state = Task::Staged + }; + return true; +} + diff --git a/src/tasking.hpp b/src/tasking.hpp new file mode 100644 index 0000000..64a738e --- /dev/null +++ b/src/tasking.hpp @@ -0,0 +1,12 @@ +#ifndef TASKING_HPP +#define TASKING_HPP + +#include "idt.hpp" + +void tasking_initialize(); +bool tasking_spawn(void (*entry)(), unsigned ssize); + +void schedule(const Registers& regs); + +#endif // TASKING_HPP + diff --git a/src/textoutput.hpp b/src/textoutput.hpp new file mode 100644 index 0000000..7ef12fa --- /dev/null +++ b/src/textoutput.hpp @@ -0,0 +1,44 @@ +#ifndef TEXTOUTPUT_HPP +#define TEXTOUTPUT_HPP + +class TextOutput +{ +public: + virtual void write(char c) noexcept = 0; + + void write(const char *s) noexcept { + if (s) { + while (*s) + write(*s++); + } + } + + void write(int n) noexcept { + char buf[32]; + auto ptr = buf + sizeof(buf); + + *--ptr = '\0'; + do { + *--ptr = "0123456789"[n % 10]; + n /= 10; + } while (n); + + write(ptr); + } + + void write(unsigned n) noexcept { + char buf[32]; + auto ptr = buf + sizeof(buf); + + *--ptr = '\0'; + do { + *--ptr = "0123456789"[n % 10]; + n /= 10; + } while (n); + + write(ptr); + } +}; + +#endif // TEXTOUTPUT_HPP + diff --git a/src/vgaterminal.cpp b/src/vgaterminal.cpp new file mode 100644 index 0000000..5f86081 --- /dev/null +++ b/src/vgaterminal.cpp @@ -0,0 +1,63 @@ +#include "portio.hpp" +#include "vgaterminal.hpp" + +#include +#include +#include +#include + +void VGATerminal::write(char c) noexcept +{ + checkpos(); + + switch (c) { + case '\b': + if (offset % Width) { + --offset; + put(' '); + --offset; + } + break; + case '\n': + offset += Width; + [[fallthrough]]; + case '\r': + offset -= offset % Width; + break; + default: + put(c); + break; + } + + updatecursor(); +} + +void VGATerminal::put(char c) noexcept +{ + std::uint16_t cell = c + | (std::to_underlying(foreground) << 8) + | (std::to_underlying(background) << 12); + + auto ptr = reinterpret_cast(Videoram); + ptr[offset++] = cell; +} + +void VGATerminal::checkpos() noexcept +{ + if (offset >= Width * Height) { + auto ptr = reinterpret_cast(Videoram); + const auto end = ptr + Width * Height; + std::copy(ptr + Width, end, ptr); + std::fill(end - Width, end, 0); + offset = Width * Height - Width; + } +} + +void VGATerminal::updatecursor() const noexcept +{ + outb(0x03d4, 0x0f); + outb(0x03d5, static_cast(offset)); + outb(0x03d4, 0x0e); + outb(0x03d5, static_cast(offset >> 8)); +} + diff --git a/src/vgaterminal.hpp b/src/vgaterminal.hpp new file mode 100644 index 0000000..9f8d5f3 --- /dev/null +++ b/src/vgaterminal.hpp @@ -0,0 +1,51 @@ +#ifndef VGATERMINAL_HPP +#define VGATERMINAL_HPP + +#include "textoutput.hpp" + +#include +#include + +class VGATerminal : public TextOutput +{ +public: + enum class Color : std::uint8_t + { + Black = 0, + Blue, + Green, + Cyan, + Red, + Magenta, + Brown, + LightGray, + DarkGray, + LightBlue, + LightGreen, + LightCyan, + LightRed, + LightMagenta, + LightBrown, + White + }; + + using enum Color; + + virtual void write(char c) noexcept final; + +private: + static constexpr std::uintptr_t Videoram = 0xB8000; + static constexpr unsigned Width = 80; + static constexpr unsigned Height = 25; + + unsigned offset = 0; + Color foreground = LightGray; + Color background = Black; + + void put(char c) noexcept; + void checkpos() noexcept; + void updatecursor() const noexcept; +}; + +#endif // VGATERMINAL_HPP + diff --git a/tasking.cpp b/tasking.cpp deleted file mode 100644 index 1b78011..0000000 --- a/tasking.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "tasking.hpp" - -#include - -struct Task -{ - enum class State { - Invalid, - Staging, - Staged, - Running - }; - - using enum State; - - std::uint32_t esp; - std::uint32_t ebp; - State state = State::Invalid; -}; - -static std::array tasks; -static int current = -1; - -void schedule(const Registers&) -{ - if (current < 0) - return; - - asm volatile(R"( - mov %%esp, %0 - mov %%ebp, %1 - )" : "=m" (tasks[current].esp), "=m" (tasks[current].ebp)); - - do { - if (++current >= static_cast(tasks.size())) - current = 0; - } while (tasks[current].state == Task::Invalid || tasks[current].state == Task::Staging); - - asm volatile(R"( - mov %0, %%esp - mov %1, %%ebp - )" :: "m" (tasks[current].esp), "m" (tasks[current].ebp)); - - if (tasks[current].state == Task::Staged) { - tasks[current].state = Task::Running; - asm volatile(R"( - pop %eax - popa - add $0x4, %esp - iret - )"); - } -} - -void tasking_initialize() -{ - tasks[0].state = Task::Running; - current = 0; - asm volatile("int $0x20"); -} - -bool tasking_spawn(void (*entry)(), unsigned ssize) -{ - unsigned i; - for (i = 0; i < tasks.size(); ++i) { - if (tasks[i].state == Task::Invalid) - break; - } - - if (i >= tasks.size()) - return false; - - tasks[i].state = Task::Staging; - - auto stack = reinterpret_cast(new std::uint8_t[ssize]); - const auto stackend = stack + ssize; - const auto regbase = stackend - sizeof(Registers); - auto r = reinterpret_cast(regbase); - r->ebp = stackend; - r->esp = stackend; - r->eip = reinterpret_cast(entry); - r->cs = 0x8; - asm volatile("pushfl; pop %%eax" : "=a"(r->eflags)); - - tasks[i] = Task { - .esp = regbase, - .ebp = stackend, - .state = Task::Staged - }; - return true; -} - diff --git a/tasking.hpp b/tasking.hpp deleted file mode 100644 index 64a738e..0000000 --- a/tasking.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef TASKING_HPP -#define TASKING_HPP - -#include "idt.hpp" - -void tasking_initialize(); -bool tasking_spawn(void (*entry)(), unsigned ssize); - -void schedule(const Registers& regs); - -#endif // TASKING_HPP - diff --git a/textoutput.hpp b/textoutput.hpp deleted file mode 100644 index 7ef12fa..0000000 --- a/textoutput.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef TEXTOUTPUT_HPP -#define TEXTOUTPUT_HPP - -class TextOutput -{ -public: - virtual void write(char c) noexcept = 0; - - void write(const char *s) noexcept { - if (s) { - while (*s) - write(*s++); - } - } - - void write(int n) noexcept { - char buf[32]; - auto ptr = buf + sizeof(buf); - - *--ptr = '\0'; - do { - *--ptr = "0123456789"[n % 10]; - n /= 10; - } while (n); - - write(ptr); - } - - void write(unsigned n) noexcept { - char buf[32]; - auto ptr = buf + sizeof(buf); - - *--ptr = '\0'; - do { - *--ptr = "0123456789"[n % 10]; - n /= 10; - } while (n); - - write(ptr); - } -}; - -#endif // TEXTOUTPUT_HPP - diff --git a/vgaterminal.cpp b/vgaterminal.cpp deleted file mode 100644 index 5f86081..0000000 --- a/vgaterminal.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "portio.hpp" -#include "vgaterminal.hpp" - -#include -#include -#include -#include - -void VGATerminal::write(char c) noexcept -{ - checkpos(); - - switch (c) { - case '\b': - if (offset % Width) { - --offset; - put(' '); - --offset; - } - break; - case '\n': - offset += Width; - [[fallthrough]]; - case '\r': - offset -= offset % Width; - break; - default: - put(c); - break; - } - - updatecursor(); -} - -void VGATerminal::put(char c) noexcept -{ - std::uint16_t cell = c - | (std::to_underlying(foreground) << 8) - | (std::to_underlying(background) << 12); - - auto ptr = reinterpret_cast(Videoram); - ptr[offset++] = cell; -} - -void VGATerminal::checkpos() noexcept -{ - if (offset >= Width * Height) { - auto ptr = reinterpret_cast(Videoram); - const auto end = ptr + Width * Height; - std::copy(ptr + Width, end, ptr); - std::fill(end - Width, end, 0); - offset = Width * Height - Width; - } -} - -void VGATerminal::updatecursor() const noexcept -{ - outb(0x03d4, 0x0f); - outb(0x03d5, static_cast(offset)); - outb(0x03d4, 0x0e); - outb(0x03d5, static_cast(offset >> 8)); -} - diff --git a/vgaterminal.hpp b/vgaterminal.hpp deleted file mode 100644 index 9f8d5f3..0000000 --- a/vgaterminal.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef VGATERMINAL_HPP -#define VGATERMINAL_HPP - -#include "textoutput.hpp" - -#include -#include - -class VGATerminal : public TextOutput -{ -public: - enum class Color : std::uint8_t - { - Black = 0, - Blue, - Green, - Cyan, - Red, - Magenta, - Brown, - LightGray, - DarkGray, - LightBlue, - LightGreen, - LightCyan, - LightRed, - LightMagenta, - LightBrown, - White - }; - - using enum Color; - - virtual void write(char c) noexcept final; - -private: - static constexpr std::uintptr_t Videoram = 0xB8000; - static constexpr unsigned Width = 80; - static constexpr unsigned Height = 25; - - unsigned offset = 0; - Color foreground = LightGray; - Color background = Black; - - void put(char c) noexcept; - void checkpos() noexcept; - void updatecursor() const noexcept; -}; - -#endif // VGATERMINAL_HPP -