Skip to content
Snippets Groups Projects
Commit e062694b authored by adrian.spycher's avatar adrian.spycher
Browse files

initial commit

parents
No related branches found
No related tags found
No related merge requests found
Makefile 0 → 100644
DISK=disk.raw
DISK_SIZE=256K
help:
@echo "Available targets:"
@echo " run_pv run paravirtualized guest"
@echo " run_phys run physical (native) guest"
@echo " vmm build VMM"
@echo " guest_pv build paravirtualized guest"
@echo " guest_phys build physical (native) guest"
@echo " clean delete VMM and guest"
@echo " clean_vmm delete VMM"
@echo " clean_guest delete guest"
guest:
$(MAKE) -C guest PV=1 OUT=guest.bin
.PHONY: guest
\ No newline at end of file
BAREMETAL_FLAGS=-m32 -march=i386 -ffreestanding -nostdlib -fno-builtin -fno-stack-protector -fno-pie -static
CC=gcc -std=gnu17 $(BAREMETAL_FLAGS) -Wall -Wextra -MMD -Ishared -I../.. -I..
CC+=-DPV=$(PV)
LD=gcc -Tguest.ld $(BAREMETAL_FLAGS)
C_SRCS=$(shell find . -name "*.c")
C_OBJS=$(C_SRCS:.c=.o)
C_DEPS=$(C_OBJS:%.o=%.d)
ASM_SRCS=$(shell find . -name "*.s")
ASM_OBJS=$(ASM_SRCS:.s=.o)
$(OUT): $(C_OBJS) $(ASM_OBJS)
$(LD) $^ -o $@
%.o: %.c
$(CC) -c $< -o $@
%.o: %.s
nasm -f elf32 $< -o $@
clean:
rm -f $(C_OBJS) $(ASM_OBJS) $(C_DEPS) *.o *.d $(OUT)
.PHONY: clean
-include $(C_DEPS)
#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_
// Privilege levels
#define DPL_USER 0x3
#define DPL_KERNEL 0x0
// Selectors
//#define LDT_SELECTOR 0x4
// Descriptor types for code and data segments
#define TYPE_DATA_RO 1 // read-only
#define TYPE_DATA_RW 3 // read-write
// Stack segments are data segments which must be read/write segments. Loading the
// SS register with a segment selector for a nonwritable data segment generates a
// general-protection exception (#GP). If the size of a stack segment needs to be
// changed dynamically, the stack segment can be an expand-down data segment
// (expansion-direction flag set). Here, dynamically changing the segment limit causes
// stack space to be added to the bottom of the stack. If the size of a stack segment is
// intended to remain static, the stack segment may be either an expand-up or expand-down type.
#define TYPE_DATA_RW_EXPAND_DOWN 6 // stack segment type
#define TYPE_CODE_EXECONLY 9
#define TYPE_CODE_EXECREAD 11
// Descriptor types for system segments and gates
#define TYPE_LDT 2
#define TYPE_TASK_GATE 5
#define TYPE_TSS 9
#define TYPE_CALL_GATE 12
#define TYPE_TRAP_GATE 15
#define TYPE_INTERRUPT_GATE 14
#endif
CODE_SELECTOR equ 0x08
DATA_SELECTOR equ 0x10
extern guest_main
section .entrypoint
[BITS 16]
cli ; disable hardware interrupts (since no IDT loaded yet)
lgdt [gdt_descriptor] ; load GDT (see segments defined after gdt_start below)
; switch to protected mode (32-bits mode)
mov eax,cr0
or eax,1 ; set PE (Protection Enable) bit in CR0 (Control Register 0)
mov cr0,eax
jmp CODE_SELECTOR:flush ; far jump [selector:offset] to load code segment (cs) with code descriptor in GDT
flush:
[BITS 32]
mov ax,DATA_SELECTOR ; update data (ds), stack (sp) and extra (es) segments with data descriptor in GDT
mov ds,ax
mov ss,ax
mov es,ax
call guest_main ; call guest C code entrypoint
@forever:
hlt ; halt the CPU
jmp @forever
; Setup a GDT table to identity map the full 4GB address space (32 bits)
gdt_start: ; the GDT table starts here
gdt_null: ; 1st descriptor: NULL descriptor (required)
dd 0
dd 0
gdt_code: ; 2nd descriptor: code descriptor: base=0, limit=0xFFFFF (4GB)
dw 0xFFFF ; limit (bits 0-15)
dw 0 ; base (bits 0-15)
db 0 ; base (bits 16-23)
db 10011001b ; bit 7: P: present in memory
; bits 5-6: DPL: privilege level (ring level)
; bit 4: S: 1 for segments, 0 for system (TSS, LDT, gates)
; bit 0-3: Type: 1001b (9) for code exec only
db 11001111b ; bits 4-7: flags:
; bit 7: G: granularity (0=1 byte, 1=4KB)
; bit 6: D: 1 for 32-bit code and data segments; 0 for system (TSS, LDT, gate)
; bit 5: must be 0
; bit 4: available for use
; bits 0-3: limit (bits 16-19)
db 0 ; base (bits 24-31)
gdt_data: ; 3rd descriptor: data descriptor: base=0, limit=0xFFFFF (4GB)
dw 0xFFFF ; limit (bits 0-15)
dw 0 ; base (bits 0-15)
db 0 ; base (bits 16-23)
db 10010011b ; bit 7: P: present in memory
; bits 5-6: DPL: privilege level (ring level)
; bit 4: S: 1 for segments, 0 for system (TSS, LDT, gates)
; bit 0-3: Type: 0011b (3) data read/write
db 11001111b ; bits 4-7: flags:
; bit 7: G: granularity (0=1 byte, 1=4KB)
; bit 6: D: 1 for 32-bit code and data segments; 0 for system (TSS, LDT, gate)
; bit 5: must be 0
; bit 4: available for use
; bits 0-3: limit (bits 16-19)
db 0 ; base (bits 24-31)
gdt_end:
gdt_descriptor: ; descriptor pointing to the GDT (required by the lgdt instruction)
dw gdt_end - gdt_start - 1 ; GDT size minus 1
dd gdt_start ; address of the GDT table
OUTPUT_FORMAT("binary")
SECTIONS {
. = 0; /* first section located at address 0 */
.entrypoint ALIGN(4): /* entry point: must be located at 0 */
{
*(.entrypoint)
}
.text ALIGN(4) : /* code */
{
*(.text*)
}
.rodata ALIGN(4) : /* read-only data */
{
*(.rodata*)
}
.data ALIGN(4) : /* initialized data */
{
*(.data*)
}
.bss ALIGN(4) : /* unitialized data */
{
*(COMMON)
*(.bss*)
}
}
#include <stdint.h>
#include "idt.h"
#include "utils.h"
#include "x86.h"
// If PV == 1 => uses paravirtualized drivers (when available)
// If PV == 0 => uses physical (hardware) drivers (when available)
#if PV == 1
#define timer_sleep timer_sleep_pv
#else
#define timer_sleep timer_sleep_phys
#endif
// Dummy examples
void timer_sleep_pv(int usec) {}
void timer_sleep_phys(int usec) {}
void guest_main() {
idt_init(); // Initialize interrupt subsystem
sti(); // Enable hardware interrupts
timer_sleep(100000);
}
#ifndef _IDE_H_
#define _IDE_H_
#define SECTOR_SIZE 512
// Write a sector.
// Implement a device driver for the true physical hardware.
void ide_write_sector_phys(int sector_idx, void *data);
#endif
#include <stdint.h>
#include "ide.h"
#include "shared/ide_regs.h"
#include "../pmio.h"
// IDE controler is programmed in PMIO.
/**
* Wait for the disk drive to be ready.
*/
static void wait_drive() {
while ((inb(IDE_STATUS_REG) & 0xC0) != 0x40);
}
/**
* Prepare the disk drive for read/write at the specified sector in 28-bit LBA mode.
* @param sector the sector to read or write
*/
static void pio_prepare(int sector) {
wait_drive();
outb(IDE_DATA_BASE_REG+2, 1); // sector count
outb(IDE_DATA_BASE_REG+3, sector & 0xff); // send bits 0-7 of LBA
outb(IDE_DATA_BASE_REG+4, (sector >> 8) & 0xff); // send bits 8-15 of LBA
outb(IDE_DATA_BASE_REG+5, (sector >> 16) & 0xff); // send bits 16-23 of LBA
outb(IDE_DATA_BASE_REG+6, ((sector >> 24) & 0x0f) | 0xe0); // send bits 24-27 of LBA + set LBA mode; 0xe0 = 11100000b;
}
/**
* Read a sector from the first disk.
* @param first sector to read (0-indexed)
* @param dst address to store to read data
* Based on the assembly code at http://wiki.osdev.org/ATA_read/write_sectors
*/
void ide_write_sector_phys(int sector, void *data) {
pio_prepare(sector);
outb(IDE_CMD_REG, 0x30); // command port: write with retry
wait_drive();
uint16_t *d = (uint16_t *)data;
for (int i = 0; i < SECTOR_SIZE/2; i++) {
outw(IDE_DATA_BASE_REG, *d);
d++;
}
}
#include "idt.h"
#include "utils.h"
#include "descriptors.h"
#include "x86.h"
#define GDT_KERNEL_CODE_SELECTOR 0x08
// Structure of an IDT descriptor. There are 3 types of descriptors:
// task-gates, interrupt-gates, trap-gates.
// See 5.11 of Intel 64 & IA32 architectures software developer's manual for more details.
// For task gates, offset must be 0.
typedef struct idt_entry_st {
uint16_t offset15_0; // only used by trap and interrupt gates
uint16_t selector; // segment selector for trap and interrupt gates; TSS segment
// selector for task gates
uint16_t reserved : 8;
uint16_t type : 5;
uint16_t dpl : 2;
uint16_t p : 1;
uint16_t offset31_16; // only used by trap and interrupt gates
} __attribute__((packed)) idt_entry_t;
// Structure describing a pointer to the IDT gate table.
// This format is required by the lidt instruction.
typedef struct idt_ptr_st {
uint16_t limit; // Limit of the table (ie. its size)
uint32_t base; // Address of the first entry
} __attribute__((packed)) idt_ptr_t;
// Gates table
static idt_entry_t idt[256];
static idt_ptr_t idt_ptr;
// Defined in idt_asm.s
void idt_load(idt_ptr_t *idt_ptr);
// Build and return an IDT entry.
// selector is the code segment selector to access the ISR
// offset is the address of the ISR (for task gates, offset must be 0)
// type indicates the IDT entry type
// dpl is the privilege level required to call the associated ISR
static idt_entry_t idt_build_entry(uint16_t selector, uint32_t offset, uint8_t type, uint8_t dpl) {
idt_entry_t entry;
entry.offset15_0 = offset & 0xffff;
entry.selector = selector;
entry.reserved = 0;
entry.type = type;
entry.dpl = dpl;
entry.p = 1;
entry.offset31_16 = (offset >> 16) & 0xffff;
return entry;
}
// Low level handler defined in idt_asm.s
void _irq0();
// IRQ handler
void irq_handler(uint32_t irq_number) {
}
void idt_init() {
idt_ptr.limit = sizeof(idt)-1;
idt_ptr.base = (uint32_t)&idt;
// Clear up all entries
memset(idt, 0, sizeof(idt));
idt[0] = idt_build_entry(GDT_KERNEL_CODE_SELECTOR, (uint32_t)_irq0, TYPE_INTERRUPT_GATE, DPL_KERNEL);
idt_load(&idt_ptr);
}
#ifndef _IDT_H_
#define _IDT_H_
void idt_init();
#endif
section .text ; start of the text (code) section
align 4 ; the code must be 4 byte aligned
global idt_load
; Load the IDT
idt_load:
mov eax,[esp+4] ; Get the pointer to the IDT, passed as a parameter.
lidt [eax] ; Load the IDT pointer.
ret
; Low-level hardware interrupts handler for irq0
global _irq0
_irq0:
push 0 ; put irq number on the stack
jmp irq_handler_wrapper
extern irq_handler
irq_handler_wrapper:
; Save all registers
push eax
push ebx
push ecx
push edx
push esi
push edi
push ebp
push ds
push es
push fs
push gs
; Load kernel data descriptor into all segments
mov ax,0x10 ; 0x10 = kernel data segment selector (3rd selector)
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
; What's on the stack at this point, in order (4 bytes each):
; gs, fs, es, ds, ebp, edi, esi, edx, ecx, ebx, eax, irq_number, eip, cs, eflags, esp, ss
; Pass irq_number as argument to the C function
mov eax,esp
add eax,44 ; to point to irq_number that was pushed in _irqX
push dword[eax]
call irq_handler
pop eax ; only here to balance the "push eax" done before the call
; Restore all registers
pop gs
pop fs
pop es
pop ds
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
; Fix-up the stack pointer due to the push done before the jump to irq_handler_wrapper
add esp,4
iret
#ifndef _PMIO_H_
#define _PMIO_H_
#include <stdint.h>
// Write a 8-bit value to the specified port
void outb(uint16_t port, uint8_t data);
// Read a 8-bit value from the specified port
uint8_t inb(uint16_t port);
// Write a 16-bit value to the specified port
void outw(uint16_t port, uint16_t data);
// Read a 16-bit value from the specified port
uint16_t inw(uint16_t port);
// Write a 32-bit value to the specified port
void outd(uint16_t port, uint32_t data);
// Read a 32-bit value from the specified port
uint32_t ind(uint16_t port);
#endif
global outb
global inb
global outw
global inw
global outd
global ind
section .text ; start of the text (code) section
align 4 ; the code must be 4 byte aligned
; void outb(uint16_t port, uint8_t data);
outb:
mov dx,word [esp+4] ; port (2 bytes)
mov al,byte [esp+8] ; data (1 byte)
out dx,al
ret
; uint8_t inb(uint16_t port);
inb:
mov dx,word [esp+4] ; port (2 bytes)
in al,dx
ret
; void outw(uint16_t port, uint16_t data);
outw:
mov dx,word [esp+4] ; port (2 bytes)
mov ax,word [esp+8] ; data (2 bytes)
out dx,ax
ret
; uint16_t inw(uint16_t port);
inw:
mov dx,word [esp+4] ; port (2 bytes)
in ax,dx
ret
; void outd(uint16_t port, uint32_t data);
outd:
mov dx,word [esp+4] ; port (2 bytes)
mov eax,[esp+8] ; data (4 bytes)
out dx,eax
ret
; uint32_t ind(uint16_t port);
ind:
mov dx,word [esp+4] ; port (2 bytes)
in eax,dx
ret
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include "stdio.h"
// Print into buf in reverse, the value val in the given base.
// The argument sign indicates whether the value is signed (true) or not (false).
// The number of characters written is returned into length.
static void printint_rev(char *buf, int *length, int val, int base, bool sign) {
static char digits[] = "0123456789ABCDEF";
int count = 0;
u_int x;
if (sign && (sign = val < 0))
x = -val;
else
x = val;
do {
buf[count++] = digits[x % base];
} while ((x /= base) != 0);
if (sign)
buf[count++] = '-';
buf[count] = 0;
*length = count;
}
int snprintf(char *dest, int size, char *fmt, ...) {
va_list args;
va_start(args, fmt);
int count = vsnprintf(dest, size, fmt, args);
va_end(args);
return count;
}
// Helper macro used in vsnprintf
#define putc(c) \
if (size > 0) { \
*dest = c; \
dest++; \
size--; \
count++; \
}
int vsnprintf(char *dest, int size, const char *fmt, va_list args) {
char buf[size+1];
int len;
int count = 0;
while (*fmt) {
if (*fmt == '%') { // found an argument
fmt++;
if (*fmt == 0) // missing value after %
break;
switch (*fmt) {
case 'c':
{
int c = va_arg(args, int);
putc(c)
}
break;
case 's':
{
char *s = va_arg(args, char *);
while (*s) {
putc(*s)
s++;
}
}
break;
case 'd':
{
int n = va_arg(args, int);
printint_rev(buf, &len, n, 10, true);
while (len) putc(buf[--len]);
}
break;
case 'u':
{
u_int n = va_arg(args, u_int);
printint_rev(buf, &len, n, 10, false);
while (len) putc(buf[--len]);
}
break;
case 'x':
{
int n = va_arg(args, int);
printint_rev(buf, &len, n, 16, false);
while (len) putc(buf[--len]);
}
break;
case '%':
putc('%')
break;
default:
// unkown argument, ignore it!
break;
}
}
else {
putc(*fmt)
}
fmt++;
}
*dest = 0;
return count;
}
#ifndef _STDIO_H_
#define _STDIO_H_
#include <stdarg.h>
// Writes at most size characters (including terminal 0) of the formatted string fmt
// into buffer dest.
// args are the variable length arguments as a va_list.
// Returns the number of characters written.
int vsnprintf(char *dest, int size, const char *fmt, va_list args);
// Writes at most size characters (including terminal 0) of the formatted string fmt
// into buffer dest.
// Any arbitrary number of arguments can be passed after fmt.
// Returns the number of characters written.
int snprintf(char *dest, int size, char *fmt, ...);
#endif
#include "utils.h"
#define X 123456789
#define Y 987654321
#define Z 43219876
static uint32_t seed = 6543217;
static uint32_t x = X;
static uint32_t y = Y;
static uint32_t z = Z;
void srand(u_int _seed) {
seed = _seed;
x = X;
y = Y;
z = Z;
}
// Implementation of Marsaglia JKISS RNG uses 64-bit value and 2x multiplies
// Source: https://github.com/SpiNNakerManchester/spinn_common/tree/master
u_int rand() {
x = 314527869 * x + 1234567;
y ^= y << 5;
y ^= y >> 7;
y ^= y << 22;
uint64_t t = 4294584393ULL * z + seed;
seed = t >> 32;
z = t;
return (uint32_t) (x + y + z);
}
void memset(void *dst, uint8_t value, u_int count) {
uint8_t *d = dst;
while (count--) {
*d++ = value;
}
}
void memcpy(void *dst, void *src, u_int count) {
uint8_t *d = dst;
uint8_t *s = src;
while (count--)
*d++ = *s++;
}
#ifndef _UTILS_H_
#define _UTILS_H_
#include <stdint.h>
#include <sys/types.h>
void srand(u_int _seed);
u_int rand();
void memset(void *dst, uint8_t value, u_int count);
void memcpy(void *dst, void *src, u_int count);
#endif
\ No newline at end of file
#ifndef _X86_H_
#define _X86_H_
// Disable hardware interrupts.
static inline void cli() {
asm volatile("cli");
}
// Enable hardware interrupts.
static inline void sti() {
asm volatile("sti");
}
// Halt the processor.
// External interrupts wake up the CPU, hence the cli instruction.
static inline void halt() {
while (1) asm volatile("cli\nhlt");
}
#endif
#ifndef _HYPERCALL_PARAMS_H_
#define _HYPERCALL_PARAMS_H_
#include <stdint.h>
typedef struct {
uint32_t us; // delay in micro-seconds
} __attribute__((packed)) hyper_timer_sleep_params_t;
#endif
#ifndef _IDE_REGS_H_
#define _IDE_REGS_H_
// Status register on the primary bus (read-only)
#define IDE_STATUS_REG 0x1F7
// Command register on the primary bus (write-only)
#define IDE_CMD_REG 0x1F7
// Base port on primary bus (write-only)
#define IDE_DATA_BASE_REG 0x1F0
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment