1
0
Fork 0
mirror of https://gitlab.com/sortix/sortix.git synced 2023-02-13 20:55:38 -05:00

Rename Sortix kernel directory to kernel.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-12-20 01:24:47 +01:00
parent 18d2695439
commit 98a87fa1e5
228 changed files with 10 additions and 10 deletions

109
kernel/x86-family/cmos.cpp Normal file
View file

@ -0,0 +1,109 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/cmos.cpp
Provides access to CMOS and the real-time clock.
*******************************************************************************/
#include <sys/types.h>
#include <stdint.h>
#include <time.h>
#include <timespec.h>
#include <sortix/clock.h>
#include <sortix/kernel/clock.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/time.h>
namespace Sortix {
namespace CMOS {
const uint16_t CMOS_ADDRESS_REG = 0x70;
const uint16_t CMOS_DATA_REG = 0x71;
uint8_t ReadRTC(uint8_t reg)
{
CPU::OutPortB(CMOS_ADDRESS_REG, reg);
return CPU::InPortB(CMOS_DATA_REG);
}
bool IsRTCUpdateInProgress()
{
return ReadRTC(0x0A) & 0x80;
}
uint8_t DecodeBCD(uint8_t bcd)
{
return bcd / 16 * 10 + bcd % 16;
}
void Init()
{
while ( !IsRTCUpdateInProgress() );
while ( IsRTCUpdateInProgress() );
uint8_t second = ReadRTC(0x00);
uint8_t minute = ReadRTC(0x02);
uint8_t hour = ReadRTC(0x04);
uint8_t day = ReadRTC(0x07);
uint8_t month = ReadRTC(0x08);
uint8_t year = ReadRTC(0x09);
uint8_t century = ReadRTC(0x32);
uint8_t reg_b = ReadRTC(0x0B);
bool hour12 = !(reg_b & 0x02);
bool is_pm = hour12 && hour & 0x80;
if ( hour12 )
hour &= 0x7F;
if ( !(reg_b & 0x04) )
{
second = DecodeBCD(second);
minute = DecodeBCD(minute);
hour = DecodeBCD(hour);
day = DecodeBCD(day);
month = DecodeBCD(month);
year = DecodeBCD(year);
century = DecodeBCD(century);
}
if ( hour12 && is_pm )
hour = (hour + 12) % 24;
time_t full_year = century * 100 + year;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_sec = second;
tm.tm_min = minute;
tm.tm_hour = hour;
tm.tm_mday = day ;
tm.tm_mon = month - 1;
tm.tm_year = full_year - 1900;
time_t now = timegm(&tm);
struct timespec current_time = timespec_make(now, 0);
Time::GetClock(CLOCK_REALTIME)->Set(&current_time, NULL);
}
} // namespace CMOS
} // namespace Sortix

59
kernel/x86-family/cmos.h Normal file
View file

@ -0,0 +1,59 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/cmos.h
Provides access to CMOS and the real-time clock.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_CMOS_H
#define SORTIX_X86_FAMILY_CMOS_H
#include <sortix/timespec.h>
namespace Sortix {
namespace CMOS {
uint8_t Read(uint8_t reg);
uint8_t Write(uint8_t reg, uint8_t val);
struct cmos_tm
{
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t weekday; /* Supposedly not reliably set in CMOS. */
uint8_t day_of_month;
uint8_t month;
uint8_t year;
uint8_t century;
};
struct timespec DecodeTime(struct cmos_tm time);
struct cmos_tm EncodeTIme(struct timespec time);
struct cmos_tm GetTime();
void SetTime(struct cmos_tm time);
void Init();
} // namespace CMOS
} // namespace Sortix
#endif

View file

@ -0,0 +1,88 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/float.cpp
Handles saving and restoration of floating point numbers.
*******************************************************************************/
#include <assert.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/thread.h>
#include "float.h"
namespace Sortix {
namespace Float {
static Thread* fputhread;
static inline void InitFPU()
{
asm volatile ("fninit");
}
static inline void SaveState(uint8_t* dest)
{
assert( (((unsigned long) dest) & (16UL-1UL)) == 0 );
asm volatile ("fxsave (%0)" : : "r"(dest));
}
static inline void LoadState(const uint8_t* src)
{
assert( (((unsigned long) src) & (16UL-1UL)) == 0 );
asm volatile ("fxrstor (%0)" : : "r"(src));
}
static void OnFPUAccess(CPU::InterruptRegisters* /*regs*/, void* /*user*/)
{
EnableFPU();
Thread* thread = CurrentThread();
if ( thread == fputhread )
return;
if ( fputhread )
SaveState(fputhread->fpuenvaligned);
fputhread = thread;
if ( !thread->fpuinitialized )
{
InitFPU();
thread->fpuinitialized = true;
return;
}
LoadState(thread->fpuenvaligned);
}
void Init()
{
fputhread = CurrentThread();
assert(fputhread);
Interrupt::RegisterHandler(7, OnFPUAccess, NULL);
}
void NofityTaskExit(Thread* thread)
{
if ( fputhread == thread )
fputhread = NULL;
DisableFPU();
}
} // namespace Float
} // namespace Sortix

58
kernel/x86-family/float.h Normal file
View file

@ -0,0 +1,58 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/float.h
Handles saving and restoration of floating point numbers.
*******************************************************************************/
#ifndef SORTIX_FLOAT_H
#define SORTIX_FLOAT_H
namespace Sortix {
class Thread;
namespace Float {
void Init();
void NofityTaskExit(Thread* thread);
static inline void EnableFPU()
{
asm volatile ("clts");
}
static inline void DisableFPU()
{
unsigned long cr0;
asm volatile ("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 1UL<<3UL;
asm volatile ("mov %0, %%cr0" : : "r"(cr0));
}
static inline void NotityTaskSwitch()
{
DisableFPU();
}
} // namespace Float
} // namespace Sortix
#endif

254
kernel/x86-family/gdt.cpp Normal file
View file

@ -0,0 +1,254 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/gdt.cpp
Initializes and handles the GDT and TSS.
*******************************************************************************/
#include <stdint.h>
#include <string.h>
#include <sortix/kernel/cpu.h>
#include "gdt.h"
namespace Sortix {
namespace GDT {
struct gdt_entry
{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
};
struct gdt_entry64
{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
uint32_t base_highest;
uint32_t reserved0;
} __attribute__((packed));
struct gdt_ptr
{
uint16_t limit;
#if defined(__i386__)
uint32_t base;
#else
uint64_t base;
#endif
} __attribute__((packed));
#if defined(__i386__)
struct tss_entry
{
uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list.
uint32_t esp0; // The stack pointer to load when we change to kernel mode.
uint32_t ss0; // The stack segment to load when we change to kernel mode.
uint32_t esp1; // Unused...
uint32_t ss1;
uint32_t esp2;
uint32_t ss2;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint32_t es; // The value to load into ES when we change to kernel mode.
uint32_t cs; // The value to load into CS when we change to kernel mode.
uint32_t ss; // The value to load into SS when we change to kernel mode.
uint32_t ds; // The value to load into DS when we change to kernel mode.
uint32_t fs; // The value to load into FS when we change to kernel mode.
uint32_t gs; // The value to load into GS when we change to kernel mode.
uint32_t ldt; // Unused...
uint16_t trap;
uint16_t iomap_base;
} __attribute__((packed));
#elif defined(__x86_64__)
struct tss_entry
{
uint32_t reserved0;
uint64_t stack0;
uint64_t stack1;
uint64_t stack2;
uint64_t reserved2;
uint64_t ist[7];
uint64_t reserved3;
uint16_t reserved4;
uint16_t iomap_base;
} __attribute__((packed));
#endif
const size_t GDT_NUM_ENTRIES = 7;
static struct gdt_entry gdt_entries[GDT_NUM_ENTRIES];
static struct tss_entry tss_entry;
const uint8_t GRAN_64_BIT_MODE = 1 << 5;
const uint8_t GRAN_32_BIT_MODE = 1 << 6;
const uint8_t GRAN_4KIB_BLOCKS = 1 << 7;
void SetGate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
{
struct gdt_entry* entry = (struct gdt_entry*) &gdt_entries[num];
entry->base_low = base >> 0 & 0xFFFF;
entry->base_middle = base >> 16 & 0xFF;
entry->base_high = base >> 24 & 0xFF;
entry->limit_low = limit & 0xFFFF;
entry->granularity = (limit >> 16 & 0x0F) | (gran & 0xF0);
entry->access = access;
}
void SetGate64(int32_t num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran)
{
struct gdt_entry64* entry = (struct gdt_entry64*) &gdt_entries[num];
entry->base_low = base >> 0 & 0xFFFF;
entry->base_middle = base >> 16 & 0xFF;
entry->base_high = base >> 24 & 0xFF;
entry->base_highest = base >> 32;
entry->limit_low = limit & 0xFFFF;
entry->granularity = (limit >> 16 & 0x0F) | (gran & 0xF0);
entry->access = access;
entry->reserved0 = 0;
}
void Init()
{
#if defined(__i386__)
const uint8_t gran = GRAN_4KIB_BLOCKS | GRAN_32_BIT_MODE;
#elif defined(__x86_64__)
const uint8_t gran = GRAN_4KIB_BLOCKS | GRAN_64_BIT_MODE;
#endif
SetGate(0, 0, 0, 0, 0); // Null segment
SetGate(1, 0, 0xFFFFFFFF, 0x9A, gran); // Code segment
SetGate(2, 0, 0xFFFFFFFF, 0x92, gran); // Data segment
SetGate(3, 0, 0xFFFFFFFF, 0xFA, gran); // User mode code segment
SetGate(4, 0, 0xFFFFFFFF, 0xF2, gran); // User mode data segment
WriteTSS(5, 0x10, 0x0);
// Reload the Global Descriptor Table.
volatile struct gdt_ptr gdt_ptr;
gdt_ptr.limit = (sizeof(struct gdt_entry) * GDT_NUM_ENTRIES) - 1;
gdt_ptr.base = (uintptr_t) &gdt_entries;
asm volatile ("lgdt (%0)" : : "r"(&gdt_ptr));
// Switch the current data segment.
asm volatile ("mov %0, %%ds\n"
"mov %0, %%es\n"
"mov %0, %%fs\n"
"mov %0, %%gs\n"
"mov %0, %%ss\n" : :
"r"(KDS));
// Switch the current code segment.
#if defined(__i386__)
asm volatile ("push %0\n"
"push $1f\n"
"retf\n"
"1:\n" : :
"r"(KCS));
#elif defined(__x86_64__)
asm volatile ("push %0\n"
"push $1f\n"
"retfq\n"
"1:\n" : :
"r"(KCS));
#endif
// Load the task state register - The index is 0x28, as it is the 5th
// selector and each is 8 bytes long, but we set the bottom two bits (making
// 0x2B) so that it has an RPL of 3, not zero.
asm volatile ("ltr %%ax" : : "a"(0x2B));
}
// Initialise our task state segment structure.
void WriteTSS(int32_t num, uint16_t ss0, uintptr_t stack0)
{
// First, let's compute the base and limit of our entry in the GDT.
uintptr_t base = (uintptr_t) &tss_entry;
uint32_t limit = base + sizeof(tss_entry);
// Now, add our TSS descriptor's address to the GDT.
#if defined(__i386__)
SetGate(num, base, limit, 0xE9, 0x00);
#elif defined(__x86_64__)
SetGate64(num, base, limit, 0xE9, 0x00);
#endif
// Ensure the descriptor is initially zero.
memset(&tss_entry, 0, sizeof(tss_entry));
#if defined(__i386__)
tss_entry.ss0 = ss0; // Set the kernel stack segment.
tss_entry.esp0 = stack0; // Set the kernel stack pointer.
// Here we set the cs, ss, ds, es, fs and gs entries in the TSS.
// These specify what segments should be loaded when the processor
// switches to kernel mode. Therefore they are just our normal
// kernel code/data segments - 0x08 and 0x10 respectively, but with
// the last two bits set, making 0x0b and 0x13. The setting of these
// bits sets the RPL (requested privilege level) to 3, meaning that
// this TSS can be used to switch to kernel mode from ring 3.
tss_entry.cs = KCS | 0x3;
tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = KDS | 0x3;
#elif defined(__x86_64__)
(void) ss0;
tss_entry.stack0 = stack0;
#endif
}
void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhigher)
{
#if defined(__i386__)
(void) stacklower;
(void) stacksize;
tss_entry.esp0 = (uint32_t) stackhigher;
#elif defined(__x86_64__)
(void) stacklower;
(void) stacksize;
tss_entry.stack0 = (uint64_t) stackhigher;
#endif
}
} // namespace GDT
} // namespace Sortix

38
kernel/x86-family/gdt.h Normal file
View file

@ -0,0 +1,38 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/gdt.h
Initializes and handles the GDT and TSS.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_GDT_H
#define SORTIX_X86_FAMILY_GDT_H
namespace Sortix {
namespace GDT {
void Init();
void WriteTSS(int32_t num, uint16_t ss0, uintptr_t stack0);
void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhigher);
} // namespace GDT
} // namespace Sortix
#endif

81
kernel/x86-family/idt.cpp Normal file
View file

@ -0,0 +1,81 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/idt.cpp
Initializes and handles the interrupt descriptor table.
*******************************************************************************/
#include <stdint.h>
#include <string.h>
#include "idt.h"
namespace Sortix {
namespace IDT {
struct idt_entry
{
uint16_t handler_low;
uint16_t sel;
uint8_t reserved0;
uint8_t flags;
uint16_t handler_high;
#if defined(__x86_64__)
uint32_t handler_highest;
uint32_t reserved1;
#endif
};
struct idt_ptr
{
uint16_t limit;
#if defined(__x86_64__)
uint64_t idt_ptr;
#else
uint32_t idt_ptr;
#endif
} __attribute__((packed));
static struct idt_entry idt_entries[256];
void Init()
{
volatile struct idt_ptr ptr;
ptr.limit = sizeof(idt_entries) - 1;
ptr.idt_ptr = (unsigned long) &idt_entries;
asm volatile ("lidt (%0)" : : "r"(&ptr));
memset(&idt_entries, 0, sizeof(idt_entries));
}
void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags)
{
idt_entries[num].flags = flags;
idt_entries[num].reserved0 = 0;
idt_entries[num].sel = sel;
idt_entries[num].handler_low = handler >> 0 & 0xFFFF;
idt_entries[num].handler_high = handler >> 16 & 0xFFFF;
#if defined(__x86_64__)
idt_entries[num].handler_highest = handler >> 32 & 0xFFFFFFFFU;
idt_entries[num].reserved1 = 0;
#endif
}
} // namespace IDT
} // namespace Sortix

