├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── arch ├── amd64 │ ├── Makefrag │ └── TODO └── riscv │ ├── Makefrag │ ├── include │ ├── asm.h │ ├── csr.h │ ├── mmu.h │ └── task.h │ ├── init.c │ ├── linker.ld │ ├── start.S │ ├── switch_to.S │ ├── task.c │ ├── trap.S │ ├── trap_handler.c │ └── user.S ├── client.py ├── dhcp.c ├── driver ├── axiethernet.c ├── clint.c ├── dummyeth.c ├── include │ ├── axiethernet.h │ ├── clint.h │ ├── dummyeth.h │ ├── uart16550.h │ └── uartlite.h ├── uart16550.c └── uartlite.c ├── elf.c ├── eth.c ├── hwtimer.c ├── include ├── arch.h ├── dhcp.h ├── elf.h ├── error.h ├── eth.h ├── hwtimer.h ├── ip.h ├── judge.h ├── mm.h ├── printf.h ├── stdbool.h ├── stdint.h ├── stdio.h ├── string.h ├── task.h ├── tftp.h ├── timer.h ├── tty.h ├── utils.h └── vm.h ├── ip.c ├── judge.c ├── main.c ├── mm.c ├── printf.c ├── riscv_soc.dtb ├── start_fileserver ├── stdin.txt ├── string.c ├── task.c ├── tftp.c ├── timer.c ├── tty.c ├── user └── riscv │ ├── Makefile │ ├── empty.S │ ├── hello.c │ ├── helloxx.cpp │ ├── linker.ld │ └── rdcycle.c ├── utils.c └── vm.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.elf 3 | kernel 4 | 5 | include/arch 6 | include/driver 7 | 8 | tftp_root/ 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCH = riscv 2 | PREFIX = riscv64-unknown-linux-gnu- 3 | GCC = $(PREFIX)gcc 4 | OBJCOPY = $(PREFIX)objcopy 5 | OBJDUMP = $(PREFIX)objdump 6 | 7 | DEFINES = -DPRINTF_DISABLE_SUPPORT_FLOAT -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL \ 8 | -DPRINTF_DISABLE_SUPPORT_LONG_LONG 9 | CFLAGS = -fno-builtin -nostdlib -static -Wl,--gc-sections,--print-gc-sections -O2 -Wall \ 10 | -Iinclude $(DEFINES) 11 | LDFLAGS = -z separate-code 12 | 13 | -include Makefile.config 14 | 15 | .PHONY: all 16 | all: 17 | include arch/$(ARCH)/Makefrag 18 | 19 | HEADERS=$(wildcard include/*.h) $(wildcard arch/$(ARCH)/include/*.h) $(wildcard driver/include/*.h) 20 | SOURCES=$(wildcard *.c *.S) $(wildcard arch/$(ARCH)/*.c arch/$(ARCH)/*.S) $(wildcard driver/*.c driver/*.S) 21 | OBJECTS=$(patsubst %.c,%.o,$(wildcard *.c)) $(patsubst %.S,%.o,$(wildcard *.S)) \ 22 | $(patsubst %.c,%.o,$(wildcard arch/$(ARCH)/*.c)) $(patsubst %.S,%.o,$(wildcard arch/$(ARCH)/*.S)) \ 23 | $(patsubst %.c,%.o,$(wildcard driver/*.c)) $(patsubst %.S,%.o,$(wildcard driver/*.S)) 24 | 25 | .PHONY: all 26 | all: $(OBJECTS) kernel tftp_root 27 | 28 | .PHONY: asm 29 | asm: kernel 30 | $(OBJDUMP) -xd $< | vi - 31 | 32 | include/arch: 33 | -rm $@ 34 | ln -s ../arch/$(ARCH)/include include/arch 35 | 36 | include/driver: 37 | -rm $@ 38 | ln -s ../driver/include include/driver 39 | 40 | %.o: %.c $(HEADERS) include/arch include/driver 41 | $(GCC) $(CFLAGS) -c $< -o $@ 42 | 43 | %.o: %.S $(HEADERS) include/arch include/driver 44 | $(GCC) $(CFLAGS) -c $< -o $@ 45 | 46 | kernel: $(OBJECTS) arch/$(ARCH)/linker.ld 47 | $(GCC) -Tarch/$(ARCH)/linker.ld $(CFLAGS) $(LDFLAGS) $^ -o $@ 48 | 49 | .PHONY: tftp_root 50 | tftp_root: kernel riscv_soc.dtb 51 | mkdir -p $@ 52 | cp kernel $@/ 53 | cp riscv_soc.dtb $@/dtb 54 | 55 | .PHONY: clean 56 | clean: 57 | -rm *.o *.elf kernel arch/*/*.o 58 | -rm include/arch 59 | -rm include/driver 60 | -rm -r tftp_root 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vijosos 2 | Vijos: Vijos Isn't Just an Operating System 3 | 4 | Build musl-libc with: 5 | 6 | ```bash 7 | ARCH=riscv CROSS_COMPILE="riscv64-unknown-linux-gnu-" CFLAGS="-march=rv64imac" ./configure 8 | make 9 | ``` 10 | 11 | Build libstdc++ with: 12 | 13 | ```bash 14 | mkdir build 15 | cd build 16 | ../configure --host=riscv64-unknown-linux-gnu --enable-clocale=generic 17 | make 18 | ``` 19 | -------------------------------------------------------------------------------- /arch/amd64/Makefrag: -------------------------------------------------------------------------------- 1 | .PHONY: qemu 2 | qemu: kernel 3 | false 4 | 5 | -------------------------------------------------------------------------------- /arch/amd64/TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vijos/vijosos/f242a71cb68438d0260cee06694ed6d61a9e52db/arch/amd64/TODO -------------------------------------------------------------------------------- /arch/riscv/Makefrag: -------------------------------------------------------------------------------- 1 | CFLAGS += -mcmodel=medany 2 | 3 | .PHONY: qemu 4 | qemu: kernel 5 | qemu-system-riscv64 -machine virt -nographic -kernel kernel -m 1024 -smp 1 6 | 7 | -------------------------------------------------------------------------------- /arch/riscv/include/asm.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_RISCV_ASM_H_ 2 | #define _ARCH_RISCV_ASM_H_ 3 | 4 | #include "stdint.h" 5 | 6 | static inline void fence() 7 | { 8 | asm volatile ("fence"); 9 | } 10 | 11 | static inline void fence_i() 12 | { 13 | asm volatile ("fence.i"); 14 | } 15 | 16 | static inline void flush_tlb() 17 | { 18 | asm volatile ("sfence.vma"); 19 | } 20 | 21 | static inline void flush_tlb_one(uintptr_t va) 22 | { 23 | asm volatile ("sfence.vma %0" : : "r"(va)); 24 | } 25 | 26 | static inline uint64_t rdcycle() 27 | { 28 | uint64_t v; 29 | asm volatile ("rdcycle %0" : "=r"(v)); 30 | return v; 31 | } 32 | 33 | #define CLOCK_FREQ 125000000ul 34 | 35 | #define STACK_OFFSET (0) 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /arch/riscv/include/csr.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, The Regents of the University of California (Regents). 3 | All Rights Reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the Regents nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 17 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 18 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 19 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 24 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 25 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 26 | */ 27 | 28 | #ifndef _ARCH_RISCV_CSR_H_ 29 | #define _ARCH_RISCV_CSR_H_ 30 | 31 | #define MSTATUS_UIE 0x00000001 32 | #define MSTATUS_SIE 0x00000002 33 | #define MSTATUS_HIE 0x00000004 34 | #define MSTATUS_MIE 0x00000008 35 | #define MSTATUS_UPIE 0x00000010 36 | #define MSTATUS_SPIE 0x00000020 37 | #define MSTATUS_HPIE 0x00000040 38 | #define MSTATUS_MPIE 0x00000080 39 | #define MSTATUS_SPP 0x00000100 40 | #define MSTATUS_HPP 0x00000600 41 | #define MSTATUS_MPP 0x00001800 42 | #define MSTATUS_VS 0x01800000 43 | #define MSTATUS_FS 0x00006000 44 | #define MSTATUS_XS 0x00018000 45 | #define MSTATUS_MPRV 0x00020000 46 | #define MSTATUS_SUM 0x00040000 47 | #define MSTATUS_MXR 0x00080000 48 | #define MSTATUS_TVM 0x00100000 49 | #define MSTATUS_TW 0x00200000 50 | #define MSTATUS_TSR 0x00400000 51 | #define MSTATUS_UXL 0x0000000300000000 52 | #define MSTATUS_SXL 0x0000000C00000000 53 | #define MSTATUS_SD 0x8000000000000000 54 | 55 | #define MSTATUS_MPP_SHIFT 11 56 | 57 | #define SSTATUS_UIE 0x00000001 58 | #define SSTATUS_SIE 0x00000002 59 | #define SSTATUS_UPIE 0x00000010 60 | #define SSTATUS_SPIE 0x00000020 61 | #define SSTATUS_SPP 0x00000100 62 | #define SSTATUS_VS 0x01800000 63 | #define SSTATUS_FS 0x00006000 64 | #define SSTATUS_XS 0x00018000 65 | #define SSTATUS_SUM 0x00040000 66 | #define SSTATUS_MXR 0x00080000 67 | #define SSTATUS_UXL 0x0000000300000000 68 | #define SSTATUS_SD 0x8000000000000000 69 | 70 | #define DCSR_XDEBUGVER (3U<<30) 71 | #define DCSR_NDRESET (1<<29) 72 | #define DCSR_FULLRESET (1<<28) 73 | #define DCSR_EBREAKM (1<<15) 74 | #define DCSR_EBREAKH (1<<14) 75 | #define DCSR_EBREAKS (1<<13) 76 | #define DCSR_EBREAKU (1<<12) 77 | #define DCSR_STOPCYCLE (1<<10) 78 | #define DCSR_STOPTIME (1<<9) 79 | #define DCSR_CAUSE (7<<6) 80 | #define DCSR_DEBUGINT (1<<5) 81 | #define DCSR_HALT (1<<3) 82 | #define DCSR_STEP (1<<2) 83 | #define DCSR_PRV (3<<0) 84 | 85 | #define DCSR_CAUSE_NONE 0 86 | #define DCSR_CAUSE_SWBP 1 87 | #define DCSR_CAUSE_HWBP 2 88 | #define DCSR_CAUSE_DEBUGINT 3 89 | #define DCSR_CAUSE_STEP 4 90 | #define DCSR_CAUSE_HALT 5 91 | 92 | #define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) 93 | #define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) 94 | #define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11)) 95 | 96 | #define MCONTROL_SELECT (1<<19) 97 | #define MCONTROL_TIMING (1<<18) 98 | #define MCONTROL_ACTION (0x3f<<12) 99 | #define MCONTROL_CHAIN (1<<11) 100 | #define MCONTROL_MATCH (0xf<<7) 101 | #define MCONTROL_M (1<<6) 102 | #define MCONTROL_H (1<<5) 103 | #define MCONTROL_S (1<<4) 104 | #define MCONTROL_U (1<<3) 105 | #define MCONTROL_EXECUTE (1<<2) 106 | #define MCONTROL_STORE (1<<1) 107 | #define MCONTROL_LOAD (1<<0) 108 | 109 | #define MCONTROL_TYPE_NONE 0 110 | #define MCONTROL_TYPE_MATCH 2 111 | 112 | #define MCONTROL_ACTION_DEBUG_EXCEPTION 0 113 | #define MCONTROL_ACTION_DEBUG_MODE 1 114 | #define MCONTROL_ACTION_TRACE_START 2 115 | #define MCONTROL_ACTION_TRACE_STOP 3 116 | #define MCONTROL_ACTION_TRACE_EMIT 4 117 | 118 | #define MCONTROL_MATCH_EQUAL 0 119 | #define MCONTROL_MATCH_NAPOT 1 120 | #define MCONTROL_MATCH_GE 2 121 | #define MCONTROL_MATCH_LT 3 122 | #define MCONTROL_MATCH_MASK_LOW 4 123 | #define MCONTROL_MATCH_MASK_HIGH 5 124 | 125 | #define MIP_SSIP (1 << IRQ_S_SOFT) 126 | #define MIP_HSIP (1 << IRQ_H_SOFT) 127 | #define MIP_MSIP (1 << IRQ_M_SOFT) 128 | #define MIP_STIP (1 << IRQ_S_TIMER) 129 | #define MIP_HTIP (1 << IRQ_H_TIMER) 130 | #define MIP_MTIP (1 << IRQ_M_TIMER) 131 | #define MIP_SEIP (1 << IRQ_S_EXT) 132 | #define MIP_HEIP (1 << IRQ_H_EXT) 133 | #define MIP_MEIP (1 << IRQ_M_EXT) 134 | 135 | #define SIP_SSIP MIP_SSIP 136 | #define SIP_STIP MIP_STIP 137 | 138 | #define PRV_U 0 139 | #define PRV_S 1 140 | #define PRV_H 2 141 | #define PRV_M 3 142 | 143 | #define PMP_R 0x01 144 | #define PMP_W 0x02 145 | #define PMP_X 0x04 146 | #define PMP_A 0x18 147 | #define PMP_L 0x80 148 | #define PMP_SHIFT 2 149 | 150 | #define PMP_TOR 0x08 151 | #define PMP_NA4 0x10 152 | #define PMP_NAPOT 0x18 153 | 154 | #define CAUSE_ILLINST 2 155 | #define CAUSE_ECALL_U 8 156 | #define CAUSE_PF_FETCH 12 157 | #define CAUSE_PF_LOAD 13 158 | #define CAUSE_PF_STORE 15 159 | #define CAUSE_INT 0x8000000000000000 160 | 161 | #define IRQ_S_SOFT 1 162 | #define IRQ_H_SOFT 2 163 | #define IRQ_M_SOFT 3 164 | #define IRQ_S_TIMER 5 165 | #define IRQ_H_TIMER 6 166 | #define IRQ_M_TIMER 7 167 | #define IRQ_S_EXT 9 168 | #define IRQ_H_EXT 10 169 | #define IRQ_M_EXT 11 170 | #define IRQ_COP 12 171 | #define IRQ_HOST 13 172 | 173 | #define DEFAULT_RSTVEC 0x00001000 174 | //#define CLINT_BASE 0x02000000 175 | #define CLINT_SIZE 0x000c0000 176 | 177 | #ifdef __riscv 178 | 179 | #ifndef __ASSEMBLER__ 180 | 181 | #ifdef __GNUC__ 182 | 183 | #define read_csr(reg) ({ unsigned long __tmp; \ 184 | asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ 185 | __tmp; }) 186 | 187 | #define write_csr(reg, val) ({ \ 188 | asm volatile ("csrw " #reg ", %0" :: "rK"(val)); }) 189 | 190 | #define swap_csr(reg, val) ({ unsigned long __tmp; \ 191 | asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ 192 | __tmp; }) 193 | 194 | #define set_csr(reg, bit) ({ unsigned long __tmp; \ 195 | asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ 196 | __tmp; }) 197 | 198 | #define clear_csr(reg, bit) ({ unsigned long __tmp; \ 199 | asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ 200 | __tmp; }) 201 | 202 | #define rdtime() read_csr(time) 203 | #define rdcycle() read_csr(cycle) 204 | #define rdinstret() read_csr(instret) 205 | 206 | #endif 207 | 208 | #endif 209 | 210 | #endif 211 | 212 | #endif 213 | -------------------------------------------------------------------------------- /arch/riscv/include/mmu.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_RISCV_VM_H_ 2 | #define _ARCH_RISCV_VM_H_ 3 | 4 | #include "stdint.h" 5 | #include "arch/asm.h" 6 | #include "arch/csr.h" 7 | 8 | #define KERNEL_BASE 0x0ul // we are in m-mode 9 | // #define KERNEL_BASE 0xffffffc000000000ul 10 | #define USER_TOP 0x4000000000ul 11 | #define USER_BOTTOM 0x0000001000ul 12 | 13 | #define p2v(x) (void *)((x) + KERNEL_BASE) 14 | #define v2p(x) ((uintptr_t)(x) - KERNEL_BASE) 15 | 16 | #define PGSIZE 0x1000ul 17 | #define PGALIGN(x) (((x) + PGSIZE - 1) & ~(PGSIZE - 1)) 18 | #define PGALIGN_FLOOR(x) ((x) & ~(PGSIZE - 1)) 19 | #define PGSHIFT 12 20 | 21 | #define SATP_MODE 0xF000000000000000 22 | #define SATP_ASID 0x0FFFF00000000000 23 | #define SATP_PPN 0x00000FFFFFFFFFFF 24 | 25 | #define SATP_MODE_OFF 0x0000000000000000 26 | #define SATP_MODE_SV39 0x8000000000000000 27 | #define SATP_MODE_SV48 0x9000000000000000 28 | #define SATP_MODE_SV57 0xa000000000000000 29 | #define SATP_MODE_SV64 0xb000000000000000 30 | 31 | // page table entry (PTE) fields 32 | #define PTE_V 0x001 // Valid 33 | #define PTE_R 0x002 // Read 34 | #define PTE_W 0x004 // Write 35 | #define PTE_X 0x008 // Execute 36 | #define PTE_U 0x010 // User 37 | #define PTE_G 0x020 // Global 38 | #define PTE_A 0x040 // Accessed 39 | #define PTE_D 0x080 // Dirty 40 | #define PTE_SOFT 0x300 // Reserved for Software 41 | 42 | #define PTE_PPN_SHIFT 10 43 | 44 | #define PTE_VALID(x) ((x) & PTE_V) 45 | #define PTE_ACCESSED(x) ((x) & PTE_A) 46 | #define PTE_DIRTY(x) ((x) & PTE_D) 47 | #define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) 48 | 49 | #define PTE_PPN(x) (((x) >> PTE_PPN_SHIFT) << PGSHIFT) 50 | 51 | #define PGLEVEL_SHIFT 9 52 | 53 | #define PGLEVEL 3 54 | 55 | #define PT_IDX(l, x) (((x) >> (PGSHIFT + PGLEVEL_SHIFT * (l))) & 0x1ff) 56 | 57 | typedef uint64_t pte_t; 58 | 59 | #include "vm.h" 60 | 61 | static inline pte_t pte_flags(uint64_t flags) 62 | { 63 | pte_t pte = 0; 64 | if (flags & VM_R) pte |= PTE_R; 65 | if (flags & VM_W) pte |= PTE_W; 66 | if (flags & VM_X) pte |= PTE_X; 67 | if (flags & VM_A) pte |= PTE_A; 68 | if (flags & VM_D) pte |= PTE_D; 69 | if (flags & VM_U) pte |= PTE_U; 70 | return pte; 71 | } 72 | 73 | static inline pte_t make_pte(int level, uintptr_t ppn, uint64_t flags) 74 | { 75 | pte_t pte = (ppn >> PGSHIFT) << PTE_PPN_SHIFT; 76 | if (level == 0) 77 | { 78 | if (flags & VM_R) pte |= PTE_R; 79 | if (flags & VM_W) pte |= PTE_W; 80 | if (flags & VM_X) pte |= PTE_X; 81 | if (flags & VM_A) pte |= PTE_A; 82 | if (flags & VM_D) pte |= PTE_D; 83 | } 84 | else 85 | { 86 | pte |= PTE_A; 87 | pte |= PTE_D; 88 | } 89 | if (flags & VM_U) pte |= PTE_U; 90 | pte |= PTE_V; 91 | return pte; 92 | } 93 | 94 | static inline void set_pt(pte_t *pt) 95 | { 96 | uintptr_t ppn = v2p(pt) >> PGSHIFT; 97 | write_csr(satp, ppn | SATP_MODE_SV39); 98 | flush_tlb(); 99 | } 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /arch/riscv/include/task.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_RISCV_TASK_H_ 2 | #define _ARCH_RISCV_TASK_H_ 3 | 4 | #include "stdint.h" 5 | 6 | typedef struct 7 | { 8 | uintptr_t ra; 9 | uintptr_t sp; 10 | uintptr_t tp; 11 | uintptr_t s0; 12 | uintptr_t s1; 13 | uintptr_t s2; 14 | uintptr_t s3; 15 | uintptr_t s4; 16 | uintptr_t s5; 17 | uintptr_t s6; 18 | uintptr_t s7; 19 | uintptr_t s8; 20 | uintptr_t s9; 21 | uintptr_t s10; 22 | uintptr_t s11; 23 | } regs_t; 24 | 25 | typedef struct 26 | { 27 | uintptr_t zero; 28 | uintptr_t ra; 29 | uintptr_t sp; 30 | uintptr_t gp; 31 | uintptr_t tp; 32 | uintptr_t t0; 33 | uintptr_t t1; 34 | uintptr_t t2; 35 | uintptr_t s0; 36 | uintptr_t s1; 37 | uintptr_t a0; 38 | uintptr_t a1; 39 | uintptr_t a2; 40 | uintptr_t a3; 41 | uintptr_t a4; 42 | uintptr_t a5; 43 | uintptr_t a6; 44 | uintptr_t a7; 45 | uintptr_t s2; 46 | uintptr_t s3; 47 | uintptr_t s4; 48 | uintptr_t s5; 49 | uintptr_t s6; 50 | uintptr_t s7; 51 | uintptr_t s8; 52 | uintptr_t s9; 53 | uintptr_t s10; 54 | uintptr_t s11; 55 | uintptr_t t3; 56 | uintptr_t t4; 57 | uintptr_t t5; 58 | uintptr_t t6; 59 | } trap_regs_t; 60 | 61 | typedef struct 62 | { 63 | union 64 | { 65 | trap_regs_t regs; 66 | uintptr_t x[32]; 67 | }; 68 | uintptr_t status; 69 | uintptr_t epc; 70 | uintptr_t tval; 71 | uintptr_t cause; 72 | uintptr_t cycle; 73 | } trap_frame_t; 74 | 75 | #define STACK_REG sp 76 | #define RETURN_ADDR_REG ra 77 | #define THREAD_POINTER_REG tp 78 | 79 | static inline struct task *get_current() 80 | { 81 | register uintptr_t tp asm("tp"); 82 | return (struct task *)tp; 83 | } 84 | 85 | static inline void set_current(struct task *task) 86 | { 87 | asm volatile ("mv tp, %0" 88 | : 89 | : "r"(task)); 90 | } 91 | 92 | void arch_return_from_user(); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /arch/riscv/init.c: -------------------------------------------------------------------------------- 1 | #include "arch/asm.h" 2 | #include "arch/csr.h" 3 | #include "arch/task.h" 4 | #include "arch/mmu.h" 5 | #include "mm.h" 6 | #include "stdio.h" 7 | 8 | #include "driver/clint.h" 9 | #include "driver/axiethernet.h" 10 | #include "driver/dummyeth.h" 11 | #include "driver/uartlite.h" 12 | #include "driver/uart16550.h" 13 | 14 | void init_arch() 15 | { 16 | uintptr_t pmpc = PMP_NAPOT | PMP_R | PMP_W | PMP_X; 17 | asm volatile ("la t0, 1f\n\t" 18 | "csrrw t0, mtvec, t0\n\t" 19 | "csrw pmpaddr0, %1\n\t" 20 | "csrw pmpcfg0, %0\n\t" 21 | ".align 2\n\t" 22 | "1: csrw mtvec, t0" 23 | : : "r" (pmpc), "r" (-1UL) : "t0"); 24 | } 25 | 26 | void init_arch_dev(int hartid, void *dtb) 27 | { 28 | // TODO: probe 29 | if ((uintptr_t)dtb >= 0x60000000) 30 | { 31 | CLINT_BASE = p2v(0x2000000ul); 32 | init_clint(); 33 | UARTLITE_BASE = p2v(0x60100000ul); 34 | init_uartlite(); 35 | ETH_BASE = p2v(0x60200000ul); 36 | DMA_BASE = p2v(0x60300000ul); 37 | init_axiethernet(); 38 | } 39 | else 40 | { 41 | // QEMU 42 | CLINT_BASE = p2v(0x2000000ul); 43 | init_clint(); 44 | UART16550_BASE = p2v(0x10000000ul); 45 | init_uart16550(); 46 | init_dummyeth(); 47 | } 48 | 49 | // TODO: probe 50 | init_mm(); 51 | add_pages(0x80000000, 0x40000); 52 | finish_add_pages(); 53 | } 54 | -------------------------------------------------------------------------------- /arch/riscv/linker.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | PA_BASE = 0x80000000; 4 | VA_BASE = 0x80000000; 5 | ENTRY(_start) 6 | . = PA_BASE; 7 | .text : 8 | { 9 | *(.text.start) 10 | *(.text*) 11 | } 12 | _text_end = .; 13 | . = ALIGN(0x1000); 14 | .rodata : 15 | { 16 | *(.rodata*) 17 | *(.srodata*) 18 | } 19 | . = ALIGN(0x1000); 20 | .data : 21 | { 22 | *(.data*) 23 | *(.sdata*) 24 | } 25 | _bss_begin = .; 26 | .bss : 27 | { 28 | *(.bss*) 29 | *(.sbss*) 30 | } 31 | . = ALIGN(0x10); 32 | _bss_end = .; 33 | /DISCARD/ : 34 | { 35 | *(.note.gnu.build-id) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /arch/riscv/start.S: -------------------------------------------------------------------------------- 1 | .section .text.start, "ax", @progbits 2 | .global _start 3 | _start: 4 | # TODO: check mhartid 5 | csrw mscratch, zero 6 | la t0, trap_entry 7 | csrw mtvec, t0 8 | la sp, _end_of_stack - 0x10 9 | j start 10 | 11 | .section .bss 12 | .balign 16 13 | _stack: 14 | .space 0x2000 15 | _end_of_stack: 16 | -------------------------------------------------------------------------------- /arch/riscv/switch_to.S: -------------------------------------------------------------------------------- 1 | .section .text, "ax", @progbits 2 | .global switch_to 3 | switch_to: 4 | beqz a0, a0_null 5 | sd ra, 0x0(a0) 6 | sd sp, 0x8(a0) 7 | sd tp, 0x10(a0) 8 | sd s0, 0x18(a0) 9 | sd s1, 0x20(a0) 10 | sd s2, 0x28(a0) 11 | sd s3, 0x30(a0) 12 | sd s4, 0x38(a0) 13 | sd s5, 0x40(a0) 14 | sd s6, 0x48(a0) 15 | sd s7, 0x50(a0) 16 | sd s8, 0x58(a0) 17 | sd s9, 0x60(a0) 18 | sd s10, 0x68(a0) 19 | sd s11, 0x70(a0) 20 | a0_null: 21 | ld ra, 0x0(a1) 22 | ld sp, 0x8(a1) 23 | ld tp, 0x10(a1) 24 | ld s0, 0x18(a1) 25 | ld s1, 0x20(a1) 26 | ld s2, 0x28(a1) 27 | ld s3, 0x30(a1) 28 | ld s4, 0x38(a1) 29 | ld s5, 0x40(a1) 30 | ld s6, 0x48(a1) 31 | ld s7, 0x50(a1) 32 | ld s8, 0x58(a1) 33 | ld s9, 0x60(a1) 34 | ld s10, 0x68(a1) 35 | ld s11, 0x70(a1) 36 | jr ra 37 | -------------------------------------------------------------------------------- /arch/riscv/task.c: -------------------------------------------------------------------------------- 1 | #include "arch/task.h" 2 | #include "task.h" 3 | #include "arch/asm.h" 4 | #include "arch/csr.h" 5 | #include "arch/mmu.h" 6 | #include "string.h" 7 | 8 | static struct task *last_task; 9 | static struct task user_task; 10 | 11 | __attribute__ ((aligned (16))) uint8_t trap_stack[0x1000]; 12 | void riscv_enter_user(void); 13 | 14 | void arch_call_user(void *pt, uintptr_t pc, uintptr_t sp) 15 | { 16 | set_pt((pte_t *)pt); 17 | write_csr(mepc, pc); 18 | uintptr_t status = MSTATUS_MPIE | (PRV_U << MSTATUS_MPP_SHIFT); 19 | write_csr(mstatus, status); 20 | write_csr(mideleg, 0); 21 | write_csr(medeleg, 0); 22 | write_csr(mie, MIP_MTIP); 23 | 24 | memset(&user_task.regs, 0, sizeof(user_task.regs)); 25 | user_task.regs.ra = (uintptr_t)riscv_enter_user; 26 | user_task.regs.sp = sp; 27 | last_task = current; 28 | // Set up trap stack. 29 | write_csr(mscratch, (uintptr_t)trap_stack + sizeof(trap_stack) + STACK_OFFSET); 30 | switch_to(current, &user_task); 31 | } 32 | 33 | void arch_return_from_user() 34 | { 35 | switch_to(NULL, last_task); 36 | } 37 | -------------------------------------------------------------------------------- /arch/riscv/trap.S: -------------------------------------------------------------------------------- 1 | #include "arch/csr.h" 2 | 3 | #define LOAD ld 4 | #define STORE sd 5 | #define REGBYTES 8 6 | 7 | .altmacro 8 | .macro SAVE_ALL 9 | LOCAL _restore_kernel_sp 10 | LOCAL _save_context 11 | 12 | # If coming from userspace, preserve the user stack pointer and load 13 | # the kernel stack pointer. If we came from the kernel, scratch 14 | # will contain 0, and we should continue on the current stack. 15 | 16 | csrrw sp, mscratch, sp 17 | bnez sp, _save_context 18 | 19 | _restore_kernel_sp: 20 | csrr sp, mscratch 21 | _save_context: 22 | addi sp, sp, -37 * REGBYTES 23 | 24 | STORE x5, 5*REGBYTES(sp) 25 | rdcycle t0 26 | STORE t0, 36*REGBYTES(sp) 27 | 28 | STORE x1, 1*REGBYTES(sp) 29 | # x2 is sp, save it later 30 | STORE x3, 3*REGBYTES(sp) 31 | STORE x4, 4*REGBYTES(sp) 32 | STORE x6, 6*REGBYTES(sp) 33 | STORE x7, 7*REGBYTES(sp) 34 | STORE x8, 8*REGBYTES(sp) 35 | STORE x9, 9*REGBYTES(sp) 36 | STORE x10, 10*REGBYTES(sp) 37 | STORE x11, 11*REGBYTES(sp) 38 | STORE x12, 12*REGBYTES(sp) 39 | STORE x13, 13*REGBYTES(sp) 40 | STORE x14, 14*REGBYTES(sp) 41 | STORE x15, 15*REGBYTES(sp) 42 | STORE x16, 16*REGBYTES(sp) 43 | STORE x17, 17*REGBYTES(sp) 44 | STORE x18, 18*REGBYTES(sp) 45 | STORE x19, 19*REGBYTES(sp) 46 | STORE x20, 20*REGBYTES(sp) 47 | STORE x21, 21*REGBYTES(sp) 48 | STORE x22, 22*REGBYTES(sp) 49 | STORE x23, 23*REGBYTES(sp) 50 | STORE x24, 24*REGBYTES(sp) 51 | STORE x25, 25*REGBYTES(sp) 52 | STORE x26, 26*REGBYTES(sp) 53 | STORE x27, 27*REGBYTES(sp) 54 | STORE x28, 28*REGBYTES(sp) 55 | STORE x29, 29*REGBYTES(sp) 56 | STORE x30, 30*REGBYTES(sp) 57 | STORE x31, 31*REGBYTES(sp) 58 | 59 | csrrw t0, mscratch, zero # read old sp to t0, clear scratch 60 | STORE t0, 2*REGBYTES(sp) 61 | 62 | # get and save status, epc, tval, cause 63 | csrr t0, mstatus 64 | STORE t0, 32*REGBYTES(sp) 65 | 66 | csrr t0, mepc 67 | STORE t0, 33*REGBYTES(sp) 68 | 69 | csrr t0, mtval 70 | STORE t0, 34*REGBYTES(sp) 71 | 72 | csrr t0, mcause 73 | STORE t0, 35*REGBYTES(sp) 74 | .endm 75 | 76 | .macro RESTORE_ALL 77 | LOCAL _save_kernel_sp 78 | LOCAL _restore_context 79 | 80 | LOAD t1, 32*REGBYTES(sp) # saved mstatus 81 | li t0, MSTATUS_MPP 82 | and t0, t1, t0 83 | bnez t0, _restore_context 84 | 85 | _save_kernel_sp: 86 | addi t0, sp, 37 * REGBYTES 87 | csrw mscratch, t0 88 | _restore_context: 89 | csrw mstatus, t1 90 | 91 | LOAD t0, 33*REGBYTES(sp) 92 | csrw mepc, t0 93 | 94 | LOAD x1, 1*REGBYTES(sp) 95 | # x2 is sp, load it later 96 | LOAD x3, 3*REGBYTES(sp) 97 | LOAD x4, 4*REGBYTES(sp) 98 | LOAD x5, 5*REGBYTES(sp) 99 | LOAD x6, 6*REGBYTES(sp) 100 | LOAD x7, 7*REGBYTES(sp) 101 | LOAD x8, 8*REGBYTES(sp) 102 | LOAD x9, 9*REGBYTES(sp) 103 | LOAD x10, 10*REGBYTES(sp) 104 | LOAD x11, 11*REGBYTES(sp) 105 | LOAD x12, 12*REGBYTES(sp) 106 | LOAD x13, 13*REGBYTES(sp) 107 | LOAD x14, 14*REGBYTES(sp) 108 | LOAD x15, 15*REGBYTES(sp) 109 | LOAD x16, 16*REGBYTES(sp) 110 | LOAD x17, 17*REGBYTES(sp) 111 | LOAD x18, 18*REGBYTES(sp) 112 | LOAD x19, 19*REGBYTES(sp) 113 | LOAD x20, 20*REGBYTES(sp) 114 | LOAD x21, 21*REGBYTES(sp) 115 | LOAD x22, 22*REGBYTES(sp) 116 | LOAD x23, 23*REGBYTES(sp) 117 | LOAD x24, 24*REGBYTES(sp) 118 | LOAD x25, 25*REGBYTES(sp) 119 | LOAD x26, 26*REGBYTES(sp) 120 | LOAD x27, 27*REGBYTES(sp) 121 | LOAD x28, 28*REGBYTES(sp) 122 | LOAD x29, 29*REGBYTES(sp) 123 | LOAD x30, 30*REGBYTES(sp) 124 | LOAD x31, 31*REGBYTES(sp) 125 | # restore sp last 126 | LOAD x2, 2*REGBYTES(sp) 127 | .endm 128 | 129 | .section .text, "ax", @progbits 130 | .balign 64 131 | .global trap_entry 132 | trap_entry: 133 | SAVE_ALL 134 | mv a0, sp 135 | jal trap_handler 136 | # sp should be the same as before "jal trap_handler" 137 | # Fall through 138 | 139 | .global trap_return 140 | trap_return: 141 | RESTORE_ALL 142 | mret # return from machine call 143 | -------------------------------------------------------------------------------- /arch/riscv/trap_handler.c: -------------------------------------------------------------------------------- 1 | #include "arch/asm.h" 2 | #include "arch/csr.h" 3 | #include "arch/task.h" 4 | #include "arch.h" 5 | #include "error.h" 6 | #include "vm.h" 7 | #include "arch/mmu.h" 8 | #include "task.h" 9 | #include "printf.h" 10 | #include "stdbool.h" 11 | 12 | const char *const cause_desc[] = 13 | { 14 | "Instruction address misaligned", 15 | "Instruction access fault", 16 | "Illegal instruction", 17 | "Breakpoint", 18 | "Load address misaligned", 19 | "Load access fault", 20 | "Store/AMO address misaligned", 21 | "Store/AMO access fault", 22 | "Environment call from U-mode", 23 | "Environment call from S-mode", 24 | "Reserved", 25 | "Environment call from M-mode", 26 | "Instruction page fault", 27 | "Load page fault", 28 | "Reserved", 29 | "Store/AMO page fault" 30 | }; 31 | 32 | void print_trap_frame(trap_frame_t *tf) 33 | { 34 | uint64_t cause = tf->cause; 35 | const char *desc = "Interrupt"; 36 | if ((cause & CAUSE_INT) == 0) 37 | { 38 | desc = cause < 16 ? cause_desc[cause] : "Reserved"; 39 | } 40 | printf("cause = 0x%016lx (%s)\n", cause, desc); 41 | printf("epc = 0x%016lx\n", tf->epc); 42 | printf("tval = 0x%016lx\n", tf->tval); 43 | printf("ra = 0x%016lx\n", tf->regs.ra); 44 | printf("sp = 0x%016lx\n", tf->regs.sp); 45 | printf("gp = 0x%016lx\n", tf->regs.gp); 46 | printf("tp = 0x%016lx\n", tf->regs.tp); 47 | printf("t0 = 0x%016lx\n", tf->regs.t0); 48 | printf("t1 = 0x%016lx\n", tf->regs.t1); 49 | printf("t2 = 0x%016lx\n", tf->regs.t2); 50 | printf("s0 = 0x%016lx\n", tf->regs.s0); 51 | printf("s1 = 0x%016lx\n", tf->regs.s1); 52 | printf("a0 = 0x%016lx\n", tf->regs.a0); 53 | printf("a1 = 0x%016lx\n", tf->regs.a1); 54 | printf("a2 = 0x%016lx\n", tf->regs.a2); 55 | printf("a3 = 0x%016lx\n", tf->regs.a3); 56 | printf("a4 = 0x%016lx\n", tf->regs.a4); 57 | printf("a5 = 0x%016lx\n", tf->regs.a5); 58 | printf("a6 = 0x%016lx\n", tf->regs.a6); 59 | printf("a7 = 0x%016lx\n", tf->regs.a7); 60 | printf("s2 = 0x%016lx\n", tf->regs.s2); 61 | printf("s3 = 0x%016lx\n", tf->regs.s3); 62 | printf("s4 = 0x%016lx\n", tf->regs.s4); 63 | printf("s5 = 0x%016lx\n", tf->regs.s5); 64 | printf("s6 = 0x%016lx\n", tf->regs.s6); 65 | printf("s7 = 0x%016lx\n", tf->regs.s7); 66 | printf("s8 = 0x%016lx\n", tf->regs.s8); 67 | printf("s9 = 0x%016lx\n", tf->regs.s9); 68 | printf("s10 = 0x%016lx\n", tf->regs.s10); 69 | printf("s11 = 0x%016lx\n", tf->regs.s11); 70 | printf("t3 = 0x%016lx\n", tf->regs.t3); 71 | printf("t4 = 0x%016lx\n", tf->regs.t4); 72 | printf("t5 = 0x%016lx\n", tf->regs.t5); 73 | printf("t6 = 0x%016lx\n", tf->regs.t6); 74 | } 75 | 76 | void kernel_exception(trap_frame_t *tf) 77 | { 78 | printf("\n\nKERNEL EXCEPTION:\n"); 79 | print_trap_frame(tf); 80 | write_csr(mstatus, 0); 81 | while (true) asm volatile ("wfi"); 82 | } 83 | 84 | user_task_t user_task; 85 | 86 | void trap_handler(trap_frame_t *tf) 87 | { 88 | if (tf->cause == CAUSE_PF_FETCH || tf->cause == CAUSE_PF_LOAD || tf->cause == CAUSE_PF_STORE) 89 | { 90 | uintptr_t ad = PTE_A; 91 | if (tf->cause == CAUSE_PF_STORE) ad |= PTE_D; 92 | uintptr_t flags = PTE_V | PTE_U; 93 | if (tf->cause == CAUSE_PF_FETCH) flags |= PTE_X; 94 | if (tf->cause == CAUSE_PF_LOAD) flags |= PTE_R; 95 | if (tf->cause == CAUSE_PF_STORE) flags |= PTE_R | PTE_W; 96 | uintptr_t va = tf->tval; 97 | pte_t *pte = get_pte(p2v(read_csr(satp) << PGSHIFT), va); 98 | if (pte && (*pte & flags) == flags) 99 | { 100 | // if (ad & PTE_A) printf("setting a bit\n"); 101 | // if (ad & PTE_D) printf("setting d bit\n"); 102 | *pte |= ad; 103 | flush_tlb_one(va); 104 | return; 105 | } 106 | else 107 | { 108 | // Page fault. 109 | user_end_time = tf->cycle; 110 | user_task.exitcode = -11; 111 | user_task.error = -EPAGEFAULT; 112 | user_task.epc = tf->epc; 113 | user_task.eval = va; 114 | print_trap_frame(tf); 115 | arch_return_from_user(); 116 | } 117 | } 118 | 119 | if (tf->cause == CAUSE_ILLINST) 120 | { 121 | uintptr_t inst = tf->tval; 122 | if (!inst) 123 | { 124 | // TODO: read inst from memory 125 | } 126 | 127 | if ((inst & 0xffeff07f) == 0xc0002073) 128 | { 129 | // rdcycle and rdtime 130 | int reg = (inst >> 7) & 31; 131 | tf->x[reg] = read_csr(cycle); 132 | tf->epc += 4; 133 | return; 134 | } 135 | } 136 | 137 | user_end_time = tf->cycle; 138 | 139 | if (!(tf->cause & CAUSE_INT)) 140 | { 141 | if (tf->status & MSTATUS_MPP) 142 | { 143 | kernel_exception(tf); 144 | return; 145 | } 146 | 147 | if (tf->cause == CAUSE_ECALL_U) 148 | { 149 | // User process exits normally. 150 | user_task.exitcode = (int32_t)tf->regs.a0; 151 | user_task.error = 0; 152 | } 153 | else 154 | { 155 | // Something goes wrong. 156 | user_task.exitcode = -1; 157 | user_task.error = -EUSER; 158 | user_task.epc = tf->epc; 159 | user_task.eval = tf->tval; 160 | } 161 | arch_return_from_user(); 162 | } 163 | else 164 | { 165 | uintptr_t irq = (tf->cause << 1) >> 1; 166 | if (irq == IRQ_M_TIMER) 167 | { 168 | // Timed out? 169 | user_task.exitcode = -1; 170 | user_task.error = -ETIMEOUT; 171 | arch_return_from_user(); 172 | } 173 | 174 | printf("\n\nUNHANDLED INTERRUPT:\n"); 175 | print_trap_frame(tf); 176 | write_csr(mstatus, 0); 177 | while (true) asm volatile ("wfi"); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /arch/riscv/user.S: -------------------------------------------------------------------------------- 1 | .section .text.start, "ax", @progbits 2 | .global riscv_enter_user 3 | riscv_enter_user: 4 | li ra, 0 5 | li gp, 0 6 | li tp, 0 7 | # clear t0, t1, t2 later 8 | li s0, 0 9 | li s1, 0 10 | li a0, 0 11 | li a1, 0 12 | li a2, 0 13 | li a3, 0 14 | li a4, 0 15 | li a5, 0 16 | li a6, 0 17 | li a7, 0 18 | li s2, 0 19 | li s3, 0 20 | li s4, 0 21 | li s5, 0 22 | li s6, 0 23 | li s7, 0 24 | li s8, 0 25 | li s9, 0 26 | li s10, 0 27 | li s11, 0 28 | li t3, 0 29 | li t4, 0 30 | li t5, 0 31 | li t6, 0 32 | 33 | # Flush D-cache. 34 | la t0, zeros 35 | la t1, zeros_end 36 | 1: 37 | ld t2, (t0) 38 | addi t0, t0, 8 39 | bne t0, t1, 1b 40 | 41 | # 128KiB of nop to flush I-cache. 42 | .rept 0x10000 43 | nop 44 | .endr 45 | 46 | .balign 64 47 | fence 48 | sfence.vma 49 | fence.i 50 | 51 | rdcycle t0 52 | sd t0, (t1) 53 | li t0, 0 54 | li t1, 0 55 | mret 56 | 57 | .section .bss 58 | zeros: 59 | .space 0x100000 60 | zeros_end: 61 | .global user_start_time 62 | user_start_time: 63 | .space 0x8 64 | .global user_end_time 65 | user_end_time: 66 | .space 0x8 67 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | import shutil 4 | import socket 5 | import struct 6 | 7 | 8 | CLOCK_FREQ = 125000000.0 9 | JUDGE_RESP_ACK = 1 10 | JUDGE_RESP_RESULT = 2 11 | RETRY_LIMIT = 3 12 | SERVER = ('192.168.1.56', 1234) 13 | TFTP_ROOT = 'tftp_root' 14 | PACKET_LOSS = 0.0 15 | 16 | 17 | def send_packet(sock, pkt): 18 | if random.random() < PACKET_LOSS: 19 | return 0 20 | return sock.send(pkt) 21 | 22 | 23 | def recv_packet(sock, length): 24 | while True: 25 | pkt = sock.recv(length) 26 | if random.random() < PACKET_LOSS: 27 | continue 28 | else: 29 | return pkt 30 | 31 | 32 | def do_judge(filename, stdin_file, time_limit, mem_limit): 33 | time_limit_cycles = int(time_limit * CLOCK_FREQ) 34 | 35 | shutil.copyfile(filename, TFTP_ROOT + '/elf') 36 | shutil.copyfile(stdin_file, TFTP_ROOT + '/stdin') 37 | 38 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 39 | sock.connect(SERVER) 40 | 41 | for i in range(RETRY_LIMIT): 42 | if i > 0: 43 | print('Retry', i, '...') 44 | seq = random.randint(0, (1 << 64) - 1) 45 | req = struct.pack('op == DHCP_OP_REPLY 22 | && dhcp->htype == DHCP_HTYPE 23 | && dhcp->hlen == DHCP_HLEN 24 | && dhcp->xid == xid 25 | && dhcp->magic_cookie == DHCP_MAGIC_COOKIE 26 | && dhcp->options[0] == DHCP_OPTION_MESSAGE_TYPE 27 | && dhcp->options[1] == 0x01)) 28 | { 29 | return -EBADPKT; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | __attribute__ ((aligned (16))) uint8_t dhcp_stack[1024]; 36 | static struct task dhcp_task; 37 | static void dhcp_entry() 38 | { 39 | if (get_ip_ready() && !ipv4_is_unspecified(get_ifinfo_ipv4())) 40 | { 41 | exit(); 42 | } 43 | 44 | const uint64_t mac = get_ifinfo_mac(); 45 | 46 | uint32_t xid = rdcycle() ^ 0xaa55ccdd; 47 | dhcp_req->op = DHCP_OP_REQUEST; 48 | dhcp_req->htype = DHCP_HTYPE; 49 | dhcp_req->hlen = DHCP_HLEN; 50 | dhcp_req->hops = 0; 51 | dhcp_req->xid = xid; 52 | dhcp_req->secs = 0; 53 | dhcp_req->flags = 0; 54 | dhcp_req->ciaddr = ipv4_unspecified(); 55 | dhcp_req->yiaddr = ipv4_unspecified(); 56 | dhcp_req->siaddr = ipv4_unspecified(); 57 | dhcp_req->giaddr = ipv4_unspecified(); 58 | memcpy(dhcp_req->chaddr, &mac, DHCP_HLEN); 59 | memset(dhcp_req->reserved, 0, sizeof(dhcp_req->reserved)); 60 | dhcp_req->magic_cookie = DHCP_MAGIC_COOKIE; 61 | 62 | dhcp_req->options[0] = DHCP_OPTION_MESSAGE_TYPE; 63 | dhcp_req->options[1] = 0x01; 64 | dhcp_req->options[2] = DHCP_MESSAGE_TYPE_DISCOVER; 65 | dhcp_req->options[3] = DHCP_OPTION_END; 66 | 67 | printf("DHCP Client: Waiting for DHCP server... "); 68 | 69 | ipv6_addr_t saddr; 70 | uint16_t sport; 71 | while (1) 72 | { 73 | send_udp(DHCP_CLIENT_PORT, ipv4_compatible(ipv4_broadcast()), DHCP_SERVER_PORT, 74 | dhcp_req, sizeof(dhcp_packet_t) + 4); 75 | size_t ret = recv_udp(DHCP_CLIENT_PORT, &saddr, &sport, dhcp_buff, sizeof(dhcp_buff), 76 | ipv6_unspecified(), DHCP_SERVER_PORT, 3 * CLOCK_FREQ); 77 | if (ret != 0) 78 | { 79 | if (dhcp_check(dhcp_packet, ret, xid) == 0 80 | && dhcp_packet->options[2] == DHCP_MESSAGE_TYPE_OFFER) 81 | { 82 | break; 83 | } 84 | } 85 | else 86 | { 87 | printf("timed out, retrying... "); 88 | } 89 | } 90 | 91 | ipv4_addr_t dhcp_server_addr = extract_ipv4(saddr); 92 | ipv4_addr_t ip = dhcp_packet->yiaddr; 93 | printf("get ip "); 94 | print_ipv4(ip.u8); 95 | printf(" from "); 96 | print_ipv4(dhcp_server_addr.u8); 97 | printf("\n"); 98 | set_ifinfo_ipv4(ip); 99 | set_dhcp_server_ip(ipv4_compatible(dhcp_server_addr)); 100 | 101 | dhcp_req->options[2] = DHCP_MESSAGE_TYPE_REQUEST; 102 | dhcp_req->options[3] = DHCP_OPTION_REQ_ADDR; 103 | dhcp_req->options[4] = 0x04; 104 | memcpy(&dhcp_req->options[5], ip.u8, 4); 105 | dhcp_req->options[9] = DHCP_OPTION_END; 106 | 107 | printf("DHCP Client: Sending request... "); 108 | 109 | while (1) 110 | { 111 | send_udp(DHCP_CLIENT_PORT, ipv4_compatible(dhcp_server_addr), DHCP_SERVER_PORT, 112 | dhcp_req, sizeof(dhcp_packet_t) + 10); 113 | ipv6_addr_t saddr; 114 | uint16_t sport; 115 | size_t ret = recv_udp(DHCP_CLIENT_PORT, &saddr, &sport, dhcp_buff, sizeof(dhcp_buff), 116 | ipv4_compatible(dhcp_server_addr), DHCP_SERVER_PORT, CLOCK_FREQ / 4); 117 | if (ret != 0) 118 | { 119 | if (dhcp_check(dhcp_packet, ret, xid) == 0 120 | && dhcp_packet->options[2] == DHCP_MESSAGE_TYPE_ACK) 121 | { 122 | break; 123 | } 124 | } 125 | else 126 | { 127 | printf("timed out, retrying... "); 128 | } 129 | } 130 | 131 | ip = dhcp_packet->yiaddr; 132 | printf("get ip "); 133 | print_ipv4(ip.u8); 134 | printf(" from "); 135 | print_ipv4(dhcp_server_addr.u8); 136 | printf("\n"); 137 | set_ifinfo_ipv4(ip); 138 | set_ip_ready(true); 139 | exit(); 140 | } 141 | 142 | void init_dhcp() 143 | { 144 | create_task(&dhcp_task, (uintptr_t)dhcp_stack + sizeof(dhcp_stack) + STACK_OFFSET, dhcp_entry); 145 | } 146 | -------------------------------------------------------------------------------- /driver/axiethernet.c: -------------------------------------------------------------------------------- 1 | #include "driver/axiethernet.h" 2 | #include "eth.h" 3 | #include "arch/asm.h" 4 | #include "arch/mmu.h" 5 | #include "stdio.h" 6 | 7 | void *DMA_BASE; 8 | void *ETH_BASE; 9 | 10 | static bool send_not_begin = true; 11 | 12 | __attribute__ ((aligned (0x40))) static dma_desc_t rx_desc; 13 | __attribute__ ((aligned (0x40))) static dma_desc_t tx_desc; 14 | 15 | static void reset(); 16 | 17 | void init_axiethernet() 18 | { 19 | eth_ops = &axiethernet_ops; 20 | 21 | send_not_begin = true; 22 | 23 | reset(); 24 | 25 | puts("Initializing ethernet... "); 26 | 27 | rx_desc.next = v2p(&rx_desc); 28 | tx_desc.next = v2p(&tx_desc); 29 | 30 | uintptr_t ptr = v2p(&rx_desc); 31 | DMA_S2MM_CURDESC = (uint32_t)(ptr & 0xffffffff); 32 | DMA_S2MM_CURDESC_MSB = (uint32_t)((ptr >> 32) & 0xffffffff); 33 | DMA_S2MM_DMACR = 0; 34 | DMA_S2MM_DMACR |= DMA_DMACR_RS; 35 | while (DMA_S2MM_DMASR & DMA_DMASR_HALTED); 36 | 37 | ptr = v2p(&tx_desc); 38 | DMA_MM2S_CURDESC = (uint32_t)(ptr & 0xffffffff); 39 | DMA_MM2S_CURDESC_MSB = (uint32_t)((ptr >> 32) & 0xffffffff); 40 | DMA_MM2S_DMACR = 0; 41 | DMA_MM2S_DMACR |= DMA_DMACR_RS; 42 | while (DMA_MM2S_DMASR & DMA_DMASR_HALTED); 43 | 44 | puts("OK\n"); 45 | } 46 | 47 | static void reset() 48 | { 49 | puts("Resetting ethernet... "); 50 | 51 | ETH_RCW1R |= ETH_RCW1R_RX; 52 | ETH_TCR |= ETH_TCR_TX; 53 | 54 | ETH_RCW1R |= ETH_RCW1R_RST; 55 | while (ETH_RCW1R & ETH_RCW1R_RST); 56 | ETH_TCR |= ETH_TCR_RST; 57 | while (ETH_TCR & ETH_TCR_RST); 58 | 59 | DMA_S2MM_DMACR = 0; 60 | DMA_S2MM_DMACR |= DMA_DMACR_RESET; 61 | while (DMA_S2MM_DMACR & DMA_DMACR_RESET); 62 | DMA_MM2S_DMACR = 0; 63 | DMA_MM2S_DMACR |= DMA_DMACR_RESET; 64 | while (DMA_MM2S_DMACR & DMA_DMACR_RESET); 65 | 66 | puts("OK\n"); 67 | } 68 | 69 | static void begin_dma_recv(void *data, uint32_t len) 70 | { 71 | rx_desc.buff = v2p(data); 72 | rx_desc.control = len & DMA_DESC_CONTROL_LENGTH_MASK; 73 | rx_desc.status = 0; 74 | 75 | uintptr_t ptr = v2p(&rx_desc); 76 | DMA_S2MM_TAILDESC = (uint32_t)(ptr & 0xffffffff); 77 | fence(); 78 | DMA_S2MM_TAILDESC_MSB = (uint32_t)((ptr >> 32) & 0xffffffff); 79 | } 80 | 81 | static void begin_dma_send(void *data, uint32_t len) 82 | { 83 | send_not_begin = false; 84 | tx_desc.buff = v2p(data); 85 | tx_desc.control = (len & DMA_DESC_CONTROL_LENGTH_MASK) 86 | | DMA_DESC_CONTROL_TXSOF | DMA_DESC_CONTROL_TXEOF; 87 | tx_desc.status = 0; 88 | 89 | uintptr_t ptr = v2p(&tx_desc); 90 | DMA_MM2S_TAILDESC = (uint32_t)(ptr & 0xffffffff); 91 | fence(); 92 | DMA_MM2S_TAILDESC_MSB = (uint32_t)((ptr >> 32) & 0xffffffff); 93 | } 94 | 95 | static bool poll_recv() 96 | { 97 | return DMA_S2MM_DMASR & DMA_DMASR_IDLE; 98 | } 99 | 100 | static bool poll_send() 101 | { 102 | return send_not_begin || (DMA_MM2S_DMASR & DMA_DMASR_IDLE); 103 | } 104 | 105 | static uint16_t last_recv_len() 106 | { 107 | return rx_desc.status & DMA_DESC_STATUS_LENGTH_MASK; 108 | } 109 | 110 | const eth_ops_t axiethernet_ops = 111 | { 112 | .reset = reset, 113 | .begin_dma_recv = begin_dma_recv, 114 | .begin_dma_send = begin_dma_send, 115 | .poll_recv = poll_recv, 116 | .poll_send = poll_send, 117 | .last_recv_len = last_recv_len 118 | }; 119 | -------------------------------------------------------------------------------- /driver/clint.c: -------------------------------------------------------------------------------- 1 | #include "driver/clint.h" 2 | #include "arch/asm.h" 3 | #include "hwtimer.h" 4 | 5 | void *CLINT_BASE; 6 | 7 | void init_clint() 8 | { 9 | hwtimer_ops = &clint_hwtimer_ops; 10 | } 11 | 12 | static void clear() 13 | { 14 | } 15 | 16 | static uintptr_t get() 17 | { 18 | return CLINT_TIME; 19 | } 20 | 21 | static uintptr_t get_freq() 22 | { 23 | return CLOCK_FREQ; // FIXME 24 | } 25 | 26 | static void set_oneshot(uintptr_t t) 27 | { 28 | CLINT_TIMECMP = CLINT_TIME + t; 29 | } 30 | 31 | const hwtimer_ops_t clint_hwtimer_ops = 32 | { 33 | .clear = clear, 34 | .get = get, 35 | .get_freq = get_freq, 36 | .set_oneshot = set_oneshot 37 | }; 38 | -------------------------------------------------------------------------------- /driver/dummyeth.c: -------------------------------------------------------------------------------- 1 | #include "driver/dummyeth.h" 2 | #include "eth.h" 3 | #include "stdio.h" 4 | 5 | void init_dummyeth() 6 | { 7 | eth_ops = &dummyeth_ops; 8 | puts("Using dummy ethernet...\n"); 9 | } 10 | 11 | static void reset() 12 | { 13 | 14 | } 15 | 16 | static void sendrecv(void *data, uint32_t len) 17 | { 18 | 19 | } 20 | 21 | static bool poll_sendrecv() 22 | { 23 | return false; 24 | } 25 | 26 | static uint16_t last_recv_len() 27 | { 28 | return 0; 29 | } 30 | 31 | const eth_ops_t dummyeth_ops = 32 | { 33 | .reset = reset, 34 | .begin_dma_recv = sendrecv, 35 | .begin_dma_send = sendrecv, 36 | .poll_recv = poll_sendrecv, 37 | .poll_send = poll_sendrecv, 38 | .last_recv_len = last_recv_len 39 | }; 40 | -------------------------------------------------------------------------------- /driver/include/axiethernet.h: -------------------------------------------------------------------------------- 1 | #ifndef _DRIVER_AXIETHERNET_H_ 2 | #define _DRIVER_AXIETHERNET_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | #include "eth.h" 7 | 8 | extern void *DMA_BASE; 9 | extern void *ETH_BASE; 10 | 11 | #define DMA_MM2S_DMACR (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x0)) 12 | #define DMA_MM2S_DMASR (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x4)) 13 | #define DMA_MM2S_CURDESC (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x8)) 14 | #define DMA_MM2S_CURDESC_MSB (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0xc)) 15 | #define DMA_MM2S_TAILDESC (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x10)) 16 | #define DMA_MM2S_TAILDESC_MSB (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x14)) 17 | #define DMA_MM2S_SA (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x18)) 18 | #define DMA_MM2S_SA_MSB (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x1c)) 19 | #define DMA_MM2S_LENGTH (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x28)) 20 | #define DMA_S2MM_DMACR (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x30)) 21 | #define DMA_S2MM_DMASR (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x34)) 22 | #define DMA_S2MM_CURDESC (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x38)) 23 | #define DMA_S2MM_CURDESC_MSB (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x3c)) 24 | #define DMA_S2MM_TAILDESC (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x40)) 25 | #define DMA_S2MM_TAILDESC_MSB (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x44)) 26 | #define DMA_S2MM_DA (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x48)) 27 | #define DMA_S2MM_DA_MSB (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x4c)) 28 | #define DMA_S2MM_LENGTH (*(volatile uint32_t *)((uintptr_t)DMA_BASE + 0x58)) 29 | 30 | #define DMA_DMACR_RS 1u 31 | #define DMA_DMACR_RESET 4u 32 | #define DMA_DMASR_HALTED 1u 33 | #define DMA_DMASR_IDLE 2u 34 | 35 | #define DMA_DESC_CONTROL_LENGTH_MASK ((1u << 26) - 1) 36 | #define DMA_DESC_CONTROL_TXSOF (1u << 27) 37 | #define DMA_DESC_CONTROL_TXEOF (1u << 26) 38 | #define DMA_DESC_STATUS_LENGTH_MASK ((1u << 26) - 1) 39 | #define DMA_DESC_STATUS_ERROR_MASK 0x70000000u 40 | #define DMA_DESC_STATUS_COMPLETE 0x80000000u 41 | 42 | typedef struct dma_desc dma_desc_t; 43 | struct dma_desc 44 | { 45 | uintptr_t next; 46 | uintptr_t buff; 47 | uint64_t reserved; 48 | uint32_t control; 49 | uint32_t status; 50 | uint32_t app[5]; 51 | uint32_t reserved1[3]; 52 | }; 53 | 54 | #define ETH_RCW1R (*(volatile uint32_t *)((uintptr_t)ETH_BASE + 0x404)) 55 | #define ETH_TCR (*(volatile uint32_t *)((uintptr_t)ETH_BASE + 0x408)) 56 | 57 | #define ETH_RCW1R_RST 0x80000000u 58 | #define ETH_RCW1R_RX 0x10000000u 59 | #define ETH_TCR_RST 0x80000000u 60 | #define ETH_TCR_TX 0x10000000u 61 | 62 | extern const eth_ops_t axiethernet_ops; 63 | void init_axiethernet(); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /driver/include/clint.h: -------------------------------------------------------------------------------- 1 | #ifndef _DRIVER_CLINT_H_ 2 | #define _DRIVER_CLINT_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | #include "hwtimer.h" 7 | 8 | extern void *CLINT_BASE; 9 | #define CLINT_TIME (*(volatile uint64_t *)((uintptr_t)CLINT_BASE + 0xbff8)) 10 | #define CLINT_TIMECMP (*(volatile uint64_t *)((uintptr_t)CLINT_BASE + 0x4000)) 11 | 12 | extern const hwtimer_ops_t clint_hwtimer_ops; 13 | void init_clint(); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /driver/include/dummyeth.h: -------------------------------------------------------------------------------- 1 | #ifndef _DRIVER_DUMMYETH_H_ 2 | #define _DRIVER_DUMMYETH_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | #include "eth.h" 7 | 8 | extern const eth_ops_t dummyeth_ops; 9 | void init_dummyeth(); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /driver/include/uart16550.h: -------------------------------------------------------------------------------- 1 | #ifndef _DRIVER_UART16550_H_ 2 | #define _DRIVER_UART16550_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | #include "tty.h" 7 | 8 | extern volatile uint8_t *UART16550_BASE; 9 | 10 | extern const tty_ops_t uart16550_ops; 11 | void init_uart16550(); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /driver/include/uartlite.h: -------------------------------------------------------------------------------- 1 | #ifndef _DRIVER_UARTLITE_H_ 2 | #define _DRIVER_UARTLITE_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | #include "tty.h" 7 | 8 | extern void *UARTLITE_BASE; 9 | #define UARTLITE_RX (*(volatile uint32_t *)((uintptr_t)UARTLITE_BASE + 0x0)) 10 | #define UARTLITE_TX (*(volatile uint32_t *)((uintptr_t)UARTLITE_BASE + 0x4)) 11 | #define UARTLITE_STAT (*(volatile uint32_t *)((uintptr_t)UARTLITE_BASE + 0x8)) 12 | #define UARTLITE_CTRL (*(volatile uint32_t *)((uintptr_t)UARTLITE_BASE + 0xc)) 13 | 14 | #define UARTLITE_TX_FIFO_FULL 8 15 | #define UARTLITE_RX_FIFO_VALID 1 16 | 17 | extern const tty_ops_t uartlite_ops; 18 | void init_uartlite(); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /driver/uart16550.c: -------------------------------------------------------------------------------- 1 | #include "driver/uart16550.h" 2 | #include "tty.h" 3 | 4 | volatile uint8_t *UART16550_BASE; 5 | // some devices require a shifted register index 6 | // (e.g. 32 bit registers instead of 8 bit registers) 7 | static uint32_t uart16550_reg_shift; 8 | static uint32_t uart16550_clock = 0x384000; // a "common" base clock 9 | 10 | #define UART_REG_QUEUE 0 // rx/tx fifo data 11 | #define UART_REG_DLL 0 // divisor latch (LSB) 12 | #define UART_REG_IER 1 // interrupt enable register 13 | #define UART_REG_DLM 1 // divisor latch (MSB) 14 | #define UART_REG_FCR 2 // fifo control register 15 | #define UART_REG_LCR 3 // line control register 16 | #define UART_REG_MCR 4 // modem control register 17 | #define UART_REG_LSR 5 // line status register 18 | #define UART_REG_MSR 6 // modem status register 19 | #define UART_REG_SCR 7 // scratch register 20 | #define UART_REG_STATUS_RX 0x01 21 | #define UART_REG_STATUS_TX 0x20 22 | 23 | // We cannot use the word DEFAULT for a parameter that cannot be overridden due to -Werror 24 | #ifndef UART_DEFAULT_BAUD 25 | #define UART_DEFAULT_BAUD 38400 26 | #endif 27 | 28 | void init_uart16550() 29 | { 30 | tty_ops = &uart16550_ops; 31 | 32 | uint32_t divisor = uart16550_clock / (16 * UART_DEFAULT_BAUD); 33 | 34 | uart16550_reg_shift = 0; 35 | // http://wiki.osdev.org/Serial_Ports 36 | UART16550_BASE[UART_REG_IER << uart16550_reg_shift] = 0x00; // Disable all interrupts 37 | UART16550_BASE[UART_REG_LCR << uart16550_reg_shift] = 0x80; // Enable DLAB (set baud rate divisor) 38 | UART16550_BASE[UART_REG_DLL << uart16550_reg_shift] = (uint8_t)divisor; // Set divisor (lo byte) 39 | UART16550_BASE[UART_REG_DLM << uart16550_reg_shift] = (uint8_t)(divisor >> 8); // (hi byte) 40 | UART16550_BASE[UART_REG_LCR << uart16550_reg_shift] = 0x03; // 8 bits, no parity, one stop bit 41 | UART16550_BASE[UART_REG_FCR << uart16550_reg_shift] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold 42 | } 43 | 44 | static bool poll_recv() 45 | { 46 | return UART16550_BASE[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_RX; 47 | } 48 | 49 | static bool poll_send() 50 | { 51 | return UART16550_BASE[UART_REG_LSR << uart16550_reg_shift] & UART_REG_STATUS_TX; 52 | } 53 | 54 | static void send(uint8_t c) 55 | { 56 | if (c == '\n') 57 | { 58 | send('\r'); 59 | } 60 | while (!poll_send()); 61 | UART16550_BASE[UART_REG_QUEUE << uart16550_reg_shift] = c; 62 | } 63 | 64 | static uint8_t recv() 65 | { 66 | while (!poll_recv()); 67 | return UART16550_BASE[UART_REG_QUEUE << uart16550_reg_shift]; 68 | } 69 | 70 | const tty_ops_t uart16550_ops = 71 | { 72 | .putchar = send, 73 | .getchar = recv, 74 | .poll_recv = poll_recv, 75 | .poll_send = poll_send 76 | }; 77 | -------------------------------------------------------------------------------- /driver/uartlite.c: -------------------------------------------------------------------------------- 1 | #include "driver/uartlite.h" 2 | #include "tty.h" 3 | 4 | void *UARTLITE_BASE; 5 | 6 | void init_uartlite() 7 | { 8 | tty_ops = &uartlite_ops; 9 | } 10 | 11 | static bool poll_recv() 12 | { 13 | return UARTLITE_STAT & UARTLITE_RX_FIFO_VALID; 14 | } 15 | 16 | static bool poll_send() 17 | { 18 | return !(UARTLITE_STAT & UARTLITE_TX_FIFO_FULL); 19 | } 20 | 21 | static void send(uint8_t c) 22 | { 23 | if (c == '\n') 24 | { 25 | send('\r'); 26 | } 27 | while (!poll_send()); 28 | UARTLITE_TX = c; 29 | } 30 | 31 | static uint8_t recv() 32 | { 33 | while (!poll_recv()); 34 | return UARTLITE_RX; 35 | } 36 | 37 | const tty_ops_t uartlite_ops = 38 | { 39 | .putchar = send, 40 | .getchar = recv, 41 | .poll_recv = poll_recv, 42 | .poll_send = poll_send 43 | }; 44 | -------------------------------------------------------------------------------- /elf.c: -------------------------------------------------------------------------------- 1 | #include "elf.h" 2 | #include "error.h" 3 | #include "vm.h" 4 | #include "stdint.h" 5 | #include "stdio.h" 6 | #include "string.h" 7 | 8 | int load_elf(pte_t *pt, void *elf, size_t len, uintptr_t *entry_va) 9 | { 10 | uint8_t *u8 = (uint8_t *)elf; 11 | 12 | if (len < sizeof(Elf64_Ehdr)) 13 | { 14 | puts("Loader: ELF file is too small (Elf64_Ehdr).\n"); 15 | return -EOOB; 16 | } 17 | 18 | Elf64_Ehdr *elf_header = (Elf64_Ehdr *)u8; 19 | 20 | if (memcmp(elf_header->e_ident, ELFMAG, SELFMAG)) 21 | { 22 | printf("Loader: Not an ELF file (0x%08x).\n", *(uint64_t *)elf_header->e_ident); 23 | return -EBADPKT; 24 | } 25 | 26 | if (elf_header->e_type != ET_EXEC) 27 | { 28 | puts("Loader: Not an executable ELF file.\n"); 29 | return -EBADPKT; 30 | } 31 | 32 | uintptr_t entry = elf_header->e_entry; 33 | if (entry_va) *entry_va = entry; 34 | 35 | printf("Loader: ELF Entry is at 0x%016lx.\n", entry); 36 | 37 | if (!elf_header->e_phnum) 38 | { 39 | puts("Loader: No program header.\n"); 40 | return -EBADPKT; 41 | } 42 | 43 | if (elf_header->e_phentsize != sizeof(Elf64_Phdr)) 44 | { 45 | puts("Loader: elf_header->e_phentsize != sizeof(Elf64_Phdr)\n"); 46 | return -ENOIMPL; 47 | } 48 | 49 | size_t phsize = elf_header->e_phnum * sizeof(Elf64_Phdr); 50 | if (!(elf_header->e_phoff <= elf_header->e_phoff + phsize && elf_header->e_phoff + phsize <= len)) 51 | { 52 | puts("Loader: ELF file is too small (Elf64_Phdr).\n"); 53 | return -EOOB; 54 | } 55 | 56 | Elf64_Phdr *phdr = (Elf64_Phdr *)(u8 + elf_header->e_phoff); 57 | 58 | puts("+-----------------------------------------------------------------------------------------------------+\n" 59 | "| PROGRAM HEADERS |\n" 60 | "+-----------------------------------------------------------------------------------------------------+\n" 61 | " TYPE OFFSET VIRT ADDR PHY ADDR FILE SIZE MEM SIZE \n"); 62 | for (int i = 0; i < elf_header->e_phnum; ++i) 63 | { 64 | printf("%02d: ", i); 65 | if (phdr[i].p_type == PT_LOAD) 66 | { 67 | printf("LOAD"); 68 | } 69 | else if (phdr[i].p_type == PT_TLS) 70 | { 71 | printf("TLS "); 72 | } 73 | else 74 | { 75 | printf("????"); 76 | } 77 | printf(" 0x%016lx 0x%016lx 0x%016lx 0x%016lx 0x%016lx\n", 78 | phdr[i].p_offset, phdr[i].p_vaddr, 79 | phdr[i].p_paddr, phdr[i].p_filesz, phdr[i].p_memsz); 80 | } 81 | 82 | for (int i = 0; i < elf_header->e_phnum; ++i) 83 | { 84 | if (phdr[i].p_type == PT_LOAD) 85 | { 86 | printf("Loader: Loading %02d... ", i); 87 | 88 | if (phdr[i].p_filesz && !(phdr[i].p_offset <= phdr[i].p_offset + phdr[i].p_filesz 89 | && phdr[i].p_offset + phdr[i].p_filesz <= len)) 90 | { 91 | puts("ELF file is too small.\n"); 92 | return -EOOB; 93 | } 94 | if (!(USER_BOTTOM <= phdr[i].p_vaddr 95 | && phdr[i].p_vaddr <= phdr[i].p_vaddr + phdr[i].p_memsz 96 | && phdr[i].p_vaddr + phdr[i].p_memsz <= USER_TOP)) 97 | { 98 | puts("virtual address out of bound.\n"); 99 | return -EOOB; 100 | } 101 | if (phdr[i].p_filesz > phdr[i].p_memsz) 102 | { 103 | puts("Bad ELF file (p_filesz > p_memsz).\n"); 104 | return -EBADPKT; 105 | } 106 | 107 | uintptr_t flags = VM_U | VM_A; 108 | if (phdr[i].p_flags & PF_R) flags |= VM_R; 109 | if (phdr[i].p_flags & PF_W) flags |= VM_W | VM_D; 110 | if (phdr[i].p_flags & PF_X) flags |= VM_X; 111 | int ret = map_and_copy(pt, phdr[i].p_vaddr, flags, 112 | u8 + phdr[i].p_offset, phdr[i].p_filesz); 113 | if (ret < 0) 114 | { 115 | puts("out of memory.\n"); 116 | return ret; 117 | } 118 | if (phdr[i].p_filesz < phdr[i].p_memsz) 119 | { 120 | puts("init .bss... "); 121 | size_t file_end = PGALIGN_FLOOR(phdr[i].p_vaddr + phdr[i].p_filesz), 122 | mem_end = PGALIGN(phdr[i].p_vaddr + phdr[i].p_memsz); 123 | for (size_t pg = file_end; pg < mem_end; pg += PGSIZE) 124 | { 125 | if (!map_page(pt, pg, flags)) 126 | { 127 | puts("out of memory.\n"); 128 | return -EOOM; 129 | } 130 | } 131 | } 132 | puts("done.\n"); 133 | } 134 | } 135 | 136 | puts("Loader: done.\n"); 137 | return 0; 138 | } 139 | 140 | int parse_elf_phdr(void *elf, size_t len, 141 | uintptr_t *phdr_offset, uintptr_t *phent, uintptr_t *phnum) 142 | { 143 | // Assuming ELF data is checked by load_elf. 144 | 145 | uint8_t *u8 = (uint8_t *)elf; 146 | Elf64_Ehdr *elf_header = (Elf64_Ehdr *)u8; 147 | *phdr_offset = elf_header->e_phoff; 148 | *phent = elf_header->e_phentsize; 149 | *phnum = elf_header->e_phnum; 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /eth.c: -------------------------------------------------------------------------------- 1 | #include "eth.h" 2 | #include "arch/asm.h" 3 | #include "stdio.h" 4 | 5 | const eth_ops_t *eth_ops; 6 | 7 | void init_eth() 8 | { 9 | } 10 | 11 | void eth_reset() 12 | { 13 | puts("Resetting ethernet... "); 14 | eth_ops->reset(); 15 | puts("OK\n"); 16 | } 17 | 18 | void eth_begin_dma_recv(void *data, uint32_t len) 19 | { 20 | eth_ops->begin_dma_recv(data, len); 21 | } 22 | 23 | void eth_begin_dma_send(void *data, uint32_t len) 24 | { 25 | eth_ops->begin_dma_send(data, len); 26 | } 27 | -------------------------------------------------------------------------------- /hwtimer.c: -------------------------------------------------------------------------------- 1 | #include "hwtimer.h" 2 | 3 | const hwtimer_ops_t *hwtimer_ops; 4 | 5 | void init_hwtimer() 6 | { 7 | hwtimer_ops->clear(); 8 | } 9 | -------------------------------------------------------------------------------- /include/arch.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_H_ 2 | #define _ARCH_H_ 3 | 4 | #include "stdint.h" 5 | 6 | void init_arch(); 7 | void init_arch_dev(int hartid, void *dtb); 8 | 9 | void arch_call_user(void *pt, uintptr_t pc, uintptr_t sp); 10 | 11 | extern uintptr_t user_start_time; 12 | extern uintptr_t user_end_time; 13 | 14 | #include "task.h" 15 | extern user_task_t user_task; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/dhcp.h: -------------------------------------------------------------------------------- 1 | #ifndef _DHCP_H_ 2 | #define _DHCP_H_ 3 | 4 | #include "stdint.h" 5 | #include "ip.h" 6 | 7 | #define DHCP_SERVER_PORT 67 8 | #define DHCP_CLIENT_PORT 68 9 | 10 | #define DHCP_OP_REQUEST 1 11 | #define DHCP_OP_REPLY 2 12 | #define DHCP_HTYPE 1 13 | #define DHCP_HLEN 6 14 | #define DHCP_MAGIC_COOKIE 0x63538263 15 | 16 | #define DHCP_OPTION_MESSAGE_TYPE 0x35 17 | #define DHCP_OPTION_REQ_ADDR 0x32 18 | #define DHCP_OPTION_END 0xff 19 | 20 | #define DHCP_MESSAGE_TYPE_DISCOVER 0x01 21 | #define DHCP_MESSAGE_TYPE_OFFER 0x02 22 | #define DHCP_MESSAGE_TYPE_REQUEST 0x03 23 | #define DHCP_MESSAGE_TYPE_ACK 0x05 24 | 25 | typedef struct 26 | { 27 | uint8_t op; 28 | uint8_t htype; 29 | uint8_t hlen; 30 | uint8_t hops; 31 | uint32_t xid; 32 | uint16_t secs; 33 | uint16_t flags; 34 | ipv4_addr_t ciaddr; 35 | ipv4_addr_t yiaddr; 36 | ipv4_addr_t siaddr; 37 | ipv4_addr_t giaddr; 38 | uint8_t chaddr[DHCP_HLEN]; 39 | uint8_t reserved[(16 - DHCP_HLEN) + 192]; 40 | uint32_t magic_cookie; 41 | uint8_t options[]; 42 | } dhcp_packet_t; 43 | 44 | void init_dhcp(); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /include/elf.h: -------------------------------------------------------------------------------- 1 | /* This file defines standard ELF types, structures, and macros. 2 | Copyright (C) 1995-2016 Free Software Foundation, Inc. 3 | This file is part of the GNU C Library. 4 | The GNU C Library is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU Lesser General Public 6 | License as published by the Free Software Foundation; either 7 | version 2.1 of the License, or (at your option) any later version. 8 | The GNU C Library is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | Lesser General Public License for more details. 12 | You should have received a copy of the GNU Lesser General Public 13 | License along with the GNU C Library; if not, see 14 | . */ 15 | 16 | #ifndef _ELF_H_ 17 | #define _ELF_H_ 18 | 19 | #include "stdint.h" 20 | 21 | /* Type for a 16-bit quantity. */ 22 | typedef uint16_t Elf32_Half; 23 | typedef uint16_t Elf64_Half; 24 | 25 | /* Types for signed and unsigned 32-bit quantities. */ 26 | typedef uint32_t Elf32_Word; 27 | typedef int32_t Elf32_Sword; 28 | typedef uint32_t Elf64_Word; 29 | typedef int32_t Elf64_Sword; 30 | 31 | /* Types for signed and unsigned 64-bit quantities. */ 32 | typedef uint64_t Elf32_Xword; 33 | typedef int64_t Elf32_Sxword; 34 | typedef uint64_t Elf64_Xword; 35 | typedef int64_t Elf64_Sxword; 36 | 37 | /* Type of addresses. */ 38 | typedef uint32_t Elf32_Addr; 39 | typedef uint64_t Elf64_Addr; 40 | 41 | /* Type of file offsets. */ 42 | typedef uint32_t Elf32_Off; 43 | typedef uint64_t Elf64_Off; 44 | 45 | /* Type for section indices, which are 16-bit quantities. */ 46 | typedef uint16_t Elf32_Section; 47 | typedef uint16_t Elf64_Section; 48 | 49 | /* Type for version symbol information. */ 50 | typedef Elf32_Half Elf32_Versym; 51 | typedef Elf64_Half Elf64_Versym; 52 | 53 | /* The ELF file header. This appears at the start of every ELF file. */ 54 | 55 | #define EI_NIDENT (16) 56 | 57 | typedef struct 58 | { 59 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ 60 | Elf32_Half e_type; /* Object file type */ 61 | Elf32_Half e_machine; /* Architecture */ 62 | Elf32_Word e_version; /* Object file version */ 63 | Elf32_Addr e_entry; /* Entry point virtual address */ 64 | Elf32_Off e_phoff; /* Program header table file offset */ 65 | Elf32_Off e_shoff; /* Section header table file offset */ 66 | Elf32_Word e_flags; /* Processor-specific flags */ 67 | Elf32_Half e_ehsize; /* ELF header size in bytes */ 68 | Elf32_Half e_phentsize; /* Program header table entry size */ 69 | Elf32_Half e_phnum; /* Program header table entry count */ 70 | Elf32_Half e_shentsize; /* Section header table entry size */ 71 | Elf32_Half e_shnum; /* Section header table entry count */ 72 | Elf32_Half e_shstrndx; /* Section header string table index */ 73 | } Elf32_Ehdr; 74 | 75 | typedef struct 76 | { 77 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ 78 | Elf64_Half e_type; /* Object file type */ 79 | Elf64_Half e_machine; /* Architecture */ 80 | Elf64_Word e_version; /* Object file version */ 81 | Elf64_Addr e_entry; /* Entry point virtual address */ 82 | Elf64_Off e_phoff; /* Program header table file offset */ 83 | Elf64_Off e_shoff; /* Section header table file offset */ 84 | Elf64_Word e_flags; /* Processor-specific flags */ 85 | Elf64_Half e_ehsize; /* ELF header size in bytes */ 86 | Elf64_Half e_phentsize; /* Program header table entry size */ 87 | Elf64_Half e_phnum; /* Program header table entry count */ 88 | Elf64_Half e_shentsize; /* Section header table entry size */ 89 | Elf64_Half e_shnum; /* Section header table entry count */ 90 | Elf64_Half e_shstrndx; /* Section header string table index */ 91 | } Elf64_Ehdr; 92 | 93 | /* Fields in the e_ident array. The EI_* macros are indices into the 94 | array. The macros under each EI_* macro are the values the byte 95 | may have. */ 96 | 97 | #define EI_MAG0 0 /* File identification byte 0 index */ 98 | #define ELFMAG0 0x7f /* Magic number byte 0 */ 99 | 100 | #define EI_MAG1 1 /* File identification byte 1 index */ 101 | #define ELFMAG1 'E' /* Magic number byte 1 */ 102 | 103 | #define EI_MAG2 2 /* File identification byte 2 index */ 104 | #define ELFMAG2 'L' /* Magic number byte 2 */ 105 | 106 | #define EI_MAG3 3 /* File identification byte 3 index */ 107 | #define ELFMAG3 'F' /* Magic number byte 3 */ 108 | 109 | /* Conglomeration of the identification bytes, for easy testing as a word. */ 110 | #define ELFMAG "\177ELF" 111 | #define SELFMAG 4 112 | 113 | #define ET_EXEC 2 /* Executable file */ 114 | 115 | #define EM_386 3 /* Intel 80386 */ 116 | 117 | #define EV_NONE 0 /* Invalid ELF version */ 118 | #define EV_CURRENT 1 /* Current version */ 119 | #define EV_NUM 2 120 | 121 | /* Program segment header. */ 122 | 123 | typedef struct 124 | { 125 | Elf32_Word p_type; /* Segment type */ 126 | Elf32_Off p_offset; /* Segment file offset */ 127 | Elf32_Addr p_vaddr; /* Segment virtual address */ 128 | Elf32_Addr p_paddr; /* Segment physical address */ 129 | Elf32_Word p_filesz; /* Segment size in file */ 130 | Elf32_Word p_memsz; /* Segment size in memory */ 131 | Elf32_Word p_flags; /* Segment flags */ 132 | Elf32_Word p_align; /* Segment alignment */ 133 | } Elf32_Phdr; 134 | 135 | typedef struct 136 | { 137 | Elf64_Word p_type; /* Segment type */ 138 | Elf64_Word p_flags; /* Segment flags */ 139 | Elf64_Off p_offset; /* Segment file offset */ 140 | Elf64_Addr p_vaddr; /* Segment virtual address */ 141 | Elf64_Addr p_paddr; /* Segment physical address */ 142 | Elf64_Xword p_filesz; /* Segment size in file */ 143 | Elf64_Xword p_memsz; /* Segment size in memory */ 144 | Elf64_Xword p_align; /* Segment alignment */ 145 | } Elf64_Phdr; 146 | 147 | #define PT_LOAD 1 /* Loadable program segment */ 148 | #define PT_TLS 7 149 | 150 | #define PF_X (1 << 0) /* Segment is executable */ 151 | #define PF_W (1 << 1) /* Segment is writable */ 152 | #define PF_R (1 << 2) /* Segment is readable */ 153 | 154 | #include "arch/mmu.h" 155 | int load_elf(pte_t *pt, void *elf, size_t len, uintptr_t *entry_va); 156 | int parse_elf_phdr(void *elf, size_t len, 157 | uintptr_t *phdr_offset, uintptr_t *phent, uintptr_t *phnum); 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /include/error.h: -------------------------------------------------------------------------------- 1 | #ifndef _ERROR_H_ 2 | #define _ERROR_H_ 3 | 4 | #define ENOIMPL 1 // not implemented 5 | #define EOOB 2 // out of bound 6 | #define ENOFOUND 3 // not found 7 | #define EBADPKT 4 // bad packet 8 | #define ENOMEM 5 // cannot allocate 9 | #define EBADSADDR 6 // bad source addr 10 | #define ETIMEOUT 7 // timed out 11 | #define EPAGEFAULT 8 // page fault 12 | #define EUSER 9 // user is doing something wrong 13 | #define EOOM 10 // out of memory 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/eth.h: -------------------------------------------------------------------------------- 1 | #ifndef _ETH_H_ 2 | #define _ETH_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | 7 | typedef struct 8 | { 9 | void (*reset)(); 10 | void (*begin_dma_recv)(void *data, uint32_t len); 11 | void (*begin_dma_send)(void *data, uint32_t len); 12 | bool (*poll_recv)(); 13 | bool (*poll_send)(); 14 | uint16_t (*last_recv_len)(); 15 | } eth_ops_t; 16 | 17 | extern const eth_ops_t *eth_ops; 18 | 19 | void init_eth(); 20 | void eth_reset(); 21 | 22 | void eth_begin_dma_recv(void *data, uint32_t len); 23 | void eth_begin_dma_send(void *data, uint32_t len); 24 | 25 | static inline bool eth_poll_recv() 26 | { 27 | return eth_ops->poll_recv(); 28 | } 29 | 30 | static inline bool eth_poll_send() 31 | { 32 | return eth_ops->poll_send(); 33 | } 34 | 35 | static inline uint16_t eth_last_recv_len() 36 | { 37 | return eth_ops->last_recv_len(); 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/hwtimer.h: -------------------------------------------------------------------------------- 1 | #ifndef _HWTIMER_H_ 2 | #define _HWTIMER_H_ 3 | 4 | #include "stdint.h" 5 | 6 | typedef struct 7 | { 8 | void (*clear)(); 9 | uintptr_t (*get)(); 10 | uintptr_t (*get_freq)(); 11 | void (*set_oneshot)(uintptr_t t); 12 | } hwtimer_ops_t; 13 | 14 | extern const hwtimer_ops_t *hwtimer_ops; 15 | 16 | void init_hwtimer(); 17 | 18 | static inline void hwtimer_clear() 19 | { 20 | hwtimer_ops->clear(); 21 | } 22 | 23 | static inline uintptr_t hwtimer_get() 24 | { 25 | return hwtimer_ops->get(); 26 | } 27 | 28 | static inline uintptr_t hwtimer_get_freq() 29 | { 30 | return hwtimer_ops->get_freq(); 31 | } 32 | 33 | static inline void hwtimer_set_oneshot(uintptr_t t) 34 | { 35 | hwtimer_ops->set_oneshot(t); 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/ip.h: -------------------------------------------------------------------------------- 1 | #ifndef _IP_H_ 2 | #define _IP_H_ 3 | 4 | #include "stdbool.h" 5 | #include "stdint.h" 6 | 7 | typedef struct 8 | { 9 | uint16_t len; // length 10 | } __attribute__((packed)) frame_meta_t; 11 | 12 | typedef struct 13 | { 14 | frame_meta_t meta; 15 | uint8_t dst_mac[6]; 16 | uint8_t src_mac[6]; 17 | uint16_t ethertype; 18 | uint8_t payload[]; 19 | } __attribute__((packed)) frame_t; 20 | 21 | typedef struct 22 | { 23 | uint8_t dst_mac[6]; 24 | uint8_t src_mac[6]; 25 | uint16_t ethertype; 26 | uint8_t payload[]; 27 | } __attribute__((packed)) eth_header_t; 28 | 29 | #define ETH_TYPE_IPV4 0x0008 30 | #define ETH_TYPE_IPV6 0xdd86 31 | #define ETH_TYPE_ARP 0x0608 32 | 33 | // Make payload (ip packet) 16-byte aligned. 34 | #define ALIGN_OFFSET (((sizeof(frame_t) + 15) & ~15) - sizeof(frame_t)) 35 | 36 | #include "eth.h" 37 | static inline void eth_begin_send_frame(frame_t *frame) 38 | { 39 | eth_begin_dma_send((uint8_t *)frame + sizeof(frame_meta_t), frame->meta.len); 40 | } 41 | 42 | typedef union 43 | { 44 | uint32_t u32; 45 | uint8_t u8[4]; 46 | } ipv4_addr_t; 47 | 48 | typedef union 49 | { 50 | uint64_t u64[2]; 51 | uint8_t u8[16]; 52 | } ipv6_addr_t; 53 | 54 | // L3 protocols 55 | 56 | #define IPV4_VERSION 0x45 57 | #define IPV6_VERSION 0x60 58 | 59 | #define IPV4_DF_BIT 0x4000 60 | #define IPV4_MF_BIT 0x2000 61 | #define IPV4_NEXT_HEADER_ICMP 1 62 | #define IPV4_NEXT_HEADER_IGMP 2 63 | #define IPV6_MF_BIT 0x0001 64 | #define IPV6_NEXT_HEADER_ICMP 58 65 | #define IPV6_NEXT_HEADER_FRAGMENT 44 66 | #define IP_NEXT_HEADER_TCP 6 67 | #define IP_NEXT_HEADER_UDP 17 68 | #define DEFAULT_HOP_LIMIT 255 69 | 70 | typedef struct 71 | { 72 | uint8_t version_header_len; 73 | uint8_t dscp_ecn; 74 | uint16_t total_len; 75 | uint16_t id; 76 | uint16_t flags; 77 | uint8_t hop_limit; 78 | uint8_t next_header; 79 | uint16_t checksum; 80 | ipv4_addr_t src; 81 | ipv4_addr_t dst; 82 | } ipv4_header_t; 83 | 84 | typedef struct 85 | { 86 | uint32_t version_flow; 87 | uint16_t payload_len; 88 | uint8_t next_header; 89 | uint8_t hop_limit; 90 | ipv6_addr_t src; 91 | ipv6_addr_t dst; 92 | } ipv6_header_t; 93 | 94 | typedef struct 95 | { 96 | uint8_t next_header; 97 | uint8_t reserved1; 98 | uint16_t offset_mf; 99 | uint32_t id; 100 | } ipv6_fragment_header_t; 101 | 102 | // For little-endian systems. 103 | #define ARP_OP_REQUEST 0x0100 104 | #define ARP_OP_REPLY 0x0200 105 | 106 | #define ARP_HTYPE_ETHERNET 0x0100 107 | #define ARP_PTYPE_IPV4 0x0008 108 | 109 | #define ARP_HLEN_ETHERNET 6 110 | #define ARP_PLEN_IPV4 4 111 | 112 | typedef struct 113 | { 114 | uint16_t htype; // should be 0x0001 (Ethernet) 115 | uint16_t ptype; // should be 0x0800 (IPv4) 116 | uint8_t hlen; // should be 6 117 | uint8_t plen; // should be 4 118 | uint16_t op; // 1 for request, 2 for reply. 119 | uint8_t mac_saddr[6]; 120 | uint8_t ip_saddr[4]; 121 | uint8_t mac_daddr[6]; 122 | uint8_t ip_daddr[4]; 123 | } arp_packet_t; 124 | 125 | // L4 protocols 126 | 127 | #define ICMPV4_TYPE_TTL 0x0b 128 | #define ICMPV4_CODE_TTL 0x00 129 | #define ICMPV6_TYPE_TTL 0x03 130 | #define ICMPV6_CODE_TTL 0x00 131 | 132 | #define ICMPV4_TYPE_DEFRAG 0x0b 133 | #define ICMPV4_CODE_DEFRAG 0x01 134 | #define ICMPV6_TYPE_DEFRAG 0x03 135 | #define ICMPV6_CODE_DEFRAG 0x01 136 | 137 | #define ICMPV4_TYPE_TOO_BIG 0x03 138 | #define ICMPV4_CODE_TOO_BIG 0x04 139 | #define ICMPV6_TYPE_TOO_BIG 0x02 140 | #define ICMPV6_CODE_TOO_BIG 0x00 141 | 142 | #define ICMPV4_TYPE_ECHO_REQUEST 0x08 143 | #define ICMPV4_CODE_ECHO_REQUEST 0x00 144 | #define ICMPV6_TYPE_ECHO_REQUEST 0x80 145 | #define ICMPV6_CODE_ECHO_REQUEST 0x00 146 | 147 | #define ICMPV4_TYPE_ECHO_REPLY 0x00 148 | #define ICMPV4_CODE_ECHO_REPLY 0x00 149 | #define ICMPV6_TYPE_ECHO_REPLY 0x81 150 | #define ICMPV6_CODE_ECHO_REPLY 0x00 151 | 152 | #define ICMPV4_TYPE_NETWORK_UNREACHABLE 0x03 153 | #define ICMPV4_CODE_NETWORK_UNREACHABLE 0x00 154 | #define ICMPV6_TYPE_NETWORK_UNREACHABLE 0x01 155 | #define ICMPV6_CODE_NETWORK_UNREACHABLE 0x00 156 | 157 | #define ICMPV4_TYPE_ADDRESS_UNREACHABLE 0x03 158 | #define ICMPV4_CODE_ADDRESS_UNREACHABLE 0x01 159 | #define ICMPV6_TYPE_ADDRESS_UNREACHABLE 0x01 160 | #define ICMPV6_CODE_ADDRESS_UNREACHABLE 0x03 161 | 162 | #define ICMPV4_TYPE_PORT_UNREACHABLE 0x03 163 | #define ICMPV4_CODE_PORT_UNREACHABLE 0x03 164 | #define ICMPV6_TYPE_PORT_UNREACHABLE 0x01 165 | #define ICMPV6_CODE_PORT_UNREACHABLE 0x04 166 | 167 | #define ICMPV6_TYPE_RS 0x85 168 | #define ICMPV6_TYPE_RA 0x86 169 | #define ICMPV6_TYPE_NS 0x87 170 | #define ICMPV6_TYPE_NA 0x88 171 | #define ICMPV6_CODE_RS 0x00 172 | #define ICMPV6_CODE_RA 0x00 173 | #define ICMPV6_CODE_NS 0x00 174 | #define ICMPV6_CODE_NA 0x00 175 | 176 | #define ICMPV6_TYPE_MLD_QUERY 0x82 177 | #define ICMPV6_TYPE_MLD_REPORT 0x83 178 | #define ICMPV6_TYPE_MLD_DONE 0x84 179 | 180 | // For little-endian systems. 181 | #define ICMPV4_TYPE_CODE_TTL 0x000b 182 | #define ICMPV6_TYPE_CODE_TTL 0x0003 183 | 184 | #define ICMPV4_TYPE_CODE_DEFRAG 0x010b 185 | #define ICMPV6_TYPE_CODE_DEFRAG 0x0103 186 | 187 | #define ICMPV4_TYPE_CODE_TOO_BIG 0x0403 188 | #define ICMPV6_TYPE_CODE_TOO_BIG 0x0002 189 | 190 | #define ICMPV4_TYPE_CODE_ECHO_REQUEST 0x0008 191 | #define ICMPV6_TYPE_CODE_ECHO_REQUEST 0x0080 192 | 193 | #define ICMPV4_TYPE_CODE_ECHO_REPLY 0x0000 194 | #define ICMPV6_TYPE_CODE_ECHO_REPLY 0x0081 195 | 196 | #define ICMPV4_TYPE_CODE_NETWORK_UNREACHABLE 0x0003 197 | #define ICMPV6_TYPE_CODE_NETWORK_UNREACHABLE 0x0001 198 | 199 | #define ICMPV4_TYPE_CODE_ADDRESS_UNREACHABLE 0x0103 200 | #define ICMPV6_TYPE_CODE_ADDRESS_UNREACHABLE 0x0301 201 | 202 | #define ICMPV4_TYPE_CODE_PORT_UNREACHABLE 0x0303 203 | #define ICMPV6_TYPE_CODE_PORT_UNREACHABLE 0x0401 204 | 205 | #define ICMPV6_TYPE_CODE_RS 0x0085 206 | #define ICMPV6_TYPE_CODE_RA 0x0086 207 | #define ICMPV6_TYPE_CODE_NS 0x0087 208 | #define ICMPV6_TYPE_CODE_NA 0x0088 209 | 210 | #define ICMPV6_TYPE_CODE_MLD_QUERY 0x0082 211 | #define ICMPV6_TYPE_CODE_MLD_REPORT 0x0083 212 | #define ICMPV6_TYPE_CODE_MLD_DONE 0x0084 213 | 214 | typedef struct 215 | { 216 | union 217 | { 218 | struct 219 | { 220 | uint8_t type; 221 | uint8_t code; 222 | }; 223 | uint16_t type_code; 224 | }; 225 | uint16_t checksum; 226 | union 227 | { 228 | uint8_t rest_bytes[4]; 229 | uint32_t rest; 230 | struct 231 | { 232 | uint16_t ununsed1; // should be 0 233 | uint16_t mtu; 234 | }; 235 | struct 236 | { 237 | uint16_t id; 238 | uint16_t seq; 239 | }; 240 | }; 241 | } icmpv4_header_t; 242 | 243 | typedef struct 244 | { 245 | union 246 | { 247 | struct 248 | { 249 | uint8_t type; 250 | uint8_t code; 251 | }; 252 | uint16_t type_code; 253 | }; 254 | uint16_t checksum; 255 | union 256 | { 257 | uint8_t rest_bytes[4]; 258 | uint32_t rest; 259 | uint32_t flags; 260 | uint32_t mtu; 261 | // for echo requests and responses 262 | struct 263 | { 264 | uint16_t id; 265 | uint16_t seq; 266 | }; 267 | // for MLD messages 268 | struct 269 | { 270 | uint16_t max_response_delay; // meaningful only in Query messages 271 | uint16_t reserved1; // Initialized to zero by the sender; ignored by receivers. 272 | }; 273 | }; 274 | } icmpv6_header_t; 275 | 276 | #define ICMPV6_FLAGS_R 0x80 277 | #define ICMPV6_FLAGS_S 0x40 278 | #define ICMPV6_FLAGS_O 0x20 279 | 280 | #define ICMPV6_OPTION_TYPE_SOURCE 1 281 | #define ICMPV6_OPTION_TYPE_TARGET 2 282 | 283 | #define MLD_HOP_LIMIT 1 284 | #define NDP_HOP_LIMIT 255 285 | 286 | typedef struct 287 | { 288 | ipv6_addr_t addr; 289 | } ndp_packet_t; 290 | 291 | typedef struct 292 | { 293 | uint8_t type; 294 | uint8_t len; // should be 1 (means 8 bytes) for ethernet 295 | uint8_t mac_addr[6]; 296 | } ndp_option_t; 297 | 298 | // The code field of the ICMPv6 header is initialized to zero by the sender, 299 | // and ignored by receivers. 300 | typedef struct 301 | { 302 | ipv6_addr_t maddr; 303 | } mld_packet_t; 304 | 305 | #define IGMP_TYPE_QUERY 0x11 306 | #define IGMP_TYPE_V2REPORT 0x16 307 | #define IGMP_TYPE_LEAVE 0x17 308 | #define IGMP_TYPE_V1REPORT 0x12 309 | 310 | typedef struct 311 | { 312 | uint8_t type; 313 | uint8_t max_response_delay; 314 | uint16_t checksum; 315 | ipv4_addr_t maddr; 316 | } igmp_packet_t; 317 | 318 | typedef struct 319 | { 320 | uint32_t header0[4]; 321 | uint16_t checksum; 322 | uint16_t header1; 323 | } tcp_header_t; 324 | 325 | typedef struct 326 | { 327 | uint16_t src_port; 328 | uint16_t dst_port; 329 | uint16_t len; 330 | uint16_t checksum; 331 | } udp_header_t; 332 | 333 | // L5 334 | 335 | #define RIP_PORT 520 336 | #define RIPNG_PORT 521 337 | 338 | #define RIP_CMD_REQUEST 1 339 | #define RIP_CMD_RESPONSE 2 340 | #define RIPNG_CMD_REQUEST 1 341 | #define RIPNG_CMD_RESPONSE 2 342 | 343 | typedef struct 344 | { 345 | uint16_t af; 346 | uint16_t tag; 347 | ipv4_addr_t prefix; 348 | uint32_t mask; 349 | uint32_t nexthop; 350 | uint32_t metric; 351 | } __attribute__((packed)) rip_entry_t; 352 | 353 | typedef struct 354 | { 355 | uint8_t cmd; 356 | uint8_t ver; // 1 or 2 357 | uint16_t zero; 358 | rip_entry_t entries[]; 359 | } __attribute__((packed)) rip_packet_t; 360 | 361 | typedef struct 362 | { 363 | ipv6_addr_t prefix; 364 | uint16_t tag; 365 | uint8_t prefixlen; 366 | uint8_t metric; 367 | } __attribute__((packed)) ripng_entry_t; 368 | 369 | typedef struct 370 | { 371 | uint8_t cmd; 372 | uint8_t ver; // 1 373 | uint16_t zero; 374 | ripng_entry_t entries[]; 375 | } __attribute__((packed)) ripng_packet_t; 376 | 377 | static inline uint16_t ntohs(uint16_t x) 378 | { 379 | return ((x << 8) & 0xff00) | ((x >> 8) & 0xff); 380 | } 381 | 382 | static inline uint16_t htons(uint16_t x) 383 | { 384 | return ntohs(x); 385 | } 386 | 387 | static inline uint32_t ntohl(uint32_t x) 388 | { 389 | return ((x << 24) & 0xff000000) 390 | | ((x << 8) & 0x00ff0000) 391 | | ((x >> 8) & 0x0000ff00) 392 | | ((x >> 24) & 0x000000ff); 393 | } 394 | 395 | static inline uint32_t htonl(uint32_t x) 396 | { 397 | return ntohl(x); 398 | } 399 | 400 | static inline uint8_t ip_version(void *pkt) 401 | { 402 | ipv4_header_t *v4pkt = (ipv4_header_t *)pkt; 403 | return v4pkt->version_header_len >> 4; 404 | } 405 | 406 | static inline uint32_t ip_checksum_partial(const void *buff, size_t len) 407 | { 408 | uint32_t checksum = 0; 409 | const uint16_t *buff16 = (const uint16_t *)buff; 410 | for (int i = 0; i < len / sizeof(uint16_t); ++i) 411 | { 412 | checksum += buff16[i]; 413 | } 414 | if (len & 1) checksum += (uint16_t)((const uint8_t *)buff)[len - 1]; 415 | return checksum; 416 | } 417 | 418 | static inline uint32_t ip_checksum_neg_partial(const void *buff, size_t len) 419 | { 420 | uint32_t checksum = 0; 421 | const uint16_t *buff16 = (const uint16_t *)buff; 422 | for (int i = 0; i < len / sizeof(uint16_t); ++i) 423 | { 424 | checksum += ~buff16[i] & 0xffff; 425 | } 426 | if (len & 1) checksum += ~((uint16_t)((const uint8_t *)buff)[len - 1]) & 0xffff; 427 | return checksum; 428 | } 429 | 430 | static inline uint16_t ip_checksum_final(uint32_t checksum) 431 | { 432 | checksum = (checksum & 0xffff) + (checksum >> 16); 433 | checksum = (checksum & 0xffff) + (checksum >> 16); 434 | return ~(checksum & 0xffff); 435 | } 436 | 437 | static inline uint16_t ip_checksum(const void *buff, size_t len) 438 | { 439 | return ip_checksum_final(ip_checksum_partial(buff, len)); 440 | } 441 | 442 | static inline ipv4_addr_t make_ipv4(uint32_t addr) 443 | { 444 | ipv4_addr_t ip; 445 | ip.u32 = addr; 446 | return ip; 447 | } 448 | 449 | static inline ipv6_addr_t make_ipv6(uint64_t lo, uint64_t hi) 450 | { 451 | ipv6_addr_t ip; 452 | ip.u64[0] = lo; 453 | ip.u64[1] = hi; 454 | return ip; 455 | } 456 | 457 | static inline bool is_ipv4(ipv6_addr_t a) 458 | { 459 | return a.u64[0] == 0 && (a.u64[1] & 0xffffffff) == 0; 460 | } 461 | 462 | static inline bool is_ipv6(ipv6_addr_t a) 463 | { 464 | return !is_ipv4(a); 465 | } 466 | 467 | static inline uint64_t ipv6_multicast_mac(ipv6_addr_t a) 468 | { 469 | return ((a.u64[1] >> 16) & 0xffffffff0000) | 0x3333; 470 | } 471 | 472 | static inline ipv4_addr_t ipv4_unspecified() 473 | { 474 | return make_ipv4(0); 475 | } 476 | 477 | static inline ipv4_addr_t ipv4_broadcast() 478 | { 479 | return make_ipv4(0xffffffff); 480 | } 481 | 482 | static inline ipv6_addr_t ipv6_unspecified() 483 | { 484 | return make_ipv6(0, 0); 485 | } 486 | 487 | static inline ipv6_addr_t ipv6_from_mac(uint64_t mac) 488 | { 489 | return make_ipv6(0x80fe, 490 | ((mac & 0xffffff) | ((mac << 16) & 0xffffff0000000000)) 491 | ^ 0x000000feff000002); 492 | } 493 | 494 | static inline ipv6_addr_t ipv6_all_nodes() 495 | { 496 | return make_ipv6(0x02ff, 0x01ul << 56); 497 | } 498 | 499 | static inline ipv6_addr_t ipv6_all_routers() 500 | { 501 | return make_ipv6(0x02ff, 0x02ul << 56); 502 | } 503 | 504 | static inline ipv6_addr_t ipv6_rip_routers() 505 | { 506 | return make_ipv6(0x02ff, 0x09ul << 56); 507 | } 508 | 509 | static inline ipv6_addr_t ipv6_solicited_node(ipv6_addr_t a) 510 | { 511 | return make_ipv6(0x02ff, (a.u64[1] & 0xffffff0000000000) | 0xff01000000); 512 | } 513 | 514 | static inline ipv6_addr_t ipv4_compatible(ipv4_addr_t a) 515 | { 516 | return make_ipv6(0, (uint64_t)a.u32 << 32); 517 | } 518 | 519 | static inline ipv4_addr_t extract_ipv4(ipv6_addr_t a) 520 | { 521 | return make_ipv4(a.u64[1] >> 32); 522 | } 523 | 524 | static inline bool ipv4_eq(ipv4_addr_t a, ipv4_addr_t b) 525 | { 526 | return a.u32 == b.u32; 527 | } 528 | 529 | static inline bool ipv6_eq(ipv6_addr_t a, ipv6_addr_t b) 530 | { 531 | return a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]; 532 | } 533 | 534 | static inline bool ipv4_is_unspecified(ipv4_addr_t a) 535 | { 536 | return a.u32 == 0; 537 | } 538 | 539 | static inline bool ipv4_is_multicast(ipv4_addr_t a) 540 | { 541 | return (a.u8[0] >> 4) == 0xe; 542 | } 543 | 544 | static inline bool ipv6_is_multicast(ipv6_addr_t a) 545 | { 546 | return a.u8[0] == 0xff; 547 | } 548 | 549 | static inline bool ipv6_is_unspecified(ipv6_addr_t a) 550 | { 551 | return a.u64[0] == 0 && a.u64[1] == 0; 552 | } 553 | 554 | static inline ipv6_addr_t ipv4_embed(ipv6_addr_t p, ipv4_addr_t a) 555 | { 556 | return make_ipv6(p.u64[0], p.u64[1] | ((uint64_t)a.u32 << 32)); 557 | } 558 | 559 | static inline bool ipv6_is_in_net96(ipv6_addr_t p, ipv6_addr_t a) 560 | { 561 | return p.u64[0] == a.u64[0] && ((p.u64[1] ^ a.u64[1]) & 0xfffffffful) == 0; 562 | } 563 | 564 | static inline bool ipv4_is_in_net(ipv4_addr_t p, ipv4_addr_t m, ipv4_addr_t a) 565 | { 566 | return ((p.u32 ^ a.u32) & m.u32) == 0; 567 | } 568 | 569 | void init_ip(); 570 | uint64_t get_ifinfo_mac(); 571 | ipv4_addr_t get_ifinfo_ipv4(); 572 | void set_ifinfo_ipv4(ipv4_addr_t ipv4); 573 | bool get_ip_ready(); 574 | void set_ip_ready(bool ready); 575 | void set_dhcp_server_ip(ipv6_addr_t ip); 576 | int arp_make_request(frame_t *frame, size_t buflen, ipv4_addr_t ip, uint64_t known_mac); 577 | int ip_make_ndp(frame_t *frame, size_t buflen, 578 | ipv6_addr_t addr, uint64_t mac, 579 | bool adv, bool unicast, uint32_t flags); 580 | void ip_send_init_packets(frame_t *frame, size_t buflen); 581 | int ip_send_with_mac(frame_t *frame, uint64_t mac); 582 | int handle_frame(frame_t *frame); 583 | int handle_arp(frame_t *frame); 584 | int handle_ipv4(frame_t *frame); 585 | int handle_ipv6(frame_t *frame); 586 | 587 | int send_udp(uint16_t sport, ipv6_addr_t daddr, uint16_t dport, void *buff, size_t len); 588 | size_t recv_udp(uint16_t dport, ipv6_addr_t *saddr, uint16_t *sport, void *buff, size_t len, 589 | ipv6_addr_t expected_saddr, uint16_t expected_sport, uint64_t timeout); 590 | int handle_udp(ipv6_addr_t saddr, udp_header_t *udp, size_t len); 591 | 592 | extern const uint64_t MAC_BROADCAST; 593 | 594 | typedef struct 595 | { 596 | uint64_t mtu; 597 | uint64_t mac; 598 | uint8_t ipv4_prefixlen, ipv6_prefixlen; 599 | ipv4_addr_t ipv4; 600 | ipv4_addr_t ipv4_multicast[4]; 601 | ipv6_addr_t ipv6; 602 | ipv6_addr_t ipv6_multicast[4]; 603 | } interface_info_t; 604 | 605 | extern ipv6_addr_t server_ip; 606 | 607 | #endif 608 | -------------------------------------------------------------------------------- /include/judge.h: -------------------------------------------------------------------------------- 1 | #ifndef _JUDGE_H_ 2 | #define _JUDGE_H_ 3 | 4 | #include "stdint.h" 5 | 6 | #define AT_PHDR 3 7 | #define AT_PHENT 4 8 | #define AT_PHNUM 5 9 | #define AT_PAGESZ 6 10 | 11 | #define JUDGE_SERVER_PORT 1234 12 | 13 | #define STACK_SIZE 0x400000 14 | #define HEAP_SIZE 0x400000 15 | #define HEAP_VA 0x10000000 16 | 17 | #define STDOUT_LIMIT 0x10000 18 | 19 | typedef struct 20 | { 21 | uint8_t *stdin_buff; 22 | size_t stdin_len; 23 | uint8_t *stdout_buff; 24 | size_t stdout_cap; 25 | size_t stdout_len; 26 | void *brk; 27 | void *brk_end; 28 | } aux_t; 29 | 30 | typedef struct 31 | { 32 | size_t seq; 33 | size_t time_limit; // clock cycles 34 | size_t mem_limit; // unused 35 | } judge_req_t; 36 | 37 | #define JUDGE_RESP_ACK 1 38 | #define JUDGE_RESP_RESULT 2 39 | 40 | typedef struct 41 | { 42 | size_t seq; 43 | uintptr_t flags; 44 | intptr_t error; 45 | intptr_t exitcode; 46 | size_t time_usage; // clock cycles 47 | size_t mem_usage; // KiB 48 | } judge_resp_t; 49 | 50 | void init_judge(); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/mm.h: -------------------------------------------------------------------------------- 1 | #ifndef _MM_H_ 2 | #define _MM_H_ 3 | 4 | #include "stdint.h" 5 | 6 | typedef struct 7 | { 8 | uint64_t size; 9 | uint64_t next; 10 | uint64_t used; 11 | uint64_t bits[]; 12 | } mm_bitmap_t; 13 | 14 | void init_mm(); 15 | void add_pages(uintptr_t start, size_t n); 16 | void finish_add_pages(); 17 | void take_pages(uintptr_t start, size_t n); 18 | uintptr_t get_free_page(); 19 | 20 | void mm_take_snapshot(); 21 | void mm_restore_snapshot(); 22 | 23 | void mm_print(); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/printf.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // \author (c) Marco Paland (info@paland.com) 3 | // 2014-2019, PALANDesign Hannover, Germany 4 | // 5 | // \license The MIT License (MIT) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on 26 | // embedded systems with a very limited resources. 27 | // Use this instead of bloated standard/newlib printf. 28 | // These routines are thread safe and reentrant. 29 | // 30 | /////////////////////////////////////////////////////////////////////////////// 31 | 32 | #ifndef _PRINTF_H_ 33 | #define _PRINTF_H_ 34 | 35 | #include 36 | #include 37 | 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | 44 | /** 45 | * Output a character to a custom device like UART, used by the printf() function 46 | * This function is declared here only. You have to write your custom implementation somewhere 47 | * \param character Character to output 48 | */ 49 | void _putchar(char character); 50 | 51 | 52 | /** 53 | * Tiny printf implementation 54 | * You have to implement _putchar if you use printf() 55 | * To avoid conflicts with the regular printf() API it is overridden by macro defines 56 | * and internal underscore-appended functions like printf_() are used 57 | * \param format A string that specifies the format of the output 58 | * \return The number of characters that are written into the array, not counting the terminating null character 59 | */ 60 | #define printf printf_ 61 | int printf_(const char* format, ...); 62 | 63 | 64 | /** 65 | * Tiny sprintf implementation 66 | * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! 67 | * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! 68 | * \param format A string that specifies the format of the output 69 | * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character 70 | */ 71 | #define sprintf sprintf_ 72 | int sprintf_(char* buffer, const char* format, ...); 73 | 74 | 75 | /** 76 | * Tiny snprintf/vsnprintf implementation 77 | * \param buffer A pointer to the buffer where to store the formatted string 78 | * \param count The maximum number of characters to store in the buffer, including a terminating null character 79 | * \param format A string that specifies the format of the output 80 | * \param va A value identifying a variable arguments list 81 | * \return The number of characters that COULD have been written into the buffer, not counting the terminating 82 | * null character. A value equal or larger than count indicates truncation. Only when the returned value 83 | * is non-negative and less than count, the string has been completely written. 84 | */ 85 | #define snprintf snprintf_ 86 | #define vsnprintf vsnprintf_ 87 | int snprintf_(char* buffer, size_t count, const char* format, ...); 88 | int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); 89 | 90 | 91 | /** 92 | * Tiny vprintf implementation 93 | * \param format A string that specifies the format of the output 94 | * \param va A value identifying a variable arguments list 95 | * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character 96 | */ 97 | #define vprintf vprintf_ 98 | int vprintf_(const char* format, va_list va); 99 | 100 | 101 | /** 102 | * printf with output function 103 | * You may use this as dynamic alternative to printf() with its fixed _putchar() output 104 | * \param out An output function which takes one character and an argument pointer 105 | * \param arg An argument pointer for user data passed to output function 106 | * \param format A string that specifies the format of the output 107 | * \return The number of characters that are sent to the output function, not counting the terminating null character 108 | */ 109 | int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); 110 | 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | 117 | #endif // _PRINTF_H_ 118 | -------------------------------------------------------------------------------- /include/stdbool.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDBOOL_H_ 2 | #define _STDBOOL_H_ 3 | 4 | #define bool _Bool 5 | #define true 1 6 | #define false 0 7 | #define __bool_true_false_are_defined 1 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDINT_H_ 2 | #define _STDINT_H_ 3 | 4 | typedef char int8_t; 5 | typedef short int16_t; 6 | typedef int int32_t; 7 | typedef long int64_t; 8 | 9 | typedef unsigned char uint8_t; 10 | typedef unsigned short uint16_t; 11 | typedef unsigned int uint32_t; 12 | typedef unsigned long uint64_t; 13 | 14 | 15 | typedef int64_t intptr_t; 16 | typedef uint64_t uintptr_t; 17 | typedef int64_t ptrdiff_t; 18 | typedef uint64_t size_t; 19 | typedef int64_t ssize_t; 20 | 21 | typedef long intmax_t; 22 | 23 | #define NULL (void *)(0ul) 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDIO_H_ 2 | #define _STDIO_H_ 3 | 4 | #include "tty.h" 5 | #include "printf.h" 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRING_H_ 2 | #define _STRING_H_ 3 | 4 | #include "stdint.h" 5 | 6 | int strcmp(const char *a, const char *b); 7 | void memset(void *dst, uint8_t b, size_t len); 8 | int memcmp(const void *a, const void *b, size_t len); 9 | void memcpy(void *dst, const void *src, size_t len); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/task.h: -------------------------------------------------------------------------------- 1 | #ifndef _TASK_H_ 2 | #define _TASK_H_ 3 | 4 | #include "stdint.h" 5 | #include "arch/task.h" 6 | #include "timer.h" 7 | #include "ip.h" 8 | 9 | #define NUM_TASKS 10 10 | 11 | typedef struct 12 | { 13 | bool is_waiting; 14 | uint16_t dport; 15 | ipv6_addr_t expected_saddr; 16 | uint16_t expected_sport; 17 | ipv6_addr_t *saddr; 18 | uint16_t *sport; 19 | void *buff; 20 | size_t len; 21 | size_t *out_len; 22 | uint64_t start; 23 | uint64_t delta; 24 | } wait_packet_t; 25 | 26 | struct task 27 | { 28 | regs_t regs; 29 | uint64_t wait; 30 | timer_t timer; 31 | wait_packet_t wait_packet; 32 | }; 33 | 34 | typedef struct 35 | { 36 | int32_t exitcode; 37 | int32_t error; 38 | uint64_t epc; 39 | uint64_t eval; 40 | } user_task_t; 41 | 42 | #define WAIT_TTY_RECV (1 << 0) 43 | #define WAIT_TTY_SEND (1 << 1) 44 | #define WAIT_ETH_RECV (1 << 2) 45 | #define WAIT_ETH_SEND (1 << 3) 46 | #define WAIT_EXIT (1ul << 63) 47 | 48 | struct task *switch_to(struct task *curr, struct task *next); 49 | 50 | static inline struct task *get_current(); 51 | static inline void set_current(struct task *task); 52 | #define current (get_current()) 53 | 54 | extern struct task *const idle_task; 55 | 56 | void init_task(); 57 | void create_task(struct task *task, uintptr_t stack, void *entry); 58 | void add_task(struct task *task); 59 | void idle_entry(); 60 | 61 | void sched_yield(); 62 | void wait(uint64_t w); 63 | void sleep(uint64_t delta); 64 | void exit(); 65 | 66 | void task_dispatch_udp(uint16_t dport, ipv6_addr_t saddr, uint16_t sport, void *buff, size_t len); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/tftp.h: -------------------------------------------------------------------------------- 1 | #ifndef _TFTP_H_ 2 | #define _TFTP_H_ 3 | 4 | #include "stdint.h" 5 | 6 | #define TFTP_SERVER_PORT 6969 7 | 8 | #define TFTP_OP_RRQ 0x0100 9 | #define TFTP_OP_WRQ 0x0200 10 | #define TFTP_OP_DATA 0x0300 11 | #define TFTP_OP_ACK 0x0400 12 | #define TFTP_OP_ERROR 0x0500 13 | 14 | #define TFTP_MODE "octet" 15 | #define TFTP_MODE_SIZE (5 + 1) 16 | 17 | #define TFTP_CHUNK_SIZE 512 18 | 19 | typedef struct 20 | { 21 | uint16_t opcode; 22 | } tftp_req_packet_t; 23 | 24 | typedef struct 25 | { 26 | uint16_t opcode; 27 | uint16_t seq; 28 | } tftp_packet_t; 29 | 30 | typedef int (*tftp_read_block_t)(void *ctx, void *buff); 31 | 32 | void init_tftp(); 33 | int tftp_get_file(char *filename, size_t filename_len, void *buff, size_t len, size_t *out_len); 34 | int tftp_put_file(char *filename, size_t filename_len, tftp_read_block_t read_block, void *ctx); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMER_H_ 2 | #define _TIMER_H_ 3 | 4 | #include "stdint.h" 5 | #include "arch/asm.h" 6 | 7 | typedef struct 8 | { 9 | uint64_t start; 10 | uint64_t delta; 11 | uint64_t timeout; 12 | } timer_t; 13 | 14 | static inline void set_timer(timer_t *t, uint64_t delta) 15 | { 16 | t->start = rdcycle(); 17 | t->delta = delta; 18 | t->timeout = 0; 19 | } 20 | 21 | static inline int poll_timer(timer_t *t) 22 | { 23 | if (t->timeout) return 1; 24 | if ((rdcycle() - t->start) >= t->delta) 25 | { 26 | t->timeout = 1; 27 | return 1; 28 | } 29 | return 0; 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef _TTY_H_ 2 | #define _TTY_H_ 3 | 4 | #include "stdint.h" 5 | #include "stdbool.h" 6 | 7 | typedef struct 8 | { 9 | void (*putchar)(uint8_t c); 10 | uint8_t (*getchar)(); 11 | bool (*poll_recv)(); 12 | bool (*poll_send)(); 13 | } tty_ops_t; 14 | 15 | extern const tty_ops_t *tty_ops; 16 | 17 | void init_tty(); 18 | 19 | void putchar(uint8_t c); 20 | uint8_t getchar(); 21 | void _putchar(char ch); 22 | 23 | void puts(char *s); 24 | void getline(char *buf, size_t len); 25 | 26 | static inline int tty_poll_recv() 27 | { 28 | return tty_ops->poll_recv(); 29 | } 30 | 31 | static inline int tty_poll_send() 32 | { 33 | return tty_ops->poll_send(); 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | 4 | #include "stdint.h" 5 | #include "ip.h" 6 | 7 | void zero_page(void *pg); 8 | void print_mem(void *data, size_t len, char delim, int newline); 9 | void print_mac(uint8_t *data); 10 | void print_ipv4(uint8_t *data); 11 | void print_ipv6(uint8_t *data); 12 | void print_frame(frame_t *frame); 13 | 14 | static inline size_t bits_find_zero(uint64_t b) 15 | { 16 | b = ~b; 17 | b = b & -b; // to one-hot 18 | size_t i = 0; 19 | if (b & 0xffffffff00000000ul) i += 32; 20 | if (b & 0xffff0000ffff0000ul) i += 16; 21 | if (b & 0xff00ff00ff00ff00ul) i += 8; 22 | if (b & 0xf0f0f0f0f0f0f0f0ul) i += 4; 23 | if (b & 0xccccccccccccccccul) i += 2; 24 | if (b & 0xaaaaaaaaaaaaaaaaul) i += 1; 25 | return i; 26 | } 27 | 28 | #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef _VM_H_ 2 | #define _VM_H_ 3 | 4 | #include "stdint.h" 5 | 6 | #define VM_R 0x1 7 | #define VM_W 0x2 8 | #define VM_X 0x4 9 | #define VM_U 0x8 10 | #define VM_A 0x10 11 | #define VM_D 0x20 12 | 13 | #include "arch/mmu.h" 14 | 15 | extern pte_t *kernel_pt; 16 | 17 | pte_t *create_pt(); 18 | pte_t *get_pte(pte_t *pt, uintptr_t va); 19 | void *map_page(pte_t *pt, uintptr_t va, uint64_t flags); 20 | int map_and_copy(pte_t *pt, uintptr_t va, uint64_t flags, void *src, size_t len); 21 | size_t count_pt(pte_t *pt, uint64_t flags); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /ip.c: -------------------------------------------------------------------------------- 1 | #include "ip.h" 2 | #include "error.h" 3 | #include "eth.h" 4 | #include "task.h" 5 | #include "string.h" 6 | 7 | const uint64_t MAC_BROADCAST = 0xffffffffffff; 8 | 9 | interface_info_t ifinfo; 10 | bool ip_ready = false; 11 | 12 | ipv6_addr_t server_ip; 13 | uint64_t server_mac = MAC_BROADCAST; 14 | uint64_t server_mac_atime = 0; 15 | 16 | ipv6_addr_t dhcp_server_ip; 17 | uint64_t dhcp_server_mac = MAC_BROADCAST; 18 | uint64_t dhcp_server_mac_atime = 0; 19 | 20 | __attribute__ ((aligned (16))) static uint8_t dma_buff[4096]; 21 | __attribute__ ((aligned (16))) static uint8_t iptask_stack[1024]; 22 | static struct task iptask_task; 23 | void iptask_entry() 24 | { 25 | frame_t *const frame = (frame_t *)(dma_buff + ALIGN_OFFSET); 26 | const uint64_t len = sizeof(dma_buff) - ALIGN_OFFSET; 27 | 28 | while (true) 29 | { 30 | eth_begin_dma_recv((uint8_t *)frame + sizeof(frame_meta_t), len - sizeof(frame_meta_t)); 31 | wait(WAIT_ETH_RECV); 32 | frame->meta.len = eth_last_recv_len(); 33 | handle_frame(frame); 34 | } 35 | } 36 | 37 | void init_ip() 38 | { 39 | server_mac = MAC_BROADCAST; 40 | dhcp_server_mac = MAC_BROADCAST; 41 | 42 | ifinfo.mtu = 1500; 43 | ifinfo.mac = 0x315f32445754; // 54:57:44:32:5f:31 44 | ifinfo.ipv4 = ipv4_unspecified(); 45 | /* // To use static IPv4 address: 46 | ifinfo.ipv4 = make_ipv4(0x8001a8c0); 47 | ip_ready = true; 48 | */ 49 | ifinfo.ipv6 = ipv6_unspecified(); 50 | server_ip = ipv4_compatible(make_ipv4(0x3701a8c0)); // 192.168.1.55 51 | 52 | create_task(&iptask_task, 53 | (uintptr_t)iptask_stack + sizeof(iptask_stack) + STACK_OFFSET, 54 | iptask_entry); 55 | } 56 | 57 | uint64_t get_ifinfo_mac() 58 | { 59 | return ifinfo.mac; 60 | } 61 | 62 | ipv4_addr_t get_ifinfo_ipv4() 63 | { 64 | return ifinfo.ipv4; 65 | } 66 | 67 | void set_ifinfo_ipv4(ipv4_addr_t ipv4) 68 | { 69 | ifinfo.ipv4 = ipv4; 70 | } 71 | 72 | bool get_ip_ready() 73 | { 74 | return ip_ready; 75 | } 76 | 77 | void set_ip_ready(bool ready) 78 | { 79 | ip_ready = ready; 80 | } 81 | 82 | void set_dhcp_server_ip(ipv6_addr_t ip) 83 | { 84 | dhcp_server_ip = ip; 85 | dhcp_server_mac = MAC_BROADCAST; 86 | } 87 | 88 | int ip_query_route(ipv6_addr_t addr, ipv6_addr_t *nexthop) 89 | { 90 | // TODO: route table 91 | *nexthop = addr; 92 | return 0; 93 | } 94 | 95 | int ip_query_mac(ipv6_addr_t addr, uint64_t *mac) 96 | { 97 | // TODO: mac cache 98 | if (ipv6_eq(addr, server_ip)) 99 | { 100 | if (server_mac == MAC_BROADCAST) 101 | { 102 | return -ENOFOUND; 103 | } 104 | *mac = server_mac; 105 | return 0; 106 | } 107 | if (ipv6_eq(addr, dhcp_server_ip)) 108 | { 109 | if (dhcp_server_mac == MAC_BROADCAST) 110 | { 111 | return -ENOFOUND; 112 | } 113 | *mac = dhcp_server_mac; 114 | return 0; 115 | } 116 | if (ipv6_eq(addr, ipv4_compatible(ipv4_broadcast()))) 117 | { 118 | *mac = MAC_BROADCAST; 119 | return 0; 120 | } 121 | return -ENOFOUND; 122 | } 123 | 124 | void ip_update_mac(ipv6_addr_t addr, uint64_t mac) 125 | { 126 | if (ipv6_eq(addr, server_ip)) 127 | { 128 | server_mac = mac; 129 | server_mac_atime = rdcycle(); 130 | } 131 | if (ipv6_eq(addr, dhcp_server_ip)) 132 | { 133 | dhcp_server_mac = mac; 134 | dhcp_server_mac_atime = rdcycle(); 135 | } 136 | } 137 | 138 | void ip_update_mac_atime(ipv6_addr_t addr) 139 | { 140 | if (ipv6_eq(addr, server_ip)) 141 | { 142 | server_mac_atime = rdcycle(); 143 | } 144 | if (ipv6_eq(addr, dhcp_server_ip)) 145 | { 146 | dhcp_server_mac_atime = rdcycle(); 147 | } 148 | } 149 | 150 | int ip_send(frame_t *frame, ipv6_addr_t nexthop) 151 | { 152 | int ret; 153 | uint64_t mac; 154 | if ((ret = ip_query_mac(nexthop, &mac)) < 0) 155 | { 156 | /* 157 | TODO: save this packet, and send an ARP request or a NDP solicitation. 158 | */ 159 | bool is_v4 = is_ipv4(nexthop); 160 | int ret; 161 | if (is_v4) 162 | { 163 | ret = arp_make_request(frame, 4096 /* FIXME */, extract_ipv4(nexthop), -1); 164 | if (ret < 0) return ret; 165 | } 166 | else 167 | { 168 | ret = ip_make_ndp(frame, 4096 /* FIXME */, nexthop, -1, false, false, 0); 169 | if (ret < 0) return ret; 170 | } 171 | eth_begin_send_frame(frame); 172 | wait(WAIT_ETH_SEND); 173 | return 0; 174 | } 175 | return ip_send_with_mac(frame, mac); 176 | } 177 | 178 | int ip_send_with_mac(frame_t *frame, uint64_t mac) 179 | { 180 | memcpy(frame->src_mac, &ifinfo.mac, sizeof(frame->src_mac)); 181 | memcpy(frame->dst_mac, &mac, sizeof(frame->dst_mac)); 182 | eth_begin_send_frame(frame); 183 | wait(WAIT_ETH_SEND); 184 | return 0; 185 | } 186 | 187 | uint16_t ipv6_l4_checksum(ipv6_header_t *ipv6) 188 | { 189 | uint32_t checksum = 0; 190 | checksum += ip_checksum_partial(ipv6->src.u8, sizeof(ipv6->src.u8)); 191 | checksum += ip_checksum_partial(ipv6->dst.u8, sizeof(ipv6->dst.u8)); 192 | checksum += ipv6->payload_len; 193 | checksum += (uint16_t)ipv6->next_header << 8; 194 | checksum += ip_checksum_partial(ipv6 + 1, ntohs(ipv6->payload_len)); 195 | return ip_checksum_final(checksum); 196 | } 197 | 198 | uint16_t ipv4_l4_checksum(ipv4_header_t *ipv4, uint16_t payload_len) 199 | { 200 | uint32_t checksum = 0; 201 | checksum += ip_checksum_partial(ipv4->src.u8, sizeof(ipv4->src.u8)); 202 | checksum += ip_checksum_partial(ipv4->dst.u8, sizeof(ipv4->dst.u8)); 203 | checksum += htons(payload_len); 204 | checksum += (uint16_t)ipv4->next_header << 8; 205 | checksum += ip_checksum_partial(ipv4 + 1, payload_len); 206 | return ip_checksum_final(checksum); 207 | } 208 | 209 | int arp_make_request(frame_t *frame, size_t buflen, ipv4_addr_t ip, uint64_t known_mac) 210 | { 211 | if (buflen < sizeof(frame_t) + sizeof(arp_packet_t)) return -EOOB; 212 | arp_packet_t *arp = (arp_packet_t *)frame->payload; 213 | 214 | frame->meta.len = sizeof(eth_header_t) + sizeof(arp_packet_t); 215 | 216 | memcpy(frame->dst_mac, &known_mac, 6); 217 | memcpy(frame->src_mac, &ifinfo.mac, 6); 218 | frame->ethertype = ETH_TYPE_ARP; 219 | 220 | arp->htype = ARP_HTYPE_ETHERNET; 221 | arp->ptype = ARP_PTYPE_IPV4; 222 | arp->hlen = 6; 223 | arp->plen = 4; 224 | arp->op = ARP_OP_REQUEST; 225 | memcpy(arp->mac_saddr, &ifinfo.mac, 6); 226 | memcpy(arp->ip_saddr, &ifinfo.ipv4, 4); 227 | memcpy(arp->mac_daddr, &known_mac, 6); 228 | memcpy(arp->ip_daddr, &ip, 4); 229 | return 0; 230 | } 231 | 232 | int handle_arp(frame_t *frame) 233 | { 234 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(arp_packet_t)) return -EOOB; 235 | arp_packet_t *arp = (arp_packet_t *)frame->payload; 236 | 237 | if (!(arp->htype == ARP_HTYPE_ETHERNET && arp->ptype == ARP_PTYPE_IPV4 238 | && arp->hlen == 6 && arp->plen == 4)) return -EBADPKT; 239 | 240 | if (arp->op != ARP_OP_REQUEST && arp->op != ARP_OP_REPLY) return -EBADPKT; 241 | 242 | uint64_t src_mac = 0; 243 | memcpy(&src_mac, arp->mac_saddr, 6); 244 | ipv4_addr_t src_ip; 245 | memcpy(&src_ip, arp->ip_saddr, 4); 246 | 247 | bool is_to_me = memcmp(arp->ip_daddr, &ifinfo.ipv4, 4) == 0; 248 | 249 | // update ARP table 250 | if (!ipv4_is_unspecified(src_ip)) 251 | { 252 | ip_update_mac(ipv4_compatible(src_ip), src_mac); 253 | } 254 | 255 | // reply ARP request 256 | if (arp->op == ARP_OP_REQUEST && is_to_me) 257 | { 258 | memcpy(frame->dst_mac, &src_mac, 6); 259 | memcpy(frame->src_mac, &ifinfo.mac, 6); 260 | frame->ethertype = ETH_TYPE_ARP; 261 | 262 | arp->op = ARP_OP_REPLY; 263 | memcpy(arp->mac_saddr, &ifinfo.mac, 6); 264 | memcpy(arp->ip_saddr, &ifinfo.ipv4, 4); 265 | memcpy(arp->mac_daddr, &src_mac, 6); 266 | memcpy(arp->ip_daddr, &src_ip, 4); 267 | 268 | frame->meta.len = sizeof(eth_header_t) + sizeof(arp_packet_t); 269 | 270 | eth_begin_send_frame(frame); 271 | wait(WAIT_ETH_SEND); 272 | } 273 | 274 | return 0; 275 | } 276 | 277 | int ip_make_mld_report(frame_t *frame, size_t buflen, ipv6_addr_t maddr) 278 | { 279 | if (buflen < sizeof(frame_t) + sizeof(ipv6_header_t) 280 | + sizeof(icmpv6_header_t) + sizeof(mld_packet_t)) 281 | { 282 | return -EOOB; 283 | } 284 | 285 | uint64_t dst_mac = ipv6_multicast_mac(maddr); 286 | 287 | frame->meta.len = sizeof(eth_header_t) + sizeof(ipv6_header_t) 288 | + sizeof(icmpv6_header_t) + sizeof(mld_packet_t); 289 | memcpy(frame->dst_mac, &dst_mac, 6); 290 | memcpy(frame->src_mac, &ifinfo.mac, 6); 291 | frame->ethertype = ETH_TYPE_IPV6; 292 | 293 | ipv6_header_t *ipv6 = (ipv6_header_t *)frame->payload; 294 | ipv6->version_flow = IPV6_VERSION; 295 | ipv6->payload_len = htons(sizeof(icmpv6_header_t) + sizeof(mld_packet_t)); 296 | ipv6->next_header = IPV6_NEXT_HEADER_ICMP; 297 | ipv6->hop_limit = MLD_HOP_LIMIT; 298 | ipv6->src = ipv6_from_mac(ifinfo.mac); 299 | ipv6->dst = maddr; 300 | 301 | icmpv6_header_t *icmp = (icmpv6_header_t *)(ipv6 + 1); 302 | icmp->type_code = ICMPV6_TYPE_CODE_MLD_REPORT; 303 | icmp->checksum = 0; 304 | icmp->max_response_delay = 0; 305 | icmp->reserved1 = 0; 306 | 307 | mld_packet_t *mld = (mld_packet_t *)(icmp + 1); 308 | mld->maddr = maddr; 309 | 310 | icmp->checksum = ipv6_l4_checksum(ipv6); 311 | return 0; 312 | } 313 | 314 | int ip_make_ndp(frame_t *frame, size_t buflen, 315 | ipv6_addr_t addr, uint64_t mac, 316 | bool adv, bool unicast, uint32_t flags) 317 | { 318 | if (buflen < sizeof(frame_t) + sizeof(ipv6_header_t) 319 | + sizeof(icmpv6_header_t) + sizeof(ndp_packet_t) + sizeof(ndp_option_t)) 320 | { 321 | return -EOOB; 322 | } 323 | 324 | ipv6_addr_t dst_ip; 325 | uint64_t dst_mac; 326 | if (unicast) 327 | { 328 | dst_ip = addr; 329 | dst_mac = mac; 330 | } 331 | else 332 | { 333 | if (!adv) 334 | { 335 | dst_ip = ipv6_solicited_node(addr); 336 | } 337 | else 338 | { 339 | dst_ip = ipv6_all_nodes(); 340 | } 341 | dst_mac = ipv6_multicast_mac(dst_ip); 342 | } 343 | 344 | frame->meta.len = sizeof(eth_header_t) + sizeof(ipv6_header_t) 345 | + sizeof(icmpv6_header_t) + sizeof(ndp_packet_t) + sizeof(ndp_option_t); 346 | memcpy(frame->dst_mac, &dst_mac, 6); 347 | memcpy(frame->src_mac, &ifinfo.mac, 6); 348 | frame->ethertype = ETH_TYPE_IPV6; 349 | 350 | ipv6_header_t *ipv6 = (ipv6_header_t *)frame->payload; 351 | ipv6->version_flow = IPV6_VERSION; 352 | ipv6->payload_len = htons(sizeof(icmpv6_header_t) + sizeof(ndp_packet_t) + sizeof(ndp_option_t)); 353 | ipv6->next_header = IPV6_NEXT_HEADER_ICMP; 354 | ipv6->hop_limit = NDP_HOP_LIMIT; 355 | ipv6->src = ifinfo.ipv6; 356 | ipv6->dst = dst_ip; 357 | 358 | icmpv6_header_t *icmp = (icmpv6_header_t *)(ipv6 + 1); 359 | icmp->type_code = !adv ? ICMPV6_TYPE_CODE_NS : ICMPV6_TYPE_CODE_NA; 360 | icmp->checksum = 0; 361 | icmp->flags = adv ? (ICMPV6_FLAGS_R | flags) : 0; 362 | 363 | ndp_packet_t *ndp = (ndp_packet_t *)(icmp + 1); 364 | if (!adv) 365 | { 366 | ndp->addr = addr; 367 | } 368 | else 369 | { 370 | ndp->addr = ifinfo.ipv6; 371 | } 372 | 373 | ndp_option_t *option = (ndp_option_t *)(ndp + 1); 374 | option->type = !adv ? ICMPV6_OPTION_TYPE_SOURCE : ICMPV6_OPTION_TYPE_TARGET; 375 | option->len = 1; 376 | memcpy(option->mac_addr, &ifinfo.mac, 6); 377 | 378 | icmp->checksum = ipv6_l4_checksum(ipv6); 379 | return 0; 380 | } 381 | 382 | void ip_send_init_packets(frame_t *frame, size_t buflen) 383 | { 384 | /* 385 | System starts. 386 | Send a gratuitous ARP packet. 387 | Join ff02::1 multicast group, since this is a node. 388 | Join ff02::2 multicast group, since this is also a router. 389 | Join Solicited-Node multicast group. 390 | Send unsolicited NDP node advertisement packet. 391 | Ref: https://tools.ietf.org/html/rfc4291#page-16 392 | */ 393 | 394 | if (arp_make_request(frame, buflen, ifinfo.ipv4, -1) == 0) 395 | { 396 | eth_begin_send_frame(frame); 397 | wait(WAIT_ETH_SEND); 398 | } 399 | for (int i = 0; i < 4; ++i) 400 | { 401 | if (ip_make_mld_report(frame, buflen, ifinfo.ipv6_multicast[i]) == 0) 402 | { 403 | eth_begin_send_frame(frame); 404 | wait(WAIT_ETH_SEND); 405 | } 406 | } 407 | if (ip_make_ndp(frame, buflen, make_ipv6(0, 0), -1, true, false, ICMPV6_FLAGS_O) == 0) 408 | { 409 | eth_begin_send_frame(frame); 410 | wait(WAIT_ETH_SEND); 411 | } 412 | } 413 | 414 | int handle_frame(frame_t *frame) 415 | { 416 | bool is_to_me = (frame->dst_mac[0] & 0x01) 417 | || (memcmp(frame->dst_mac, &ifinfo.mac, 6) == 0); 418 | if (!is_to_me) return 0; 419 | 420 | switch (frame->ethertype) 421 | { 422 | case ETH_TYPE_ARP: 423 | return handle_arp(frame); 424 | break; 425 | case ETH_TYPE_IPV4: 426 | return handle_ipv4(frame); 427 | break; 428 | case ETH_TYPE_IPV6: 429 | return handle_ipv6(frame); 430 | break; 431 | default: 432 | return -ENOIMPL; 433 | break; 434 | } 435 | } 436 | 437 | bool ipv4_is_to_me(ipv4_addr_t dst) 438 | { 439 | return ipv4_eq(dst, ifinfo.ipv4) || ipv4_is_unspecified(ifinfo.ipv4); 440 | } 441 | 442 | int do_icmpv4_echo(frame_t *frame) 443 | { 444 | // special path to send icmpv4 echo reply 445 | 446 | ipv4_header_t *ipv4 = (ipv4_header_t *)frame->payload; 447 | icmpv4_header_t *icmp = (icmpv4_header_t *)(ipv4 + 1); 448 | 449 | if (icmp->code != 0) return -EBADPKT; 450 | 451 | frame->meta.len = sizeof(eth_header_t) + ntohs(ipv4->total_len); 452 | 453 | memcpy(frame->dst_mac, frame->src_mac, 6); 454 | memcpy(frame->src_mac, &ifinfo.mac, 6); 455 | 456 | ipv4->hop_limit = DEFAULT_HOP_LIMIT; 457 | 458 | ipv4_addr_t old_dst = ipv4->dst; 459 | ipv4->dst = ipv4->src; 460 | if (ipv4_is_multicast(old_dst)) 461 | { 462 | ipv4->src = ifinfo.ipv4; 463 | } 464 | else 465 | { 466 | ipv4->src = old_dst; 467 | } 468 | 469 | ipv4->checksum = 0; 470 | ipv4->checksum = ip_checksum(ipv4, sizeof(ipv4_header_t)); 471 | 472 | icmp->type = ICMPV4_TYPE_ECHO_REPLY; 473 | 474 | uint32_t checksum = ~icmp->checksum & 0xffff; 475 | checksum += (~ICMPV4_TYPE_ECHO_REQUEST & 0xffff) + ICMPV4_TYPE_ECHO_REPLY; 476 | icmp->checksum = ip_checksum_final(checksum); 477 | 478 | eth_begin_send_frame(frame); 479 | wait(WAIT_ETH_SEND); 480 | return 0; 481 | } 482 | 483 | int handle_icmpv4(frame_t *frame) 484 | { 485 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(ipv4_header_t) 486 | + sizeof(icmpv4_header_t)) return -EOOB; 487 | ipv4_header_t *ipv4 = (ipv4_header_t *)frame->payload; 488 | icmpv4_header_t *icmp = (icmpv4_header_t *)(ipv4 + 1); 489 | 490 | uint16_t checksum = ip_checksum(icmp, ntohs(ipv4->total_len) - sizeof(ipv4_header_t)); 491 | if (checksum != 0) return -EBADPKT; 492 | 493 | switch (icmp->type) 494 | { 495 | case ICMPV4_TYPE_ECHO_REQUEST: 496 | return do_icmpv4_echo(frame); 497 | break; 498 | default: 499 | return -ENOIMPL; 500 | break; 501 | } 502 | } 503 | 504 | int handle_ipv4(frame_t *frame) 505 | { 506 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(ipv4_header_t)) return -EOOB; 507 | 508 | ipv4_header_t *ipv4 = (ipv4_header_t *)frame->payload; 509 | if (ip_version(ipv4) != 4) return -EBADPKT; 510 | if ((ipv4->version_header_len & 0xf) != 0x5) return -ENOIMPL; 511 | 512 | uint16_t checksum = ip_checksum(ipv4, sizeof(ipv4_header_t)); 513 | if (checksum != 0) return -EBADPKT; 514 | 515 | if (frame->meta.len < sizeof(eth_header_t) + ntohs(ipv4->total_len)) return -EOOB; 516 | if (ntohs(ipv4->total_len) < sizeof(ipv4_header_t)) return -EBADPKT; 517 | frame->meta.len = sizeof(eth_header_t) + ntohs(ipv4->total_len); 518 | 519 | bool is_to_me = ipv4_is_to_me(ipv4->dst); 520 | if (!is_to_me) return 0; 521 | 522 | // Only receive DHCP packets before IP address gets ready. 523 | if (!ip_ready && ipv4->next_header != IP_NEXT_HEADER_UDP) return 0; 524 | 525 | switch (ipv4->next_header) 526 | { 527 | case IPV4_NEXT_HEADER_ICMP: 528 | return handle_icmpv4(frame); 529 | break; 530 | case IP_NEXT_HEADER_UDP: 531 | { 532 | uint64_t payload_len = ntohs(ipv4->total_len) - sizeof(ipv4_header_t); 533 | if (payload_len < sizeof(udp_header_t)) return -EOOB; 534 | udp_header_t *udp = (udp_header_t *)(ipv4 + 1); 535 | if (udp->checksum) 536 | { 537 | checksum = ipv4_l4_checksum(ipv4, payload_len); 538 | if (checksum != 0) return -EBADPKT; 539 | } 540 | return handle_udp(ipv4_compatible(ipv4->src), (udp_header_t *)(ipv4 + 1), 541 | frame->meta.len - (sizeof(eth_header_t) + sizeof(ipv4_header_t))); 542 | break; 543 | } 544 | default: 545 | return -ENOIMPL; 546 | break; 547 | } 548 | } 549 | 550 | bool ipv6_is_to_me(ipv6_addr_t dst) 551 | { 552 | if (ipv6_eq(dst, ifinfo.ipv6)) return true; 553 | for (int i = 0; i < 4; ++i) 554 | { 555 | if (ipv6_eq(dst, ifinfo.ipv6_multicast[i])) return true; 556 | } 557 | return false; 558 | } 559 | 560 | int handle_ndp(frame_t *frame) 561 | { 562 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(ipv6_header_t) 563 | + sizeof(icmpv6_header_t) + sizeof(ndp_packet_t)) return -EOOB; 564 | ipv6_header_t *ipv6 = (ipv6_header_t *)frame->payload; 565 | icmpv6_header_t *icmp = (icmpv6_header_t *)(ipv6 + 1); 566 | ndp_packet_t *ndp = (ndp_packet_t *)(icmp + 1); 567 | 568 | if (ipv6->hop_limit != NDP_HOP_LIMIT) return -EBADPKT; 569 | if (icmp->code != 0) return -EBADPKT; 570 | 571 | bool adv = icmp->type == ICMPV6_TYPE_NA; 572 | bool has_option = frame->meta.len >= sizeof(eth_header_t) + sizeof(ipv6_header_t) 573 | + sizeof(icmpv6_header_t) + sizeof(ndp_packet_t) + sizeof(ndp_option_t); 574 | uint64_t option_mac = 0; 575 | if (has_option) 576 | { 577 | ndp_option_t *option = (ndp_option_t *)(ndp + 1); 578 | has_option = option->len == 1 && option->type == (!adv ? ICMPV6_OPTION_TYPE_SOURCE 579 | : ICMPV6_OPTION_TYPE_TARGET); 580 | if (has_option) 581 | { 582 | memcpy(&option_mac, option->mac_addr, 6); 583 | } 584 | } 585 | 586 | uint64_t src_mac = 0; 587 | memcpy(&src_mac, frame->src_mac, 6); 588 | ipv6_addr_t src_ip = ipv6->src; 589 | ipv6_addr_t target_addr = ndp->addr; 590 | 591 | // TODO: what if has_option && src_mac != option_mac??? 592 | // TODO: what if adv && src_ip != target_addr??? 593 | 594 | if (!adv) 595 | { 596 | bool is_to_me = ipv6_eq(target_addr, ifinfo.ipv6); 597 | if (has_option && !ipv6_is_unspecified(src_ip)) 598 | { 599 | ip_update_mac(src_ip, option_mac); 600 | } 601 | 602 | if (is_to_me) 603 | { 604 | int ret; 605 | if (!ipv6_is_unspecified(src_ip)) 606 | { 607 | // FIXME: according to RFC 4861, we should either use the MAC address in ndp option, 608 | // or do Neighbor Discovery to figure out the MAC address of src_ip. 609 | // Don't use src_mac! 610 | ret = ip_make_ndp(frame, 4096, src_ip, src_mac, 611 | true, true, ICMPV6_FLAGS_S | ICMPV6_FLAGS_O); 612 | } 613 | else 614 | { 615 | ret = ip_make_ndp(frame, 4096, make_ipv6(0, 0), -1, true, false, ICMPV6_FLAGS_O); 616 | } 617 | 618 | if (ret == 0) 619 | { 620 | eth_begin_send_frame(frame); 621 | wait(WAIT_ETH_SEND); 622 | } 623 | } 624 | } 625 | else 626 | { 627 | if (has_option && (icmp->flags & (ICMPV6_FLAGS_S | ICMPV6_FLAGS_O))) 628 | { 629 | // FIXME: do not always create the ARC entry. 630 | // Only do that after some solicitation is sent. 631 | ip_update_mac(target_addr, option_mac); 632 | } 633 | else 634 | { 635 | // Update ARC entry of target_addr without changing the MAC address. 636 | ip_update_mac_atime(target_addr); 637 | } 638 | } 639 | return 0; 640 | } 641 | 642 | int do_icmpv6_echo(frame_t *frame) 643 | { 644 | // special path to send icmpv6 echo reply 645 | 646 | ipv6_header_t *ipv6 = (ipv6_header_t *)frame->payload; 647 | icmpv6_header_t *icmp = (icmpv6_header_t *)(ipv6 + 1); 648 | 649 | if (icmp->code != 0) return -EBADPKT; 650 | 651 | frame->meta.len = sizeof(eth_header_t) + sizeof(ipv6_header_t) + ntohs(ipv6->payload_len); 652 | 653 | memcpy(frame->dst_mac, frame->src_mac, 6); 654 | memcpy(frame->src_mac, &ifinfo.mac, 6); 655 | 656 | ipv6->hop_limit = DEFAULT_HOP_LIMIT; 657 | 658 | icmp->type = ICMPV6_TYPE_ECHO_REPLY; 659 | 660 | uint32_t checksum = ~icmp->checksum & 0xffff; 661 | ipv6_addr_t old_dst = ipv6->dst; 662 | ipv6->dst = ipv6->src; 663 | if (ipv6_is_multicast(old_dst)) 664 | { 665 | ipv6->src = ifinfo.ipv6; 666 | checksum += ip_checksum_neg_partial(old_dst.u8, sizeof(old_dst.u8)); 667 | checksum += ip_checksum_partial(ipv6->src.u8, sizeof(ipv6->src.u8)); 668 | } 669 | else 670 | { 671 | ipv6->src = old_dst; 672 | } 673 | 674 | checksum += (~ICMPV6_TYPE_ECHO_REQUEST & 0xffff) + ICMPV6_TYPE_ECHO_REPLY; 675 | icmp->checksum = ip_checksum_final(checksum); 676 | 677 | eth_begin_send_frame(frame); 678 | wait(WAIT_ETH_SEND); 679 | return 0; 680 | } 681 | 682 | int handle_icmpv6(frame_t *frame) 683 | { 684 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(ipv6_header_t) 685 | + sizeof(icmpv6_header_t)) return -EOOB; 686 | ipv6_header_t *ipv6 = (ipv6_header_t *)frame->payload; 687 | icmpv6_header_t *icmp = (icmpv6_header_t *)(ipv6 + 1); 688 | 689 | if (ipv6_l4_checksum(ipv6) != 0) return -EBADPKT; 690 | 691 | switch (icmp->type) 692 | { 693 | case ICMPV6_TYPE_NS: 694 | case ICMPV6_TYPE_NA: 695 | return handle_ndp(frame); 696 | break; 697 | case ICMPV6_TYPE_ECHO_REQUEST: 698 | return do_icmpv6_echo(frame); 699 | break; 700 | default: 701 | return -ENOIMPL; 702 | break; 703 | } 704 | } 705 | 706 | int handle_ipv6(frame_t *frame) 707 | { 708 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(ipv6_header_t)) return -EOOB; 709 | ipv6_header_t *ipv6 = (ipv6_header_t *)frame->payload; 710 | if (ip_version(ipv6) != 6) return -EBADPKT; 711 | if (frame->meta.len < sizeof(eth_header_t) + sizeof(ipv6_header_t) 712 | + ntohs(ipv6->payload_len)) return -EOOB; 713 | frame->meta.len = sizeof(eth_header_t) + sizeof(ipv6_header_t) 714 | + ntohs(ipv6->payload_len); 715 | 716 | bool is_to_me = ipv6_is_to_me(ipv6->dst); 717 | if (!is_to_me) return 0; 718 | 719 | switch (ipv6->next_header) 720 | { 721 | case IPV6_NEXT_HEADER_ICMP: 722 | return handle_icmpv6(frame); 723 | break; 724 | case IP_NEXT_HEADER_UDP: 725 | { 726 | uint64_t checksum = ipv6_l4_checksum(ipv6); 727 | if (checksum != 0) return -EBADPKT; 728 | return handle_udp(ipv6->src, (udp_header_t *)(ipv6 + 1), 729 | frame->meta.len - (sizeof(eth_header_t) + sizeof(ipv6_header_t))); 730 | break; 731 | } 732 | default: 733 | return -ENOIMPL; 734 | break; 735 | } 736 | } 737 | 738 | __attribute__ ((aligned (16))) uint8_t udp_buff[4096]; 739 | 740 | int send_udp(uint16_t sport, ipv6_addr_t daddr, uint16_t dport, void *buff, size_t len) 741 | { 742 | frame_t *const frame = (frame_t *)(udp_buff + ALIGN_OFFSET); 743 | const uint64_t frame_cap = sizeof(udp_buff) - ALIGN_OFFSET; 744 | 745 | if (frame_cap < sizeof(frame_t) + sizeof(ipv4_header_t) 746 | + sizeof(udp_header_t) + len) 747 | { 748 | return -EOOB; 749 | } 750 | 751 | frame->meta.len = sizeof(eth_header_t) + sizeof(ipv4_header_t) 752 | + sizeof(udp_header_t) + len; 753 | 754 | frame->ethertype = ETH_TYPE_IPV4; 755 | 756 | ipv4_header_t *ipv4 = (ipv4_header_t *)frame->payload; 757 | ipv4->version_header_len = 0x45; 758 | ipv4->dscp_ecn = 0; 759 | ipv4->total_len = htons(sizeof(ipv4_header_t) + sizeof(udp_header_t) + len); 760 | ipv4->id = 0; 761 | ipv4->flags = 0; 762 | ipv4->hop_limit = DEFAULT_HOP_LIMIT; 763 | ipv4->next_header = IP_NEXT_HEADER_UDP; 764 | ipv4->src = ifinfo.ipv4; 765 | ipv4->dst = extract_ipv4(daddr); 766 | 767 | ipv4->checksum = 0; 768 | ipv4->checksum = ip_checksum(ipv4, sizeof(ipv4_header_t)); 769 | 770 | udp_header_t *udp = (udp_header_t *)(ipv4 + 1); 771 | 772 | udp->src_port = htons(sport); 773 | udp->dst_port = htons(dport); 774 | udp->len = htons(sizeof(udp_header_t) + len); 775 | 776 | void *payload = (void *)(udp + 1); 777 | memcpy(payload, buff, len); 778 | 779 | udp->checksum = 0; 780 | udp->checksum = ipv4_l4_checksum(ipv4, sizeof(udp_header_t) + len); 781 | 782 | return ip_send(frame, daddr); // TODO: query route table 783 | } 784 | 785 | size_t recv_udp(uint16_t dport, ipv6_addr_t *saddr, uint16_t *sport, void *buff, size_t len, 786 | ipv6_addr_t expected_saddr, uint16_t expected_sport, uint64_t timeout) 787 | { 788 | current->wait_packet.is_waiting = true; 789 | current->wait_packet.dport = dport; 790 | current->wait_packet.expected_saddr = expected_saddr; 791 | current->wait_packet.expected_sport = expected_sport; 792 | current->wait_packet.saddr = saddr; 793 | current->wait_packet.sport = sport; 794 | current->wait_packet.buff = buff; 795 | current->wait_packet.len = len; 796 | size_t out_len = 0; 797 | current->wait_packet.out_len = &out_len; 798 | current->wait_packet.start = rdcycle(); 799 | current->wait_packet.delta = timeout; 800 | sched_yield(); 801 | return out_len; 802 | } 803 | 804 | int handle_udp(ipv6_addr_t saddr, udp_header_t *udp, size_t len) 805 | { 806 | if (len < sizeof(udp_header_t)) return -EOOB; 807 | size_t payload_len = ntohs(udp->len); 808 | if (len < payload_len) return -EOOB; 809 | if (payload_len < sizeof(udp_header_t)) return -EBADPKT; 810 | payload_len -= sizeof(udp_header_t); 811 | 812 | task_dispatch_udp(ntohs(udp->dst_port), saddr, ntohs(udp->src_port), 813 | (void *)(udp + 1), payload_len); 814 | return 0; 815 | } 816 | -------------------------------------------------------------------------------- /judge.c: -------------------------------------------------------------------------------- 1 | #include "judge.h" 2 | #include "arch.h" 3 | #include "arch/asm.h" 4 | #include "arch/task.h" 5 | #include "mm.h" 6 | #include "vm.h" 7 | #include "error.h" 8 | #include "hwtimer.h" 9 | #include "eth.h" 10 | #include "tftp.h" 11 | #include "task.h" 12 | #include "string.h" 13 | #include "stdio.h" 14 | #include "elf.h" 15 | 16 | __attribute__ ((aligned (16))) static uint8_t judge_tftp_buff[0x400000]; 17 | 18 | // read_block context 19 | typedef struct 20 | { 21 | pte_t *pt; 22 | uintptr_t buff; 23 | size_t len; 24 | } ctx_t; 25 | 26 | static int judge_read_block(void *ctx, void *buff) 27 | { 28 | ctx_t *c = (ctx_t *)ctx; 29 | int len = TFTP_CHUNK_SIZE; 30 | if (c->len < len) 31 | { 32 | len = c->len; 33 | } 34 | uint8_t *buff_mapped = (uint8_t *)map_page(c->pt, c->buff, VM_U | VM_R | VM_W) 35 | + (c->buff & (PGSIZE - 1)); 36 | memcpy(buff, buff_mapped, len); 37 | c->buff += len; 38 | c->len -= len; 39 | return len; 40 | } 41 | 42 | static int do_judge(judge_req_t *req, judge_resp_t *resp) 43 | { 44 | printf("Judge Daemon: Receive task %lu.\n", req->seq); 45 | 46 | size_t elf_len; 47 | int ret = tftp_get_file("elf", 3, judge_tftp_buff, sizeof(judge_tftp_buff), &elf_len); 48 | if (ret < 0) 49 | { 50 | return ret; 51 | } 52 | 53 | pte_t *pt = create_pt(); 54 | uintptr_t entry_va; 55 | if ((ret = load_elf(pt, judge_tftp_buff, elf_len, &entry_va)) < 0) 56 | { 57 | return ret; 58 | } 59 | 60 | uintptr_t elf_va = USER_TOP - PGSIZE; 61 | size_t copylen = PGSIZE; 62 | if (elf_len < copylen) copylen = elf_len; 63 | if ((ret = map_and_copy(pt, elf_va, VM_U | VM_R | VM_A, judge_tftp_buff, copylen)) < 0) 64 | { 65 | puts("out of memory.\n"); 66 | return ret; 67 | } 68 | 69 | uintptr_t phdr_va, phent, phnum; 70 | if ((ret = parse_elf_phdr(judge_tftp_buff, elf_len, &phdr_va, &phent, &phnum)) < 0) 71 | { 72 | return ret; 73 | } 74 | phdr_va += elf_va; 75 | 76 | uintptr_t stdin_len; 77 | if ((ret = tftp_get_file("stdin", 5, judge_tftp_buff, sizeof(judge_tftp_buff), &stdin_len)) < 0) 78 | { 79 | return ret; 80 | } 81 | 82 | // stdin buffer 83 | uintptr_t stdin_va = elf_va - PGALIGN(stdin_len); 84 | if ((ret = map_and_copy(pt, stdin_va, VM_U | VM_R | VM_A, judge_tftp_buff, stdin_len)) < 0) 85 | { 86 | puts("out of memory.\n"); 87 | return ret; 88 | } 89 | 90 | // stdout buffer 91 | size_t stdout_len = STDOUT_LIMIT; 92 | uintptr_t stdout_va = stdin_va - stdout_len; 93 | for (size_t pg = stdout_va; pg < stdout_va + stdout_len; pg += PGSIZE) 94 | { 95 | if (!map_page(pt, pg, VM_U | VM_R | VM_W)) 96 | { 97 | puts("out of memory.\n"); 98 | return -EOOM; 99 | } 100 | } 101 | 102 | // TODO: stderr buffer 103 | 104 | uintptr_t stack_va = stdout_va; 105 | uintptr_t stack_limit = stack_va - STACK_SIZE; 106 | for (size_t pg = stack_limit; pg < stack_va; pg += PGSIZE) 107 | { 108 | if (!map_page(pt, pg, VM_U | VM_R | VM_W)) 109 | { 110 | puts("out of memory.\n"); 111 | return -EOOM; 112 | } 113 | } 114 | 115 | size_t heap_size = HEAP_SIZE; 116 | uintptr_t heap_va = HEAP_VA; 117 | for (size_t pg = heap_va; pg < heap_va + heap_size; pg += PGSIZE) 118 | { 119 | if (!map_page(pt, pg, VM_U | VM_R | VM_W)) 120 | { 121 | puts("out of memory.\n"); 122 | return -EOOM; 123 | } 124 | } 125 | 126 | /* 127 | Setup stack arguments: 128 | aux 129 | NULL 130 | NULL (auxv[]) 131 | PGSIZE 132 | AT_PAGESZ (auxv[]) 133 | phnum 134 | AT_PHNUM (auxv[]) 135 | phent 136 | AT_PHENT (auxv[]) 137 | phdr_va 138 | AT_PHDR (auxv[]) 139 | NULL (envp[]) 140 | NULL (argv[]) 141 | 0 (argc) 142 | */ 143 | 144 | stack_va -= 0x10; 145 | uint8_t *stack = (uint8_t *)map_page(pt, stack_va, VM_U | VM_R | VM_W) + PGSIZE - 0x10; 146 | 147 | stack_va -= sizeof(aux_t); 148 | stack -= sizeof(aux_t); 149 | aux_t *aux = (aux_t *)stack; 150 | aux->stdin_buff = (uint8_t *)stdin_va; 151 | aux->stdin_len = stdin_len; 152 | aux->stdout_buff = (uint8_t *)stdout_va; 153 | aux->stdout_cap = stdout_len; 154 | aux->stdout_len = 0; 155 | aux->brk = (void *)heap_va; 156 | aux->brk_end = (void *)(heap_va + heap_size); 157 | 158 | const uintptr_t stack_word[] = 159 | { 160 | 0, 0, 0, AT_PHDR, phdr_va, AT_PHENT, phent, AT_PHNUM, phnum, AT_PAGESZ, PGSIZE, 0, 0 161 | }; 162 | stack_va -= sizeof(stack_word); 163 | stack -= sizeof(stack_word); 164 | memcpy(stack, stack_word, sizeof(stack_word)); 165 | 166 | printf("Judge Daemon: Entering user-mode.\n"); 167 | //uintptr_t freq = hwtimer_get_freq(); 168 | hwtimer_set_oneshot(req->time_limit + req->time_limit / 10 + 2000000); // +10% 169 | arch_call_user(pt, entry_va, stack_va); 170 | resp->exitcode = user_task.exitcode; 171 | resp->time_usage = user_end_time - user_start_time; 172 | resp->mem_usage = count_pt(pt, VM_U | VM_A) << PGSHIFT >> 10; 173 | printf("Judge Daemon: Left user-mode, exitcode %d, %lu cycles, %lu KiB.\n", 174 | resp->exitcode, resp->time_usage, resp->mem_usage); 175 | 176 | // Upload stdout. 177 | if (aux->stdout_len > stdout_len) 178 | { 179 | aux->stdout_len = stdout_len; 180 | } 181 | ctx_t ctx; 182 | ctx.pt = pt; 183 | ctx.buff = stdout_va; 184 | ctx.len = aux->stdout_len; 185 | ret = tftp_put_file("stdout", 6, judge_read_block, &ctx); 186 | if (ret < 0) 187 | { 188 | return ret; 189 | } 190 | 191 | resp->seq = req->seq; 192 | printf("Judge Daemon: Done task %lu.\n", resp->seq); 193 | return 0; 194 | } 195 | 196 | __attribute__ ((aligned (16))) static uint8_t judge_stack[1024]; 197 | static struct task judge_task; 198 | void judge_entry() 199 | { 200 | while (!get_ip_ready() || ipv4_is_unspecified(get_ifinfo_ipv4())) 201 | { 202 | sched_yield(); 203 | } 204 | 205 | puts("Judge Daemon: started!\n"); 206 | 207 | judge_req_t req; 208 | judge_resp_t resp; 209 | 210 | while (true) 211 | { 212 | ipv6_addr_t saddr; 213 | uint16_t sport; 214 | size_t ret = recv_udp(JUDGE_SERVER_PORT, &saddr, &sport, &req, sizeof(req), 215 | ipv6_unspecified(), 0, 1 * CLOCK_FREQ); 216 | if (ret == 0) 217 | { 218 | continue; 219 | } 220 | 221 | if (ret != sizeof(req)) 222 | { 223 | printf("Judge Daemon: Bad request.\n"); 224 | continue; 225 | } 226 | 227 | memset(&resp, 0, sizeof(resp)); 228 | resp.seq = req.seq; 229 | resp.flags = JUDGE_RESP_ACK; 230 | send_udp(JUDGE_SERVER_PORT, saddr, sport, &resp, sizeof(resp)); 231 | 232 | mm_take_snapshot(); 233 | resp.error = do_judge(&req, &resp); 234 | mm_restore_snapshot(); 235 | 236 | resp.flags = JUDGE_RESP_ACK | JUDGE_RESP_RESULT; 237 | send_udp(JUDGE_SERVER_PORT, saddr, sport, &resp, sizeof(resp)); 238 | } 239 | } 240 | 241 | void init_judge() 242 | { 243 | create_task(&judge_task, 244 | (uintptr_t)judge_stack + sizeof(judge_stack) + STACK_OFFSET, 245 | judge_entry); 246 | } 247 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "arch/asm.h" 2 | #include "arch.h" 3 | #include "mm.h" 4 | #include "stdio.h" 5 | #include "string.h" 6 | #include "hwtimer.h" 7 | #include "tty.h" 8 | #include "eth.h" 9 | #include "task.h" 10 | #include "ip.h" 11 | #include "dhcp.h" 12 | #include "judge.h" 13 | 14 | void init_bss() 15 | { 16 | extern uint8_t _bss_begin[]; 17 | extern uint8_t _bss_end[]; 18 | memset(_bss_begin, 0, (uintptr_t)_bss_end - (uintptr_t)_bss_begin); 19 | } 20 | 21 | void start(int hartid, void *dtb) 22 | { 23 | fence(); 24 | fence_i(); 25 | 26 | init_bss(); 27 | 28 | init_arch(); 29 | init_arch_dev(hartid, dtb); 30 | 31 | init_hwtimer(); 32 | init_tty(); 33 | init_eth(); 34 | init_task(); 35 | init_ip(); 36 | 37 | printf("***********************************************\n" 38 | "* Vijos: Vijos Isn't Just an Operating System *\n" 39 | "***********************************************\n"); 40 | mm_print(); 41 | printf("dtb is at 0x%016lx\n", (uintptr_t)dtb); 42 | printf("rdcycle: %lu, mtime: %lu\n", rdcycle(), hwtimer_get()); 43 | printf("rdcycle: %lu, mtime: %lu\n", rdcycle(), hwtimer_get()); 44 | 45 | init_dhcp(); 46 | init_judge(); 47 | idle_entry(); 48 | } 49 | -------------------------------------------------------------------------------- /mm.c: -------------------------------------------------------------------------------- 1 | #include "mm.h" 2 | #include "arch/mmu.h" 3 | #include "utils.h" 4 | #include "string.h" 5 | #include "stdio.h" 6 | 7 | static mm_bitmap_t *mmbm; 8 | static mm_bitmap_t *mmbm_snapshot; 9 | 10 | static size_t mmbm_size() 11 | { 12 | return sizeof(mm_bitmap_t) + ALIGN(mmbm->size >> 3, 8ul); 13 | } 14 | 15 | static size_t mmbm_snapshot_size() 16 | { 17 | return sizeof(mm_bitmap_t) + ALIGN(mmbm_snapshot->size >> 3, 8ul); 18 | } 19 | 20 | void init_mm() 21 | { 22 | extern uint8_t _bss_end[]; 23 | mmbm = (mm_bitmap_t *)ALIGN((uintptr_t)_bss_end, 0x10ul); 24 | memset(mmbm, 0, sizeof(mm_bitmap_t)); 25 | } 26 | 27 | void add_pages(uintptr_t start, size_t n) 28 | { 29 | uintptr_t last_added = mmbm->size; 30 | start >>= PGSHIFT; 31 | if (start < last_added) 32 | { 33 | printf("Error: Adding previous added pages.\n"); 34 | while (true); 35 | } 36 | mmbm->size = start + n; 37 | 38 | uintptr_t last_nblocks = last_added >> 6; 39 | uintptr_t nblocks = mmbm->size >> 6; 40 | memset(&mmbm->bits[last_nblocks], 0, (nblocks - last_nblocks) * sizeof(mmbm->bits[0])); 41 | // Take [last_added, start) 42 | take_pages(last_added << PGSHIFT, start - last_added); 43 | } 44 | 45 | void take_pages(uintptr_t start, size_t n) 46 | { 47 | uintptr_t start_pg = start >> PGSHIFT; 48 | for (size_t i = 0; i < n; ++i) 49 | { 50 | uintptr_t pg = i + start_pg; 51 | size_t index = pg >> 6; 52 | uint64_t mask = 1ul << (pg & 63); 53 | if (!(mmbm->bits[index] & mask)) 54 | { 55 | mmbm->bits[index] |= mask; 56 | ++mmbm->used; 57 | } 58 | } 59 | } 60 | 61 | void finish_add_pages() 62 | { 63 | mmbm_snapshot = (mm_bitmap_t *)((uintptr_t)mmbm + mmbm_size()); 64 | uintptr_t available_start = PGALIGN((uintptr_t)mmbm_snapshot + mmbm_size()); 65 | take_pages(0, available_start >> PGSHIFT); 66 | } 67 | 68 | uintptr_t get_free_page() 69 | { 70 | if (mmbm->used == mmbm->size) 71 | { 72 | return 0; 73 | } 74 | size_t nblocks = mmbm->size >> 6; 75 | size_t index = mmbm->next; 76 | for (size_t i = 0; i < nblocks; ++i) 77 | { 78 | if (mmbm->bits[index] != -1ul) 79 | { 80 | size_t offset = bits_find_zero(mmbm->bits[index]); 81 | mmbm->bits[index] |= 1ul << offset; 82 | mmbm->next = index; 83 | ++mmbm->used; 84 | return ((index << 6) | offset) << PGSHIFT; 85 | } 86 | if (index == nblocks - 1) index = 0; 87 | else ++index; 88 | } 89 | return 0; 90 | } 91 | 92 | void mm_take_snapshot() 93 | { 94 | memcpy(mmbm_snapshot, mmbm, mmbm_size()); 95 | } 96 | 97 | void mm_restore_snapshot() 98 | { 99 | memcpy(mmbm, mmbm_snapshot, mmbm_snapshot_size()); 100 | } 101 | 102 | static bool mm_is_in_use(uintptr_t pg) 103 | { 104 | uintptr_t n = pg >> PGSHIFT; 105 | size_t index = n >> 6; 106 | uint64_t mask = 1ul << (n & 63); 107 | return mmbm->bits[index] & mask; 108 | } 109 | 110 | void mm_print() 111 | { 112 | printf("Memory Status:\n"); 113 | printf(" Metadata is at 0x%016lx\n", (uintptr_t)mmbm); 114 | printf(" Metadata snapshot is at 0x%016lx\n", (uintptr_t)mmbm_snapshot); 115 | printf(" Metadata: %lu bytes\n", mmbm_size()); 116 | printf(" Total Memory: % 12lu bytes\n", mmbm->size << PGSHIFT); 117 | printf(" Used Memory: % 12lu bytes\n", mmbm->used << PGSHIFT); 118 | printf(" Free Memory: % 12lu bytes\n", (mmbm->size - mmbm->used) << PGSHIFT); 119 | uintptr_t start = 0; 120 | bool start_in_use = mm_is_in_use(0); 121 | for (uintptr_t pg = 0; pg < mmbm->size; ++pg) 122 | { 123 | bool in_use = mm_is_in_use(pg << PGSHIFT); 124 | if (in_use != start_in_use) 125 | { 126 | printf(" [0x%016lx - 0x%016lx]: %s\n", start << PGSHIFT, (pg << PGSHIFT) - 1, 127 | start_in_use ? "In Use" : "Free"); 128 | start = pg; 129 | start_in_use = in_use; 130 | } 131 | } 132 | printf(" [0x%016lx - 0x%016lx]: %s\n", start << PGSHIFT, (mmbm->size << PGSHIFT) - 1, 133 | start_in_use ? "In Use" : "Free"); 134 | } 135 | -------------------------------------------------------------------------------- /printf.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // \author (c) Marco Paland (info@paland.com) 3 | // 2014-2019, PALANDesign Hannover, Germany 4 | // 5 | // \license The MIT License (MIT) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on 26 | // embedded systems with a very limited resources. These routines are thread 27 | // safe and reentrant! 28 | // Use this instead of the bloated standard/newlib printf cause these use 29 | // malloc for printf (and may not be thread safe). 30 | // 31 | /////////////////////////////////////////////////////////////////////////////// 32 | 33 | #include 34 | #include 35 | 36 | #include "printf.h" 37 | 38 | 39 | // define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the 40 | // printf_config.h header file 41 | // default: undefined 42 | #ifdef PRINTF_INCLUDE_CONFIG_H 43 | #include "printf_config.h" 44 | #endif 45 | 46 | 47 | // 'ntoa' conversion buffer size, this must be big enough to hold one converted 48 | // numeric number including padded zeros (dynamically created on stack) 49 | // default: 32 byte 50 | #ifndef PRINTF_NTOA_BUFFER_SIZE 51 | #define PRINTF_NTOA_BUFFER_SIZE 32U 52 | #endif 53 | 54 | // 'ftoa' conversion buffer size, this must be big enough to hold one converted 55 | // float number including padded zeros (dynamically created on stack) 56 | // default: 32 byte 57 | #ifndef PRINTF_FTOA_BUFFER_SIZE 58 | #define PRINTF_FTOA_BUFFER_SIZE 32U 59 | #endif 60 | 61 | // support for the floating point type (%f) 62 | // default: activated 63 | #ifndef PRINTF_DISABLE_SUPPORT_FLOAT 64 | #define PRINTF_SUPPORT_FLOAT 65 | #endif 66 | 67 | // support for exponential floating point notation (%e/%g) 68 | // default: activated 69 | #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL 70 | #define PRINTF_SUPPORT_EXPONENTIAL 71 | #endif 72 | 73 | // define the default floating point precision 74 | // default: 6 digits 75 | #ifndef PRINTF_DEFAULT_FLOAT_PRECISION 76 | #define PRINTF_DEFAULT_FLOAT_PRECISION 6U 77 | #endif 78 | 79 | // define the largest float suitable to print with %f 80 | // default: 1e9 81 | #ifndef PRINTF_MAX_FLOAT 82 | #define PRINTF_MAX_FLOAT 1e9 83 | #endif 84 | 85 | // support for the long long types (%llu or %p) 86 | // default: activated 87 | #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG 88 | #define PRINTF_SUPPORT_LONG_LONG 89 | #endif 90 | 91 | // support for the ptrdiff_t type (%t) 92 | // ptrdiff_t is normally defined in as long or long long type 93 | // default: activated 94 | #ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T 95 | #define PRINTF_SUPPORT_PTRDIFF_T 96 | #endif 97 | 98 | /////////////////////////////////////////////////////////////////////////////// 99 | 100 | // internal flag definitions 101 | #define FLAGS_ZEROPAD (1U << 0U) 102 | #define FLAGS_LEFT (1U << 1U) 103 | #define FLAGS_PLUS (1U << 2U) 104 | #define FLAGS_SPACE (1U << 3U) 105 | #define FLAGS_HASH (1U << 4U) 106 | #define FLAGS_UPPERCASE (1U << 5U) 107 | #define FLAGS_CHAR (1U << 6U) 108 | #define FLAGS_SHORT (1U << 7U) 109 | #define FLAGS_LONG (1U << 8U) 110 | #define FLAGS_LONG_LONG (1U << 9U) 111 | #define FLAGS_PRECISION (1U << 10U) 112 | #define FLAGS_ADAPT_EXP (1U << 11U) 113 | 114 | 115 | // import float.h for DBL_MAX 116 | #if defined(PRINTF_SUPPORT_FLOAT) 117 | #include 118 | #endif 119 | 120 | 121 | // output function type 122 | typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); 123 | 124 | 125 | // wrapper (used as buffer) for output function type 126 | typedef struct { 127 | void (*fct)(char character, void* arg); 128 | void* arg; 129 | } out_fct_wrap_type; 130 | 131 | 132 | // internal buffer output 133 | static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) 134 | { 135 | if (idx < maxlen) { 136 | ((char*)buffer)[idx] = character; 137 | } 138 | } 139 | 140 | 141 | // internal null output 142 | static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) 143 | { 144 | (void)character; (void)buffer; (void)idx; (void)maxlen; 145 | } 146 | 147 | 148 | // internal _putchar wrapper 149 | static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) 150 | { 151 | (void)buffer; (void)idx; (void)maxlen; 152 | if (character) { 153 | _putchar(character); 154 | } 155 | } 156 | 157 | 158 | // internal output function wrapper 159 | static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) 160 | { 161 | (void)idx; (void)maxlen; 162 | if (character) { 163 | // buffer is the output fct pointer 164 | ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); 165 | } 166 | } 167 | 168 | 169 | // internal secure strlen 170 | // \return The length of the string (excluding the terminating 0) limited by 'maxsize' 171 | static inline unsigned int _strnlen_s(const char* str, size_t maxsize) 172 | { 173 | const char* s; 174 | for (s = str; *s && maxsize--; ++s); 175 | return (unsigned int)(s - str); 176 | } 177 | 178 | 179 | // internal test if char is a digit (0-9) 180 | // \return true if char is a digit 181 | static inline bool _is_digit(char ch) 182 | { 183 | return (ch >= '0') && (ch <= '9'); 184 | } 185 | 186 | 187 | // internal ASCII string to unsigned int conversion 188 | static unsigned int _atoi(const char** str) 189 | { 190 | unsigned int i = 0U; 191 | while (_is_digit(**str)) { 192 | i = i * 10U + (unsigned int)(*((*str)++) - '0'); 193 | } 194 | return i; 195 | } 196 | 197 | 198 | // output the specified string in reverse, taking care of any zero-padding 199 | static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) 200 | { 201 | const size_t start_idx = idx; 202 | 203 | // pad spaces up to given width 204 | if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { 205 | for (size_t i = len; i < width; i++) { 206 | out(' ', buffer, idx++, maxlen); 207 | } 208 | } 209 | 210 | // reverse string 211 | while (len) { 212 | out(buf[--len], buffer, idx++, maxlen); 213 | } 214 | 215 | // append pad spaces up to given width 216 | if (flags & FLAGS_LEFT) { 217 | while (idx - start_idx < width) { 218 | out(' ', buffer, idx++, maxlen); 219 | } 220 | } 221 | 222 | return idx; 223 | } 224 | 225 | 226 | // internal itoa format 227 | static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) 228 | { 229 | // pad leading zeros 230 | if (!(flags & FLAGS_LEFT)) { 231 | if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { 232 | width--; 233 | } 234 | while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 235 | buf[len++] = '0'; 236 | } 237 | while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 238 | buf[len++] = '0'; 239 | } 240 | } 241 | 242 | // handle hash 243 | if (flags & FLAGS_HASH) { 244 | if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { 245 | len--; 246 | if (len && (base == 16U)) { 247 | len--; 248 | } 249 | } 250 | if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 251 | buf[len++] = 'x'; 252 | } 253 | else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 254 | buf[len++] = 'X'; 255 | } 256 | else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 257 | buf[len++] = 'b'; 258 | } 259 | if (len < PRINTF_NTOA_BUFFER_SIZE) { 260 | buf[len++] = '0'; 261 | } 262 | } 263 | 264 | if (len < PRINTF_NTOA_BUFFER_SIZE) { 265 | if (negative) { 266 | buf[len++] = '-'; 267 | } 268 | else if (flags & FLAGS_PLUS) { 269 | buf[len++] = '+'; // ignore the space if the '+' exists 270 | } 271 | else if (flags & FLAGS_SPACE) { 272 | buf[len++] = ' '; 273 | } 274 | } 275 | 276 | return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); 277 | } 278 | 279 | 280 | // internal itoa for 'long' type 281 | static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) 282 | { 283 | char buf[PRINTF_NTOA_BUFFER_SIZE]; 284 | size_t len = 0U; 285 | 286 | // no hash for 0 values 287 | if (!value) { 288 | flags &= ~FLAGS_HASH; 289 | } 290 | 291 | // write if precision != 0 and value is != 0 292 | if (!(flags & FLAGS_PRECISION) || value) { 293 | do { 294 | const char digit = (char)(value % base); 295 | buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; 296 | value /= base; 297 | } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); 298 | } 299 | 300 | return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); 301 | } 302 | 303 | 304 | // internal itoa for 'long long' type 305 | #if defined(PRINTF_SUPPORT_LONG_LONG) 306 | static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) 307 | { 308 | char buf[PRINTF_NTOA_BUFFER_SIZE]; 309 | size_t len = 0U; 310 | 311 | // no hash for 0 values 312 | if (!value) { 313 | flags &= ~FLAGS_HASH; 314 | } 315 | 316 | // write if precision != 0 and value is != 0 317 | if (!(flags & FLAGS_PRECISION) || value) { 318 | do { 319 | const char digit = (char)(value % base); 320 | buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; 321 | value /= base; 322 | } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); 323 | } 324 | 325 | return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); 326 | } 327 | #endif // PRINTF_SUPPORT_LONG_LONG 328 | 329 | 330 | #if defined(PRINTF_SUPPORT_FLOAT) 331 | 332 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 333 | // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT 334 | static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); 335 | #endif 336 | 337 | 338 | // internal ftoa for fixed decimal floating point 339 | static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) 340 | { 341 | char buf[PRINTF_FTOA_BUFFER_SIZE]; 342 | size_t len = 0U; 343 | double diff = 0.0; 344 | 345 | // powers of 10 346 | static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; 347 | 348 | // test for special values 349 | if (value != value) 350 | return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); 351 | if (value < -DBL_MAX) 352 | return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); 353 | if (value > DBL_MAX) 354 | return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); 355 | 356 | // test for very large values 357 | // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad 358 | if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { 359 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 360 | return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); 361 | #else 362 | return 0U; 363 | #endif 364 | } 365 | 366 | // test for negative 367 | bool negative = false; 368 | if (value < 0) { 369 | negative = true; 370 | value = 0 - value; 371 | } 372 | 373 | // set default precision, if not set explicitly 374 | if (!(flags & FLAGS_PRECISION)) { 375 | prec = PRINTF_DEFAULT_FLOAT_PRECISION; 376 | } 377 | // limit precision to 9, cause a prec >= 10 can lead to overflow errors 378 | while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { 379 | buf[len++] = '0'; 380 | prec--; 381 | } 382 | 383 | int whole = (int)value; 384 | double tmp = (value - whole) * pow10[prec]; 385 | unsigned long frac = (unsigned long)tmp; 386 | diff = tmp - frac; 387 | 388 | if (diff > 0.5) { 389 | ++frac; 390 | // handle rollover, e.g. case 0.99 with prec 1 is 1.0 391 | if (frac >= pow10[prec]) { 392 | frac = 0; 393 | ++whole; 394 | } 395 | } 396 | else if (diff < 0.5) { 397 | } 398 | else if ((frac == 0U) || (frac & 1U)) { 399 | // if halfway, round up if odd OR if last digit is 0 400 | ++frac; 401 | } 402 | 403 | if (prec == 0U) { 404 | diff = value - (double)whole; 405 | if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { 406 | // exactly 0.5 and ODD, then round up 407 | // 1.5 -> 2, but 2.5 -> 2 408 | ++whole; 409 | } 410 | } 411 | else { 412 | unsigned int count = prec; 413 | // now do fractional part, as an unsigned number 414 | while (len < PRINTF_FTOA_BUFFER_SIZE) { 415 | --count; 416 | buf[len++] = (char)(48U + (frac % 10U)); 417 | if (!(frac /= 10U)) { 418 | break; 419 | } 420 | } 421 | // add extra 0s 422 | while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { 423 | buf[len++] = '0'; 424 | } 425 | if (len < PRINTF_FTOA_BUFFER_SIZE) { 426 | // add decimal 427 | buf[len++] = '.'; 428 | } 429 | } 430 | 431 | // do whole part, number is reversed 432 | while (len < PRINTF_FTOA_BUFFER_SIZE) { 433 | buf[len++] = (char)(48 + (whole % 10)); 434 | if (!(whole /= 10)) { 435 | break; 436 | } 437 | } 438 | 439 | // pad leading zeros 440 | if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { 441 | if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { 442 | width--; 443 | } 444 | while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { 445 | buf[len++] = '0'; 446 | } 447 | } 448 | 449 | if (len < PRINTF_FTOA_BUFFER_SIZE) { 450 | if (negative) { 451 | buf[len++] = '-'; 452 | } 453 | else if (flags & FLAGS_PLUS) { 454 | buf[len++] = '+'; // ignore the space if the '+' exists 455 | } 456 | else if (flags & FLAGS_SPACE) { 457 | buf[len++] = ' '; 458 | } 459 | } 460 | 461 | return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); 462 | } 463 | 464 | 465 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 466 | // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse 467 | static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) 468 | { 469 | // check for NaN and special values 470 | if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { 471 | return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); 472 | } 473 | 474 | // determine the sign 475 | const bool negative = value < 0; 476 | if (negative) { 477 | value = -value; 478 | } 479 | 480 | // default precision 481 | if (!(flags & FLAGS_PRECISION)) { 482 | prec = PRINTF_DEFAULT_FLOAT_PRECISION; 483 | } 484 | 485 | // determine the decimal exponent 486 | // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) 487 | union { 488 | uint64_t U; 489 | double F; 490 | } conv; 491 | 492 | conv.F = value; 493 | int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 494 | conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) 495 | // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 496 | int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); 497 | // now we want to compute 10^expval but we want to be sure it won't overflow 498 | exp2 = (int)(expval * 3.321928094887362 + 0.5); 499 | const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; 500 | const double z2 = z * z; 501 | conv.U = (uint64_t)(exp2 + 1023) << 52U; 502 | // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex 503 | conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); 504 | // correct for rounding errors 505 | if (value < conv.F) { 506 | expval--; 507 | conv.F /= 10; 508 | } 509 | 510 | // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters 511 | unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; 512 | 513 | // in "%g" mode, "prec" is the number of *significant figures* not decimals 514 | if (flags & FLAGS_ADAPT_EXP) { 515 | // do we want to fall-back to "%f" mode? 516 | if ((value >= 1e-4) && (value < 1e6)) { 517 | if ((int)prec > expval) { 518 | prec = (unsigned)((int)prec - expval - 1); 519 | } 520 | else { 521 | prec = 0; 522 | } 523 | flags |= FLAGS_PRECISION; // make sure _ftoa respects precision 524 | // no characters in exponent 525 | minwidth = 0U; 526 | expval = 0; 527 | } 528 | else { 529 | // we use one sigfig for the whole part 530 | if ((prec > 0) && (flags & FLAGS_PRECISION)) { 531 | --prec; 532 | } 533 | } 534 | } 535 | 536 | // will everything fit? 537 | unsigned int fwidth = width; 538 | if (width > minwidth) { 539 | // we didn't fall-back so subtract the characters required for the exponent 540 | fwidth -= minwidth; 541 | } else { 542 | // not enough characters, so go back to default sizing 543 | fwidth = 0U; 544 | } 545 | if ((flags & FLAGS_LEFT) && minwidth) { 546 | // if we're padding on the right, DON'T pad the floating part 547 | fwidth = 0U; 548 | } 549 | 550 | // rescale the float value 551 | if (expval) { 552 | value /= conv.F; 553 | } 554 | 555 | // output the floating part 556 | const size_t start_idx = idx; 557 | idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); 558 | 559 | // output the exponent part 560 | if (minwidth) { 561 | // output the exponential symbol 562 | out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); 563 | // output the exponent value 564 | idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); 565 | // might need to right-pad spaces 566 | if (flags & FLAGS_LEFT) { 567 | while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); 568 | } 569 | } 570 | return idx; 571 | } 572 | #endif // PRINTF_SUPPORT_EXPONENTIAL 573 | #endif // PRINTF_SUPPORT_FLOAT 574 | 575 | 576 | // internal vsnprintf 577 | static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) 578 | { 579 | unsigned int flags, width, precision, n; 580 | size_t idx = 0U; 581 | 582 | if (!buffer) { 583 | // use null output function 584 | out = _out_null; 585 | } 586 | 587 | while (*format) 588 | { 589 | // format specifier? %[flags][width][.precision][length] 590 | if (*format != '%') { 591 | // no 592 | out(*format, buffer, idx++, maxlen); 593 | format++; 594 | continue; 595 | } 596 | else { 597 | // yes, evaluate it 598 | format++; 599 | } 600 | 601 | // evaluate flags 602 | flags = 0U; 603 | do { 604 | switch (*format) { 605 | case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; 606 | case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; 607 | case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; 608 | case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; 609 | case '#': flags |= FLAGS_HASH; format++; n = 1U; break; 610 | default : n = 0U; break; 611 | } 612 | } while (n); 613 | 614 | // evaluate width field 615 | width = 0U; 616 | if (_is_digit(*format)) { 617 | width = _atoi(&format); 618 | } 619 | else if (*format == '*') { 620 | const int w = va_arg(va, int); 621 | if (w < 0) { 622 | flags |= FLAGS_LEFT; // reverse padding 623 | width = (unsigned int)-w; 624 | } 625 | else { 626 | width = (unsigned int)w; 627 | } 628 | format++; 629 | } 630 | 631 | // evaluate precision field 632 | precision = 0U; 633 | if (*format == '.') { 634 | flags |= FLAGS_PRECISION; 635 | format++; 636 | if (_is_digit(*format)) { 637 | precision = _atoi(&format); 638 | } 639 | else if (*format == '*') { 640 | const int prec = (int)va_arg(va, int); 641 | precision = prec > 0 ? (unsigned int)prec : 0U; 642 | format++; 643 | } 644 | } 645 | 646 | // evaluate length field 647 | switch (*format) { 648 | case 'l' : 649 | flags |= FLAGS_LONG; 650 | format++; 651 | if (*format == 'l') { 652 | flags |= FLAGS_LONG_LONG; 653 | format++; 654 | } 655 | break; 656 | case 'h' : 657 | flags |= FLAGS_SHORT; 658 | format++; 659 | if (*format == 'h') { 660 | flags |= FLAGS_CHAR; 661 | format++; 662 | } 663 | break; 664 | #if defined(PRINTF_SUPPORT_PTRDIFF_T) 665 | case 't' : 666 | flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 667 | format++; 668 | break; 669 | #endif 670 | case 'j' : 671 | flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 672 | format++; 673 | break; 674 | case 'z' : 675 | flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 676 | format++; 677 | break; 678 | default : 679 | break; 680 | } 681 | 682 | // evaluate specifier 683 | switch (*format) { 684 | case 'd' : 685 | case 'i' : 686 | case 'u' : 687 | case 'x' : 688 | case 'X' : 689 | case 'o' : 690 | case 'b' : { 691 | // set the base 692 | unsigned int base; 693 | if (*format == 'x' || *format == 'X') { 694 | base = 16U; 695 | } 696 | else if (*format == 'o') { 697 | base = 8U; 698 | } 699 | else if (*format == 'b') { 700 | base = 2U; 701 | } 702 | else { 703 | base = 10U; 704 | flags &= ~FLAGS_HASH; // no hash for dec format 705 | } 706 | // uppercase 707 | if (*format == 'X') { 708 | flags |= FLAGS_UPPERCASE; 709 | } 710 | 711 | // no plus or space flag for u, x, X, o, b 712 | if ((*format != 'i') && (*format != 'd')) { 713 | flags &= ~(FLAGS_PLUS | FLAGS_SPACE); 714 | } 715 | 716 | // ignore '0' flag when precision is given 717 | if (flags & FLAGS_PRECISION) { 718 | flags &= ~FLAGS_ZEROPAD; 719 | } 720 | 721 | // convert the integer 722 | if ((*format == 'i') || (*format == 'd')) { 723 | // signed 724 | if (flags & FLAGS_LONG_LONG) { 725 | #if defined(PRINTF_SUPPORT_LONG_LONG) 726 | const long long value = va_arg(va, long long); 727 | idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); 728 | #endif 729 | } 730 | else if (flags & FLAGS_LONG) { 731 | const long value = va_arg(va, long); 732 | idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); 733 | } 734 | else { 735 | const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); 736 | idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); 737 | } 738 | } 739 | else { 740 | // unsigned 741 | if (flags & FLAGS_LONG_LONG) { 742 | #if defined(PRINTF_SUPPORT_LONG_LONG) 743 | idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); 744 | #endif 745 | } 746 | else if (flags & FLAGS_LONG) { 747 | idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); 748 | } 749 | else { 750 | const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); 751 | idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); 752 | } 753 | } 754 | format++; 755 | break; 756 | } 757 | #if defined(PRINTF_SUPPORT_FLOAT) 758 | case 'f' : 759 | case 'F' : 760 | if (*format == 'F') flags |= FLAGS_UPPERCASE; 761 | idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); 762 | format++; 763 | break; 764 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 765 | case 'e': 766 | case 'E': 767 | case 'g': 768 | case 'G': 769 | if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; 770 | if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; 771 | idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); 772 | format++; 773 | break; 774 | #endif // PRINTF_SUPPORT_EXPONENTIAL 775 | #endif // PRINTF_SUPPORT_FLOAT 776 | case 'c' : { 777 | unsigned int l = 1U; 778 | // pre padding 779 | if (!(flags & FLAGS_LEFT)) { 780 | while (l++ < width) { 781 | out(' ', buffer, idx++, maxlen); 782 | } 783 | } 784 | // char output 785 | out((char)va_arg(va, int), buffer, idx++, maxlen); 786 | // post padding 787 | if (flags & FLAGS_LEFT) { 788 | while (l++ < width) { 789 | out(' ', buffer, idx++, maxlen); 790 | } 791 | } 792 | format++; 793 | break; 794 | } 795 | 796 | case 's' : { 797 | const char* p = va_arg(va, char*); 798 | unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); 799 | // pre padding 800 | if (flags & FLAGS_PRECISION) { 801 | l = (l < precision ? l : precision); 802 | } 803 | if (!(flags & FLAGS_LEFT)) { 804 | while (l++ < width) { 805 | out(' ', buffer, idx++, maxlen); 806 | } 807 | } 808 | // string output 809 | while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { 810 | out(*(p++), buffer, idx++, maxlen); 811 | } 812 | // post padding 813 | if (flags & FLAGS_LEFT) { 814 | while (l++ < width) { 815 | out(' ', buffer, idx++, maxlen); 816 | } 817 | } 818 | format++; 819 | break; 820 | } 821 | 822 | case 'p' : { 823 | width = sizeof(void*) * 2U; 824 | flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; 825 | #if defined(PRINTF_SUPPORT_LONG_LONG) 826 | const bool is_ll = sizeof(uintptr_t) == sizeof(long long); 827 | if (is_ll) { 828 | idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); 829 | } 830 | else { 831 | #endif 832 | idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); 833 | #if defined(PRINTF_SUPPORT_LONG_LONG) 834 | } 835 | #endif 836 | format++; 837 | break; 838 | } 839 | 840 | case '%' : 841 | out('%', buffer, idx++, maxlen); 842 | format++; 843 | break; 844 | 845 | default : 846 | out(*format, buffer, idx++, maxlen); 847 | format++; 848 | break; 849 | } 850 | } 851 | 852 | // termination 853 | out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); 854 | 855 | // return written chars without terminating \0 856 | return (int)idx; 857 | } 858 | 859 | 860 | /////////////////////////////////////////////////////////////////////////////// 861 | 862 | int printf_(const char* format, ...) 863 | { 864 | va_list va; 865 | va_start(va, format); 866 | char buffer[1]; 867 | const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); 868 | va_end(va); 869 | return ret; 870 | } 871 | 872 | 873 | int sprintf_(char* buffer, const char* format, ...) 874 | { 875 | va_list va; 876 | va_start(va, format); 877 | const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); 878 | va_end(va); 879 | return ret; 880 | } 881 | 882 | 883 | int snprintf_(char* buffer, size_t count, const char* format, ...) 884 | { 885 | va_list va; 886 | va_start(va, format); 887 | const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); 888 | va_end(va); 889 | return ret; 890 | } 891 | 892 | 893 | int vprintf_(const char* format, va_list va) 894 | { 895 | char buffer[1]; 896 | return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); 897 | } 898 | 899 | 900 | int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) 901 | { 902 | return _vsnprintf(_out_buffer, buffer, count, format, va); 903 | } 904 | 905 | 906 | int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) 907 | { 908 | va_list va; 909 | va_start(va, format); 910 | const out_fct_wrap_type out_fct_wrap = { out, arg }; 911 | const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); 912 | va_end(va); 913 | return ret; 914 | } 915 | -------------------------------------------------------------------------------- /riscv_soc.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vijos/vijosos/f242a71cb68438d0260cee06694ed6d61a9e52db/riscv_soc.dtb -------------------------------------------------------------------------------- /start_fileserver: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo atftpd --daemon --no-fork --port 6969 --no-source-port-checking --logfile - tftp_root 4 | -------------------------------------------------------------------------------- /stdin.txt: -------------------------------------------------------------------------------- 1 | 2 123 500 2 | hello, world 3 | -------------------------------------------------------------------------------- /string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | int strcmp(const char *a, const char *b) 4 | { 5 | while (*a && *b) 6 | { 7 | if (*a != *b) return 1; 8 | ++a; 9 | ++b; 10 | } 11 | return !(*a == 0 && *b == 0); 12 | } 13 | 14 | void memset(void *dst, uint8_t b, size_t len) 15 | { 16 | uint8_t *u8 = (uint8_t *)dst; 17 | for (size_t i = 0; i < len; ++i) u8[i] = b; 18 | } 19 | 20 | int memcmp(const void *a, const void *b, size_t len) 21 | { 22 | uint8_t *u8a = (uint8_t *)a, *u8b = (uint8_t *)b; 23 | for (size_t i = 0; i < len; ++i) if (u8a[i] != u8b[i]) return 1; 24 | return 0; 25 | } 26 | 27 | void memcpy(void *dst, const void *src, size_t len) 28 | { 29 | uint8_t *dst8 = (uint8_t *)dst, *src8 = (uint8_t *)src; 30 | for (size_t i = 0; i < len; ++i) *dst8++ = *src8++; 31 | } 32 | -------------------------------------------------------------------------------- /task.c: -------------------------------------------------------------------------------- 1 | #include "task.h" 2 | #include "tty.h" 3 | #include "eth.h" 4 | #include "timer.h" 5 | #include "string.h" 6 | 7 | static struct task _idle_task; 8 | struct task *const idle_task = &_idle_task; 9 | 10 | static struct task *tasks[NUM_TASKS]; 11 | static size_t num_task = 0; 12 | static size_t num_waiting_task = 0; 13 | 14 | void init_task() 15 | { 16 | create_task(idle_task, 0 /* don't care */, 0 /* don't care */); 17 | set_current(idle_task); 18 | } 19 | 20 | void create_task(struct task *task, uintptr_t stack, void *entry) 21 | { 22 | task->regs.STACK_REG = stack; 23 | task->regs.RETURN_ADDR_REG = (uintptr_t)entry; 24 | task->regs.THREAD_POINTER_REG = (uintptr_t)task; 25 | task->timer.timeout = 1; 26 | task->wait_packet.is_waiting = false; 27 | add_task(task); 28 | } 29 | 30 | void add_task(struct task *task) 31 | { 32 | tasks[num_task] = task; 33 | ++num_task; 34 | } 35 | 36 | static struct task *find_task_waiting_for(uint64_t wait) 37 | { 38 | for (size_t i = 0; i < num_task; ++i) 39 | { 40 | if (tasks[i]->wait & wait) 41 | { 42 | return tasks[i]; 43 | } 44 | } 45 | return 0; 46 | } 47 | 48 | static int switch_to_task_waiting_for(uint64_t wait) 49 | { 50 | struct task *next = find_task_waiting_for(wait); 51 | if (!next) return 0; 52 | switch_to(current, next); 53 | return 1; 54 | } 55 | 56 | static bool task_poll_udp(struct task *task) 57 | { 58 | if (!task->wait_packet.is_waiting) return true; 59 | if ((rdcycle() - task->wait_packet.start) >= task->wait_packet.delta) 60 | { 61 | task->wait_packet.is_waiting = false; 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | void idle_entry() 68 | { 69 | size_t rr = 0; 70 | while (1) 71 | { 72 | if (tty_poll_recv()) switch_to_task_waiting_for(WAIT_TTY_RECV); 73 | if (tty_poll_send()) switch_to_task_waiting_for(WAIT_TTY_SEND); 74 | if (eth_poll_recv()) switch_to_task_waiting_for(WAIT_ETH_RECV); 75 | if (eth_poll_send()) switch_to_task_waiting_for(WAIT_ETH_SEND); 76 | if (num_waiting_task < num_task) 77 | { 78 | for (size_t i = 0; i < num_task; ++i) 79 | { 80 | struct task *task = tasks[rr]; 81 | ++rr; 82 | if (rr == num_task) rr = 0; 83 | if (task != current && !task->wait 84 | && poll_timer(&task->timer) && task_poll_udp(task)) 85 | { 86 | switch_to(current, task); 87 | break; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | void sched_yield() 95 | { 96 | switch_to(current, idle_task); 97 | } 98 | 99 | void wait(uint64_t w) 100 | { 101 | current->wait |= w; 102 | ++num_waiting_task; 103 | sched_yield(); 104 | --num_waiting_task; 105 | current->wait &= ~w; 106 | } 107 | 108 | void sleep(uint64_t delta) 109 | { 110 | set_timer(¤t->timer, delta); 111 | sched_yield(); 112 | } 113 | 114 | void exit() 115 | { 116 | wait(WAIT_EXIT); 117 | } 118 | 119 | static bool task_match_udp(struct task *task, uint16_t dport, ipv6_addr_t saddr, uint16_t sport) 120 | { 121 | return (task->wait_packet.dport == 0 || task->wait_packet.dport == dport) 122 | && (ipv6_is_unspecified(task->wait_packet.expected_saddr) 123 | || ipv6_eq(task->wait_packet.expected_saddr, saddr)) 124 | && (task->wait_packet.expected_sport == 0 || task->wait_packet.expected_sport == sport); 125 | } 126 | 127 | static struct task *task_find_udp(uint16_t dport, ipv6_addr_t saddr, uint16_t sport) 128 | { 129 | for (size_t i = 0; i < num_task; ++i) 130 | { 131 | struct task *task = tasks[i]; 132 | if (task->wait_packet.is_waiting && task_match_udp(task, dport, saddr, sport)) 133 | { 134 | return task; 135 | } 136 | } 137 | return 0; 138 | } 139 | 140 | void task_dispatch_udp(uint16_t dport, ipv6_addr_t saddr, uint16_t sport, void *buff, size_t len) 141 | { 142 | struct task *target_task = task_find_udp(dport, saddr, sport); 143 | if (!target_task) 144 | { 145 | sched_yield(); 146 | target_task = task_find_udp(dport, saddr, sport); 147 | } 148 | if (!target_task) 149 | { 150 | return; 151 | } 152 | target_task->wait_packet.is_waiting = false; 153 | *target_task->wait_packet.saddr = saddr; 154 | *target_task->wait_packet.sport = sport; 155 | size_t copy_len = target_task->wait_packet.len; 156 | if (len < copy_len) copy_len = len; 157 | memcpy(target_task->wait_packet.buff, buff, copy_len); 158 | *target_task->wait_packet.out_len = copy_len; 159 | } 160 | -------------------------------------------------------------------------------- /tftp.c: -------------------------------------------------------------------------------- 1 | #include "tftp.h" 2 | #include "arch/asm.h" 3 | #include "error.h" 4 | #include "eth.h" 5 | #include "task.h" 6 | #include "string.h" 7 | #include "printf.h" 8 | #include "elf.h" 9 | 10 | __attribute__ ((aligned (16))) uint8_t tftp_buff[1024]; 11 | tftp_packet_t *const tftp_packet = (tftp_packet_t *)tftp_buff; 12 | 13 | __attribute__ ((aligned (16))) uint8_t tftp_req_buff[128]; 14 | tftp_req_packet_t *const tftp_req = (tftp_req_packet_t *)tftp_req_buff; 15 | 16 | __attribute__ ((aligned (16))) uint8_t tftp_resp_buff[1024]; 17 | tftp_packet_t *const tftp_resp = (tftp_packet_t *)tftp_resp_buff; 18 | 19 | static uint16_t get_port() 20 | { 21 | uint16_t port; 22 | do 23 | { 24 | port = rdcycle() ^ 0x5744; 25 | } 26 | while (port < 1024); 27 | return port; 28 | } 29 | 30 | static int tftp_build_req(void *buff, size_t size, 31 | uint16_t opcode, char *filename, size_t filename_len) 32 | { 33 | const size_t filename_size = filename_len + 1; 34 | if (size < sizeof(tftp_req_packet_t) + filename_size + TFTP_MODE_SIZE) 35 | { 36 | return -EOOB; 37 | } 38 | tftp_req_packet_t *req = (tftp_req_packet_t *)buff; 39 | req->opcode = opcode; 40 | char *req_filename = (char *)(req + 1); 41 | memcpy(req_filename, filename, filename_size); 42 | char *mode = req_filename + filename_size; 43 | memcpy(mode, TFTP_MODE, TFTP_MODE_SIZE); 44 | return sizeof(tftp_req_packet_t) + filename_size + TFTP_MODE_SIZE; 45 | } 46 | 47 | static int tftp_check_resp(void *buff, size_t size, uint16_t expected_opcode) 48 | { 49 | if (size < sizeof(tftp_packet_t)) 50 | { 51 | printf("TFTP Client: UDP datagram is too small.\n"); 52 | return -EBADPKT; 53 | } 54 | 55 | tftp_packet_t *resp = (tftp_packet_t *)buff; 56 | 57 | if (resp->opcode == TFTP_OP_ERROR) 58 | { 59 | printf("TFTP Client: Error %d: %s\n", 60 | ntohs(resp->seq), (uint8_t *)(resp + 1)); 61 | return -EBADPKT; 62 | } 63 | else if (resp->opcode != expected_opcode) 64 | { 65 | printf("TFTP Client: Unexpected Opcode %d\n", ntohs(resp->opcode)); 66 | return -EBADPKT; 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | int tftp_get_file(char *filename, size_t filename_len, void *buff, size_t len, size_t *out_len) 73 | { 74 | int req_size = tftp_build_req(tftp_req, sizeof(tftp_req_buff), 75 | TFTP_OP_RRQ, filename, filename_len); 76 | if (req_size < 0) 77 | { 78 | return req_size; 79 | } 80 | 81 | printf("TFTP Client: Getting \"%s\"...\n", filename); 82 | 83 | // Send the request and wait for the first data block. 84 | size_t ret; 85 | uint16_t client_port = get_port(); 86 | uint16_t server_port; 87 | while (true) 88 | { 89 | send_udp(client_port, server_ip, TFTP_SERVER_PORT, tftp_req, req_size); 90 | ipv6_addr_t saddr; 91 | ret = recv_udp(client_port, &saddr, &server_port, tftp_buff, sizeof(tftp_buff), 92 | server_ip, 0, CLOCK_FREQ / 4); 93 | if (ret != 0) break; 94 | } 95 | 96 | int r = tftp_check_resp(tftp_packet, ret, TFTP_OP_DATA); 97 | if (r < 0) 98 | { 99 | return r; 100 | } 101 | 102 | if (tftp_packet->seq != htons(1)) 103 | { 104 | printf("TFTP Client: Not First Block???\n"); 105 | return -EBADPKT; 106 | } 107 | 108 | tftp_packet->opcode = TFTP_OP_ACK; 109 | send_udp(client_port, server_ip, server_port, tftp_packet, sizeof(tftp_packet_t)); 110 | 111 | size_t data_len = ret - sizeof(tftp_packet_t); 112 | size_t total_len = data_len; 113 | 114 | uint8_t *u8buff = (uint8_t *)buff; 115 | memcpy(u8buff, (void *)(tftp_packet + 1), data_len); 116 | u8buff += data_len; 117 | 118 | if (data_len < TFTP_CHUNK_SIZE) 119 | { 120 | printf("TFTP Client: Done, received %lu bytes.\n", total_len); 121 | if (out_len) *out_len = total_len; 122 | return 0; 123 | } 124 | 125 | printf("TFTP Client: Received %lu bytes.", total_len); 126 | 127 | uint16_t window = 2; 128 | while (true) 129 | { 130 | ipv6_addr_t saddr; 131 | uint16_t sport; 132 | ret = recv_udp(client_port, &saddr, &sport, tftp_buff, sizeof(tftp_buff), 133 | server_ip, server_port, 30 * CLOCK_FREQ); 134 | if (ret == 0) 135 | { 136 | printf("TFTP Client: Timed out!\n"); 137 | return -EBADPKT; 138 | } 139 | 140 | int r = tftp_check_resp(tftp_packet, ret, TFTP_OP_DATA); 141 | if (r < 0) 142 | { 143 | return r; 144 | } 145 | 146 | tftp_packet->opcode = TFTP_OP_ACK; 147 | send_udp(client_port, server_ip, server_port, tftp_packet, sizeof(tftp_packet_t)); 148 | 149 | if (ntohs(tftp_packet->seq) == window) 150 | { 151 | data_len = ret - sizeof(tftp_packet_t); 152 | total_len += data_len; 153 | memcpy(u8buff, (void *)(tftp_packet + 1), data_len); 154 | u8buff += data_len; 155 | 156 | if ((total_len & 0x7ffff) == 0) 157 | { 158 | printf("\rTFTP Client: Received %lu bytes. ", total_len); 159 | } 160 | 161 | if (data_len < TFTP_CHUNK_SIZE) 162 | { 163 | printf("\nTFTP Client: Done, received %lu bytes.\n", total_len); 164 | if (out_len) *out_len = total_len; 165 | return 0; 166 | } 167 | 168 | ++window; 169 | } 170 | else 171 | { 172 | printf("TFTP Client: Warning: Block %d is not in the window.\n", 173 | ntohs(tftp_packet->seq)); 174 | } 175 | } 176 | } 177 | 178 | int tftp_put_file(char *filename, size_t filename_len, tftp_read_block_t read_block, void *ctx) 179 | { 180 | int req_size = tftp_build_req(tftp_req, sizeof(tftp_req_buff), 181 | TFTP_OP_WRQ, filename, filename_len); 182 | if (req_size < 0) 183 | { 184 | return req_size; 185 | } 186 | 187 | printf("TFTP Client: Putting \"%s\"...\n", filename); 188 | 189 | // Send the request and wait for an ack. 190 | size_t ret; 191 | uint16_t client_port = get_port(); 192 | uint16_t server_port; 193 | while (true) 194 | { 195 | send_udp(client_port, server_ip, TFTP_SERVER_PORT, tftp_req, req_size); 196 | ipv6_addr_t saddr; 197 | ret = recv_udp(client_port, &saddr, &server_port, tftp_resp_buff, sizeof(tftp_resp_buff), 198 | server_ip, 0, CLOCK_FREQ / 4); 199 | if (ret != 0) break; 200 | } 201 | 202 | int r = tftp_check_resp(tftp_resp, ret, TFTP_OP_ACK); 203 | if (r < 0) 204 | { 205 | return r; 206 | } 207 | 208 | if (tftp_resp->seq != htons(0)) 209 | { 210 | printf("TFTP Client: Not First Ack???\n"); 211 | return -EBADPKT; 212 | } 213 | 214 | size_t total_len = 0; 215 | printf("TFTP Client: Sent %lu bytes.", total_len); 216 | 217 | uint16_t window = 1; 218 | while (true) 219 | { 220 | tftp_packet->opcode = TFTP_OP_DATA; 221 | tftp_packet->seq = htons(window); 222 | int block_size = read_block(ctx, tftp_packet + 1); 223 | if (block_size < 0) 224 | { 225 | return block_size; 226 | } 227 | 228 | send_udp(client_port, server_ip, server_port, tftp_packet, 229 | sizeof(tftp_packet_t) + block_size); 230 | 231 | while (true) 232 | { 233 | ipv6_addr_t saddr; 234 | uint16_t sport; 235 | ret = recv_udp(client_port, &saddr, &sport, tftp_resp_buff, sizeof(tftp_resp_buff), 236 | server_ip, server_port, 1 * CLOCK_FREQ); 237 | if (ret == 0) 238 | { 239 | printf("TFTP Client: Timed out!\n"); 240 | send_udp(client_port, server_ip, server_port, tftp_packet, 241 | sizeof(tftp_packet_t) + block_size); 242 | continue; 243 | } 244 | 245 | int r = tftp_check_resp(tftp_resp, ret, TFTP_OP_ACK); 246 | if (r < 0) 247 | { 248 | return r; 249 | } 250 | 251 | if (tftp_resp->seq == tftp_packet->seq) 252 | { 253 | break; 254 | } 255 | else 256 | { 257 | printf("TFTP Client: Warning: Ack %d is not in the window.\n", 258 | ntohs(tftp_resp->seq)); 259 | continue; 260 | } 261 | } 262 | 263 | total_len += block_size; 264 | 265 | if ((total_len & 0x7ffff) == 0) 266 | { 267 | printf("\rTFTP Client: Sent %lu bytes. ", total_len); 268 | } 269 | 270 | if (block_size < TFTP_CHUNK_SIZE) 271 | { 272 | printf("\nTFTP Client: Done, sent %lu bytes.\n", total_len); 273 | break; 274 | } 275 | 276 | ++window; 277 | } 278 | return 0; 279 | } 280 | 281 | void init_tftp() 282 | { 283 | 284 | } 285 | -------------------------------------------------------------------------------- /timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | 4 | -------------------------------------------------------------------------------- /tty.c: -------------------------------------------------------------------------------- 1 | #include "tty.h" 2 | 3 | const tty_ops_t *tty_ops; 4 | 5 | void init_tty() 6 | { 7 | } 8 | 9 | void putchar(uint8_t c) 10 | { 11 | tty_ops->putchar(c); 12 | } 13 | 14 | uint8_t getchar() 15 | { 16 | return tty_ops->getchar(); 17 | } 18 | 19 | // for printf.h 20 | void _putchar(char ch) __attribute__((alias("putchar"))); 21 | 22 | void puts(char *s) 23 | { 24 | char ch; 25 | while ((ch = *s++)) putchar(ch); 26 | } 27 | 28 | void getline(char *buf, size_t len) 29 | { 30 | char *buf_end = buf + len - 1; 31 | while (buf != buf_end) 32 | { 33 | *buf = getchar(); 34 | if (*buf == '\n' || *buf == '\r') break; 35 | putchar(*buf); // echo 36 | ++buf; 37 | } 38 | puts("\n"); 39 | *buf = 0; 40 | } 41 | -------------------------------------------------------------------------------- /user/riscv/Makefile: -------------------------------------------------------------------------------- 1 | ARCH = riscv 2 | PREFIX = riscv64-unknown-linux-gnu- 3 | GCC = $(PREFIX)gcc 4 | CC = $(PREFIX)g++ 5 | OBJCOPY = $(PREFIX)objcopy 6 | OBJDUMP = $(PREFIX)objdump 7 | 8 | LIBC = ../../../musl-1.2.0 9 | LIBSTDCXX = ../../../riscv-gcc/libstdc++-v3/build 10 | RISCV = /opt/riscv 11 | 12 | CFLAGS = -fno-builtin -nostdlib -static -Wl,--gc-sections -O2 -Wall \ 13 | -march=rv64imac \ 14 | -I$(LIBC)/include -I$(LIBC)/obj/include -I$(LIBC)/arch/riscv64 -I$(LIBC)/arch/generic $(DEFINES) 15 | 16 | CCFLAGS = -nostdinc++ \ 17 | -I$(LIBSTDCXX)/include \ 18 | -I$(LIBSTDCXX)/include/riscv64-unknown-linux-gnu \ 19 | -I$(LIBSTDCXX)/../libsupc++ 20 | 21 | LDFLAGS = -z separate-code -L$(LIBC)/lib -L$(LIBSTDCXX)/src/.libs -L$(LIBSTDCXX)/libsupc++/.libs #-Tlinker.ld 22 | 23 | CRTBEGIN_OBJ := $(shell $(GCC) $(CFLAGS) -print-file-name=crtbeginT.o) 24 | CRTEND_OBJ := $(shell $(GCC) $(CFLAGS) -print-file-name=crtend.o) 25 | 26 | HEADERS=$(wildcard *.h) 27 | SOURCES=$(wildcard *.c *.cpp) 28 | OBJECTS=$(patsubst %.c,%.elf,$(wildcard *.c)) $(patsubst %.cpp,%.elf,$(wildcard *.cpp)) 29 | 30 | PRE_OBJS = $(LIBC)/lib/crt1.o $(LIBC)/lib/crti.o $(CRTBEGIN_OBJ) 31 | POST_OBJS = $(CRTEND_OBJ) $(LIBC)/lib/crtn.o 32 | 33 | .PHONY: all 34 | all: hello.elf helloxx.elf empty.elf rdcycle.elf 35 | 36 | %.elf: %.c $(HEADERS) linker.ld 37 | $(GCC) $(CFLAGS) $(LDFLAGS) $(PRE_OBJS) $< -Wl,--start-group -lgcc -lgcc_eh -lc -Wl,--end-group $(POST_OBJS) -o $@ 38 | 39 | %.elf: %.cpp $(HEADERS) linker.ld 40 | $(CC) $(CCFLAGS) $(CFLAGS) $(LDFLAGS) $(PRE_OBJS) $< -lstdc++ -lsupc++ -Wl,--start-group -lgcc -lgcc_eh -lc -Wl,--end-group $(POST_OBJS) -o $@ 41 | 42 | %.elf: %.S $(HEADERS) linker.ld 43 | $(GCC) $(CFLAGS) $(LDFLAGS) $< -o $@ 44 | 45 | .PHONY: clean 46 | clean: 47 | -rm *.elf 48 | -------------------------------------------------------------------------------- /user/riscv/empty.S: -------------------------------------------------------------------------------- 1 | .section .text, "ax", @progbits 2 | .global _start 3 | _start: 4 | ecall 5 | -------------------------------------------------------------------------------- /user/riscv/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | __attribute__ ((constructor)) void foo(void) 7 | { 8 | printf("foo is running and printf is available at this point\n"); 9 | } 10 | 11 | void zero1(void *buff, size_t len) 12 | { 13 | uint8_t *end = (uint8_t *)buff + len; 14 | uint8_t *u8 = (uint8_t *)buff; 15 | for (; u8 != end; u8 += 1) 16 | { 17 | u8[0] = 0; 18 | } 19 | } 20 | 21 | void zero2(void *buff, size_t len) 22 | { 23 | uint64_t *end = (uint64_t *)((uint8_t *)buff + len); 24 | uint64_t *u64 = (uint64_t *)buff; 25 | for (; u64 != end; u64 += 8) 26 | { 27 | u64[0] = 0; 28 | u64[1] = 0; 29 | u64[2] = 0; 30 | u64[3] = 0; 31 | u64[4] = 0; 32 | u64[5] = 0; 33 | u64[6] = 0; 34 | u64[7] = 0; 35 | } 36 | } 37 | 38 | #define SIZE (1 << 20) 39 | 40 | int main() 41 | { 42 | int *a = (int *)malloc(SIZE); 43 | 44 | int c; 45 | scanf("%d", &c); 46 | if (c == 0) 47 | { 48 | memset(a, 0, SIZE); 49 | } 50 | else if (c == 1) 51 | { 52 | zero1(a, SIZE); 53 | } 54 | else 55 | { 56 | zero2(a, SIZE); 57 | } 58 | 59 | scanf("%d%d", a, a + 1); 60 | printf("%d\n", a[0] + a[1]); 61 | free(a); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /user/riscv/helloxx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | std::cout << "hello, world" << std::endl; 6 | int a, b; 7 | std::cin >> a >> b; 8 | std::cout << a + b << std::endl; 9 | std::cout << "bye, world" << std::endl; 10 | try 11 | { 12 | throw "test"; 13 | } 14 | catch (const char *s) 15 | { 16 | std::cout << "exception: " << s << std::endl; 17 | } 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /user/riscv/linker.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | . = 0x10000; 4 | ENTRY(_start) 5 | .text : 6 | { 7 | *(.text*) 8 | } 9 | _text_end = .; 10 | . = ALIGN(0x1000); 11 | .rodata : 12 | { 13 | *(.rodata*) 14 | *(.srodata*) 15 | } 16 | . = ALIGN(0x1000); 17 | .data : 18 | { 19 | *(.data*) 20 | *(.sdata*) 21 | } 22 | _bss_begin = .; 23 | .bss : 24 | { 25 | *(.bss*) 26 | *(.sbss*) 27 | } 28 | . = ALIGN(0x10); 29 | _bss_end = .; 30 | /DISCARD/ : 31 | { 32 | *(.note.gnu.build-id) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /user/riscv/rdcycle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static inline uint64_t rdcycle() 6 | { 7 | uint64_t cycle; 8 | asm volatile ("rdtime %0" : "=r"(cycle)); 9 | return cycle; 10 | } 11 | 12 | int main() 13 | { 14 | uint64_t c1 = rdcycle(), c2 = rdcycle(); 15 | printf("%lu - %lu = %lu\n", c2, c1, c2 - c1); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "stdio.h" 3 | #include "ip.h" 4 | #include "arch/mmu.h" 5 | 6 | void zero_page(void *pg) 7 | { 8 | uint64_t *end = (uint64_t *)((uint8_t *)pg + PGSIZE); 9 | uint64_t *u64 = (uint64_t *)pg; 10 | for (; u64 != end; u64 += 8) 11 | { 12 | u64[0] = 0; 13 | u64[1] = 0; 14 | u64[2] = 0; 15 | u64[3] = 0; 16 | u64[4] = 0; 17 | u64[5] = 0; 18 | u64[6] = 0; 19 | u64[7] = 0; 20 | } 21 | } 22 | 23 | void print_mem(void *data, size_t len, char delim, int newline) 24 | { 25 | uint8_t *ptr = data; 26 | for (int i = 0; i < len; ++i) 27 | { 28 | printf("%02x", ptr[i]); 29 | if (newline && (i & 0xf) == 0xf) 30 | { 31 | puts("\n"); 32 | } 33 | else 34 | { 35 | if (i != len - 1) 36 | { 37 | putchar(delim); 38 | } 39 | } 40 | } 41 | if (newline && (len & 0xf) != 0) 42 | { 43 | puts("\n"); 44 | } 45 | } 46 | 47 | void print_mac(uint8_t *data) 48 | { 49 | printf("%02x-%02x-%02x-%02x-%02x-%02x", data[0], data[1], data[2], data[3], data[4], data[5]); 50 | } 51 | 52 | void print_ipv4(uint8_t *data) 53 | { 54 | printf("%u.%u.%u.%u", data[0], data[1], data[2], data[3]); 55 | } 56 | 57 | void print_ipv6(uint8_t *data) 58 | { 59 | printf("%02x%02x", data[0], data[1]); 60 | for (int i = 1; i < 8; ++i) 61 | { 62 | printf(":%02x%02x", data[i * 2], data[i * 2 + 1]); 63 | } 64 | } 65 | 66 | void print_frame(frame_t *frame) 67 | { 68 | puts("recv: "); 69 | print_mac(frame->src_mac); puts(" -> "); print_mac(frame->dst_mac); 70 | printf(", type %04x", ntohs(frame->ethertype)); 71 | printf(", length %d\n", frame->meta.len); 72 | } 73 | -------------------------------------------------------------------------------- /vm.c: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | #include "mm.h" 3 | #include "arch/mmu.h" 4 | #include "error.h" 5 | #include "utils.h" 6 | #include "string.h" 7 | #include "stdio.h" 8 | 9 | pte_t *kernel_pt = NULL; 10 | 11 | pte_t *create_pt() 12 | { 13 | uintptr_t pg = get_free_page(); 14 | if (!pg) return NULL; 15 | 16 | pte_t *new_pt = p2v(pg); 17 | if (kernel_pt) 18 | { 19 | memcpy(new_pt, kernel_pt, PGSIZE); 20 | } 21 | else 22 | { 23 | zero_page(new_pt); 24 | } 25 | return new_pt; 26 | } 27 | 28 | static pte_t *get_pt_level(int level, pte_t *pt, uintptr_t idx) 29 | { 30 | if (!PTE_VALID(pt[idx])) 31 | { 32 | uintptr_t new_pt = get_free_page(); 33 | if (!new_pt) return NULL; 34 | zero_page(p2v(new_pt)); 35 | pt[idx] = make_pte(level, new_pt, 0); 36 | return (pte_t *)p2v(new_pt); 37 | } 38 | else 39 | { 40 | return (pte_t *)p2v(PTE_PPN(pt[idx])); 41 | } 42 | } 43 | 44 | pte_t *get_pte(pte_t *pt, uintptr_t va) 45 | { 46 | if (!pt) return NULL; 47 | if (PGLEVEL >= 4) pt = get_pt_level(3, pt, PT_IDX(3, va)); 48 | if (!pt) return NULL; 49 | if (PGLEVEL >= 3) pt = get_pt_level(2, pt, PT_IDX(2, va)); 50 | if (!pt) return NULL; 51 | if (PGLEVEL >= 2) pt = get_pt_level(1, pt, PT_IDX(1, va)); 52 | if (!pt) return NULL; 53 | return &pt[PT_IDX(0, va)]; 54 | } 55 | 56 | void *map_page(pte_t *pt, uintptr_t va, uint64_t flags) 57 | { 58 | pte_t *pte = get_pte(pt, va); 59 | if (!pte) return NULL; 60 | if (!PTE_VALID(*pte)) 61 | { 62 | uintptr_t new_page = get_free_page(); 63 | if (!new_page) return NULL; 64 | zero_page(p2v(new_page)); 65 | *pte = make_pte(0, new_page, flags); 66 | return (void *)p2v(new_page); 67 | } 68 | else 69 | { 70 | return (void *)p2v(PTE_PPN(*pte)); 71 | } 72 | } 73 | 74 | int map_and_copy(pte_t *pt, uintptr_t va, uint64_t flags, void *src, size_t len) 75 | { 76 | if (len == 0) return 0; 77 | 78 | uint8_t *u8 = (uint8_t *)src; 79 | uintptr_t begin = va, end = va + len; 80 | 81 | // [page][page][page] 82 | // ~~~~~~~~~~~~~ 83 | // 1 2 3 84 | 85 | // 1 86 | uintptr_t begin_aligned = PGALIGN_FLOOR(begin); 87 | if (begin != begin_aligned) 88 | { 89 | uint8_t *pg = (uint8_t *)map_page(pt, begin_aligned, flags); 90 | if (!pg) return -EOOM; 91 | size_t chunk = PGSIZE - (begin - begin_aligned); // == mid_begin - begin 92 | if (len < chunk) 93 | { 94 | chunk = len; 95 | } 96 | memcpy(pg + (begin - begin_aligned), u8, chunk); 97 | u8 += chunk; 98 | len -= chunk; 99 | } 100 | 101 | // 2 102 | uintptr_t mid_begin = PGALIGN(begin), mid_end = PGALIGN_FLOOR(end); 103 | for (uintptr_t va = mid_begin; va < mid_end; va += PGSIZE) 104 | { 105 | uint8_t *pg = (uint8_t *)map_page(pt, va, flags); 106 | if (!pg) return -EOOM; 107 | memcpy(pg, u8, PGSIZE); 108 | u8 += PGSIZE; 109 | len -= PGSIZE; 110 | } 111 | 112 | // 3 113 | if (len > 0) 114 | { 115 | uint8_t *pg = (uint8_t *)map_page(pt, mid_end, flags); 116 | if (!pg) return -EOOM; 117 | memcpy(pg, u8, len); 118 | } 119 | return 0; 120 | } 121 | 122 | static size_t count_pt_level(int level, pte_t *pt, uint64_t flags) 123 | { 124 | size_t count = 0; 125 | for (size_t i = 0; i < PGSIZE / sizeof(pte_t); ++i) 126 | { 127 | if (PTE_VALID(pt[i])) 128 | { 129 | if (level > 0 && PTE_TABLE(pt[i])) 130 | { 131 | count += count_pt_level(level - 1, (pte_t *)p2v(PTE_PPN(pt[i])), flags); 132 | } 133 | else if ((pt[i] & flags) == flags) 134 | { 135 | count += 1ul << (level * PGLEVEL_SHIFT); 136 | } 137 | } 138 | } 139 | return count; 140 | } 141 | 142 | size_t count_pt(pte_t *pt, uint64_t flags) 143 | { 144 | return count_pt_level(PGLEVEL - 1, pt, pte_flags(flags)); 145 | } 146 | --------------------------------------------------------------------------------