diff --git a/Makefile b/Makefile index 9a9196c..6427c60 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ compile: - riscv64-unknown-elf-c++ -g -ffreestanding -O0 -Wl,--gc-sections \ + riscv64-unknown-linux-gnu-g++ -g -ffreestanding -O0 -mcmodel=medany -Wl,--gc-sections \ -nostartfiles -nostdlib -nodefaultlibs -Wl,-T,virt.lds\ - boot.S trap.S main.cc + boot.S trap.S mem.S main.cc uart.h debug: - qemu-system-riscv64 -machine virt -m 128M -gdb tcp::1234 -kernel a.out -S -bios none & \ - xterm -e riscv64-unknown-elf-gdb \ + qemu-system-riscv64 -machine virt -m 128M -serial mon:stdio -serial null -nographic -gdb tcp::1234 -kernel a.out -S & \ + xterm -e riscv64-unknown-linux-gnu-gdb \ -ex "target remote:1234" -ex "set confirm off" \ -ex "add-symbol-file ./a.out 0x80000000" run: - qemu-system-riscv64 -machine virt -m 128M -kernel a.out -bios none + qemu-system-riscv64 -machine virt -m 128M -serial mon:stdio -serial null -nographic -kernel a.out -bios none diff --git a/a.out b/a.out index 52eef51..d5c4894 100755 Binary files a/a.out and b/a.out differ diff --git a/boot.S b/boot.S index 3eb00f8..f74406b 100644 --- a/boot.S +++ b/boot.S @@ -1,68 +1,123 @@ -# boot.s -# bootloader +# boot.S +# bootloader for SoS +# Stephen Marz +# 8 February 2019 +# Disable generation of compressed instructions. .option norvc + +# Define a .data section. .section .data -.global TOTAL_NUM_HARTS -TOTAL_NUM_HARTS: .byte 1 +# Define a .text.init section. .section .text.init + +# Execution starts here. .global _start _start: - csrr t0, mhartid #reading CSR mhartid - bnez t0, 3f - csrw satp, zero # seta supervisor address translation and protection register to 0. MMU related + # Any hardware threads (hart) that are not bootstrapping + # need to wait for an IPI + csrr t0, mhartid + bnez t0, 3f + # SATP should be zero, but let's make sure + csrw satp, zero + + # Disable linker instruction relaxation for the `la` instruction below. + # This disallows the assembler from assuming that `gp` is already initialized. + # This causes the value stored in `gp` to be calculated from `pc`. .option push .option norelax - la gp, _global_pointer + la gp, _global_pointer .option pop - la a0, _bss_start - la a1, _bss_end - bgeu a0, a1, 2f + # Set all bytes in the BSS section to zero. + la a0, _bss_start + la a1, _bss_end + bgeu a0, a1, 2f 1: - sd zero, (a0) # store doubleword - guarda "duas palavras 32bits = 64" - addi a0, a0, 8 # incrementa 64 bits até chegar no fim da bss - bltu a0, a1, 1b + sd zero, (a0) + addi a0, a0, 8 + bltu a0, a1, 1b 2: + # Control registers, set the stack, mstatus, mepc, + # and mtvec to return to the main function. # li t5, 0xffff; - # csrw medeleg t5 + # csrw medeleg, t5 # csrw mideleg, t5 + # The stack grows from bottom to top, so we put the stack pointer + # to the very end of the stack range. la sp, _stack_end - li t0, (0b11 << 11) | (1 << 7) | (1 << 3) - # [11,12] = mpp = 11 => MACHINE PRIVILEDGE - # 7 = MPIE = 1 => bit do interrupt ativo - # 3 = MIE = 1 => ativo interrupcao nivel maquina - csrw mstatus, t0 # load in mstatus (machine status register) (o atual estado da thread) os op + # Setting `mstatus` register: + # 0b11 << 11: Machine's previous protection mode is 3 (MPP=3). + li t0, 0b11 << 11 + csrw mstatus, t0 + # Machine's exception program counter (MEPC) is set to `kinit`. + la t1, kmain + csrw mepc, t1 + # Machine's trap vector base address is set to `asm_trap_vector`. + la t2, asm_trap_vector + csrw mtvec, t2 + # Set the return address to get us into supervisor mode + la ra, 2f + # We use mret here so that the mstatus register is properly updated. + mret +2: + # We set the return address (ra above) to this label. When kinit() is finished + # in Rust, it will return here. + + # Setting `sstatus` (supervisor status) register: + # 1 << 8 : Supervisor's previous protection mode is 1 (SPP=1 [Supervisor]). + # 1 << 5 : Supervisor's previous interrupt-enable bit is 1 (SPIE=1 [Enabled]). + # 1 << 1 : Supervisor's interrupt-enable bit will be set to 1 after sret. + # We set the "previous" bits because the sret will write the current bits + # with the previous bits. + li t0, (1 << 8) | (1 << 5) + csrw sstatus, t0 la t1, kmain - csrw mepc, t1 # carrega no registrador de pc_exception endereco da kmain - la t2, m_trap_vector - csrw mtvec, t2 #carrega em no reg trap_handler o endereco do trap_handler - li t3, (1 << 3) | (1 << 7) | (1 << 11) - csrw mie, t3 #carrega no registrador machine interrupt enable o padrao - # msie = 1 => habilita interrupcao maquina - # mtie = 1 => timer modo maquina habilitado - # meie = 1 => habilita interrupcoes externas - la ra, 4f - mret + csrw sepc, t1 + # Setting `mideleg` (machine interrupt delegate) register: + # 1 << 1 : Software interrupt delegated to supervisor mode + # 1 << 5 : Timer interrupt delegated to supervisor mode + # 1 << 9 : External interrupt delegated to supervisor mode + # By default all traps (interrupts or exceptions) automatically + # cause an elevation to the machine privilege mode (mode 3). + # When we delegate, we're telling the CPU to only elevate to + # the supervisor privilege mode (mode 1) + li t2, (1 << 1) | (1 << 5) | (1 << 9) + csrw mideleg, t2 + # Setting `sie` (supervisor interrupt enable) register: + # This register takes the same bits as mideleg + # 1 << 1 : Supervisor software interrupt enable (SSIE=1 [Enabled]) + # 1 << 5 : Supervisor timer interrupt enable (STIE=1 [Enabled]) + # 1 << 9 : Supervisor external interrupt enable (SEIE=1 [Enabled]) + csrw sie, t2 + # Setting `stvec` (supervisor trap vector) register: + # Essentially this is a function pointer, but the last two bits can be 00 or 01 + # 00 : All exceptions set pc to BASE + # 01 : Asynchronous interrupts set pc to BASE + 4 x scause + la t3, asm_trap_vector + csrw stvec, t3 + # kinit() is required to return back the SATP value (including MODE) via a0 + csrw satp, a0 + # Force the CPU to take our SATP register. + # To be efficient, if the address space identifier (ASID) portion of SATP is already + # in cache, it will just grab whatever's in cache. However, that means if we've updated + # it in memory, it will be the old table. So, sfence.vma will ensure that the MMU always + # grabs a fresh copy of the SATP register and associated tables. + sfence.vma + # sret will put us in supervisor mode and re-enable interrupts + sret 3: + # Parked harts go here. We need to set these # to only awaken if it receives a software interrupt, # which we're going to call the SIPI (Software Intra-Processor Interrupt). - # We only use these to run user-space programs, although this may + # We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT) + # Which is calculated by: base_address + hart * 4 + # where base address is 0x0200_0000 (MMIO CLINT base address) + # We only use additional harts to run user-space programs, although this may # change. - la t0, TOTAL_NUM_HARTS - lb t1, 0(t0) - addi t1, t1, 1 - sb t1, 0(t0) - - li t0, 1 << 3 - csrw mstatus, t0 - li t1, 1 << 3 - csrw mie, t1 - la t2, kmain - csrw mtvec, t2 - # Waiting loop. An IPI will cause the other harts to - # wake up and start executing. 4: wfi - j 4b + j 4b + + diff --git a/main.cc b/main.cc index 85eae37..339ea9b 100644 --- a/main.cc +++ b/main.cc @@ -1,10 +1,14 @@ typedef unsigned long long uint64; +typedef char uint8; + +#include "uart.h" + struct trap_frame{ uint64 regs[32]; // 0 - 255 bytes - uint64 fregs[32]; // 256 - 511 bytes - uint64 satp; // 512 - 519 bytes - uint64* trap_stack; // 520 byte + uint64 fregs[32]; // 256 - 511 bytes + uint64 satp; // 512 - 519 bytes + uint64* trap_stack; // 520 byte uint64 hartid; // 528 byte }; @@ -29,12 +33,39 @@ void add_timer(int seconds) { *mtimecmp = *mtime + (seconds * 10000000); } +void mmio_write(unsigned long address, int offset, char value) +{ + volatile char* reg = (char*) address; + *(reg+offset) = value; +} + +char mmio_read(unsigned long address, int offset) +{ + volatile char* reg = (char*) address; + return *(reg+offset); +} +extern "C" { + static unsigned int TEXT_START; + static unsigned int TEXT_END; + static unsigned int DATA_START; + static unsigned int DATA_END; + static unsigned int RODATA_START; + static unsigned int RODATA_END; + static unsigned int BSS_START; + static unsigned int BSS_END; + static unsigned int KERNEL_STACK_START; + static unsigned int KERNEL_STACK_END; + static unsigned int HEAP_START; + static unsigned int HEAP_SIZE; + static unsigned int KERNEL_TABLE; +} + extern "C" int kmain(){ + UART uart(0x10000000); + uart.write("fasdf ad f"); // disparar interrupcao de timer - add_timer(1); return 0; - } extern "C" void m_trap(uint64 epc, uint64 tval,uint64 cause,uint64 hart, uint64 status, trap_frame* trap_frame) diff --git a/mem.S b/mem.S new file mode 100644 index 0000000..e394063 --- /dev/null +++ b/mem.S @@ -0,0 +1,43 @@ +// mem.S +// Importation of linker symbols + +.section .rodata +.global HEAP_START +HEAP_START: .dword _heap_start + +.global HEAP_SIZE +HEAP_SIZE: .dword _heap_size + +.global TEXT_START +TEXT_START: .dword _text_start + +.global TEXT_END +TEXT_END: .dword _text_end + +.global DATA_START +DATA_START: .dword _data_start + +.global DATA_END +DATA_END: .dword _data_end + +.global RODATA_START +RODATA_START: .dword _rodata_start + +.global RODATA_END +RODATA_END: .dword _rodata_end + +.global BSS_START +BSS_START: .dword _bss_start + +.global BSS_END +BSS_END: .dword _bss_end + +.global KERNEL_STACK_START +KERNEL_STACK_START: .dword _stack_start + +.global KERNEL_STACK_END +KERNEL_STACK_END: .dword _stack_end + +.section .data +.global KERNEL_TABLE +KERNEL_TABLE: .dword 0 \ No newline at end of file diff --git a/trap.S b/trap.S index 2112a27..ca94305 100644 --- a/trap.S +++ b/trap.S @@ -1,8 +1,8 @@ .section .text -.global m_trap_vector +.global asm_trap_vector .align 4 -m_trap_vector: +asm_trap_vector: # Save registers addi sp, sp, -128 # Make some space in the stack sd ra, 0(sp) # Return address diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..929b28c --- /dev/null +++ b/uart.h @@ -0,0 +1,61 @@ + +typedef unsigned long long uint64; + +class UART { +private: + uint64 _base_addr = 0x100000000; +public: + UART(uint64 base_addr):_base_addr(base_addr) { + this->_init(_base_addr); + } +private: + void _init(uint64 base_addr) + { + char* ptr = reinterpret_cast(base_addr); + + // lcr register - word size + char lcr = (1<<0) | (1<<1); + *(ptr + 3) = lcr; + + // enable fifo and enable buffer interrupt + *(ptr + 2) = (1 << 0); + *(ptr + 1 ) = (0 << 0); + + unsigned int divisor = 592; + char divisor_least = divisor & 0xff; + char divisor_most = divisor >> 8; + + *(ptr + 3) = (lcr | 1 << 7); + + *(ptr + 0) = divisor_least; + *(ptr + 1) = divisor_most; + *(ptr + 3) = lcr; + } +public: + + void write(char* str){ + int i=0; + while(str[i] != '\0'){ + this->put(*(str+ i)); + i++; + } + } + + char get() { + char* ptr = reinterpret_cast(this->_base_addr); + if(*(ptr+5) & 1 == 0){ + return 0; + }else{ + return *ptr; + } + } + + void put(char character) + { + char* ptr = reinterpret_cast(this->_base_addr); + *ptr = character; + } + + +}; + diff --git a/uart.h.gch b/uart.h.gch new file mode 100644 index 0000000..f43d78e Binary files /dev/null and b/uart.h.gch differ