38
kernel/x86-family/idt.h Normal file
View file

@ -0,0 +1,38 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/idt.h
Initializes and handles the IDT.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_IDT_H
#define SORTIX_X86_FAMILY_IDT_H
namespace Sortix {
namespace IDT {
void Init();
void SetEntry(uint8_t num, uintptr_t handler, uint16_t sel, uint8_t flags);
void Flush();
} // namespace IDT
} // namespace Sortix
#endif

View file

@ -0,0 +1,770 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/memorymanagement.cpp
Handles memory for the x86 family of architectures.
*******************************************************************************/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sortix/mman.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/panic.h>
#include <sortix/kernel/syscall.h>
#include "multiboot.h"
#include "memorymanagement.h"
#include "msr.h"
namespace Sortix
{
extern size_t end;
namespace Page
{
void InitPushRegion(addr_t position, size_t length);
size_t pagesnotonstack;
size_t stackused;
size_t stackreserved;
size_t stacklength;
size_t totalmem;
kthread_mutex_t pagelock;
}
namespace Memory
{
addr_t currentdir = 0;
void InitCPU();
void AllocateKernelPMLs();
int SysMemStat(size_t* memused, size_t* memtotal);
addr_t PAT2PMLFlags[PAT_NUM];
void InitCPU(multiboot_info_t* bootinfo)
{
const size_t MAXKERNELEND = 0x400000UL; /* 4 MiB */
addr_t kernelend = Page::AlignUp((addr_t) &end);
if ( MAXKERNELEND < kernelend )
{
Log::PrintF("Warning: The kernel is too big! It ends at 0x%zx, "
"but the highest ending address supported is 0x%zx. "
"The system may not boot correctly.\n", kernelend,
MAXKERNELEND);
}
Page::stackreserved = 0;
Page::pagesnotonstack = 0;
Page::totalmem = 0;
Page::pagelock = KTHREAD_MUTEX_INITIALIZER;
if ( !( bootinfo->flags & MULTIBOOT_INFO_MEM_MAP ) )
{
Panic("memorymanagement.cpp: The memory map flag was't set in "
"the multiboot structure. Are your bootloader multiboot "
"specification compliant?");
}
// If supported, setup the Page Attribute Table feature that allows
// us to control the memory type (caching) of memory more precisely.
if ( MSR::IsPATSupported() )
{
MSR::InitializePAT();
for ( addr_t i = 0; i < PAT_NUM; i++ )
PAT2PMLFlags[i] = EncodePATAsPMLFlag(i);
}
// Otherwise, reroute all requests to the backwards compatible
// scheme. TODO: Not all early 32-bit x86 CPUs supports these
// values, so we need yet another fallback.
else
{
PAT2PMLFlags[PAT_UC] = PML_WRTHROUGH | PML_NOCACHE;
PAT2PMLFlags[PAT_WC] = PML_WRTHROUGH | PML_NOCACHE; // Approx.
PAT2PMLFlags[2] = 0; // No such flag.
PAT2PMLFlags[3] = 0; // No such flag.
PAT2PMLFlags[PAT_WT] = PML_WRTHROUGH;
PAT2PMLFlags[PAT_WP] = PML_WRTHROUGH; // Approx.
PAT2PMLFlags[PAT_WB] = 0;
PAT2PMLFlags[PAT_UCM] = PML_NOCACHE;
}
// Initialize CPU-specific things.
InitCPU();
typedef const multiboot_memory_map_t* mmap_t;
// Loop over every detected memory region.
for (
mmap_t mmap = (mmap_t) (addr_t) bootinfo->mmap_addr;
(addr_t) mmap < bootinfo->mmap_addr + bootinfo->mmap_length;
mmap = (mmap_t) ((addr_t) mmap + mmap->size + sizeof(mmap->size))
)
{
// Check that we can use this kind of RAM.
if ( mmap->type != 1 ) { continue; }
// The kernel's code may split this memory area into multiple pieces.
addr_t base = (addr_t) mmap->addr;
size_t length = Page::AlignDown(mmap->len);
#if defined(__i386__)
// Figure out if the memory area is addressable (are our pointers big enough?)
if ( 0xFFFFFFFFULL < mmap->addr ) { continue; }
if ( 0xFFFFFFFFULL < mmap->addr + mmap->len ) { length = 0x100000000ULL - mmap->addr; }
#endif
// Count the amount of usable RAM (even if reserved for kernel).
Page::totalmem += length;
// Give all the physical memory to the physical memory allocator
// but make sure not to give it things we already use.
addr_t regionstart = base;
addr_t regionend = base + length;
addr_t processed = regionstart;
while ( processed < regionend )
{
addr_t lowest = processed;
addr_t highest = regionend;
// Don't allocate the kernel.
if ( lowest < kernelend ) { processed = kernelend; continue; }
// Don't give any of our modules to the physical page
// allocator, we'll need them.
bool continuing = false;
uint32_t* modules = (uint32_t*) (addr_t) bootinfo->mods_addr;
for ( uint32_t i = 0; i < bootinfo->mods_count; i++ )
{
size_t modsize = (size_t) (modules[2*i+1] - modules[2*i+0]);
addr_t modstart = (addr_t) modules[2*i+0];
addr_t modend = modstart + modsize;
if ( modstart <= processed && processed < modend )
{
processed = modend;
continuing = true;
break;
}
if ( lowest <= modstart && modstart < highest )
{
highest = modstart;
}
}
if ( continuing ) { continue; }
if ( highest <= lowest ) { break; }
// Now that we have a continious area not used by anything,
// let's forward it to the physical page allocator.
lowest = Page::AlignUp(lowest);
highest = Page::AlignUp(highest);
size_t size = highest - lowest;
Page::InitPushRegion(lowest, size);
processed = highest;
}
}
// If the physical allocator couldn't handle the vast amount of
// physical pages, it may decide to drop some. This shouldn't happen
// until the pebibyte era of RAM.
if ( 0 < Page::pagesnotonstack )
{
Log::PrintF("%zu bytes of RAM aren't used due to technical "
"restrictions.\n", Page::pagesnotonstack * 0x1000UL);
}
Memory::Unmap(0x0); // Remove NULL.
// Finish allocating the top level PMLs for the kernels use.
AllocateKernelPMLs();
}
void Statistics(size_t* amountused, size_t* totalmem)
{
size_t memfree = (Page::stackused - Page::stackreserved) << 12UL;
size_t memused = Page::totalmem - memfree;
if ( amountused ) { *amountused = memused; }
if ( totalmem ) { *totalmem = Page::totalmem; }
}
// Prepare the non-forkable kernel PMLs such that forking the kernel
// address space will always keep the kernel mapped.
void AllocateKernelPMLs()
{
const addr_t flags = PML_PRESENT | PML_WRITABLE;
PML* const pml = PMLS[TOPPMLLEVEL];
size_t start = ENTRIES / 2;
size_t end = ENTRIES;
for ( size_t i = start; i < end; i++ )
{
if ( pml->entry[i] & PML_PRESENT ) { continue; }
addr_t page = Page::Get();
if ( !page ) { Panic("out of memory allocating boot PMLs"); }
pml->entry[i] = page | flags;
// Invalidate the new PML and reset it to zeroes.
addr_t pmladdr = (addr_t) (PMLS[TOPPMLLEVEL-1] + i);
InvalidatePage(pmladdr);
memset((void*) pmladdr, 0, sizeof(PML));
}
}
}
namespace Page
{
void ExtendStack()
{
// This call will always succeed, if it didn't, then the stack
// wouldn't be full, and thus this function won't be called.
addr_t page = GetUnlocked();
// This call will also succeed, since there are plenty of physical
// pages available and it might need some.
addr_t virt = (addr_t) (STACK + stacklength);
if ( !Memory::Map(page, virt, PROT_KREAD | PROT_KWRITE) )
{
Panic("Unable to extend page stack, which should have worked");
}
// TODO: This may not be needed during the boot process!
//Memory::InvalidatePage((addr_t) (STACK + stacklength));
stacklength += 4096UL / sizeof(addr_t);
}
void InitPushRegion(addr_t position, size_t length)
{
// Align our entries on page boundaries.
addr_t newposition = Page::AlignUp(position);
length = Page::AlignDown((position + length) - newposition);
position = newposition;
while ( length )
{
if ( unlikely(stackused == stacklength) )
{
if ( stackused == MAXSTACKLENGTH )
{
pagesnotonstack += length / 4096UL;
return;
}
ExtendStack();
}
addr_t* stackentry = &(STACK[stackused++]);
*stackentry = position;
length -= 4096UL;
position += 4096UL;
}
}
bool ReserveUnlocked(size_t* counter, size_t least, size_t ideal)
{
assert(least < ideal);
size_t available = stackused - stackreserved;
if ( least < available ) { errno = ENOMEM; return false; }
if ( available < ideal ) { ideal = available; }
stackreserved += ideal;
*counter += ideal;
return true;
}
bool Reserve(size_t* counter, size_t least, size_t ideal)
{
ScopedLock lock(&pagelock);
return ReserveUnlocked(counter, least, ideal);
}
bool ReserveUnlocked(size_t* counter, size_t amount)
{
return ReserveUnlocked(counter, amount, amount);
}
bool Reserve(size_t* counter, size_t amount)
{
ScopedLock lock(&pagelock);
return ReserveUnlocked(counter, amount);
}
addr_t GetReservedUnlocked(size_t* counter)
{
if ( !*counter ) { return 0; }
assert(stackused); // After all, we did _reserve_ the memory.
addr_t result = STACK[--stackused];
assert(result == AlignDown(result));
stackreserved--;
(*counter)--;
return result;
}
addr_t GetReserved(size_t* counter)
{
ScopedLock lock(&pagelock);
return GetReservedUnlocked(counter);
}
addr_t GetUnlocked()
{
assert(stackreserved <= stackused);
if ( unlikely(stackreserved == stackused) )
{
errno = ENOMEM;
return 0;
}
addr_t result = STACK[--stackused];
assert(result == AlignDown(result));
return result;
}
addr_t Get()
{
ScopedLock lock(&pagelock);
return GetUnlocked();
}
void PutUnlocked(addr_t page)
{
assert(page == AlignDown(page));
if ( unlikely(stackused == stacklength) )
{
if ( stackused == MAXSTACKLENGTH )
{
pagesnotonstack++;
return;
}
ExtendStack();
}
STACK[stackused++] = page;
}
void Put(addr_t page)
{
ScopedLock lock(&pagelock);
PutUnlocked(page);
}
void Lock()
{
kthread_mutex_lock(&pagelock);
}
void Unlock()
{
kthread_mutex_unlock(&pagelock);
}
}
namespace Memory
{
addr_t ProtectionToPMLFlags(int prot)
{
addr_t result = 0;
if ( prot & PROT_EXEC ) { result |= PML_USERSPACE; }
if ( prot & PROT_READ ) { result |= PML_USERSPACE; }
if ( prot & PROT_WRITE ) { result |= PML_USERSPACE | PML_WRITABLE; }
if ( prot & PROT_KEXEC ) { result |= 0; }
if ( prot & PROT_KREAD ) { result |= 0; }
if ( prot & PROT_KWRITE ) { result |= 0; }
if ( prot & PROT_FORK ) { result |= PML_FORK; }
return result;
}
int PMLFlagsToProtection(addr_t flags)
{
int prot = PROT_KREAD | PROT_KWRITE | PROT_KEXEC;
bool user = flags & PML_USERSPACE;
bool write = flags & PML_WRITABLE;
if ( user ) { prot |= PROT_EXEC | PROT_READ; }
if ( user && write ) { prot |= PROT_WRITE; }
return prot;
}
int ProvidedProtection(int prot)
{
addr_t flags = ProtectionToPMLFlags(prot);
return PMLFlagsToProtection(flags);
}
bool LookUp(addr_t mapto, addr_t* physical, int* protection)
{
// Translate the virtual address into PML indexes.
const size_t MASK = (1<<TRANSBITS)-1;
size_t pmlchildid[TOPPMLLEVEL + 1];
for ( size_t i = 1; i <= TOPPMLLEVEL; i++ )
{
pmlchildid[i] = (mapto >> (12+(i-1)*TRANSBITS)) & MASK;
}
int prot = PROT_USER | PROT_KERNEL | PROT_FORK;
// For each PML level, make sure it exists.
size_t offset = 0;
for ( size_t i = TOPPMLLEVEL; i > 1; i-- )
{
size_t childid = pmlchildid[i];
PML* pml = PMLS[i] + offset;
addr_t entry = pml->entry[childid];
if ( !(entry & PML_PRESENT) ) { return false; }
int entryflags = entry & PML_ADDRESS;
int entryprot = PMLFlagsToProtection(entryflags);
prot &= entryprot;
// Find the index of the next PML in the fractal mapped memory.
offset = offset * ENTRIES + childid;
}
addr_t entry = (PMLS[1] + offset)->entry[pmlchildid[1]];
if ( !(entry & PML_PRESENT) ) { return false; }
int entryflags = entry & PML_ADDRESS;
int entryprot = PMLFlagsToProtection(entryflags);
prot &= entryprot;
addr_t phys = entry & PML_ADDRESS;
if ( physical ) { *physical = phys; }
if ( protection ) { *protection = prot; }
return true;
}
void InvalidatePage(addr_t /*addr*/)
{
// TODO: Actually just call the instruction.
Flush();
}
// Flushes the Translation Lookaside Buffer (TLB).
void Flush()
{
asm volatile("mov %0, %%cr3":: "r"(currentdir));
}
addr_t GetAddressSpace()
{
return currentdir;
}
addr_t SwitchAddressSpace(addr_t addrspace)
{
// Have fun debugging this.
if ( currentdir != Page::AlignDown(currentdir) )
{
PanicF("The variable containing the current address space "
"contains garbage all of sudden: it isn't page-aligned. "
"It contains the value 0x%zx.", currentdir);
}
// Don't switch if we are already there.
if ( addrspace == currentdir ) { return currentdir; }
if ( addrspace & 0xFFFUL ) { PanicF("addrspace 0x%zx was not page-aligned!", addrspace); }
addr_t previous = currentdir;
// Switch and flush the TLB.
asm volatile("mov %0, %%cr3":: "r"(addrspace));
currentdir = addrspace;
return previous;
}
bool MapRange(addr_t where, size_t bytes, int protection)
{
for ( addr_t page = where; page < where + bytes; page += 4096UL )
{
addr_t physicalpage = Page::Get();
if ( physicalpage == 0 )
{
while ( where < page )
{
page -= 4096UL;
physicalpage = Unmap(page);
Page::Put(physicalpage);
}
return false;
}
Map(physicalpage, page, protection);
}
return true;
}
bool UnmapRange(addr_t where, size_t bytes)
{
for ( addr_t page = where; page < where + bytes; page += 4096UL )
{
addr_t physicalpage = Unmap(page);
Page::Put(physicalpage);
}
return true;
}
static bool MapInternal(addr_t physical, addr_t mapto, int prot, addr_t extraflags = 0)
{
addr_t flags = ProtectionToPMLFlags(prot) | PML_PRESENT;
// Translate the virtual address into PML indexes.
const size_t MASK = (1<<TRANSBITS)-1;
size_t pmlchildid[TOPPMLLEVEL + 1];
for ( size_t i = 1; i <= TOPPMLLEVEL; i++ )
{
pmlchildid[i] = (mapto >> (12+(i-1)*TRANSBITS)) & MASK;
}
// For each PML level, make sure it exists.
size_t offset = 0;
for ( size_t i = TOPPMLLEVEL; i > 1; i-- )
{
size_t childid = pmlchildid[i];
PML* pml = PMLS[i] + offset;
addr_t& entry = pml->entry[childid];
// Find the index of the next PML in the fractal mapped memory.
size_t childoffset = offset * ENTRIES + childid;
if ( !(entry & PML_PRESENT) )
{
// TODO: Possible memory leak when page allocation fails.
addr_t page = Page::Get();
if ( !page ) { return false; }
addr_t pmlflags = PML_PRESENT | PML_WRITABLE | PML_USERSPACE
| PML_FORK;
entry = page | pmlflags;
// Invalidate the new PML and reset it to zeroes.
addr_t pmladdr = (addr_t) (PMLS[i-1] + childoffset);
InvalidatePage(pmladdr);
memset((void*) pmladdr, 0, sizeof(PML));
}
offset = childoffset;
}
// Actually map the physical page to the virtual page.
const addr_t entry = physical | flags | extraflags;
(PMLS[1] + offset)->entry[pmlchildid[1]] = entry;
return true;
}
bool Map(addr_t physical, addr_t mapto, int prot)
{
return MapInternal(physical, mapto, prot);
}
void PageProtect(addr_t mapto, int protection)
{
addr_t phys;
if ( !LookUp(mapto, &phys, NULL) )
return;
Map(phys, mapto, protection);
}
void PageProtectAdd(addr_t mapto, int protection)
{
addr_t phys;
int prot;
if ( !LookUp(mapto, &phys, &prot) )
return;
prot |= protection;
Map(phys, mapto, prot);
}
void PageProtectSub(addr_t mapto, int protection)
{
addr_t phys;
int prot;
if ( !LookUp(mapto, &phys, &prot) )
return;
prot &= ~protection;
Map(phys, mapto, prot);
}
addr_t Unmap(addr_t mapto)
{
// Translate the virtual address into PML indexes.
const size_t MASK = (1<<TRANSBITS)-1;
size_t pmlchildid[TOPPMLLEVEL + 1];
for ( size_t i = 1; i <= TOPPMLLEVEL; i++ )
{
pmlchildid[i] = (mapto >> (12+(i-1)*TRANSBITS)) & MASK;
}
// For each PML level, make sure it exists.
size_t offset = 0;
for ( size_t i = TOPPMLLEVEL; i > 1; i-- )
{
size_t childid = pmlchildid[i];
PML* pml = PMLS[i] + offset;
addr_t& entry = pml->entry[childid];
if ( !(entry & PML_PRESENT) )
{
PanicF("Attempted to unmap virtual page %p, but the virtual"
" page was wasn't mapped. This is a bug in the code "
"code calling this function", mapto);
}
// Find the index of the next PML in the fractal mapped memory.
offset = offset * ENTRIES + childid;
}
addr_t& entry = (PMLS[1] + offset)->entry[pmlchildid[1]];
addr_t result = entry & PML_ADDRESS;
entry = 0;
// TODO: If all the entries in PML[N] are not-present, then who
// unmaps its entry from PML[N-1]?
return result;
}
bool MapPAT(addr_t physical, addr_t mapto, int prot, addr_t mtype)
{
addr_t extraflags = PAT2PMLFlags[mtype];
return MapInternal(physical, mapto, prot, extraflags);
}
void ForkCleanup(size_t i, size_t level)
{
PML* destpml = FORKPML + level;
if ( !i ) { return; }
for ( size_t n = 0; n < i-1; n++ )
{
addr_t entry = destpml->entry[i];
if ( !(entry & PML_FORK ) ) { continue; }
addr_t phys = entry & PML_ADDRESS;
if ( 1 < level )
{
addr_t destaddr = (addr_t) (FORKPML + level-1);
Map(phys, destaddr, PROT_KREAD | PROT_KWRITE);
InvalidatePage(destaddr);
ForkCleanup(ENTRIES+1UL, level-1);
}
Page::Put(phys);
}
}
// TODO: Copying every frame is endlessly useless in many uses. It'd be
// nice to upgrade this to a copy-on-write algorithm.
bool Fork(size_t level, size_t pmloffset)
{
PML* destpml = FORKPML + level;
for ( size_t i = 0; i < ENTRIES; i++ )
{
addr_t entry = (PMLS[level] + pmloffset)->entry[i];
// Link the entry if it isn't supposed to be forked.
if ( !(entry & PML_FORK ) )
{
destpml->entry[i] = entry;
continue;
}
addr_t phys = Page::Get();
if ( unlikely(!phys) ) { ForkCleanup(i, level); return false; }
addr_t flags = entry & PML_FLAGS;
destpml->entry[i] = phys | flags;
// Map the destination page.
addr_t destaddr = (addr_t) (FORKPML + level-1);
Map(phys, destaddr, PROT_KREAD | PROT_KWRITE);
InvalidatePage(destaddr);
size_t offset = pmloffset * ENTRIES + i;
if ( 1 < level )
{
if ( !Fork(level-1, offset) )
{
Page::Put(phys);
ForkCleanup(i, level);
return false;
}
continue;
}
// Determine the source page's address.
const void* src = (const void*) (offset * 4096UL);
// Determine the destination page's address.
void* dest = (void*) (FORKPML + level - 1);
memcpy(dest, src, 4096UL);
}
return true;
}
bool Fork(addr_t dir, size_t level, size_t pmloffset)
{
PML* destpml = FORKPML + level;
// This call always succeeds.
Map(dir, (addr_t) destpml, PROT_KREAD | PROT_KWRITE);
InvalidatePage((addr_t) destpml);
return Fork(level, pmloffset);
}
// Create an exact copy of the current address space.
addr_t Fork()
{
addr_t dir = Page::Get();
if ( dir == 0 ) { return 0; }
if ( !Fork(dir, TOPPMLLEVEL, 0) ) { Page::Put(dir); return 0; }
// Now, the new top pml needs to have its fractal memory fixed.
const addr_t flags = PML_PRESENT | PML_WRITABLE;
addr_t mapto;
addr_t childaddr;
(FORKPML + TOPPMLLEVEL)->entry[ENTRIES-1] = dir | flags;
childaddr = (FORKPML + TOPPMLLEVEL)->entry[ENTRIES-2] & PML_ADDRESS;
for ( size_t i = TOPPMLLEVEL-1; i > 0; i-- )
{
mapto = (addr_t) (FORKPML + i);
Map(childaddr, mapto, PROT_KREAD | PROT_KWRITE);
InvalidatePage(mapto);
(FORKPML + i)->entry[ENTRIES-1] = dir | flags;
childaddr = (FORKPML + i)->entry[ENTRIES-2] & PML_ADDRESS;
}
return dir;
}
}
}

View file

@ -0,0 +1,101 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/memorymanagement.h
Handles memory for the x86 family of architectures.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_MEMORYMANAGEMENT_H
#define SORTIX_X86_FAMILY_MEMORYMANAGEMENT_H
namespace Sortix
{
struct PML
{
addr_t entry[4096 / sizeof(addr_t)];
};
namespace Memory
{
const addr_t PML_PRESENT = (1<<0);
const addr_t PML_WRITABLE = (1<<1);
const addr_t PML_USERSPACE = (1<<2);
const addr_t PML_WRTHROUGH = (1<<3);
const addr_t PML_NOCACHE = (1<<4);
const addr_t PML_PAT = (1<<7);
const addr_t PML_AVAILABLE1 = (1<<9);
const addr_t PML_AVAILABLE2 = (1<<10);
const addr_t PML_AVAILABLE3 = (1<<11);
const addr_t PML_FORK = PML_AVAILABLE1;
const addr_t PML_FLAGS = (0xFFFUL); // Bits used for the flags.
const addr_t PML_ADDRESS = (~0xFFFUL); // Bits used for the address.
const addr_t PAT_UC = 0x00; // Uncacheable
const addr_t PAT_WC = 0x01; // Write-Combine
const addr_t PAT_WT = 0x04; // Writethrough
const addr_t PAT_WP = 0x05; // Write-Protect
const addr_t PAT_WB = 0x06; // Writeback
const addr_t PAT_UCM = 0x07; // Uncacheable, overruled by MTRR.
const addr_t PAT_NUM = 0x08;
// Desired PAT-Register PA-Field Indexing (different from BIOS defaults)
const addr_t PA[PAT_NUM] =
{
PAT_WB,
PAT_WT,
PAT_UCM,
PAT_UC,
PAT_WC,
PAT_WP,
0,
0,
};
// Inverse function of the above.
const addr_t PAINV[PAT_NUM] =
{
3, // UC
4, // WC
7, // No such
8, // No such
1, // WT
5, // WP,
0, // WB
2, // UCM
};
static inline addr_t EncodePATAsPMLFlag(addr_t pat)
{
pat = PAINV[pat];
addr_t result = 0;
if ( pat & 0x1 ) { result |= PML_WRTHROUGH; }
if ( pat & 0x2 ) { result |= PML_NOCACHE; }
if ( pat & 0x4 ) { result |= PML_PAT; }
return result;
}
bool MapPAT(addr_t physical, addr_t mapto, int prot, addr_t mtype);
addr_t ProtectionToPMLFlags(int prot);
int PMLFlagsToProtection(addr_t flags);
}
}
#if defined(__i386__)
#include "../x86/memorymanagement.h"
#elif defined(__x86_64__)
#include "../x64/memorymanagement.h"
#endif
#endif

