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:
parent
18d2695439
commit
98a87fa1e5
228 changed files with 10 additions and 10 deletions
109
kernel/x86-family/cmos.cpp
Normal file
109
kernel/x86-family/cmos.cpp
Normal 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(¤t_time, NULL);
|
||||
}
|
||||
|
||||
} // namespace CMOS
|
||||
} // namespace Sortix
|
||||
59
kernel/x86-family/cmos.h
Normal file
59
kernel/x86-family/cmos.h
Normal 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
|
||||
88
kernel/x86-family/float.cpp
Normal file
88
kernel/x86-family/float.cpp
Normal 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
58
kernel/x86-family/float.h
Normal 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
254
kernel/x86-family/gdt.cpp
Normal 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
38
kernel/x86-family/gdt.h
Normal 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
81
kernel/x86-family/idt.cpp
Normal 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
38
kernel/x86-family/idt.h
Normal 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
|
||||
770
kernel/x86-family/memorymanagement.cpp
Normal file
770
kernel/x86-family/memorymanagement.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
kernel/x86-family/memorymanagement.h
Normal file
101
kernel/x86-family/memorymanagement.h
Normal 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
274
kernel/x86-family/msr.cpp
Normal 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
42
kernel/x86-family/msr.h
Normal 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
135
kernel/x86-family/time.cpp
Normal 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
|
||||
108
kernel/x86-family/x86-family.cpp
Normal file
108
kernel/x86-family/x86-family.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
65
kernel/x86-family/x86-family.h
Normal file
65
kernel/x86-family/x86-family.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue