merge paging into main
This commit is contained in:
commit
526ebed2fb
24 changed files with 1656 additions and 582 deletions
4
Makefile
4
Makefile
|
|
@ -11,10 +11,12 @@ KERNEL_SOURCES_x86_64 := \
|
||||||
src/x86_64/apic.c \
|
src/x86_64/apic.c \
|
||||||
src/x86_64/loadcs.S \
|
src/x86_64/loadcs.S \
|
||||||
src/x86_64/uart.c \
|
src/x86_64/uart.c \
|
||||||
src/x86_64/mem.c \
|
|
||||||
src/x86_64/string.S \
|
src/x86_64/string.S \
|
||||||
|
src/x86_64/cpu.c \
|
||||||
|
src/x86_64/paging.c \
|
||||||
src/x86_64/asm.c \
|
src/x86_64/asm.c \
|
||||||
src/x86_64/address.c \
|
src/x86_64/address.c \
|
||||||
|
src/x86_64/ps2_driver.c \
|
||||||
# end of x86_64 specific kernel sources list
|
# end of x86_64 specific kernel sources list
|
||||||
|
|
||||||
# Architecture-agnostic kernel sources.
|
# Architecture-agnostic kernel sources.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
qemu-system-x86_64 -drive if=ide,format=raw,file=build/disk.img -drive if=virtio,media=disk,format=raw,file=drive.img "$@" -S -gdb tcp::3117
|
qemu-system-x86_64 -drive if=ide,format=raw,file=build/disk.img -drive if=virtio,media=disk,format=raw,file=drive.img -d int "$@" -S -gdb tcp::3117
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,24 @@
|
||||||
#ifndef KARLOS_ADDRESS_H
|
#ifndef KARLOS_ADDRESS_H
|
||||||
#define KARLOS_ADDRESS_H
|
#define KARLOS_ADDRESS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// do not use these fields directly! use *_to_* functions.
|
// do not use these fields directly! use *_to_* functions.
|
||||||
struct va {
|
struct va {
|
||||||
uint64_t value; // 48-bit
|
uint64_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vpn {
|
struct vpn {
|
||||||
uint64_t pagenum; // 36-bit
|
uint64_t pagenum;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pa {
|
struct pa {
|
||||||
uint64_t value; // 48 - 12 = 36-bit
|
uint64_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ppn {
|
struct ppn {
|
||||||
uint64_t pagenum; // 36 - 12 = 24-bit
|
uint64_t pagenum;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct va va_from_value(uint64_t value);
|
struct va va_from_value(uint64_t value);
|
||||||
|
|
@ -27,15 +28,23 @@ struct va va_from_vpn_with_offset(struct vpn vpn, uint64_t offset);
|
||||||
uint64_t va_to_value(struct va va);
|
uint64_t va_to_value(struct va va);
|
||||||
uint64_t va_to_canonical(struct va va);
|
uint64_t va_to_canonical(struct va va);
|
||||||
uint64_t va_offset(struct va va);
|
uint64_t va_offset(struct va va);
|
||||||
|
uint64_t va_offset_huge_2mb(struct va va);
|
||||||
|
uint64_t va_offset_huge_1gb(struct va va);
|
||||||
|
|
||||||
struct vpn vpn_from_pagenum(uint64_t pagenum);
|
struct vpn vpn_from_pagenum(uint64_t pagenum);
|
||||||
struct vpn vpn_from_aligned_va(struct va va);
|
struct vpn vpn_from_aligned_va(struct va va);
|
||||||
struct vpn vpn_from_unaligned_va(struct va va);
|
struct vpn vpn_from_unaligned_va(struct va va);
|
||||||
uint64_t vpn_to_pagenum(struct vpn vpn);
|
uint64_t vpn_to_pagenum(struct vpn vpn);
|
||||||
|
unsigned int vpn_level_idx(struct vpn vpn, unsigned int level);
|
||||||
|
bool vpn_is_huge_2mb(struct vpn vpn);
|
||||||
|
bool vpn_is_huge_1gb(struct vpn vpn);
|
||||||
|
|
||||||
struct pa pa_from_value(uint64_t value);
|
struct pa pa_from_value(uint64_t value);
|
||||||
struct pa pa_from_ppn(struct ppn ppn);
|
struct pa pa_from_ppn(struct ppn ppn);
|
||||||
struct pa pa_from_ppn_with_offset(struct ppn ppn, uint64_t offset);
|
struct pa pa_from_ppn_with_offset(struct ppn ppn, uint64_t offset);
|
||||||
|
struct pa pa_from_ppn_huge_2mb_with_offset(struct ppn ppn, uint64_t offset);
|
||||||
|
struct pa pa_from_ppn_huge_1gb_with_offset(struct ppn ppn, uint64_t offset);
|
||||||
|
struct pa pa_from_pt_with_idx(struct ppn ppn, unsigned int idx);
|
||||||
uint64_t pa_to_value(struct pa pa);
|
uint64_t pa_to_value(struct pa pa);
|
||||||
void *pa_to_pointer(struct pa pa);
|
void *pa_to_pointer(struct pa pa);
|
||||||
uint64_t pa_offset(struct pa pa);
|
uint64_t pa_offset(struct pa pa);
|
||||||
|
|
@ -44,5 +53,9 @@ struct ppn ppn_from_pagenum(uint64_t pagenum);
|
||||||
struct ppn ppn_from_aligned_pa(struct pa pa);
|
struct ppn ppn_from_aligned_pa(struct pa pa);
|
||||||
struct ppn ppn_from_unaligned_pa(struct pa pa);
|
struct ppn ppn_from_unaligned_pa(struct pa pa);
|
||||||
uint64_t ppn_to_pagenum(struct ppn ppn);
|
uint64_t ppn_to_pagenum(struct ppn ppn);
|
||||||
|
bool ppn_is_huge_2mb(struct ppn ppn);
|
||||||
|
bool ppn_is_huge_1gb(struct ppn ppn);
|
||||||
|
|
||||||
|
void set_identity_mapping(struct vpn first_page);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
10
include/cpu.h
Normal file
10
include/cpu.h
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef KARLOS_CPU_H
|
||||||
|
#define KARLOS_CPU_H
|
||||||
|
|
||||||
|
void cpu_init(void);
|
||||||
|
unsigned int cpu_get_core_id(void);
|
||||||
|
void cpu_set_timer(void);
|
||||||
|
|
||||||
|
void interrupt_handler_register(unsigned int vector, void (*handler)(void));
|
||||||
|
|
||||||
|
#endif
|
||||||
38
include/paging.h
Normal file
38
include/paging.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef KARLOS_PAGING_H
|
||||||
|
#define KARLOS_PAGING_H
|
||||||
|
|
||||||
|
// restricted, architecture-agnostic interface.
|
||||||
|
// For full interface see include/<arch>/paging.h
|
||||||
|
|
||||||
|
// TODO remove mentions of cr3 and replace with top_pt or root or something
|
||||||
|
// I guess we only have to do this here
|
||||||
|
|
||||||
|
#include "address.h"
|
||||||
|
|
||||||
|
// --- init ---
|
||||||
|
|
||||||
|
void init_paging();
|
||||||
|
|
||||||
|
// --- traversal and mapping ---
|
||||||
|
|
||||||
|
bool pt_translate(struct va va, struct ppn cr3, struct pa *pa_out);
|
||||||
|
bool pt_translate_current(struct va va, struct pa *pa_out);
|
||||||
|
|
||||||
|
bool pt_map_single(struct vpn virt, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn cr3);
|
||||||
|
bool pt_map_single_current(struct vpn virt, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global);
|
||||||
|
bool pt_map_range(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn cr3);
|
||||||
|
bool pt_map_range_current(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
||||||
|
bool writable, bool supervisor, bool global);
|
||||||
|
void pt_free(struct ppn cr3);
|
||||||
|
|
||||||
|
// --- page table creation ---
|
||||||
|
|
||||||
|
__attribute__((noreturn))
|
||||||
|
void pt_create_minimal(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <address.h>
|
||||||
#include <x86_64/address.h>
|
|
||||||
|
|
||||||
enum frame_size {
|
enum frame_size {
|
||||||
RAM_PAGE_NORMAL = 0,
|
RAM_PAGE_NORMAL = 0,
|
||||||
|
|
|
||||||
6
include/serial.h
Normal file
6
include/serial.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef KARLOS_SERIAL_H
|
||||||
|
#define KARLOS_SERIAL_H
|
||||||
|
|
||||||
|
void serial_write_char(char c);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
#ifndef KARLOS_APIC_H
|
#ifndef KARLOS_X86_64_APIC_H
|
||||||
#define KARLOS_APIC_H
|
#define KARLOS_X86_64_APIC_H
|
||||||
|
|
||||||
void lapic_init(void);
|
void lapic_init(void);
|
||||||
unsigned lapic_get_id(void);
|
unsigned lapic_get_id(void);
|
||||||
void lapic_eoi(void);
|
void lapic_eoi(void);
|
||||||
void lapic_set_timer(void);
|
void lapic_set_timer(void);
|
||||||
|
|
||||||
|
void pic_disable(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef KARLOS_ASM_H
|
#ifndef KARLOS_X86_64_ASM_H
|
||||||
#define KARLOS_ASM_H
|
#define KARLOS_X86_64_ASM_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
@ -20,8 +20,11 @@ void out32(int port, uint32_t value);
|
||||||
|
|
||||||
uint64_t get_cr0(void);
|
uint64_t get_cr0(void);
|
||||||
uint64_t get_cr3(void);
|
uint64_t get_cr3(void);
|
||||||
|
void set_cr3(uint64_t value);
|
||||||
uint64_t get_cr4(void);
|
uint64_t get_cr4(void);
|
||||||
|
|
||||||
|
#define X86_ASM_INT(value) __asm__("int $" #value :: )
|
||||||
|
|
||||||
static inline uint64_t
|
static inline uint64_t
|
||||||
readmsr(uint32_t msr)
|
readmsr(uint32_t msr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
#ifndef KARLOS_MEM_H
|
|
||||||
#define KARLOS_MEM_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "x86_64/address.h"
|
|
||||||
|
|
||||||
void init_gdt();
|
|
||||||
void init_idt();
|
|
||||||
|
|
||||||
struct pt_entry {
|
|
||||||
bool present;
|
|
||||||
bool writable;
|
|
||||||
bool supervisor;
|
|
||||||
bool writethrough;
|
|
||||||
bool cache_disable;
|
|
||||||
bool accessed;
|
|
||||||
bool dirty;
|
|
||||||
bool page_attr_table_low; // in high this is the page_size field to signal hugepages.
|
|
||||||
bool global;
|
|
||||||
bool page_attr_table_high;
|
|
||||||
struct ppn ppn;
|
|
||||||
// TODO MPK, NX
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t pt_entry_pack(const struct pt_entry *ent_in);
|
|
||||||
void pt_entry_unpack(uint64_t ent_in, struct pt_entry *ent_out);
|
|
||||||
void pt_entry_print(const struct pt_entry *ent);
|
|
||||||
|
|
||||||
bool pt_translate(struct va va, struct ppn cr3, struct pa *pa_out);
|
|
||||||
bool pt_translate_current(struct va va, struct pa *pa_out);
|
|
||||||
|
|
||||||
bool pt_map_single(struct vpn virt, struct ppn phys,
|
|
||||||
bool writable, bool supervisor, bool global,
|
|
||||||
struct ppn cr3);
|
|
||||||
bool pt_map_single_current(struct vpn virt, struct ppn phys,
|
|
||||||
bool writable, bool supervisor, bool global);
|
|
||||||
bool pt_map_range(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
|
||||||
bool writable, bool supervisor, bool global,
|
|
||||||
struct ppn cr3);
|
|
||||||
bool pt_map_range_current(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
|
||||||
bool writable, bool supervisor, bool global);
|
|
||||||
|
|
||||||
struct mem_range {
|
|
||||||
struct vpn vpn_start;
|
|
||||||
struct pt_entry entry_start;
|
|
||||||
uint64_t npages;
|
|
||||||
};
|
|
||||||
|
|
||||||
void mem_range_print(const struct mem_range *mr);
|
|
||||||
|
|
||||||
struct mem_range_buf {
|
|
||||||
struct mem_range *ptr;
|
|
||||||
uint64_t next_entry;
|
|
||||||
uint64_t num_entries;
|
|
||||||
};
|
|
||||||
#define FOR_MEM_RANGE_IN(var, buf) for(struct mem_range *var = (buf)->ptr; \
|
|
||||||
var < (buf)->ptr + (buf)->next_entry; \
|
|
||||||
var++)
|
|
||||||
|
|
||||||
void pt_get_ranges(struct mem_range_buf *buf_out);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
132
include/x86_64/paging.h
Normal file
132
include/x86_64/paging.h
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
#ifndef KARLOS_X86_64_PAGING_H
|
||||||
|
#define KARLOS_X86_64_PAGING_H
|
||||||
|
|
||||||
|
// Naming conventions used in paging code:
|
||||||
|
//
|
||||||
|
// A _pt_ (page table) can refer to a single level or to an entire hierarchy.
|
||||||
|
// We mostly use it to refer to a single level.
|
||||||
|
//
|
||||||
|
// Pts have 4 _levels_.
|
||||||
|
// The highest level is level 4. There is exactly one level 4 pt in a hierarchy.
|
||||||
|
// The lowest level is level 1.
|
||||||
|
//
|
||||||
|
// Pts contain _entries_. Each entry is also associated with a level.
|
||||||
|
// It's the level this entry is located in, NOT the level it points to.
|
||||||
|
// This means entry levels also range from 4 to 1.
|
||||||
|
//
|
||||||
|
// A _leaf entry_ is an entry that points to data pages.
|
||||||
|
// This includes all entries on level 1, as well as hugepage entries on level 2 and 3.
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "address.h"
|
||||||
|
|
||||||
|
// --- init ---
|
||||||
|
|
||||||
|
void init_paging();
|
||||||
|
|
||||||
|
// --- entries ---
|
||||||
|
|
||||||
|
struct pt_entry {
|
||||||
|
// fields not in actual entry
|
||||||
|
unsigned int level; // possible values are 1..=4
|
||||||
|
|
||||||
|
// fields that are level-independent
|
||||||
|
bool present;
|
||||||
|
bool writable;
|
||||||
|
bool supervisor;
|
||||||
|
bool writethrough;
|
||||||
|
bool cache_disable;
|
||||||
|
bool accessed;
|
||||||
|
bool dirty;
|
||||||
|
bool global;
|
||||||
|
struct ppn ppn;
|
||||||
|
|
||||||
|
// fields that are level-dependent
|
||||||
|
bool hugepage;
|
||||||
|
bool pat_bit;
|
||||||
|
|
||||||
|
// TODO MPK, NX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PT_ENTRY_IS_LEAF(ent) ((ent).present && ((ent).level == 1 || (ent).hugepage))
|
||||||
|
#define PT_ENTRY_IS_NONLEAF(ent) ((ent).present && (ent).level > 1 && !(ent).hugepage)
|
||||||
|
|
||||||
|
uint64_t pt_entry_pack(const struct pt_entry *ent_in);
|
||||||
|
void pt_entry_unpack(uint64_t ent_in, unsigned int level, struct pt_entry *ent_out);
|
||||||
|
void pt_entry_init_nonleaf(struct pt_entry *ent, unsigned int level, struct ppn ppn);
|
||||||
|
void pt_entry_init_leaf(struct pt_entry *ent, unsigned int level,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn ppn);
|
||||||
|
void pt_entry_print(const struct pt_entry *ent);
|
||||||
|
|
||||||
|
// --- traversal and mapping ---
|
||||||
|
|
||||||
|
struct access_level {
|
||||||
|
struct ppn ppn;
|
||||||
|
unsigned int idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct traversal {
|
||||||
|
unsigned int depth;
|
||||||
|
struct access_level levels[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
bool pt_translate(struct va va, struct ppn cr3, struct pa *pa_out);
|
||||||
|
bool pt_translate_current(struct va va, struct pa *pa_out);
|
||||||
|
|
||||||
|
bool pt_map_single(struct vpn virt, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn cr3);
|
||||||
|
bool pt_map_single_current(struct vpn virt, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global);
|
||||||
|
bool pt_map_range(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn cr3);
|
||||||
|
bool pt_map_range_current(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
||||||
|
bool writable, bool supervisor, bool global);
|
||||||
|
void pt_free(struct ppn root);
|
||||||
|
|
||||||
|
// --- page table creation ---
|
||||||
|
|
||||||
|
__attribute__((noreturn))
|
||||||
|
void pt_create_minimal(void);
|
||||||
|
|
||||||
|
// --- iterators ---
|
||||||
|
|
||||||
|
struct pt_leaf {
|
||||||
|
struct vpn vpn_start;
|
||||||
|
struct pt_entry ent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pt_leaf_iter {
|
||||||
|
bool done;
|
||||||
|
struct traversal trav;
|
||||||
|
};
|
||||||
|
|
||||||
|
void pt_leaf_iter_init(struct pt_leaf_iter *it, struct ppn cr3);
|
||||||
|
void pt_leaf_iter_init_current(struct pt_leaf_iter *it);
|
||||||
|
void pt_leaf_iter_init_custom_start(struct pt_leaf_iter *it, struct ppn cr3, struct vpn virt);
|
||||||
|
bool pt_leaf_iter_next(struct pt_leaf_iter *it, struct pt_leaf *leaf_out);
|
||||||
|
|
||||||
|
struct mem_range {
|
||||||
|
struct vpn vpn_start;
|
||||||
|
struct pt_entry entry_start;
|
||||||
|
uint64_t npages;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mem_range_print(const struct mem_range *mr);
|
||||||
|
|
||||||
|
struct mem_range_iter {
|
||||||
|
struct pt_leaf_iter leaf_it;
|
||||||
|
bool have_held_leaf;
|
||||||
|
struct pt_leaf held_leaf;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mem_range_iter_init(struct mem_range_iter *it, struct ppn cr3);
|
||||||
|
void mem_range_iter_init_current(struct mem_range_iter *it);
|
||||||
|
void mem_range_iter_init_custom_start(struct mem_range_iter *it, struct ppn cr3, struct vpn virt);
|
||||||
|
bool mem_range_iter_next(struct mem_range_iter *it, struct mem_range *range_out);
|
||||||
|
|
||||||
|
#endif
|
||||||
9
include/x86_64/ps2_driver.h
Normal file
9
include/x86_64/ps2_driver.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <std.h>
|
||||||
|
|
||||||
|
uint8_t ps2_read_data();
|
||||||
|
void ps2_write_data(uint8_t data);
|
||||||
|
uint8_t ps2_cmd_response(uint8_t cmd);
|
||||||
|
void ps2_cmd_with_data(uint8_t cmd, uint8_t data);
|
||||||
|
void ps2_init();
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef KARLOS_UART_H
|
#ifndef KARLOS_X86_64_UART_H
|
||||||
#define KARLOS_UART_H
|
#define KARLOS_X86_64_UART_H
|
||||||
|
|
||||||
void uart_write_char(char c);
|
void uart_write_char(char c);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
qemu-system-x86_64 -drive if=ide,format=raw,file=build/disk.img -drive if=virtio,media=disk,format=raw,file=drive.img "$@"
|
qemu-system-x86_64 -drive if=ide,format=raw,file=build/disk.img -drive if=virtio,media=disk,format=raw,file=drive.img -d int "$@"
|
||||||
|
|
|
||||||
146
src/kernel.c
146
src/kernel.c
|
|
@ -30,59 +30,25 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
|
|
||||||
#include "x86_64/apic.h"
|
|
||||||
#include "x86_64/asm.h"
|
|
||||||
#include "x86_64/address.h"
|
|
||||||
#include "x86_64/mem.h"
|
|
||||||
|
|
||||||
#include "bootboot.h"
|
#include "bootboot.h"
|
||||||
#include "ram.h"
|
#include "ram.h"
|
||||||
#include "pci.h"
|
|
||||||
#include "std.h"
|
#include "std.h"
|
||||||
#include "tar.h"
|
|
||||||
#include "framebuffer.h"
|
#include "framebuffer.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "paging.h"
|
||||||
|
|
||||||
|
#include "x86_64/ps2_driver.h"
|
||||||
|
|
||||||
/* imported virtual addresses, see linker script */
|
/* imported virtual addresses, see linker script */
|
||||||
extern BOOTBOOT bootboot; // see bootboot.h
|
extern BOOTBOOT bootboot; // see bootboot.h
|
||||||
extern unsigned char environment[4096]; // configuration, UTF-8 text key=value pairs
|
extern unsigned char environment[4096]; // configuration, UTF-8 text key=value pairs
|
||||||
extern uint8_t fb; // linear framebuffer mapped
|
extern uint8_t fb; // linear framebuffer mapped
|
||||||
|
|
||||||
int temp_global_var = 5; // TODO remove
|
void basic_interrupt_handler(void) {
|
||||||
|
printf("Hello from interrupt!\n");
|
||||||
void print_virtio_blk_bars() {
|
|
||||||
uint16_t bdf;
|
|
||||||
if (pci_search(0x1AF4, 0x1001, &bdf) && pci_search(0x1AF4, 0x1042, &bdf)) {
|
|
||||||
PANIC("couldn't find virtio_blk device!");
|
|
||||||
}
|
|
||||||
printf("found virtio_blk device at bdf %X16.\n", bdf);
|
|
||||||
// read BARs
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
struct pci_bar_desc desc = pci_bar_desc_read(bdf, i);
|
|
||||||
pci_bar_desc_print(&desc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_initrd() {
|
|
||||||
putln();
|
|
||||||
uint64_t printed_in_line = 0;
|
|
||||||
for (uint64_t off = 0; off < bootboot.initrd_size; off++) {
|
|
||||||
uint8_t byte = ((uint8_t *)bootboot.initrd_ptr)[off];
|
|
||||||
|
|
||||||
printf("%X8 ", byte);
|
|
||||||
|
|
||||||
if (++printed_in_line % 32 == 0) {
|
|
||||||
printed_in_line = 0;
|
|
||||||
putln();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
putln();
|
|
||||||
}
|
|
||||||
|
|
||||||
void console_init(void);
|
|
||||||
|
|
||||||
struct mem_range range_ptr[10];
|
|
||||||
|
|
||||||
/******************************************
|
/******************************************
|
||||||
* Entry point, called by BOOTBOOT Loader *
|
* Entry point, called by BOOTBOOT Loader *
|
||||||
******************************************/
|
******************************************/
|
||||||
|
|
@ -100,10 +66,7 @@ void _start() {
|
||||||
|
|
||||||
console_init();
|
console_init();
|
||||||
|
|
||||||
// memory stuff
|
cpu_init();
|
||||||
init_gdt();
|
|
||||||
init_idt();
|
|
||||||
lapic_init();
|
|
||||||
ram_init();
|
ram_init();
|
||||||
|
|
||||||
// HACK by luck the frame at phys. addr 0 might be returned by the allocator.
|
// HACK by luck the frame at phys. addr 0 might be returned by the allocator.
|
||||||
|
|
@ -118,97 +81,30 @@ void _start() {
|
||||||
fb_init();
|
fb_init();
|
||||||
console_clear();
|
console_clear();
|
||||||
|
|
||||||
printf("LAPIC ID: %d\n", lapic_get_id());
|
printf("LAPIC ID: %d\n", cpu_get_core_id());
|
||||||
|
|
||||||
#if 0
|
interrupt_handler_register(0xf1, basic_interrupt_handler);
|
||||||
struct tar_header hd;
|
__asm__("int $0xf1" :: );
|
||||||
int res = tar_get_file("hello.txt", &hd);
|
__asm__("int $0xf7" :: );
|
||||||
ASSERT(res == 1);
|
|
||||||
printf("%s %X32\n", hd.name, hd.size);
|
|
||||||
for (uint64_t i = 0; i < hd.size; i++) {
|
|
||||||
putc(((char *)hd.data)[i]);
|
|
||||||
}
|
|
||||||
putln();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
printf("Core ID: %d\n", cpu_get_core_id());
|
||||||
for (int l = 0; l < 100; l++) {
|
|
||||||
for (int i = 0; i < 300; i++) {
|
|
||||||
printf("line %d ", l);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__asm__("sti");
|
__asm__("sti");
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
lapic_set_timer();
|
cpu_set_timer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 1
|
ps2_init();
|
||||||
struct mem_range_buf buf_out = { .ptr = range_ptr, .next_entry = 0, .num_entries = 10 };
|
while (1) {
|
||||||
pt_get_ranges(&buf_out);
|
// do nothing. PS2 controller should send interrupts.
|
||||||
FOR_MEM_RANGE_IN(curr_range, &buf_out) {
|
|
||||||
mem_range_print(curr_range);
|
// uint8_t data = ps2_read_data();
|
||||||
|
// putu8x(data);
|
||||||
|
// putln();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
pt_create_minimal();
|
||||||
// get mmapents
|
|
||||||
for (MMapEnt *mmap_ent = &bootboot.mmap;
|
|
||||||
mmap_ent < (MMapEnt*)((char*)&bootboot + bootboot.size);
|
|
||||||
mmap_ent++)
|
|
||||||
{
|
|
||||||
printf("MMapEnt {\n");
|
|
||||||
printf(" ptr=");
|
|
||||||
putu64x(MMapEnt_Ptr(mmap_ent));
|
|
||||||
printf("\n");
|
|
||||||
printf(" size=");
|
|
||||||
putu64x(MMapEnt_Size(mmap_ent));
|
|
||||||
printf("\n");
|
|
||||||
char *type = NULL;
|
|
||||||
switch (MMapEnt_Type(mmap_ent)) {
|
|
||||||
case MMAP_USED:
|
|
||||||
type = "used";
|
|
||||||
break;
|
|
||||||
case MMAP_FREE:
|
|
||||||
type = "free";
|
|
||||||
break;
|
|
||||||
case MMAP_ACPI:
|
|
||||||
type = "acpi";
|
|
||||||
break;
|
|
||||||
case MMAP_MMIO:
|
|
||||||
type = "mmio";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
printf(" type=%s\n", type);
|
|
||||||
printf("}\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
int temp_local_var = 5;
|
|
||||||
temp_global_var = 6;
|
|
||||||
temp_local_var = 6;
|
|
||||||
struct pa pa;
|
|
||||||
ASSERT(pt_translate_current(va_from_canonical((uint64_t)(intptr_t)&temp_global_var), &pa));
|
|
||||||
uint64_t data_phys = pa_to_value(pa);
|
|
||||||
ASSERT(pt_translate_current(va_from_canonical((uint64_t)(intptr_t)&temp_local_var), &pa));
|
|
||||||
uint64_t stack_phys = pa_to_value(pa);
|
|
||||||
ASSERT(pt_translate_current(va_from_canonical((uint64_t)(intptr_t)&_start), &pa));
|
|
||||||
uint64_t code_phys = pa_to_value(pa);
|
|
||||||
printf("data: %p -> %p\n", &temp_global_var, data_phys);
|
|
||||||
printf("stack: %p -> %p\n", &temp_local_var, stack_phys);
|
|
||||||
printf("code: %p -> %p\n", &check_initrd, code_phys);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
uint64_t cr0 = get_cr0();
|
|
||||||
printf("wp=%d\n", (cr0 >> 16) & 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// hang for now
|
// hang for now
|
||||||
PANIC("end of kernel");
|
PANIC("end of kernel");
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "std.h"
|
#include "std.h"
|
||||||
#include "x86_64/uart.h"
|
#include "serial.h"
|
||||||
|
|
||||||
#define BUFFER_SIZE 1024
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
|
@ -71,9 +71,9 @@ static unsigned int current_buffer_position = 0;
|
||||||
|
|
||||||
static void linebuf_flush(void) {
|
static void linebuf_flush(void) {
|
||||||
for (unsigned int i = 0; i < current_buffer_position; i++) {
|
for (unsigned int i = 0; i < current_buffer_position; i++) {
|
||||||
uart_write_char(linebuf[i]);
|
serial_write_char(linebuf[i]);
|
||||||
extern void visual_putc(char c);
|
// extern void visual_putc(char c);
|
||||||
visual_putc(linebuf[i]);
|
// visual_putc(linebuf[i]);
|
||||||
}
|
}
|
||||||
current_buffer_position = 0;
|
current_buffer_position = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
#include "x86_64/address.h"
|
#include "address.h"
|
||||||
#include "std.h"
|
#include "std.h"
|
||||||
|
|
||||||
#define MEM_SIZE (16ull * 1024 * 1024 * 1024)
|
uint64_t identity_mapping_start = 0;
|
||||||
#define IDMAP_END 0x400000000ull
|
|
||||||
#define IDMAP_START (IDMAP_END - MEM_SIZE)
|
|
||||||
#define PHYS_TO_IDMAPPED(addr) ((addr) + IDMAP_START)
|
|
||||||
|
|
||||||
struct va va_from_value(uint64_t value) {
|
struct va va_from_value(uint64_t value) {
|
||||||
ASSERT(value < (1ull << 48));
|
ASSERT(value < (1ull << 48));
|
||||||
|
|
@ -45,6 +42,14 @@ uint64_t va_offset(struct va va) {
|
||||||
return va.value & 0xfff;
|
return va.value & 0xfff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t va_offset_huge_2mb(struct va va) {
|
||||||
|
return va.value & 0x1fffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t va_offset_huge_1gb(struct va va) {
|
||||||
|
return va.value & 0x3fffffff;
|
||||||
|
}
|
||||||
|
|
||||||
struct vpn vpn_from_pagenum(uint64_t pagenum) {
|
struct vpn vpn_from_pagenum(uint64_t pagenum) {
|
||||||
ASSERT(pagenum < (1ull << 36));
|
ASSERT(pagenum < (1ull << 36));
|
||||||
return (struct vpn){ .pagenum = pagenum };
|
return (struct vpn){ .pagenum = pagenum };
|
||||||
|
|
@ -63,6 +68,20 @@ uint64_t vpn_to_pagenum(struct vpn vpn) {
|
||||||
return vpn.pagenum;
|
return vpn.pagenum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int vpn_level_idx(struct vpn vpn, unsigned int level) {
|
||||||
|
ASSERT(level >= 1 && level <= 4);
|
||||||
|
|
||||||
|
return (vpn_to_pagenum(vpn) >> ((level - 1)*9)) & 0x1ffull;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vpn_is_huge_2mb(struct vpn vpn) {
|
||||||
|
return (vpn_to_pagenum(vpn) & ((1ull << 9) - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vpn_is_huge_1gb(struct vpn vpn) {
|
||||||
|
return (vpn_to_pagenum(vpn) & ((1ull << 18) - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct pa pa_from_value(uint64_t value) {
|
struct pa pa_from_value(uint64_t value) {
|
||||||
ASSERT(value < (1ull << 36));
|
ASSERT(value < (1ull << 36));
|
||||||
return (struct pa){ .value = value };
|
return (struct pa){ .value = value };
|
||||||
|
|
@ -77,12 +96,29 @@ struct pa pa_from_ppn_with_offset(struct ppn ppn, uint64_t offset) {
|
||||||
return pa_from_value((ppn.pagenum << 12) + offset);
|
return pa_from_value((ppn.pagenum << 12) + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct pa pa_from_ppn_huge_2mb_with_offset(struct ppn ppn, uint64_t offset) {
|
||||||
|
ASSERT(ppn_is_huge_2mb(ppn));
|
||||||
|
ASSERT(offset < (1ull << 21));
|
||||||
|
return pa_from_value((ppn.pagenum << 12) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pa pa_from_ppn_huge_1gb_with_offset(struct ppn ppn, uint64_t offset) {
|
||||||
|
ASSERT(ppn_is_huge_1gb(ppn));
|
||||||
|
ASSERT(offset < (1ull << 30));
|
||||||
|
return pa_from_value((ppn.pagenum << 12) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pa pa_from_pt_with_idx(struct ppn ppn, unsigned int idx) {
|
||||||
|
return pa_from_ppn_with_offset(ppn, (uint64_t)idx << 3);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t pa_to_value(struct pa pa) {
|
uint64_t pa_to_value(struct pa pa) {
|
||||||
return pa.value;
|
return pa.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *pa_to_pointer(struct pa pa) {
|
void *pa_to_pointer(struct pa pa) {
|
||||||
return (void*)PHYS_TO_IDMAPPED(pa.value);
|
struct va va = va_from_value(pa_to_value(pa) + identity_mapping_start);
|
||||||
|
return (void *)va_to_canonical(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t pa_offset(struct pa pa) {
|
uint64_t pa_offset(struct pa pa) {
|
||||||
|
|
@ -106,3 +142,15 @@ struct ppn ppn_from_unaligned_pa(struct pa pa) {
|
||||||
uint64_t ppn_to_pagenum(struct ppn ppn) {
|
uint64_t ppn_to_pagenum(struct ppn ppn) {
|
||||||
return ppn.pagenum;
|
return ppn.pagenum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ppn_is_huge_2mb(struct ppn ppn) {
|
||||||
|
return (ppn_to_pagenum(ppn) & ((1ull << 9) - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ppn_is_huge_1gb(struct ppn ppn) {
|
||||||
|
return (ppn_to_pagenum(ppn) & ((1ull << 18) - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_identity_mapping(struct vpn first_page) {
|
||||||
|
identity_mapping_start = va_to_value(va_from_vpn(first_page));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
|
#include "x86_64/asm.h"
|
||||||
#include "x86_64/apic.h"
|
#include "x86_64/apic.h"
|
||||||
|
#include "address.h"
|
||||||
#include "std.h"
|
#include "std.h"
|
||||||
|
|
||||||
#define MEM_SIZE (16ull * 1024 * 1024 * 1024)
|
#define LAPIC_BASE_PHYS 0xFEE00000
|
||||||
#define IDMAP_END 0x400000000ull
|
|
||||||
#define IDMAP_START (IDMAP_END - MEM_SIZE)
|
|
||||||
#define PHYS_TO_IDMAPPED(addr) ((addr) + IDMAP_START)
|
|
||||||
|
|
||||||
#define LAPIC_BASE 0xFEE00000
|
|
||||||
|
|
||||||
#define SPURIOUS_VECTOR_APIC_ENABLE 0x100
|
#define SPURIOUS_VECTOR_APIC_ENABLE 0x100
|
||||||
|
|
||||||
|
|
@ -47,31 +44,44 @@ struct lapic {
|
||||||
struct lapic_register timer_divisor;
|
struct lapic_register timer_divisor;
|
||||||
struct lapic_register reserved4[1];
|
struct lapic_register reserved4[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof (struct lapic) == 0x400);
|
STATIC_ASSERT(sizeof (struct lapic) == 0x400);
|
||||||
|
|
||||||
static struct lapic *lapic = (struct lapic *)PHYS_TO_IDMAPPED(LAPIC_BASE);
|
#define LAPIC_ADDR ((volatile struct lapic *)pa_to_pointer(pa_from_value(LAPIC_BASE_PHYS)))
|
||||||
|
|
||||||
void lapic_init(void)
|
void lapic_init(void)
|
||||||
{
|
{
|
||||||
ASSERT((lapic->lapic_version.value & 0xff) <= 0x15);
|
ASSERT((LAPIC_ADDR->lapic_version.value & 0xff) <= 0x15);
|
||||||
|
|
||||||
lapic->spurious_vector.value = SPURIOUS_VECTOR_APIC_ENABLE | 0xff;
|
LAPIC_ADDR->spurious_vector.value = SPURIOUS_VECTOR_APIC_ENABLE | 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned lapic_get_id(void)
|
unsigned lapic_get_id(void)
|
||||||
{
|
{
|
||||||
return lapic->lapic_id.value >> 24;
|
return LAPIC_ADDR->lapic_id.value >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cpu_get_core_id(void) {
|
||||||
|
return lapic_get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
void lapic_eoi(void)
|
void lapic_eoi(void)
|
||||||
{
|
{
|
||||||
lapic->eoi.value = 0;
|
LAPIC_ADDR->eoi.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lapic_set_timer(void)
|
void lapic_set_timer(void)
|
||||||
{
|
{
|
||||||
lapic->timer_divisor.value = 0b1010;
|
LAPIC_ADDR->timer_divisor.value = 0b1010;
|
||||||
lapic->lvt_timer.value = 0xFE | (1 << 17);
|
LAPIC_ADDR->lvt_timer.value = 0xFE | (1 << 17);
|
||||||
lapic->timer_initial.value = 0x1000000;
|
LAPIC_ADDR->timer_initial.value = 0x1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pic_disable(void) {
|
||||||
|
// https://wiki.osdev.org/8259_PIC#Programming_the_PIC_chips
|
||||||
|
#define PIC1 0x20
|
||||||
|
#define PIC2 0xA0
|
||||||
|
#define PIC1_DATA (PIC1 + 1)
|
||||||
|
#define PIC2_DATA (PIC2 + 1)
|
||||||
|
out8(PIC1_DATA, 0xff);
|
||||||
|
out8(PIC2_DATA, 0xff);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ uint64_t get_cr3(void) {
|
||||||
return cr3;
|
return cr3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_cr3(uint64_t value) {
|
||||||
|
__asm__("mov %0, %%cr3" :: "r"(value));
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t get_cr4(void) {
|
uint64_t get_cr4(void) {
|
||||||
uint64_t cr4;
|
uint64_t cr4;
|
||||||
__asm__("mov %%cr4, %0" : "=r"(cr4)::);
|
__asm__("mov %%cr4, %0" : "=r"(cr4)::);
|
||||||
|
|
|
||||||
466
src/x86_64/cpu.c
Normal file
466
src/x86_64/cpu.c
Normal file
|
|
@ -0,0 +1,466 @@
|
||||||
|
#include "x86_64/apic.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "std.h"
|
||||||
|
|
||||||
|
// --- segmentation ---
|
||||||
|
|
||||||
|
extern void loadcs(uint16_t ss, uint16_t cs);
|
||||||
|
|
||||||
|
static uint64_t gdt[3];
|
||||||
|
|
||||||
|
static void write_segment_descriptor(uint64_t *entry, uint8_t dpl, uint8_t executable) {
|
||||||
|
uint8_t access_byte = (1 << 7) // present bit
|
||||||
|
| (dpl << 5) | (1 << 4) // S
|
||||||
|
| (executable << 3) | (0 << 2) // DC
|
||||||
|
| (1 << 1) // RW
|
||||||
|
| (0 << 0); // A
|
||||||
|
|
||||||
|
uint8_t flags = (1 << 3) // G
|
||||||
|
| (0 << 2) // DB
|
||||||
|
| (1 << 1) // L
|
||||||
|
| (0 << 0); // reserved
|
||||||
|
|
||||||
|
*entry = 0xffff // limit
|
||||||
|
| ((uint64_t)0x0000 << 16) // base
|
||||||
|
| ((uint64_t)0x00 << 32) // base
|
||||||
|
| ((uint64_t)access_byte << 40) // access byte
|
||||||
|
| ((uint64_t)0xf << 48) // limit
|
||||||
|
| ((uint64_t)flags << 52) // flags
|
||||||
|
| ((uint64_t)0x00 << 56); // base
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CODE_SEGMENT 1
|
||||||
|
#define DATA_SEGMENT 2
|
||||||
|
|
||||||
|
void init_gdt() {
|
||||||
|
gdt[0] = 0;
|
||||||
|
write_segment_descriptor(&gdt[CODE_SEGMENT], 0, 1);
|
||||||
|
write_segment_descriptor(&gdt[DATA_SEGMENT], 0, 0);
|
||||||
|
|
||||||
|
uint8_t gdtr[10];
|
||||||
|
|
||||||
|
*(uint16_t *)gdtr = sizeof(gdt) - 1;
|
||||||
|
*(uint64_t **)(gdtr + 2) = (uint64_t *)gdt;
|
||||||
|
|
||||||
|
__asm__("lgdt (%0)" ::"r"(gdtr));
|
||||||
|
|
||||||
|
__asm__("mov %0, %%es" ::"r"(DATA_SEGMENT << 3));
|
||||||
|
__asm__("mov %0, %%ds" ::"r"(DATA_SEGMENT << 3));
|
||||||
|
__asm__("mov %0, %%fs" ::"r"(DATA_SEGMENT << 3));
|
||||||
|
__asm__("mov %0, %%gs" ::"r"(DATA_SEGMENT << 3));
|
||||||
|
loadcs(DATA_SEGMENT << 3, CODE_SEGMENT << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- interrupts ---
|
||||||
|
|
||||||
|
struct int_desc_entry {
|
||||||
|
uint16_t offset1;
|
||||||
|
uint16_t selector;
|
||||||
|
uint8_t ist;
|
||||||
|
uint8_t attr;
|
||||||
|
uint16_t offset2;
|
||||||
|
uint32_t offset3;
|
||||||
|
uint32_t pad;
|
||||||
|
} __attribute__((aligned(16))); // TODO not sure if this is necessary
|
||||||
|
STATIC_ASSERT(sizeof(struct int_desc_entry) == 16);
|
||||||
|
|
||||||
|
#define PRIV_KERNEL 0
|
||||||
|
#define PRIV_USER 3
|
||||||
|
|
||||||
|
static void write_segment_selector(uint16_t *ss, uint16_t index) {
|
||||||
|
*ss = (uint16_t)PRIV_KERNEL
|
||||||
|
| 0 // use GDT
|
||||||
|
| (index << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GATE_TYPE_INTERRUPT 0xe
|
||||||
|
|
||||||
|
static void write_int_desc_entry(struct int_desc_entry *e, uint64_t offset) {
|
||||||
|
e->offset1 = (uint16_t)(offset & 0xffff);
|
||||||
|
write_segment_selector(&e->selector, CODE_SEGMENT);
|
||||||
|
e->ist = 0;
|
||||||
|
e->attr = (uint8_t)GATE_TYPE_INTERRUPT
|
||||||
|
| (uint8_t)(PRIV_KERNEL << 5)
|
||||||
|
| (uint8_t)(1 << 7); // present
|
||||||
|
e->offset2 = (uint16_t) ((offset >> 16) & 0xffff);
|
||||||
|
e->offset3 = (uint32_t) ((offset >> 32) & 0xffffffff);
|
||||||
|
e->pad = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_INTERRUPTS 256
|
||||||
|
static struct int_desc_entry idt[NUM_INTERRUPTS];
|
||||||
|
|
||||||
|
void load_idt() {
|
||||||
|
uint8_t idtr[10];
|
||||||
|
|
||||||
|
*(uint16_t *)idtr = sizeof(idt) - 1;
|
||||||
|
*(uint64_t **)(idtr + 2) = (uint64_t *)idt;
|
||||||
|
|
||||||
|
__asm__("lidt (%0)" ::"r"(idtr));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NUM_INTERRUPT_HANDLERS 32
|
||||||
|
struct {
|
||||||
|
uint8_t vector;
|
||||||
|
void (*handler)(void);
|
||||||
|
} interrupt_handlers[NUM_INTERRUPT_HANDLERS];
|
||||||
|
unsigned int num_interrupt_handlers = 0;
|
||||||
|
|
||||||
|
void interrupt_handler_register(unsigned int vector, void (*handler)(void)) {
|
||||||
|
ASSERT(vector <= 0xff);
|
||||||
|
ASSERT(num_interrupt_handlers < NUM_INTERRUPT_HANDLERS);
|
||||||
|
unsigned int i = num_interrupt_handlers++;
|
||||||
|
interrupt_handlers[i].vector = vector;
|
||||||
|
interrupt_handlers[i].handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEBUG_INTERRUPT
|
||||||
|
|
||||||
|
void common_isr_2(uint64_t num) {
|
||||||
|
#ifdef DEBUG_INTERRUPT
|
||||||
|
printf("DEBUG: Interrupt %lu\n", num);
|
||||||
|
#endif
|
||||||
|
for (unsigned int i = 0; i < num_interrupt_handlers; i++) {
|
||||||
|
if (interrupt_handlers[i].vector == num) {
|
||||||
|
interrupt_handlers[i].handler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lapic_eoi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO This is done manually because we need to know how the stack looks.
|
||||||
|
__attribute__((naked))
|
||||||
|
void common_isr(void) {
|
||||||
|
__asm__("push %%rbp" :: );
|
||||||
|
__asm__("mov %%rsp,%%rbp" :: );
|
||||||
|
__asm__("push %%r11" :: );
|
||||||
|
__asm__("push %%r10" :: );
|
||||||
|
__asm__("push %%r9" :: );
|
||||||
|
__asm__("push %%r8" :: );
|
||||||
|
__asm__("push %%rdi" :: );
|
||||||
|
__asm__("push %%rsi" :: );
|
||||||
|
__asm__("push %%rcx" :: );
|
||||||
|
__asm__("push %%rdx" :: );
|
||||||
|
__asm__("push %%rax" :: );
|
||||||
|
|
||||||
|
// https://wiki.osdev.org/Interrupt_Service_Routines
|
||||||
|
// "C code following the sysV ABI requires DF to be clear on function entry"
|
||||||
|
__asm__("cld");
|
||||||
|
|
||||||
|
// get interrupt number and call "real" handler
|
||||||
|
__asm__("mov 0x8(%%rbp),%%rdi" :: );
|
||||||
|
__asm__("call common_isr_2" :: );
|
||||||
|
|
||||||
|
__asm__("pop %%rax" :: );
|
||||||
|
__asm__("pop %%rdx" :: );
|
||||||
|
__asm__("pop %%rcx" :: );
|
||||||
|
__asm__("pop %%rsi" :: );
|
||||||
|
__asm__("pop %%rdi" :: );
|
||||||
|
__asm__("pop %%r8" :: );
|
||||||
|
__asm__("pop %%r9" :: );
|
||||||
|
__asm__("pop %%r10" :: );
|
||||||
|
__asm__("pop %%r11" :: );
|
||||||
|
__asm__("pop %%rbp" :: );
|
||||||
|
// remove interrupt number and error code pushed in the isr stub (16 bytes)
|
||||||
|
__asm__("add $0x10,%%rsp" :: );
|
||||||
|
__asm__("iretq" :: );
|
||||||
|
}
|
||||||
|
|
||||||
|
// this has to be set manually.
|
||||||
|
#define ISR_STUB_SIZE 16
|
||||||
|
|
||||||
|
// absolutely horrifying stringify thing
|
||||||
|
// https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html
|
||||||
|
#define MY_XSTR(s) MY_STR(s)
|
||||||
|
#define MY_STR(s) #s
|
||||||
|
|
||||||
|
// TODO removed cli for now
|
||||||
|
#define ISR_STUB_NO_PUSH_ERROR(n) __asm__( \
|
||||||
|
".align " MY_XSTR(ISR_STUB_SIZE) "\n\t" \
|
||||||
|
"push $" #n "\n\t" \
|
||||||
|
"jmp common_isr\n\t" \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define ISR_STUB(n) __asm__( \
|
||||||
|
".align " MY_XSTR(ISR_STUB_SIZE) "\n\t" \
|
||||||
|
"push $0 \n\t" \
|
||||||
|
"push $" #n "\n\t" \
|
||||||
|
"jmp common_isr\n\t" \
|
||||||
|
)
|
||||||
|
|
||||||
|
// error code or not: see https://wiki.osdev.org/Exceptions
|
||||||
|
__attribute__((aligned(ISR_STUB_SIZE)))
|
||||||
|
__attribute__((naked))
|
||||||
|
void inttable(void) {
|
||||||
|
ISR_STUB(0);
|
||||||
|
ISR_STUB(1);
|
||||||
|
ISR_STUB(2);
|
||||||
|
ISR_STUB(3);
|
||||||
|
ISR_STUB(4);
|
||||||
|
ISR_STUB(5);
|
||||||
|
ISR_STUB(6);
|
||||||
|
ISR_STUB(7);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(8);
|
||||||
|
ISR_STUB(9);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(10);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(11);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(12);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(13);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(14);
|
||||||
|
ISR_STUB(15);
|
||||||
|
ISR_STUB(16);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(17);
|
||||||
|
ISR_STUB(18);
|
||||||
|
ISR_STUB(19);
|
||||||
|
ISR_STUB(20);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(21);
|
||||||
|
ISR_STUB(22);
|
||||||
|
ISR_STUB(23);
|
||||||
|
ISR_STUB(24);
|
||||||
|
ISR_STUB(25);
|
||||||
|
ISR_STUB(26);
|
||||||
|
ISR_STUB(27);
|
||||||
|
ISR_STUB(28);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(29);
|
||||||
|
ISR_STUB_NO_PUSH_ERROR(30);
|
||||||
|
ISR_STUB(31);
|
||||||
|
ISR_STUB(32);
|
||||||
|
ISR_STUB(33);
|
||||||
|
ISR_STUB(34);
|
||||||
|
ISR_STUB(35);
|
||||||
|
ISR_STUB(36);
|
||||||
|
ISR_STUB(37);
|
||||||
|
ISR_STUB(38);
|
||||||
|
ISR_STUB(39);
|
||||||
|
ISR_STUB(40);
|
||||||
|
ISR_STUB(41);
|
||||||
|
ISR_STUB(42);
|
||||||
|
ISR_STUB(43);
|
||||||
|
ISR_STUB(44);
|
||||||
|
ISR_STUB(45);
|
||||||
|
ISR_STUB(46);
|
||||||
|
ISR_STUB(47);
|
||||||
|
ISR_STUB(48);
|
||||||
|
ISR_STUB(49);
|
||||||
|
ISR_STUB(50);
|
||||||
|
ISR_STUB(51);
|
||||||
|
ISR_STUB(52);
|
||||||
|
ISR_STUB(53);
|
||||||
|
ISR_STUB(54);
|
||||||
|
ISR_STUB(55);
|
||||||
|
ISR_STUB(56);
|
||||||
|
ISR_STUB(57);
|
||||||
|
ISR_STUB(58);
|
||||||
|
ISR_STUB(59);
|
||||||
|
ISR_STUB(60);
|
||||||
|
ISR_STUB(61);
|
||||||
|
ISR_STUB(62);
|
||||||
|
ISR_STUB(63);
|
||||||
|
ISR_STUB(64);
|
||||||
|
ISR_STUB(65);
|
||||||
|
ISR_STUB(66);
|
||||||
|
ISR_STUB(67);
|
||||||
|
ISR_STUB(68);
|
||||||
|
ISR_STUB(69);
|
||||||
|
ISR_STUB(70);
|
||||||
|
ISR_STUB(71);
|
||||||
|
ISR_STUB(72);
|
||||||
|
ISR_STUB(73);
|
||||||
|
ISR_STUB(74);
|
||||||
|
ISR_STUB(75);
|
||||||
|
ISR_STUB(76);
|
||||||
|
ISR_STUB(77);
|
||||||
|
ISR_STUB(78);
|
||||||
|
ISR_STUB(79);
|
||||||
|
ISR_STUB(80);
|
||||||
|
ISR_STUB(81);
|
||||||
|
ISR_STUB(82);
|
||||||
|
ISR_STUB(83);
|
||||||
|
ISR_STUB(84);
|
||||||
|
ISR_STUB(85);
|
||||||
|
ISR_STUB(86);
|
||||||
|
ISR_STUB(87);
|
||||||
|
ISR_STUB(88);
|
||||||
|
ISR_STUB(89);
|
||||||
|
ISR_STUB(90);
|
||||||
|
ISR_STUB(91);
|
||||||
|
ISR_STUB(92);
|
||||||
|
ISR_STUB(93);
|
||||||
|
ISR_STUB(94);
|
||||||
|
ISR_STUB(95);
|
||||||
|
ISR_STUB(96);
|
||||||
|
ISR_STUB(97);
|
||||||
|
ISR_STUB(98);
|
||||||
|
ISR_STUB(99);
|
||||||
|
ISR_STUB(100);
|
||||||
|
ISR_STUB(101);
|
||||||
|
ISR_STUB(102);
|
||||||
|
ISR_STUB(103);
|
||||||
|
ISR_STUB(104);
|
||||||
|
ISR_STUB(105);
|
||||||
|
ISR_STUB(106);
|
||||||
|
ISR_STUB(107);
|
||||||
|
ISR_STUB(108);
|
||||||
|
ISR_STUB(109);
|
||||||
|
ISR_STUB(110);
|
||||||
|
ISR_STUB(111);
|
||||||
|
ISR_STUB(112);
|
||||||
|
ISR_STUB(113);
|
||||||
|
ISR_STUB(114);
|
||||||
|
ISR_STUB(115);
|
||||||
|
ISR_STUB(116);
|
||||||
|
ISR_STUB(117);
|
||||||
|
ISR_STUB(118);
|
||||||
|
ISR_STUB(119);
|
||||||
|
ISR_STUB(120);
|
||||||
|
ISR_STUB(121);
|
||||||
|
ISR_STUB(122);
|
||||||
|
ISR_STUB(123);
|
||||||
|
ISR_STUB(124);
|
||||||
|
ISR_STUB(125);
|
||||||
|
ISR_STUB(126);
|
||||||
|
ISR_STUB(127);
|
||||||
|
ISR_STUB(128);
|
||||||
|
ISR_STUB(129);
|
||||||
|
ISR_STUB(130);
|
||||||
|
ISR_STUB(131);
|
||||||
|
ISR_STUB(132);
|
||||||
|
ISR_STUB(133);
|
||||||
|
ISR_STUB(134);
|
||||||
|
ISR_STUB(135);
|
||||||
|
ISR_STUB(136);
|
||||||
|
ISR_STUB(137);
|
||||||
|
ISR_STUB(138);
|
||||||
|
ISR_STUB(139);
|
||||||
|
ISR_STUB(140);
|
||||||
|
ISR_STUB(141);
|
||||||
|
ISR_STUB(142);
|
||||||
|
ISR_STUB(143);
|
||||||
|
ISR_STUB(144);
|
||||||
|
ISR_STUB(145);
|
||||||
|
ISR_STUB(146);
|
||||||
|
ISR_STUB(147);
|
||||||
|
ISR_STUB(148);
|
||||||
|
ISR_STUB(149);
|
||||||
|
ISR_STUB(150);
|
||||||
|
ISR_STUB(151);
|
||||||
|
ISR_STUB(152);
|
||||||
|
ISR_STUB(153);
|
||||||
|
ISR_STUB(154);
|
||||||
|
ISR_STUB(155);
|
||||||
|
ISR_STUB(156);
|
||||||
|
ISR_STUB(157);
|
||||||
|
ISR_STUB(158);
|
||||||
|
ISR_STUB(159);
|
||||||
|
ISR_STUB(160);
|
||||||
|
ISR_STUB(161);
|
||||||
|
ISR_STUB(162);
|
||||||
|
ISR_STUB(163);
|
||||||
|
ISR_STUB(164);
|
||||||
|
ISR_STUB(165);
|
||||||
|
ISR_STUB(166);
|
||||||
|
ISR_STUB(167);
|
||||||
|
ISR_STUB(168);
|
||||||
|
ISR_STUB(169);
|
||||||
|
ISR_STUB(170);
|
||||||
|
ISR_STUB(171);
|
||||||
|
ISR_STUB(172);
|
||||||
|
ISR_STUB(173);
|
||||||
|
ISR_STUB(174);
|
||||||
|
ISR_STUB(175);
|
||||||
|
ISR_STUB(176);
|
||||||
|
ISR_STUB(177);
|
||||||
|
ISR_STUB(178);
|
||||||
|
ISR_STUB(179);
|
||||||
|
ISR_STUB(180);
|
||||||
|
ISR_STUB(181);
|
||||||
|
ISR_STUB(182);
|
||||||
|
ISR_STUB(183);
|
||||||
|
ISR_STUB(184);
|
||||||
|
ISR_STUB(185);
|
||||||
|
ISR_STUB(186);
|
||||||
|
ISR_STUB(187);
|
||||||
|
ISR_STUB(188);
|
||||||
|
ISR_STUB(189);
|
||||||
|
ISR_STUB(190);
|
||||||
|
ISR_STUB(191);
|
||||||
|
ISR_STUB(192);
|
||||||
|
ISR_STUB(193);
|
||||||
|
ISR_STUB(194);
|
||||||
|
ISR_STUB(195);
|
||||||
|
ISR_STUB(196);
|
||||||
|
ISR_STUB(197);
|
||||||
|
ISR_STUB(198);
|
||||||
|
ISR_STUB(199);
|
||||||
|
ISR_STUB(200);
|
||||||
|
ISR_STUB(201);
|
||||||
|
ISR_STUB(202);
|
||||||
|
ISR_STUB(203);
|
||||||
|
ISR_STUB(204);
|
||||||
|
ISR_STUB(205);
|
||||||
|
ISR_STUB(206);
|
||||||
|
ISR_STUB(207);
|
||||||
|
ISR_STUB(208);
|
||||||
|
ISR_STUB(209);
|
||||||
|
ISR_STUB(210);
|
||||||
|
ISR_STUB(211);
|
||||||
|
ISR_STUB(212);
|
||||||
|
ISR_STUB(213);
|
||||||
|
ISR_STUB(214);
|
||||||
|
ISR_STUB(215);
|
||||||
|
ISR_STUB(216);
|
||||||
|
ISR_STUB(217);
|
||||||
|
ISR_STUB(218);
|
||||||
|
ISR_STUB(219);
|
||||||
|
ISR_STUB(220);
|
||||||
|
ISR_STUB(221);
|
||||||
|
ISR_STUB(222);
|
||||||
|
ISR_STUB(223);
|
||||||
|
ISR_STUB(224);
|
||||||
|
ISR_STUB(225);
|
||||||
|
ISR_STUB(226);
|
||||||
|
ISR_STUB(227);
|
||||||
|
ISR_STUB(228);
|
||||||
|
ISR_STUB(229);
|
||||||
|
ISR_STUB(230);
|
||||||
|
ISR_STUB(231);
|
||||||
|
ISR_STUB(232);
|
||||||
|
ISR_STUB(233);
|
||||||
|
ISR_STUB(234);
|
||||||
|
ISR_STUB(235);
|
||||||
|
ISR_STUB(236);
|
||||||
|
ISR_STUB(237);
|
||||||
|
ISR_STUB(238);
|
||||||
|
ISR_STUB(239);
|
||||||
|
ISR_STUB(240);
|
||||||
|
ISR_STUB(241);
|
||||||
|
ISR_STUB(242);
|
||||||
|
ISR_STUB(243);
|
||||||
|
ISR_STUB(244);
|
||||||
|
ISR_STUB(245);
|
||||||
|
ISR_STUB(246);
|
||||||
|
ISR_STUB(247);
|
||||||
|
ISR_STUB(248);
|
||||||
|
ISR_STUB(249);
|
||||||
|
ISR_STUB(250);
|
||||||
|
ISR_STUB(251);
|
||||||
|
ISR_STUB(252);
|
||||||
|
ISR_STUB(253);
|
||||||
|
ISR_STUB(254);
|
||||||
|
ISR_STUB(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_init(void) {
|
||||||
|
init_gdt();
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
write_int_desc_entry(&idt[i], (uint64_t)(intptr_t)((char*)&inttable + ISR_STUB_SIZE * i));
|
||||||
|
}
|
||||||
|
load_idt();
|
||||||
|
|
||||||
|
pic_disable();
|
||||||
|
lapic_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_set_timer(void) {
|
||||||
|
lapic_set_timer();
|
||||||
|
}
|
||||||
353
src/x86_64/mem.c
353
src/x86_64/mem.c
|
|
@ -1,353 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "x86_64/asm.h"
|
|
||||||
#include "x86_64/mem.h"
|
|
||||||
|
|
||||||
#include "std.h" // remove later, this is only for interrupt handler
|
|
||||||
#include "x86_64/apic.h"
|
|
||||||
|
|
||||||
extern void loadcs(uint16_t ss, uint16_t cs);
|
|
||||||
|
|
||||||
static uint64_t gdt[3];
|
|
||||||
|
|
||||||
static void write_segment_descriptor(uint64_t *entry, uint8_t dpl, uint8_t executable) {
|
|
||||||
uint8_t access_byte = (1 << 7) // present bit
|
|
||||||
| (dpl << 5) | (1 << 4) // S
|
|
||||||
| (executable << 3) | (0 << 2) // DC
|
|
||||||
| (1 << 1) // RW
|
|
||||||
| (0 << 0); // A
|
|
||||||
|
|
||||||
uint8_t flags = (1 << 3) // G
|
|
||||||
| (0 << 2) // DB
|
|
||||||
| (1 << 1) // L
|
|
||||||
| (0 << 0); // reserved
|
|
||||||
|
|
||||||
*entry = 0xffff // limit
|
|
||||||
| ((uint64_t)0x0000 << 16) // base
|
|
||||||
| ((uint64_t)0x00 << 32) // base
|
|
||||||
| ((uint64_t)access_byte << 40) // access byte
|
|
||||||
| ((uint64_t)0xf << 48) // limit
|
|
||||||
| ((uint64_t)flags << 52) // flags
|
|
||||||
| ((uint64_t)0x00 << 56); // base
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CODE_SEGMENT 1
|
|
||||||
#define DATA_SEGMENT 2
|
|
||||||
|
|
||||||
void init_gdt() {
|
|
||||||
gdt[0] = 0;
|
|
||||||
write_segment_descriptor(&gdt[CODE_SEGMENT], 0, 1);
|
|
||||||
write_segment_descriptor(&gdt[DATA_SEGMENT], 0, 0);
|
|
||||||
|
|
||||||
uint8_t gdtr[10];
|
|
||||||
|
|
||||||
*(uint16_t *)gdtr = sizeof(gdt) - 1;
|
|
||||||
*(uint64_t **)(gdtr + 2) = (uint64_t *)gdt;
|
|
||||||
|
|
||||||
__asm__("lgdt (%0)" ::"r"(gdtr));
|
|
||||||
|
|
||||||
__asm__("mov %0, %%es" ::"r"(DATA_SEGMENT << 3));
|
|
||||||
__asm__("mov %0, %%ds" ::"r"(DATA_SEGMENT << 3));
|
|
||||||
__asm__("mov %0, %%fs" ::"r"(DATA_SEGMENT << 3));
|
|
||||||
__asm__("mov %0, %%gs" ::"r"(DATA_SEGMENT << 3));
|
|
||||||
loadcs(DATA_SEGMENT << 3, CODE_SEGMENT << 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct int_desc_entry {
|
|
||||||
uint16_t offset1;
|
|
||||||
uint16_t selector;
|
|
||||||
uint8_t ist;
|
|
||||||
uint8_t attr;
|
|
||||||
uint16_t offset2;
|
|
||||||
uint32_t offset3;
|
|
||||||
uint32_t pad;
|
|
||||||
};
|
|
||||||
STATIC_ASSERT(sizeof(struct int_desc_entry) == 16);
|
|
||||||
|
|
||||||
#define PRIV_KERNEL 0
|
|
||||||
#define PRIV_USER 3
|
|
||||||
|
|
||||||
static void write_segment_selector(uint16_t *ss, uint16_t index) {
|
|
||||||
*ss = (uint16_t)PRIV_KERNEL
|
|
||||||
| 0 // use GDT
|
|
||||||
| (index << 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GATE_TYPE_INTERRUPT 0xe
|
|
||||||
|
|
||||||
static void write_int_desc_entry(struct int_desc_entry *e, uint64_t offset) {
|
|
||||||
e->offset1 = (uint16_t)(offset & 0xffff);
|
|
||||||
write_segment_selector(&e->selector, CODE_SEGMENT);
|
|
||||||
e->ist = 0;
|
|
||||||
e->attr = (uint8_t)GATE_TYPE_INTERRUPT
|
|
||||||
| (uint8_t)(PRIV_KERNEL << 5)
|
|
||||||
| (uint8_t)(1 << 7); // present
|
|
||||||
e->offset2 = (uint16_t) ((offset >> 16) & 0xffff);
|
|
||||||
e->offset3 = (uint32_t) ((offset >> 32) & 0xffffffff);
|
|
||||||
e->pad = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t interrupt_counter;
|
|
||||||
|
|
||||||
__attribute__ ((interrupt))
|
|
||||||
void basic_interrupt_handler(void *ptr) {
|
|
||||||
printf("Hello Interrupt %lu!\n", interrupt_counter);
|
|
||||||
interrupt_counter++;
|
|
||||||
lapic_eoi();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NUM_INTERRUPTS 256
|
|
||||||
static struct int_desc_entry idt[NUM_INTERRUPTS];
|
|
||||||
|
|
||||||
void init_idt() {
|
|
||||||
write_int_desc_entry(&idt[0xFE], (uint64_t)(intptr_t)basic_interrupt_handler);
|
|
||||||
|
|
||||||
uint8_t idtr[10];
|
|
||||||
|
|
||||||
*(uint16_t *)idtr = sizeof(idt) - 1;
|
|
||||||
*(uint64_t **)(idtr + 2) = (uint64_t *)idt;
|
|
||||||
|
|
||||||
__asm__("lidt (%0)" ::"r"(idtr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- paging ---
|
|
||||||
|
|
||||||
uint64_t pt_entry_pack(const struct pt_entry *ent_in) {
|
|
||||||
return (uint64_t)ent_in->present
|
|
||||||
| ((uint64_t)ent_in->writable) << 1
|
|
||||||
| ((uint64_t)ent_in->supervisor) << 2
|
|
||||||
| ((uint64_t)ent_in->writethrough) << 3
|
|
||||||
| ((uint64_t)ent_in->cache_disable) << 4
|
|
||||||
| ((uint64_t)ent_in->accessed) << 5
|
|
||||||
| ((uint64_t)ent_in->dirty) << 6
|
|
||||||
| ((uint64_t)ent_in->page_attr_table_low) << 7
|
|
||||||
| ((uint64_t)ent_in->global) << 8
|
|
||||||
| ((uint64_t)ent_in->page_attr_table_high) << 12
|
|
||||||
| pa_to_value(pa_from_ppn(ent_in->ppn));
|
|
||||||
}
|
|
||||||
|
|
||||||
void pt_entry_unpack(uint64_t ent_in, struct pt_entry *ent_out) {
|
|
||||||
ent_out->present = (ent_in & (0x1ull << 0)) != 0;
|
|
||||||
ent_out->writable = (ent_in & (0x1ull << 1)) != 0;
|
|
||||||
ent_out->supervisor = (ent_in & (0x1ull << 2)) != 0;
|
|
||||||
ent_out->writethrough = (ent_in & (0x1ull << 3)) != 0;
|
|
||||||
ent_out->cache_disable = (ent_in & (0x1ull << 4)) != 0;
|
|
||||||
ent_out->accessed = (ent_in & (0x1ull << 5)) != 0;
|
|
||||||
ent_out->dirty = (ent_in & (0x1ull << 6)) != 0;
|
|
||||||
ent_out->page_attr_table_low = (ent_in & (0x1ull << 7)) != 0;
|
|
||||||
ent_out->global = (ent_in & (0x1ull << 8)) != 0;
|
|
||||||
ent_out->page_attr_table_high = (ent_in & (0x1ull << 12)) != 0;
|
|
||||||
// this may panic if the address is 57 bit, which is intended here.
|
|
||||||
ent_out->ppn = ppn_from_aligned_pa(pa_from_value(ent_in & 0x000ffffffffff000ull));
|
|
||||||
}
|
|
||||||
|
|
||||||
void pt_entry_print(const struct pt_entry *ent) {
|
|
||||||
printf("pt_entry {\n");
|
|
||||||
printf(" present: %d\n", (int)ent->present);
|
|
||||||
printf(" writable: %d\n", (int)ent->writable);
|
|
||||||
printf(" supervisor: %d\n", (int)ent->supervisor);
|
|
||||||
printf(" writethrough: %d\n", (int)ent->writethrough);
|
|
||||||
printf(" cache_disable: %d\n", (int)ent->cache_disable);
|
|
||||||
printf(" accessed: %d\n", (int)ent->accessed);
|
|
||||||
printf(" dirty: %d\n", (int)ent->dirty);
|
|
||||||
printf(" page_attr_table_low: %d\n", (int)ent->page_attr_table_low);
|
|
||||||
printf(" global: %d\n", (int)ent->global);
|
|
||||||
printf(" page_attr_table_high: %d\n", (int)ent->page_attr_table_high);
|
|
||||||
printf(" page base: %p\n", pa_from_ppn(ent->ppn));
|
|
||||||
printf("}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ppn get_cr3_ppn(void) {
|
|
||||||
uint64_t cr3_val = get_cr3();
|
|
||||||
return ppn_from_aligned_pa(pa_from_value(cr3_val & 0x000ffffffffff000ull));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NUM_LEVELS 4
|
|
||||||
|
|
||||||
static uint64_t *pt_get_leaf_ptr(struct vpn vpn, struct ppn cr3, bool alloc) {
|
|
||||||
uint64_t va_value = va_to_value(va_from_vpn(vpn));
|
|
||||||
int level = NUM_LEVELS;
|
|
||||||
struct ppn ppn = cr3;
|
|
||||||
while (1) {
|
|
||||||
uint64_t idx = (va_value >> (12 + (level - 1)*9)) & 0x1ffull;
|
|
||||||
uint64_t *entry_ptr = (uint64_t*)pa_to_pointer(pa_from_ppn_with_offset(ppn, idx << 3));
|
|
||||||
|
|
||||||
struct pt_entry ent;
|
|
||||||
pt_entry_unpack(*entry_ptr, &ent);
|
|
||||||
|
|
||||||
if (level > 1 && ent.page_attr_table_low) {
|
|
||||||
PANIC("no hugepage support yet!");
|
|
||||||
}
|
|
||||||
if (level == 1) {
|
|
||||||
return entry_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ent.present) {
|
|
||||||
if (alloc) {
|
|
||||||
TODO();
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ppn = ent.ppn;
|
|
||||||
level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pt_translate(struct va va, struct ppn cr3, struct pa *pa_out) {
|
|
||||||
uint64_t *leaf_ptr = pt_get_leaf_ptr(vpn_from_unaligned_va(va), cr3, false);
|
|
||||||
if (leaf_ptr == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
struct pt_entry ent;
|
|
||||||
pt_entry_unpack(*leaf_ptr, &ent);
|
|
||||||
*pa_out = pa_from_ppn_with_offset(ent.ppn, va_offset(va));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pt_translate_current(struct va va, struct pa *pa_out) {
|
|
||||||
return pt_translate(va, get_cr3_ppn(), pa_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pt_map_single(struct vpn virt, struct ppn phys,
|
|
||||||
bool writable, bool supervisor, bool global,
|
|
||||||
struct ppn cr3)
|
|
||||||
{
|
|
||||||
uint64_t *leaf_ptr = pt_get_leaf_ptr(virt, cr3, true);
|
|
||||||
if (leaf_ptr == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
struct pt_entry ent;
|
|
||||||
ent.present = true;
|
|
||||||
ent.writable = writable;
|
|
||||||
ent.supervisor = supervisor;
|
|
||||||
ent.writethrough = false;
|
|
||||||
ent.cache_disable = false;
|
|
||||||
ent.accessed = false;
|
|
||||||
ent.dirty = false;
|
|
||||||
ent.page_attr_table_low = false;
|
|
||||||
ent.global = global;
|
|
||||||
ent.page_attr_table_high = false;
|
|
||||||
ent.ppn = phys;
|
|
||||||
*leaf_ptr = pt_entry_pack(&ent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pt_map_single_current(struct vpn virt, struct ppn phys,
|
|
||||||
bool writable, bool supervisor, bool global)
|
|
||||||
{
|
|
||||||
return pt_map_single(virt, phys, writable, supervisor, global, get_cr3_ppn());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pt_map_range(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
|
||||||
bool writable, bool supervisor, bool global,
|
|
||||||
struct ppn cr3)
|
|
||||||
{
|
|
||||||
for (uint64_t i = 0; i < num_pages; i++) {
|
|
||||||
// TODO error handling: what to do if it fails in the middle?
|
|
||||||
pt_map_single(virt, phys, writable, supervisor, global, cr3);
|
|
||||||
virt = vpn_from_pagenum(vpn_to_pagenum(virt) + 1);
|
|
||||||
phys = ppn_from_pagenum(ppn_to_pagenum(phys) + 1);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pt_map_range_current(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
|
||||||
bool writable, bool supervisor, bool global)
|
|
||||||
{
|
|
||||||
return pt_map_range(virt, phys, num_pages, writable, supervisor, global, get_cr3_ppn());
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- range finder ---
|
|
||||||
|
|
||||||
void mem_range_print(const struct mem_range *mr) {
|
|
||||||
uint64_t virt_canonical = va_to_canonical(va_from_vpn(mr->vpn_start));
|
|
||||||
uint64_t phys_start = pa_to_value(pa_from_ppn(mr->entry_start.ppn));
|
|
||||||
printf("virt: %p .. %p (exclusive)\n",
|
|
||||||
virt_canonical,
|
|
||||||
virt_canonical + (mr->npages << 12));
|
|
||||||
printf("phys: %p .. %p (exclusive)\n",
|
|
||||||
phys_start,
|
|
||||||
phys_start + (mr->npages << 12));
|
|
||||||
printf("attr: %c, %c, %s, %s, %s | npages: %lu, size: %lu\n\n",
|
|
||||||
mr->entry_start.writable ? 'w' : 'r',
|
|
||||||
mr->entry_start.supervisor ? 's' : 'u',
|
|
||||||
mr->entry_start.writethrough ? "wt" : "wb",
|
|
||||||
mr->entry_start.cache_disable ? "cached:n" : "cached:y",
|
|
||||||
mr->entry_start.global ? "global:y" : "global:n",
|
|
||||||
mr->npages,
|
|
||||||
mr->npages << 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool pt_contiguous(struct mem_range *mr, struct vpn vpn, struct pt_entry *ent) {
|
|
||||||
return vpn_to_pagenum(vpn) == vpn_to_pagenum(mr->vpn_start) + mr->npages
|
|
||||||
&& ppn_to_pagenum(ent->ppn) == ppn_to_pagenum(mr->entry_start.ppn) + mr->npages
|
|
||||||
&& ent->writable == mr->entry_start.writable
|
|
||||||
&& ent->supervisor == mr->entry_start.supervisor
|
|
||||||
&& ent->writethrough == mr->entry_start.writethrough
|
|
||||||
&& ent->cache_disable == mr->entry_start.cache_disable
|
|
||||||
&& ent->global == mr->entry_start.global;
|
|
||||||
// TODO should PAT also be same?
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOWEST_LEVEL(level) ((level) == 1)
|
|
||||||
#define LEAF(level, ent) (LOWEST_LEVEL(level) || (ent).page_attr_table_low)
|
|
||||||
|
|
||||||
static void pt_get_ranges_rec(struct ppn ppn, int level, uint64_t virt_prev,
|
|
||||||
struct mem_range_buf *buf_out, struct mem_range *curr_range)
|
|
||||||
{
|
|
||||||
struct pt_entry ent;
|
|
||||||
for (uint64_t i = 0; i < 512; i++) {
|
|
||||||
uint64_t entry = *(uint64_t*)pa_to_pointer(pa_from_ppn_with_offset(ppn, i << 3));
|
|
||||||
pt_entry_unpack(entry, &ent);
|
|
||||||
if (!ent.present) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t virt_part = i << (12 + 9*(level - 1));
|
|
||||||
uint64_t virt_new = virt_prev | virt_part;
|
|
||||||
|
|
||||||
if (LEAF(level, ent)) {
|
|
||||||
struct vpn curr_vpn = vpn_from_aligned_va(va_from_value(virt_new));
|
|
||||||
// for huge pages this is > 1
|
|
||||||
uint64_t num_pages_covered = 1ull << (9*(level - 1));
|
|
||||||
if (curr_range->npages == 0) {
|
|
||||||
curr_range->vpn_start = curr_vpn;
|
|
||||||
curr_range->entry_start = ent;
|
|
||||||
curr_range->npages = num_pages_covered;
|
|
||||||
} else {
|
|
||||||
if (pt_contiguous(curr_range, curr_vpn, &ent)) {
|
|
||||||
curr_range->npages += num_pages_covered;
|
|
||||||
} else {
|
|
||||||
// copy last range and start new one
|
|
||||||
ASSERT(buf_out->next_entry < buf_out->num_entries);
|
|
||||||
buf_out->ptr[buf_out->next_entry++] = *curr_range;
|
|
||||||
|
|
||||||
curr_range->vpn_start = curr_vpn;
|
|
||||||
curr_range->entry_start = ent;
|
|
||||||
curr_range->npages = num_pages_covered;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pt_get_ranges_rec(ent.ppn, level - 1, virt_new, buf_out, curr_range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CR4_LA57 12
|
|
||||||
|
|
||||||
void pt_get_ranges(struct mem_range_buf *buf_out) {
|
|
||||||
uint64_t cr4 = get_cr4();
|
|
||||||
|
|
||||||
// find out if we have 5 levels or 4
|
|
||||||
if (cr4 & (1ull << CR4_LA57)) {
|
|
||||||
PANIC("we don't support 5-level page tables");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mem_range curr_range = { .npages = 0 };
|
|
||||||
pt_get_ranges_rec(get_cr3_ppn(), NUM_LEVELS, 0, buf_out, &curr_range);
|
|
||||||
// last range
|
|
||||||
ASSERT(buf_out->next_entry < buf_out->num_entries);
|
|
||||||
buf_out->ptr[buf_out->next_entry++] = curr_range;
|
|
||||||
}
|
|
||||||
700
src/x86_64/paging.c
Normal file
700
src/x86_64/paging.c
Normal file
|
|
@ -0,0 +1,700 @@
|
||||||
|
#include "std.h"
|
||||||
|
#include "ram.h"
|
||||||
|
#include "address.h"
|
||||||
|
#include "x86_64/asm.h"
|
||||||
|
#include "x86_64/paging.h"
|
||||||
|
|
||||||
|
// --- init ---
|
||||||
|
void init_paging() {
|
||||||
|
// assert paging enabled (PG)
|
||||||
|
ASSERT((get_cr0() >> 31) & 1ull);
|
||||||
|
// assert PAE enabled (this should never be disabled in 64-bit mode)
|
||||||
|
ASSERT((get_cr4() >> 5) & 1ull);
|
||||||
|
// PSE is ignored in long mode
|
||||||
|
// ASSERT((get_cr4() >> 4) & 1ull);
|
||||||
|
|
||||||
|
// TODO check that everything is setup correctly
|
||||||
|
// - CR0.WP
|
||||||
|
// - Long-Mode Active (EFER.LMA)
|
||||||
|
// - PAT idx 000 points to default strategy
|
||||||
|
// - See if NX bits are used and decide if we want to
|
||||||
|
// - See if MPK is used and decide if we want to
|
||||||
|
// - SMEP/SMAP?
|
||||||
|
// - ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- pat ---
|
||||||
|
|
||||||
|
enum page_attr {
|
||||||
|
PA_UC = 0x00,
|
||||||
|
PA_WC = 0x01,
|
||||||
|
PA_WT = 0x04,
|
||||||
|
PA_WP = 0x05,
|
||||||
|
PA_WB = 0x06,
|
||||||
|
PA_UCMINUS = 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MSR_PAT 0x277
|
||||||
|
|
||||||
|
static enum page_attr get_pa(unsigned int idx) {
|
||||||
|
ASSERT(idx < 8);
|
||||||
|
uint64_t value = readmsr(MSR_PAT);
|
||||||
|
value = (value >> (idx << 3)) & 0x7;
|
||||||
|
ASSERT(value == PA_UC
|
||||||
|
|| value == PA_WC
|
||||||
|
|| value == PA_WT
|
||||||
|
|| value == PA_WP
|
||||||
|
|| value == PA_WB
|
||||||
|
|| value == PA_UCMINUS);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t pt_entry_to_pat_idx(const struct pt_entry *ent) {
|
||||||
|
return ((uint8_t)ent->pat_bit) << 2
|
||||||
|
| ((uint8_t)ent->cache_disable) << 1
|
||||||
|
| ((uint8_t)ent->writethrough);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *page_attr_to_str(enum page_attr attr) {
|
||||||
|
switch (attr) {
|
||||||
|
case PA_UC: return "UC (uncacheable)";
|
||||||
|
case PA_WC: return "WC (write-combining)";
|
||||||
|
case PA_WT: return "WT (write-through)";
|
||||||
|
case PA_WP: return "WP (write-protect)";
|
||||||
|
case PA_WB: return "WB (write-back)";
|
||||||
|
case PA_UCMINUS: return "UC- (uncacheable minus)";
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- entries ---
|
||||||
|
|
||||||
|
uint64_t pt_entry_pack(const struct pt_entry *ent_in) {
|
||||||
|
uint64_t retval = (uint64_t)ent_in->present
|
||||||
|
| ((uint64_t)ent_in->writable) << 1
|
||||||
|
| ((uint64_t)ent_in->supervisor) << 2
|
||||||
|
| ((uint64_t)ent_in->writethrough) << 3
|
||||||
|
| ((uint64_t)ent_in->cache_disable) << 4
|
||||||
|
| ((uint64_t)ent_in->accessed) << 5
|
||||||
|
| ((uint64_t)ent_in->dirty) << 6
|
||||||
|
| ((uint64_t)ent_in->global) << 8
|
||||||
|
| pa_to_value(pa_from_ppn(ent_in->ppn));
|
||||||
|
|
||||||
|
switch (ent_in->level) {
|
||||||
|
case 4:
|
||||||
|
ASSERT(ent_in->pat_bit == 0); // cannot use PAT bit on highest level (AMD 2 p.228)
|
||||||
|
ASSERT(ent_in->hugepage == false);
|
||||||
|
return retval;
|
||||||
|
case 3: // fallthrough
|
||||||
|
case 2:
|
||||||
|
return retval
|
||||||
|
| ((uint64_t)ent_in->hugepage) << 7
|
||||||
|
| ((uint64_t)ent_in->pat_bit) << 12;
|
||||||
|
case 1:
|
||||||
|
ASSERT(ent_in->hugepage == false);
|
||||||
|
return retval
|
||||||
|
| ((uint64_t)ent_in->pat_bit) << 7;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_entry_unpack(uint64_t ent_in, unsigned int level, struct pt_entry *ent_out) {
|
||||||
|
ent_out->level = level;
|
||||||
|
ent_out->present = (ent_in & (0x1ull << 0)) != 0;
|
||||||
|
ent_out->writable = (ent_in & (0x1ull << 1)) != 0;
|
||||||
|
ent_out->supervisor = (ent_in & (0x1ull << 2)) != 0;
|
||||||
|
ent_out->writethrough = (ent_in & (0x1ull << 3)) != 0;
|
||||||
|
ent_out->cache_disable = (ent_in & (0x1ull << 4)) != 0;
|
||||||
|
ent_out->accessed = (ent_in & (0x1ull << 5)) != 0;
|
||||||
|
ent_out->dirty = (ent_in & (0x1ull << 6)) != 0;
|
||||||
|
ent_out->global = (ent_in & (0x1ull << 8)) != 0;
|
||||||
|
ent_out->ppn = ppn_from_aligned_pa(pa_from_value(ent_in & 0x000ffffffffff000ull));
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case 4:
|
||||||
|
ent_out->hugepage = 0;
|
||||||
|
ent_out->pat_bit = false;
|
||||||
|
break;
|
||||||
|
case 3: // fallthrough
|
||||||
|
case 2:
|
||||||
|
ent_out->hugepage = (ent_in & (0x1ull << 7)) != 0;
|
||||||
|
ent_out->pat_bit = (ent_in & (0x1ull << 12)) != 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ent_out->hugepage = 0;
|
||||||
|
ent_out->pat_bit = (ent_in & (0x1ull << 7)) != 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_entry_init_nonleaf(struct pt_entry *ent, unsigned int level, struct ppn ppn) {
|
||||||
|
ASSERT(level == 4 || level == 3 || level == 2);
|
||||||
|
|
||||||
|
ent->level = level;
|
||||||
|
ent->present = true;
|
||||||
|
// maximum privileges in upper level
|
||||||
|
ent->writable = true;
|
||||||
|
ent->supervisor = false;
|
||||||
|
ent->writethrough = false;
|
||||||
|
ent->cache_disable = false;
|
||||||
|
ent->accessed = false;
|
||||||
|
ent->dirty = false;
|
||||||
|
ent->global = false; // ignored
|
||||||
|
ent->ppn = ppn;
|
||||||
|
ent->hugepage = false;
|
||||||
|
ent->pat_bit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_entry_init_leaf(struct pt_entry *ent, unsigned int level,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn ppn)
|
||||||
|
{
|
||||||
|
ASSERT(level == 3 || level == 2 || level == 1);
|
||||||
|
|
||||||
|
ent->level = level;
|
||||||
|
ent->present = true;
|
||||||
|
ent->writable = writable;
|
||||||
|
ent->supervisor = supervisor;
|
||||||
|
ent->writethrough = false;
|
||||||
|
ent->cache_disable = false;
|
||||||
|
ent->accessed = false;
|
||||||
|
ent->dirty = false;
|
||||||
|
ent->global = global;
|
||||||
|
ent->ppn = ppn;
|
||||||
|
ent->hugepage = (level == 3 || level == 2);
|
||||||
|
ent->pat_bit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_entry_print(const struct pt_entry *ent) {
|
||||||
|
printf("pt_entry {\n");
|
||||||
|
printf(" level: %d\n", (int)ent->level);
|
||||||
|
printf(" present: %d\n", (int)ent->present);
|
||||||
|
printf(" writable: %d\n", (int)ent->writable);
|
||||||
|
printf(" supervisor: %d\n", (int)ent->supervisor);
|
||||||
|
printf(" accessed: %d\n", (int)ent->accessed);
|
||||||
|
printf(" dirty: %d\n", (int)ent->dirty);
|
||||||
|
printf(" global: %d\n", (int)ent->global);
|
||||||
|
printf(" page base: %p\n", pa_from_ppn(ent->ppn));
|
||||||
|
|
||||||
|
switch (ent->level) {
|
||||||
|
case 4:
|
||||||
|
printf(" (no hugepage)\n");
|
||||||
|
break;
|
||||||
|
case 3: // fallthrough
|
||||||
|
case 2:
|
||||||
|
printf(" hugepage: %d\n", (int)ent->hugepage);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
printf(" (no hugepage)\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO right now we assume PAT is present
|
||||||
|
printf(" caching: %s\n", page_attr_to_str(get_pa(pt_entry_to_pat_idx(ent))));
|
||||||
|
|
||||||
|
printf("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- convenience ---
|
||||||
|
|
||||||
|
static struct ppn get_cr3_ppn(void) {
|
||||||
|
uint64_t cr3_val = get_cr3();
|
||||||
|
return ppn_from_aligned_pa(pa_from_value(cr3_val & 0x000ffffffffff000ull));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- traversal and mapping ---
|
||||||
|
|
||||||
|
static struct pa access_level_pa(const struct access_level *ac) {
|
||||||
|
return pa_from_pt_with_idx(ac->ppn, ac->idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO for inspection, we need to accumulate permissions over all levels
|
||||||
|
// (upper supervisor bit will mean lower PTs are also supervisor, even without their bit set)
|
||||||
|
static uint64_t *pt_get_leaf_ptr(struct vpn vpn, struct ppn cr3, bool alloc, unsigned int *level_out) {
|
||||||
|
unsigned int level = 4;
|
||||||
|
struct ppn ppn = cr3;
|
||||||
|
while (1) {
|
||||||
|
uint64_t *entry_ptr = (uint64_t*)pa_to_pointer(pa_from_pt_with_idx(ppn, vpn_level_idx(vpn, level)));
|
||||||
|
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*entry_ptr, level, &ent);
|
||||||
|
|
||||||
|
if (level == 1 || ent.hugepage) {
|
||||||
|
*level_out = level;
|
||||||
|
return entry_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ent.present) {
|
||||||
|
if (alloc) {
|
||||||
|
struct ppn new_ppn;
|
||||||
|
ASSERT(ram_alloc_frame_zeroed(&new_ppn, RAM_PAGE_NORMAL));
|
||||||
|
pt_entry_init_nonleaf(&ent, level, new_ppn);
|
||||||
|
*entry_ptr = pt_entry_pack(&ent);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ppn = ent.ppn;
|
||||||
|
level -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pt_translate(struct va va, struct ppn cr3, struct pa *pa_out) {
|
||||||
|
unsigned int leaf_level = 42;
|
||||||
|
uint64_t *leaf_ptr = pt_get_leaf_ptr(vpn_from_unaligned_va(va), cr3, false, &leaf_level);
|
||||||
|
if (leaf_ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ASSERT(leaf_level != 42);
|
||||||
|
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*leaf_ptr, leaf_level, &ent);
|
||||||
|
|
||||||
|
switch (leaf_level) {
|
||||||
|
case 1:
|
||||||
|
*pa_out = pa_from_ppn_with_offset(ent.ppn, va_offset(va));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*pa_out = pa_from_ppn_huge_2mb_with_offset(ent.ppn, va_offset_huge_2mb(va));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*pa_out = pa_from_ppn_huge_1gb_with_offset(ent.ppn, va_offset_huge_1gb(va));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pt_translate_current(struct va va, struct pa *pa_out) {
|
||||||
|
return pt_translate(va, get_cr3_ppn(), pa_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pt_map_single(struct vpn virt, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn cr3)
|
||||||
|
{
|
||||||
|
unsigned int leaf_level = 42;
|
||||||
|
uint64_t *leaf_ptr = pt_get_leaf_ptr(virt, cr3, true, &leaf_level);
|
||||||
|
if (leaf_ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ASSERT(leaf_level != 42);
|
||||||
|
// check for and avoid double mapping
|
||||||
|
struct pt_entry old_ent;
|
||||||
|
pt_entry_unpack(*leaf_ptr, leaf_level, &old_ent);
|
||||||
|
if (leaf_level != 1 || old_ent.present) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_init_leaf(&ent, 1, writable, supervisor, global, phys);
|
||||||
|
*leaf_ptr = pt_entry_pack(&ent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pt_map_single_current(struct vpn virt, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global)
|
||||||
|
{
|
||||||
|
return pt_map_single(virt, phys, writable, supervisor, global, get_cr3_ppn());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRAVERSAL_LEVEL(trav) (4 - (trav)->depth)
|
||||||
|
#define TRAVERSAL_NPAGES_COVERED(trav) (1ull << (9 * (TRAVERSAL_LEVEL(trav) - 1)))
|
||||||
|
|
||||||
|
static void traversal_init(struct traversal *trav, struct vpn virt, struct ppn cr3) {
|
||||||
|
trav->depth = 0;
|
||||||
|
trav->levels[0].ppn = cr3;
|
||||||
|
trav->levels[0].idx = vpn_level_idx(virt, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t *traversal_entry_ptr(const struct traversal *trav) {
|
||||||
|
return pa_to_pointer(access_level_pa(&trav->levels[trav->depth]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vpn traversal_vpn(const struct traversal *trav) {
|
||||||
|
uint64_t value = 0;
|
||||||
|
for (unsigned int i = 0; i <= trav->depth; i++) {
|
||||||
|
unsigned int level = 4 - i;
|
||||||
|
int shift = 9 * (level - 1);
|
||||||
|
value |= (uint64_t)(trav->levels[i].idx) << shift;
|
||||||
|
}
|
||||||
|
return vpn_from_pagenum(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool traversal_have_leaf(const struct traversal *trav) {
|
||||||
|
uint64_t *entry_ptr = traversal_entry_ptr(trav);
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*entry_ptr, TRAVERSAL_LEVEL(trav), &ent);
|
||||||
|
return PT_ENTRY_IS_LEAF(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void traversal_push(struct traversal *trav, struct pt_entry *ent, struct vpn virt) {
|
||||||
|
ASSERT(PT_ENTRY_IS_NONLEAF(*ent));
|
||||||
|
|
||||||
|
unsigned int old_depth = trav->depth;
|
||||||
|
unsigned int new_depth = old_depth + 1;
|
||||||
|
trav->levels[new_depth].ppn = ent->ppn;
|
||||||
|
trav->levels[new_depth].idx = vpn_level_idx(virt, 4 - new_depth);
|
||||||
|
trav->depth = new_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return if we descended or not
|
||||||
|
static bool traversal_descend_single_noalloc(struct traversal *trav, struct vpn virt) {
|
||||||
|
ASSERT(trav->depth < 3);
|
||||||
|
|
||||||
|
uint64_t *entry_ptr = traversal_entry_ptr(trav);
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*entry_ptr, TRAVERSAL_LEVEL(trav), &ent);
|
||||||
|
|
||||||
|
if (ent.present) {
|
||||||
|
traversal_push(trav, &ent, virt);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void traversal_descend_single_with_alloc(struct traversal *trav, struct vpn virt) {
|
||||||
|
ASSERT(trav->depth < 3);
|
||||||
|
|
||||||
|
uint64_t *entry_ptr = traversal_entry_ptr(trav);
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*entry_ptr, TRAVERSAL_LEVEL(trav), &ent);
|
||||||
|
|
||||||
|
if (!ent.present) {
|
||||||
|
struct ppn ppn;
|
||||||
|
ASSERT(ram_alloc_frame_zeroed(&ppn, RAM_PAGE_NORMAL));
|
||||||
|
pt_entry_init_nonleaf(&ent, TRAVERSAL_LEVEL(trav), ppn);
|
||||||
|
*entry_ptr = pt_entry_pack(&ent);
|
||||||
|
}
|
||||||
|
traversal_push(trav, &ent, virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void traversal_map(struct traversal *trav, struct ppn phys,
|
||||||
|
bool writable, bool supervisor, bool global)
|
||||||
|
{
|
||||||
|
switch (trav->depth) {
|
||||||
|
case 0: UNREACHABLE(); // there are no 512 GB pages
|
||||||
|
case 1: ASSERT(ppn_is_huge_1gb(phys)); break;
|
||||||
|
case 2: ASSERT(ppn_is_huge_2mb(phys)); break;
|
||||||
|
case 3: break;
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t *entry_ptr = traversal_entry_ptr(trav);
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*entry_ptr, TRAVERSAL_LEVEL(trav), &ent);
|
||||||
|
ASSERT(!ent.present);
|
||||||
|
pt_entry_init_leaf(&ent, TRAVERSAL_LEVEL(trav), writable, supervisor, global, phys);
|
||||||
|
*entry_ptr = pt_entry_pack(&ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns whether we're done or not.
|
||||||
|
static bool traversal_advance_idx(struct traversal *trav) {
|
||||||
|
while (true) {
|
||||||
|
trav->levels[trav->depth].idx += 1;
|
||||||
|
if (trav->levels[trav->depth].idx == 512) {
|
||||||
|
if (trav->depth > 0) {
|
||||||
|
trav->depth -= 1;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool can_map_huge_1gb(struct ppn phys, uint64_t num_pages) {
|
||||||
|
return ppn_is_huge_1gb(phys) && num_pages >= (1ull << 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool can_map_huge_2mb(struct ppn phys, uint64_t num_pages) {
|
||||||
|
return ppn_is_huge_2mb(phys) && num_pages >= (1ull << 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a single mapping fails, PT is left in half-mapped state
|
||||||
|
bool pt_map_range(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
||||||
|
bool writable, bool supervisor, bool global,
|
||||||
|
struct ppn cr3)
|
||||||
|
{
|
||||||
|
struct traversal trav;
|
||||||
|
traversal_init(&trav, virt, cr3);
|
||||||
|
// we always need at least 2 levels
|
||||||
|
traversal_descend_single_with_alloc(&trav, virt);
|
||||||
|
if (!vpn_is_huge_1gb(virt) || !can_map_huge_1gb(phys, num_pages)) {
|
||||||
|
traversal_descend_single_with_alloc(&trav, virt);
|
||||||
|
}
|
||||||
|
if (!vpn_is_huge_2mb(virt) || !can_map_huge_2mb(phys, num_pages)) {
|
||||||
|
traversal_descend_single_with_alloc(&trav, virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
traversal_map(&trav, phys, writable, supervisor, global);
|
||||||
|
|
||||||
|
// advance
|
||||||
|
uint64_t np = TRAVERSAL_NPAGES_COVERED(&trav);
|
||||||
|
num_pages -= np;
|
||||||
|
if (num_pages == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
virt = vpn_from_pagenum(vpn_to_pagenum(virt) + np);
|
||||||
|
phys = ppn_from_pagenum(ppn_to_pagenum(phys) + np);
|
||||||
|
ASSERT(!traversal_advance_idx(&trav));
|
||||||
|
if (trav.depth < 1) {
|
||||||
|
traversal_descend_single_with_alloc(&trav, virt);
|
||||||
|
}
|
||||||
|
if (trav.depth < 2 && !can_map_huge_1gb(phys, num_pages)) {
|
||||||
|
traversal_descend_single_with_alloc(&trav, virt);
|
||||||
|
}
|
||||||
|
if (trav.depth < 3 && !can_map_huge_2mb(phys, num_pages)) {
|
||||||
|
traversal_descend_single_with_alloc(&trav, virt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pt_map_range_current(struct vpn virt, struct ppn phys, uint64_t num_pages,
|
||||||
|
bool writable, bool supervisor, bool global)
|
||||||
|
{
|
||||||
|
return pt_map_range(virt, phys, num_pages, writable, supervisor, global, get_cr3_ppn());
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_free(struct ppn cr3) {
|
||||||
|
// this assumes single ownership
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- page table creation ---
|
||||||
|
|
||||||
|
#define PT_CREATE_DEBUG
|
||||||
|
|
||||||
|
__attribute__((noreturn))
|
||||||
|
void pt_create_minimal(void) {
|
||||||
|
// get a top level page table
|
||||||
|
struct ppn new_cr3;
|
||||||
|
ASSERT(ram_alloc_frame_zeroed(&new_cr3, RAM_PAGE_NORMAL));
|
||||||
|
|
||||||
|
// copy mappings we had previously for everything important
|
||||||
|
// this start value comes from the bootboot docs
|
||||||
|
struct mem_range range;
|
||||||
|
struct mem_range_iter it;
|
||||||
|
mem_range_iter_init_custom_start(&it, get_cr3_ppn(),
|
||||||
|
vpn_from_aligned_va(va_from_canonical(0xfffffffff8000000ull)));
|
||||||
|
while (mem_range_iter_next(&it, &range)) {
|
||||||
|
pt_map_range(range.vpn_start, range.entry_start.ppn, range.npages,
|
||||||
|
true, true, false,
|
||||||
|
new_cr3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16 GiB identity map with hugepages starting at low end of upper half of AS
|
||||||
|
unsigned int npages_in_gb = 1ull << 18;
|
||||||
|
unsigned int npages_idmap = 16 * npages_in_gb;
|
||||||
|
struct vpn vpn_idmap_start = vpn_from_aligned_va(va_from_value(1ull << 47));
|
||||||
|
pt_map_range(vpn_idmap_start,
|
||||||
|
ppn_from_pagenum(0),
|
||||||
|
npages_idmap,
|
||||||
|
true, true, false,
|
||||||
|
new_cr3);
|
||||||
|
|
||||||
|
// new stack with 4 GiB size and guard pages around
|
||||||
|
// TODO one stack for each core
|
||||||
|
struct vpn stack_virt = vpn_from_pagenum(vpn_to_pagenum(vpn_idmap_start) + npages_idmap + 1);
|
||||||
|
struct ppn stack_phys;
|
||||||
|
ASSERT(ram_alloc_frame_zeroed(&stack_phys, RAM_PAGE_NORMAL));
|
||||||
|
pt_map_single(stack_virt, stack_phys, true, true, false, new_cr3);
|
||||||
|
|
||||||
|
#ifdef PT_CREATE_DEBUG
|
||||||
|
uint64_t value = pa_to_value(pa_from_ppn(new_cr3));
|
||||||
|
printf("new cr3: %p\n", value);
|
||||||
|
|
||||||
|
// compare old and new to make sure we don't do any bullshit
|
||||||
|
printf("old ranges:\n");
|
||||||
|
mem_range_iter_init_current(&it);
|
||||||
|
while (true) {
|
||||||
|
if (!mem_range_iter_next(&it, &range)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mem_range_print(&range);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("new ranges:\n");
|
||||||
|
mem_range_iter_init(&it, new_cr3);
|
||||||
|
while (true) {
|
||||||
|
if (!mem_range_iter_next(&it, &range)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mem_range_print(&range);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// switch
|
||||||
|
set_cr3(value);
|
||||||
|
set_identity_mapping(vpn_idmap_start);
|
||||||
|
|
||||||
|
// stack switch
|
||||||
|
// it doesn't really make sense to copy stack contents because rbp and pointers
|
||||||
|
// to local variables will point to the wrong location.
|
||||||
|
// this means we won't leave this function for now.
|
||||||
|
uint64_t new_sp = va_to_canonical(va_from_vpn(stack_virt)) + 4096;
|
||||||
|
__asm__("mov %0,%%rsp" ::"r"(new_sp));
|
||||||
|
|
||||||
|
#ifdef PT_CREATE_DEBUG
|
||||||
|
printf("hello from new page table!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PANIC("end of pt_create_minimal");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- iterators ---
|
||||||
|
|
||||||
|
void mem_range_print(const struct mem_range *mr) {
|
||||||
|
uint64_t virt_canonical = va_to_canonical(va_from_vpn(mr->vpn_start));
|
||||||
|
uint64_t phys_start = pa_to_value(pa_from_ppn(mr->entry_start.ppn));
|
||||||
|
printf("virt: %p .. %p (exclusive)\n",
|
||||||
|
virt_canonical,
|
||||||
|
virt_canonical + (mr->npages << 12));
|
||||||
|
printf("phys: %p .. %p (exclusive)\n",
|
||||||
|
phys_start,
|
||||||
|
phys_start + (mr->npages << 12));
|
||||||
|
printf("attr: %c, %c, %s, %s | npages: %lu, size: %lu\n\n",
|
||||||
|
mr->entry_start.writable ? 'w' : 'r',
|
||||||
|
mr->entry_start.supervisor ? 's' : 'u',
|
||||||
|
// TODO right now we assume PAT is present
|
||||||
|
page_attr_to_str(get_pa(pt_entry_to_pat_idx(&mr->entry_start))),
|
||||||
|
mr->entry_start.global ? "global:y" : "global:n",
|
||||||
|
mr->npages,
|
||||||
|
mr->npages << 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pt_contiguous(struct mem_range *mr, struct vpn vpn, struct pt_entry *ent) {
|
||||||
|
return vpn_to_pagenum(vpn) == vpn_to_pagenum(mr->vpn_start) + mr->npages
|
||||||
|
&& ppn_to_pagenum(ent->ppn) == ppn_to_pagenum(mr->entry_start.ppn) + mr->npages
|
||||||
|
&& ent->writable == mr->entry_start.writable
|
||||||
|
&& ent->supervisor == mr->entry_start.supervisor
|
||||||
|
&& ent->writethrough == mr->entry_start.writethrough
|
||||||
|
&& ent->cache_disable == mr->entry_start.cache_disable
|
||||||
|
&& ent->global == mr->entry_start.global
|
||||||
|
&& ent->pat_bit == mr->entry_start.pat_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_leaf_iter_init(struct pt_leaf_iter *it, struct ppn cr3) {
|
||||||
|
it->done = false;
|
||||||
|
traversal_init(&it->trav, vpn_from_pagenum(0), cr3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_leaf_iter_init_current(struct pt_leaf_iter *it) {
|
||||||
|
pt_leaf_iter_init(it, get_cr3_ppn());
|
||||||
|
}
|
||||||
|
|
||||||
|
void pt_leaf_iter_init_custom_start(struct pt_leaf_iter *it, struct ppn cr3, struct vpn virt) {
|
||||||
|
it->done = false;
|
||||||
|
traversal_init(&it->trav, virt, cr3);
|
||||||
|
// "incorporate" virt into this traversal so we don't have to store it
|
||||||
|
while(!traversal_have_leaf(&it->trav) && traversal_descend_single_noalloc(&it->trav, virt)) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pt_leaf_iter_next(struct pt_leaf_iter *it, struct pt_leaf *leaf_out) {
|
||||||
|
ASSERT(it != NULL && leaf_out != NULL);
|
||||||
|
|
||||||
|
if (it->done) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint64_t *entry_ptr = traversal_entry_ptr(&it->trav);
|
||||||
|
struct pt_entry ent;
|
||||||
|
pt_entry_unpack(*entry_ptr, TRAVERSAL_LEVEL(&it->trav), &ent);
|
||||||
|
if (!ent.present) {
|
||||||
|
it->done = traversal_advance_idx(&it->trav);
|
||||||
|
if (it->done) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (PT_ENTRY_IS_LEAF(ent)) {
|
||||||
|
*leaf_out = (struct pt_leaf){ .vpn_start = traversal_vpn(&it->trav), .ent = ent };
|
||||||
|
it->done = traversal_advance_idx(&it->trav);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
traversal_push(&it->trav, &ent, vpn_from_pagenum(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t leaf_level_to_npages(unsigned int level) {
|
||||||
|
switch (level) {
|
||||||
|
case 1: return 1;
|
||||||
|
case 2: return 1 << 9;
|
||||||
|
case 3: return 1 << 18;
|
||||||
|
default: UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mem_range_iter_init(struct mem_range_iter *it, struct ppn cr3) {
|
||||||
|
pt_leaf_iter_init(&it->leaf_it, cr3);
|
||||||
|
it->have_held_leaf = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mem_range_iter_init_current(struct mem_range_iter *it) {
|
||||||
|
mem_range_iter_init(it, get_cr3_ppn());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mem_range_iter_init_custom_start(struct mem_range_iter *it, struct ppn cr3, struct vpn virt) {
|
||||||
|
pt_leaf_iter_init_custom_start(&it->leaf_it, cr3, virt);
|
||||||
|
it->have_held_leaf = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mem_range_iter_leaf_get(struct mem_range_iter *it, struct pt_leaf *leaf_out) {
|
||||||
|
if (it->have_held_leaf) {
|
||||||
|
it->have_held_leaf = false;
|
||||||
|
*leaf_out = it->held_leaf;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return pt_leaf_iter_next(&it->leaf_it, leaf_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mem_range_iter_leaf_unget(struct mem_range_iter *it, struct pt_leaf leaf) {
|
||||||
|
ASSERT(!it->have_held_leaf);
|
||||||
|
it->have_held_leaf = true;
|
||||||
|
it->held_leaf = leaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mem_range_iter_next(struct mem_range_iter *it, struct mem_range *range_out) {
|
||||||
|
struct pt_leaf leaf;
|
||||||
|
bool success = mem_range_iter_leaf_get(it, &leaf);
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct mem_range range = (struct mem_range){
|
||||||
|
.vpn_start = leaf.vpn_start,
|
||||||
|
.entry_start = leaf.ent,
|
||||||
|
.npages = leaf_level_to_npages(leaf.ent.level),
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
bool success = mem_range_iter_leaf_get(it, &leaf);
|
||||||
|
if (!success) {
|
||||||
|
*range_out = range;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt_contiguous(&range, leaf.vpn_start, &leaf.ent)) {
|
||||||
|
range.npages += leaf_level_to_npages(leaf.ent.level);
|
||||||
|
} else {
|
||||||
|
mem_range_iter_leaf_unget(it, leaf);
|
||||||
|
*range_out = range;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
149
src/x86_64/ps2_driver.c
Normal file
149
src/x86_64/ps2_driver.c
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "std.h"
|
||||||
|
#include "x86_64/asm.h"
|
||||||
|
#include "x86_64/ps2_driver.h"
|
||||||
|
|
||||||
|
#define PORT_STATUS 0x64
|
||||||
|
#define PORT_CMD 0x64
|
||||||
|
#define PORT_DATA 0x60
|
||||||
|
|
||||||
|
#define CMD_CONFIGBYTE_READ 0x20
|
||||||
|
#define CMD_CONFIGBYTE_WRITE 0x20
|
||||||
|
#define CMD_PORT2_DISABLE 0xA7
|
||||||
|
#define CMD_PORT2_ENABLE 0xA8
|
||||||
|
#define CMD_PORT2_TEST 0xA9
|
||||||
|
#define CMD_TEST 0xAA
|
||||||
|
#define CMD_PORT1_TEST 0xAB
|
||||||
|
#define CMD_PORT1_DISABLE 0xAD
|
||||||
|
#define CMD_PORT1_ENABLE 0xAE
|
||||||
|
|
||||||
|
#define STATUS_BIT_OUTPUT_FULL 0
|
||||||
|
#define STATUS_BIT_INPUT_FULL 1
|
||||||
|
#define STATUS_BIT_SYSTEM 2
|
||||||
|
#define STATUS_BIT_COMMAND_DATA 3
|
||||||
|
#define STATUS_BIT_ERR_TIMEOUT 6
|
||||||
|
#define STATUS_BIT_ERR_PARITY 7
|
||||||
|
|
||||||
|
#define CONFIG_BIT_PORT1_INT_ENABLED 0
|
||||||
|
#define CONFIG_BIT_PORT2_INT_ENABLED 1
|
||||||
|
#define CONFIG_BIT_SYSTEM 2
|
||||||
|
#define CONFIG_BIT_PORT1_CLOCK_DISABLED 4
|
||||||
|
#define CONFIG_BIT_PORT2_CLOCK_DISABLED 5
|
||||||
|
#define CONFIG_BIT_PORT1_TRANSLATION 6
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
ps2_read_status() {
|
||||||
|
return in8(PORT_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ps2_get_status_bit(uint8_t bit) {
|
||||||
|
return (ps2_read_status() >> bit) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ps2_can_read() {
|
||||||
|
return ps2_get_status_bit(STATUS_BIT_OUTPUT_FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ps2_can_write() {
|
||||||
|
return !ps2_get_status_bit(STATUS_BIT_INPUT_FULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ps2_write_cmd(uint8_t cmd) {
|
||||||
|
while (!ps2_can_write()) {
|
||||||
|
// wait
|
||||||
|
}
|
||||||
|
out8(PORT_CMD, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ps2_read_data() {
|
||||||
|
while(!ps2_can_read()) {
|
||||||
|
// wait
|
||||||
|
}
|
||||||
|
return in8(PORT_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ps2_empty_output_buffer() {
|
||||||
|
while (ps2_can_read()) {
|
||||||
|
in8(PORT_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ps2_write_data(uint8_t data) {
|
||||||
|
while (!ps2_can_write()) {
|
||||||
|
// wait
|
||||||
|
}
|
||||||
|
out8(PORT_DATA, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ps2_cmd_response(uint8_t cmd) {
|
||||||
|
ps2_write_cmd(cmd);
|
||||||
|
return ps2_read_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ps2_cmd_with_data(uint8_t cmd, uint8_t data) {
|
||||||
|
ps2_write_cmd(cmd);
|
||||||
|
ps2_write_data(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ps2_init() {
|
||||||
|
uint8_t config_byte;
|
||||||
|
|
||||||
|
/* Disable devices */
|
||||||
|
ps2_write_cmd(CMD_PORT1_DISABLE);
|
||||||
|
ps2_write_cmd(CMD_PORT2_DISABLE);
|
||||||
|
|
||||||
|
/* Flush Outputbuffer */
|
||||||
|
ps2_empty_output_buffer();
|
||||||
|
|
||||||
|
/* Set Configbyte */
|
||||||
|
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
|
||||||
|
config_byte &= ~CONFIG_BIT_PORT1_INT_ENABLED;
|
||||||
|
config_byte &= ~CONFIG_BIT_PORT1_CLOCK_DISABLED;
|
||||||
|
config_byte &= ~CONFIG_BIT_PORT1_TRANSLATION;
|
||||||
|
ps2_cmd_with_data(CMD_CONFIGBYTE_WRITE, config_byte);
|
||||||
|
|
||||||
|
/* Selftest */
|
||||||
|
uint8_t test = ps2_cmd_response(CMD_TEST);
|
||||||
|
ASSERT(test == 0x55);
|
||||||
|
|
||||||
|
/* Query for 2nd port */
|
||||||
|
ps2_write_cmd(CMD_PORT2_ENABLE);
|
||||||
|
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
|
||||||
|
bool port_2_enabled = ((config_byte >> 5) & 1) == 0;
|
||||||
|
if (port_2_enabled) {
|
||||||
|
ps2_write_cmd(CMD_PORT2_DISABLE);
|
||||||
|
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
|
||||||
|
config_byte &= ~CONFIG_BIT_PORT2_INT_ENABLED;
|
||||||
|
config_byte &= ~CONFIG_BIT_PORT2_CLOCK_DISABLED;
|
||||||
|
ps2_cmd_with_data(CMD_CONFIGBYTE_WRITE, config_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test Ports */
|
||||||
|
uint8_t port_test = ps2_cmd_response(CMD_PORT1_TEST);
|
||||||
|
ASSERT(port_test == 0x00);
|
||||||
|
if (port_2_enabled) {
|
||||||
|
port_test = ps2_cmd_response(CMD_PORT2_TEST);
|
||||||
|
ASSERT(port_test == 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable devices */
|
||||||
|
ps2_write_cmd(CMD_PORT1_ENABLE);
|
||||||
|
if (port_2_enabled) {
|
||||||
|
ps2_write_cmd(CMD_PORT2_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable interrupts */
|
||||||
|
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
|
||||||
|
config_byte |= CONFIG_BIT_PORT1_INT_ENABLED;
|
||||||
|
if (port_2_enabled) {
|
||||||
|
config_byte |= CONFIG_BIT_PORT2_INT_ENABLED;
|
||||||
|
}
|
||||||
|
ps2_cmd_with_data(CMD_CONFIGBYTE_WRITE, config_byte);
|
||||||
|
|
||||||
|
/* Reset devices */
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
@ -12,3 +12,7 @@ void uart_write_char(char c) {
|
||||||
|
|
||||||
out8(COM1_BASE_PORT, c);
|
out8(COM1_BASE_PORT, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serial_write_char(char c) {
|
||||||
|
uart_write_char(c);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue