mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 01:41:59 -05:00
Import the "gerbert" kernel I worked on earlier this year.
It's a lot crappier than I remembered it. It's gonna need a lot of work.
This commit is contained in:
parent
f608629704
commit
9396108034
55 changed files with 4600 additions and 0 deletions
4
Kernel/.gitignore
vendored
Normal file
4
Kernel/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.o
|
||||
.floppy-image
|
||||
Boot/boot.bin
|
||||
kernel
|
8
Kernel/Assertions.h
Normal file
8
Kernel/Assertions.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "VGA.h"
|
||||
|
||||
#define CRASH() do { asm volatile("cli;hlt"); } while(0)
|
||||
#define ASSERT(x) do { if (!(x)) { vga_set_attr(0x4f); kprintf("ASSERTION FAILED: " #x "\n%s:%u in %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); CRASH(); } } while(0)
|
||||
#define RELEASE_ASSERT(x) do { if (!(x)) CRASH(); } while(0)
|
||||
#define ASSERT_NOT_REACHED() ASSERT(false)
|
106
Kernel/Boot/boot.asm
Executable file
106
Kernel/Boot/boot.asm
Executable file
|
@ -0,0 +1,106 @@
|
|||
; asmsyntax=nasm
|
||||
|
||||
[org 0x7c00]
|
||||
[bits 16]
|
||||
|
||||
boot:
|
||||
push cs
|
||||
pop ds
|
||||
xor bx, bx
|
||||
mov ah, 0x0e
|
||||
mov si, message
|
||||
lodsb
|
||||
.lewp:
|
||||
int 0x10
|
||||
lodsb
|
||||
cmp al, 0
|
||||
jne .lewp
|
||||
|
||||
mov bx, 0x1000
|
||||
mov es, bx
|
||||
xor bx, bx ; Load kernel @ 0x10000
|
||||
|
||||
mov ah, 0x02 ; cmd 0x02 - Read Disk Sectors
|
||||
mov al, 72 ; 72 sectors (max allowed by bochs BIOS)
|
||||
mov ch, 0 ; track 0
|
||||
mov cl, 10 ; sector 10
|
||||
mov dh, 0 ; head 0
|
||||
mov dl, 0 ; drive 0 (fd0)
|
||||
int 0x13
|
||||
|
||||
jc fug
|
||||
|
||||
mov ah, 0x02
|
||||
mov al, 32
|
||||
add bx, 0x9000
|
||||
mov ch, 2
|
||||
mov cl, 10
|
||||
mov dh, 0
|
||||
mov dl, 0
|
||||
int 0x13
|
||||
|
||||
jc fug
|
||||
|
||||
lgdt [cs:test_gdt_ptr]
|
||||
|
||||
mov eax, cr0
|
||||
or al, 1
|
||||
mov cr0, eax
|
||||
|
||||
jmp 0x08:pmode
|
||||
|
||||
pmode:
|
||||
[bits 32]
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
mov ss, ax
|
||||
mov esp, 0x2000
|
||||
|
||||
jmp 0x10000
|
||||
|
||||
hlt
|
||||
|
||||
test_gdt_ptr:
|
||||
dw (test_gdt_end-test_gdt)
|
||||
dd test_gdt
|
||||
|
||||
test_gdt:
|
||||
dd 0
|
||||
dd 0
|
||||
dd 0x0000ffff
|
||||
dd 0x00cf9a00
|
||||
dd 0x0000ffff
|
||||
dd 0x00cf9200
|
||||
dd 0
|
||||
dd 0
|
||||
dd 0
|
||||
dd 0
|
||||
test_gdt_end:
|
||||
|
||||
[bits 16]
|
||||
fug:
|
||||
xor bx, bx
|
||||
mov ah, 0x0e
|
||||
mov si, fug_message
|
||||
lodsb
|
||||
.lewp:
|
||||
int 0x10
|
||||
lodsb
|
||||
cmp al, 0
|
||||
jne .lewp
|
||||
|
||||
cli
|
||||
hlt
|
||||
|
||||
message:
|
||||
db "boot!", 0x0d, 0x0a, 0
|
||||
|
||||
fug_message:
|
||||
db "FUG!", 0x0d, 0x0a, 0
|
||||
|
||||
times 510-($-$$) db 0
|
||||
dw 0xaa55
|
18
Kernel/CMOS.cpp
Normal file
18
Kernel/CMOS.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "CMOS.h"
|
||||
#include "IO.h"
|
||||
|
||||
namespace CMOS {
|
||||
|
||||
BYTE read(BYTE index)
|
||||
{
|
||||
IO::out8(0x70, index);
|
||||
return IO::in8(0x71);
|
||||
}
|
||||
|
||||
void write(BYTE index, BYTE data)
|
||||
{
|
||||
IO::out8(0x70, index);
|
||||
IO::out8(0x71, data);
|
||||
}
|
||||
|
||||
}
|
10
Kernel/CMOS.h
Normal file
10
Kernel/CMOS.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace CMOS {
|
||||
|
||||
BYTE read(BYTE index);
|
||||
void write(BYTE index, BYTE data);
|
||||
|
||||
}
|
75
Kernel/DataBuffer.cpp
Normal file
75
Kernel/DataBuffer.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "DataBuffer.h"
|
||||
#include "StdLib.h"
|
||||
|
||||
#define SANITIZE_DATABUFFER
|
||||
|
||||
DataBuffer::DataBuffer(size_t length)
|
||||
: m_length(length)
|
||||
, m_owned(true)
|
||||
{
|
||||
m_data = new BYTE[m_length];
|
||||
#ifdef SANITIZE_DATABUFFER
|
||||
memset(m_data, 0x1a, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
DataBuffer::DataBuffer(BYTE* data, size_t length, ConstructionMode mode)
|
||||
: m_length(length)
|
||||
{
|
||||
switch (mode) {
|
||||
case Copy:
|
||||
m_data = new BYTE[m_length];
|
||||
memcpy(m_data, data, m_length);
|
||||
m_owned = true;
|
||||
break;
|
||||
case Adopt:
|
||||
m_data = data;
|
||||
m_owned = true;
|
||||
break;
|
||||
case Wrap:
|
||||
m_data = data;
|
||||
m_owned = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DataBuffer::~DataBuffer()
|
||||
{
|
||||
clear();
|
||||
#ifdef SANITIZE_DATABUFFER
|
||||
m_data = (BYTE*)0x88888888;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DataBuffer::clear()
|
||||
{
|
||||
if (m_owned) {
|
||||
#ifdef SANITIZE_DATABUFFER
|
||||
memset(m_data, 0x99, m_length);
|
||||
#endif
|
||||
delete [] m_data;
|
||||
}
|
||||
m_owned = false;
|
||||
m_data = nullptr;
|
||||
m_length = 0;
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> DataBuffer::createUninitialized(size_t length)
|
||||
{
|
||||
return adoptRef(new DataBuffer(length));
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> DataBuffer::copy(const BYTE* data, size_t length)
|
||||
{
|
||||
return adoptRef(new DataBuffer(const_cast<BYTE*>(data), length, Copy));
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> DataBuffer::wrap(BYTE* data, size_t length)
|
||||
{
|
||||
return adoptRef(new DataBuffer(data, length, Wrap));
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> DataBuffer::adopt(BYTE* data, size_t length)
|
||||
{
|
||||
return adoptRef(new DataBuffer(data, length, Adopt));
|
||||
}
|
39
Kernel/DataBuffer.h
Normal file
39
Kernel/DataBuffer.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "RefCounted.h"
|
||||
#include "RefPtr.h"
|
||||
|
||||
class DataBuffer : public RefCounted<DataBuffer> {
|
||||
public:
|
||||
~DataBuffer();
|
||||
|
||||
BYTE operator[](size_t i) const { return m_data[i]; }
|
||||
bool isEmpty() const { return !m_length; }
|
||||
size_t length() const { return m_length; }
|
||||
BYTE* data() { return m_data; }
|
||||
const BYTE* data() const { return m_data; }
|
||||
|
||||
static RefPtr<DataBuffer> copy(const BYTE*, size_t length);
|
||||
static RefPtr<DataBuffer> wrap(BYTE*, size_t length);
|
||||
static RefPtr<DataBuffer> adopt(BYTE*, size_t length);
|
||||
static RefPtr<DataBuffer> createUninitialized(size_t length);
|
||||
|
||||
void clear();
|
||||
void leak() { m_data = nullptr; m_length = 0; m_owned = false; }
|
||||
|
||||
private:
|
||||
DataBuffer() { }
|
||||
DataBuffer(DataBuffer&&) = delete;
|
||||
DataBuffer& operator=(DataBuffer&&) = delete;
|
||||
|
||||
enum ConstructionMode { Copy, Wrap, Adopt };
|
||||
explicit DataBuffer(size_t length);
|
||||
DataBuffer(BYTE*, size_t length, ConstructionMode);
|
||||
DataBuffer(const DataBuffer&) = delete;
|
||||
DataBuffer& operator=(const DataBuffer&) = delete;
|
||||
|
||||
BYTE* m_data { nullptr };
|
||||
size_t m_length { 0 };
|
||||
bool m_owned { false };
|
||||
};
|
195
Kernel/Disk.cpp
Normal file
195
Kernel/Disk.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
#include "types.h"
|
||||
#include "Task.h"
|
||||
#include "IPC.h"
|
||||
#include "VGA.h"
|
||||
#include "Disk.h"
|
||||
#include "kmalloc.h"
|
||||
#include "StdLib.h"
|
||||
#include "IO.h"
|
||||
#include "i386.h"
|
||||
#include "DataBuffer.h"
|
||||
#include "PIC.h"
|
||||
|
||||
//#define DISK_DEBUG
|
||||
|
||||
extern "C" void handle_interrupt();
|
||||
|
||||
namespace Disk {
|
||||
|
||||
ide_drive_t drive[4];
|
||||
static volatile bool interrupted;
|
||||
|
||||
#define IRQ_FIXED_DISK 14
|
||||
|
||||
extern "C" void ide_ISR();
|
||||
|
||||
asm(
|
||||
".globl ide_ISR \n"
|
||||
"ide_ISR: \n"
|
||||
" pusha\n"
|
||||
" pushw %ds\n"
|
||||
" pushw %es\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" popw %ds\n"
|
||||
" popw %es\n"
|
||||
" call handle_interrupt\n"
|
||||
" popw %es\n"
|
||||
" popw %ds\n"
|
||||
" popa\n"
|
||||
" iret\n"
|
||||
);
|
||||
|
||||
static void enableIRQ()
|
||||
{
|
||||
PIC::enable(IRQ_FIXED_DISK);
|
||||
}
|
||||
|
||||
static void disableIRQ()
|
||||
{
|
||||
PIC::disable(IRQ_FIXED_DISK);
|
||||
}
|
||||
|
||||
static bool waitForInterrupt()
|
||||
{
|
||||
#ifdef DISK_DEBUG
|
||||
kprintf("disk: waiting for interrupt...\n");
|
||||
#endif
|
||||
// FIXME: Add timeout.
|
||||
while (!interrupted) {
|
||||
yield();
|
||||
}
|
||||
#ifdef DISK_DEBUG
|
||||
kprintf("disk: got interrupt!\n");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
IRQHandlerScope scope(IRQ_FIXED_DISK);
|
||||
#ifdef DISK_DEBUG
|
||||
BYTE status = IO::in8(0x1f7);
|
||||
kprintf("disk:interrupt: DRQ=%u BUSY=%u DRDY=%u\n", (status & DRQ) != 0, (status & BUSY) != 0, (status & DRDY) != 0);
|
||||
#endif
|
||||
interrupted = true;
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
disableIRQ();
|
||||
interrupted = false;
|
||||
registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_FIXED_DISK, ide_ISR);
|
||||
|
||||
while (IO::in8(IDE0_STATUS) & BUSY);
|
||||
|
||||
IO::out8(0x1F6, 0xA0); // 0xB0 for 2nd device
|
||||
IO::out8(IDE0_COMMAND, IDENTIFY_DRIVE);
|
||||
|
||||
enableIRQ();
|
||||
waitForInterrupt();
|
||||
|
||||
RefPtr<DataBuffer> wbuf = DataBuffer::createUninitialized(512);
|
||||
BYTE* byteBuffer = new BYTE[512];
|
||||
BYTE* b = byteBuffer;
|
||||
WORD* wbufbase = (WORD*)wbuf->data();
|
||||
WORD* w = (WORD*)wbuf->data();
|
||||
|
||||
for (DWORD i = 0; i < 256; ++i) {
|
||||
WORD data = IO::in16(IDE0_DATA);
|
||||
*(w++) = data;
|
||||
*(b++) = MSB(data);
|
||||
*(b++) = LSB(data);
|
||||
}
|
||||
|
||||
// "Unpad" the device name string.
|
||||
for (DWORD i = 93; i > 54 && byteBuffer[i] == ' '; --i)
|
||||
byteBuffer[i] = 0;
|
||||
|
||||
drive[0].cylinders = wbufbase[1];
|
||||
drive[0].heads = wbufbase[3];
|
||||
drive[0].sectors_per_track = wbufbase[6];
|
||||
|
||||
kprintf(
|
||||
"ide0: Master=\"%s\", C/H/Spt=%u/%u/%u\n",
|
||||
byteBuffer + 54,
|
||||
drive[0].cylinders,
|
||||
drive[0].heads,
|
||||
drive[0].sectors_per_track
|
||||
);
|
||||
|
||||
delete byteBuffer;
|
||||
}
|
||||
|
||||
struct CHS {
|
||||
DWORD cylinder;
|
||||
WORD head;
|
||||
WORD sector;
|
||||
};
|
||||
|
||||
static CHS lba2chs(BYTE drive_index, DWORD lba)
|
||||
{
|
||||
ide_drive_t& d = drive[drive_index];
|
||||
CHS chs;
|
||||
chs.cylinder = lba / (d.sectors_per_track * d.heads);
|
||||
chs.head = (lba / d.sectors_per_track) % d.heads;
|
||||
chs.sector = (lba % d.sectors_per_track) + 1;
|
||||
return chs;
|
||||
}
|
||||
|
||||
bool readSectors(DWORD startSector, WORD count, BYTE* outbuf)
|
||||
{
|
||||
#ifdef DISK_DEBUG
|
||||
kprintf("%s: Disk::readSectors request (%u sector(s) @ %u)\n",
|
||||
current->name().characters(),
|
||||
count,
|
||||
startSector);
|
||||
#endif
|
||||
Task::checkSanity("Disk::readSectors");
|
||||
|
||||
disableIRQ();
|
||||
|
||||
CHS chs = lba2chs(IDE0_DISK0, startSector);
|
||||
|
||||
while (IO::in8(IDE0_STATUS) & BUSY);
|
||||
|
||||
#ifdef DISK_DEBUG
|
||||
kprintf("ide0: Reading %u sector(s) @ LBA %u (%u/%u/%u)\n", count, startSector, chs.cylinder, chs.head, chs.sector);
|
||||
#endif
|
||||
|
||||
IO::out8(0x1F2, count == 256 ? 0 : LSB(count));
|
||||
IO::out8(0x1F3, chs.sector);
|
||||
IO::out8(0x1F4, LSB(chs.cylinder));
|
||||
IO::out8(0x1F5, MSB(chs.cylinder));
|
||||
|
||||
IO::out8(0x1F6, 0xA0 | chs.head); /* 0xB0 for 2nd device */
|
||||
|
||||
IO::out8(0x3F6, 0x08);
|
||||
while (!(IO::in8(IDE0_STATUS) & DRDY));
|
||||
|
||||
IO::out8(IDE0_COMMAND, READ_SECTORS);
|
||||
interrupted = false;
|
||||
enableIRQ();
|
||||
waitForInterrupt();
|
||||
|
||||
BYTE status = IO::in8(0x1f7);
|
||||
if (status & DRQ) {
|
||||
#ifdef DISK_DEBUG
|
||||
kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf);
|
||||
#endif
|
||||
for (DWORD i = 0; i < (count * 512); i += 2) {
|
||||
WORD w = IO::in16(IDE0_DATA);
|
||||
outbuf[i] = LSB(w);
|
||||
outbuf[i+1] = MSB(w);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" void handle_interrupt()
|
||||
{
|
||||
Disk::interrupt();
|
||||
}
|
35
Kernel/Disk.h
Normal file
35
Kernel/Disk.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "DataBuffer.h"
|
||||
|
||||
#define IDE0_DATA 0x1F0
|
||||
#define IDE0_STATUS 0x1F7
|
||||
#define IDE0_COMMAND 0x1F7
|
||||
#define BUSY 0x80
|
||||
#define DRDY 0x40
|
||||
#define DRQ 0x08
|
||||
#define IDENTIFY_DRIVE 0xEC
|
||||
#define READ_SECTORS 0x21
|
||||
|
||||
#define IDE0_DISK0 0
|
||||
#define IDE0_DISK1 1
|
||||
#define IDE1_DISK0 2
|
||||
#define IDE1_DISK1 3
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD cylinders;
|
||||
WORD heads;
|
||||
WORD sectors_per_track;
|
||||
} ide_drive_t;
|
||||
|
||||
extern void ide_init();
|
||||
extern ide_drive_t drive[4];
|
||||
|
||||
namespace Disk {
|
||||
|
||||
void initialize();
|
||||
bool readSectors(DWORD sectorIndex, WORD count, BYTE* buffer);
|
||||
|
||||
}
|
166
Kernel/DoublyLinkedList.h
Normal file
166
Kernel/DoublyLinkedList.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "types.h"
|
||||
|
||||
template<typename T> class DoublyLinkedListNode {
|
||||
public:
|
||||
DoublyLinkedListNode();
|
||||
|
||||
void setPrev(T*);
|
||||
void setNext(T*);
|
||||
|
||||
T* prev() const;
|
||||
T* next() const;
|
||||
};
|
||||
|
||||
template<typename T> inline DoublyLinkedListNode<T>::DoublyLinkedListNode()
|
||||
{
|
||||
setPrev(0);
|
||||
setNext(0);
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedListNode<T>::setPrev(T* prev)
|
||||
{
|
||||
static_cast<T*>(this)->m_prev = prev;
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedListNode<T>::setNext(T* next)
|
||||
{
|
||||
static_cast<T*>(this)->m_next = next;
|
||||
}
|
||||
|
||||
template<typename T> inline T* DoublyLinkedListNode<T>::prev() const
|
||||
{
|
||||
return static_cast<const T*>(this)->m_prev;
|
||||
}
|
||||
|
||||
template<typename T> inline T* DoublyLinkedListNode<T>::next() const
|
||||
{
|
||||
return static_cast<const T*>(this)->m_next;
|
||||
}
|
||||
|
||||
template<typename T> class DoublyLinkedList {
|
||||
public:
|
||||
DoublyLinkedList() { }
|
||||
|
||||
bool isEmpty() const { return !m_head; }
|
||||
size_t size() const;
|
||||
void clear();
|
||||
|
||||
T* head() const { return m_head; }
|
||||
T* removeHead();
|
||||
|
||||
T* tail() const { return m_tail; }
|
||||
|
||||
void prepend(T*);
|
||||
void append(T*);
|
||||
void remove(T*);
|
||||
void append(DoublyLinkedList<T>&);
|
||||
|
||||
private:
|
||||
T* m_head { nullptr };
|
||||
T* m_tail { nullptr };
|
||||
};
|
||||
|
||||
template<typename T> inline size_t DoublyLinkedList<T>::size() const
|
||||
{
|
||||
size_t size = 0;
|
||||
for (T* node = m_head; node; node = node->next())
|
||||
++size;
|
||||
return size;
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedList<T>::clear()
|
||||
{
|
||||
m_head = 0;
|
||||
m_tail = 0;
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedList<T>::prepend(T* node)
|
||||
{
|
||||
if (!m_head) {
|
||||
ASSERT(!m_tail);
|
||||
m_head = node;
|
||||
m_tail = node;
|
||||
node->setPrev(0);
|
||||
node->setNext(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(m_tail);
|
||||
m_head->setPrev(node);
|
||||
node->setNext(m_head);
|
||||
node->setPrev(0);
|
||||
m_head = node;
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedList<T>::append(T* node)
|
||||
{
|
||||
if (!m_tail) {
|
||||
ASSERT(!m_head);
|
||||
m_head = node;
|
||||
m_tail = node;
|
||||
node->setPrev(0);
|
||||
node->setNext(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(m_head);
|
||||
m_tail->setNext(node);
|
||||
node->setPrev(m_tail);
|
||||
node->setNext(0);
|
||||
m_tail = node;
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedList<T>::remove(T* node)
|
||||
{
|
||||
if (node->prev()) {
|
||||
ASSERT(node != m_head);
|
||||
node->prev()->setNext(node->next());
|
||||
} else {
|
||||
ASSERT(node == m_head);
|
||||
m_head = node->next();
|
||||
}
|
||||
|
||||
if (node->next()) {
|
||||
ASSERT(node != m_tail);
|
||||
node->next()->setPrev(node->prev());
|
||||
} else {
|
||||
ASSERT(node == m_tail);
|
||||
m_tail = node->prev();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> inline T* DoublyLinkedList<T>::removeHead()
|
||||
{
|
||||
T* node = head();
|
||||
if (node)
|
||||
remove(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
template<typename T> inline void DoublyLinkedList<T>::append(DoublyLinkedList<T>& other)
|
||||
{
|
||||
if (!other.head())
|
||||
return;
|
||||
|
||||
if (!head()) {
|
||||
m_head = other.head();
|
||||
m_tail = other.tail();
|
||||
other.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(tail());
|
||||
ASSERT(other.head());
|
||||
T* otherHead = other.head();
|
||||
T* otherTail = other.tail();
|
||||
other.clear();
|
||||
|
||||
ASSERT(!m_tail->next());
|
||||
m_tail->setNext(otherHead);
|
||||
ASSERT(!otherHead->prev());
|
||||
otherHead->setPrev(m_tail);
|
||||
m_tail = otherTail;
|
||||
}
|
74
Kernel/Ext2FileSystem.h
Normal file
74
Kernel/Ext2FileSystem.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#include "ext2fs.h"
|
||||
#include "OwnPtr.h"
|
||||
#include "DataBuffer.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const size_t bytesPerSector = 512;
|
||||
|
||||
class Ext2VirtualNode;
|
||||
|
||||
class Ext2FileSystem {
|
||||
public:
|
||||
Ext2FileSystem() { }
|
||||
~Ext2FileSystem();
|
||||
|
||||
void initialize();
|
||||
RefPtr<DataBuffer> loadFile(ext2_dir_entry*);
|
||||
|
||||
ext2_inode* findInode(DWORD index);
|
||||
ext2_inode* findPath(const String& path, DWORD& inodeIndex);
|
||||
|
||||
private:
|
||||
friend class Ext2VirtualNode;
|
||||
|
||||
void readSuperBlock();
|
||||
void readBlockGroup(DWORD);
|
||||
void readInodeTable(DWORD);
|
||||
void dumpDirectory(ext2_inode&);
|
||||
void dumpFile(ext2_inode&);
|
||||
|
||||
template<typename F> void forEachBlockIn(ext2_inode&, F func);
|
||||
template<typename F> void traverseDirectory(ext2_dir_entry&, DWORD blockCount, F);
|
||||
template<typename F> void traverseDirectory(ext2_inode&, F);
|
||||
RefPtr<DataBuffer> readFile(ext2_inode&);
|
||||
|
||||
RefPtr<DataBuffer> readBlocks(DWORD blockIndex, BYTE count);
|
||||
void readDiskSector(DWORD sectorIndex, BYTE* buffer);
|
||||
|
||||
size_t blockSize() const { return 1024 << m_superBlock->s_log_frag_size; }
|
||||
size_t sectorsPerBlock() const { return blockSize() / bytesPerSector; }
|
||||
DWORD blockGroupForInode(DWORD inode) const;
|
||||
DWORD toInodeTableIndex(DWORD inode) const;
|
||||
|
||||
ext2_super_block& superBlock() { ASSERT(m_superBlock); return *m_superBlock; }
|
||||
|
||||
OwnPtr<ext2_super_block> m_superBlock;
|
||||
ext2_inode* m_root { nullptr }; // raw pointer into one of the m_inodeTables
|
||||
|
||||
size_t m_blockGroupCount { 0 };
|
||||
ext2_group_descriptor* m_groupTable { nullptr };
|
||||
ext2_inode** m_inodeTables { nullptr };
|
||||
};
|
||||
|
||||
class Ext2VirtualNode final : public FileSystem::VirtualNode {
|
||||
public:
|
||||
static RefPtr<Ext2VirtualNode> create(DWORD index, String&& path, Ext2FileSystem&, DWORD inodeNumber);
|
||||
|
||||
virtual ~Ext2VirtualNode();
|
||||
|
||||
virtual size_t size() const override { return m_inode.i_size; }
|
||||
virtual uid_t uid() const override { return m_inode.i_uid; }
|
||||
virtual gid_t gid() const override { return m_inode.i_gid; }
|
||||
virtual size_t mode() const override { return m_inode.i_mode; }
|
||||
|
||||
virtual size_t read(BYTE* outbuf, size_t start, size_t maxLength) override;
|
||||
|
||||
private:
|
||||
Ext2VirtualNode(DWORD index, String&& path, Ext2FileSystem&, ext2_inode&, DWORD inodeNumber);
|
||||
|
||||
Ext2FileSystem& m_fileSystem;
|
||||
ext2_inode& m_inode;
|
||||
DWORD m_inodeNumber { 0 };
|
||||
};
|
35
Kernel/FileSystem.h
Normal file
35
Kernel/FileSystem.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "RefCounted.h"
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
void initialize();
|
||||
|
||||
class VirtualNode : public RefCounted<VirtualNode> {
|
||||
public:
|
||||
DWORD saneValue = 0x850209;
|
||||
virtual ~VirtualNode();
|
||||
|
||||
DWORD index() const { return m_index; }
|
||||
const String& path() const { return m_path; }
|
||||
|
||||
virtual size_t size() const = 0;
|
||||
virtual uid_t uid() const = 0;
|
||||
virtual gid_t gid() const = 0;
|
||||
virtual size_t mode() const = 0;
|
||||
|
||||
virtual size_t read(BYTE* outbuf, size_t start, size_t maxLength) = 0;
|
||||
|
||||
protected:
|
||||
VirtualNode(DWORD index, String&& path);
|
||||
|
||||
private:
|
||||
DWORD m_index { 0 };
|
||||
String m_path;
|
||||
};
|
||||
|
||||
RefPtr<VirtualNode> createVirtualNode(String&& path);
|
||||
|
||||
}
|
41
Kernel/IO.cpp
Normal file
41
Kernel/IO.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "IO.h"
|
||||
|
||||
namespace IO {
|
||||
|
||||
BYTE in8(WORD port)
|
||||
{
|
||||
BYTE value;
|
||||
asm("inb %%dx, %%al":"=a"(value):"d"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
WORD in16(WORD port)
|
||||
{
|
||||
WORD value;
|
||||
asm("inw %%dx, %%ax":"=a"(value):"d"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
DWORD in32(DWORD port)
|
||||
{
|
||||
DWORD value;
|
||||
asm("inl %%dx, %%eax":"=a"(value):"d"(port));
|
||||
return value;
|
||||
}
|
||||
|
||||
void out8(WORD port, BYTE value)
|
||||
{
|
||||
asm("outb %%al, %%dx"::"d"(port), "a"(value));
|
||||
}
|
||||
|
||||
void out16(WORD port, WORD value)
|
||||
{
|
||||
asm("outw %%ax, %%dx"::"d"(port), "a"(value));
|
||||
}
|
||||
|
||||
void out32(WORD port, WORD value)
|
||||
{
|
||||
asm("outl %%eax, %%dx"::"d"(port), "a"(value));
|
||||
}
|
||||
|
||||
}
|
15
Kernel/IO.h
Normal file
15
Kernel/IO.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace IO {
|
||||
|
||||
BYTE in8(WORD port);
|
||||
WORD in16(WORD port);
|
||||
DWORD in32(WORD port);
|
||||
|
||||
void out8(WORD port, BYTE data);
|
||||
void out16(WORD port, WORD data);
|
||||
void out32(WORD port, DWORD data);
|
||||
|
||||
}
|
104
Kernel/IPC.cpp
Normal file
104
Kernel/IPC.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "IPC.h"
|
||||
#include "Task.h"
|
||||
#include "i386.h"
|
||||
#include "StdLib.h"
|
||||
#include "VGA.h"
|
||||
#include "system.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
Message receive(Handle src)
|
||||
{
|
||||
for (;;) {
|
||||
current->ipc.src = src;
|
||||
block(Task::BlockedReceive);
|
||||
if (src == Handle::Any && current->ipc.notifies) {
|
||||
for (BYTE i = 0; i < 32; ++i) {
|
||||
if (current->ipc.notifies & (1 << i)) {
|
||||
// FIXME: Source PID is `i' here. Do something with it?
|
||||
current->ipc.notifies &= ~(1 << i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Message(MSG_NOTIFY);
|
||||
}
|
||||
|
||||
if (src == Handle::Any || src == current->ipc.msg.sender()) {
|
||||
return move(current->ipc.msg);
|
||||
}
|
||||
|
||||
// Why are we here?
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void send(Handle dst, Message&& msg)
|
||||
{
|
||||
Task* task;
|
||||
|
||||
// TODO: Block waiting for `dst' to spawn.
|
||||
for (;;) {
|
||||
task = Task::fromIPCHandle(dst);
|
||||
if (task)
|
||||
break;
|
||||
yield();
|
||||
}
|
||||
|
||||
// I'll fill this in myself thankyouverymuch.
|
||||
msg.setSender(current->handle());
|
||||
|
||||
// Block until `dst' is ready to receive a message.
|
||||
current->ipc.dst = dst;
|
||||
block(Task::BlockedSend);
|
||||
|
||||
ASSERT(msg.isValid());
|
||||
task->ipc.msg = move(msg);
|
||||
}
|
||||
|
||||
void notify(Handle dst)
|
||||
{
|
||||
Task* task = Task::fromIPCHandle(dst);
|
||||
|
||||
if (!task) {
|
||||
// Can't really block here since we might be coming from
|
||||
// an interrupt handler and that'd be devastating...
|
||||
// XXX: Need to figure that one out.
|
||||
kprintf("notify(): no such task %u\n", dst.data());
|
||||
return;
|
||||
}
|
||||
|
||||
if (current->pid() >= 32) {
|
||||
kprintf( "notify(): PID must be < 32\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
task->ipc.notifies |= 1 << current->pid();
|
||||
}
|
||||
|
||||
Message::Message(Message&& other)
|
||||
: m_data(move(other.m_data))
|
||||
, m_type(other.m_type)
|
||||
, m_sender(other.m_sender)
|
||||
, m_isValid(other.m_isValid)
|
||||
{
|
||||
other.m_type = 0;
|
||||
other.m_sender = Handle();
|
||||
other.m_isValid = false;
|
||||
}
|
||||
|
||||
Message& Message::operator=(Message&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_data = move(other.m_data);
|
||||
m_type = other.m_type;
|
||||
m_sender = other.m_sender;
|
||||
m_isValid = other.m_isValid;
|
||||
other.m_type = 0;
|
||||
other.m_sender = Handle();
|
||||
other.m_isValid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
}
|
81
Kernel/IPC.h
Normal file
81
Kernel/IPC.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "DataBuffer.h"
|
||||
#include "RefPtr.h"
|
||||
|
||||
/* IPC message types. There will be moar. */
|
||||
#define MSG_INTERRUPT 0x00000001
|
||||
#define MSG_KILL 0x00000002
|
||||
#define MSG_NOTIFY 0x00000003
|
||||
|
||||
#define DEV_READ 0x00000004
|
||||
|
||||
#define FS_OPEN 0x00000100
|
||||
#define FS_CLOSE 0x00000101
|
||||
#define FS_READ 0x00000102
|
||||
|
||||
#define SYS_KILL 0x00000666
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class Handle {
|
||||
public:
|
||||
// If Handle::Any is passed as the `src' parameter of receive(),
|
||||
// any process can send us a message.
|
||||
enum AnyHandle { Any };
|
||||
Handle(AnyHandle) : m_data(0xffffffff) { }
|
||||
|
||||
enum KernelTask {
|
||||
PanelTask = 4001,
|
||||
DiskTask = 4002,
|
||||
FileSystemTask = 4003,
|
||||
MotdTask = 4004,
|
||||
UserTask = 4005,
|
||||
};
|
||||
Handle(KernelTask task) : m_data((DWORD)task) { }
|
||||
|
||||
Handle() { }
|
||||
explicit Handle(DWORD data) : m_data(data) { }
|
||||
|
||||
DWORD data() const { return m_data; }
|
||||
bool operator==(const Handle& o) const { return m_data == o.m_data; }
|
||||
bool operator!=(const Handle& o) const { return m_data != o.m_data; }
|
||||
|
||||
private:
|
||||
DWORD m_data { 0 };
|
||||
};
|
||||
|
||||
class Message {
|
||||
public:
|
||||
Message() { }
|
||||
explicit Message(DWORD type) : m_type(type), m_isValid(true) { }
|
||||
Message(DWORD type, RefPtr<DataBuffer>&& d) : m_data(move(d)), m_type(type), m_isValid(true) { }
|
||||
Message(Message&&);
|
||||
Message& operator=(Message&&);
|
||||
|
||||
size_t length() const { return m_data ? m_data->length() : 0; }
|
||||
const BYTE* data() const { return m_data ? m_data->data() : nullptr; }
|
||||
BYTE* data() { return m_data ? m_data->data() : nullptr; }
|
||||
|
||||
bool isValid() const { return m_isValid; }
|
||||
|
||||
DWORD type() const { return m_type; }
|
||||
Handle sender() const { return m_sender; }
|
||||
|
||||
void setType(DWORD t) { m_type = t; }
|
||||
void setSender(Handle s) { m_sender = s; }
|
||||
|
||||
private:
|
||||
RefPtr<DataBuffer> m_data;
|
||||
DWORD m_type { 0 };
|
||||
Handle m_sender;
|
||||
bool m_isValid { false };
|
||||
};
|
||||
|
||||
Message receive(Handle);
|
||||
void send(Handle, Message&&);
|
||||
void notify(Handle);
|
||||
|
||||
}
|
||||
|
135
Kernel/Keyboard.cpp
Normal file
135
Kernel/Keyboard.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "types.h"
|
||||
#include "i386.h"
|
||||
#include "IO.h"
|
||||
#include "IPC.h"
|
||||
#include "Task.h"
|
||||
#include "VGA.h"
|
||||
#include "PIC.h"
|
||||
#include "Keyboard.h"
|
||||
|
||||
#define IRQ_KEYBOARD 1
|
||||
|
||||
#define I8042_BUFFER 0x60
|
||||
#define I8042_STATUS 0x64
|
||||
|
||||
#define SET_LEDS 0xED
|
||||
#define DATA_AVAILABLE 0x01
|
||||
#define I8042_ACK 0xFA
|
||||
|
||||
extern "C" void handleKeyboardInterrupt();
|
||||
extern "C" void keyboard_ISR();
|
||||
|
||||
static BYTE s_ledState;
|
||||
|
||||
asm(
|
||||
".globl keyboard_ISR \n"
|
||||
"keyboard_ISR: \n"
|
||||
" pusha\n"
|
||||
" pushw %ds\n"
|
||||
" pushw %es\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" popw %ds\n"
|
||||
" popw %es\n"
|
||||
" call handleKeyboardInterrupt\n"
|
||||
" popw %es\n"
|
||||
" popw %ds\n"
|
||||
" popa\n"
|
||||
" iret\n"
|
||||
);
|
||||
|
||||
void handleKeyboardInterrupt()
|
||||
{
|
||||
IRQHandlerScope scope(IRQ_KEYBOARD);
|
||||
Keyboard::handleInterrupt();
|
||||
}
|
||||
|
||||
namespace Keyboard {
|
||||
|
||||
#define MOD_ALT 1
|
||||
#define MOD_CTRL 2
|
||||
#define MOD_SHIFT 4
|
||||
|
||||
static char map[0x100] =
|
||||
{
|
||||
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 0,
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0, 0,
|
||||
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\',
|
||||
'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
|
||||
};
|
||||
|
||||
static char shift_map[0x100] =
|
||||
{
|
||||
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 0,
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0, 0,
|
||||
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|',
|
||||
'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
|
||||
};
|
||||
|
||||
static BYTE s_modifiers;
|
||||
|
||||
void handleInterrupt()
|
||||
{
|
||||
while (IO::in8(0x64) & 1) {
|
||||
BYTE ch = IO::in8(0x60);
|
||||
switch (ch) {
|
||||
case 0x38: s_modifiers |= MOD_ALT; break;
|
||||
case 0xB8: s_modifiers &= ~MOD_ALT; break;
|
||||
case 0x1D: s_modifiers |= MOD_CTRL; break;
|
||||
case 0x9D: s_modifiers &= ~MOD_CTRL; break;
|
||||
case 0x2A: s_modifiers |= MOD_SHIFT; break;
|
||||
case 0xAA: s_modifiers &= ~MOD_SHIFT; break;
|
||||
case 0x1C: /* enter */ kprintf("\n"); break;
|
||||
case 0xFA: /* i8042 ack */ break;
|
||||
default:
|
||||
if (ch & 0x80) {
|
||||
// key has been depressed
|
||||
break;
|
||||
}
|
||||
if (!s_modifiers)
|
||||
kprintf("%c", map[ch]);
|
||||
else if (s_modifiers & MOD_SHIFT)
|
||||
kprintf("%c", shift_map[ch]);
|
||||
else if (s_modifiers & MOD_CTRL)
|
||||
kprintf("^%c", shift_map[ch]);
|
||||
}
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
s_modifiers = 0;
|
||||
s_ledState = 0;
|
||||
|
||||
// Empty the buffer of any pending data.
|
||||
// I don't care what you've been pressing until now!
|
||||
while (IO::in8(I8042_STATUS ) & DATA_AVAILABLE)
|
||||
IO::in8(I8042_BUFFER);
|
||||
|
||||
registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_KEYBOARD, keyboard_ISR);
|
||||
|
||||
PIC::enable(IRQ_KEYBOARD);
|
||||
}
|
||||
|
||||
void setLED(LED led)
|
||||
{
|
||||
s_ledState |= (BYTE)led & 7;
|
||||
|
||||
while (IO::in8(I8042_STATUS) & DATA_AVAILABLE);
|
||||
IO::out8(I8042_BUFFER, SET_LEDS);
|
||||
while (IO::in8(I8042_BUFFER) != I8042_ACK);
|
||||
IO::out8(I8042_BUFFER, s_ledState);
|
||||
}
|
||||
|
||||
void unsetLED(LED led)
|
||||
{
|
||||
s_ledState &= ~((BYTE)led & 7);
|
||||
|
||||
while (IO::in8(I8042_STATUS) & DATA_AVAILABLE);
|
||||
IO::out8(I8042_BUFFER, SET_LEDS);
|
||||
while (IO::in8(I8042_BUFFER) != I8042_ACK);
|
||||
IO::out8(I8042_BUFFER, s_ledState);
|
||||
}
|
||||
|
||||
}
|
16
Kernel/Keyboard.h
Normal file
16
Kernel/Keyboard.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
namespace Keyboard {
|
||||
|
||||
enum class LED {
|
||||
ScrollLock = 1 << 0,
|
||||
NumLock = 1 << 1,
|
||||
CapsLock = 1 << 2,
|
||||
};
|
||||
|
||||
void initialize();
|
||||
void setLED(LED);
|
||||
void unsetLED(LED);
|
||||
void handleInterrupt();
|
||||
|
||||
}
|
58
Kernel/Makefile
Normal file
58
Kernel/Makefile
Normal file
|
@ -0,0 +1,58 @@
|
|||
OBJS = \
|
||||
_start.o \
|
||||
init.o \
|
||||
VGA.o \
|
||||
kmalloc.o \
|
||||
StdLib.o \
|
||||
i386.o \
|
||||
Task.o \
|
||||
i8253.o \
|
||||
Keyboard.o \
|
||||
IPC.o \
|
||||
CMOS.o \
|
||||
IO.o \
|
||||
PIC.o \
|
||||
Syscall.o \
|
||||
DataBuffer.o \
|
||||
String.o \
|
||||
panel.o \
|
||||
Disk.o \
|
||||
fs.o \
|
||||
Userspace.o
|
||||
NASM = nasm
|
||||
KERNEL = kernel
|
||||
BOOTLOADER = Boot/boot.bin
|
||||
IMAGE = .floppy-image
|
||||
ARCH_FLAGS =
|
||||
STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib
|
||||
KERNEL_FLAGS = -ffreestanding -fno-stack-protector -fno-ident
|
||||
WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings
|
||||
FLAVOR_FLAGS = -fomit-frame-pointer -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -fmerge-all-constants -fno-unroll-loops -falign-functions=1 -falign-jumps=1 -falign-loops=1
|
||||
#FLAVOR_FLAGS = -fomit-frame-pointer -mregparm=3 -march=i386 -m32 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections
|
||||
OPTIMIZATION_FLAGS = -Os -fno-asynchronous-unwind-tables
|
||||
INCLUDE_FLAGS = -Iinclude/ -I.
|
||||
CXXFLAGS = $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(KERNEL_FLAGS) $(FLAVOR_FLAGS) $(ARCH_FLAGS) $(STANDARD_FLAGS) $(INCLUDE_FLAGS)
|
||||
#CXX = /usr/local/gcc-4.8.1-for-linux64/bin/x86_64-pc-linux-g++
|
||||
#LD = /usr/local/gcc-4.8.1-for-linux64/bin/x86_64-pc-linux-ld
|
||||
CXX = g++
|
||||
LD = ld
|
||||
LDFLAGS = -T linker.ld --strip-debug -melf_i386 --gc-sections --build-id=none -z norelro -z now
|
||||
|
||||
|
||||
all: $(KERNEL) $(IMAGE)
|
||||
|
||||
$(KERNEL): $(OBJS)
|
||||
@echo "LD $@"; $(LD) $(LDFLAGS) -o $@ -Ttext 0x10000 $(OBJS)
|
||||
|
||||
$(IMAGE): $(KERNEL) $(BOOTLOADER)
|
||||
@echo "CREATE $@"; cat $(BOOTLOADER) $(KERNEL) > $(IMAGE)
|
||||
|
||||
$(BOOTLOADER): Boot/boot.asm
|
||||
@echo "NASM $<"; $(NASM) -f bin -o $@ $<
|
||||
|
||||
.cpp.o:
|
||||
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
@echo "CLEAN"; rm -f $(KERNEL) $(OBJS) $(BOOTLOADER) $(IMAGE)
|
||||
|
78
Kernel/OwnPtr.h
Normal file
78
Kernel/OwnPtr.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
template<typename T>
|
||||
class OwnPtr {
|
||||
public:
|
||||
OwnPtr() { }
|
||||
explicit OwnPtr(T* ptr) : m_ptr(ptr) { }
|
||||
OwnPtr(OwnPtr&& other) : m_ptr(other.leakPtr()) { }
|
||||
template<typename U> OwnPtr(OwnPtr<U>&& other) : m_ptr(static_cast<T*>(other.leakPtr())) { }
|
||||
~OwnPtr() { clear(); }
|
||||
|
||||
OwnPtr& operator=(OwnPtr&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
delete m_ptr;
|
||||
m_ptr = other.leakPtr();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
OwnPtr& operator=(OwnPtr<U>&& other)
|
||||
{
|
||||
if (this != static_cast<void*>(&other)) {
|
||||
delete m_ptr;
|
||||
m_ptr = other.leakPtr();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OwnPtr& operator=(T* ptr)
|
||||
{
|
||||
if (m_ptr != ptr)
|
||||
delete m_ptr;
|
||||
m_ptr = ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
delete m_ptr;
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
bool operator!() const { return !m_ptr; }
|
||||
|
||||
typedef T* OwnPtr::*UnspecifiedBoolType;
|
||||
operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : nullptr; }
|
||||
|
||||
T* leakPtr()
|
||||
{
|
||||
T* leakedPtr = m_ptr;
|
||||
m_ptr = nullptr;
|
||||
return leakedPtr;
|
||||
}
|
||||
|
||||
T* ptr() { return m_ptr; }
|
||||
const T* ptr() const { return m_ptr; }
|
||||
|
||||
T* operator->() { return m_ptr; }
|
||||
const T* operator->() const { return m_ptr; }
|
||||
|
||||
T& operator*() { return *m_ptr; }
|
||||
const T& operator*() const { return *m_ptr; }
|
||||
|
||||
operator bool() { return !!m_ptr; }
|
||||
|
||||
private:
|
||||
T* m_ptr = nullptr;
|
||||
};
|
||||
|
||||
template<class T, class... Args> inline OwnPtr<T>
|
||||
make(Args&&... args)
|
||||
{
|
||||
return OwnPtr<T>(new T(forward<Args>(args)...));
|
||||
}
|
97
Kernel/PIC.cpp
Normal file
97
Kernel/PIC.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include "types.h"
|
||||
#include "i386.h"
|
||||
#include "IO.h"
|
||||
#include "VGA.h"
|
||||
#include "PIC.h"
|
||||
#include "Assertions.h"
|
||||
|
||||
// The slave 8259 is connected to the master's IRQ2 line.
|
||||
// This is really only to enhance clarity.
|
||||
#define SLAVE_INDEX 2
|
||||
|
||||
#define PIC0_CTL 0x20
|
||||
#define PIC0_CMD 0x21
|
||||
#define PIC1_CTL 0xA0
|
||||
#define PIC1_CMD 0xA1
|
||||
|
||||
#ifdef DEBUG_PIC
|
||||
static bool initialized;
|
||||
#endif
|
||||
|
||||
namespace PIC {
|
||||
|
||||
void disable(BYTE irq)
|
||||
{
|
||||
BYTE imr;
|
||||
if (irq & 8) {
|
||||
imr = IO::in8(PIC1_CMD);
|
||||
imr |= 1 << (irq - 8);
|
||||
IO::out8(PIC1_CMD, imr);
|
||||
} else {
|
||||
imr = IO::in8(PIC0_CMD);
|
||||
imr |= 1 << irq;
|
||||
IO::out8(PIC0_CMD, imr);
|
||||
}
|
||||
}
|
||||
|
||||
void enable(BYTE irq)
|
||||
{
|
||||
BYTE imr;
|
||||
if (irq & 8) {
|
||||
imr = IO::in8(PIC1_CMD);
|
||||
imr &= ~(1 << (irq - 8));
|
||||
IO::out8(PIC1_CMD, imr);
|
||||
} else {
|
||||
imr = IO::in8(PIC0_CMD);
|
||||
imr &= ~(1 << irq);
|
||||
IO::out8(PIC0_CMD, imr);
|
||||
}
|
||||
}
|
||||
|
||||
void eoi(BYTE irq)
|
||||
{
|
||||
if (irq & 8)
|
||||
IO::out8(PIC1_CTL, 0x20);
|
||||
else
|
||||
IO::out8(PIC0_CTL, 0x20);
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
#ifdef DEBUG_PIC
|
||||
ASSERT(!initialized);
|
||||
#endif
|
||||
|
||||
/* ICW1 (edge triggered mode, cascading controllers, expect ICW4) */
|
||||
IO::out8(PIC0_CTL, 0x11);
|
||||
IO::out8(PIC1_CTL, 0x11);
|
||||
|
||||
/* ICW2 (upper 5 bits specify ISR indices, lower 3 idunno) */
|
||||
IO::out8(PIC0_CMD, IRQ_VECTOR_BASE);
|
||||
IO::out8(PIC1_CMD, IRQ_VECTOR_BASE + 0x08);
|
||||
|
||||
/* ICW3 (configure master/slave relationship) */
|
||||
IO::out8(PIC0_CMD, 1 << SLAVE_INDEX);
|
||||
IO::out8(PIC1_CMD, SLAVE_INDEX);
|
||||
|
||||
/* ICW4 (set x86 mode) */
|
||||
IO::out8(PIC0_CMD, 0x01);
|
||||
IO::out8(PIC1_CMD, 0x01 );
|
||||
|
||||
// Mask -- enable all interrupts on both PICs.
|
||||
// Not really what I want here, but I'm unsure how to
|
||||
// selectively enable secondary PIC IRQs...
|
||||
IO::out8(PIC0_CMD, 0x00);
|
||||
IO::out8(PIC1_CMD, 0x00);
|
||||
|
||||
// HACK: Disable busmouse IRQ for now.
|
||||
disable(5);
|
||||
|
||||
kprintf("PIC(i8259): cascading mode, vectors 0x%b-0x%b\n", IRQ_VECTOR_BASE, IRQ_VECTOR_BASE + 0x08);
|
||||
|
||||
#ifdef DEBUG_PIC
|
||||
initialized = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
19
Kernel/PIC.h
Normal file
19
Kernel/PIC.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
namespace PIC {
|
||||
|
||||
void enable(BYTE number);
|
||||
void disable(BYTE number);
|
||||
void eoi(BYTE number);
|
||||
void initialize();
|
||||
|
||||
}
|
||||
|
||||
class IRQHandlerScope {
|
||||
public:
|
||||
explicit IRQHandlerScope(BYTE irq) : m_irq(irq) { }
|
||||
~IRQHandlerScope() { PIC::eoi(m_irq); }
|
||||
|
||||
private:
|
||||
BYTE m_irq { 0 };
|
||||
};
|
95
Kernel/Queue.h
Normal file
95
Kernel/Queue.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
template<typename T>
|
||||
class Queue {
|
||||
public:
|
||||
struct Node {
|
||||
explicit Node(T&& value)
|
||||
: value(move(value))
|
||||
{ }
|
||||
|
||||
Node* next { nullptr };
|
||||
Node* prev { nullptr };
|
||||
T value;
|
||||
};
|
||||
|
||||
Queue() { }
|
||||
~Queue()
|
||||
{
|
||||
while (!isEmpty())
|
||||
dequeue();
|
||||
}
|
||||
|
||||
bool isEmpty() const { return !m_head; }
|
||||
void enqueue(T&& item)
|
||||
{
|
||||
auto* newNode = new Node(move(item));
|
||||
if (!m_head) {
|
||||
m_head = newNode;
|
||||
m_tail = newNode;
|
||||
} else if (m_tail) {
|
||||
newNode->prev = m_tail;
|
||||
m_tail->next = newNode;
|
||||
m_tail = newNode;
|
||||
}
|
||||
dump("enqueue");
|
||||
}
|
||||
|
||||
T dequeue()
|
||||
{
|
||||
ASSERT(m_head);
|
||||
T value = move(m_head->value);
|
||||
auto* oldHead = m_head;
|
||||
if (oldHead->next) {
|
||||
oldHead->next->prev = nullptr;
|
||||
m_head = oldHead->next;
|
||||
} else {
|
||||
m_head = nullptr;
|
||||
}
|
||||
if (m_tail == oldHead)
|
||||
m_tail = nullptr;
|
||||
delete oldHead;
|
||||
dump("dequeue");
|
||||
//asm volatile("cli;hlt");
|
||||
return value;
|
||||
}
|
||||
|
||||
Node* head() { return m_head; }
|
||||
|
||||
T take(Node& node)
|
||||
{
|
||||
T value = move(node.value);
|
||||
if (node.prev) {
|
||||
node.prev->next = node.next;
|
||||
} else {
|
||||
m_head = node.next;
|
||||
}
|
||||
if (node.next) {
|
||||
node.next->prev = node.prev;
|
||||
} else {
|
||||
m_tail = node.prev;
|
||||
}
|
||||
delete &node;
|
||||
dump("take");
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
void dump(const char* op)
|
||||
{
|
||||
return;
|
||||
asm volatile("cli");
|
||||
ASSERT(m_head != (void*)0xaaaaaaaa);
|
||||
ASSERT(m_tail != (void*)0xaaaaaaaa);
|
||||
kprintf("Queue %p after %s: {m_head=%p, m_tail=%p}\n", this, op, m_head, m_tail);
|
||||
for (auto* node = m_head; node; node = node->next) {
|
||||
kprintf(" Queue::Node %p%s%s next=%p prev=%p\n", node, node == m_head ? " (head)" : "", node == m_tail ? " (tail)" : "", node->next, node->prev);
|
||||
}
|
||||
asm volatile("sti");
|
||||
}
|
||||
|
||||
Node* m_head { nullptr };
|
||||
Node* m_tail { nullptr };
|
||||
};
|
48
Kernel/RefCounted.h
Normal file
48
Kernel/RefCounted.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "VGA.h"
|
||||
|
||||
#define DEBUG_REFCOUNTED
|
||||
|
||||
class RefCountedBase {
|
||||
|
||||
protected:
|
||||
bool derefBase() const
|
||||
{
|
||||
return !--m_refCount;
|
||||
}
|
||||
mutable size_t m_refCount { 1 };
|
||||
#ifdef DEBUG_REFCOUNTED
|
||||
//mutable bool m_adopted { false };
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class RefCounted : public RefCountedBase {
|
||||
public:
|
||||
size_t refCount() const { return m_refCount; }
|
||||
|
||||
void ref() const
|
||||
{
|
||||
#ifdef DEBUG_REFCOUNTED
|
||||
ASSERT(m_refCount);
|
||||
//ASSERT(m_adopted);
|
||||
#endif
|
||||
++m_refCount;
|
||||
}
|
||||
|
||||
void deref() const
|
||||
{
|
||||
#ifdef DEBUG_REFCOUNTED
|
||||
ASSERT(m_refCount);
|
||||
//ASSERT(m_adopted);
|
||||
#endif
|
||||
if (derefBase())
|
||||
delete static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCounted() { }
|
||||
~RefCounted() { }
|
||||
};
|
100
Kernel/RefPtr.h
Normal file
100
Kernel/RefPtr.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define SANITIZE_REFPTR
|
||||
|
||||
template<typename T> class RefPtr;
|
||||
template<typename T> RefPtr<T> adoptRef(T*);
|
||||
|
||||
template<typename T>
|
||||
class RefPtr {
|
||||
public:
|
||||
RefPtr() { }
|
||||
RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(m_ptr); }
|
||||
|
||||
~RefPtr()
|
||||
{
|
||||
derefIfNotNull(m_ptr);
|
||||
#ifdef SANITIZE_REFPTR
|
||||
m_ptr = (T*)(0xeeeeeeee);
|
||||
#endif
|
||||
}
|
||||
|
||||
RefPtr(RefPtr&& other)
|
||||
: m_ptr(other.leakPtr())
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr& operator=(RefPtr&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_ptr = other.leakPtr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
RefPtr(RefPtr<U>&& other)
|
||||
: m_ptr(static_cast<T*>(other.leakPtr()))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
RefPtr& operator=(RefPtr<U>&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_ptr = static_cast<T*>(other.leakPtr());
|
||||
return *this;
|
||||
}
|
||||
|
||||
RefPtr(const RefPtr& other)
|
||||
: m_ptr(other.m_ptr)
|
||||
{
|
||||
refIfNotNull(m_ptr);
|
||||
}
|
||||
|
||||
RefPtr& operator=(const RefPtr& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_ptr = other.m_ptr;
|
||||
refIfNotNull(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* ptr() { return m_ptr; }
|
||||
const T* ptr() const { return m_ptr; }
|
||||
T* operator->() { return m_ptr; }
|
||||
const T* operator->() const { return m_ptr; }
|
||||
T& operator*() { return *m_ptr; }
|
||||
const T& operator*() const { return *m_ptr; }
|
||||
operator bool() const { return m_ptr; }
|
||||
|
||||
T* leakPtr()
|
||||
{
|
||||
T* ptr = m_ptr;
|
||||
m_ptr = nullptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename U> friend class RefPtr;
|
||||
friend RefPtr adoptRef<T>(T*);
|
||||
|
||||
enum AdoptTag { Adopt };
|
||||
RefPtr(AdoptTag, T* ptr) : m_ptr(ptr) { }
|
||||
|
||||
inline void refIfNotNull(T* ptr) { if (ptr) ptr->ref(); }
|
||||
inline void derefIfNotNull(T* ptr) { if (ptr) ptr->deref(); }
|
||||
|
||||
T* m_ptr { nullptr };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline RefPtr<T> adoptRef(T* ptr)
|
||||
{
|
||||
ASSERT(ptr->refCount() == 1);
|
||||
return RefPtr<T>(RefPtr<T>::Adopt, ptr);
|
||||
}
|
55
Kernel/StdLib.cpp
Normal file
55
Kernel/StdLib.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include "types.h"
|
||||
#include "Assertions.h"
|
||||
#include "kmalloc.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, DWORD n)
|
||||
{
|
||||
BYTE* bdest = (BYTE*)dest;
|
||||
const BYTE* bsrc = (const BYTE*)src;
|
||||
for (; n; --n)
|
||||
*(bdest++) = *(bsrc++);
|
||||
}
|
||||
|
||||
void strcpy(char* dest, const char *src)
|
||||
{
|
||||
while (*src)
|
||||
*(dest++) = *(src++);
|
||||
}
|
||||
|
||||
void* memset(void* dest, BYTE c, DWORD n)
|
||||
{
|
||||
BYTE *bdest = (BYTE *)dest;
|
||||
for (; n; --n)
|
||||
*(bdest++) = c;
|
||||
return dest;
|
||||
}
|
||||
|
||||
DWORD strlen(const char* str)
|
||||
{
|
||||
DWORD len = 0;
|
||||
while (*(str++))
|
||||
++len;
|
||||
return len;
|
||||
}
|
||||
|
||||
int strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
for (; *s1 == *s2; ++s1, ++s2) {
|
||||
if (*s1 == 0)
|
||||
return 0;
|
||||
}
|
||||
return *(const BYTE*)s1 < *(const BYTE*)s2 ? -1 : 1;
|
||||
}
|
||||
|
||||
char* strdup(const char *str)
|
||||
{
|
||||
DWORD len = strlen(str);
|
||||
char *s = (char*)kmalloc(len);
|
||||
memcpy(s, str, len);
|
||||
return s;
|
||||
}
|
||||
|
||||
extern "C" void __cxa_pure_virtual()
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
10
Kernel/StdLib.h
Normal file
10
Kernel/StdLib.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void memcpy(void *, const void *, DWORD);
|
||||
void strcpy(char *, const char *);
|
||||
int strcmp(char const *, const char *);
|
||||
DWORD strlen(const char *) PURE;
|
||||
void *memset(void *, BYTE, DWORD);
|
||||
char *strdup(const char *);
|
86
Kernel/String.cpp
Normal file
86
Kernel/String.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include "String.h"
|
||||
#include "StdLib.h"
|
||||
|
||||
String::String()
|
||||
{
|
||||
}
|
||||
|
||||
String::String(const char* characters)
|
||||
: m_data(DataBuffer::copy((const BYTE*)characters, strlen(characters) + 1))
|
||||
{
|
||||
}
|
||||
|
||||
String::String(const char* characters, size_t length)
|
||||
: m_data(DataBuffer::createUninitialized(length + 1))
|
||||
{
|
||||
memcpy(m_data->data(), characters, length);
|
||||
m_data->data()[length] = '\0';
|
||||
}
|
||||
|
||||
String::String(String&& other)
|
||||
: m_data(move(other.m_data))
|
||||
{
|
||||
}
|
||||
|
||||
String::String(const String& other)
|
||||
: m_data(other.m_data)
|
||||
{
|
||||
}
|
||||
|
||||
String& String::operator=(const String& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_data = other.m_data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(const String&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_data = move(other.m_data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
String::~String()
|
||||
{
|
||||
}
|
||||
|
||||
bool String::operator==(const String& other) const
|
||||
{
|
||||
if (length() != other.length())
|
||||
return false;
|
||||
return strcmp(characters(), other.characters()) == 0;
|
||||
}
|
||||
|
||||
String String::substring(size_t start, size_t length) const
|
||||
{
|
||||
ASSERT(start + length <= m_data->length());
|
||||
// FIXME: This needs some input bounds checking.
|
||||
auto buffer = DataBuffer::createUninitialized(length + 1);
|
||||
memcpy(buffer->data(), characters() + start, length);
|
||||
buffer->data()[length] = '\0';
|
||||
String s;
|
||||
s.m_data = move(buffer);
|
||||
return s;
|
||||
}
|
||||
|
||||
Vector<String> String::split(char separator) const
|
||||
{
|
||||
Vector<String> v;
|
||||
size_t substart = 0;
|
||||
for (size_t i = 0; i < length(); ++i) {
|
||||
char ch = characters()[i];
|
||||
if (ch == separator) {
|
||||
size_t sublen = i - substart;
|
||||
if (sublen != 0)
|
||||
v.append(substring(substart, sublen));
|
||||
substart = i + 1;
|
||||
}
|
||||
}
|
||||
size_t taillen = length() - 1 - substart;
|
||||
if (taillen != 0)
|
||||
v.append(substring(substart, taillen));
|
||||
return v;
|
||||
}
|
31
Kernel/String.h
Normal file
31
Kernel/String.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "DataBuffer.h"
|
||||
#include "Vector.h"
|
||||
|
||||
class String {
|
||||
public:
|
||||
String();
|
||||
String(const char* characters);
|
||||
String(const char* characters, size_t length);
|
||||
String(String&&);
|
||||
String(const String&);
|
||||
String& operator=(const String&);
|
||||
String& operator=(const String&&);
|
||||
~String();
|
||||
|
||||
bool isEmpty() const { return m_data ? m_data->isEmpty() : true; }
|
||||
size_t length() const { return m_data ? m_data->length() : 0; }
|
||||
|
||||
char operator[](size_t i) const { return (char)m_data->data()[i]; }
|
||||
|
||||
const char* characters() const { return m_data ? (const char*)m_data->data() : nullptr; }
|
||||
|
||||
bool operator==(const String&) const;
|
||||
|
||||
Vector<String> split(char separator) const;
|
||||
String substring(size_t start, size_t length) const;
|
||||
|
||||
private:
|
||||
RefPtr<DataBuffer> m_data;
|
||||
};
|
144
Kernel/Syscall.cpp
Normal file
144
Kernel/Syscall.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
#include "i386.h"
|
||||
#include "VGA.h"
|
||||
#include "Task.h"
|
||||
#include "Syscall.h"
|
||||
|
||||
struct RegisterDump {
|
||||
WORD gs;
|
||||
WORD fs;
|
||||
WORD es;
|
||||
WORD ds;
|
||||
DWORD edi;
|
||||
DWORD esi;
|
||||
DWORD ebp;
|
||||
DWORD esp;
|
||||
DWORD ebx;
|
||||
DWORD edx;
|
||||
DWORD ecx;
|
||||
DWORD eax;
|
||||
DWORD eip;
|
||||
WORD cs;
|
||||
WORD __csPadding;
|
||||
DWORD eflags;
|
||||
} PACKED;
|
||||
|
||||
extern "C" void syscall_entry();
|
||||
extern "C" void syscall_ISR();
|
||||
extern volatile RegisterDump* syscallRegDump;
|
||||
|
||||
asm(
|
||||
".globl syscall_ISR \n"
|
||||
".globl syscallRegDump \n"
|
||||
"syscallRegDump: \n"
|
||||
".long 0\n"
|
||||
"syscall_ISR:\n"
|
||||
" pusha\n"
|
||||
" pushw %ds\n"
|
||||
" pushw %es\n"
|
||||
" pushw %fs\n"
|
||||
" pushw %gs\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" popw %ds\n"
|
||||
" popw %es\n"
|
||||
" popw %fs\n"
|
||||
" popw %gs\n"
|
||||
" mov %esp, syscallRegDump\n"
|
||||
" call syscall_entry\n"
|
||||
" popw %gs\n"
|
||||
" popw %fs\n"
|
||||
" popw %es\n"
|
||||
" popw %ds\n"
|
||||
" popa\n"
|
||||
" iret\n"
|
||||
);
|
||||
|
||||
namespace Syscall {
|
||||
|
||||
void initialize()
|
||||
{
|
||||
registerUserCallableInterruptHandler(0x80, syscall_ISR);
|
||||
|
||||
kprintf("syscall: int 0x80 handler installed\n");
|
||||
}
|
||||
|
||||
DWORD invoke(DWORD function)
|
||||
{
|
||||
DWORD result;
|
||||
asm("int $0x80":"=a"(result):"a"(function));
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD invoke(DWORD function, DWORD arg1)
|
||||
{
|
||||
DWORD result;
|
||||
asm("int $0x80":"=a"(result):"a"(function),"d"(arg1));
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD invoke(DWORD function, DWORD arg1, DWORD arg2)
|
||||
{
|
||||
DWORD result;
|
||||
asm("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2));
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3)
|
||||
{
|
||||
DWORD result;
|
||||
asm volatile("int $0x80":"=a"(result):"a"(function),"d"(arg1),"c"(arg2),"b"(arg3));
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD handle(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3)
|
||||
{
|
||||
switch (function) {
|
||||
case Syscall::Yield:
|
||||
yield();
|
||||
break;
|
||||
case 0x1235: // putch
|
||||
kprintf( "%c", arg1 & 0xFF );
|
||||
break;
|
||||
case Syscall::Sleep:
|
||||
kprintf("syscall: sleep(%d)\n", arg1);
|
||||
current->sys$sleep(arg1);
|
||||
break;
|
||||
case Syscall::PosixOpen:
|
||||
Task::checkSanity("syscall");
|
||||
kprintf("syscall: open('%s', %u)\n", arg1, arg2);
|
||||
return current->sys$open((const char*)arg1, (size_t)arg2);
|
||||
case Syscall::PosixClose:
|
||||
kprintf("syscall: close(%d)\n", arg1);
|
||||
return current->sys$close((int)arg1);
|
||||
case Syscall::PosixRead:
|
||||
kprintf("syscall: read(%d, %p, %u)\n", arg1, arg2, arg3);
|
||||
return current->sys$read((int)arg1, (void*)arg2, (size_t)arg3);
|
||||
case Syscall::PosixSeek:
|
||||
// FIXME: This has the wrong signature, should be like lseek()
|
||||
kprintf("syscall: seek(%d, %p, %u)\n", arg1, arg2, arg3);
|
||||
return current->sys$read((int)arg1, (void*)arg2, (size_t)arg3);
|
||||
case Syscall::PosixKill:
|
||||
kprintf("syscall: kill(%d, %d)\n", arg1, arg2);
|
||||
return current->sys$kill((pid_t)arg1, (int)arg2);
|
||||
case Syscall::PosixGetuid:
|
||||
return current->sys$getuid();
|
||||
default:
|
||||
kprintf("int0x80: Unknown function %x requested {%x, %x, %x}\n", function, arg1, arg2, arg3);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void syscall_entry()
|
||||
{
|
||||
auto& regs = *syscallRegDump;
|
||||
DWORD function = regs.eax;
|
||||
DWORD arg1 = regs.edx;
|
||||
DWORD arg2 = regs.ecx;
|
||||
DWORD arg3 = regs.ebx;
|
||||
regs.eax = Syscall::handle(function, arg1, arg2, arg3);
|
||||
}
|
27
Kernel/Syscall.h
Normal file
27
Kernel/Syscall.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#define DO_SYSCALL_A0(function) Syscall::invoke((DWORD)(function))
|
||||
#define DO_SYSCALL_A1(function, arg1) Syscall::invoke((DWORD)(function), (DWORD)(arg1))
|
||||
#define DO_SYSCALL_A2(function, arg1, arg2) Syscall::invoke((DWORD)(function), (DWORD)(arg1), (DWORD)(arg2))
|
||||
#define DO_SYSCALL_A3(function, arg1, arg2, arg3) Syscall::invoke((DWORD)(function), (DWORD)(arg1), (DWORD)(arg2), (DWORD)arg3)
|
||||
|
||||
namespace Syscall {
|
||||
|
||||
enum Function {
|
||||
Sleep = 0x1982,
|
||||
Yield = 0x1983,
|
||||
PosixOpen = 0x1985,
|
||||
PosixClose = 0x1986,
|
||||
PosixRead = 0x1987,
|
||||
PosixSeek = 0x1988,
|
||||
PosixKill = 0x1989,
|
||||
PosixGetuid = 0x1990,
|
||||
};
|
||||
|
||||
void initialize();
|
||||
DWORD invoke(DWORD function);
|
||||
DWORD invoke(DWORD function, DWORD arg1);
|
||||
DWORD invoke(DWORD function, DWORD arg1, DWORD arg2);
|
||||
DWORD invoke(DWORD function, DWORD arg1, DWORD arg2, DWORD arg3);
|
||||
|
||||
}
|
21
Kernel/TSS.h
Normal file
21
Kernel/TSS.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
struct TSS32 {
|
||||
unsigned short backlink, __blh;
|
||||
unsigned long esp0;
|
||||
unsigned short ss0, __ss0h;
|
||||
unsigned long esp1;
|
||||
unsigned short ss1, __ss1h;
|
||||
unsigned long esp2;
|
||||
unsigned short ss2, __ss2h;
|
||||
unsigned long cr3, eip, eflags;
|
||||
unsigned long eax,ecx,edx,ebx,esp,ebp,esi,edi;
|
||||
unsigned short es, __esh;
|
||||
unsigned short cs, __csh;
|
||||
unsigned short ss, __ssh;
|
||||
unsigned short ds, __dsh;
|
||||
unsigned short fs, __fsh;
|
||||
unsigned short gs, __gsh;
|
||||
unsigned short ldt, __ldth;
|
||||
unsigned short trace, iomapbase;
|
||||
} PACKED;
|
515
Kernel/Task.cpp
Normal file
515
Kernel/Task.cpp
Normal file
|
@ -0,0 +1,515 @@
|
|||
#include "types.h"
|
||||
#include "Task.h"
|
||||
#include "kmalloc.h"
|
||||
#include "VGA.h"
|
||||
#include "StdLib.h"
|
||||
#include "i386.h"
|
||||
#include "system.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
Task* current;
|
||||
static Task* kt;
|
||||
Task* Task::s_kernelTask;
|
||||
|
||||
static pid_t next_pid;
|
||||
static DoublyLinkedList<Task>* s_tasks;
|
||||
|
||||
PRIVATE void context_switch(Task*);
|
||||
|
||||
static void redo_kt_td()
|
||||
{
|
||||
Descriptor td;
|
||||
|
||||
td.setBase(&kt->tss());
|
||||
td.setLimit(0xffff);
|
||||
td.dpl = 0;
|
||||
td.segment_present = 1;
|
||||
td.granularity = 1;
|
||||
td.zero = 0;
|
||||
td.operation_size = 1;
|
||||
td.descriptor_type = 0;
|
||||
td.type = 9;
|
||||
|
||||
if (!kt->selector())
|
||||
kt->setSelector(allocateGDTEntry());
|
||||
|
||||
writeGDTEntry(kt->selector(), td);
|
||||
flushGDT();
|
||||
}
|
||||
|
||||
void Task::initialize()
|
||||
{
|
||||
current = nullptr;
|
||||
next_pid = 0;
|
||||
Task::s_kernelTask = nullptr;
|
||||
s_tasks = new DoublyLinkedList<Task>;
|
||||
|
||||
kt = new Task(0, "dummy", IPC::Handle::Any, Task::Ring0);
|
||||
|
||||
redo_kt_td();
|
||||
}
|
||||
|
||||
#ifdef TASK_SANITY_CHECKS
|
||||
void Task::checkSanity(const char* msg)
|
||||
{
|
||||
char ch = current->name()[0];
|
||||
kprintf("<%p> %s{%u}%b [%d] :%b: sanity check <%s>\n",
|
||||
current->name().characters(),
|
||||
current->name().characters(),
|
||||
current->name().length(),
|
||||
current->name()[current->name().length() - 1],
|
||||
current->pid(), ch, msg ? msg : "");
|
||||
ASSERT((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));
|
||||
}
|
||||
#endif
|
||||
|
||||
void Task::allocateLDT()
|
||||
{
|
||||
ASSERT(!m_tss.ldt);
|
||||
static const WORD numLDTEntries = 4;
|
||||
WORD newLDTSelector = allocateGDTEntry();
|
||||
m_ldtEntries = new Descriptor[numLDTEntries];
|
||||
#if 1
|
||||
kprintf("new ldt selector = %x\n", newLDTSelector);
|
||||
kprintf("new ldt table at = %p\n", m_ldtEntries);
|
||||
kprintf("new ldt table size = %u\n", (numLDTEntries * 8) - 1);
|
||||
#endif
|
||||
Descriptor& ldt = getGDTEntry(newLDTSelector);
|
||||
ldt.setBase(m_ldtEntries);
|
||||
ldt.setLimit(numLDTEntries * 8 - 1);
|
||||
ldt.dpl = 0;
|
||||
ldt.segment_present = 1;
|
||||
ldt.granularity = 0;
|
||||
ldt.zero = 0;
|
||||
ldt.operation_size = 1;
|
||||
ldt.descriptor_type = 0;
|
||||
ldt.type = Descriptor::LDT;
|
||||
m_tss.ldt = newLDTSelector;
|
||||
}
|
||||
|
||||
Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring)
|
||||
: m_name(n)
|
||||
, m_entry(e)
|
||||
, m_pid(next_pid++)
|
||||
, m_handle(h)
|
||||
, m_state(Runnable)
|
||||
, m_ring(ring)
|
||||
{
|
||||
memset(&m_tss, 0, sizeof(m_tss));
|
||||
memset(&m_ldtEntries, 0, sizeof(m_ldtEntries));
|
||||
|
||||
if (ring == Ring3) {
|
||||
allocateLDT();
|
||||
}
|
||||
|
||||
// Only IF is set when a task boots.
|
||||
m_tss.eflags = 0x0202;
|
||||
|
||||
WORD dataSegment;
|
||||
WORD stackSegment;
|
||||
WORD codeSegment;
|
||||
|
||||
if (ring == Ring0) {
|
||||
codeSegment = 0x08;
|
||||
dataSegment = 0x10;
|
||||
stackSegment = dataSegment;
|
||||
} else {
|
||||
codeSegment = 0x1b;
|
||||
dataSegment = 0x23;
|
||||
stackSegment = dataSegment;
|
||||
}
|
||||
|
||||
m_tss.ds = dataSegment;
|
||||
m_tss.es = dataSegment;
|
||||
m_tss.fs = dataSegment;
|
||||
m_tss.gs = dataSegment;
|
||||
m_tss.ss = stackSegment;
|
||||
m_tss.cs = codeSegment;
|
||||
|
||||
m_tss.eip = (DWORD)m_entry;
|
||||
|
||||
// NOTE: Each task gets 4KB of stack.
|
||||
// This memory is leaked ATM.
|
||||
// But uh, there's also no process termination, so I guess it's not technically leaked...
|
||||
static const DWORD defaultStackSize = 4096;
|
||||
m_stackTop = ((DWORD)kmalloc(defaultStackSize) + defaultStackSize) & 0xffffff8;
|
||||
m_tss.esp = m_stackTop;
|
||||
|
||||
if (ring == Ring3) {
|
||||
// Set up a separate stack for Ring0.
|
||||
// FIXME: Don't leak this stack either.
|
||||
DWORD ring0StackTop = ((DWORD)kmalloc(defaultStackSize) + defaultStackSize) & 0xffffff8;
|
||||
m_tss.ss0 = 0x10;
|
||||
m_tss.esp0 = ring0StackTop;
|
||||
}
|
||||
|
||||
// HACK: Ring2 SS in the TSS is the current PID.
|
||||
m_tss.ss2 = m_pid;
|
||||
|
||||
// Get a nice GDT entry @ next context switch.
|
||||
m_selector = 0;
|
||||
|
||||
// Don't add task 0 (kernel dummy task) to task list.
|
||||
// FIXME: This doesn't belong in the constructor.
|
||||
if (m_pid == 0)
|
||||
return;
|
||||
|
||||
if (m_pid == 1)
|
||||
s_kernelTask = this;
|
||||
|
||||
// Add it to head of task list (meaning it's next to run too, ATM.)
|
||||
s_tasks->prepend(this);
|
||||
|
||||
system.nprocess++;
|
||||
|
||||
kprintf("Task %u (%s) spawned @ %p\n", m_pid, m_name.characters(), m_entry);
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
{
|
||||
delete [] m_ldtEntries;
|
||||
m_ldtEntries = nullptr;
|
||||
}
|
||||
|
||||
void yield()
|
||||
{
|
||||
if (!current) {
|
||||
kprintf( "PANIC: yield() with !current" );
|
||||
HANG;
|
||||
}
|
||||
|
||||
current->setTicksLeft(1);
|
||||
|
||||
// HACK: To avoid ridiculous clock skew, decrement the system uptime
|
||||
// counter. It's incremented by int 0x50...
|
||||
--system.uptime;
|
||||
asm("int $0x50");
|
||||
}
|
||||
|
||||
void sched()
|
||||
{
|
||||
if (!current) {
|
||||
// XXX: The first ever context_switch() goes to the idle task.
|
||||
// This to setup a reliable place we can return to.
|
||||
context_switch(Task::kernelTask());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check and unblock tasks whose wait conditions have been met.
|
||||
for (auto* task = s_tasks->head(); task; task = task->next()) {
|
||||
if (task->state() == Task::BlockedReceive && (task->ipc.msg.isValid() || task->ipc.notifies)) {
|
||||
task->unblock();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (task->state() == Task::BlockedSend) {
|
||||
Task* peer = Task::fromIPCHandle(task->ipc.dst);
|
||||
if (peer && peer->state() == Task::BlockedReceive && peer->acceptsMessageFrom(*task)) {
|
||||
task->unblock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (task->state() == Task::BlockedSleep) {
|
||||
if (task->wakeupTime() <= system.uptime) {
|
||||
task->unblock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto* prevHead = s_tasks->head();
|
||||
for (;;) {
|
||||
// Move head to tail.
|
||||
s_tasks->append(s_tasks->removeHead());
|
||||
auto* task = s_tasks->head();
|
||||
|
||||
if (task->state() == Task::Runnable || task->state() == Task::Running) {
|
||||
//kprintf("switch to %s\n", task->name().characters());
|
||||
context_switch(task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (task == prevHead) {
|
||||
// Back at task_head, nothing wants to run.
|
||||
context_switch(Task::kernelTask());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void drawSchedulerBanner(Task& task)
|
||||
{
|
||||
auto c = vga_get_cursor();
|
||||
auto a = vga_get_attr();
|
||||
vga_set_cursor(0, 50);
|
||||
vga_set_attr(0x20);
|
||||
kprintf(" ");
|
||||
kprintf(" ");
|
||||
kprintf(" ");
|
||||
vga_set_cursor(0, 50);
|
||||
kprintf("pid: %u ", task.pid());
|
||||
vga_set_cursor(0, 58);
|
||||
kprintf("%s", task.name().characters());
|
||||
vga_set_cursor(0, 65);
|
||||
kprintf("eip: %p", task.tss().eip);
|
||||
vga_set_attr(a);
|
||||
vga_set_cursor(c);
|
||||
}
|
||||
|
||||
static void context_switch(Task* t)
|
||||
{
|
||||
t->setTicksLeft(5);
|
||||
|
||||
// If the last task hasn't blocked (still marked as running),
|
||||
// mark it as runnable for the next round.
|
||||
if (current->state() == Task::Running)
|
||||
current->setState(Task::Runnable);
|
||||
|
||||
current = t;
|
||||
t->setState(Task::Running);
|
||||
|
||||
// You might be looking at the slowest i386 context switcher ever made.
|
||||
// (But I don't think so.)
|
||||
|
||||
Descriptor td;
|
||||
|
||||
td.limit_hi = 0;
|
||||
td.limit_lo = 0xFFFF;
|
||||
td.base_lo = (DWORD)(&t->tss()) & 0xFFFF;
|
||||
td.base_hi = ((DWORD)(&t->tss()) >> 16) & 0xFF;
|
||||
td.base_hi2 = ((DWORD)(&t->tss()) >> 24) & 0xFF;
|
||||
td.dpl = 0;
|
||||
td.segment_present = 1;
|
||||
td.granularity = 1;
|
||||
td.zero = 0;
|
||||
td.operation_size = 1;
|
||||
td.descriptor_type = 0;
|
||||
td.type = 11;
|
||||
|
||||
if (!t->selector()) {
|
||||
t->setSelector(allocateGDTEntry());
|
||||
writeGDTEntry(t->selector(), td);
|
||||
flushGDT();
|
||||
} else {
|
||||
writeGDTEntry(t->selector(), td);
|
||||
}
|
||||
|
||||
drawSchedulerBanner(*t);
|
||||
|
||||
redo_kt_td();
|
||||
kt->tss().backlink = t->selector();
|
||||
loadTaskRegister(kt->selector());
|
||||
}
|
||||
|
||||
Task* Task::fromPID(pid_t pid)
|
||||
{
|
||||
for (auto* task = s_tasks->head(); task; task = task->next()) {
|
||||
if (task->pid() == pid)
|
||||
return task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Task* Task::fromIPCHandle(IPC::Handle handle)
|
||||
{
|
||||
for (auto* task = s_tasks->head(); task; task = task->next()) {
|
||||
if (task->handle() == handle)
|
||||
return task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class FileHandle {
|
||||
public:
|
||||
FileHandle() { }
|
||||
~FileHandle() { }
|
||||
|
||||
int seek(int offset);
|
||||
size_t read(void* buffer, int bufferSize);
|
||||
int fd() const { return m_fd; }
|
||||
|
||||
static FileHandle* fromFileDescriptor(int fd);
|
||||
|
||||
//private:
|
||||
RefPtr<FileSystem::VirtualNode> m_vnode;
|
||||
int m_fd { -1 };
|
||||
size_t m_offset { 0 };
|
||||
};
|
||||
|
||||
size_t FileHandle::read(void* buffer, int bufferSize)
|
||||
{
|
||||
Task::checkSanity("FileHandle::read");
|
||||
size_t nread = m_vnode->read((BYTE*)buffer, m_offset, bufferSize);
|
||||
m_offset += nread;
|
||||
return nread;
|
||||
}
|
||||
|
||||
int FileHandle::seek(int offset)
|
||||
{
|
||||
if (!m_vnode)
|
||||
return -1;
|
||||
ASSERT(offset >= 0);
|
||||
if ((unsigned)offset >= m_vnode->size())
|
||||
return -1;
|
||||
m_offset = offset;
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
FileHandle* FileHandle::fromFileDescriptor(int fd)
|
||||
{
|
||||
return current->fileHandleIfExists(fd);
|
||||
}
|
||||
|
||||
FileHandle* Task::fileHandleIfExists(int fd)
|
||||
{
|
||||
if (fd < 0)
|
||||
return nullptr;
|
||||
if ((unsigned)fd < m_fileHandles.size())
|
||||
return m_fileHandles[fd];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Task::sys$seek(int fd, int offset)
|
||||
{
|
||||
auto* handle = FileHandle::fromFileDescriptor(fd);
|
||||
if (!handle)
|
||||
return -1;
|
||||
return handle->seek(offset);
|
||||
}
|
||||
|
||||
int Task::sys$read(int fd, void* outbuf, size_t nread)
|
||||
{
|
||||
Task::checkSanity("Task::sys$read");
|
||||
kprintf("Task::sys$read: called(%d, %p, %u)\n", fd, outbuf, nread);
|
||||
auto* handle = FileHandle::fromFileDescriptor(fd);
|
||||
kprintf("Task::sys$read: handle=%p\n", handle);
|
||||
if (!handle) {
|
||||
kprintf("Task::sys$read: handle not found :(\n");
|
||||
return -1;
|
||||
}
|
||||
kprintf("call read on handle=%p\n", handle);
|
||||
nread = handle->read(outbuf, nread);
|
||||
kprintf("called read\n");
|
||||
kprintf("Task::sys$read: nread=%u\n", nread);
|
||||
return nread;
|
||||
}
|
||||
|
||||
int Task::sys$close(int fd)
|
||||
{
|
||||
auto* handle = FileHandle::fromFileDescriptor(fd);
|
||||
if (!handle)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Task::sys$open(const char* path, size_t pathLength)
|
||||
{
|
||||
Task::checkSanity("sys$open");
|
||||
kprintf("Task::sys$open(): PID=%u, path=%s {%u}\n", m_pid, path, pathLength);
|
||||
auto* handle = current->openFile(String(path, pathLength));
|
||||
if (handle)
|
||||
return handle->fd();
|
||||
return -1;
|
||||
}
|
||||
|
||||
FileHandle* Task::openFile(String&& path)
|
||||
{
|
||||
auto vnode = FileSystem::createVirtualNode(move(path));
|
||||
if (!vnode) {
|
||||
kprintf("createVirtualNode failed\n");
|
||||
return nullptr;
|
||||
}
|
||||
#if 1
|
||||
FileHandle* fh = new FileHandle;
|
||||
kprintf("made new FileHandle\n");
|
||||
fh->m_fd = m_fileHandles.size();
|
||||
kprintf(" fd = %d\n", fh->m_fd);
|
||||
fh->m_vnode = move(vnode);
|
||||
kprintf(" vnode = %p\n", fh->m_vnode.ptr());
|
||||
m_fileHandles.append(move(fh)); // FIXME: allow non-move Vector::append
|
||||
kprintf("Task::openFile(): FileHandle{%p} fd=%d\n", fh, fh->m_fd);
|
||||
return fh;
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Task::sys$kill(pid_t pid, int sig)
|
||||
{
|
||||
(void) sig;
|
||||
if (pid == 0) {
|
||||
// FIXME: Send to same-group processes.
|
||||
ASSERT(pid != 0);
|
||||
}
|
||||
if (pid == -1) {
|
||||
// FIXME: Send to all processes.
|
||||
ASSERT(pid != -1);
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
Task* peer = Task::fromPID(pid);
|
||||
if (!peer) {
|
||||
// errno = ESRCH;
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
send(peer->handle(), IPC::Message(SYS_KILL, DataBuffer::copy((BYTE*)&sig, sizeof(sig))));
|
||||
IPC::Message response = receive(peer->handle());
|
||||
return *(int*)response.data();
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
uid_t Task::sys$getuid()
|
||||
{
|
||||
return m_uid;
|
||||
}
|
||||
|
||||
bool Task::acceptsMessageFrom(Task& peer)
|
||||
{
|
||||
return !ipc.msg.isValid() && (ipc.src == IPC::Handle::Any || ipc.src == peer.handle());
|
||||
}
|
||||
|
||||
void Task::unblock()
|
||||
{
|
||||
ASSERT(m_state != Task::Runnable && m_state != Task::Running);
|
||||
system.nblocked--;
|
||||
m_state = Task::Runnable;
|
||||
}
|
||||
|
||||
void Task::block(Task::State state)
|
||||
{
|
||||
ASSERT(current->state() == Task::Running);
|
||||
system.nblocked++;
|
||||
current->setState(state);
|
||||
}
|
||||
|
||||
void block(Task::State state)
|
||||
{
|
||||
current->block(state);
|
||||
yield();
|
||||
}
|
||||
|
||||
void sleep(DWORD ticks)
|
||||
{
|
||||
ASSERT(current->state() == Task::Running);
|
||||
current->setWakeupTime(system.uptime + ticks);
|
||||
current->block(Task::BlockedSleep);
|
||||
yield();
|
||||
}
|
||||
|
||||
void Task::sys$sleep(DWORD ticks)
|
||||
{
|
||||
ASSERT(this == current);
|
||||
sleep(ticks);
|
||||
}
|
||||
|
||||
Task* Task::kernelTask()
|
||||
{
|
||||
ASSERT(s_kernelTask);
|
||||
return s_kernelTask;
|
||||
}
|
||||
|
||||
void Task::setError(int error)
|
||||
{
|
||||
m_error = error;
|
||||
}
|
126
Kernel/Task.h
Normal file
126
Kernel/Task.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "IPC.h"
|
||||
#include "DoublyLinkedList.h"
|
||||
#include "String.h"
|
||||
#include "TSS.h"
|
||||
#include "Vector.h"
|
||||
#include "i386.h"
|
||||
|
||||
//#define TASK_SANITY_CHECKS
|
||||
|
||||
class FileHandle;
|
||||
|
||||
class Task : public DoublyLinkedListNode<Task> {
|
||||
friend class DoublyLinkedListNode<Task>;
|
||||
public:
|
||||
#ifdef TASK_SANITY_CHECKS
|
||||
static void checkSanity(const char* msg = nullptr);
|
||||
#else
|
||||
static void checkSanity(const char*) { }
|
||||
#endif
|
||||
|
||||
enum State {
|
||||
Invalid = 0,
|
||||
Runnable = 1,
|
||||
Running = 2,
|
||||
BlockedReceive = 3,
|
||||
BlockedSend = 4,
|
||||
BlockedSleep = 5,
|
||||
Terminated = 6,
|
||||
};
|
||||
|
||||
enum RingLevel {
|
||||
Ring0 = 0,
|
||||
Ring3 = 3,
|
||||
};
|
||||
|
||||
static Task* fromPID(pid_t);
|
||||
static Task* fromIPCHandle(IPC::Handle);
|
||||
static Task* kernelTask();
|
||||
|
||||
Task(void (*entry)(), const char* name, IPC::Handle, RingLevel);
|
||||
~Task();
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
pid_t pid() const { return m_pid; }
|
||||
DWORD ticks() const { return m_ticks; }
|
||||
WORD selector() const { return m_selector; }
|
||||
TSS32& tss() { return m_tss; }
|
||||
State state() const { return m_state; }
|
||||
IPC::Handle handle() const { return m_handle; }
|
||||
|
||||
FileHandle* fileHandleIfExists(int fd);
|
||||
FileHandle* createFileHandle();
|
||||
|
||||
bool acceptsMessageFrom(Task&);
|
||||
|
||||
void block(Task::State);
|
||||
void unblock();
|
||||
|
||||
void setWakeupTime(DWORD t) { m_wakeupTime = t; }
|
||||
DWORD wakeupTime() const { return m_wakeupTime; }
|
||||
|
||||
bool tick() { ++m_ticks; return --m_ticksLeft; }
|
||||
void setTicksLeft(DWORD t) { m_ticksLeft = t; }
|
||||
|
||||
void setSelector(WORD s) { m_selector = s; }
|
||||
void setState(State s) { m_state = s; }
|
||||
|
||||
uid_t sys$getuid();
|
||||
int sys$open(const char* path, size_t pathLength);
|
||||
int sys$close(int fd);
|
||||
int sys$read(int fd, void* outbuf, size_t nread);
|
||||
int sys$seek(int fd, int offset);
|
||||
int sys$kill(pid_t pid, int sig);
|
||||
int sys$geterror() { return m_error; }
|
||||
void sys$sleep(DWORD ticks);
|
||||
|
||||
struct
|
||||
{
|
||||
IPC::Message msg;
|
||||
IPC::Handle dst;
|
||||
IPC::Handle src;
|
||||
DWORD notifies { 0 };
|
||||
} ipc;
|
||||
|
||||
static void initialize();
|
||||
void setError(int);
|
||||
|
||||
private:
|
||||
FileHandle* openFile(String&&);
|
||||
|
||||
void allocateLDT();
|
||||
|
||||
Task* m_prev { nullptr };
|
||||
Task* m_next { nullptr };
|
||||
|
||||
String m_name;
|
||||
void (*m_entry)() { nullptr };
|
||||
pid_t m_pid { 0 };
|
||||
uid_t m_uid { 0 };
|
||||
DWORD m_ticks { 0 };
|
||||
DWORD m_ticksLeft { 0 };
|
||||
IPC::Handle m_handle { 0 };
|
||||
DWORD m_stackTop { 0 };
|
||||
WORD m_selector { 0 };
|
||||
State m_state { Invalid };
|
||||
DWORD m_wakeupTime { 0 };
|
||||
TSS32 m_tss;
|
||||
Descriptor* m_ldtEntries { nullptr };
|
||||
Vector<FileHandle*> m_fileHandles;
|
||||
RingLevel m_ring { Ring0 };
|
||||
int m_error { 0 };
|
||||
|
||||
static Task* s_kernelTask;
|
||||
};
|
||||
|
||||
extern void task_init();
|
||||
extern void yield();
|
||||
extern void sched();
|
||||
extern void block(Task::State);
|
||||
extern void sleep(DWORD ticks);
|
||||
|
||||
/* The currently executing task. NULL during kernel bootup. */
|
||||
extern Task* current;
|
42
Kernel/Userspace.cpp
Normal file
42
Kernel/Userspace.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "Userspace.h"
|
||||
#include "Syscall.h"
|
||||
#include "StdLib.h"
|
||||
|
||||
namespace Userspace {
|
||||
|
||||
int open(const char* path)
|
||||
{
|
||||
return DO_SYSCALL_A2(Syscall::PosixOpen, path, strlen(path));
|
||||
}
|
||||
|
||||
int close(int fd)
|
||||
{
|
||||
return DO_SYSCALL_A1(Syscall::PosixClose, fd);
|
||||
}
|
||||
|
||||
int read(int fd, void* outbuf, size_t nread)
|
||||
{
|
||||
return DO_SYSCALL_A3(Syscall::PosixRead, fd, outbuf, nread);
|
||||
}
|
||||
|
||||
int seek(int fd, int offset)
|
||||
{
|
||||
return DO_SYSCALL_A2(Syscall::PosixRead, fd, offset);
|
||||
}
|
||||
|
||||
int kill(pid_t pid, int sig)
|
||||
{
|
||||
return DO_SYSCALL_A2(Syscall::PosixKill, pid, sig);
|
||||
}
|
||||
|
||||
uid_t getuid()
|
||||
{
|
||||
return DO_SYSCALL_A0(Syscall::PosixGetuid);
|
||||
}
|
||||
|
||||
void sleep(DWORD ticks)
|
||||
{
|
||||
DO_SYSCALL_A1(Syscall::Sleep, ticks);
|
||||
}
|
||||
|
||||
}
|
15
Kernel/Userspace.h
Normal file
15
Kernel/Userspace.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Userspace {
|
||||
|
||||
int open(const char* path);
|
||||
int close(int fd);
|
||||
int read(int fd, void* outbuf, size_t nread);
|
||||
int seek(int fd, int offset);
|
||||
int kill(pid_t pid, int sig);
|
||||
uid_t getuid();
|
||||
void sleep(DWORD ticks);
|
||||
|
||||
}
|
237
Kernel/VGA.cpp
Normal file
237
Kernel/VGA.cpp
Normal file
|
@ -0,0 +1,237 @@
|
|||
#include "types.h"
|
||||
#include "VGA.h"
|
||||
#include "i386.h"
|
||||
#include "IO.h"
|
||||
#include "StdLib.h"
|
||||
#include "Task.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
PRIVATE BYTE *vga_mem = 0L;
|
||||
PRIVATE BYTE current_attr = 0x07;
|
||||
|
||||
PRIVATE volatile WORD soft_cursor;
|
||||
|
||||
PRIVATE void print_num( DWORD );
|
||||
PRIVATE void print_hex( DWORD, BYTE fields );
|
||||
static void printSignedNumber(int);
|
||||
|
||||
PRIVATE void
|
||||
putch( char ch )
|
||||
{
|
||||
WORD row;
|
||||
|
||||
switch( ch )
|
||||
{
|
||||
case '\n':
|
||||
row = soft_cursor / 80;
|
||||
if( row == 23 )
|
||||
{
|
||||
memcpy( vga_mem, vga_mem + 160, 160 * 23 );
|
||||
memset( vga_mem + (160 * 23), 0, 160 );
|
||||
soft_cursor = row * 80;
|
||||
}
|
||||
else
|
||||
soft_cursor = (row + 1) * 80;
|
||||
return;
|
||||
default:
|
||||
vga_mem[soft_cursor * 2] = ch;
|
||||
vga_mem[soft_cursor * 2 + 1] = current_attr;
|
||||
soft_cursor++;
|
||||
}
|
||||
row = soft_cursor / 80;
|
||||
if ((row >= 24 && current->handle() != IPC::Handle::PanelTask)) {
|
||||
memcpy( vga_mem, vga_mem + 160, 160 * 23 );
|
||||
memset( vga_mem + (160 * 23), 0, 160 );
|
||||
soft_cursor = 23 * 80;
|
||||
}
|
||||
}
|
||||
|
||||
PUBLIC void
|
||||
kprintf( const char *fmt, ... )
|
||||
{
|
||||
const char *p;
|
||||
va_list ap;
|
||||
|
||||
soft_cursor = vga_get_cursor();
|
||||
|
||||
va_start( ap, fmt );
|
||||
|
||||
for( p = fmt; *p; ++p )
|
||||
{
|
||||
if( *p == '%' && *(p + 1) )
|
||||
{
|
||||
++p;
|
||||
switch( *p )
|
||||
{
|
||||
case 's':
|
||||
{
|
||||
const char* sp = va_arg(ap, const char*);
|
||||
//ASSERT(sp != nullptr);
|
||||
if (!sp) {
|
||||
putch('<');
|
||||
putch('N');
|
||||
putch('u');
|
||||
putch('L');
|
||||
putch('>');
|
||||
} else {
|
||||
for (; *sp; ++sp)
|
||||
putch(*sp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
printSignedNumber(va_arg(ap, int));
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
print_num( va_arg( ap, DWORD ));
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
print_hex( va_arg( ap, DWORD ), 8 );
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
print_hex( va_arg( ap, int ), 2 );
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
putch( (char)va_arg( ap, int ));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
putch( '0' );
|
||||
putch( 'x' );
|
||||
print_hex( va_arg( ap, DWORD ), 8 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
putch( *p );
|
||||
}
|
||||
}
|
||||
|
||||
/* va_arg( ap, type ); */
|
||||
va_end( ap );
|
||||
|
||||
vga_set_cursor( soft_cursor );
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
print_hex( DWORD number, BYTE fields )
|
||||
{
|
||||
static const char h[] = {
|
||||
'0','1','2','3','4','5','6','7',
|
||||
'8','9','a','b','c','d','e','f'
|
||||
};
|
||||
|
||||
BYTE shr_count = fields * 4;
|
||||
while( shr_count )
|
||||
{
|
||||
shr_count -= 4;
|
||||
putch( h[(number >> shr_count) & 0x0F] );
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
print_num( DWORD number )
|
||||
{
|
||||
DWORD divisor = 1000000000;
|
||||
char ch;
|
||||
char padding = 1;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
ch = '0' + (number / divisor);
|
||||
number %= divisor;
|
||||
|
||||
if( ch != '0' )
|
||||
padding = 0;
|
||||
|
||||
if( !padding || divisor == 1 )
|
||||
putch( ch );
|
||||
|
||||
if( divisor == 1 )
|
||||
break;
|
||||
divisor /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
static void printSignedNumber(int number)
|
||||
{
|
||||
if (number < 0) {
|
||||
putch('-');
|
||||
print_num(0 - number);
|
||||
} else {
|
||||
print_num(number);
|
||||
}
|
||||
}
|
||||
|
||||
PUBLIC void
|
||||
vga_set_attr( BYTE attr )
|
||||
{
|
||||
current_attr = attr;
|
||||
}
|
||||
|
||||
PUBLIC BYTE
|
||||
vga_get_attr()
|
||||
{
|
||||
return current_attr;
|
||||
}
|
||||
|
||||
PUBLIC void
|
||||
vga_init()
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
current_attr = 0x07;
|
||||
vga_mem = (BYTE *)0xb8000;
|
||||
|
||||
for( i = 0; i < (80 * 24); ++i )
|
||||
{
|
||||
vga_mem[i*2] = ' ';
|
||||
vga_mem[i*2 + 1] = 0x07;
|
||||
}
|
||||
|
||||
/* Fill the bottom line with blue. */
|
||||
for( i = (80 * 24); i < (80 * 25); ++i )
|
||||
{
|
||||
vga_mem[i*2] = ' ';
|
||||
vga_mem[i*2 + 1] = 0x17;
|
||||
}
|
||||
vga_set_cursor( 0 );
|
||||
}
|
||||
|
||||
PUBLIC WORD
|
||||
vga_get_cursor()
|
||||
{
|
||||
WORD value;
|
||||
IO::out8(0x3d4, 0x0e);
|
||||
value = IO::in8(0x3d5) << 8;
|
||||
IO::out8(0x3d4, 0x0f);
|
||||
value |= IO::in8(0x3d5);
|
||||
return value;
|
||||
}
|
||||
|
||||
PUBLIC void
|
||||
vga_set_cursor( WORD value )
|
||||
{
|
||||
if( value >= (80 * 25) )
|
||||
{
|
||||
/* XXX: If you try to move the cursor off the screen, I will go reddish pink! */
|
||||
vga_set_cursor( 0 );
|
||||
current_attr = 0x0C;
|
||||
return;
|
||||
}
|
||||
IO::out8(0x3d4, 0x0e);
|
||||
IO::out8(0x3d5, MSB(value));
|
||||
IO::out8(0x3d4, 0x0f);
|
||||
IO::out8(0x3d5, LSB(value));
|
||||
}
|
||||
|
||||
void vga_set_cursor(BYTE row, BYTE column)
|
||||
{
|
||||
vga_set_cursor(row * 80 + column);
|
||||
}
|
11
Kernel/VGA.h
Normal file
11
Kernel/VGA.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void vga_init();
|
||||
BYTE vga_get_attr();
|
||||
void vga_set_attr(BYTE);
|
||||
void vga_set_cursor(WORD);
|
||||
void vga_set_cursor(BYTE row, BYTE column);
|
||||
WORD vga_get_cursor();
|
||||
void kprintf(const char *fmt, ...);
|
90
Kernel/Vector.h
Normal file
90
Kernel/Vector.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "kmalloc.h"
|
||||
|
||||
#define SANITIZE_VECTOR
|
||||
|
||||
template<typename T>
|
||||
class Vector {
|
||||
public:
|
||||
Vector() { }
|
||||
~Vector();
|
||||
|
||||
Vector(const Vector&&);
|
||||
Vector& operator=(const Vector&&);
|
||||
|
||||
bool isEmpty() const { return m_size == 0; }
|
||||
size_t size() const { return m_size; }
|
||||
size_t capacity() const { return m_capacity; }
|
||||
|
||||
void append(T&&);
|
||||
void clear();
|
||||
|
||||
const T& operator[](size_t i) const { return m_elements[i]; }
|
||||
T& operator[](size_t i) { return m_elements[i]; }
|
||||
|
||||
private:
|
||||
Vector(const Vector&) = delete;
|
||||
Vector& operator=(const Vector&) = delete;
|
||||
|
||||
void ensureCapacity(size_t);
|
||||
|
||||
T* m_elements { nullptr };
|
||||
size_t m_size { 0 };
|
||||
size_t m_capacity { 0 };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Vector<T>::~Vector()
|
||||
{
|
||||
clear();
|
||||
#ifdef SANITIZE_VECTOR
|
||||
m_elements = (T*)0xdddddddd;
|
||||
m_size = 0x8a8a8a8a;
|
||||
m_capacity = 0xa8a8a8a8;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Vector<T>::clear()
|
||||
{
|
||||
if (!m_elements)
|
||||
return;
|
||||
for (size_t i = 0; i < m_size; ++i) {
|
||||
m_elements[i].~T();
|
||||
}
|
||||
kfree(m_elements);
|
||||
m_elements = nullptr;
|
||||
m_size = 0;
|
||||
m_capacity = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Vector<T>::append(T&& element)
|
||||
{
|
||||
ensureCapacity(m_size + 1);
|
||||
new (&m_elements[m_size]) T(move(element));
|
||||
++m_size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Vector<T>::ensureCapacity(size_t neededCapacity)
|
||||
{
|
||||
if (neededCapacity <= m_capacity)
|
||||
return;
|
||||
size_t newCapacity = (neededCapacity + 8) & ~7;
|
||||
// FIXME: We need to support further growth here, jeez...
|
||||
ASSERT(m_capacity == 0);
|
||||
ASSERT(!m_elements);
|
||||
m_capacity = newCapacity;
|
||||
T* newElements = (T*)kmalloc(m_capacity * sizeof(T));
|
||||
#ifdef SANITIZE_VECTOR
|
||||
memset(newElements, 0x66, m_capacity * sizeof(T));
|
||||
#endif
|
||||
if (m_elements) {
|
||||
memcpy(newElements, m_elements, m_size * sizeof(T));
|
||||
kfree(m_elements);
|
||||
}
|
||||
m_elements = newElements;
|
||||
}
|
BIN
Kernel/_fs_contents
Normal file
BIN
Kernel/_fs_contents
Normal file
Binary file not shown.
15
Kernel/_start.cpp
Normal file
15
Kernel/_start.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* _start()
|
||||
*
|
||||
* This is where we land immediately after leaving the bootloader.
|
||||
*
|
||||
* ATM it's really shaky so don't put code before it ^_^
|
||||
*/
|
||||
|
||||
extern void init();
|
||||
|
||||
extern "C" void _start()
|
||||
{
|
||||
init();
|
||||
asm volatile("cli; hlt");
|
||||
}
|
150
Kernel/ext2fs.h
Normal file
150
Kernel/ext2fs.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
#ifndef __ext2fs_h__
|
||||
#define __ext2fs_h__
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define EXT2_MAGIC 0xef53
|
||||
#define EXT2_NDIR_BLOCKS 12
|
||||
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
|
||||
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
|
||||
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
|
||||
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
|
||||
#define EXT2_NAME_LEN 255
|
||||
|
||||
#define EXT2_FT_UNKNOWN 0
|
||||
#define EXT2_FT_REG_FILE 1
|
||||
#define EXT2_FT_DIR 2
|
||||
#define EXT2_FT_CHRDEV 3
|
||||
#define EXT2_FT_BLKDEV 4
|
||||
#define EXT2_FT_FIFO 5
|
||||
#define EXT2_FT_SOCK 6
|
||||
#define EXT2_FT_SYMLINK 7
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__u32 s_inodes_count; /* Inodes count */
|
||||
__u32 s_blocks_count; /* Blocks count */
|
||||
__u32 s_r_blocks_count; /* Reserved blocks count */
|
||||
__u32 s_free_blocks_count; /* Free blocks count */
|
||||
__u32 s_free_inodes_count; /* Free inodes count */
|
||||
__u32 s_first_data_block; /* First Data Block */
|
||||
__u32 s_log_block_size; /* Block size */
|
||||
__s32 s_log_frag_size; /* Fragment size */
|
||||
__u32 s_blocks_per_group; /* # Blocks per group */
|
||||
__u32 s_frags_per_group; /* # Fragments per group */
|
||||
__u32 s_inodes_per_group; /* # Inodes per group */
|
||||
__u32 s_mtime; /* Mount time */
|
||||
__u32 s_wtime; /* Write time */
|
||||
__u16 s_mnt_count; /* Mount count */
|
||||
__s16 s_max_mnt_count; /* Maximal mount count */
|
||||
__u16 s_magic; /* Magic signature */
|
||||
__u16 s_state; /* File system state */
|
||||
__u16 s_errors; /* Behaviour when detecting errors */
|
||||
__u16 s_pad;
|
||||
__u32 s_lastcheck; /* time of last check */
|
||||
__u32 s_checkinterval; /* max. time between checks */
|
||||
__u32 s_creator_os; /* OS */
|
||||
__u32 s_rev_level; /* Revision level */
|
||||
__u16 s_def_resuid; /* Default uid for reserved blocks */
|
||||
__u16 s_def_resgid; /* Default gid for reserved blocks */
|
||||
__u32 s_first_ino; // First non-reserved inode
|
||||
__u16 s_inode_size; // inode size
|
||||
__u16 s_block_group_nr; // Index of block group hosting this superblock
|
||||
__u32 s_feature_compat;
|
||||
__u32 s_feature_incompat;
|
||||
__u32 s_feature_ro_compat;
|
||||
__u8 s_uuid[16];
|
||||
__u32 s_reserved[226]; /* Padding to the end of the block */
|
||||
} PACKED ext2_super_block;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__u32 bg_block_bitmap;
|
||||
__u32 bg_inode_bitmap;
|
||||
__u32 bg_inode_table;
|
||||
__u16 bg_free_blocks_count;
|
||||
__u16 bg_free_inodes_count;
|
||||
__u16 bg_used_dirs_count;
|
||||
__u16 bg_pad;
|
||||
__u32 bg_reserved;
|
||||
} PACKED ext2_group_descriptor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__u16 i_mode; /* File mode */
|
||||
__u16 i_uid; /* Owner Uid */
|
||||
__u32 i_size; /* 4: Size in bytes */
|
||||
__u32 i_atime; /* Access time */
|
||||
__u32 i_ctime; /* 12: Creation time */
|
||||
__u32 i_mtime; /* Modification time */
|
||||
__u32 i_dtime; /* 20: Deletion Time */
|
||||
__u16 i_gid; /* Group Id */
|
||||
__u16 i_links_count; /* 24: Links count */
|
||||
__u32 i_blocks; /* Blocks count */
|
||||
__u32 i_flags; /* 32: File flags */
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
__u32 l_i_reserved1;
|
||||
}
|
||||
linux1;
|
||||
struct
|
||||
{
|
||||
__u32 h_i_translator;
|
||||
}
|
||||
hurd1;
|
||||
struct
|
||||
{
|
||||
__u32 m_i_reserved1;
|
||||
}
|
||||
masix1;
|
||||
}
|
||||
osd1; /* OS dependent 1 */
|
||||
__u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */
|
||||
__u32 i_version; /* File version (for NFS) */
|
||||
__u32 i_file_acl; /* File ACL */
|
||||
__u32 i_dir_acl; /* Directory ACL */
|
||||
__u32 i_faddr; /* Fragment address */
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
__u8 l_i_frag; /* Fragment number */
|
||||
__u8 l_i_fsize; /* Fragment size */
|
||||
__u16 i_pad1;
|
||||
__u32 l_i_reserved2[2];
|
||||
}
|
||||
linux2;
|
||||
struct
|
||||
{
|
||||
__u8 h_i_frag; /* Fragment number */
|
||||
__u8 h_i_fsize; /* Fragment size */
|
||||
__u16 h_i_mode_high;
|
||||
__u16 h_i_uid_high;
|
||||
__u16 h_i_gid_high;
|
||||
__u32 h_i_author;
|
||||
}
|
||||
hurd2;
|
||||
struct
|
||||
{
|
||||
__u8 m_i_frag; /* Fragment number */
|
||||
__u8 m_i_fsize; /* Fragment size */
|
||||
__u16 m_pad1;
|
||||
__u32 m_i_reserved2[2];
|
||||
}
|
||||
masix2;
|
||||
}
|
||||
osd2; /* OS dependent 2 */
|
||||
} PACKED ext2_inode;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__u32 d_inode;
|
||||
__u16 d_rec_len;
|
||||
__u8 d_name_len;
|
||||
__u8 d_file_type;
|
||||
char d_name[EXT2_NAME_LEN];
|
||||
} PACKED ext2_dir_entry;
|
||||
|
||||
#endif
|
451
Kernel/fs.cpp
Normal file
451
Kernel/fs.cpp
Normal file
|
@ -0,0 +1,451 @@
|
|||
#include "Disk.h"
|
||||
#include "Task.h"
|
||||
#include "VGA.h"
|
||||
#include "kmalloc.h"
|
||||
#include "Ext2FileSystem.h"
|
||||
#include "i386.h"
|
||||
#include "StdLib.h"
|
||||
#include "OwnPtr.h"
|
||||
#include "FileSystem.h"
|
||||
#include "String.h"
|
||||
|
||||
//#define FS_DEBUG
|
||||
|
||||
Ext2FileSystem::~Ext2FileSystem()
|
||||
{
|
||||
kprintf("fs: kill Ext2FileSystem\n");
|
||||
ASSERT(false);
|
||||
kfree(m_groupTable);
|
||||
m_groupTable = nullptr;
|
||||
if (m_inodeTables) {
|
||||
for (DWORD i = 0; i < m_blockGroupCount; ++i)
|
||||
delete [] m_inodeTables[i];
|
||||
}
|
||||
delete [] m_inodeTables;
|
||||
m_inodeTables = nullptr;
|
||||
}
|
||||
|
||||
static Ext2FileSystem* fileSystem;
|
||||
|
||||
void Ext2FileSystem::readDiskSector(DWORD sectorIndex, BYTE* buffer)
|
||||
{
|
||||
Task::checkSanity("Ext2FileSystem::readDiskSector");
|
||||
bool success = Disk::readSectors(sectorIndex, 1, buffer);
|
||||
(void) success;
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> Ext2FileSystem::readBlocks(DWORD blockIndex, BYTE count)
|
||||
{
|
||||
Task::checkSanity("readBlocks");
|
||||
if (!m_superBlock) {
|
||||
kprintf("fs: Attempt to read blocks without superblock!\n");
|
||||
HANG;
|
||||
}
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("Read %u block(s) starting at %u\n", count, blockIndex);
|
||||
#endif
|
||||
// FIXME: This is broken up into 1-sector reads because the disk task can't handle multi-sector reads yet.
|
||||
auto buffer = DataBuffer::createUninitialized(count * sectorsPerBlock() * bytesPerSector);
|
||||
BYTE* bufptr = (BYTE*)buffer->data();
|
||||
for (DWORD i = 0; i < count; ++i) {
|
||||
readDiskSector(((blockIndex + i) * sectorsPerBlock()) + 0, bufptr);
|
||||
readDiskSector(((blockIndex + i) * sectorsPerBlock()) + 1, bufptr + bytesPerSector);
|
||||
bufptr += bytesPerSector * 2;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void Ext2FileSystem::readSuperBlock()
|
||||
{
|
||||
ASSERT(!m_superBlock);
|
||||
ASSERT(!m_groupTable);
|
||||
|
||||
m_superBlock = make<ext2_super_block>();
|
||||
readDiskSector(2, (BYTE*)m_superBlock.ptr());
|
||||
|
||||
if (m_superBlock->s_magic != EXT2_MAGIC) {
|
||||
kprintf("fs: PANIC! No ext2 filesystem found\n");
|
||||
HANG;
|
||||
}
|
||||
|
||||
kprintf("fs: ext2 filesystem found -- %u inodes, %u blocks\n",
|
||||
m_superBlock->s_inodes_count,
|
||||
m_superBlock->s_blocks_count);
|
||||
|
||||
const BYTE* u = m_superBlock->s_uuid;
|
||||
kprintf("fs: UUID: %b%b%b%b-%b%b-%b%b-%b%b-%b%b%b%b%b%b\n",
|
||||
u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
|
||||
u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16]);
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("fs: Block size is %u bytes\n", 1024 << m_superBlock->s_log_frag_size);
|
||||
kprintf("fs: Blocks per group: %u\n", m_superBlock->s_blocks_per_group);
|
||||
#endif
|
||||
|
||||
m_blockGroupCount = m_superBlock->s_blocks_count / m_superBlock->s_blocks_per_group;
|
||||
if ((m_superBlock->s_blocks_count % m_superBlock->s_blocks_per_group) != 0)
|
||||
++m_blockGroupCount;
|
||||
|
||||
m_inodeTables = new ext2_inode*[m_blockGroupCount];
|
||||
memset(m_inodeTables, 0, sizeof(ext2_inode*) * m_blockGroupCount);
|
||||
}
|
||||
|
||||
void Ext2FileSystem::readBlockGroup(DWORD index)
|
||||
{
|
||||
Task::checkSanity("readBlockGroup");
|
||||
DWORD superBlockBI = m_superBlock->s_first_data_block + (m_superBlock->s_blocks_per_group * index);
|
||||
DWORD descriptorTableBI = superBlockBI + 1;
|
||||
//DWORD blockBitmapBI = descriptorTableBI + 1;
|
||||
//DWORD inodeBitmapBI = blockBitmapBI + 1;
|
||||
//DWORD inodeTableBI = inodeBitmapBI + 1;
|
||||
|
||||
auto buffer = readBlocks(descriptorTableBI, 1);
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("Inodes per group = %u\n", superBlock().s_inodes_per_group);
|
||||
kprintf("First data block = %u\n", superBlock().s_first_data_block);
|
||||
#endif
|
||||
|
||||
m_groupTable = (ext2_group_descriptor*)kmalloc(blockSize());
|
||||
memcpy(m_groupTable, buffer->data(), buffer->length());
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("[%u] block bitmap: %u\n", index, m_groupTable[index].bg_block_bitmap);
|
||||
kprintf("[%u] inode bitmap: %u\n", index, m_groupTable[index].bg_inode_bitmap);
|
||||
kprintf("[%u] inode table: %u\n", index, m_groupTable[index].bg_inode_table);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void Ext2FileSystem::traverseDirectory(ext2_dir_entry& firstEntry, DWORD blockCount, F func)
|
||||
{
|
||||
Task::checkSanity("traverseDirectory1");
|
||||
auto* entry = &firstEntry;
|
||||
char* name = new char[EXT2_NAME_LEN + 1];
|
||||
auto* end = (ext2_dir_entry*)((BYTE*)entry + blockCount * blockSize());
|
||||
|
||||
while (entry < end) {
|
||||
if (entry->d_inode != 0) {
|
||||
memcpy(name, entry->d_name, entry->d_name_len);
|
||||
name[entry->d_name_len] = 0;
|
||||
func(name, *entry);
|
||||
}
|
||||
entry = (ext2_dir_entry*)((BYTE *)entry + entry->d_rec_len);
|
||||
}
|
||||
|
||||
delete [] name;
|
||||
}
|
||||
|
||||
void Ext2FileSystem::readInodeTable(DWORD blockGroup)
|
||||
{
|
||||
Task::checkSanity("readInodeTable");
|
||||
ext2_inode*& inodeTable = m_inodeTables[blockGroup];
|
||||
|
||||
if (!inodeTable)
|
||||
inodeTable = new ext2_inode[m_superBlock->s_inodes_per_group];
|
||||
|
||||
DWORD inodeTableBlocks = (m_superBlock->s_inodes_per_group * sizeof(ext2_inode)) / blockSize();
|
||||
// kprintf("inode table blocks: %u\n", inodeTableBlocks);
|
||||
|
||||
auto buffer = readBlocks(m_groupTable[blockGroup].bg_inode_table, inodeTableBlocks);
|
||||
memcpy(inodeTable, buffer->data(), m_superBlock->s_inodes_per_group * sizeof(ext2_inode));
|
||||
m_root = &inodeTable[1];
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("Root directory inode:\n");
|
||||
kprintf("sizeof(ext2_inode): %u\n", sizeof(ext2_inode));
|
||||
kprintf("sizeof(ext2_dir_entry): %u\n", sizeof(ext2_dir_entry));
|
||||
kprintf("Mode: %u, Owner: %u/%u, Size: %u\n", m_root->i_mode, m_root->i_uid, m_root->i_gid, m_root->i_size);
|
||||
|
||||
kprintf("Directory blocks: { ");
|
||||
for (DWORD i = 0; i < 12; ++i) {
|
||||
kprintf( "%u ", m_root->i_block[i] );
|
||||
}
|
||||
kprintf("}\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void Ext2FileSystem::forEachBlockIn(ext2_inode& inode, F func)
|
||||
{
|
||||
Task::checkSanity("forEachBlockIn");
|
||||
DWORD blockCount = inode.i_blocks / (2 << m_superBlock->s_log_block_size);
|
||||
// FIXME: Support indirect blocks
|
||||
for (DWORD i = 0; i < blockCount; ++i) {
|
||||
//kprintf(" [blk %u]\n", inode.i_block[i]);
|
||||
auto buffer = readBlocks(inode.i_block[i], 1);
|
||||
func(move(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
DWORD Ext2FileSystem::blockGroupForInode(DWORD inode) const
|
||||
{
|
||||
// FIXME: Implement
|
||||
(void)inode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD Ext2FileSystem::toInodeTableIndex(DWORD inode) const
|
||||
{
|
||||
// FIXME: Implement
|
||||
return inode - 1;
|
||||
}
|
||||
|
||||
ext2_inode* Ext2FileSystem::findInode(DWORD index)
|
||||
{
|
||||
if (index >= m_superBlock->s_inodes_count)
|
||||
return nullptr;
|
||||
return &m_inodeTables[blockGroupForInode(index)][toInodeTableIndex(index)];
|
||||
}
|
||||
|
||||
ext2_inode* Ext2FileSystem::findPath(const String& path, DWORD& inodeIndex)
|
||||
{
|
||||
Task::checkSanity("findPath entry");
|
||||
ASSERT(m_root);
|
||||
Task::checkSanity("findPath entry2");
|
||||
|
||||
if (path.isEmpty())
|
||||
return nullptr;
|
||||
if (path[0] != '/')
|
||||
return nullptr;
|
||||
|
||||
ext2_inode* dir = m_root;
|
||||
|
||||
Task::checkSanity("findPath pre-vector");
|
||||
Vector<String> pathParts = path.split('/');
|
||||
Task::checkSanity("findPath post-split");
|
||||
|
||||
for (size_t i = 0; i < pathParts.size(); ++i) {
|
||||
//kprintf("[%u] %s\n", i, pathParts[i].characters());
|
||||
auto& part = pathParts[i];
|
||||
bool foundPart = false;
|
||||
//kprintf("looking for part '%s' in inode{%p}\n", part.characters(), dir);
|
||||
traverseDirectory(*dir, [&] (const char* name, ext2_dir_entry& entry) {
|
||||
//kprintf(" ?= %s\n", name);
|
||||
if (String(name) == part) {
|
||||
foundPart = true;
|
||||
//kprintf("found part ==> inode %u (type %b)\n", entry.d_inode, entry.d_file_type);
|
||||
dir = findInode(entry.d_inode);
|
||||
inodeIndex = entry.d_inode;
|
||||
// FIXME: don't try to traverse files as if they're directories
|
||||
// FIXME: need a way to skip the remaining traverseDirectory() callbacks
|
||||
}
|
||||
});
|
||||
if (!foundPart)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void Ext2FileSystem::traverseDirectory(ext2_inode& inode, F func)
|
||||
{
|
||||
Task::checkSanity("traverseDirectory2");
|
||||
//kprintf("in traverseDir\n");
|
||||
forEachBlockIn(inode, [this, &func] (RefPtr<DataBuffer>&& data) {
|
||||
auto* directory = (ext2_dir_entry*)data->data();
|
||||
traverseDirectory<F>(*directory, 1, func);
|
||||
});
|
||||
//kprintf("out traverseDir\n");
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> Ext2FileSystem::readFile(ext2_inode& inode)
|
||||
{
|
||||
auto buffer = DataBuffer::createUninitialized(inode.i_size + 1);
|
||||
BYTE* bufptr = buffer->data();
|
||||
size_t dataRemaining = inode.i_size;
|
||||
forEachBlockIn(inode, [this, &bufptr, &dataRemaining] (RefPtr<DataBuffer>&& data) {
|
||||
memcpy(bufptr, data->data(), min(dataRemaining, data->length()));
|
||||
dataRemaining -= blockSize();
|
||||
bufptr += blockSize();
|
||||
});
|
||||
// HACK: This is silly, but let's just null terminate here for comfort.
|
||||
buffer->data()[buffer->length() - 1] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void Ext2FileSystem::dumpFile(ext2_inode& inode)
|
||||
{
|
||||
auto buffer = readFile(inode);
|
||||
kprintf("%s", buffer->data());
|
||||
}
|
||||
|
||||
void Ext2FileSystem::dumpDirectory(ext2_inode& inode)
|
||||
{
|
||||
traverseDirectory(inode, [this] (const char* name, ext2_dir_entry& entry) {
|
||||
bool isDirectory = entry.d_file_type == EXT2_FT_DIR;
|
||||
ext2_inode& inode = m_inodeTables[blockGroupForInode(entry.d_inode)][toInodeTableIndex(entry.d_inode)];
|
||||
kprintf("i:%x %b %u:%u %x %s%s\n",
|
||||
entry.d_inode,
|
||||
entry.d_file_type,
|
||||
inode.i_uid,
|
||||
inode.i_gid,
|
||||
inode.i_size,
|
||||
name,
|
||||
isDirectory ? "/" : "");
|
||||
});
|
||||
}
|
||||
|
||||
#if 0
|
||||
void Ext2FileSystem::readRoot()
|
||||
{
|
||||
auto buffer = readBlocks(m_root->i_block[0], 1);
|
||||
auto* dir_block = (ext2_dir_entry*)buffer.data();
|
||||
|
||||
traverseDirectory(dir_block, [this] (const char* name, ext2_dir_entry* entry) {
|
||||
if (!strcmp(name, "test2")) {
|
||||
auto test2_entry = loadFile(entry);
|
||||
new Task((void (*)())test2_entry.data(), "test2", IPC::Handle::Any, Task::Ring3);
|
||||
// HACK: Don't delete the code we just started running :)
|
||||
test2_entry.leak();
|
||||
} else if (!strcmp( name, "motd.txt")) {
|
||||
auto motd_txt = loadFile(entry);
|
||||
kprintf("===============================================\n\n");
|
||||
vga_set_attr(0x03);
|
||||
kprintf("%s\n", motd_txt.data());
|
||||
vga_set_attr(0x07);
|
||||
kprintf("===============================================\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
void Ext2FileSystem::initialize()
|
||||
{
|
||||
readSuperBlock();
|
||||
readBlockGroup(0);
|
||||
readInodeTable(0);
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
dumpDirectory(*m_root);
|
||||
#endif
|
||||
|
||||
DWORD inodeIndex;
|
||||
auto* file = findPath("/motd.txt", inodeIndex);
|
||||
dumpFile(*file);
|
||||
}
|
||||
|
||||
RefPtr<DataBuffer> Ext2FileSystem::loadFile(ext2_dir_entry* dirent)
|
||||
{
|
||||
Task::checkSanity("loadFile");
|
||||
DWORD inode_group = (dirent->d_inode - 1) / superBlock().s_inodes_per_group;
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("inode: %u (group %u)\n", dirent->d_inode, inode_group);
|
||||
kprintf("inode table at block %u\n", m_groupTable[inode_group].bg_inode_table);
|
||||
#endif
|
||||
|
||||
// Calculate interesting offset into inode block.
|
||||
DWORD inode_index = (dirent->d_inode - 1) % superBlock().s_inodes_per_group;
|
||||
|
||||
// Load the relevant inode block.
|
||||
auto buffer = readBlocks(m_groupTable[inode_group].bg_inode_table, 4);
|
||||
auto* inode_table = (ext2_inode*)buffer->data();
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("inode index: %u\n", inode_index);
|
||||
#endif
|
||||
|
||||
ext2_inode* inode = &inode_table[inode_index];
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("Mode: %u UID: %u GID: %u Size: %u Block0: %u\n", inode->i_mode, inode->i_uid, inode->i_gid, inode->i_size, inode->i_block[0]);
|
||||
#endif
|
||||
|
||||
auto fileContents = readBlocks(inode->i_block[0], 1);
|
||||
|
||||
#ifdef FS_DEBUG
|
||||
kprintf("File @ %p\n", fileContents->data());
|
||||
kprintf("File contents: %b %b %b %b %b\n",
|
||||
(*fileContents)[0],
|
||||
(*fileContents)[1],
|
||||
(*fileContents)[2],
|
||||
(*fileContents)[3],
|
||||
(*fileContents)[4]);
|
||||
#endif
|
||||
|
||||
return fileContents;
|
||||
}
|
||||
|
||||
RefPtr<Ext2VirtualNode> Ext2VirtualNode::create(DWORD index, String&& path, Ext2FileSystem& fs, DWORD inodeNumber)
|
||||
{
|
||||
Task::checkSanity("enter E2VN::create");
|
||||
ext2_inode* inode = fs.findInode(inodeNumber);
|
||||
Task::checkSanity("post findInode");
|
||||
if (!inode)
|
||||
return nullptr;
|
||||
auto* v = new Ext2VirtualNode(index, move(path), fs, *inode, inodeNumber);
|
||||
kprintf("v=%p\n", v);
|
||||
auto r = adoptRef(v);
|
||||
kprintf("adopted(v)=%p\n", r.ptr());
|
||||
return r;
|
||||
}
|
||||
|
||||
Ext2VirtualNode::Ext2VirtualNode(DWORD index, String&& path, Ext2FileSystem& fs, ext2_inode& inode, DWORD inodeNumber)
|
||||
: VirtualNode(index, move(path))
|
||||
, m_fileSystem(fs)
|
||||
, m_inode(inode)
|
||||
, m_inodeNumber(inodeNumber)
|
||||
{
|
||||
Task::checkSanity("Ext2VirtualNode::Ext2VirtualNode");
|
||||
}
|
||||
|
||||
Ext2VirtualNode::~Ext2VirtualNode()
|
||||
{
|
||||
Task::checkSanity("Ext2VirtualNode::~Ext2VirtualNode");
|
||||
}
|
||||
|
||||
size_t Ext2VirtualNode::read(BYTE* outbuf, size_t start, size_t maxLength)
|
||||
{
|
||||
Task::checkSanity("Ext2VirtualNode::read");
|
||||
kprintf("Ext2VirtualNode::read\n");
|
||||
if (start >= size())
|
||||
return 0;
|
||||
|
||||
auto fileContents = m_fileSystem.readFile(m_inode);
|
||||
if (!fileContents)
|
||||
return 0;
|
||||
ASSERT(start < fileContents->length());
|
||||
size_t nread = min(maxLength, fileContents->length() - start);
|
||||
memcpy(outbuf, fileContents->data(), nread);
|
||||
return nread;
|
||||
}
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
static DWORD nextVNodeIndex;
|
||||
|
||||
void initialize()
|
||||
{
|
||||
nextVNodeIndex = 0;
|
||||
fileSystem = new Ext2FileSystem;
|
||||
fileSystem->initialize();
|
||||
}
|
||||
|
||||
VirtualNode::VirtualNode(DWORD index, String&& path)
|
||||
: m_index(index)
|
||||
, m_path(move(path))
|
||||
{
|
||||
}
|
||||
|
||||
VirtualNode::~VirtualNode()
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<VirtualNode> createVirtualNode(String&& path)
|
||||
{
|
||||
Task::checkSanity("createVirtualNode");
|
||||
DWORD inodeIndex = 0x12345678;
|
||||
Task::checkSanity("pre-findPath");
|
||||
kprintf("path: '%s'\n", path.characters());
|
||||
auto* inode = fileSystem->findPath(path, inodeIndex);
|
||||
Task::checkSanity("post-findPath");
|
||||
if (!inode)
|
||||
return nullptr;
|
||||
kprintf("creating e2vn\n");
|
||||
return Ext2VirtualNode::create(nextVNodeIndex++, move(path), *fileSystem, inodeIndex);
|
||||
}
|
||||
|
||||
}
|
180
Kernel/i386.cpp
Normal file
180
Kernel/i386.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
#include "types.h"
|
||||
#include "kmalloc.h"
|
||||
#include "VGA.h"
|
||||
#include "i386.h"
|
||||
#include "Assertions.h"
|
||||
#include "Task.h"
|
||||
|
||||
struct DescriptorTablePointer {
|
||||
WORD size;
|
||||
void* address;
|
||||
} PACKED;
|
||||
|
||||
static DescriptorTablePointer s_idtr;
|
||||
static DescriptorTablePointer s_gdtr;
|
||||
static Descriptor* s_idt;
|
||||
static Descriptor* s_gdt;
|
||||
|
||||
static WORD s_gdtLength;
|
||||
|
||||
WORD allocateGDTEntry()
|
||||
{
|
||||
// FIXME: This should not grow indefinitely.
|
||||
ASSERT(s_gdtLength < 256);
|
||||
WORD newGDTEntry = s_gdtLength * 8;
|
||||
s_gdtLength++;
|
||||
return newGDTEntry;
|
||||
}
|
||||
|
||||
#define EH(i, msg) \
|
||||
static void _exception ## i () \
|
||||
{ \
|
||||
vga_set_attr(0x0a); \
|
||||
kprintf(msg"\n"); \
|
||||
DWORD cr0, cr2, cr3, cr4; \
|
||||
asm ("movl %%cr0, %%eax":"=a"(cr0)); \
|
||||
asm ("movl %%cr2, %%eax":"=a"(cr2)); \
|
||||
asm ("movl %%cr3, %%eax":"=a"(cr3)); \
|
||||
asm ("movl %%cr4, %%eax":"=a"(cr4)); \
|
||||
kprintf("CR0=%x CR2=%x CR3=%x CR4=%x\n", cr0, cr2, cr3, cr4); \
|
||||
HANG; \
|
||||
}
|
||||
|
||||
EH(0, "Divide error")
|
||||
EH(1, "Debug exception")
|
||||
EH(2, "Unknown error")
|
||||
EH(3, "Breakpoint")
|
||||
EH(4, "Overflow")
|
||||
EH(5, "Bounds check")
|
||||
EH(6, "Invalid opcode")
|
||||
EH(7, "Coprocessor not available")
|
||||
EH(8, "Double fault")
|
||||
EH(9, "Coprocessor segment overrun")
|
||||
EH(10, "Invalid TSS")
|
||||
EH(11, "Segment not present")
|
||||
EH(12, "Stack exception")
|
||||
EH(13, "General protection fault")
|
||||
EH(14, "Page fault")
|
||||
EH(15, "Unknown error")
|
||||
EH(16, "Coprocessor error")
|
||||
|
||||
static void writeRawGDTEntry(WORD selector, DWORD low, DWORD high)
|
||||
{
|
||||
WORD i = (selector & 0xfffc) >> 3;
|
||||
s_gdt[i].low = low;
|
||||
s_gdt[i].high = high;
|
||||
|
||||
if (i > s_gdtLength) {
|
||||
s_gdtr.size = (s_gdtLength + 1) * 8;
|
||||
}
|
||||
}
|
||||
|
||||
void writeGDTEntry(WORD selector, Descriptor& descriptor)
|
||||
{
|
||||
writeRawGDTEntry(selector, descriptor.low, descriptor.high);
|
||||
}
|
||||
|
||||
Descriptor& getGDTEntry(WORD selector)
|
||||
{
|
||||
WORD i = (selector & 0xfffc) >> 3;
|
||||
return *(Descriptor*)(&s_gdt[i]);
|
||||
}
|
||||
|
||||
void flushGDT()
|
||||
{
|
||||
s_gdtr.address = s_gdt;
|
||||
s_gdtr.size = (s_gdtLength * 8) - 1;
|
||||
asm("lgdt %0"::"m"(s_gdtr));
|
||||
}
|
||||
|
||||
void gdt_init()
|
||||
{
|
||||
s_gdt = new Descriptor[256];
|
||||
s_gdtLength = 5;
|
||||
|
||||
s_gdtr.address = s_gdt;
|
||||
s_gdtr.size = (s_gdtLength * 8) - 1;
|
||||
|
||||
writeRawGDTEntry(0x0000, 0x00000000, 0x00000000);
|
||||
writeRawGDTEntry(0x0008, 0x0000ffff, 0x00cf9a00);
|
||||
writeRawGDTEntry(0x0010, 0x0000ffff, 0x00cf9200);
|
||||
writeRawGDTEntry(0x0018, 0x0000ffff, 0x00cffa00);
|
||||
writeRawGDTEntry(0x0020, 0x0000ffff, 0x00cff200);
|
||||
|
||||
flushGDT();
|
||||
}
|
||||
|
||||
static void unimp_trap()
|
||||
{
|
||||
kprintf("Unhandled IRQ.");
|
||||
HANG;
|
||||
}
|
||||
|
||||
void registerInterruptHandler(BYTE index, void (*f)())
|
||||
{
|
||||
s_idt[index].low = 0x00080000 | LSW((f));
|
||||
s_idt[index].high = ((DWORD)(f) & 0xffff0000) | 0x8e00;
|
||||
flushIDT();
|
||||
}
|
||||
|
||||
void registerUserCallableInterruptHandler(BYTE index, void (*f)())
|
||||
{
|
||||
s_idt[index].low = 0x00080000 | LSW((f));
|
||||
s_idt[index].high = ((DWORD)(f) & 0xffff0000) | 0xee00;
|
||||
flushIDT();
|
||||
}
|
||||
|
||||
void flushIDT()
|
||||
{
|
||||
asm("lidt %0"::"m"(s_idtr));
|
||||
}
|
||||
|
||||
/* If an 8259 gets cranky, it'll generate a spurious IRQ7.
|
||||
* ATM I don't have a clear grasp on when/why this happens,
|
||||
* so I ignore them and assume it makes no difference.
|
||||
*/
|
||||
|
||||
extern "C" void irq7_handler();
|
||||
asm(
|
||||
".globl irq7_handler \n"
|
||||
"irq7_handler: \n"
|
||||
" iret\n"
|
||||
);
|
||||
|
||||
void idt_init()
|
||||
{
|
||||
s_idt = new Descriptor[256];
|
||||
|
||||
s_idtr.address = s_idt;
|
||||
s_idtr.size = 0x100 * 8;
|
||||
|
||||
for (BYTE i = 0xff; i > 0x10; --i)
|
||||
registerInterruptHandler(i, unimp_trap);
|
||||
|
||||
registerInterruptHandler(0x00, _exception0);
|
||||
registerInterruptHandler(0x01, _exception1);
|
||||
registerInterruptHandler(0x02, _exception2);
|
||||
registerInterruptHandler(0x03, _exception3);
|
||||
registerInterruptHandler(0x04, _exception4);
|
||||
registerInterruptHandler(0x05, _exception5);
|
||||
registerInterruptHandler(0x06, _exception6);
|
||||
registerInterruptHandler(0x07, _exception7);
|
||||
registerInterruptHandler(0x08, _exception8);
|
||||
registerInterruptHandler(0x09, _exception9);
|
||||
registerInterruptHandler(0x0a, _exception10);
|
||||
registerInterruptHandler(0x0b, _exception11);
|
||||
registerInterruptHandler(0x0c, _exception12);
|
||||
registerInterruptHandler(0x0d, _exception13);
|
||||
registerInterruptHandler(0x0e, _exception14);
|
||||
registerInterruptHandler(0x0f, _exception15);
|
||||
registerInterruptHandler(0x10, _exception16);
|
||||
|
||||
registerInterruptHandler(0x57, irq7_handler);
|
||||
|
||||
flushIDT();
|
||||
}
|
||||
|
||||
void loadTaskRegister(WORD selector)
|
||||
{
|
||||
asm("ltr %0"::"r"(selector));
|
||||
}
|
77
Kernel/i386.h
Normal file
77
Kernel/i386.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
union Descriptor {
|
||||
struct {
|
||||
WORD limit_lo;
|
||||
WORD base_lo;
|
||||
BYTE base_hi;
|
||||
BYTE type : 4;
|
||||
BYTE descriptor_type : 1;
|
||||
BYTE dpl : 2;
|
||||
BYTE segment_present : 1;
|
||||
BYTE limit_hi : 4;
|
||||
BYTE : 1;
|
||||
BYTE zero : 1;
|
||||
BYTE operation_size : 1;
|
||||
BYTE granularity : 1;
|
||||
BYTE base_hi2;
|
||||
};
|
||||
struct {
|
||||
DWORD low;
|
||||
DWORD high;
|
||||
};
|
||||
|
||||
enum Type {
|
||||
Invalid = 0,
|
||||
AvailableTSS_16bit = 0x1,
|
||||
LDT = 0x2,
|
||||
BusyTSS_16bit = 0x3,
|
||||
CallGate_16bit = 0x4,
|
||||
TaskGate = 0x5,
|
||||
InterruptGate_16bit = 0x6,
|
||||
TrapGate_16bit = 0x7,
|
||||
AvailableTSS_32bit = 0x9,
|
||||
BusyTSS_32bit = 0xb,
|
||||
CallGate_32bit = 0xc,
|
||||
InterruptGate_32bit = 0xe,
|
||||
TrapGate_32bit = 0xf,
|
||||
};
|
||||
|
||||
void setBase(void* b)
|
||||
{
|
||||
base_lo = (DWORD)(b) & 0xffff;
|
||||
base_hi = ((DWORD)(b) >> 16) & 0xff;
|
||||
base_hi2 = ((DWORD)(b) >> 24) & 0xff;
|
||||
}
|
||||
|
||||
void setLimit(DWORD l)
|
||||
{
|
||||
limit_lo = (DWORD)l & 0xffff;
|
||||
limit_hi = ((DWORD)l >> 16) & 0xff;
|
||||
}
|
||||
} PACKED;
|
||||
|
||||
void gdt_init();
|
||||
void idt_init();
|
||||
void registerInterruptHandler(BYTE number, void (*f)());
|
||||
void registerUserCallableInterruptHandler(BYTE number, void (*f)());
|
||||
void flushIDT();
|
||||
void flushGDT();
|
||||
void loadTaskRegister(WORD selector);
|
||||
WORD allocateGDTEntry();
|
||||
Descriptor& getGDTEntry(WORD selector);
|
||||
void writeGDTEntry(WORD selector, Descriptor&);
|
||||
|
||||
#define HANG asm volatile( "cli; hlt" );
|
||||
#define LSW(x) ((DWORD)(x) & 0xFFFF)
|
||||
#define MSW(x) (((DWORD)(x) >> 16) & 0xFFFF)
|
||||
#define LSB(x) ((x) & 0xFF)
|
||||
#define MSB(x) (((x)>>8) & 0xFF)
|
||||
|
||||
#define disableInterrupts() asm volatile("cli");
|
||||
#define enableInterrupts() asm volatile("sti");
|
||||
|
||||
/* Map IRQ0-15 @ ISR 0x50-0x5F */
|
||||
#define IRQ_VECTOR_BASE 0x50
|
169
Kernel/i8253.cpp
Normal file
169
Kernel/i8253.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
#include "i8253.h"
|
||||
#include "i386.h"
|
||||
#include "IO.h"
|
||||
#include "VGA.h"
|
||||
#include "Task.h"
|
||||
#include "system.h"
|
||||
#include "PIC.h"
|
||||
|
||||
#define IRQ_TIMER 0
|
||||
|
||||
extern "C" void tick_ISR();
|
||||
extern "C" void clock_handle();
|
||||
|
||||
extern volatile DWORD state_dump;
|
||||
|
||||
asm(
|
||||
".globl tick_ISR \n"
|
||||
".globl state_dump \n"
|
||||
"state_dump: \n"
|
||||
".long 0\n"
|
||||
"tick_ISR: \n"
|
||||
" pusha\n"
|
||||
" pushw %ds\n"
|
||||
" pushw %es\n"
|
||||
" pushw %fs\n"
|
||||
" pushw %gs\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" pushw %ss\n"
|
||||
" popw %ds\n"
|
||||
" popw %es\n"
|
||||
" popw %fs\n"
|
||||
" popw %gs\n"
|
||||
" mov %esp, state_dump\n"
|
||||
" call clock_handle\n"
|
||||
" popw %gs\n"
|
||||
" popw %fs\n"
|
||||
" popw %es\n"
|
||||
" popw %ds\n"
|
||||
" popa\n"
|
||||
" iret\n"
|
||||
);
|
||||
|
||||
/* Timer related ports */
|
||||
#define TIMER0_CTL 0x40
|
||||
#define TIMER1_CTL 0x41
|
||||
#define TIMER2_CTL 0x42
|
||||
#define PIT_CTL 0x43
|
||||
|
||||
/* Building blocks for PIT_CTL */
|
||||
#define TIMER0_SELECT 0x00
|
||||
#define TIMER1_SELECT 0x40
|
||||
#define TIMER2_SELECT 0x80
|
||||
|
||||
#define MODE_COUNTDOWN 0x00
|
||||
#define MODE_ONESHOT 0x02
|
||||
#define MODE_RATE 0x04
|
||||
#define MODE_SQUARE_WAVE 0x06
|
||||
|
||||
#define WRITE_WORD 0x30
|
||||
|
||||
/* Miscellaneous */
|
||||
#define BASE_FREQUENCY 1193182
|
||||
|
||||
struct RegisterDump {
|
||||
WORD gs;
|
||||
WORD fs;
|
||||
WORD es;
|
||||
WORD ds;
|
||||
DWORD edi;
|
||||
DWORD esi;
|
||||
DWORD ebp;
|
||||
DWORD esp;
|
||||
DWORD ebx;
|
||||
DWORD edx;
|
||||
DWORD ecx;
|
||||
DWORD eax;
|
||||
DWORD eip;
|
||||
WORD cs;
|
||||
WORD __csPadding;
|
||||
DWORD eflags;
|
||||
} PACKED;
|
||||
|
||||
void clock_handle()
|
||||
{
|
||||
IRQHandlerScope scope(IRQ_TIMER);
|
||||
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
system.uptime++;
|
||||
|
||||
if (current->tick())
|
||||
return;
|
||||
|
||||
|
||||
auto& regs = *reinterpret_cast<RegisterDump*>(state_dump);
|
||||
current->tss().gs = regs.gs;
|
||||
current->tss().fs = regs.fs;
|
||||
current->tss().es = regs.es;
|
||||
current->tss().ds = regs.ds;
|
||||
current->tss().edi = regs.edi;
|
||||
current->tss().esi = regs.esi;
|
||||
current->tss().ebp = regs.ebp;
|
||||
current->tss().ebx = regs.ebx;
|
||||
current->tss().edx = regs.edx;
|
||||
current->tss().ecx = regs.ecx;
|
||||
current->tss().eax = regs.eax;
|
||||
current->tss().eip = regs.eip;
|
||||
current->tss().cs = regs.cs;
|
||||
current->tss().eflags = regs.eflags;
|
||||
|
||||
#if 0
|
||||
BYTE a = vga_get_attr();
|
||||
WORD foo = vga_get_cursor();
|
||||
|
||||
vga_set_attr(0x50);
|
||||
vga_set_cursor(1600);
|
||||
|
||||
kprintf("Task %u interrupted at %x\n", current->pid(), regs.eip );
|
||||
kprintf("EAX=%x EBX=%x ECX=%x EDX=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
|
||||
kprintf("ESI=%x EDI=%x EBP=%x ESP=%x\n", regs.esi, regs.edi, regs.ebp, regs.esp);
|
||||
kprintf("FLAGS=%x", regs.eflags);
|
||||
|
||||
vga_set_cursor(foo);
|
||||
vga_set_attr(a);
|
||||
#endif
|
||||
|
||||
// Compute task ESP.
|
||||
// Add 12 for CS, EIP, EFLAGS (interrupt mechanic)
|
||||
current->tss().esp = regs.esp + 12;
|
||||
|
||||
// Prepare a new task to run.
|
||||
sched();
|
||||
|
||||
// Set the NT (nested task) flag.
|
||||
// sched() has LTRed a dummy task with a backlink to the next task.
|
||||
// This is probably super slow/stupid, but I'm just learning...
|
||||
asm(
|
||||
"pushf\n"
|
||||
"orl $0x00004000, (%esp)\n"
|
||||
"popf\n"
|
||||
);
|
||||
}
|
||||
|
||||
namespace PIT {
|
||||
|
||||
void initialize()
|
||||
{
|
||||
WORD timer_reload;
|
||||
|
||||
IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_SQUARE_WAVE);
|
||||
|
||||
timer_reload = (BASE_FREQUENCY / TICKS_PER_SECOND);
|
||||
|
||||
/* Send LSB and MSB of timer reload value. */
|
||||
|
||||
kprintf("PIT(i8253): %u Hz, square wave (%x)\n", TICKS_PER_SECOND, timer_reload);
|
||||
|
||||
IO::out8(TIMER0_CTL, LSB(timer_reload));
|
||||
IO::out8(TIMER0_CTL, MSB(timer_reload));
|
||||
|
||||
registerInterruptHandler(IRQ_VECTOR_BASE + IRQ_TIMER, tick_ISR);
|
||||
|
||||
PIC::enable(IRQ_TIMER);
|
||||
}
|
||||
|
||||
}
|
9
Kernel/i8253.h
Normal file
9
Kernel/i8253.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#define TICKS_PER_SECOND 600
|
||||
|
||||
namespace PIT {
|
||||
|
||||
void initialize();
|
||||
|
||||
}
|
135
Kernel/init.cpp
Normal file
135
Kernel/init.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "types.h"
|
||||
#include "VGA.h"
|
||||
#include "kmalloc.h"
|
||||
#include "i386.h"
|
||||
#include "i8253.h"
|
||||
#include "Keyboard.h"
|
||||
#include "Task.h"
|
||||
#include "IPC.h"
|
||||
#include "system.h"
|
||||
#include "Disk.h"
|
||||
#include "PIC.h"
|
||||
#include "StdLib.h"
|
||||
#include "Syscall.h"
|
||||
#include "CMOS.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Userspace.h"
|
||||
|
||||
#if 0
|
||||
/* Keyboard LED disco task ;^) */
|
||||
|
||||
static void led_disco() NORETURN;
|
||||
|
||||
static void led_disco()
|
||||
{
|
||||
BYTE b = 0;
|
||||
for (;;) {
|
||||
sleep(0.5 * TICKS_PER_SECOND);
|
||||
Keyboard::unsetLED((Keyboard::LED)b++);
|
||||
b &= 7;
|
||||
Keyboard::setLED((Keyboard::LED)b);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void motd_main() NORETURN;
|
||||
static void motd_main()
|
||||
{
|
||||
kprintf("Hello in motd_main!\n");
|
||||
int fd = Userspace::open("/test.asm");
|
||||
kprintf("motd: fd=%d\n", fd);
|
||||
ASSERT(fd != -1);
|
||||
DO_SYSCALL_A3(0x2000, 1, 2, 3);
|
||||
kprintf("getuid(): %u\n", Userspace::getuid());
|
||||
auto buffer = DataBuffer::createUninitialized(33);
|
||||
memset(buffer->data(), 0, buffer->length());
|
||||
int nread = Userspace::read(fd, buffer->data(), buffer->length() - 1);
|
||||
kprintf("read(): %d\n", nread);
|
||||
buffer->data()[nread] = 0;
|
||||
kprintf("read(): '%s'\n", buffer->data());
|
||||
for (;;) {
|
||||
//kill(4, 5);
|
||||
sleep(1 * TICKS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
static void user_main() NORETURN;
|
||||
static void user_main()
|
||||
{
|
||||
DO_SYSCALL_A3(0x3000, 2, 3, 4);
|
||||
// Crash ourselves!
|
||||
char* x = reinterpret_cast<char*>(0xbeefbabe);
|
||||
*x = 1;
|
||||
//HANG;
|
||||
for (;;) {
|
||||
// nothing?
|
||||
Userspace::sleep(1 * TICKS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
system_t system;
|
||||
|
||||
void banner()
|
||||
{
|
||||
kprintf("\n");
|
||||
vga_set_attr(0x0a);
|
||||
kprintf(" _____ _ _ \n");
|
||||
vga_set_attr(0x0b);
|
||||
kprintf("| __|___ ___| |_ ___ ___| |_ \n");
|
||||
vga_set_attr(0x0c);
|
||||
kprintf("| | | -_| _| . | -_| _| _|\n");
|
||||
vga_set_attr(0x0d);
|
||||
kprintf("|_____|___|_| |___|___|_| |_| \n");
|
||||
vga_set_attr(0x07);
|
||||
kprintf("\n");
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
disableInterrupts();
|
||||
|
||||
kmalloc_init();
|
||||
vga_init();
|
||||
|
||||
PIC::initialize();
|
||||
gdt_init();
|
||||
idt_init();
|
||||
|
||||
// Anything that registers interrupts goes *after* PIC and IDT for obvious reasons.
|
||||
Syscall::initialize();
|
||||
PIT::initialize();
|
||||
Keyboard::initialize();
|
||||
Task::initialize();
|
||||
|
||||
memset(&system, 0, sizeof(system));
|
||||
|
||||
WORD base_memory = (CMOS::read(0x16) << 8) | CMOS::read(0x15);
|
||||
WORD ext_memory = (CMOS::read(0x18) << 8) | CMOS::read(0x17);
|
||||
|
||||
kprintf("%u kB base memory\n", base_memory);
|
||||
kprintf("%u kB extended memory\n", ext_memory);
|
||||
|
||||
extern void panel_main();
|
||||
|
||||
new Task(0, "KernelTask", IPC::Handle::Any, Task::Ring0);
|
||||
new Task(panel_main, "panel", IPC::Handle::PanelTask, Task::Ring0);
|
||||
|
||||
//new Task(led_disco, "led-disco", IPC::Handle::Any, Task::Ring0);
|
||||
|
||||
sched();
|
||||
enableInterrupts();
|
||||
|
||||
banner();
|
||||
|
||||
Disk::initialize();
|
||||
FileSystem::initialize();
|
||||
|
||||
// new Task(motd_main, "motd", IPC::Handle::MotdTask, Task::Ring0);
|
||||
new Task(user_main, "user", IPC::Handle::UserTask, Task::Ring3);
|
||||
|
||||
// The idle task will spend its eternity here for now.
|
||||
for (;;) {
|
||||
asm("hlt");
|
||||
}
|
||||
}
|
||||
|
169
Kernel/kmalloc.cpp
Normal file
169
Kernel/kmalloc.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Really really *really* Q&D malloc() and free() implementations
|
||||
* just to get going. Don't ever let anyone see this shit. :^)
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
#include "kmalloc.h"
|
||||
#include "StdLib.h"
|
||||
#include "i386.h"
|
||||
#include "VGA.h"
|
||||
#include "system.h"
|
||||
#include "Assertions.h"
|
||||
|
||||
#define SANITIZE_KMALLOC
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD start;
|
||||
DWORD nchunk;
|
||||
} PACKED allocation_t;
|
||||
|
||||
#define CHUNK_SIZE 128
|
||||
#define POOL_SIZE (512 * 1024)
|
||||
|
||||
#define BASE_PHYS 0x200000
|
||||
|
||||
PRIVATE BYTE alloc_map[POOL_SIZE / CHUNK_SIZE / 8];
|
||||
|
||||
DWORD sum_alloc = 0;
|
||||
DWORD sum_free = POOL_SIZE;
|
||||
|
||||
PUBLIC void
|
||||
kmalloc_init()
|
||||
{
|
||||
memset( &alloc_map, 0, sizeof(alloc_map) );
|
||||
memset( (void *)BASE_PHYS, 0, POOL_SIZE );
|
||||
|
||||
sum_alloc = 0;
|
||||
sum_free = POOL_SIZE;
|
||||
}
|
||||
|
||||
PUBLIC void *
|
||||
kmalloc( DWORD size )
|
||||
{
|
||||
DWORD chunks_needed, chunks_here, first_chunk;
|
||||
DWORD real_size;
|
||||
DWORD i, j, k;
|
||||
|
||||
/* We need space for the allocation_t structure at the head of the block. */
|
||||
real_size = size + sizeof(allocation_t);
|
||||
|
||||
if (sum_free < real_size) {
|
||||
kprintf("kmalloc(): PANIC! Out of memory (sucks, dude)\nsum_free=%u, real_size=%x\n", sum_free, real_size);
|
||||
HANG;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
chunks_needed = real_size / CHUNK_SIZE;
|
||||
if( real_size % CHUNK_SIZE )
|
||||
chunks_needed++;
|
||||
|
||||
chunks_here = 0;
|
||||
first_chunk = 0;
|
||||
|
||||
for( i = 0; i < (POOL_SIZE / CHUNK_SIZE / 8); ++i )
|
||||
{
|
||||
for( j = 0; j < 8; ++j )
|
||||
{
|
||||
if( !(alloc_map[i] & (1<<j)) )
|
||||
{
|
||||
if( chunks_here == 0 )
|
||||
{
|
||||
/* Mark where potential allocation starts. */
|
||||
first_chunk = i * 8 + j;
|
||||
}
|
||||
|
||||
chunks_here++;
|
||||
|
||||
if( chunks_here == chunks_needed )
|
||||
{
|
||||
auto* a = (allocation_t *)(BASE_PHYS + (first_chunk * CHUNK_SIZE));
|
||||
BYTE *ptr = (BYTE *)a;
|
||||
ptr += sizeof(allocation_t);
|
||||
a->nchunk = chunks_needed;
|
||||
a->start = first_chunk;
|
||||
|
||||
for( k = first_chunk; k < (first_chunk + chunks_needed); ++k )
|
||||
{
|
||||
alloc_map[k / 8] |= 1 << (k % 8);
|
||||
}
|
||||
|
||||
sum_alloc += a->nchunk * CHUNK_SIZE;
|
||||
sum_free -= a->nchunk * CHUNK_SIZE;
|
||||
#ifdef SANITIZE_KMALLOC
|
||||
memset(ptr, 0xbb, (a->nchunk * CHUNK_SIZE) - sizeof(allocation_t));
|
||||
#endif
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is in use, so restart chunks_here counter. */
|
||||
chunks_here = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kprintf( "kmalloc(): PANIC! Out of memory (no suitable block)" );
|
||||
HANG;
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
PUBLIC void
|
||||
kfree( void *ptr )
|
||||
{
|
||||
if( !ptr )
|
||||
return;
|
||||
|
||||
allocation_t *a = (allocation_t *)((((BYTE *)ptr) - sizeof(allocation_t)));
|
||||
|
||||
#if 0
|
||||
DWORD hdr = (DWORD)a;
|
||||
DWORD mhdr = hdr & ~0x7;
|
||||
kprintf("hdr / mhdr %p / %p\n", hdr, mhdr);
|
||||
ASSERT(hdr == mhdr);
|
||||
#endif
|
||||
|
||||
for (DWORD k = a->start; k < (a->start + a->nchunk); ++k) {
|
||||
alloc_map[k / 8] &= ~(1 << (k % 8));
|
||||
}
|
||||
|
||||
sum_alloc -= a->nchunk * CHUNK_SIZE;
|
||||
sum_free += a->nchunk * CHUNK_SIZE;
|
||||
|
||||
#ifdef SANITIZE_KMALLOC
|
||||
memset(a, 0xaa, a->nchunk * CHUNK_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* operator new(unsigned int size)
|
||||
{
|
||||
return kmalloc(size);
|
||||
}
|
||||
|
||||
void* operator new[](unsigned int size)
|
||||
{
|
||||
return kmalloc(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, unsigned int)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr, unsigned int)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
11
Kernel/kmalloc.h
Normal file
11
Kernel/kmalloc.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
void kmalloc_init();
|
||||
void *kmalloc(DWORD size) __attribute__ ((malloc));
|
||||
void kfree(void*);
|
||||
|
||||
extern DWORD sum_alloc;
|
||||
extern DWORD sum_free;
|
||||
|
||||
inline void* operator new(size_t, void* p) { return p; }
|
||||
inline void* operator new[](size_t, void* p) { return p; }
|
44
Kernel/linker.ld
Normal file
44
Kernel/linker.ld
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* The bootloader will look at this image and start execution at the symbol
|
||||
designated as the entry point. */
|
||||
ENTRY(_start)
|
||||
|
||||
/* Tell where the various sections of the object files will be put in the final
|
||||
kernel image. */
|
||||
SECTIONS
|
||||
{
|
||||
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
|
||||
loaded at by the bootloader. */
|
||||
. = 0x10000;
|
||||
|
||||
/* First put the multiboot header, as it is required to be put very early
|
||||
early in the image or the bootloader won't recognize the file format.
|
||||
Next we'll put the .text section. */
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
_start.o
|
||||
*(.multiboot)
|
||||
*(.text)
|
||||
}
|
||||
|
||||
/* Read-only data. */
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
/* Read-write data (initialized) */
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
|
||||
/* Read-write data (uninitialized) and stack */
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
/* The compiler may produce other sections, by default it will put them in
|
||||
a segment with the same name. Simply add stuff here as needed. */
|
||||
}
|
46
Kernel/panel.cpp
Normal file
46
Kernel/panel.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include "types.h"
|
||||
#include "Task.h"
|
||||
#include "VGA.h"
|
||||
#include "system.h"
|
||||
#include "i386.h"
|
||||
#include "i8253.h"
|
||||
#include "kmalloc.h"
|
||||
|
||||
PUBLIC void panel_main() NORETURN;
|
||||
|
||||
PUBLIC void
|
||||
panel_main()
|
||||
{
|
||||
WORD c;
|
||||
BYTE a;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
c = vga_get_cursor();
|
||||
a = vga_get_attr();
|
||||
|
||||
/* HACK: Avoid getting interrupted while painting since
|
||||
* that could lead to fugly artifacts ;P */
|
||||
disableInterrupts();
|
||||
|
||||
vga_set_attr( 0x17 );
|
||||
vga_set_cursor( 80 * 24 );
|
||||
|
||||
kprintf(
|
||||
" Uptime: %u -- %u tasks (%u blocked) kmalloc: %u/%u ",
|
||||
system.uptime / TICKS_PER_SECOND,
|
||||
system.nprocess,
|
||||
system.nblocked,
|
||||
sum_alloc,
|
||||
sum_free
|
||||
);
|
||||
|
||||
vga_set_attr( a );
|
||||
vga_set_cursor( c );
|
||||
|
||||
/* HACK cont.d */
|
||||
enableInterrupts();
|
||||
|
||||
sleep( 1 * TICKS_PER_SECOND );
|
||||
}
|
||||
}
|
5
Kernel/run
Executable file
5
Kernel/run
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
#bochs -q -f .bochs.conf
|
||||
|
||||
qemu-system-i386 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents $@
|
12
Kernel/system.h
Normal file
12
Kernel/system.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct system_t
|
||||
{
|
||||
time_t uptime;
|
||||
DWORD nprocess;
|
||||
DWORD nblocked;
|
||||
};
|
||||
|
||||
extern system_t system;
|
65
Kernel/types.h
Normal file
65
Kernel/types.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#define PACKED __attribute__ ((packed))
|
||||
#define NORETURN __attribute__ ((noreturn))
|
||||
#define PURE __attribute__ ((pure))
|
||||
#define PUBLIC
|
||||
#define PRIVATE static
|
||||
|
||||
template <typename T>
|
||||
T&& move(T& arg)
|
||||
{
|
||||
return static_cast<T&&>(arg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T min(T a, T b)
|
||||
{
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T max(T a, T b)
|
||||
{
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void swap(T& a, U& b)
|
||||
{
|
||||
U tmp = move((U&)a);
|
||||
a = (T&&)move(b);
|
||||
b = move(tmp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct identity {
|
||||
typedef T type;
|
||||
};
|
||||
template<typename T>
|
||||
T&& forward(typename identity<T>::type&& param)
|
||||
{ return static_cast<typename identity<T>::type&&>(param); }
|
||||
|
||||
template<typename T, typename U>
|
||||
T exchange(T& a, U&& b)
|
||||
{
|
||||
T tmp = move(a);
|
||||
a = move(b);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned int DWORD;
|
||||
|
||||
typedef DWORD __u32;
|
||||
typedef WORD __u16;
|
||||
typedef BYTE __u8;
|
||||
typedef int __s32;
|
||||
typedef short __s16;
|
||||
|
||||
typedef DWORD uid_t;
|
||||
typedef DWORD gid_t;
|
||||
typedef int pid_t;
|
||||
typedef DWORD time_t;
|
||||
typedef DWORD size_t;
|
Loading…
Reference in a new issue