274
kernel/x86-family/msr.cpp Normal file
View file

@ -0,0 +1,274 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012.
Copyright(C) Free Software Foundation, Inc. 2005, 2006, 2007, 2008, 2009.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/msr.cpp
Functions to manipulate Model Specific Registers. MTRR code is partially
based on code from GNU GRUB.
*******************************************************************************/
#include <sortix/kernel/kernel.h>
#include "memorymanagement.h"
namespace Sortix {
namespace MSR {
const uint32_t bit_MTRR = 0x00001000U;
const uint32_t bit_PAT = 0x00010000U;
// TODO: Move this to a better location or use <cpuid.h>.
static inline bool IsCPUIdSupported()
{
#ifdef __x86_64__
// TODO: Isn't this always supported under x86_64?
uint64_t id_supported;
asm ("pushfq\n\t"
"popq %%rax /* Get EFLAGS into EAX */\n\t"
"movq %%rax, %%rcx /* Save original flags in ECX */\n\t"
"xorq $0x200000, %%rax /* Flip ID bit in EFLAGS */\n\t"
"pushq %%rax /* Store modified EFLAGS on stack */\n\t"
"popfq /* Replace current EFLAGS */\n\t"
"pushfq /* Read back the EFLAGS */\n\t"
"popq %%rax /* Get EFLAGS into EAX */\n\t"
"xorq %%rcx, %%rax /* Check if flag could be modified */\n\t"
: "=a" (id_supported)
: /* No inputs. */
: /* Clobbered: */ "%rcx");
#else
uint32_t id_supported;
asm ("pushfl\n\t"
"popl %%eax /* Get EFLAGS into EAX */\n\t"
"movl %%eax, %%ecx /* Save original flags in ECX */\n\t"
"xorl $0x200000, %%eax /* Flip ID bit in EFLAGS */\n\t"
"pushl %%eax /* Store modified EFLAGS on stack */\n\t"
"popfl /* Replace current EFLAGS */\n\t"
"pushfl /* Read back the EFLAGS */\n\t"
"popl %%eax /* Get EFLAGS into EAX */\n\t"
"xorl %%ecx, %%eax /* Check if flag could be modified */\n\t"
: "=a" (id_supported)
: /* No inputs. */
: /* Clobbered: */ "%rcx");
#endif
return id_supported != 0;
}
#define cpuid(num,a,b,c,d) \
asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \
: "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
: "0" (num))
#define rdmsr(num,a,d) \
asm volatile ("rdmsr" : "=a" (a), "=d" (d) : "c" (num))
#define wrmsr(num,lo,hi) \
asm volatile ("wrmsr" : : "c" (num), "a" (lo), "d" (hi) : "memory")
#define mtrr_base(reg) (0x200 + (reg) * 2)
#define mtrr_mask(reg) (0x200 + (reg) * 2 + 1)
void EnableMTRR(int mtrr)
{
uint32_t eax, edx;
uint32_t mask_lo, mask_hi;
rdmsr(mtrr_mask(mtrr), eax, edx);
mask_lo = eax;
mask_hi = edx;
mask_lo |= 0x800 /* valid */;
wrmsr(mtrr_mask(mtrr), mask_lo, mask_hi);
}
void DisableMTRR(int mtrr)
{
uint32_t eax, edx;
uint32_t mask_lo, mask_hi;
rdmsr(mtrr_mask(mtrr), eax, edx);
mask_lo = eax;
mask_hi = edx;
mask_lo &= ~0x800 /* valid */;
wrmsr(mtrr_mask(mtrr), mask_lo, mask_hi);
}
void CopyMTRR(int dst, int src)
{
uint32_t base_lo, base_hi;
uint32_t mask_lo, mask_hi;
rdmsr(mtrr_base(src), base_lo, base_hi);
rdmsr(mtrr_mask(src), mask_lo, mask_hi);
wrmsr(mtrr_base(dst), base_lo, base_hi);
wrmsr(mtrr_mask(dst), mask_lo, mask_hi);
}
bool IsPATSupported()
{
if ( !IsCPUIdSupported() ) { return false; }
uint32_t eax, ebx, ecx, edx;
cpuid(1, eax, ebx, ecx, edx);
uint32_t features = edx;
return features & bit_PAT;
}
void InitializePAT()
{
using namespace Sortix::Memory;
const uint32_t LO = PA[0] << 0 | PA[1] << 8 | PA[2] << 16 | PA[3] << 24;
const uint32_t HI = PA[4] << 0 | PA[5] << 8 | PA[6] << 16 | PA[7] << 24;
const int PAT_REG = 0x0277;
wrmsr(PAT_REG, LO, HI);
}
bool IsMTRRSupported()
{
if ( !IsCPUIdSupported() ) { return false; }
uint32_t eax, ebx, ecx, edx;
cpuid(1, eax, ebx, ecx, edx);
uint32_t features = edx;
return features & bit_MTRR;
}
// TODO: Yes, returning a string as an error and giving the result in a pointer
// is very bad design. Please fix this at some point. Also improve the code such
// that it is more flexible.
const char* SetupMTRRForWC(addr_t base, size_t size, int* ret)
{
uint32_t eax, ebx, ecx, edx;
uint32_t mtrrcap;
int var_mtrrs;
uint32_t max_extended_cpuid;
uint32_t maxphyaddr;
uint64_t fb_base, fb_size;
uint64_t size_bits, fb_mask;
uint32_t bits_lo, bits_hi;
uint64_t bits;
int i, first_unused = -1;
uint32_t base_lo, base_hi, mask_lo, mask_hi;
fb_base = (uint64_t) base;
fb_size = (uint64_t) size;
// Check that fb_base and fb_size can be represented using a single MTRR.
if ( fb_base < (1 << 20) )
return "below 1MB, so covered by fixed-range MTRRs";
if ( fb_base >= (1LL << 36) )
return "over 36 bits, so out of range";
if ( fb_size < (1 << 12) )
return "variable-range MTRRs must cover at least 4KB";
size_bits = fb_size;
while ( size_bits > 1 )
size_bits >>= 1;
if ( size_bits != 1 )
return "not a power of two";
if ( fb_base & (fb_size - 1) )
return "not aligned on size boundary";
fb_mask = ~(fb_size - 1);
// Check CPU capabilities.
if ( !IsCPUIdSupported() )
return "cpuid not supported, therefore mtrr not supported";
if ( !IsMTRRSupported() )
return "cpu does not support mtrr";
rdmsr(0xFE, eax, edx);
mtrrcap = eax;
if ( !(mtrrcap & 0x00000400) ) /* write-combining */
return "write combining doesn't seem to be supported";
var_mtrrs = (mtrrcap & 0xFF);
cpuid (0x80000000, eax, ebx, ecx, edx);
max_extended_cpuid = eax;
if ( max_extended_cpuid >= 0x80000008 )
{
cpuid(0x80000008, eax, ebx, ecx, edx);
maxphyaddr = (eax & 0xFF);
}
else
maxphyaddr = 36;
bits_lo = 0xFFFFF000; /* assume maxphyaddr >= 36 */
bits_hi = (1 << (maxphyaddr - 32)) - 1;
bits = bits_lo | ((uint64_t) bits_hi << 32);
// Check whether an MTRR already covers this region. If not, take an unused
// one if possible.
for ( i = 0; i < var_mtrrs; i++ )
{
rdmsr(mtrr_mask (i), eax, edx);
mask_lo = eax;
mask_hi = edx;
if ( mask_lo & 0x800 ) /* valid */
{
uint64_t real_base, real_mask;
rdmsr(mtrr_base(i), eax, edx);
base_lo = eax;
base_hi = edx;
real_base = ((uint64_t) (base_hi & bits_hi) << 32) |
(base_lo & bits_lo);
real_mask = ((uint64_t) (mask_hi & bits_hi) << 32) |
(mask_lo & bits_lo);
if ( real_base < (fb_base + fb_size) &&
real_base + (~real_mask & bits) >= fb_base)
return "region already covered by another mtrr";
}
else if ( first_unused < 0 )
first_unused = i;
}
if ( first_unused < 0 )
return "all MTRRs in use";
// Set up the first unused MTRR we found.
rdmsr(mtrr_base(first_unused), eax, edx);
base_lo = eax;
base_hi = edx;
rdmsr(mtrr_mask(first_unused), eax, edx);
mask_lo = eax;
mask_hi = edx;
base_lo = (base_lo & ~bits_lo & ~0xFF) |
(fb_base & bits_lo) | 0x01 /* WC */;
base_hi = (base_hi & ~bits_hi) |
((fb_base >> 32) & bits_hi);
wrmsr(mtrr_base(first_unused), base_lo, base_hi);
mask_lo = (mask_lo & ~bits_lo) |
(fb_mask & bits_lo) | 0x800 /* valid */;
mask_hi = (mask_hi & ~bits_hi) |
((fb_mask >> 32) & bits_hi);
wrmsr(mtrr_mask(first_unused), mask_lo, mask_hi);
if ( ret )
*ret = first_unused;
return NULL;
}
} // namespace MSR
} // namespace Sortix

42
kernel/x86-family/msr.h Normal file
View file

@ -0,0 +1,42 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/msr.h
Functions to manipulate Model Specific Registers.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_MSR_H
#define SORTIX_X86_FAMILY_MSR_H
namespace Sortix {
namespace MSR {
bool IsPATSupported();
void InitializePAT();
bool IsMTRRSupported();
const char* SetupMTRRForWC(addr_t base, size_t size, int* ret = NULL);
void EnableMTRR(int mtrr);
void DisableMTRR(int mtrr);
void CopyMTRR(int dst, int src);
} // namespace MSR
} // namespace Sortix
#endif

135
kernel/x86-family/time.cpp Normal file
View file

@ -0,0 +1,135 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/time.cpp
Retrieving the current time.
*******************************************************************************/
#include <sys/types.h>
#include <timespec.h>
#include <sortix/timespec.h>
#include <sortix/kernel/clock.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/time.h>
namespace Sortix {
namespace Time {
static uint16_t DivisorOfFrequency(long frequency)
{
// The value we send to the PIT is the value to divide it's input clock
// (1193180 Hz) by, to get our required frequency. Note that the divisor
// must be small enough to fit into 16 bits.
return 1193180 / frequency;
}
static long FrequencyOfDivisor(uint16_t divisor)
{
return 1193180 / divisor;
}
static long RealFrequencyOfFrequency(long frequency)
{
return FrequencyOfDivisor(DivisorOfFrequency(frequency));
}
static struct timespec PeriodOfFrequency(long frequency)
{
long period_ns = 1000000000L / frequency;
return timespec_make(0, period_ns);
}
static void RequestIRQ0(uint16_t divisor)
{
CPU::OutPortB(0x43, 0x36);
CPU::OutPortB(0x40, divisor >> 0 & 0xFF);
CPU::OutPortB(0x40, divisor >> 8 & 0xFF);
}
extern Clock* realtime_clock;
extern Clock* uptime_clock;
static struct timespec tick_period;
static long tick_frequency;
static uint16_t tick_divisor;
static void OnIRQ0(CPU::InterruptRegisters* regs, void* /*user*/)
{
OnTick(tick_period, !regs->InUserspace());
Scheduler::Switch(regs);
// TODO: There is a horrible bug that causes Sortix to only receive
// one IRQ0 on my laptop, but it works in virtual machines. But
// re-requesting an addtional time seems to work. Hacky and ugly.
// TODO: Confirm whether this still happens and whether it is trigged by
// another bug in my system.
static bool did_ugly_irq0_hack = false;
if ( !did_ugly_irq0_hack )
RequestIRQ0(tick_divisor),
did_ugly_irq0_hack = true;
}
void CPUInit()
{
// Estimate the rate that interrupts will be coming at.
long desired_frequency = 100/*Hz*/;
tick_frequency = RealFrequencyOfFrequency(desired_frequency);
tick_divisor = DivisorOfFrequency(tick_frequency);
tick_period = PeriodOfFrequency(tick_frequency);
// Initialize the clocks on this system.
realtime_clock->SetCallableFromInterrupts(true);
uptime_clock->SetCallableFromInterrupts(true);
struct timespec nul_time = timespec_nul();
realtime_clock->Set(&nul_time, &tick_period);
uptime_clock->Set(&nul_time, &tick_period);
}
void InitializeProcessClocks(Process* process)
{
struct timespec nul_time = timespec_nul();
process->execute_clock.SetCallableFromInterrupts(true);
process->execute_clock.Set(&nul_time, &tick_period);
process->system_clock.SetCallableFromInterrupts(true);
process->system_clock.Set(&nul_time, &tick_period);
process->child_execute_clock.Set(&nul_time, &tick_period);
process->child_execute_clock.SetCallableFromInterrupts(true);
process->child_system_clock.Set(&nul_time, &tick_period);
process->child_system_clock.SetCallableFromInterrupts(true);
}
void Start()
{
// Handle timer interrupts if they arrive.
Interrupt::RegisterHandler(Interrupt::IRQ0, &OnIRQ0, NULL);
// Request a timer interrupt now that we can handle them safely.
RequestIRQ0(tick_divisor);
}
} // namespace Time
} // namespace Sortix

View file

@ -0,0 +1,108 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/x86-family.cpp
CPU stuff for the x86 CPU family.
*******************************************************************************/
#include <sortix/kernel/kernel.h>
namespace Sortix
{
namespace CPU
{
void OutPortB(uint16_t Port, uint8_t Value)
{
asm volatile ("outb %1, %0" : : "dN" (Port), "a" (Value));
}
void OutPortW(uint16_t Port, uint16_t Value)
{
asm volatile ("outw %1, %0" : : "dN" (Port), "a" (Value));
}
void OutPortL(uint16_t Port, uint32_t Value)
{
asm volatile ("outl %1, %0" : : "dN" (Port), "a" (Value));
}
uint8_t InPortB(uint16_t Port)
{
uint8_t Result;
asm volatile("inb %1, %0" : "=a" (Result) : "dN" (Port));
return Result;
}
uint16_t InPortW(uint16_t Port)
{
uint16_t Result;
asm volatile("inw %1, %0" : "=a" (Result) : "dN" (Port));
return Result;
}
uint32_t InPortL(uint16_t Port)
{
uint32_t Result;
asm volatile("inl %1, %0" : "=a" (Result) : "dN" (Port));
return Result;
}
void Reboot()
{
// Keyboard interface IO port: data and control.
const uint16_t KEYBOARD_INTERFACE = 0x64;
// Keyboard IO port.
const uint16_t KEYBOARD_IO = 0x60;
// Keyboard data is in buffer (output buffer is empty) (bit 0).
const uint8_t KEYBOARD_DATA = (1<<0);
// User data is in buffer (command buffer is empty) (bit 1).
const uint8_t USER_DATA = (1<<1);
// Disable interrupts.
asm volatile("cli");
// Clear all keyboard buffers (output and command buffers).
uint8_t byte;
do
{
byte = InPortB(KEYBOARD_INTERFACE);
if ( ( byte & KEYBOARD_DATA ) != 0 ) { InPortB(KEYBOARD_IO); }
} while ( ( byte & USER_DATA ) != 0 );
// CPU reset command.
uint8_t KEYBOARD_RESET_CPU = 0xFE;
// Now pulse the CPU reset line and reset.
OutPortB(KEYBOARD_INTERFACE, KEYBOARD_RESET_CPU);
// If that didn't work, just halt.
asm volatile("hlt");
}
void ShutDown()
{
// TODO: Unimplemented, just reboot.
Reboot();
}
}
}

View file

@ -0,0 +1,65 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
x86-family/x86-family.h
CPU stuff for the x86 CPU family.
*******************************************************************************/
#ifndef SORTIX_X86_FAMILY_H
#define SORTIX_X86_FAMILY_H
namespace Sortix
{
namespace CPU
{
void OutPortB(uint16_t Port, uint8_t Value);
void OutPortW(uint16_t Port, uint16_t Value);
void OutPortL(uint16_t Port, uint32_t Value);
uint8_t InPortB(uint16_t Port);
uint16_t InPortW(uint16_t Port);
uint32_t InPortL(uint16_t Port);
void Reboot();
void ShutDown();
}
const size_t FLAGS_CARRY = (1<<0); // 0x000001
const size_t FLAGS_RESERVED1 = (1<<1); // 0x000002, read as one
const size_t FLAGS_PARITY = (1<<2); // 0x000004
const size_t FLAGS_RESERVED2 = (1<<3); // 0x000008
const size_t FLAGS_AUX = (1<<4); // 0x000010
const size_t FLAGS_RESERVED3 = (1<<5); // 0x000020
const size_t FLAGS_ZERO = (1<<6); // 0x000040
const size_t FLAGS_SIGN = (1<<7); // 0x000080
const size_t FLAGS_TRAP = (1<<8); // 0x000100
const size_t FLAGS_INTERRUPT = (1<<9); // 0x000200
const size_t FLAGS_DIRECTION = (1<<10); // 0x000400
const size_t FLAGS_OVERFLOW = (1<<11); // 0x000800
const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13);
const size_t FLAGS_NESTEDTASK = (1<<14); // 0x004000
const size_t FLAGS_RESERVED4 = (1<<15); // 0x008000
const size_t FLAGS_RESUME = (1<<16); // 0x010000
const size_t FLAGS_VIRTUAL8086 = (1<<17); // 0x020000
const size_t FLAGS_ALIGNCHECK = (1<<18); // 0x040000
const size_t FLAGS_VIRTINTR = (1<<19); // 0x080000
const size_t FLAGS_VIRTINTRPEND = (1<<20); // 0x100000
const size_t FLAGS_ID = (1<<21); // 0x200000
}
#endif