├── app ├── sqlite │ ├── app.c │ └── shog.build ├── http-hello │ ├── shog.build │ ├── test.rb │ └── app.c └── memcached │ ├── shog.build │ └── app.c ├── test ├── unit │ ├── .gitignore │ ├── asan_test.c │ ├── config.h │ ├── build.sh │ └── allocator_test.c ├── integration │ └── common │ │ └── integration.rb └── acutest │ ├── README │ └── LICENSE.md ├── arch └── x86 │ ├── ahci.h │ ├── pit.h │ ├── pic.h │ ├── keyboard.h │ ├── microcode.h │ ├── vga_console.h │ ├── stacktrace.h │ ├── ioapic.h │ ├── acpica │ └── shog.build │ ├── rtc.h │ ├── include │ └── arch.h │ ├── x86_init.h │ ├── keyboard.c │ ├── pic.c │ ├── cpu.c │ ├── acpi.h │ ├── multiboot.S │ ├── apic.h │ ├── smp.h │ ├── Kconfig │ ├── stacktrace.c │ ├── serial.c │ ├── pit.c │ ├── smp_trampoline.S │ ├── mmu.h │ ├── shog.build │ ├── start_smp_32.c │ ├── vga_console.c │ ├── linker.ld │ ├── interrupt.h │ ├── rtc.c │ ├── ioapic.c │ ├── interrupt_handlers.S │ ├── x2apic.c │ ├── hpet.c │ ├── xapic.c │ └── microcode.c ├── .clang-format ├── .gitignore ├── include ├── stack.h ├── init.h ├── err.h ├── net │ ├── dhcp.h │ ├── icmp6.h │ ├── ip6.h │ ├── icmp.h │ ├── arp.h │ ├── udp.h │ ├── eth.h │ ├── tcp.h │ └── ip4.h ├── unicycle.h ├── console.h ├── sort.h ├── multibyte.h ├── mmio.h ├── arch_timer.h ├── slab.h ├── kconfig.h ├── rand.h ├── stdio.h ├── event.h ├── cpu.h ├── asan.h ├── string.h ├── blk.h ├── mem.h ├── buddy.h ├── buffer.h ├── sha3.h ├── timer.h ├── lock.h ├── new_list.h ├── kalloc.h ├── float.h ├── compiler.h └── shout.h ├── core ├── blk │ ├── shog.build │ └── blk.c ├── crypto │ └── shog.build ├── alloc │ └── shog.build ├── net │ ├── shog.build │ ├── ip6.c │ ├── icmp6.c │ ├── icmp.c │ ├── eth.c │ ├── udp.c │ └── arp.c ├── shog.build ├── MemoryLayout.txt ├── sort.c ├── deferredevent.c ├── buffer.c ├── multibyte.c ├── unicycle.c ├── mem.c ├── float.c ├── rand.c ├── timer.c └── string.c ├── drv ├── shog.build ├── virtio │ ├── shog.build │ ├── virtio_rng.h │ ├── virtio.h │ ├── virtio_9p.h │ ├── virtio.c │ ├── virtio_ids.h │ ├── virtio_input.h │ ├── virtio_console.h │ ├── virtio_vsock.h │ ├── virtio_config.h │ ├── virtio_virtq.h │ ├── virtio_balloon.h │ └── virtio_mmio.h └── net │ ├── shog.build │ ├── intel_e1000e.regs │ └── intel_e1000e.regs.h ├── scripts ├── gdb.init ├── gdb.sh ├── gen_config_h.rb ├── gen_regmap.rb ├── bin2c.rb └── run ├── .gitmodules ├── unicycle.sublime-project ├── Makefile ├── TODO ├── LICENSE ├── shog.build └── Kconfig /app/sqlite/app.c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/unit/.gitignore: -------------------------------------------------------------------------------- 1 | allocator_test 2 | -------------------------------------------------------------------------------- /arch/x86/ahci.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once -------------------------------------------------------------------------------- /test/integration/common/integration.rb: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | IndentWidth: 4 2 | ColumnLimit: 140 3 | AlignEscapedNewlinesLeft: true 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | unicycle.sublime-workspace 2 | .tags 3 | .tags_sorted_by_file 4 | .config 5 | out 6 | -------------------------------------------------------------------------------- /arch/x86/pit.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void pit_init(void); -------------------------------------------------------------------------------- /arch/x86/pic.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void pic_disable(void); -------------------------------------------------------------------------------- /include/stack.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void dump_stack(void); -------------------------------------------------------------------------------- /arch/x86/keyboard.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void keyboard_init(void); -------------------------------------------------------------------------------- /arch/x86/microcode.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void microcode_load(void); -------------------------------------------------------------------------------- /arch/x86/vga_console.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void console_init(); 6 | -------------------------------------------------------------------------------- /include/init.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | void application_init(void); 6 | -------------------------------------------------------------------------------- /core/blk/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | blk.c 5 | ] 6 | 7 | emit_each(:cc, srcs) 8 | -------------------------------------------------------------------------------- /test/acutest/README: -------------------------------------------------------------------------------- 1 | Copied from https://github.com/mity/acutest 2 | commit 3c2aa2e3126cd7d44e3d7d8834beaaba613bbbfb 3 | -------------------------------------------------------------------------------- /app/http-hello/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | app.c 5 | ] 6 | 7 | emit_each(:cc, srcs) 8 | -------------------------------------------------------------------------------- /core/crypto/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | sha3.c 5 | ] 6 | 7 | emit_each(:cc, srcs) 8 | -------------------------------------------------------------------------------- /drv/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | subdirs = %w[ 4 | net 5 | virtio 6 | ] 7 | 8 | visit(subdirs) 9 | -------------------------------------------------------------------------------- /include/err.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | typedef enum { 6 | SUCCESS = 0, 7 | E_INUSE = 1, 8 | } err_t; -------------------------------------------------------------------------------- /include/net/dhcp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "net/ip4.h" 6 | 7 | void dhcp_init(struct ip4if *ip4if); -------------------------------------------------------------------------------- /include/unicycle.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | 7 | NORETURN void unicycle_loop(void); -------------------------------------------------------------------------------- /arch/x86/stacktrace.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | void print_stack_trace(uint64_t bp); -------------------------------------------------------------------------------- /include/console.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | void console_write(const char *str, size_t len); -------------------------------------------------------------------------------- /include/sort.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void sort(void *array, size_t num, size_t elem_size, int (*cmp)(const void *, const void *)); 6 | -------------------------------------------------------------------------------- /arch/x86/ioapic.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include 4 | 5 | void ioapic_init(void); 6 | void ioapic_route_irq(uint8_t irq, uint8_t vector); -------------------------------------------------------------------------------- /app/sqlite/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | app.c 5 | ] 6 | 7 | objs = emit_each(:cc, srcs) 8 | objs += visit("sqlite") 9 | 10 | objs 11 | -------------------------------------------------------------------------------- /app/memcached/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | app.c 5 | ] 6 | 7 | objs = emit_each(:cc, srcs) 8 | objs += visit("memcached") 9 | 10 | objs 11 | -------------------------------------------------------------------------------- /scripts/gdb.init: -------------------------------------------------------------------------------- 1 | set pagination off 2 | set logging file out/gdb.log 3 | set logging on 4 | file out/app.elf 5 | target remote :1234 6 | b x86_exception_handler 7 | info breakpoints 8 | continue 9 | -------------------------------------------------------------------------------- /include/net/icmp6.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include 7 | 8 | struct ip4if; 9 | bool icmp6_receive(struct ip4if *ip4if, buffer_t *buf); -------------------------------------------------------------------------------- /drv/virtio/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | virtio.c 5 | virtio_blk.c 6 | virtio_net.c 7 | ] 8 | 9 | @rule[:cc].includes << "../../arch/x86" 10 | 11 | emit_each(:cc, srcs) 12 | -------------------------------------------------------------------------------- /include/net/ip6.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include 7 | 8 | struct ip6if {}; 9 | 10 | void ip6_receive(struct ip6if *ip6if, buffer_t *buff); 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/acpica"] 2 | path = third_party/acpica 3 | url = ../acpica 4 | [submodule "third_party/intel-ucode"] 5 | path = third_party/intel-ucode 6 | url = ../Intel-Linux-Processor-Microcode-Data-Files 7 | -------------------------------------------------------------------------------- /drv/virtio/virtio_rng.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. */ 5 | #include 6 | #include 7 | -------------------------------------------------------------------------------- /include/net/icmp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include 7 | 8 | struct ip4if; 9 | struct ip4_hdr; 10 | void icmp_receive(struct ip4if *ip4if, buffer_t *buf); -------------------------------------------------------------------------------- /drv/net/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | rtl8168.c 5 | intel_e1000e.c 6 | ] 7 | 8 | # TODO: move PCI framework out of Arch x86 9 | @rule[:cc].includes << "../../arch/x86" 10 | emit_each(:cc, srcs) 11 | -------------------------------------------------------------------------------- /test/unit/asan_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "acutest.h" 4 | #include "asan.h" 5 | 6 | void test_asan(void) { asan_init_shadow(uintptr_t max_addr); } 7 | 8 | TEST_LIST = {{"asan allocator", test_asan}, {NULL, NULL}}; 9 | -------------------------------------------------------------------------------- /scripts/gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CMD=gdb 4 | #TOOLCHAIN_GDB=../toolchains/x86_64-elf-7.3.0-Linux-x86_64/bin/x86_64-elf-gdb 5 | 6 | if [ -f "$TOOLCHAIN_GDB" ] 7 | then 8 | CMD=$TOOLCHAIN_GDB 9 | fi 10 | 11 | $CMD --command=scripts/gdb.init 12 | -------------------------------------------------------------------------------- /unicycle.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": ".", 6 | "name": "Unicycle", 7 | "file_exclude_patterns": ["arch/x86/*.o32", ".tags", ".tags_sorted_by_file"], 8 | "folder_exclude_patterns": ["out"] 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /include/multibyte.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | typedef struct mbstate mbstate_t; 8 | 9 | size_t wcrtomb(char *restrict s, wchar_t wc, mbstate_t *restrict st); 10 | int wctomb(char *s, wchar_t wc); -------------------------------------------------------------------------------- /core/alloc/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | buddy.c 5 | slab.c 6 | ] 7 | 8 | objs = emit_each(:cc, srcs) 9 | 10 | objs += emit_each(:cc, ["asan.c"], :cflags => "-fno-sanitize=kernel-address") if @config[:ASAN] 11 | 12 | objs 13 | -------------------------------------------------------------------------------- /include/mmio.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #define MMIO8(addr) (*(volatile uint8_t *)(addr)) 6 | #define MMIO16(addr) (*(volatile uint16_t *)(addr)) 7 | #define MMIO32(addr) (*(volatile uint32_t *)(addr)) 8 | #define MMIO64(addr) (*(volatile uint64_t *)(addr)) -------------------------------------------------------------------------------- /arch/x86/acpica/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | obj = [] 4 | 5 | srcs = %w[ 6 | acpica_hooks.c 7 | acpica.c 8 | ] 9 | 10 | @rule[:cc].includes << Path.make("third_party/acpica/source/include", :root => true) 11 | @rule[:cc].includes << ".." 12 | 13 | emit_each(:cc, srcs) 14 | -------------------------------------------------------------------------------- /include/arch_timer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "event.h" 6 | 7 | // Architecture specific API for timer implementation 8 | 9 | void arch_timer_init(event_handler_t irq_handler); 10 | void arch_timer_set_alarm(uint64_t time); 11 | void arch_timer_disable(void); -------------------------------------------------------------------------------- /arch/x86/rtc.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | struct rtc_date { 8 | uint8_t seconds; 9 | uint8_t minutes; 10 | uint8_t hours; 11 | uint8_t day; 12 | uint8_t month; 13 | uint16_t year; 14 | }; 15 | 16 | void rtc_read(struct rtc_date *data); 17 | -------------------------------------------------------------------------------- /core/net/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | srcs = %w[ 4 | arp.c 5 | dhcp.c 6 | eth.c 7 | http_parser.c 8 | icmp.c 9 | icmp6.c 10 | ip4.c 11 | ip6.c 12 | tcp.c 13 | udp.c 14 | ] 15 | 16 | # TODO: remove dependency to x86 headers 17 | 18 | @rule[:cc].includes << "../../arch/x86/include" 19 | emit_each(:cc, srcs) 20 | -------------------------------------------------------------------------------- /include/slab.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | // slab allocator works with objects up to 32KiB 9 | // anything else should be allocated with buddy allocator 10 | #define ALLOC_SLAB_MAX_SIZE (32 * 1024) 11 | 12 | void *alloc_slab_allocate(size_t size); 13 | void alloc_slab_free(void *obj, size_t size); -------------------------------------------------------------------------------- /arch/x86/include/arch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../x86.h" 4 | 5 | #define arch_stop x86_stop 6 | 7 | #define LDBL_MANT_DIG 64 8 | #define LDBL_MIN_EXP (-16381) 9 | #define LDBL_MAX_EXP 16384 10 | 11 | #define LDBL_TRUE_MIN 3.6451995318824746025e-4951L 12 | #define LDBL_MIN 3.3621031431120935063e-4932L 13 | #define LDBL_MAX 1.1897314953572317650e+4932L 14 | #define LDBL_EPSILON 1.0842021724855044340e-19L -------------------------------------------------------------------------------- /include/kconfig.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #define __ARG_PLACEHOLDER_1 0, 6 | #define __take_second_arg(__ignored, val, ...) val 7 | 8 | #define __is_defined(x) ___is_defined(x) 9 | #define ___is_defined(val) ____is_defined(__ARG_PLACEHOLDER_##val) 10 | #define ____is_defined(arg1_or_junk) __take_second_arg(arg1_or_junk 1, 0) 11 | 12 | #define IS_ENABLED(cfg) __is_defined(cfg) 13 | -------------------------------------------------------------------------------- /core/net/ip6.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "net/ip6.h" 4 | #include "buffer.h" 5 | #include "compiler.h" 6 | #include "net/icmp6.h" 7 | #include "net/tcp.h" 8 | #include "net/udp.h" 9 | #include "stdio.h" 10 | #include 11 | 12 | struct __be PACKED ip6_hdr {}; 13 | 14 | void ip6_receive(UNUSED struct ip6if *ip6if, UNUSED buffer_t *buff) { 15 | // struct ip6_hdr *hdr = buff->pos; 16 | 17 | // printf("IP6\n"); 18 | } 19 | -------------------------------------------------------------------------------- /core/net/icmp6.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "net/icmp6.h" 4 | #include "buffer.h" 5 | #include "compiler.h" 6 | #include "stdio.h" 7 | #include 8 | 9 | struct __be PACKED icmp6_hdr {}; 10 | 11 | bool icmp6_receive(UNUSED struct ip4if *ip4if, UNUSED buffer_t *buff) { 12 | // struct icmp6_hdr *hdr = buff->pos; 13 | // buff->pos += sizeof(struct icmp6_hdr); 14 | 15 | printf("ICMP6\n"); 16 | 17 | return true; 18 | } 19 | -------------------------------------------------------------------------------- /include/rand.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | // This method increases entropy of RNG by using CPU jitter information 9 | void rand_mixin_cpu_jitter(void); 10 | 11 | uint8_t rand8(void); 12 | uint16_t rand16(void); 13 | uint32_t rand32(void); 14 | uint64_t rand64(void); 15 | // generates double between 0 and 1 16 | double rand_double(void); 17 | void rand_array(void *array, size_t length); -------------------------------------------------------------------------------- /test/unit/config.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #define CONFIG_APPLICATION_PATH "test/integration/http" 8 | #define CONFIG_DEBUG false 9 | #define CONFIG_TOOLCHAIN_PATH "" 10 | #define CONFIG_COMPILER "gcc" 11 | #define CONFIG_STACK_SIZE 131072 12 | #define CONFIG_SMP true 13 | #define CONFIG_ARCH_X86 true 14 | #define CONFIG_SERIAL_CONSOLE true 15 | #define CONFIG_VGA_CONSOLE false 16 | #define CONFIG_X2APIC false 17 | -------------------------------------------------------------------------------- /core/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | subdirs = %w[ 4 | alloc 5 | crypto 6 | net 7 | blk 8 | ] 9 | 10 | objs = visit(subdirs) 11 | 12 | srcs = %w[ 13 | buffer.c 14 | deferredevent.c 15 | float.c 16 | mem.c 17 | multibyte.c 18 | printf.c 19 | rand.c 20 | sort.c 21 | string.c 22 | timer.c 23 | unicycle.c 24 | ] 25 | 26 | srcs << 'ubsan.c' if @config[:UBSAN] 27 | 28 | @rule[:cc].includes << "../arch/x86/include" 29 | 30 | objs += emit_each(:cc, srcs) 31 | 32 | objs 33 | -------------------------------------------------------------------------------- /include/net/arp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include "net/eth.h" 7 | #include "net/ip4.h" 8 | #include 9 | 10 | void arp_receive(struct eth_device *eth, buffer_t *buff); 11 | void arp_cache_add(ip4addr_t ip, ethaddr_t eth); 12 | // looks up MAC address by given ip address 13 | // if ip address found in ARP cache then it fills in eth structure 14 | // and return true. Otherwise it return false. 15 | bool arp_cache_lookup(ip4addr_t ip, ethaddr_t *eth); -------------------------------------------------------------------------------- /core/MemoryLayout.txt: -------------------------------------------------------------------------------- 1 | Memory layout: 2 | 3 | * headers (like multiboot) 4 | * text - executables 5 | * destructors - list of functions to call when the app goes down 6 | * ro - readonly data 7 | * data - read/write data 8 | * init - section (text/ro/data) unloaded from the memory after init complete 9 | * constructors - pointers to initialization functions 10 | 11 | * HEAP - phisical contingent memory available to app 12 | 13 | * per_cpu - each cpu gets a slice of this area. And each slice is divided to [data/bss/stack] -------------------------------------------------------------------------------- /arch/x86/x86_init.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "x86.h" 4 | 5 | static inline void fpu_init(void) { 6 | regsize_t cr0 = x86_get_cr0(); 7 | cr0 &= ~CR0_EM; // clear coprocessor emulation 8 | cr0 |= CR0_MP; // set coprocessor monitoring 9 | cr0 |= CR0_NE; // set native exceptions 10 | x86_set_cr0(cr0); 11 | 12 | __asm__ volatile("fninit"); 13 | } 14 | 15 | static inline void sse_init(void) { 16 | regsize_t cr4 = x86_get_cr4(); 17 | cr4 |= (CR4_OSFXSR | CR4_OSXMMEXCPT); 18 | x86_set_cr4(cr4); 19 | } -------------------------------------------------------------------------------- /scripts/gen_config_h.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # SPDX-License-Identifier: MIT 3 | 4 | # This script generates config.h C header file from .config 5 | 6 | puts %Q{// This file is automatically generated by #{__FILE__} 7 | // Please do not modify it 8 | 9 | #pragma once 10 | 11 | #include 12 | #include "kconfig.h" 13 | 14 | } 15 | 16 | while l = STDIN.gets 17 | if l =~ /^(CONFIG_\w+)=(.*)/ 18 | value = $2 19 | value = "true" if value == "y" 20 | 21 | puts "#define #{$1} #{value}" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/unit/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SPDX-License-Identifier: MIT 3 | 4 | set -e 5 | 6 | CC=${CC:-gcc} 7 | 8 | $CC -g -W -Wall -Wextra -Wno-incompatible-library-redeclaration -iquote ../acutest -iquote ../../include -iquote . ./allocator_test.c ../../core/alloc/buddy.c ../../core/alloc/slab.c -o allocator_test 9 | ./allocator_test 10 | 11 | $CC -g -W -Wall -Wextra -Wno-incompatible-library-redeclaration -DCONFIG_ASAN -iquote ../acutest -iquote ../../include -iquote . ./asan_test.c ../../core/alloc/asan.c -o asan_test 12 | ./asan_test 13 | -------------------------------------------------------------------------------- /include/stdio.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | 7 | #include 8 | #include 9 | 10 | int printf(const char *fmt, ...) PRINTFLIKE(1, 2); 11 | int vprintf(const char *fmt, va_list ap); 12 | 13 | int sprintf(char *str, const char *fmt, ...) PRINTFLIKE(2, 3); 14 | int vsprintf(char *str, const char *fmt, va_list ap); 15 | 16 | int snprintf(char *str, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4); 17 | int vsnprintf(char *str, size_t len, const char *fmt, va_list ap); 18 | -------------------------------------------------------------------------------- /arch/x86/keyboard.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "keyboard.h" 4 | #include "interrupt.h" 5 | #include "stdio.h" 6 | #include "x86.h" 7 | #include 8 | 9 | static void handle_keyboard(UNUSED void *data) { 10 | uint8_t key = inb(0x60); 11 | printf("Keyboard key %d\n", key); 12 | 13 | // reset the keyboard controller 14 | uint8_t a = inb(0x61); 15 | a |= 0x82; 16 | outb(0x61, a); 17 | a &= 0x7f; 18 | outb(0x61, a); 19 | } 20 | 21 | void keyboard_init(void) { irq_register(IRQ_KEYBOARD, handle_keyboard, NULL); } -------------------------------------------------------------------------------- /arch/x86/pic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "pic.h" 4 | #include "x86.h" 5 | 6 | // Disable 8259a interrupt controller 7 | void pic_disable(void) { 8 | // Set ICW1 9 | outb(0x20, 0x11); 10 | outb(0xa0, 0x11); 11 | 12 | // Set ICW2 (IRQ base offset) 13 | outb(0x21, 0x20); 14 | outb(0xa1, 0x28); 15 | 16 | // Set ICW3 17 | outb(0x21, 4); 18 | outb(0xa1, 2); 19 | 20 | // Set ICW4 21 | outb(0x21, 1); 22 | outb(0xa1, 1); 23 | 24 | // Set OCW1 (mask all interrupts) 25 | outb(0x21, 0xff); 26 | outb(0xa1, 0xff); 27 | } -------------------------------------------------------------------------------- /include/event.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | typedef void (*event_handler_t)(void *data); 9 | 10 | struct event { 11 | event_handler_t handler; 12 | void *data; 13 | }; 14 | 15 | struct event *event_peek(bool wait); 16 | 17 | // Number of events available in the buffer 18 | uint16_t deferredevent_count(void); 19 | 20 | // it will halt CPU if no events available 21 | struct event deferredevent_peek(void); 22 | 23 | // Puts a new event to buffer. Safe to call from interrupt context. 24 | void deferredevent_queue(struct event); -------------------------------------------------------------------------------- /arch/x86/cpu.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "cpu.h" 4 | #include "apic.h" 5 | #include "x86.h" 6 | 7 | size_t cpu_nodes_num = 0; 8 | struct cpu_node *cpu_nodes = NULL; 9 | 10 | PERCPU uint32_t current_cpu_id; 11 | uint32_t bootstrap_cpu_id; 12 | 13 | uint64_t cpu_cycles(void) { return x86_rdtsc(); } 14 | 15 | uint32_t cpu_id_get(void) { 16 | uint32_t apic_id = apic_cpu_id(); 17 | for (size_t i = 0; i < cpu_nodes_num; i++) { 18 | if (cpu_nodes[i].apic_id == apic_id) 19 | return i; 20 | } 21 | PANIC("Current CPU node with apic_id=%d is not registered", apic_id); 22 | } 23 | -------------------------------------------------------------------------------- /arch/x86/acpi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include 7 | 8 | // TODO: this memory region need to be configured as 'strong uncachable' 9 | extern uintptr_t g_apic_addr; 10 | extern uintptr_t g_hpet_addr; 11 | 12 | // It is the same as ACPI_MCFG_ALLOCATION 13 | struct PACKED acpi_mcfg_baseaddr_alloc { 14 | uint64_t base_addr; 15 | uint16_t segment_group_num; 16 | uint8_t start_bus_num; 17 | uint8_t end_bus_num; 18 | uint32_t __reserved0; 19 | }; 20 | extern __mmio const struct acpi_mcfg_baseaddr_alloc *g_pci_ext_config; 21 | 22 | void acpi_init(void *root); 23 | -------------------------------------------------------------------------------- /include/cpu.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "stdio.h" 7 | #include 8 | 9 | #define CPU_NODE_ID_INVALID ((uint32_t)-1) 10 | 11 | struct cpu_node { 12 | uint32_t apic_id; 13 | void *percpu_area; 14 | bool online; // SMP initialized it 15 | }; 16 | 17 | extern size_t cpu_nodes_num; 18 | extern struct cpu_node *cpu_nodes; 19 | 20 | extern PERCPU uint32_t current_cpu_id; // CPU identifier, the index in 'cpu' array 21 | extern uint32_t bootstrap_cpu_id; 22 | 23 | // Return value for CPU cycle counter 24 | uint64_t cpu_cycles(void); 25 | uint32_t cpu_id_get(void); 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | app.elf=out/app.elf 4 | 5 | .PHONY: $(app.elf) 6 | 7 | $(app.elf): 8 | shog 9 | 10 | gdb: 11 | @gdb "$(app.elf)" -ex "target remote :1234" 12 | 13 | config: 14 | @KCONFIG_CONFIG=.config mconf -s Kconfig 15 | 16 | format-code: 17 | find . -path ./third_party -prune -o -path ./out -prune -o -path ./test/acutest -prune -o -path ./app/memcached/memcached -prune -o \( -iname \*.h -o -iname \*.c \) -exec clang-format -i -style=file {} + 18 | 19 | update-regmap: 20 | find . -name *.regs -exec sh -c "./scripts/gen_regmap.rb {}; clang-format -i -style=file {}.h" \; 21 | find . -name *.rb -o name *shog.build -exec sh -c "rufo {}" \; 22 | -------------------------------------------------------------------------------- /arch/x86/multiboot.S: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | #include "multiboot1.h" 4 | 5 | .code32 6 | .section ".multiboot.header" 7 | 8 | #define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE) 9 | 10 | .type multiboot_header,STT_OBJECT 11 | multiboot_header: 12 | .int MULTIBOOT_HEADER_MAGIC /* magic */ 13 | .int MULTIBOOT_HEADER_FLAGS /* flags */ 14 | .int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) /* checksum */ 15 | 16 | .int multiboot_header /* header_addr */ 17 | .int __kernel_start /* load_addr */ 18 | .int __kernel_end /* load_end_addr */ /* TODO minus .bss .tbss */ 19 | .int 0 /* bss_end_addr */ 20 | .int _entry /* entry_addr */ -------------------------------------------------------------------------------- /drv/virtio/virtio.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "virtio_pci.h" 7 | #include "virtio_virtq.h" 8 | #include 9 | 10 | uint64_t virtio_read_device_features(__mmio struct virtio_pci_common_cfg *cfg); 11 | void virtio_write_driver_features(__mmio struct virtio_pci_common_cfg *cfg, uint64_t features); 12 | 13 | void virtio_enable_queue(__mmio struct virtio_pci_common_cfg *cfg, struct virtq *queue); 14 | void virtio_init_queue(__mmio struct virtio_pci_common_cfg *cfg, uint16_t index, struct virtq *queue, __mmio void *notify_base, 15 | uint32_t notify_off_multiplier); 16 | void virtio_queue_desc_link(struct virtq *queue); -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | aarch64 support 2 | add ASAN+TASAN+USAN 3 | add 'save crash support' and general debug tools 4 | setup symbolizer https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md 5 | setup test framework, benchmark framework; continuous testing server with qemu and real HW 6 | Implement own package manager / app starter 7 | Reimplement shog build tool to mruby 8 | Add tests, unit tests, fuzzy and integration tests 9 | add a watchdog support to track state of the application. If application is dead then it restarts the app. 10 | profiling 11 | https integration 12 | check/profile IRQ handler latency 13 | Intel performance monitoring 14 | lua server example 15 | mruby server example 16 | database based on sqlite 17 | -------------------------------------------------------------------------------- /core/sort.c: -------------------------------------------------------------------------------- 1 | #include "sort.h" 2 | #include "mem.h" 3 | #include 4 | 5 | typedef int (*cmpfun)(const void *, const void *); 6 | 7 | // A function to implement bubble sort 8 | void sort(void *arr, size_t num, size_t elem_size, cmpfun cmp) { 9 | for (size_t i = num - 1; i > 0; i--) { 10 | for (size_t j = 0; j < i; j++) { 11 | void *e1 = arr + j * elem_size; 12 | void *e2 = arr + (j + 1) * elem_size; // e1+1 element 13 | 14 | if (cmp(e1, e2) > 0) { 15 | void *temp = alloca(elem_size); 16 | memcpy(temp, e1, elem_size); 17 | memcpy(e1, e2, elem_size); 18 | memcpy(e2, temp, elem_size); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /include/net/udp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include "err.h" 7 | #include "net/ip4.h" 8 | #include 9 | 10 | struct ip4if; 11 | void udp_receive(struct ip4if *ip4if, ip4addr_t src_ip, ip4addr_t dest_ip, buffer_t *buf); 12 | void udp_send(struct ip4if *ip4if, buffer_t *buf, ip4addr_t dest_ip4, uint16_t src_port, uint16_t dest_port); 13 | 14 | typedef void (*udp_handler_t)(struct udp_listener *listener, buffer_t *buf); 15 | struct udp_listener { 16 | struct ip4if *ip4if; 17 | uint16_t port; 18 | udp_handler_t handler; 19 | LIST_ENTRY(udp_listener) next; 20 | }; 21 | 22 | err_t udp_bind(struct ip4if *ip4if, uint16_t port, udp_handler_t handler); 23 | void udp_unbind(struct udp_listener *listener); -------------------------------------------------------------------------------- /arch/x86/apic.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct apic_ioapic { 10 | uint8_t id; // IO APIC id 11 | uint32_t pin_num; // number of pins 12 | uint32_t irq_base; 13 | __mmio void *addr; 14 | }; 15 | 16 | struct apic_irq_override { 17 | uint8_t source; // Original IRQ 18 | uint32_t irq; 19 | }; 20 | 21 | extern size_t apic_ioapic_num; 22 | extern struct apic_ioapic *apic_ioapics; 23 | 24 | extern size_t apic_irq_override_num; 25 | extern struct apic_irq_override *apic_irq_overrides; 26 | 27 | void apic_init(void); 28 | uint32_t apic_cpu_id(void); 29 | void apic_interrupt_ack(void); 30 | void apic_ap_init(uint32_t apic_id); 31 | void apic_ap_start(uint32_t apic_id, uint32_t address); 32 | -------------------------------------------------------------------------------- /include/asan.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "config.h" 7 | #include 8 | #include 9 | 10 | // TODO: revise the list of memory tags 11 | #define ASAN_TAG_UNINITIALIZED 0xff 12 | #define ASAN_TAG_SLAB_FREED 0xfe 13 | 14 | // 0 means all bytes are valid 15 | // 1-7 means only first N bytes are valid and the rest is poisoned 16 | #define ASAN_TAG_RW 0x0 17 | 18 | #ifdef CONFIG_ASAN 19 | 20 | #define ASAN_REDZONE_SIZE 1 21 | 22 | void asan_enable_reporting(void); 23 | uintptr_t asan_init_shadow(uintptr_t max_addr); 24 | // tag parameter on of the ASAN_TAG_* values 25 | void asan_mark_memory_region(uintptr_t start, size_t size, uint8_t tag); 26 | 27 | #else 28 | 29 | #define ASAN_REDZONE_SIZE 0 30 | 31 | static inline void asan_enable_reporting(void) {} 32 | static inline void asan_mark_memory_region(UNUSED uintptr_t start, UNUSED size_t size, UNUSED uint8_t tag) {} 33 | 34 | #endif -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | size_t strlen(const char *s); 9 | size_t strnlen(const char *s, size_t maxlen); 10 | int strcmp(const char *s1, const char *s2); 11 | int strncmp(const char *s1, const char *s2, size_t n); 12 | char *strcat(char *restrict dest, const char *restrict src); 13 | char *strncat(char *restrict d, const char *restrict s, size_t n); 14 | char *strcpy(char *restrict dest, const char *restrict src); 15 | char *strncpy(char *dest, const char *src, size_t n); 16 | char *strstr(const char *haystack, const char *needle); 17 | 18 | int64_t strtonum(const char *nptr, const char **endptr, int base); 19 | #define strtoul strtonum 20 | 21 | int isalpha(int c); 22 | int isprint(int c); 23 | int isspace(int c); 24 | int isdigit(int c); 25 | int isxdigit(int c); 26 | 27 | int toupper(int c); 28 | int tolower(int c); 29 | 30 | int isupper(int c); 31 | int islower(int c); -------------------------------------------------------------------------------- /core/deferredevent.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "compiler.h" 4 | #include "event.h" 5 | #include "shout.h" 6 | #include 7 | 8 | #define BUFFER_SIZE 1024 // must be power of 2 9 | 10 | PERCPU struct { 11 | uint16_t event_idx; // next event to process 12 | uint16_t avail_idx; // space to add next event 13 | struct event ring[BUFFER_SIZE]; 14 | } buffer; 15 | 16 | uint16_t deferredevent_count(void) { 17 | uint16_t num = buffer.avail_idx - buffer.event_idx; 18 | return num % BUFFER_SIZE; // handle situation when avail_idx overflowed to 0 19 | } 20 | 21 | struct event deferredevent_peek(void) { 22 | PANIC_IF(buffer.avail_idx == buffer.event_idx, "Work buffer is empty"); 23 | return buffer.ring[buffer.event_idx++ % BUFFER_SIZE]; 24 | } 25 | 26 | void deferredevent_queue(struct event e) { 27 | IFD SHOUT_IF(deferredevent_count() == BUFFER_SIZE - 1, "Work buffer overflow"); 28 | buffer.ring[buffer.avail_idx++ % BUFFER_SIZE] = e; 29 | } -------------------------------------------------------------------------------- /include/blk.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include "queue.h" 7 | #include 8 | 9 | enum blk_op_status { BLK_OP_SUCCESS, BLK_OP_ERROR, BLK_OP_UNSUPPORTED }; 10 | 11 | struct blk_device; 12 | typedef void (*blk_op_callback)(struct blk_device *blk, void *data, enum blk_op_status status, void *context); 13 | 14 | struct blk_device { 15 | // Ops 16 | void (*send)(struct blk_device *blk, void *data, size_t data_size, size_t start_sector, bool write, blk_op_callback on_complete, 17 | void *callback_context); 18 | 19 | SLIST_ENTRY(blk_device) next; 20 | }; 21 | 22 | void blk_dev_register(struct blk_device *dev); 23 | struct blk_device *blk_dev_get(size_t idx); 24 | void blk_read(struct blk_device *blk, void *data, size_t size, size_t start_sector, blk_op_callback on_complete, void *context); 25 | void blk_write(struct blk_device *blk, void *data, size_t size, size_t start_sector, blk_op_callback on_complete, void *context); -------------------------------------------------------------------------------- /include/mem.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | void memset(void *p, uint8_t c, size_t n); 9 | void memset8(uint8_t *p, uint8_t c, size_t n); 10 | void memset16(uint16_t *p, uint16_t c, size_t n); 11 | void memset32(uint32_t *p, uint32_t c, size_t n); 12 | void memset64(uint64_t *p, uint64_t c, size_t n); 13 | 14 | void memcpy(void *dest, const void *src, size_t n); 15 | void memcpy8(uint8_t *dest, const uint8_t *src, size_t n); 16 | void memcpy16(uint16_t *dest, const uint16_t *src, size_t n); 17 | void memcpy32(uint32_t *dest, const uint32_t *src, size_t n); 18 | void memcpy64(uint64_t *dest, const uint64_t *src, size_t n); 19 | 20 | // Clears memory specified by the pointer 21 | // TODO: check that ptr is not void* type 22 | #define memzero(ptr) memset(ptr, 0, sizeof(*ptr)) 23 | 24 | const void *memchr(const void *s, char c, size_t n); 25 | const void *memrchr(const void *s, char c, size_t n); 26 | 27 | int memcmp(const void *vl, const void *vr, size_t n); 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Anatol Pomazau 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /core/buffer.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "buffer.h" 4 | #include "compiler.h" 5 | #include "kalloc.h" 6 | #include "shout.h" 7 | #include 8 | 9 | // allocate buffer and buffer area 10 | buffer_t *buffer_allocate(uint32_t size, uint32_t prefix_size) { 11 | void *area = kalloc_size(size); 12 | buffer_t *buf = kalloc(buffer_t); 13 | buf->area = area; 14 | buf->pos = area + prefix_size; 15 | buf->area_size = size; 16 | buf->data_size = prefix_size; 17 | return buf; 18 | } 19 | 20 | // frees buffer *and* undelying buffer area 21 | void buffer_free(buffer_t *buf) { 22 | kfree_size(buf->area, buf->area_size); 23 | kfree(buf); 24 | } 25 | 26 | bool buffer_has_more_data(buffer_t *buf) { return buffer_data_available(buf) != 0; } 27 | 28 | size_t buffer_data_available(buffer_t *buf) { 29 | IFD SHOUT_IF(buf->pos > buf->area + buf->data_size); 30 | return buf->data_size - (buf->pos - buf->area); 31 | } 32 | 33 | size_t buffer_free_space(buffer_t *buf) { return buf->area_size - buf->data_size; } 34 | -------------------------------------------------------------------------------- /arch/x86/smp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #define SMP_AP_INIT_AREA 0x1000 // AP trampoline should be below 1M and aligned to PAGE_SIZE 6 | #define SMP_AP_STACK_SIZE 0x1000 // 4K for AP stack 7 | 8 | #define STA_X 0x8 // Executable segment 9 | #define STA_E 0x4 // Expand down (non-executable segments) 10 | #define STA_C 0x4 // Conforming code segment (executable only) 11 | #define STA_W 0x2 // Writeable (non-executable segments) 12 | #define STA_R 0x2 // Readable (executable segments) 13 | #define STA_A 0x1 // Accessed 14 | 15 | #ifdef __ASSEMBLER__ 16 | 17 | #define RELOC(sym) ((sym)-smp_entry + SMP_AP_INIT_AREA) 18 | #define SEG_NULL \ 19 | .word 0, 0; \ 20 | .byte 0, 0, 0, 0 21 | 22 | #define SEG(type, base, lim) \ 23 | .word(((lim) >> 12) & 0xffff); \ 24 | .word((base)&0xffff); \ 25 | .byte(((base) >> 16) & 0xff); \ 26 | .byte(0x90 | (type)); \ 27 | .byte(0xC0 | (((lim) >> 28) & 0xf)); \ 28 | .byte(((base) >> 24) & 0xff) 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /core/blk/blk.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "blk.h" 4 | #include "queue.h" 5 | #include "stdio.h" 6 | 7 | SLIST_HEAD(, blk_device) devices = SLIST_HEAD_INITIALIZER(&devices); 8 | 9 | void blk_dev_register(struct blk_device *dev) { SLIST_INSERT_HEAD(&devices, dev, next); } 10 | 11 | struct blk_device *blk_dev_get(size_t idx) { 12 | size_t i = 0; 13 | struct blk_device *dev; 14 | 15 | // find idx element in the list 16 | SLIST_FOREACH(dev, &devices, next) { 17 | if (i == idx) { 18 | return dev; 19 | } 20 | i++; 21 | } 22 | return NULL; 23 | } 24 | 25 | void blk_read(struct blk_device *blk, void *data, size_t size, size_t start_sector, blk_op_callback on_complete, void *context) { 26 | blk->send(blk, data, size, start_sector, false, on_complete, context); 27 | } 28 | void blk_write(struct blk_device *blk, void *data, size_t size, size_t start_sector, blk_op_callback on_complete, void *context) { 29 | blk->send(blk, data, size, start_sector, true, on_complete, context); 30 | } 31 | -------------------------------------------------------------------------------- /arch/x86/Kconfig: -------------------------------------------------------------------------------- 1 | choice 2 | prompt "Console interface" 3 | default SERIAL_CONSOLE 4 | 5 | config SERIAL_CONSOLE 6 | bool "Serial console" 7 | 8 | config VGA_CONSOLE 9 | bool "VGA console" 10 | 11 | endchoice 12 | 13 | config X2APIC 14 | bool "x2APIC support" 15 | def_bool y 16 | help 17 | x2APIC is newer version of Advanced Programmable Interrupt Controller (APIC) 18 | introduced with Intel's Nahalem architecture. 19 | 20 | Choose 'N' if you want to use legacy APIC functionality. 21 | 22 | config INTEL_MICROCODE 23 | bool "Intel microcode support" 24 | def_bool y 25 | help 26 | Load Intel microcode at boot time 27 | 28 | choice 29 | prompt "ACPI adapter implementation" 30 | default ACPI_INHOUSE 31 | 32 | config ACPI_INHOUSE 33 | bool "In-house" 34 | help 35 | A simple in-house adapter 36 | 37 | config ACPI_ACPICA 38 | bool "ACPICA based" 39 | help 40 | ACPI adapter based on ACPICA library 41 | 42 | endchoice 43 | 44 | config AHCI 45 | bool "AHCI support" 46 | -------------------------------------------------------------------------------- /include/buddy.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | // Smallest size that can be served by buddy allocator 9 | #define BUDDY_SIZE_GRANULARITY (4 * 1024) 10 | 11 | // Add region of memory to allocator, its functionality similar to alloc_buddy_free() 12 | // except its begin/end might cross 2^N area. 13 | // Should be used only at the boot time 14 | void alloc_buddy_append(uintptr_t begin, uintptr_t end); 15 | 16 | // Allocates area of given size. Its functionality similar to alloc_buddy_allocate 17 | // except it does not require size to be power of 2. 18 | // Should be used only at the boot time 19 | // void *alloc_buddy_substruct(size_t size); 20 | 21 | void *alloc_buddy_allocate(uint32_t page_order); 22 | void alloc_buddy_free(void *area, uint32_t page_order); 23 | 24 | // Expands or shrinks buddy area 25 | // Returns pointer to the new area or NULL in case of error (e.g. not possible to expand 26 | // as buddy is already in use). 27 | // void *alloc_buddy_area_resize(void *area, uint32_t new_order); 28 | -------------------------------------------------------------------------------- /arch/x86/stacktrace.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "stacktrace.h" 4 | #include "elf.h" 5 | #include "stdio.h" 6 | #include 7 | 8 | /*const char *elf_symbol_name(uintptr_t ptr) { 9 | // This function does not work as linker does not want to put .symtab into data section 10 | extern Elf_Sym __symtab_begin, __symtab_end; 11 | extern char __strtab_begin; 12 | 13 | Elf_Sym *sym = &__symtab_begin; 14 | 15 | while (sym < &__symtab_end) { 16 | if (ptr >= sym->st_value && ptr < (sym->st_value + sym->st_size)) { 17 | return &__strtab_begin + sym->st_name; 18 | } 19 | sym++; 20 | } 21 | 22 | return "?"; 23 | }*/ 24 | 25 | void dump_stack(void) { 26 | intptr_t ebp; 27 | __asm__ volatile("mov %%rbp, %0" : "=r"(ebp)); 28 | print_stack_trace(ebp); 29 | } 30 | 31 | void print_stack_trace(uint64_t bp) { 32 | printf("Stacktrace:\n"); 33 | while (bp) { 34 | uint64_t *ip = (uint64_t *)bp + 1; 35 | if (*ip) { 36 | printf(" 0x%lx [bp=0x%lx]\n", *ip, bp); 37 | } 38 | bp = *(uint64_t *)bp; 39 | } 40 | } -------------------------------------------------------------------------------- /include/buffer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | void *area; // beginning of the buffer area 11 | void *pos; // currently processed position 12 | uint32_t area_size; // total size of the buffer area 13 | uint32_t data_size; // size of data available in the buffer 14 | } buffer_t; 15 | 16 | // allocate buffer and buffer area 17 | buffer_t *buffer_allocate(uint32_t size, uint32_t prefix_size); 18 | 19 | // frees buffer *and* undelying buffer area 20 | void buffer_free(buffer_t *buffer); 21 | 22 | bool buffer_has_more_data(buffer_t *buf); 23 | size_t buffer_data_available(buffer_t *buf); 24 | 25 | // Returns how much free space this buffer has 26 | size_t buffer_free_space(buffer_t *buf); 27 | 28 | #define BUFFER_NET_SIZE 2048 // default size for network buffer, it should fit one IP datagram with ethernet header 29 | 30 | #define HDR_LEN_RAW 0 31 | #define HDR_LEN_ETH (HDR_LEN_RAW + 14U) 32 | #define HDR_LEN_IP4 (HDR_LEN_ETH + 20U) 33 | #define HDR_LEN_UDP (HDR_LEN_IP4 + 8U) 34 | #define HDR_LEN_TCP (HDR_LEN_IP4 + 20U) -------------------------------------------------------------------------------- /include/net/eth.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include "compiler.h" 7 | #include "ip4.h" 8 | #include "ip6.h" 9 | #include "queue.h" 10 | #include 11 | #include 12 | #include 13 | 14 | typedef struct { 15 | uint8_t addr[6]; 16 | } ethaddr_t; 17 | 18 | struct eth_device { 19 | ethaddr_t addr; 20 | struct ip4if ip4if; 21 | struct ip6if ip4if6; 22 | 23 | // Ops 24 | void (*send)(struct eth_device *eth, buffer_t *buff); 25 | }; 26 | 27 | #define ETHADDR_PRINT_FMT "%02x:%02x:%02x:%02x:%02x:%02x" 28 | #define ETHADDR_PRINT_PARAMS(var) (var.addr)[0], (var.addr)[1], (var.addr)[2], (var.addr)[3], (var.addr)[4], (var.addr)[5] 29 | 30 | extern const ethaddr_t ETH_BROADCAST; 31 | 32 | // eth_hdr.type field 33 | #define ETH_TYPE_IP4 0x0800 34 | #define ETH_TYPE_ARP 0x0806 35 | #define ETH_TYPE_IP6 0x86dd 36 | 37 | void eth_dev_register(struct eth_device *dev); 38 | void eth_receive(struct eth_device *dev, buffer_t *buf); 39 | void eth_send(struct eth_device *dev, const ethaddr_t *dest, uint16_t type, buffer_t *buff); 40 | 41 | void ethaddr_cpy(uint8_t *dest, const uint8_t *src); 42 | -------------------------------------------------------------------------------- /test/acutest/LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | 4 | Copyright © 2013-2017 Martin Mitáš 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the “Software”), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /include/sha3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /* 'Words' here refers to uint64_t */ 7 | #define SHA3_KECCAK_SPONGE_WORDS (((1600) / 8 /*bits to byte*/) / sizeof(uint64_t)) 8 | 9 | typedef struct { 10 | uint64_t saved; /* the portion of the input message that we 11 | * didn't consume yet */ 12 | union { /* Keccak's state */ 13 | uint64_t s[SHA3_KECCAK_SPONGE_WORDS]; 14 | uint8_t sb[SHA3_KECCAK_SPONGE_WORDS * 8]; 15 | }; 16 | unsigned byteIndex; /* 0..7--the next byte after the set one 17 | * (starts from 0; 0--none are buffered) */ 18 | unsigned wordIndex; /* 0..24--the next word to integrate input 19 | * (starts from 0) */ 20 | unsigned capacityWords; /* the double size of the hash output in 21 | * words (e.g. 16 for Keccak 512) */ 22 | } sha3_context; 23 | 24 | void sha3_Init256(sha3_context *ctx); 25 | void sha3_Init384(sha3_context *ctx); 26 | void sha3_Init512(sha3_context *ctx); 27 | void sha3_Update(sha3_context *ctx, void const *bufIn, size_t len); 28 | void const *sha3_Finalize(sha3_context *ctx); -------------------------------------------------------------------------------- /arch/x86/serial.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "console.h" 4 | #include "x86.h" 5 | 6 | #define PORT 0x3f8 /* COM1 */ 7 | 8 | void console_init(void) { 9 | outb(PORT + 1, 0x00); // Disable all interrupts 10 | outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) 11 | outb(PORT + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud 12 | outb(PORT + 1, 0x00); // (hi byte) 13 | outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit 14 | outb(PORT + 2, 0xc7); // Enable FIFO, clear them, with 14-byte threshold 15 | outb(PORT + 4, 0x0b); // IRQs enabled, RTS/DSR set 16 | } 17 | 18 | static uint8_t is_transmit_empty() { return inb(PORT + 5) & BIT(5); } 19 | 20 | static void symbol_write(char ch) { 21 | while (is_transmit_empty() == 0) 22 | ; 23 | 24 | outb(PORT, ch); 25 | } 26 | 27 | void console_write(const char *str, size_t len) { 28 | for (size_t i = 0; i < len; i++) { 29 | char ch = *str++; 30 | if (ch == '\n') 31 | symbol_write('\r'); 32 | symbol_write(ch); 33 | } 34 | } 35 | 36 | static uint8_t serial_received() { return inb(PORT + 5) & BIT(0); } 37 | 38 | char console_read() { 39 | while (serial_received() == 0) 40 | ; 41 | 42 | return inb(PORT); 43 | } -------------------------------------------------------------------------------- /app/http-hello/test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # SPDX-License-Identifier: MIT 3 | 4 | require "net/http" 5 | require "test/unit" 6 | require "typhoeus" 7 | require_relative "../common/integration" 8 | 9 | DEBUG = ENV["DEBUG"] 10 | ROOT = File.join(File.dirname(__FILE__), "../../../") 11 | 12 | class TestHttpService < Test::Unit::TestCase 13 | def setup 14 | # TODO: in the future when we have correct app build encapsulation 15 | # we will need to run config / build for our application. 16 | # config.generate 17 | # shog.build 18 | @qemu = fork do 19 | exec "qemu-system-x86_64 -cpu max -smp 4 -kernel #{ROOT}/out/app.elf -device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:80 -serial file:application.log --no-reboot -display none" 20 | end 21 | sleep(1) 22 | end 23 | 24 | def teardown 25 | Process.kill("HUP", @qemu) 26 | # unicycle.check_no_leaks 27 | end 28 | 29 | def test_service 30 | hydra = Typhoeus::Hydra.new(max_concurrency: 80) 31 | 100.times do 32 | request = Typhoeus::Request.new("localhost:5555", followlocation: true) 33 | request.on_complete do |response| 34 | assert response.success? 35 | assert_equal "

Hello, World!

", response.body 36 | end 37 | hydra.queue(request) 38 | end 39 | hydra.run 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /arch/x86/pit.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // Programmable Interval Timer 4 | 5 | #include "x86.h" 6 | #include 7 | 8 | #define FREQ 1193182 9 | 10 | #define PORT_COUNTER0 0x40 11 | #define PORT_CMD 0x43 12 | 13 | // Command Register 14 | 15 | // BCD 16 | #define CMD_BINARY 0x00 // Use Binary counter values 17 | #define CMD_BCD 0x01 // Use Binary Coded Decimal counter values 18 | 19 | // Mode 20 | #define CMD_MODE0 0x00 // Interrupt on Terminal Count 21 | #define CMD_MODE1 0x02 // Hardware Retriggerable One-Shot 22 | #define CMD_MODE2 0x04 // Rate Generator 23 | #define CMD_MODE3 0x06 // Square Wave 24 | #define CMD_MODE4 0x08 // Software Triggered Strobe 25 | #define CMD_MODE5 0x0a // Hardware Triggered Strobe 26 | 27 | // Read/Write 28 | #define CMD_LATCH 0x00 29 | #define CMD_RW_LOW 0x10 // Least Significant Byte 30 | #define CMD_RW_HI 0x20 // Most Significant Byte 31 | #define CMD_RW_BOTH 0x30 // Least followed by Most Significant Byte 32 | 33 | // Counter Select 34 | #define CMD_COUNTER0 0x00 35 | #define CMD_COUNTER1 0x40 36 | #define CMD_COUNTER2 0x80 37 | #define CMD_READBACK 0xc0 38 | 39 | void pit_init(void) { 40 | uint16_t hz = 100; 41 | uint16_t divisor = FREQ / hz; 42 | outb(PORT_CMD, CMD_BINARY | CMD_MODE3 | CMD_RW_BOTH | CMD_COUNTER0); 43 | outb(PORT_COUNTER0, (uint8_t)divisor); 44 | outb(PORT_COUNTER0, (uint8_t)(divisor >> 8)); 45 | } -------------------------------------------------------------------------------- /core/multibyte.c: -------------------------------------------------------------------------------- 1 | /* This file is based on MUSL project code */ 2 | 3 | #include "multibyte.h" 4 | #include "compiler.h" 5 | #include 6 | 7 | #define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80) 8 | #define CURRENT_UTF8 true 9 | #define MB_CUR_MAX (CURRENT_UTF8 ? 4 : 1) 10 | 11 | size_t wcrtomb(char *restrict s, wchar_t wc, UNUSED mbstate_t *restrict st) { 12 | if (!s) 13 | return 1; 14 | if ((unsigned)wc < 0x80) { 15 | *s = wc; 16 | return 1; 17 | } else if (MB_CUR_MAX == 1) { 18 | if (!IS_CODEUNIT(wc)) { 19 | return -1; 20 | } 21 | *s = wc; 22 | return 1; 23 | } else if ((unsigned)wc < 0x800) { 24 | *s++ = 0xc0 | (wc >> 6); 25 | *s = 0x80 | (wc & 0x3f); 26 | return 2; 27 | } else if ((unsigned)wc < 0xd800 || (unsigned)wc - 0xe000 < 0x2000) { 28 | *s++ = 0xe0 | (wc >> 12); 29 | *s++ = 0x80 | ((wc >> 6) & 0x3f); 30 | *s = 0x80 | (wc & 0x3f); 31 | return 3; 32 | } else if ((unsigned)wc - 0x10000 < 0x100000) { 33 | *s++ = 0xf0 | (wc >> 18); 34 | *s++ = 0x80 | ((wc >> 12) & 0x3f); 35 | *s++ = 0x80 | ((wc >> 6) & 0x3f); 36 | *s = 0x80 | (wc & 0x3f); 37 | return 4; 38 | } 39 | return -1; 40 | } 41 | 42 | int wctomb(char *s, wchar_t wc) { 43 | if (!s) 44 | return 0; 45 | return wcrtomb(s, wc, NULL); 46 | } 47 | -------------------------------------------------------------------------------- /core/unicycle.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "unicycle.h" 4 | #include "compiler.h" 5 | #include "cpu.h" 6 | #include "event.h" 7 | #include "shout.h" 8 | 9 | #include "stdio.h" 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_DEBUG) && defined(CONFIG_SMP) 14 | 15 | #define PERCPU_MARKER 0xbeef5520 16 | 17 | PERCPU uint32_t percpu_marker = PERCPU_MARKER; 18 | 19 | // Setting up per-CPU data area is a bit tricky. Let's check that it works correctly. 20 | // Make sure that .tdata value is exactly as expected. 21 | static void check_percpu_data_location(void) { 22 | PANIC_IF(percpu_marker != PERCPU_MARKER, "Invalid .tdata value. Check that percpu area is initialized correctly."); 23 | } 24 | #else 25 | static void check_percpu_data_location(void) {} 26 | #endif 27 | 28 | NORETURN void unicycle_loop(void) { 29 | check_percpu_data_location(); 30 | 31 | bool wait_for_irq = true; // wait for interruption if there are no deferred events to process 32 | while (true) { 33 | struct event *irq = event_peek(wait_for_irq); 34 | if (irq) { 35 | irq->handler(irq->data); 36 | } 37 | 38 | if (deferredevent_count() != 0) { 39 | struct event e = deferredevent_peek(); 40 | e.handler(e.data); 41 | wait_for_irq = (deferredevent_count() == 0); 42 | } else { 43 | wait_for_irq = true; 44 | } 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /include/timer.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "tree.h" 7 | #include 8 | #include 9 | 10 | void timer_system_init(void); 11 | 12 | // An architecture might have multiple timers with different characteristics (first of all time granularity) 13 | // Following type is timer-specific, its granularity depends on the timer used by the system 14 | typedef uint64_t time_t; 15 | struct timer; 16 | 17 | // TODO: or maybe use more generic event_handler_t? 18 | typedef void (*timer_callback)(void *context); 19 | 20 | struct timer *timer_add(time_t alarm_time, timer_callback callback, void *context); 21 | void timer_change(struct timer *timer, time_t alarm_time); 22 | void timer_delete(struct timer *timer); 23 | 24 | time_t time_now(void); 25 | time_t time_ns_from_now(uint64_t ns); 26 | time_t time_us_from_now(uint64_t us); 27 | time_t time_ms_from_now(uint64_t ms); 28 | time_t time_sec_from_now(uint64_t sec); 29 | time_t time_min_from_now(uint64_t min); 30 | time_t time_hours_from_now(uint64_t hours); 31 | time_t time_days_from_now(uint64_t days); 32 | 33 | void sleep_us(uint64_t value); 34 | void sleep_ns(uint64_t value); 35 | 36 | // return true if timeout, false otherwise 37 | bool wait_for_clear(__mmio const uint32_t *reg, uint32_t mask, time_t timeout); 38 | // return true if timeout, false otherwise 39 | bool wait_for_set(__mmio const uint32_t *reg, uint32_t mask, time_t timeout); 40 | -------------------------------------------------------------------------------- /include/lock.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "config.h" 7 | #include "kconfig.h" 8 | #include "shout.h" 9 | #include 10 | 11 | #ifdef CONFIG_SMP 12 | 13 | #include 14 | 15 | typedef atomic_bool lock_t; 16 | 17 | #define lock_init() ATOMIC_VAR_INIT(false) 18 | 19 | static inline void lock(lock_t *l) { 20 | bool expected = false; 21 | while (!atomic_compare_exchange_weak_explicit(l, &expected, true, memory_order_acquire, memory_order_relaxed)) { 22 | expected = false; 23 | if (IS_ENABLED(CONFIG_ARCH_X86)) { 24 | // x86 spin lock hint 25 | __asm__ volatile("pause"); 26 | } 27 | }; 28 | } 29 | 30 | static inline bool trylock(lock_t *l) { 31 | bool expected = false; 32 | return atomic_compare_exchange_strong_explicit(l, &expected, true, memory_order_acquire, memory_order_relaxed); 33 | } 34 | 35 | static inline void unlock(lock_t *l) { 36 | bool expected = true; 37 | bool was_locked = atomic_compare_exchange_strong_explicit(l, &expected, false, memory_order_release, memory_order_relaxed); 38 | PANIC_IF(!was_locked, "Trying to unlock non-locked object"); 39 | } 40 | 41 | #else 42 | 43 | typedef struct lock { 44 | } lock_t; 45 | 46 | #define lock_init() \ 47 | {} 48 | 49 | static inline void lock(UNUSED lock_t *l) {} 50 | static inline bool trylock(UNUSED lock_t *l) { return true; } 51 | static inline void unlock(UNUSED lock_t *l) {} 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /arch/x86/smp_trampoline.S: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # This is the very first code executed by an AP during SMP initialization. 4 | # AP initialization starts in 16-bit mode and no stack is set. 5 | # We need to switch AP to 32-bit mode, setup stack pointer 6 | # and then we can use initialization logic implemented in C 7 | # See more info about AP initialization in Intel documentation 8.4.4.2 8 | 9 | #include "smp.h" 10 | 11 | .section .smp.trampoline 12 | .global smp_entry 13 | 14 | .code16 15 | smp_entry: 16 | cli 17 | 18 | # Switch to protected mode. See Intel documentation chapter 9.8 19 | lgdt RELOC(smp.gdt.pointer) 20 | 21 | mov %cr0, %eax 22 | or $1, %eax # set BIT(0) to enable protected mode 23 | mov %eax, %cr0 24 | 25 | jmp $0x8, $(RELOC(protected_mode)) 26 | 27 | .code32 28 | protected_mode: 29 | mov $0x10, %ax 30 | mov %ax, %ds 31 | mov %ax, %es 32 | mov %ax, %fs 33 | mov %ax, %gs 34 | mov %ax, %ss 35 | 36 | # 1024 is size of the AP stack, it should be equal to SMP_AP_STACK_SIZE 37 | mov $SMP_AP_STACK_SIZE, %eax 38 | # each initialized AP atomically increases the counter and uses the allocated area 39 | # for its stack 40 | lock xadd %eax, smp_stack_pointer 41 | mov %eax, %esp 42 | 43 | jmp $0x8, $(smp_ap_start) 44 | 45 | .p2align 2 46 | smp.gdt: 47 | SEG_NULL # null 48 | SEG(STA_X | STA_R, 0, 0xffffffff) # code 49 | SEG(STA_W, 0, 0xffffffff) # data 50 | smp.gdt.pointer: 51 | .word smp.gdt.pointer - smp.gdt - 1 52 | .long RELOC(smp.gdt) 53 | -------------------------------------------------------------------------------- /arch/x86/mmu.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "shout.h" 7 | #include "x86.h" 8 | #include 9 | 10 | #define NULL_SELECTOR 0x0 11 | #define CODE_SELECTOR 0x8 12 | #define DATA_SELECTOR 0x10 13 | 14 | // Top 16 bits is 4K page aligned address, low 16 bits are page flags 15 | typedef uint64_t page_entry; 16 | BUILD_PANIC_IF(sizeof(page_entry) != 8, "page structure must be 64-bit long"); 17 | 18 | #define PAGE_PRESENT BIT(0) 19 | #define PAGE_WRITABLE BIT(1) 20 | #define PAGE_USERMODE BIT(2) // if the page flag is set then user-mode allows access this memory 21 | #define PAGE_WRITE_THROUGH BIT(3) 22 | #define PAGE_CACHE_DISABLE BIT(4) 23 | #define PAGE_ACCESSED BIT(5) // system accessed this page 24 | #define PAGE_DIRTY BIT(6) 25 | #define PAGE_LARGE BIT(7) // 1G, 2M or 4K page 26 | #define PAGE_NO_EXECUTABLE BIT(63) 27 | 28 | #define PAGE_ENTRIES_PER_TABLE (PAGE_SIZE / sizeof(page_entry)) 29 | BUILD_PANIC_IF(PAGE_ENTRIES_PER_TABLE != 512); 30 | extern page_entry p4_table[PAGE_ENTRIES_PER_TABLE]; 31 | 32 | struct PACKED gdt64_pointer { 33 | uint16_t size; 34 | uint64_t pointer; 35 | }; 36 | extern const struct gdt64_pointer gdt64_pointer; 37 | 38 | void page_table_set_bit(size_t start, size_t length, uint64_t mask, uint64_t bits); 39 | void page_table_set_readable(size_t start, size_t length, bool readable); 40 | void page_table_set_writable(size_t start, size_t length, bool writable); 41 | void page_table_set_cacheable(size_t start, size_t length, bool cacheable); 42 | void page_table_set_executable(size_t start, size_t length, bool executable); 43 | 44 | void page_table_dump(void); -------------------------------------------------------------------------------- /arch/x86/shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | obj = [] 4 | 5 | srcs = %w[ 6 | cpu.c 7 | interrupt.c 8 | interrupt_handlers.S 9 | ioapic.c 10 | hpet.c 11 | keyboard.c 12 | mmu.c 13 | pic.c 14 | pci.c 15 | rtc.c 16 | stacktrace.c 17 | start.c 18 | ] 19 | srcs << "smp_trampoline.S" if @config[:SMP] 20 | srcs << "acpi.c" if @config[:ACPI_INHOUSE] 21 | 22 | srcs << "ahci.c" if @config[:AHCI] 23 | 24 | # pit.c PIT is replaced with HPET 25 | 26 | srcs << (@config[:X2APIC] ? "x2apic.c" : "xapic.c") 27 | 28 | srcs << "serial.c" if @config[:SERIAL_CONSOLE] 29 | srcs << "vga_console.c" if @config[:VGA_CONSOLE] 30 | 31 | @rule[:cc].includes << "include" 32 | 33 | obj += emit_each(:cc, srcs) 34 | 35 | if @config[:INTEL_MICROCODE] 36 | ucode_h = Path.make("third_party/intel-ucode/intel-ucode.h", :outoftree => true, :root => true) 37 | obj += emit(:cc, "microcode.c", :includes => ucode_h.dir, :implicit_input => ucode_h) 38 | end 39 | 40 | # Multiboot loads in 32bit mode, thus initial boot sequence need to be a 32bit binary. 41 | # This 32bit code sets up hardware and switches to 64 bit mode. 42 | # So we do a trick here, compile obj file as 32bit and then use 'objcopy' to convert 43 | # to 64 bit for the linker 44 | 45 | src32 = [] 46 | src32 << "start_smp_32.c" if @config[:SMP] 47 | 48 | # objcopy does not like files with LTO data 49 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81342 50 | # thus disable LTO 51 | obj32 = emit_each(:cc, src32, :cflags => "-m32 -mno-sse -fno-lto") 52 | obj += emit_each(:objcopy, obj32, :suffix => ".64", :arch => "elf64-x86-64") 53 | 54 | obj += visit("acpica") if @config[:ACPI_ACPICA] 55 | 56 | obj 57 | -------------------------------------------------------------------------------- /include/net/tcp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include "err.h" 7 | #include "ip4.h" 8 | #include 9 | 10 | struct ip4_hdr; 11 | 12 | enum tcp_state { 13 | TCP_STATE_SYN_CLOSED = 0, 14 | TCP_STATE_SYN_RECEIVED, 15 | TCP_STATE_SYN_SENT, 16 | TCP_STATE_ESTABLISHED, 17 | TCP_STATE_FIN_WAIT_1, 18 | TCP_STATE_FIN_WAIT_2, 19 | TCP_STATE_CLOSING, 20 | TCP_STATE_TIME_WAIT, 21 | TCP_STATE_CLOSE_WAIT, 22 | TCP_STATE_LAST_ACK, 23 | }; 24 | 25 | struct tcp_connection { 26 | struct ip4if *ip4if; 27 | uint16_t port; // local port 28 | ip4addr_t remote_ip; 29 | uint16_t remote_port; 30 | uint32_t local_seq; // *next* byte that we will send 31 | uint32_t remote_seq; // *next* byte remote peer will sent 32 | // XXX: add delayed ACK mechanism 33 | enum tcp_state state : 4; 34 | uint16_t flags : 12; 35 | const struct tcp_ops *ops; 36 | void *user_data; // Pointer that can be used by user to preserve per-connection data 37 | LIST_ENTRY(tcp_connection) next; 38 | }; 39 | 40 | struct tcp_ops { 41 | // Returns true if user successfully accepted the connection, false otherwise 42 | // If false is returned then the TCP connection will be closed and *conn pointer destroyed 43 | bool (*accept)(struct tcp_connection *conn); 44 | void (*receive)(struct tcp_connection *conn, buffer_t *buf); 45 | void (*finish)(struct tcp_connection *conn); 46 | }; 47 | 48 | err_t tcp_bind(struct ip4if *ip4if, uint16_t port, const struct tcp_ops *ops); 49 | void tcp_unbind(struct tcp_listener *listener); 50 | 51 | void tcp_receive(struct ip4if *ip4if, ip4addr_t src_ip, buffer_t *buf); 52 | void tcp_send(struct tcp_connection *conn, buffer_t *buf); 53 | void tcp_close(struct tcp_connection *conn); -------------------------------------------------------------------------------- /scripts/gen_regmap.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | regname = nil 4 | fieldname = nil 5 | maskstart = nil 6 | 7 | filename = ARGV[0] 8 | outf = File.open(filename + ".h", "w") 9 | outf.puts "// Generated by gen_regmap.rb #{filename}" 10 | outf.puts 11 | outf.puts "#pragma once" 12 | outf.puts 13 | 14 | for l in File.readlines(filename) 15 | case l 16 | when /^ *;(.*)$/ # a comment 17 | comment = $1.strip 18 | outf.puts "// " + comment 19 | when /^(\h+)?: (\w+)( *;(.*))?$/ 20 | # NOTE: register value is hex, field values are decimals. That is the usual datasheet representation 21 | outf.puts 22 | 23 | num = $1 24 | regname = $2 25 | comment = $4 26 | comment = " // " + comment.strip if comment 27 | outf.puts "#define REG_#{regname} 0x#{num}#{comment}" if num 28 | 29 | fieldname = nil 30 | maskstart = nil 31 | when /^ (\d+): (\w+)( *;(.*))?$/ # single bit register 32 | num = $1 33 | fieldname = $2 34 | comment = $4 35 | comment = " // " + comment.strip if comment 36 | outf.puts "#define #{regname}_#{fieldname} ((uint64_t)1 << #{num})#{comment}" 37 | 38 | fieldname = nil 39 | maskstart = nil 40 | when /^ (\d+)\.\.(\d+): (\w+)( *;(.*))?$/ # multiple bits register 41 | maskstart = $1.to_i 42 | maskfinish = $2.to_i 43 | mask = ((2 ** (maskfinish - maskstart + 1)) - 1).to_s(16) 44 | fieldname = $3 45 | comment = $5 46 | comment = " // " + comment.strip if comment 47 | outf.puts "#define #{regname}_#{fieldname}_MASK ((uint64_t)0x#{mask} << #{maskstart})#{comment}" 48 | when /^ (\d+): (\w+)( *;(.*))?$/ # enum value for 49 | num = $1 50 | enumname = $2 51 | comment = $4 52 | comment = " // " + comment.strip if comment 53 | outf.puts "#define #{regname}_#{fieldname}_#{enumname} ((uint64_t)#{num} << #{maskstart})#{comment}" 54 | end 55 | end 56 | 57 | outf.close 58 | -------------------------------------------------------------------------------- /core/net/icmp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "net/icmp.h" 4 | #include "buffer.h" 5 | #include "compiler.h" 6 | #include "stdio.h" 7 | #include 8 | 9 | struct __be PACKED icmp_hdr { 10 | uint8_t type; 11 | uint8_t code; 12 | uint16_t checksum; 13 | // rest of the header, content meaning depends on type 14 | }; 15 | 16 | #define ICMP_TYPE_ECHO_REPLY 0 17 | #define ICMP_TYPE_DEST_UNREACHABLE 3 18 | #define ICMP_TYPE_SRC_QUENCH 4 19 | #define ICMP_TYPE_REDIRECT 5 20 | #define ICMP_TYPE_ECHO_REQUEST 8 21 | #define ICMP_TYPE_ROUTER_AD 9 22 | #define ICMP_TYPE_ROUTER_SOLICIT 10 23 | #define ICMP_TYPE_TIME_EXCEEDED 11 24 | #define ICMP_TYPE_BAD_IP_HDR 12 25 | #define ICMP_TYPE_TIMESTAMP 13 26 | #define ICMP_TYPE_TIMESTAMP_REPLY 14 27 | 28 | // code for ICMP_TYPE_DEST_UNREACHABLE type 29 | #define ICMP_UNREACH_NETWORK 0 30 | #define ICMP_UNREACH_HOST 1 31 | #define ICMP_UNREACH_PROTOCOL 2 32 | #define ICMP_UNREACH_PORT 3 33 | #define ICMP_UNREACH_FRAG_REQUIRED 4 34 | #define ICMP_UNREACH_SRC_ROUTE_FAILED 5 35 | #define ICMP_UNREACH_DEST_NET_UNKNOWN 6 36 | #define ICMP_UNREACH_DEST_HOST_UNKNOWN 7 37 | #define ICMP_UNREACH_SRC_ISOLATED 8 38 | #define ICMP_UNREACH_NET_PROHIBITED 9 39 | #define ICMP_UNREACH_HOST_PROHIBITED 10 40 | #define ICMP_UNREACH_NET_TOS 11 41 | #define ICMP_UNREACH_HOST_TOS 12 42 | #define ICMP_UNREACH_COMM_PROHIBITED 13 43 | #define ICMP_UNREACH_PRECEDENCE_VIOLATION 14 44 | #define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 45 | 46 | // code for ICMP_TYPE_REDIRECT type 47 | #define ICMP_REDIR_NET 0 48 | #define ICMP_REDIR_HOST 1 49 | #define ICMP_REDIR_TOS_NET 2 50 | #define ICMP_REDIR_TOS_HOST 3 51 | 52 | void icmp_receive(UNUSED struct ip4if *ip4if, buffer_t *buff) { 53 | struct icmp_hdr *hdr = buff->pos; 54 | buff->pos += sizeof(struct icmp_hdr); 55 | 56 | printf("ICMP type=%d, code=%d\n", hdr->type, hdr->code); 57 | 58 | buffer_free(buff); 59 | } 60 | -------------------------------------------------------------------------------- /include/new_list.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "stdio.h" 4 | #include 5 | #include 6 | 7 | struct list_head { 8 | struct list_head *next, *prev; 9 | }; 10 | 11 | static inline void INIT_LIST_HEAD(struct list_head *list) { 12 | list->next = list; 13 | list->prev = list; 14 | } 15 | 16 | static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { 17 | next->prev = new; 18 | new->next = next; 19 | new->prev = prev; 20 | prev->next = new; 21 | } 22 | 23 | static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } 24 | 25 | #define list_entry(ptr, type, member) container_of(ptr, type, member) 26 | 27 | #define define_list(name, type) \ 28 | struct name##_head { \ 29 | struct list_head _private; \ 30 | }; \ 31 | static inline type *name##_head_first(struct name##_head *head) { return list_entry(head->_private.next, type, name); } \ 32 | static inline type *name##_next(const type *node) { return list_entry(node->name.next, type, name); } \ 33 | static inline bool name##_is_last(struct name##_head *head, const type *node) { return &head->_private == &node->name; } \ 34 | static inline void name##_init(struct name##_head *head) { INIT_LIST_HEAD(&head->_private); } \ 35 | static inline void name##_add_tail(struct name##_head *head, type *new) { list_add_tail(&new->name, &head->_private); } 36 | -------------------------------------------------------------------------------- /arch/x86/start_smp_32.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "compiler.h" 4 | #include "config.h" 5 | #include "mmu.h" 6 | #include "multiboot1.h" 7 | #include "stdio.h" 8 | #include "x86.h" 9 | #include "x86_init.h" 10 | #include 11 | #include 12 | #include 13 | 14 | // Number of AP already initialized 15 | INIT_DATA atomic_int ap_count = 0; 16 | 17 | void start_ap_64(void); 18 | 19 | // Starting point of execution for AP 20 | INIT_CODE NORETURN void smp_ap_start(void) { 21 | atomic_fetch_add_explicit(&ap_count, 1, memory_order_relaxed); 22 | 23 | fpu_init(); 24 | sse_init(); 25 | 26 | x86_set_cr3((uintptr_t)p4_table); 27 | // enable PAE 28 | x86_set_cr4(x86_get_cr4() | CR4_PAE); 29 | 30 | // set long mode bit 31 | uint64_t mode_msr = x86_rdmsr(MSR_EXT_FEATURES); 32 | mode_msr |= MSR_EXT_FEATURES_LONG_MODE | MSR_EXT_FEATURES_NO_EXECUTE; 33 | x86_wrmsr(MSR_EXT_FEATURES, mode_msr); 34 | 35 | // enable paging 36 | x86_set_cr0(x86_get_cr0() | CR0_PG); 37 | 38 | // load global description table 39 | __asm__ volatile("lgdt %0" ::"m"(gdt64_pointer)); 40 | 41 | // We jump to 64-bit function and its ABI requires 16-byte stack alignment (to make SSE work properly with stack-located data). 42 | // Make it aligned according to the spec. 43 | // It is an equivalent of using ((force_align_arg_pointer)) function attribute in the callee, 44 | // unfortunately Clang does not support the attribute for x86_64 callees http://lists.llvm.org/pipermail/cfe-dev/2017-June/054359.html 45 | __asm__ volatile("and %0, %%esp" ::"irm"(-16)); 46 | __asm__ volatile("sub %0, %%esp" ::"irm"(8)); // 8 bytes that x86_64 ABI uses for %eip and %cs values 47 | // jump to long mode. 'call' does not work in qemu https://bugs.launchpad.net/qemu/+bug/1699867 48 | __asm__("jmp %0, %1" ::"i"(CODE_SELECTOR), "p"(start_ap_64)); 49 | __builtin_unreachable(); 50 | } -------------------------------------------------------------------------------- /scripts/bin2c.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # SPDX-License-Identifier: MIT 3 | 4 | require "optparse" 5 | 6 | @section = "" 7 | OptionParser.new do |opts| 8 | opts.on("", "--section=SECTION", "Section to put the variables") do |s| 9 | @section = "__attribute__((section(\"#{s}\"))) " 10 | end 11 | end.parse! 12 | 13 | def var_name(filename) 14 | "BLOB_" + File.basename(filename).gsub(/[\.\-]/, "_").upcase 15 | end 16 | 17 | def process_file(filename, out, outh, outc) 18 | var = var_name(out) 19 | size = File.size(filename) 20 | 21 | outh.write "extern const uint8_t #{var}[#{size}];\n" 22 | outc.write "#{@section}const uint8_t #{var}[#{size}] = {" 23 | 24 | File.new(filename).each_byte() do |b| 25 | outc.write b.to_s 26 | outc.write ", " 27 | end 28 | 29 | outc.write "};\n" 30 | 31 | return var, size 32 | end 33 | 34 | inp, out = *ARGV 35 | 36 | outh = File.new(out + ".h", "w") 37 | outc = File.new(out + ".c", "w") 38 | 39 | outh.write "// Generated from content of #{inp}\n" 40 | outh.write "#pragma once\n\n" 41 | outh.write "#include \n" 42 | outh.write "#include \n\n" 43 | 44 | outc.write "// Generated from #{inp}\n\n" 45 | outc.write "#include \n" 46 | outc.write "#include \"#{File.basename(out)}.h\"\n\n" 47 | 48 | if File.directory?(inp) 49 | # XXX right now we handle only 1 level deep, maybe we should scan dirs recurvively? 50 | 51 | files = [] 52 | for f in Dir.glob(inp + "/*") 53 | var, size = process_file(f, f, outh, outc) 54 | files << [var, size] 55 | end 56 | 57 | # all files in a directory also represented as an array 58 | var = var_name(out) 59 | outh.write "\nstruct blob_record {const void *data; const size_t size;};\n" 60 | outh.write "extern const struct blob_record #{var}[#{files.size}];\n" 61 | outc.write "\n#{@section}const struct blob_record #{var}[#{files.size}] = {" 62 | for f in files 63 | outc.write "{#{f[0]}, #{f[1]}}, " 64 | end 65 | outc.write "};\n" 66 | else 67 | process_file(inp, out, outh, outc) 68 | end 69 | 70 | outh.close 71 | outc.close 72 | -------------------------------------------------------------------------------- /core/mem.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "mem.h" 4 | 5 | void memset(void *p, uint8_t c, size_t n) { memset8(p, c, n); } 6 | 7 | void memset8(uint8_t *p, uint8_t c, size_t n) { 8 | for (size_t i = 0; i < n; i++) { 9 | *p++ = c; 10 | } 11 | } 12 | 13 | void memset16(uint16_t *p, uint16_t c, size_t n) { 14 | for (size_t i = 0; i < n; i++) { 15 | *p++ = c; 16 | } 17 | } 18 | 19 | void memset32(uint32_t *p, uint32_t c, size_t n) { 20 | for (size_t i = 0; i < n; i++) { 21 | *p++ = c; 22 | } 23 | } 24 | 25 | void memset64(uint64_t *p, uint64_t c, size_t n) { 26 | for (size_t i = 0; i < n; i++) { 27 | *p++ = c; 28 | } 29 | } 30 | 31 | void memcpy(void *dest, const void *src, size_t n) { memcpy8(dest, src, n); } 32 | 33 | void memcpy8(uint8_t *dest, const uint8_t *src, size_t n) { 34 | for (size_t i = 0; i < n; i++) { 35 | *dest++ = *src++; 36 | } 37 | } 38 | 39 | void memcpy16(uint16_t *dest, const uint16_t *src, size_t n) { 40 | for (size_t i = 0; i < n; i++) { 41 | *dest++ = *src++; 42 | } 43 | } 44 | 45 | void memcpy32(uint32_t *dest, const uint32_t *src, size_t n) { 46 | for (size_t i = 0; i < n; i++) { 47 | *dest++ = *src++; 48 | } 49 | } 50 | 51 | void memcpy64(uint64_t *dest, const uint64_t *src, size_t n) { 52 | for (size_t i = 0; i < n; i++) { 53 | *dest++ = *src++; 54 | } 55 | } 56 | 57 | const void *memchr(const void *s, char c, size_t n) { 58 | for (size_t i = 0; i < n; i++, s++) { 59 | if (*(char *)s == c) 60 | return s; 61 | } 62 | return NULL; 63 | } 64 | const void *memrchr(const void *s, char c, size_t n) { 65 | const char *ptr = s + n - 1; 66 | for (size_t i = 0; i < n; i++, s--) { 67 | if (*ptr == c) 68 | return s; 69 | } 70 | return NULL; 71 | } 72 | 73 | int memcmp(const void *vl, const void *vr, size_t n) { 74 | const unsigned char *l = vl, *r = vr; 75 | for (; n && *l == *r; n--, l++, r++) 76 | ; 77 | return n ? *l - *r : 0; 78 | } 79 | -------------------------------------------------------------------------------- /drv/virtio/virtio_9p.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. */ 28 | #include 29 | #include 30 | #include 31 | 32 | /* The feature bitmap for virtio 9P */ 33 | 34 | /* The mount point is specified in a config variable */ 35 | #define VIRTIO_9P_MOUNT_TAG 0 36 | 37 | struct virtio_9p_config { 38 | /* length of the tag name */ 39 | __u16 tag_len; 40 | /* non-NULL terminated tag name */ 41 | __u8 tag[0]; 42 | } PACKED; 43 | -------------------------------------------------------------------------------- /shog.build: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | require_relative "build/bin2c" 4 | register_rule(Bin2C) 5 | 6 | require_relative "build/genconfig" 7 | register_rule(GenConfig) 8 | 9 | @config = Kconfig.parse(".config") 10 | @config.freeze 11 | 12 | @rule[:cc].bin = @config[:TOOLCHAIN_PATH] + @config[:COMPILER] 13 | #@rule[:cc].bin = 'include-what-you-use' 14 | @rule[:ld].bin = @rule[:cc].bin 15 | @rule[:objcopy].bin = @config[:TOOLCHAIN_PATH] + "objcopy" 16 | 17 | @rule[:cc].cflags << "-g -ggdb -flto=auto -nostdlib -ffreestanding -std=c11 -fno-stack-protector -mno-red-zone -fno-math-errno -fno-trapping-math -fno-common -fno-PIC -fno-PIE -static -W -Wall -Wextra -O3" 18 | @rule[:cc].cflags << (@config[:FRAME_POINTER] ? "-fno-omit-frame-pointer" : "-fomit-frame-pointer") 19 | @rule[:cc].cflags << "-Wno-scalar-storage-order" 20 | 21 | # For now compile for current CPU. Later we should have a config file option that specifies what Arch/Tune we are targeting for. 22 | @rule[:cc].cflags << "-mtune=native" 23 | @rule[:cc].includes << "include" 24 | 25 | @rule[:cc].cflags << '-fsanitize=undefined' if @config[:UBSAN] 26 | @rule[:cc].cflags << '-fsanitize=kernel-address' if @config[:ASAN] 27 | 28 | config_h = Path.make("include/config.h", :outoftree => true) 29 | emit(:genconfig, ".config", :output => config_h) 30 | @rule[:cc].includes << config_h.dir 31 | @rule[:cc].implicit_input << config_h 32 | 33 | subdirs = %w[core drv] 34 | subdirs << "arch/x86" if @config[:ARCH_X86] 35 | subdirs << "third_party/intel-ucode" if @config[:INTEL_MICROCODE] 36 | subdirs << "third_party/acpica/source/components" if @config[:ACPI_ACPICA] 37 | subdirs << @config[:APPLICATION_PATH] 38 | objs = visit(subdirs) 39 | 40 | #linkerscript = emit(:cc, 'arch/x86/linker.ld', :cflags => '-P -E -x c -D __ASSEMBLER__', :output => 'linker.ld', :includes => 'include') 41 | linkerscript = cwd("arch/x86/linker.ld") 42 | 43 | ldflags = ["-Wl,-n -Wl,--gc-sections -Wl,-T" + linkerscript] 44 | ldflags += @rule[:cc].cflags # we link with gcc so let's pass cflags to it as well 45 | app = emit(:ld, objs, :output => "app.elf", :ldflags => ldflags, :implicit_input => linkerscript) 46 | @default_target << app 47 | 48 | emit(:generate_build, ".config") 49 | -------------------------------------------------------------------------------- /arch/x86/vga_console.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "vga_console.h" 4 | #include "console.h" 5 | #include "mem.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #define VIDEO_MEM ((volatile uint16_t *)0xb8000) 11 | 12 | #define WIDTH 80 13 | #define HEIGHT 25 14 | 15 | enum color { 16 | BLACK, 17 | BLUE, 18 | GREEN, 19 | CYAN, 20 | RED, 21 | MAGENTA, 22 | BROWN, 23 | GRAY, 24 | DARK_GRAY, 25 | BRIGHT_BLUE, 26 | BRIGHT_GREEN, 27 | BRIGHT_CYAN, 28 | BRIGHT_RED, 29 | BRIGHT_MAGENTA, 30 | YELLOW, 31 | WHITE 32 | }; 33 | 34 | #define DEFAULT_COLOR (vga_color(WHITE, BLACK, false)) 35 | #define BLANK ' ' 36 | 37 | volatile uint16_t *cursor = VIDEO_MEM; 38 | size_t column = 0, row = 0; 39 | 40 | void console_init(void) { 41 | memset16((uint16_t *)VIDEO_MEM, BLANK, WIDTH * HEIGHT); 42 | cursor = VIDEO_MEM; 43 | } 44 | 45 | static inline uint16_t vga_color(enum color foreground, enum color background, bool blinking) { 46 | uint16_t color = 0; 47 | color |= ((foreground & 0xf) << 8); 48 | color |= ((background & 0x8) << 12); 49 | if (blinking) 50 | color |= (1 << 15); 51 | return color; 52 | } 53 | 54 | static void console_newline(void) { 55 | row++; 56 | 57 | if (row == HEIGHT) { 58 | memcpy16((uint16_t *)VIDEO_MEM, (uint16_t *)VIDEO_MEM + WIDTH, WIDTH * (HEIGHT - 1)); 59 | row--; 60 | 61 | cursor = VIDEO_MEM + (HEIGHT - 1) * WIDTH; 62 | // blank the last line 63 | for (size_t i = 0; i < column; i++) { 64 | *(cursor + i) = BLANK; 65 | } 66 | } else { 67 | cursor = VIDEO_MEM + row * WIDTH; 68 | } 69 | 70 | column = 0; 71 | } 72 | 73 | void console_write(const char *str, size_t len) { 74 | // TODO: put this function into critical section 75 | 76 | for (size_t i = 0; i < len; i++) { 77 | const char c = *str++; 78 | if (c == '\n') { 79 | console_newline(); 80 | } else { 81 | *cursor++ = DEFAULT_COLOR + c; // set only ASCII part, for color use default value 82 | column++; 83 | if (column == WIDTH) { 84 | console_newline(); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /arch/x86/linker.ld: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | 3 | ENTRY(start) 4 | 5 | SECTIONS { 6 | . = 1M; 7 | PROVIDE_HIDDEN(__kernel_start = .); 8 | 9 | .text : ALIGN(4096) { 10 | KEEP(*(.multiboot.header)) 11 | *(.text .text.*) 12 | } :code 13 | 14 | .rodata : ALIGN(4096) { 15 | *(.rodata .rodata.*) 16 | } :rodata 17 | 18 | .note.gnu.build-id : { 19 | PROVIDE_HIDDEN(__build_id_note_start = .); 20 | *(.note.gnu.build-id) 21 | PROVIDE_HIDDEN(__build_id_note_end = .); 22 | } :rodata 23 | 24 | .data : ALIGN(4096) { 25 | *(.data .data.*) 26 | } :data 27 | 28 | PROVIDE_HIDDEN(__kernel_bss_start = .); 29 | .bss : { 30 | *(.bss .bss.*) 31 | } :data 32 | 33 | .init : ALIGN(4096) { 34 | /* Segment for data that is needed only during the initial phase and can be dropped once application is initialized */ 35 | 36 | /* TODO: put microcode into this section as well */ 37 | 38 | *(.init .init.*) 39 | 40 | PROVIDE_HIDDEN(__smp_trampoline_start = smp_entry); 41 | KEEP(*(.smp.trampoline)) 42 | PROVIDE_HIDDEN(__smp_trampoline_end = .); 43 | 44 | /* Different compiler have different alignment rules for struct(pci_device): 45 | clang - 8 bytes 46 | gcc - 16 bytes 47 | TODO: find compiler independent way to find alignment requirements for it */ 48 | . = ALIGN(16); 49 | PROVIDE_HIDDEN(__drivers_pci_start = .); 50 | KEEP(*(.drivers.pci)) 51 | PROVIDE_HIDDEN(__drivers_pci_end = .); 52 | } :init 53 | 54 | /* End of runtime kernel text/data, the rest of the loaded sections are needed only during 55 | * initialization and can be unloaded afterwards */ 56 | PROVIDE_HIDDEN(__kernel_end = .); 57 | 58 | PROVIDE_HIDDEN(__kernel_tdata_start = .); 59 | .tdata : { 60 | *(.tdata .tdata.*) 61 | } :init :tls 62 | 63 | PROVIDE_HIDDEN(__kernel_tbss_start = .); 64 | .tbss : { 65 | *(.tbss .tbss.*) 66 | } :init :tls 67 | } 68 | 69 | PHDRS { 70 | code PT_LOAD FLAGS(5); /* PF_R|PF_X */ 71 | rodata PT_LOAD FLAGS(4); /* PF_R */ 72 | data PT_LOAD FLAGS(6); /* PF_R|PF_W */ 73 | init PT_LOAD FLAGS(7); /* PF_R|PF_W|PF_X, contains code/rodata/data that is needed at initialization only */ 74 | tls PT_TLS; 75 | } 76 | -------------------------------------------------------------------------------- /core/net/eth.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "net/eth.h" 4 | #include "buffer.h" 5 | #include "compiler.h" 6 | #include "kalloc.h" 7 | #include "mem.h" 8 | #include "net/arp.h" 9 | #include "net/ip4.h" 10 | #include "net/ip6.h" 11 | #include "queue.h" 12 | #include "shout.h" 13 | #include "stdio.h" 14 | 15 | struct __be PACKED eth_hdr { 16 | ethaddr_t dest; 17 | ethaddr_t src; 18 | uint16_t type; 19 | }; 20 | BUILD_PANIC_IF(sizeof(struct eth_hdr) != HDR_LEN_ETH - HDR_LEN_RAW, "HDR_LEN_ETH is not specified correctly"); 21 | 22 | const ethaddr_t ETH_BROADCAST = {.addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; 23 | 24 | static struct eth_device *eth_dev_from_ip4if(struct ip4if *ip4if) { return container_of(ip4if, struct eth_device, ip4if); }; 25 | 26 | void eth_dev_register(struct eth_device *dev) { 27 | dev->ip4if.eth_dev = eth_dev_from_ip4if; 28 | ip4if_register(&dev->ip4if); 29 | } 30 | 31 | void eth_dev_unregister(UNUSED struct eth_device *dev) {} 32 | 33 | void eth_receive(struct eth_device *dev, buffer_t *buff) { 34 | struct eth_hdr *hdr = buff->pos; 35 | buff->pos += sizeof(struct eth_hdr); 36 | 37 | IFD printf("Received eth src=" ETHADDR_PRINT_FMT " dest=" ETHADDR_PRINT_FMT " type=%x\n", ETHADDR_PRINT_PARAMS(hdr->src), 38 | ETHADDR_PRINT_PARAMS(hdr->dest), hdr->type); 39 | 40 | if (hdr->type == ETH_TYPE_IP4) { 41 | arp_cache_add(ip4_src_addr((struct ip4_hdr *)buff->pos), hdr->src); 42 | ip4_receive(&dev->ip4if, buff); 43 | } else if (hdr->type == ETH_TYPE_IP6) { 44 | ip6_receive(&dev->ip4if6, buff); 45 | } else if (hdr->type == ETH_TYPE_ARP) { 46 | arp_receive(dev, buff); 47 | } else { 48 | IFD printf("Unknown eth frame type %d\n", hdr->type); 49 | buffer_free(buff); 50 | } 51 | } 52 | 53 | void eth_send(struct eth_device *dev, const ethaddr_t *dest, uint16_t type, buffer_t *buff) { 54 | buff->pos -= sizeof(struct eth_hdr); 55 | struct eth_hdr *hdr = buff->pos; 56 | 57 | hdr->type = type; 58 | hdr->src = dev->addr; 59 | hdr->dest = *dest; 60 | 61 | IFD printf("Sent eth src=" ETHADDR_PRINT_FMT " dest=" ETHADDR_PRINT_FMT " type=%x\n", ETHADDR_PRINT_PARAMS(hdr->src), 62 | ETHADDR_PRINT_PARAMS(hdr->dest), hdr->type); 63 | 64 | dev->send(dev, buff); 65 | } 66 | 67 | void ethaddr_cpy(uint8_t *dest, const uint8_t *src) { memcpy(dest, src, sizeof(ethaddr_t)); } -------------------------------------------------------------------------------- /include/net/ip4.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "buffer.h" 6 | #include "queue.h" 7 | #include 8 | #include 9 | 10 | typedef uint32_t ip4addr_t; 11 | 12 | #define IPADDR_PRINT_FMT "%d.%d.%d.%d" 13 | #define IPADDR_PRINT_PARAMS(addr) ((uint8_t)(addr >> 24)), ((uint8_t)(addr >> 16)), ((uint8_t)(addr >> 8)), ((uint8_t)addr) 14 | 15 | #define IPADDR(p1, p2, p3, p4) (ip4addr_t)(((uint32_t)p1 << 24) | ((uint32_t)p2 << 16) | ((uint32_t)p3 << 8) | ((uint32_t)p4)) 16 | #define IPADDR_ANY IPADDR(0, 0, 0, 0) 17 | #define IPADDR_INVALID IPADDR_ANY 18 | #define IPADDR_BROADCAST IPADDR(255, 255, 255, 255) 19 | 20 | bool ipaddr_isbroadcaset(ip4addr_t mask, ip4addr_t addr); 21 | bool ipaddr_ismulticast(ip4addr_t addr); 22 | 23 | struct udp_listener; 24 | struct tcp_listener; 25 | struct tcp_connection; 26 | struct ip4_hdr; 27 | 28 | struct ip4if { 29 | ip4addr_t addr; 30 | ip4addr_t mask; 31 | ip4addr_t router_addr; 32 | 33 | // valid only if DHCP is used 34 | ip4addr_t dhcp_server_addr; 35 | uint32_t dhcp_lease_time; // in seconds 36 | 37 | struct eth_device *(*eth_dev)(struct ip4if *ip4if); 38 | 39 | LIST_HEAD(, udp_listener) udp_listeners; 40 | LIST_HEAD(, tcp_listener) tcp_listeners; 41 | LIST_HEAD(, tcp_connection) tcp_connections; 42 | }; 43 | 44 | // https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers 45 | #define IP_PROTO_HOPOPT 0 46 | #define IP_PROTO_ICMP 1 47 | #define IP_PROTO_IGMP 2 48 | #define IP_PROTO_GGP 3 49 | #define IP_PROTO_IPinIP 4 50 | #define IP_PROTO_ST 5 51 | #define IP_PROTO_TCP 6 52 | #define IP_PROTO_CBT 7 53 | #define IP_PROTO_EGP 8 54 | #define IP_PROTO_IGP 9 55 | #define IP_PROTO_UDP 17 56 | #define IP_PROTO_HMP 20 57 | #define IP_PROTO_IP6 41 // encapsulation 58 | 59 | typedef void (*ip4if_init_callback_t)(struct ip4if *); 60 | void ip4if_register(struct ip4if *ip4if); 61 | void ip4if_on_init(ip4if_init_callback_t callback); 62 | void ip4if_init_complete(struct ip4if *ip4if); 63 | 64 | ip4addr_t ip4_src_addr(struct ip4_hdr *hdr); 65 | void ip4_receive(struct ip4if *ip4if, buffer_t *buf); 66 | void ip4_send(struct ip4if *ip4if, buffer_t *buf, ip4addr_t dest, uint8_t protocol); 67 | bool ip4_broadcast(struct ip4if *ip4if, ip4addr_t addr); 68 | 69 | // It includes pseudo-header and used for UDP and TCP checksumming 70 | uint16_t checksum_calculate(void *payload, uint16_t length, uint16_t proto, ip4addr_t src, ip4addr_t dest); -------------------------------------------------------------------------------- /arch/x86/interrupt.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #define INTERRUPT_DIVIDE 0x0 6 | #define INTERRUPT_DEBUG 0x1 7 | #define INTERRUPT_NON_MASKABLE 0x2 8 | #define INTERRUPT_BREAKPOINT 0x3 9 | #define INTERRUPT_OVERFLOW 0x4 10 | #define INTERRUPT_BOUND_RANGE 0x5 11 | #define INTERRUPT_INVALID_OPCODE 0x6 12 | #define INTERRUPT_DEVICE_NOT_AVAILABLE 0x7 13 | #define INTERRUPT_DOUBLE_FAULT 0x8 // with errorcode (always zero) 14 | #define INTERRUPT_COPROCESSOR_SEGMENT_OVERRUN 0x9 15 | #define INTERRUPT_INVALID_TSS 0xa // with errorcode (selector index) 16 | #define INTERRUPT_SEGMENT_NOT_PRESENT 0xb // with errorcode (selector index) 17 | #define INTERRUPT_STACK_SEGMENT_FAULT 0xc // with errorcode (selector index) 18 | #define INTERRUPT_GENERAL_PROTECTION_FAULT 0xd // with errorcode (segment selector index or zero) 19 | #define INTERRUPT_PAGE_FAULT 0xe // with errorcode (see http://wiki.osdev.org/Exceptions for description) 20 | #define INTERRUPT_FPU_EXCEPTION 0x10 21 | #define INTERRUPT_ALIGNMENT_CHECK 0x11 // with errorcode 22 | #define INTERRUPT_MACHINE_CHECK 0x12 23 | #define INTERRUPT_SIMD_FPU_EXCEPTION 0x13 24 | #define INTERRUPT_VRTUALIZATION_EXCEPTION 0x14 25 | #define INTERRUPT_SECURITY_EXCEPTION 0x1e // with errorcode 26 | 27 | // Range IRQ_BASE..INTERRUPT_LOCAL_APIC_BASE is allocated for dynamically registered interrupts 28 | #define IRQ_BASE 0x20 29 | 30 | #define IRQ_TIMER 0x0 31 | #define IRQ_KEYBOARD 0x1 32 | #define IRQ_SERIAL 0x4 33 | #define IRQ_RTC 0x8 34 | #define IRQ_ATA1 0xe 35 | #define IRQ_ATA2 0xf 36 | 37 | // Range INTERRUPT_LOCAL_APIC_BASE..INTERRUPTS_NUM is for predefined APIC interrupts 38 | #define INTERRUPT_LOCAL_APIC_BASE 0xf0 39 | #define INTERRUPT_APIC_SPURIOUS 0xf0 40 | #define INTERRUPT_APIC_TIMER 0xf1 41 | 42 | #define INTERRUPTS_NUM 256 43 | 44 | #ifndef __ASSEMBLER__ 45 | 46 | #include "event.h" 47 | #include 48 | 49 | int interrupt_reserve(uint8_t size); 50 | // Returns number of registered interrupt. 51 | // Negative number means no handler registered. 52 | int interrupt_register(event_handler_t handler, void *data); 53 | void interrupt_register_with_vector(uint16_t vector, event_handler_t handler, void *data); 54 | void interrupt_unregister(uint8_t num); 55 | 56 | // Registers handler for an IRQ line 57 | int irq_register(uint8_t irq_line, event_handler_t handler, void *data); 58 | 59 | void idt_struct_setup(void); 60 | void idt_load(void); 61 | 62 | #endif /* ! __ASSEMBLER__ */ -------------------------------------------------------------------------------- /core/net/udp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "net/udp.h" 4 | #include "buffer.h" 5 | #include "compiler.h" 6 | #include "kalloc.h" 7 | #include "net/arp.h" 8 | #include "net/eth.h" 9 | #include "net/ip4.h" 10 | #include "shout.h" 11 | #include "stdio.h" 12 | #include 13 | 14 | struct __be PACKED udp_hdr { 15 | uint16_t src_port; 16 | uint16_t dest_port; 17 | uint16_t length; // datagram length 18 | uint16_t checksum; 19 | }; 20 | BUILD_PANIC_IF(sizeof(struct udp_hdr) != HDR_LEN_UDP - HDR_LEN_IP4, "HDR_LEN_UDP is not specified correctly"); 21 | 22 | void udp_receive(struct ip4if *ip4if, ip4addr_t src_ip, ip4addr_t dest_ip, buffer_t *buff) { 23 | struct udp_hdr *hdr = buff->pos; 24 | buff->pos += sizeof(struct udp_hdr); 25 | 26 | if (checksum_calculate(hdr, hdr->length, IP_PROTO_UDP, src_ip, dest_ip) != 0) { 27 | IFD printf("UDP checksum failed\n"); 28 | goto buff_free; 29 | } 30 | 31 | struct udp_listener *l; 32 | LIST_FOREACH(l, &ip4if->udp_listeners, next) { 33 | if (l->port == hdr->dest_port) { 34 | l->handler(l, buff); 35 | goto buff_free; 36 | } 37 | } 38 | IFD printf("No listeners found for UDP port %d\n", hdr->dest_port); 39 | 40 | buff_free: 41 | buffer_free(buff); 42 | } 43 | 44 | void udp_send(struct ip4if *ip4if, buffer_t *buff, ip4addr_t dest, uint16_t src_port, uint16_t dest_port) { 45 | buff->pos -= sizeof(struct udp_hdr); 46 | struct udp_hdr *hdr = buff->pos; 47 | 48 | hdr->src_port = src_port; 49 | hdr->dest_port = dest_port; 50 | hdr->length = buff->data_size - HDR_LEN_IP4; // size of UDP header + payload 51 | hdr->checksum = 0; // set it to zero before calculating checksum 52 | hdr->checksum = checksum_calculate(hdr, hdr->length, IP_PROTO_UDP, ip4if->addr, dest); 53 | 54 | ip4_send(ip4if, buff, dest, IP_PROTO_UDP); 55 | } 56 | 57 | err_t udp_bind(struct ip4if *ip4if, uint16_t port, udp_handler_t handler) { 58 | struct udp_listener *l; 59 | LIST_FOREACH(l, &ip4if->udp_listeners, next) { 60 | if (l->port == port) 61 | return E_INUSE; 62 | } 63 | 64 | struct udp_listener *listener = kalloc(struct udp_listener); 65 | listener->ip4if = ip4if; 66 | listener->port = port; 67 | listener->handler = handler; 68 | 69 | LIST_INSERT_HEAD(&ip4if->udp_listeners, listener, next); 70 | return SUCCESS; 71 | } 72 | 73 | void udp_unbind(struct udp_listener *listener) { LIST_REMOVE(listener, next); } -------------------------------------------------------------------------------- /drv/virtio/virtio.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "virtio.h" 4 | #include "kalloc.h" 5 | #include "mem.h" 6 | 7 | uint64_t virtio_read_device_features(__mmio struct virtio_pci_common_cfg *cfg) { 8 | // read low 32 bits 9 | cfg->device_feature_select = 0; 10 | uint64_t features = cfg->device_feature; 11 | 12 | // read top 32 bits 13 | cfg->device_feature_select = 1; 14 | features |= (uint64_t)cfg->device_feature << 32; 15 | 16 | return features; 17 | } 18 | 19 | void virtio_write_driver_features(__mmio struct virtio_pci_common_cfg *cfg, uint64_t features) { 20 | // write low 32 bits 21 | cfg->driver_feature_select = 0; 22 | cfg->driver_feature = (uint32_t)features; 23 | 24 | // write top 32 bits 25 | cfg->driver_feature_select = 1; 26 | cfg->driver_feature = (uint32_t)(features >> 32); 27 | } 28 | 29 | void virtio_enable_queue(__mmio struct virtio_pci_common_cfg *cfg, struct virtq *queue) { 30 | cfg->queue_select = queue->index; 31 | cfg->queue_enable = 1; 32 | } 33 | 34 | void virtio_init_queue(__mmio struct virtio_pci_common_cfg *cfg, uint16_t index, struct virtq *queue, __mmio void *notify_base, 35 | uint32_t notify_off_multiplier) { 36 | cfg->queue_select = index; 37 | uint16_t size = cfg->queue_size; 38 | 39 | queue->size = size; 40 | queue->index = index; 41 | queue->last_used_idx = 0; 42 | 43 | queue->desc = kalloc_align(16 * size, 16); 44 | memset(queue->desc, 0, 16 * size); 45 | 46 | queue->avail = kalloc_align(6 + 2 * size, 2); 47 | memset(queue->avail, 0, 6 + 2 * size); 48 | 49 | queue->used = kalloc_align(6 + 8 * size, 4); 50 | memset(queue->used, 0, 6 + 8 * size); 51 | 52 | if (notify_base) { 53 | queue->notify = notify_base + notify_off_multiplier * cfg->queue_notify_off; 54 | } 55 | 56 | cfg->queue_desc_lo = (uintptr_t)queue->desc; 57 | cfg->queue_desc_hi = ((uintptr_t)queue->desc) >> 32; 58 | cfg->queue_avail_lo = (uintptr_t)queue->avail; 59 | cfg->queue_avail_hi = ((uintptr_t)queue->avail) >> 32; 60 | cfg->queue_used_lo = (uintptr_t)queue->used; 61 | cfg->queue_used_hi = ((uintptr_t)queue->used) >> 32; 62 | 63 | cfg->queue_msix_vector = index; 64 | } 65 | 66 | void virtio_queue_desc_link(struct virtq *queue) { 67 | // free descriptors are linked together 68 | queue->unused_desc_idx = 0; 69 | queue->unused_desc_size = queue->size; 70 | for (int i = 0; i < queue->size - 1; i++) { 71 | queue->desc[i].next = i + 1; 72 | } 73 | } -------------------------------------------------------------------------------- /drv/virtio/virtio_ids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Virtio IDs 5 | * 6 | * This header is BSD licensed so anyone can use the definitions to implement 7 | * compatible drivers/servers. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 3. Neither the name of IBM nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. */ 31 | 32 | #define VIRTIO_ID_NET 1 /* virtio net */ 33 | #define VIRTIO_ID_BLOCK 2 /* virtio block */ 34 | #define VIRTIO_ID_CONSOLE 3 /* virtio console */ 35 | #define VIRTIO_ID_RNG 4 /* virtio rng */ 36 | #define VIRTIO_ID_BALLOON 5 /* virtio balloon */ 37 | #define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */ 38 | #define VIRTIO_ID_SCSI 8 /* virtio scsi */ 39 | #define VIRTIO_ID_9P 9 /* 9p virtio console */ 40 | #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ 41 | #define VIRTIO_ID_CAIF 12 /* Virtio caif */ 42 | #define VIRTIO_ID_GPU 16 /* virtio GPU */ 43 | #define VIRTIO_ID_INPUT 18 /* virtio input */ 44 | #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ 45 | #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ 46 | -------------------------------------------------------------------------------- /core/float.c: -------------------------------------------------------------------------------- 1 | /* This file is based on MUSL project code */ 2 | 3 | #include "float.h" 4 | #include "arch.h" 5 | #include 6 | 7 | #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 8 | long double frexpl(long double x, int *e) { return frexp(x, e); } 9 | #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 10 | long double frexpl(long double x, int *e) { 11 | union ldshape u = {x}; 12 | int ee = u.i.se & 0x7fff; 13 | 14 | if (!ee) { 15 | if (x) { 16 | x = frexpl(x * 0x1p120, e); 17 | *e -= 120; 18 | } else 19 | *e = 0; 20 | return x; 21 | } else if (ee == 0x7fff) { 22 | return x; 23 | } 24 | 25 | *e = ee - 0x3ffe; 26 | u.i.se &= 0x8000; 27 | u.i.se |= 0x3ffe; 28 | return u.f; 29 | } 30 | #endif 31 | 32 | double frexp(double x, int *e) { 33 | union { 34 | double d; 35 | uint64_t i; 36 | } y = {x}; 37 | int ee = y.i >> 52 & 0x7ff; 38 | 39 | if (!ee) { 40 | if (x) { 41 | x = frexp(x * 0x1p64, e); 42 | *e -= 64; 43 | } else 44 | *e = 0; 45 | return x; 46 | } else if (ee == 0x7ff) { 47 | return x; 48 | } 49 | 50 | *e = ee - 0x3fe; 51 | y.i &= 0x800fffffffffffffull; 52 | y.i |= 0x3fe0000000000000ull; 53 | return y.d; 54 | } 55 | 56 | #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 57 | int __fpclassifyl(long double x) { return __fpclassify(x); } 58 | #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 59 | int __fpclassifyl(long double x) { 60 | union ldshape u = {x}; 61 | int e = u.i.se & 0x7fff; 62 | int msb = u.i.m >> 63; 63 | if (!e && !msb) 64 | return u.i.m ? FP_SUBNORMAL : FP_ZERO; 65 | if (!msb) 66 | return FP_NAN; 67 | if (e == 0x7fff) 68 | return u.i.m << 1 ? FP_NAN : FP_INFINITE; 69 | return FP_NORMAL; 70 | } 71 | #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 72 | int __fpclassifyl(long double x) { 73 | union ldshape u = {x}; 74 | int e = u.i.se & 0x7fff; 75 | u.i.se = 0; 76 | if (!e) 77 | return u.i2.lo | u.i2.hi ? FP_SUBNORMAL : FP_ZERO; 78 | if (e == 0x7fff) 79 | return u.i2.lo | u.i2.hi ? FP_NAN : FP_INFINITE; 80 | return FP_NORMAL; 81 | } 82 | #endif 83 | 84 | #if (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 85 | int __signbitl(long double x) { 86 | union ldshape u = {x}; 87 | return u.i.se >> 15; 88 | } 89 | #elif LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 90 | int __signbitl(long double x) { return __signbit(x); } 91 | #endif -------------------------------------------------------------------------------- /arch/x86/rtc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "rtc.h" 4 | #include "mem.h" 5 | #include "x86.h" 6 | 7 | #define REG_RTC_INDEX 0x70 8 | #define REG_RTC_DATE 0x71 9 | 10 | #define REG_SECONDS 0 11 | #define REG_SECONDS_ALARM 1 12 | #define REG_MINUTES 2 13 | #define REG_MINUTES_ALARM 3 14 | #define REG_HOURS 4 15 | #define REG_HOURS_ALARM 5 16 | #define REG_DAY_OF_WEEK 6 17 | #define REG_DAY_OF_MONTH 7 18 | #define REG_MONTH 8 19 | #define REG_YEAR 9 20 | #define REG_A 10 21 | #define REG_B 11 22 | #define REG_C 12 23 | #define REG_D 13 24 | 25 | // Register B bits 26 | #define REG_B_HOUR_FORMAT BIT(1) 27 | #define REG_B_DATA_MODE BIT(2) 28 | 29 | // Register HOURS bits 30 | #define REG_HOURS_PM BIT(7) 31 | 32 | static uint8_t from_bcd(uint8_t data) { return (data >> 4) * 10 + (data & 0xf); } 33 | 34 | static uint8_t rtc_reg_read_raw(uint8_t reg) { 35 | outb(REG_RTC_INDEX, reg); 36 | return inb(REG_RTC_DATE); 37 | } 38 | 39 | static uint8_t rtc_reg_read(uint8_t reg, bool is_binary) { 40 | uint8_t data = rtc_reg_read_raw(reg); 41 | return is_binary ? data : from_bcd(data); 42 | } 43 | 44 | static void rtc_read_mode(bool *is_24_hour, bool *is_binary) { 45 | uint8_t reg_b = rtc_reg_read_raw(REG_B); 46 | *is_24_hour = reg_b & REG_B_HOUR_FORMAT; 47 | *is_binary = reg_b & REG_B_DATA_MODE; 48 | } 49 | 50 | static uint8_t rtc_read_hour(bool is_binary, bool is_24_hour) { 51 | uint8_t data = rtc_reg_read_raw(REG_HOURS); 52 | 53 | bool pm = data & REG_HOURS_PM; 54 | data &= ~REG_HOURS_PM; 55 | 56 | uint8_t hour = is_binary ? data : from_bcd(data); 57 | 58 | if (is_24_hour) { 59 | return hour; 60 | } 61 | 62 | if (hour == 12) { 63 | hour = 0; 64 | } 65 | 66 | if (pm) { 67 | hour += 12; 68 | } 69 | 70 | return hour; 71 | } 72 | 73 | static void rtc_read_date(struct rtc_date *date) { 74 | bool is_24_hour, is_binary; 75 | rtc_read_mode(&is_24_hour, &is_binary); 76 | 77 | date->seconds = rtc_reg_read(REG_SECONDS, is_binary); 78 | date->minutes = rtc_reg_read(REG_MINUTES, is_binary); 79 | date->hours = rtc_read_hour(is_binary, is_24_hour); 80 | date->day = rtc_reg_read(REG_DAY_OF_MONTH, is_binary); 81 | date->month = rtc_reg_read(REG_MONTH, is_binary); 82 | date->year = rtc_reg_read(REG_YEAR, is_binary) + 2000; 83 | } 84 | 85 | void rtc_read(struct rtc_date *date) { 86 | struct rtc_date *prev; 87 | do { 88 | // keep reading date until two consecutive reads show the same data 89 | memcpy(&prev, date, sizeof(struct rtc_date)); 90 | rtc_read_date(date); 91 | } while (memcmp(date, &prev, sizeof(struct rtc_date))); 92 | } 93 | -------------------------------------------------------------------------------- /drv/virtio/virtio_input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR 21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 24 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. */ 29 | 30 | #include 31 | 32 | enum virtio_input_config_select { 33 | VIRTIO_INPUT_CFG_UNSET = 0x00, 34 | VIRTIO_INPUT_CFG_ID_NAME = 0x01, 35 | VIRTIO_INPUT_CFG_ID_SERIAL = 0x02, 36 | VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03, 37 | VIRTIO_INPUT_CFG_PROP_BITS = 0x10, 38 | VIRTIO_INPUT_CFG_EV_BITS = 0x11, 39 | VIRTIO_INPUT_CFG_ABS_INFO = 0x12, 40 | }; 41 | 42 | struct virtio_input_absinfo { 43 | __u32 min; 44 | __u32 max; 45 | __u32 fuzz; 46 | __u32 flat; 47 | __u32 res; 48 | }; 49 | 50 | struct virtio_input_devids { 51 | __u16 bustype; 52 | __u16 vendor; 53 | __u16 product; 54 | __u16 version; 55 | }; 56 | 57 | struct virtio_input_config { 58 | __u8 select; 59 | __u8 subsel; 60 | __u8 size; 61 | __u8 reserved[5]; 62 | union { 63 | char string[128]; 64 | __u8 bitmap[128]; 65 | struct virtio_input_absinfo abs; 66 | struct virtio_input_devids ids; 67 | } u; 68 | }; 69 | 70 | struct __le virtio_input_event { 71 | uint16_t type; 72 | uint16_t code; 73 | uint32_t value; 74 | }; 75 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | mainmenu "Unicycle Framework Configuration" 4 | 5 | config APPLICATION_PATH 6 | string "Path to directory with unikernel service code" 7 | default "app/http-hello" 8 | help 9 | Specifies directory where user's service code is located. 10 | 11 | config DEBUG 12 | bool "Debugging" 13 | def_bool y 14 | help 15 | Enables additional debugging and tracing capabilities. This might add some overhead during run-time. 16 | 17 | config TOOLCHAIN_PATH 18 | string "Path to compiler toolchain" 19 | help 20 | Specifies project's compiler/linker toolchain. 21 | 22 | config COMPILER 23 | string "Compiler binary" 24 | default "gcc" 25 | help 26 | Specifies compiler used for project. At this point only 'gcc' and 'clang' are supported. 27 | 28 | config PER_CPU_AREA_SIZE 29 | int "Per-cpu memory size" 30 | default 131072 # 128K 31 | help 32 | Each CPU has its own execution context. It needs memory for stack and per-cpu data (.tdata and .tbss sections). 33 | Currently Unicycle allocates per-cpu space at init-time. 34 | This value should be power-of-two so we can use buddy allocator and allocate area at run-time. 35 | 36 | config MIN_STACK_SIZE 37 | int "Minimum size for stack" 38 | default 65536 # 64K 39 | help 40 | Stack is area allocated for each CPU from its per-CPU area, minus area used for thread-local data (.tbss .tdata). 41 | This value is a guard that makes sure thread-local data does not eat too much per-cpu data. 42 | 43 | config SMP 44 | bool "Symmetric multi-processing support" 45 | def_bool y 46 | help 47 | Enables support for systems with multiple CPUs. 48 | 49 | config FRAME_POINTER 50 | bool "Enable frame pointer" 51 | def_bool n 52 | help 53 | Frame pointer is a special register (e.g. %rbp at x86_64) that keeps information about call stack. 54 | This pointer helps to reconstruct call stack that is useful for error reporting. 55 | Downsize of this feature is that it requires a register that is a scarse resource on many platforms. 56 | 57 | choice ARCH 58 | prompt "Target architecture" 59 | default ARCH_X86 60 | help 61 | Target hardware architecture 62 | 63 | config ARCH_X86 64 | bool "Intel X86_64" 65 | help 66 | Intel X86 64bit architecture 67 | 68 | endchoice 69 | 70 | if ARCH_X86 71 | source "arch/x86/Kconfig" 72 | endif 73 | 74 | source "third_party/acpica/source/components/Kconfig" 75 | 76 | config ASAN 77 | bool "ASAN runtime memory debugger" 78 | help 79 | Enables address sanitizer https://github.com/google/sanitizers/wiki/AddressSanitizer 80 | designed to find out-of-bounds access and use-after-free bugs. 81 | 82 | 83 | config UBSAN 84 | bool "UBSAN (Undefined Behavior Sanitizer)" 85 | help 86 | Enables Undefined Behavior Sanitizer 87 | -------------------------------------------------------------------------------- /drv/net/intel_e1000e.regs: -------------------------------------------------------------------------------- 1 | ; INTEL 82574 GbE NIC driver 2 | ; http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf 3 | 4 | 0: CTRL ; Device control register, section 10.2.2.1 5 | 0: FULL_DUPLEX ; 0 - Half duplex, 1 - Full duplex 6 | 2: MASTER_DISABLE 7 | 5: ASDE ; Auto-Speed detection 8 | 6: SLU ; Set link up 9 | 8..9: SPEED ; Speed in Mb/s 10 | 0: 10 11 | 1: 100 12 | 2: 1000 13 | 11: FRCSPD ; Force speed 14 | 26: RST ; Reset 15 | 31: PHY_RST 16 | 17 | 8: STATUS ; Status, R/O 18 | 0: FULL_DUPLEX 19 | 1: LINK_UP 20 | 2: TXOFF 21 | 6..7: SPEED 22 | 0: 10 23 | 1: 100 24 | 2: 1000 25 | 26 | 20: MDIC ; MDI control (PHY access) 27 | 26..27: OP 28 | 1: WRITE 29 | 2: READ 30 | 28: R ; Ready Bit 31 | 29: I ; Interrupt Enable 32 | 30: E ; Error 33 | 34 | : INTR ; Interrupt bits shared among the registers below 35 | 2: LSC ; Link Status Change 36 | 7: RXT0 ; Receiver Timer 37 | c0: ICR ; Interrupt Cause Read 38 | c8: ICS ; Interrupt Cause Set 39 | d0: IMS ; Interrupt Mask Set 40 | d8: IMC ; Interrupt Mask Clear 41 | 42 | 5000: RXCSUM 43 | 5200: MTA ; Multicast Table Array 44 | 5400: RAL 45 | 5404: RAH 46 | 47 | 100: RCTL ; Rx Control, section 10.2.5.1 48 | 0: RST ; Rx Reset 49 | 1: EN ; Rx Enable 50 | 2: SBP ; Store Bad Packates 51 | 3: UPE ; Unicast Promisc Enable 52 | 4: MPE ; Multicast Promisc Enable 53 | 5: LPE ; Long Packet Rx Enable (>1522 bytes) 54 | 6..7: LBM 55 | 0: NORMAL 56 | 1: MAC_LOOPBACK ; Test mode 57 | 8..9: RDMTS ; Receive Descriptor Minimum Threshold Size 58 | 12..13: MO ; Multicast Offset 59 | 0: 36 ; Multicast Filter Offset 36..47 60 | 1: 35 ; Multicast Filter Offset 35..46 61 | 2: 34 ; Multicast Filter Offset 34..45 62 | 3: 32 ; Multicast Filter Offset 32..43 63 | 15: BAM ; Rx Broadcast Packets Enable 64 | 16..17: BSIZE ; Receive Buffer Size 65 | 0: 2048 ; Rx Buffer 2048 * (BSEX * 16) 66 | 1: 1024 ; Rx Buffer 1024 * (BSEX * 16) 67 | 2: 512 ; Rx Buffer 512 * (BSEX * 16) 68 | 3: 256 ; Rx Buffer 256 * (BSEX * 16) 69 | 22: DPF ; Discard Pause Frames 70 | 23: PMCF ; Pass MAC Control Frames 71 | 25: BSEX ; Buffer Size Extension (x16) 72 | 26: SECRC ; Strip CRC Field 73 | 2800: RDBAL ; Rx Descriptor Base Low 74 | 2804: RDBAH ; Rx Descriptor Base High 75 | 2808: RDLEN ; Rx Descriptor Length 76 | 2810: RDH ; Rx Descriptor Head 77 | 2818: RDT ; Rx Descriptor Tail 78 | 2828: RXDCTL ; Rx Descriptor Control 79 | 24: GRAN ; Writeback granularity. 0 - Cache lines, 1 - Descriptors 80 | 81 | 0400: TCTL ; Tx Control 82 | 0: RST ; Tx Reset 83 | 1: EN ; Tx Enable 84 | 3: PSP ; Pad Short Packets (to 64b) 85 | 22: SWXOFF ; XOFF Tx (self-clearing) 86 | 3800: TDBAL ; Tx Descriptor Base Low 87 | 3804: TDBAH ; Tx Descriptor Base High 88 | 3808: TDLEN ; Tx Descriptor Length 89 | 3810: TDH ; Tx Descriptor Head 90 | 3818: TDT ; Tx Descriptor Tail 91 | 3828: TXDCTL ; Tx Descriptor Control 92 | 24: GRAN ; Writeback granularity. 0 - Cache lines, 1 - Descriptors 93 | -------------------------------------------------------------------------------- /arch/x86/ioapic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "ioapic.h" 4 | #include "apic.h" 5 | #include "compiler.h" 6 | #include "mmio.h" 7 | #include "mmu.h" 8 | #include "shout.h" 9 | #include "stdio.h" 10 | #include 11 | 12 | #define IOAPIC_REGSEL 0x0 13 | #define IOAPIC_WIN 0x10 14 | 15 | #define IOAPIC_ID 0 16 | #define IOAPIC_VER 1 17 | 18 | #define IOAPIC_REDTLB 0x10 19 | #define IOAPIC_IRQ_MASK BIT(16) 20 | 21 | size_t apic_ioapic_num = 0; 22 | struct apic_ioapic *apic_ioapics = NULL; 23 | 24 | size_t apic_irq_override_num; 25 | struct apic_irq_override *apic_irq_overrides; 26 | 27 | // IO APIC documentation can be found at https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf 28 | static inline uint32_t ioapic_read(__mmio void *addr, uint32_t reg) { 29 | MMIO32(addr + IOAPIC_REGSEL) = reg & 0xff; 30 | return MMIO32(addr + IOAPIC_WIN); 31 | } 32 | 33 | static inline void ioapic_write(__mmio void *addr, uint32_t reg, uint32_t data) { 34 | MMIO32(addr + IOAPIC_REGSEL) = reg & 0xff; 35 | MMIO32(addr + IOAPIC_WIN) = data; 36 | } 37 | 38 | void ioapic_init(void) { 39 | for (size_t i = 0; i < apic_ioapic_num; i++) { 40 | struct apic_ioapic *ioapic = &apic_ioapics[i]; 41 | 42 | page_table_set_bit((uintptr_t)ioapic->addr, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITABLE | PAGE_CACHE_DISABLE, 43 | PAGE_PRESENT | PAGE_WRITABLE | PAGE_CACHE_DISABLE); 44 | 45 | uint32_t data = ioapic_read(ioapic->addr, IOAPIC_VER); 46 | uint8_t version = data & 0xff; 47 | uint8_t pin_num = ((data >> 16) & 0xff) + 1; 48 | 49 | ioapic->pin_num = pin_num; 50 | 51 | printf("IOAPIC id=%d addr=%p version=%d irq_base=%d pin_num=%d\n", ioapic->id, ioapic->addr, ioapic->irq_base, version, pin_num); 52 | 53 | for (size_t i = 0; i < pin_num; i++) { 54 | uint32_t reg = IOAPIC_REDTLB + i * 2; 55 | // mask all irq 56 | ioapic_write(ioapic->addr, reg, IOAPIC_IRQ_MASK); 57 | } 58 | } 59 | } 60 | 61 | static uint8_t remap_irq(uint8_t irq) { 62 | for (size_t i = 0; i < apic_irq_override_num; i++) { 63 | struct apic_irq_override *remap = &apic_irq_overrides[i]; 64 | 65 | if (remap->source == irq) 66 | return remap->irq; 67 | } 68 | 69 | return irq; 70 | } 71 | 72 | void ioapic_route_irq(uint8_t original_irq, uint8_t vector) { 73 | uint8_t irq = remap_irq(original_irq); 74 | 75 | for (size_t i = 0; i < apic_ioapic_num; i++) { 76 | struct apic_ioapic *ioapic = &apic_ioapics[i]; 77 | uint8_t irq_begin = ioapic->irq_base; 78 | uint8_t irq_end = ioapic->irq_base + ioapic->pin_num; 79 | if (irq < irq_begin || irq >= irq_end) 80 | continue; 81 | 82 | uint32_t reg = IOAPIC_REDTLB + irq * 2; 83 | 84 | ioapic_write(ioapic->addr, reg, vector); 85 | ioapic_write(ioapic->addr, reg + 1, 0); // apic # 0 86 | 87 | return; 88 | } 89 | SHOUT("Cannot find IO APIC for irq %d\n", irq); 90 | } 91 | -------------------------------------------------------------------------------- /arch/x86/interrupt_handlers.S: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | #include "interrupt.h" 4 | #include "compiler.h" 5 | #include "config.h" 6 | 7 | .text 8 | 9 | .global exception_handlers 10 | .global exception_handlers_end 11 | 12 | # Handlers for exceptions 13 | exception_handlers: 14 | .set i, 0 15 | .rept IRQ_BASE 16 | 17 | .if i == 8 || (i >= 10 && i <= 14) || i == 17 || i == 30 18 | nop # nop filling to make all interrupt handlers text of the same size 19 | nop 20 | .else 21 | pushq $0 # this interrupt has no code soe fill it to zero 22 | .endif 23 | 24 | pushq $i # interrupt number 25 | jmp exception_common 26 | 27 | .set i, i + 1 28 | .endr 29 | exception_handlers_end: 30 | 31 | 32 | .global irq_handlers 33 | .global irq_handlers_end 34 | 35 | # implementation of apic_interrupt_ack() in ASM 36 | .macro apic_eoi 37 | #ifdef CONFIG_X2APIC 38 | push %rax 39 | push %rcx 40 | push %rdx 41 | mov $0x80b, %ecx 42 | mov $0, %eax 43 | mov $0, %edx 44 | wrmsr 45 | pop %rdx 46 | pop %rcx 47 | pop %rax 48 | #else 49 | push %rax 50 | mov g_apic_addr, %rax 51 | movl $1, 0xb0(%rax) # REG_EOI == 0xb0 52 | pop %rax 53 | #endif 54 | .endm 55 | 56 | 57 | # Handlers for hardware interrupts 58 | irq_handlers: 59 | .set i, IRQ_BASE 60 | .rept INTERRUPTS_NUM-IRQ_BASE 61 | 62 | cli # we serialize IRQ handlers and mask all interruptsion until we process the current IRQ 63 | #ifdef CONFIG_SMP 64 | movb $i, %fs:irq_ready_num@tpoff 65 | #else 66 | movb $i, irq_ready_num 67 | #endif 68 | # Acknowledge APIC interruption here 69 | # It is a bit fuzzy when this ack should be called, Intel docs mentions that it should be done before IRET 70 | # but our experiments show that it also can be called from the main context (function event_peek) - 71 | # it works with qemu and vmware. 72 | apic_eoi 73 | iretq 74 | 75 | .set i, i + 1 76 | .endr 77 | irq_handlers_end: 78 | 79 | # We put this section far enough from exception_handlers so assembler does not use short jumps, 80 | # instead it uses near jumps for all handlers and the handler code has the same size 81 | exception_common: 82 | push %r15 83 | push %r14 84 | push %r13 85 | push %r12 86 | push %r11 87 | push %r10 88 | push %r9 89 | push %r8 90 | push %rax 91 | push %rcx 92 | push %rdx 93 | push %rbx 94 | push %rbp 95 | push %rsi 96 | push %rdi 97 | 98 | # TODO: save/restore SSE registers 99 | # TODO: stack alignment for SSE? 100 | 101 | mov %rsp, %rdi # pass pointer to iframe as first argument 102 | 103 | call x86_exception_handler 104 | 105 | pop %rdi 106 | pop %rsi 107 | pop %rbp 108 | pop %rbx 109 | pop %rdx 110 | pop %rcx 111 | pop %rax 112 | pop %r8 113 | pop %r9 114 | pop %r10 115 | pop %r11 116 | pop %r12 117 | pop %r13 118 | pop %r14 119 | pop %r15 120 | 121 | # drop vector number and error code 122 | add $16, %rsp 123 | iretq 124 | -------------------------------------------------------------------------------- /drv/virtio/virtio_console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * Copyright (C) Red Hat, Inc., 2009, 2010, 2011 30 | * Copyright (C) Amit Shah , 2009, 2010, 2011 31 | */ 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /* Feature bits */ 38 | #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ 39 | #define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ 40 | #define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */ 41 | 42 | #define VIRTIO_CONSOLE_BAD_ID (~(__u32)0) 43 | 44 | struct virtio_console_config { 45 | /* columns of the screens */ 46 | __u16 cols; 47 | /* rows of the screens */ 48 | __u16 rows; 49 | /* max. number of ports this device can hold */ 50 | __u32 max_nr_ports; 51 | /* emergency write register */ 52 | __u32 emerg_wr; 53 | } PACKED; 54 | 55 | /* 56 | * A message that's passed between the Host and the Guest for a 57 | * particular port. 58 | */ 59 | struct virtio_console_control { 60 | __virtio32 id; /* Port number */ 61 | __virtio16 event; /* The kind of control event (see below) */ 62 | __virtio16 value; /* Extra information for the key */ 63 | }; 64 | 65 | /* Some events for control messages */ 66 | #define VIRTIO_CONSOLE_DEVICE_READY 0 67 | #define VIRTIO_CONSOLE_PORT_ADD 1 68 | #define VIRTIO_CONSOLE_PORT_REMOVE 2 69 | #define VIRTIO_CONSOLE_PORT_READY 3 70 | #define VIRTIO_CONSOLE_CONSOLE_PORT 4 71 | #define VIRTIO_CONSOLE_RESIZE 5 72 | #define VIRTIO_CONSOLE_PORT_OPEN 6 73 | #define VIRTIO_CONSOLE_PORT_NAME 7 74 | -------------------------------------------------------------------------------- /drv/virtio/virtio_vsock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * Copyright (C) Red Hat, Inc., 2013-2015 30 | * Copyright (C) Asias He , 2013 31 | * Copyright (C) Stefan Hajnoczi , 2015 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | struct __le virtio_vsock_config { 39 | uint64_t guest_cid; 40 | } PACKED; 41 | 42 | enum virtio_vsock_event_id { 43 | VIRTIO_VSOCK_EVENT_TRANSPORT_RESET = 0, 44 | }; 45 | 46 | struct __le virtio_vsock_event { 47 | uint32_t id; 48 | } PACKED; 49 | 50 | struct __le virtio_vsock_hdr { 51 | uint64_t src_cid; 52 | uint64_t dst_cid; 53 | uint32_t src_port; 54 | uint32_t dst_port; 55 | uint32_t len; 56 | uint16_t type; /* enum virtio_vsock_type */ 57 | uint16_t op; /* enum virtio_vsock_op */ 58 | uint32_t flags; 59 | uint32_t buf_alloc; 60 | uint32_t fwd_cnt; 61 | } PACKED; 62 | 63 | enum virtio_vsock_type { 64 | VIRTIO_VSOCK_TYPE_STREAM = 1, 65 | }; 66 | 67 | enum virtio_vsock_op { 68 | VIRTIO_VSOCK_OP_INVALID = 0, 69 | 70 | /* Connect operations */ 71 | VIRTIO_VSOCK_OP_REQUEST = 1, 72 | VIRTIO_VSOCK_OP_RESPONSE = 2, 73 | VIRTIO_VSOCK_OP_RST = 3, 74 | VIRTIO_VSOCK_OP_SHUTDOWN = 4, 75 | 76 | /* To send payload */ 77 | VIRTIO_VSOCK_OP_RW = 5, 78 | 79 | /* Tell the peer our credit info */ 80 | VIRTIO_VSOCK_OP_CREDIT_UPDATE = 6, 81 | /* Request the peer to send the credit info to us */ 82 | VIRTIO_VSOCK_OP_CREDIT_REQUEST = 7, 83 | }; 84 | 85 | /* VIRTIO_VSOCK_OP_SHUTDOWN flags values */ 86 | enum virtio_vsock_shutdown { 87 | VIRTIO_VSOCK_SHUTDOWN_RCV = 1, 88 | VIRTIO_VSOCK_SHUTDOWN_SEND = 2, 89 | }; 90 | -------------------------------------------------------------------------------- /drv/virtio/virtio_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. */ 28 | 29 | /* Virtio devices use a standardized configuration space to define their 30 | * features and pass configuration information, but each implementation can 31 | * store and access that space differently. */ 32 | 33 | /* Status byte for guest to report progress, and synchronize features. */ 34 | /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ 35 | #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 36 | /* We have found a driver for the device. */ 37 | #define VIRTIO_CONFIG_S_DRIVER 2 38 | /* Driver has used its parts of the config, and is happy */ 39 | #define VIRTIO_CONFIG_S_DRIVER_OK 4 40 | /* Driver has finished configuring features */ 41 | #define VIRTIO_CONFIG_S_FEATURES_OK 8 42 | /* Device entered invalid state, driver must reset it */ 43 | #define VIRTIO_CONFIG_S_NEEDS_RESET 0x40 44 | /* We've given up on this device. */ 45 | #define VIRTIO_CONFIG_S_FAILED 0x80 46 | 47 | /* Some virtio feature bits (currently bits 28 through 32) are reserved for the 48 | * transport being used (eg. virtio_ring), the rest are per-device feature 49 | * bits. */ 50 | #define VIRTIO_TRANSPORT_F_START 28 51 | #define VIRTIO_TRANSPORT_F_END 34 52 | 53 | #ifndef VIRTIO_CONFIG_NO_LEGACY 54 | /* Do we get callbacks when the ring is completely used, even if we've 55 | * suppressed them? */ 56 | #define VIRTIO_F_NOTIFY_ON_EMPTY 24 57 | 58 | /* Can the device handle any descriptor layout? */ 59 | #define VIRTIO_F_ANY_LAYOUT 27 60 | #endif /* VIRTIO_CONFIG_NO_LEGACY */ 61 | 62 | /* v1.0 compliant. */ 63 | #define VIRTIO_F_VERSION_1 32 64 | 65 | /* 66 | * If clear - device has the IOMMU bypass quirk feature. 67 | * If set - use platform tools to detect the IOMMU. 68 | * 69 | * Note the reverse polarity (compared to most other features), 70 | * this is for compatibility with legacy systems. 71 | */ 72 | #define VIRTIO_F_IOMMU_PLATFORM 33 73 | -------------------------------------------------------------------------------- /core/net/arp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "net/arp.h" 4 | #include "compiler.h" 5 | #include "kalloc.h" 6 | #include "net/eth.h" 7 | #include "net/ip4.h" 8 | #include "stdio.h" 9 | #include "tree.h" 10 | #include 11 | 12 | #define APR_OP_REQUEST 1 13 | #define APR_OP_REPLY 2 14 | 15 | #define HWTYPE_ETH 1 16 | 17 | struct __be PACKED arp_hdr { 18 | uint16_t hw_type; 19 | uint16_t protocol_type; 20 | uint8_t eth_addr_len; 21 | uint8_t ip_addr_len; 22 | uint16_t operation; 23 | ethaddr_t sender_eth_addr; 24 | ip4addr_t sender_ip_addr; 25 | ethaddr_t target_eth_addr; 26 | ip4addr_t target_ip_addr; 27 | }; 28 | 29 | struct arp_entry { 30 | ethaddr_t eth; 31 | ip4addr_t ip; 32 | // (type) 33 | RB_ENTRY(arp_entry) entries; 34 | }; 35 | 36 | // (name, type) 37 | RB_HEAD(arp_cache, arp_entry) arp_tree; 38 | 39 | static int compare(struct arp_entry *a, struct arp_entry *b) { 40 | if (a->ip < b->ip) 41 | return -1; 42 | else if (a->ip > b->ip) 43 | return 1; 44 | 45 | return 0; 46 | } 47 | 48 | // (name, type, field, cmp) 49 | RB_PROTOTYPE(arp_cache, arp_entry, entries, compare); 50 | RB_GENERATE(arp_cache, arp_entry, entries, compare); 51 | 52 | void arp_cache_add(ip4addr_t ip, ethaddr_t eth) { 53 | struct arp_entry *new = kalloc(struct arp_entry); 54 | new->ip = ip; 55 | new->eth = eth; 56 | 57 | struct arp_entry *existing = RB_INSERT(arp_cache, &arp_tree, new); 58 | if (existing) { 59 | // if the address already exist then update that one instead 60 | kfree(new); 61 | existing->eth = eth; 62 | } 63 | } 64 | 65 | bool arp_cache_lookup(ip4addr_t ip, ethaddr_t *eth) { 66 | struct arp_entry e; 67 | e.ip = ip; 68 | 69 | struct arp_entry *found = RB_FIND(arp_cache, &arp_tree, &e); 70 | if (found) { 71 | *eth = found->eth; 72 | } 73 | return found; 74 | } 75 | 76 | void arp_receive(struct eth_device *dev, buffer_t *buff) { 77 | struct arp_hdr *hdr_in = buff->pos; 78 | 79 | IFVV printf("ARP hw_type=%d proto_type=0x%x sender_ip=" IPADDR_PRINT_FMT " target_ip=" IPADDR_PRINT_FMT " op=%d\n", hdr_in->hw_type, 80 | hdr_in->protocol_type, IPADDR_PRINT_PARAMS(hdr_in->sender_ip_addr), IPADDR_PRINT_PARAMS(hdr_in->target_ip_addr), 81 | hdr_in->operation); 82 | 83 | if (hdr_in->target_ip_addr != dev->ip4if.addr) 84 | return; // not for us 85 | 86 | if (hdr_in->operation == APR_OP_REQUEST) { 87 | buffer_t *outbuff = buffer_allocate(BUFFER_NET_SIZE, HDR_LEN_ETH); 88 | struct arp_hdr *hdr_out = outbuff->pos; 89 | outbuff->data_size += sizeof(*hdr_out); 90 | 91 | hdr_out->operation = APR_OP_REPLY; 92 | hdr_out->hw_type = HWTYPE_ETH; 93 | hdr_out->protocol_type = ETH_TYPE_IP4; 94 | hdr_out->eth_addr_len = sizeof(ethaddr_t); 95 | hdr_out->ip_addr_len = sizeof(ip4addr_t); 96 | 97 | hdr_out->target_ip_addr = hdr_in->sender_ip_addr; 98 | hdr_out->target_eth_addr = hdr_in->sender_eth_addr; 99 | hdr_out->sender_ip_addr = dev->ip4if.addr; 100 | hdr_out->sender_eth_addr = dev->addr; 101 | 102 | eth_send(dev, &hdr_out->target_eth_addr, ETH_TYPE_ARP, outbuff); 103 | } else if (hdr_in->operation == APR_OP_REPLY) { 104 | arp_cache_add(hdr_in->sender_ip_addr, hdr_in->sender_eth_addr); 105 | } else { 106 | printf("Unknown ARP operation %d\n", hdr_in->operation); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /include/kalloc.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "asan.h" 6 | #include "compiler.h" 7 | #include "kalloc.h" 8 | #include "lock.h" 9 | #include "mem.h" 10 | #include "shout.h" 11 | #include "slab.h" 12 | #include "stdio.h" 13 | #include 14 | #include 15 | #include 16 | 17 | #define KALLOC_NO_REDZONE BIT(0) // Do not allocate redzone at the end of the object 18 | #define KALLOC_ASAN_SKIP_INIT BIT(1) // Do not mark allocated area as 'available' 19 | #define KALLOC_WIPE BIT(2) // Do not mark allocated area as 'available' 20 | 21 | static inline void *kalloc_size(size_t size) { 22 | void *obj = alloc_slab_allocate(size + ASAN_REDZONE_SIZE); 23 | // TOTHINK: maybe we should pass redzone size to asan function as well? 24 | // having redzone information there we can check that the area is properly poisoned... 25 | asan_mark_memory_region((uintptr_t)obj, size, ASAN_TAG_RW); 26 | return obj; 27 | } 28 | 29 | static inline void *kalloc_size_flags(size_t size, uint32_t flags) { 30 | size_t total_size = size; 31 | if (!(flags & KALLOC_NO_REDZONE)) 32 | total_size += ASAN_REDZONE_SIZE; 33 | void *obj = alloc_slab_allocate(total_size); 34 | if (!(flags & KALLOC_ASAN_SKIP_INIT)) 35 | asan_mark_memory_region((uintptr_t)obj, size, ASAN_TAG_RW); 36 | PANIC_IF(flags & KALLOC_ASAN_SKIP_INIT && flags & KALLOC_WIPE); // the area is marked as unavailable so we can't touch it here 37 | if (flags & KALLOC_WIPE) 38 | memset(obj, 0, size); 39 | return obj; 40 | } 41 | 42 | static inline void *kalloc_align(size_t size, size_t align) { 43 | PANIC_IF(!ISPOW2(align), "Alignment requirement expected to be power of 2"); 44 | return kalloc_size(ROUND_UP(size, align)); 45 | } 46 | 47 | // BUILD_PANIC_IF(_Generic((type), void : false, default : true), "Please specify type of allocated object or use kalloc_size()"); 48 | #define kalloc(type) ({ (type *)kalloc_align(sizeof(type), __alignof__(type)); }) 49 | 50 | // Allocate array of types 51 | #define kalloca(type, num) (type *)kalloc_align(sizeof(type) * num, __alignof__(type)) 52 | 53 | static inline void kfree_size(void *ptr, size_t size) { 54 | alloc_slab_free(ptr, size + ASAN_REDZONE_SIZE); 55 | asan_mark_memory_region((uintptr_t)ptr, size, ASAN_TAG_SLAB_FREED); 56 | } 57 | 58 | static inline void kfree_size_flags(void *ptr, size_t size, uint32_t flags) { 59 | size_t total_size = size; 60 | if (!(flags & KALLOC_NO_REDZONE)) 61 | total_size += ASAN_REDZONE_SIZE; 62 | alloc_slab_free(ptr, total_size); 63 | 64 | // Note that we mark the memory as 'FREED' even if KALLOC_ASAN_SKIP_INIT is not set. 65 | // KALLOC_ASAN_SKIP_INIT is more about skipping initialization during allocation. 66 | // But when we return the memory back we want to be sure nobody accidentally uses it. 67 | asan_mark_memory_region((uintptr_t)ptr, size, ASAN_TAG_SLAB_FREED); 68 | } 69 | 70 | // This macro needs a pointer to valid type (not to void). It uses type to figure out 71 | // data size to be freed. 72 | #define kfree(ptr) \ 73 | ({ \ 74 | BUILD_PANIC_IF(_Generic((ptr), void * : true, default : false), "Please specify type of freeing pointer or use kfree_size()"); \ 75 | kfree_size(ptr, sizeof(*ptr)); \ 76 | }) 77 | -------------------------------------------------------------------------------- /core/rand.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "rand.h" 4 | #include "compiler.h" 5 | #include "cpu.h" 6 | #include "sha3.h" 7 | #include "shout.h" 8 | #include "stdio.h" 9 | 10 | // Pseudo Random Generator based on maximally equidistributed combined lfsr generator algorithm 11 | // See http://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-01039-X/S0025-5718-99-01039-X.pdf 12 | 13 | PERCPU uint64_t st[5]; 14 | 15 | #define c1 0xFFFFFFFFFFFFFFFEULL // 18446744073709551614ULL 16 | #define c2 0xFFFFFFFFFFFFFE00ULL // 18446744073709551104ULL 17 | #define c3 0xFFFFFFFFFFFFF000ULL // 18446744073709547520ULL 18 | #define c4 0xFFFFFFFFFFFE0000ULL // 18446744073709420544ULL 19 | #define c5 0xFFFFFFFFFF800000ULL // 18446744073701163008ULL 20 | 21 | static uint64_t lfsr258(void) { 22 | st[0] = ((st[0] & c1) << 10) ^ (((st[0] << 1) ^ st[0]) >> 53); 23 | st[1] = ((st[1] & c2) << 5) ^ (((st[1] << 24) ^ st[1]) >> 50); 24 | st[2] = ((st[2] & c3) << 29) ^ (((st[2] << 3) ^ st[2]) >> 23); 25 | st[3] = ((st[3] & c4) << 23) ^ (((st[3] << 5) ^ st[3]) >> 24); 26 | st[4] = ((st[4] & c5) << 8) ^ (((st[4] << 3) ^ st[4]) >> 33); 27 | return st[0] ^ st[1] ^ st[2] ^ st[3] ^ st[4]; 28 | } 29 | 30 | uint64_t rand64(void) { return lfsr258(); } 31 | uint32_t rand32(void) { return (uint32_t)lfsr258(); } 32 | uint16_t rand16(void) { return (uint16_t)lfsr258(); } 33 | uint8_t rand8(void) { return (uint8_t)lfsr258(); } 34 | double rand_double(void) { return lfsr258() * 5.4210108624275221e-20; } 35 | 36 | void rand_array(void *array, size_t length) { 37 | for (; length >= sizeof(uint64_t); length -= sizeof(uint64_t)) { 38 | *(uint64_t *)array = rand64(); 39 | } 40 | for (; length >= sizeof(uint8_t); length -= sizeof(uint8_t)) { 41 | *(uint8_t *)array = rand8(); 42 | } 43 | } 44 | 45 | #define CYCLES_ENTROPY_SIZE 64 46 | 47 | // Modern multiscalar, out-of-order CPUs are devices with a large number of hardware blocks. 48 | // Examples of hardware blocks are: TLB cache, write buffer, branch prediction, hyperthreading, 49 | // pipeline, memory controller. 50 | // And each block has a complex internal state that affects instruction execution time. 51 | // It is hard to predict instructions execution time unless one has a full access to CPU internal state. 52 | // See more information here http://www.chronox.de/jent/doc/CPU-Jitter-NPTRNG.html 53 | // We can use instruction execution time jitter as a source of entropy for our Random Number Generator. 54 | void rand_mixin_cpu_jitter(void) { 55 | sha3_context ctx; 56 | sha3_Init512(&ctx); 57 | 58 | uint64_t entropy_buffer[CYCLES_ENTROPY_SIZE]; 59 | 60 | for (int i = 0; i < CYCLES_ENTROPY_SIZE; i++) { 61 | // Each call to cpu_cycles() has execution time jitter. It is going to be used as a source of entropy. 62 | // Bigger CYCLES_ENTROPY_SIZE more entropy will be added. 63 | // Leter we use a hash alrorithm to whitening entropy of this array. 64 | entropy_buffer[i] = cpu_cycles(); 65 | } 66 | // Use cryptographically strong hash algorithm (SHA3) to whitening CPU cycle jitter 67 | sha3_Update(&ctx, &entropy_buffer, sizeof(entropy_buffer)); 68 | sha3_Finalize(&ctx); // SHA3 algorithm results in 25 64-bit words 69 | 70 | // Mixing in the whitened entropy 71 | size_t i, c; 72 | for (i = 0, c = 0; i < ARRAY_SIZE(st); i++) { 73 | do { 74 | st[i] ^= ctx.s[c++]; 75 | // LSFR generator needs a non zero value for its state, otherwise it generates only zeros 76 | // if st[i] is zero let's get the next word from jitter entropy and XOR it with state again 77 | } while (!st[i] && c < ARRAY_SIZE(ctx.s)); 78 | 79 | if (c == ARRAY_SIZE(ctx.s)) { 80 | SHOUT_IF(i < ARRAY_SIZE(st), "Whitening entropy ran out of data\n"); 81 | break; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /include/float.h: -------------------------------------------------------------------------------- 1 | /* This file is based on MUSL project code */ 2 | 3 | #pragma once 4 | 5 | #include "arch.h" 6 | #include 7 | 8 | #define INT_MAX 0x7fffffff 9 | #define ULONG_MAX -1UL 10 | 11 | static inline int isdigit(int c) { return (unsigned)c - '0' < 10; } 12 | 13 | #define FP_NAN 0 14 | #define FP_INFINITE 1 15 | #define FP_ZERO 2 16 | #define FP_SUBNORMAL 3 17 | #define FP_NORMAL 4 18 | 19 | static __inline unsigned __FLOAT_BITS(float __f) { 20 | union { 21 | float __f; 22 | unsigned __i; 23 | } __u; 24 | __u.__f = __f; 25 | return __u.__i; 26 | } 27 | static __inline unsigned long long __DOUBLE_BITS(double __f) { 28 | union { 29 | double __f; 30 | unsigned long long __i; 31 | } __u; 32 | __u.__f = __f; 33 | return __u.__i; 34 | } 35 | 36 | #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 37 | #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN 38 | union ldshape { 39 | long double f; 40 | struct { 41 | uint64_t m; 42 | uint16_t se; 43 | } i; 44 | }; 45 | #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN 46 | union ldshape { 47 | long double f; 48 | struct { 49 | uint64_t lo; 50 | uint32_t mid; 51 | uint16_t top; 52 | uint16_t se; 53 | } i; 54 | struct { 55 | uint64_t lo; 56 | uint64_t hi; 57 | } i2; 58 | }; 59 | #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN 60 | union ldshape { 61 | long double f; 62 | struct { 63 | uint16_t se; 64 | uint16_t top; 65 | uint32_t mid; 66 | uint64_t lo; 67 | } i; 68 | struct { 69 | uint64_t hi; 70 | uint64_t lo; 71 | } i2; 72 | }; 73 | #else 74 | #error Unsupported long double representation 75 | #endif 76 | 77 | int __fpclassifyl(long double x); 78 | 79 | #define fpclassify(x) (sizeof(x) == sizeof(float) ? __fpclassifyf(x) : sizeof(x) == sizeof(double) ? __fpclassify(x) : __fpclassifyl(x)) 80 | 81 | #define isinf(x) \ 82 | (sizeof(x) == sizeof(float) \ 83 | ? (__FLOAT_BITS(x) & 0x7fffffff) == 0x7f800000 \ 84 | : sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL >> 1) == 0x7ffULL << 52 : __fpclassifyl(x) == FP_INFINITE) 85 | 86 | #define isnan(x) \ 87 | (sizeof(x) == sizeof(float) \ 88 | ? (__FLOAT_BITS(x) & 0x7fffffff) > 0x7f800000 \ 89 | : sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL >> 1) > 0x7ffULL << 52 : __fpclassifyl(x) == FP_NAN) 90 | 91 | #define isnormal(x) \ 92 | (sizeof(x) == sizeof(float) \ 93 | ? ((__FLOAT_BITS(x) + 0x00800000) & 0x7fffffff) >= 0x01000000 \ 94 | : sizeof(x) == sizeof(double) ? ((__DOUBLE_BITS(x) + (1ULL << 52)) & -1ULL >> 1) >= 1ULL << 53 : __fpclassifyl(x) == FP_NORMAL) 95 | 96 | #define isfinite(x) \ 97 | (sizeof(x) == sizeof(float) \ 98 | ? (__FLOAT_BITS(x) & 0x7fffffff) < 0x7f800000 \ 99 | : sizeof(x) == sizeof(double) ? (__DOUBLE_BITS(x) & -1ULL >> 1) < 0x7ffULL << 52 : __fpclassifyl(x) > FP_INFINITE) 100 | 101 | int __signbitl(long double x); 102 | 103 | #define signbit(x) \ 104 | (sizeof(x) == sizeof(float) ? (int)(__FLOAT_BITS(x) >> 31) \ 105 | : sizeof(x) == sizeof(double) ? (int)(__DOUBLE_BITS(x) >> 63) : __signbitl(x)) 106 | 107 | double frexp(double x, int *e); 108 | long double frexpl(long double x, int *e); -------------------------------------------------------------------------------- /drv/virtio/virtio_virtq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * 5 | * Virtual I/O Device (VIRTIO) Version 1.0 6 | * Committee Specification 04 7 | * 03 March 2016 8 | * Copyright (c) OASIS Open 2016. All Rights Reserved. 9 | * Source: http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/listings/ 10 | * Link to latest version of the specification documentation: http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html 11 | * 12 | */ 13 | #include "compiler.h" 14 | #include "shout.h" 15 | #include 16 | 17 | /* This marks a buffer as continuing via the next field. */ 18 | #define VIRTQ_DESC_F_NEXT BIT(0) 19 | /* This marks a buffer as write-only (otherwise read-only). */ 20 | #define VIRTQ_DESC_F_WRITE BIT(1) 21 | /* This means the buffer contains a list of buffer descriptors. */ 22 | #define VIRTQ_DESC_F_INDIRECT BIT(2) 23 | 24 | /* The device uses this in used->flags to advise the driver: don't kick me 25 | * when you add a buffer. It's unreliable, so it's simply an 26 | * optimization. */ 27 | #define VIRTQ_USED_F_NO_NOTIFY 1 28 | /* The driver uses this in avail->flags to advise the device: don't 29 | * interrupt me when you consume a buffer. It's unreliable, so it's 30 | * simply an optimization. */ 31 | #define VIRTQ_AVAIL_F_NO_INTERRUPT 1 32 | 33 | /* Support for indirect descriptors */ 34 | #define VIRTIO_F_INDIRECT_DESC 28 35 | 36 | /* Support for avail_event and used_event fields */ 37 | #define VIRTIO_F_EVENT_IDX 29 38 | 39 | /* Arbitrary descriptor layouts. */ 40 | #define VIRTIO_F_ANY_LAYOUT 27 41 | 42 | /* Virtqueue descriptors: 16 bytes. 43 | * These can chain together via "next". */ 44 | struct __le virtq_desc { 45 | /* Address (guest-physical). */ 46 | uint64_t addr; 47 | /* Length. */ 48 | uint32_t len; 49 | /* The flags as indicated above. */ 50 | uint16_t flags; 51 | /* We chain unused descriptors via this, too */ 52 | uint16_t next; 53 | }; 54 | BUILD_PANIC_IF(sizeof(struct virtq_desc) != 16); 55 | 56 | struct __le virtq_avail { 57 | uint16_t flags; 58 | uint16_t idx; 59 | uint16_t ring[]; 60 | /* Only if VIRTIO_F_EVENT_IDX: uint16_t used_event; */ 61 | }; 62 | 63 | /* uint32_t is used here for ids for padding reasons. */ 64 | struct __le virtq_used_elem { 65 | /* Index of start of used descriptor chain. */ 66 | uint32_t id; 67 | /* Total length of the descriptor chain which was written to. */ 68 | uint32_t len; 69 | }; 70 | 71 | struct __le virtq_used { 72 | uint16_t flags; 73 | uint16_t idx; 74 | struct virtq_used_elem ring[]; 75 | /* Only if VIRTIO_F_EVENT_IDX: uint16_t avail_event; */ 76 | }; 77 | 78 | struct virtq { 79 | uint16_t index; 80 | uint16_t size; 81 | __mmio uint16_t *notify; // writing virtq index to this address notifies device 82 | 83 | uint16_t last_used_idx; // last item consumed by driver 84 | uint16_t unused_desc_idx; // free descriptors are linked to each other, this index points to the first unused descriptor 85 | uint16_t unused_desc_size; // number of descriptors in the 'unused' list 86 | 87 | struct virtq_desc *desc; 88 | struct virtq_avail *avail; 89 | struct virtq_used *used; 90 | }; 91 | 92 | static inline int virtq_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old_idx) { 93 | return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old_idx); 94 | } 95 | 96 | /* Get location of event indices (only with VIRTIO_F_EVENT_IDX) */ 97 | static inline uint16_t *virtq_used_event(struct virtq *vq) { 98 | /* For backwards compat, used event index is at *end* of avail ring. */ 99 | return &vq->avail->ring[vq->size]; 100 | } 101 | 102 | static inline uint16_t *virtq_avail_event(struct virtq *vq) { 103 | /* For backwards compat, avail event index is at *end* of used ring. */ 104 | return (uint16_t *)&vq->used->ring[vq->size]; 105 | } -------------------------------------------------------------------------------- /arch/x86/x2apic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "apic.h" 4 | #include "compiler.h" 5 | #include "config.h" 6 | #include "cpu.h" 7 | #include "interrupt.h" 8 | #include "stdio.h" 9 | #include "x86.h" 10 | 11 | #define REG_ID 0x2 // Local APIC ID 12 | #define REG_VERSION 0x3 // Local APIC Version 13 | #define REG_TPR 0x8 // Task Priority 14 | #define REG_EOI 0xb 15 | #define REG_LOGIC_DEST 0xd // Loginc Destination 16 | #define REG_DEST_FORMAT 0xe // Destination Format 17 | #define REG_SPURIOUS_INT_VECTOR 0xf 18 | #define REG_ICR 0x30 19 | #define REG_LVT_TIMER 0x32 20 | #define REG_TIMER_INIT_COUNT 0x38 21 | #define REG_TIMER_CURRENT_COUNT 0x39 22 | #define REG_TIMER_DIVIDER 0x3e 23 | #define REG_SELF_IPI 0x3f 24 | 25 | #define APIC_ENABLE BIT(8) // set in REG_SPURIOUS_INT_VECTOR register 26 | 27 | #define TIMER_ONE_SHOT (0 << 17) 28 | #define TIMER_PERIODIC (1 << 17) 29 | #define TIMER_TSC_DEADLINE (2 << 17) 30 | 31 | #define ICR_DEST_NOSHORTHAND (0 << 18) 32 | #define ICR_DEST_SELF (1 << 18) 33 | #define ICR_DEST_ALLINCLUDINGSELF (2 << 18) 34 | #define ICR_DEST_ALLEXCLUDINGSELF (3 << 18) 35 | 36 | #define ICR_TRIGGER_LEVEL BIT(15) 37 | #define ICR_ASSERT BIT(14) 38 | #define ICR_DESTMODEL_LOGICAL BIT(11) 39 | 40 | #define ICR_MODE_FIXED (0b000 << 8) 41 | #define ICR_MODE_SMI (0b010 << 8) 42 | #define ICR_MODE_NMI (0b100 << 8) 43 | #define ICR_MODE_INIT (0b101 << 8) 44 | #define ICR_MODE_STARUP (0b110 << 8) 45 | 46 | #define APIC_BASE_ADDR 0x800 // start of APIC MSR register space 47 | 48 | #define APIC_READ(reg) x86_rdmsr(APIC_BASE_ADDR + reg) 49 | #define APIC_WRITE(reg, val) x86_wrmsr(APIC_BASE_ADDR + reg, val) 50 | 51 | static void apic_timer_handler(UNUSED void *data) { 52 | static uint32_t counter = 0; 53 | printf("Hello from APIC timer #%d, counter %d\n", current_cpu_id, counter++); 54 | apic_interrupt_ack(); 55 | } 56 | 57 | UNUSED static void apic_timer_setup(void) { 58 | uint32_t eax, unused; 59 | x86_cpuid(CPUID_THERMAL_N_POWER, &eax, &unused, &unused, &unused); 60 | if (!(eax & BIT(2))) { 61 | printf("The device does not support constant frequency APIC timer"); 62 | return; 63 | } 64 | 65 | // register APIC timer 66 | interrupt_register_with_vector(INTERRUPT_APIC_TIMER, apic_timer_handler, NULL); 67 | 68 | APIC_WRITE(REG_TIMER_DIVIDER, 0xa); // divider is 128 69 | APIC_WRITE(REG_LVT_TIMER, INTERRUPT_APIC_TIMER | TIMER_PERIODIC); 70 | APIC_WRITE(REG_TIMER_INIT_COUNT, 10000000); // ~5 seconds delay on my 4GHz computer 71 | } 72 | 73 | void apic_init(void) { 74 | uint32_t ecx, unused; 75 | x86_cpuid(CPUID_FEATURES, &unused, &unused, &ecx, &unused); 76 | PANIC_IF(!(ecx & CPUID_ECX_X2APIC), "Hardware does not support x2APIC mode"); 77 | 78 | uint64_t msr = x86_rdmsr(MSR_APIC_BASE); 79 | msr |= MSR_APIC_ENABLE; 80 | msr |= MSR_APIC_X2MODE; 81 | x86_wrmsr(MSR_APIC_BASE, msr); 82 | 83 | // Specify spurious intr vector and enable APIC 84 | APIC_WRITE(REG_SPURIOUS_INT_VECTOR, INTERRUPT_APIC_SPURIOUS | APIC_ENABLE); 85 | 86 | uint32_t data = APIC_READ(REG_VERSION); 87 | uint8_t version = data & 0xff; 88 | uint32_t max_lvt = (data >> 16) & 0xff; 89 | IFD printf("Local x2APIC: id=0x%lx ver=0x%x max_lvt=%d\n", APIC_READ(REG_ID), version, max_lvt); 90 | 91 | // apic_timer_setup(); 92 | } 93 | 94 | uint32_t apic_cpu_id(void) { return APIC_READ(REG_ID); } 95 | 96 | void apic_interrupt_ack(void) { APIC_WRITE(REG_EOI, 0); } 97 | 98 | void apic_ap_init(uint32_t apic_id) { 99 | uint64_t dest = (uint64_t)apic_id << 32; 100 | APIC_WRITE(REG_ICR, dest | ICR_ASSERT | ICR_MODE_INIT); 101 | } 102 | 103 | void apic_ap_start(uint32_t apic_id, uint32_t address) { 104 | uint8_t vector = address >> PAGE_SIZE_SFT; 105 | uint64_t dest = (uint64_t)apic_id << 32; 106 | APIC_WRITE(REG_ICR, dest | ICR_ASSERT | ICR_MODE_STARUP | vector); 107 | } 108 | -------------------------------------------------------------------------------- /drv/virtio/virtio_balloon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* This header is BSD licensed so anyone can use the definitions to implement 4 | * compatible drivers/servers. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of IBM nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | /* The feature bitmap for virtio balloon */ 34 | #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ 35 | #define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */ 36 | #define VIRTIO_BALLOON_F_DEFLATE_ON_OOM 2 /* Deflate balloon on OOM */ 37 | 38 | /* Size of a PFN in the balloon interface. */ 39 | #define VIRTIO_BALLOON_PFN_SHIFT 12 40 | 41 | struct virtio_balloon_config { 42 | /* Number of pages host wants Guest to give up. */ 43 | __u32 num_pages; 44 | /* Number of pages we've actually got in balloon. */ 45 | __u32 actual; 46 | }; 47 | 48 | #define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ 49 | #define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ 50 | #define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ 51 | #define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ 52 | #define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ 53 | #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ 54 | #define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */ 55 | #define VIRTIO_BALLOON_S_NR 7 56 | 57 | /* 58 | * Memory statistics structure. 59 | * Driver fills an array of these structures and passes to device. 60 | * 61 | * NOTE: fields are laid out in a way that would make compiler add padding 62 | * between and after fields, so we have to use compiler-specific attributes to 63 | * pack it, to disable this padding. This also often causes compiler to 64 | * generate suboptimal code. 65 | * 66 | * We maintain this statistics structure format for backwards compatibility, 67 | * but don't follow this example. 68 | * 69 | * If implementing a similar structure, do something like the below instead: 70 | * struct virtio_balloon_stat { 71 | * __virtio16 tag; 72 | * __u8 reserved[6]; 73 | * __virtio64 val; 74 | * }; 75 | * 76 | * In other words, add explicit reserved fields to align field and 77 | * structure boundaries at field size, avoiding compiler padding 78 | * without the packed attribute. 79 | */ 80 | struct virtio_balloon_stat { 81 | __virtio16 tag; 82 | __virtio64 val; 83 | } PACKED; 84 | -------------------------------------------------------------------------------- /core/timer.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "timer.h" 4 | #include "arch_timer.h" 5 | #include "kalloc.h" 6 | #include 7 | 8 | struct timer { 9 | time_t alarm_time; 10 | timer_callback callback; 11 | void *context; 12 | RB_ENTRY(timer) rbnode; 13 | }; 14 | RB_HEAD(timer_list, timer) timers_head = RB_INITIALIZER(&timers_head); 15 | time_t next_alarm_time = 0; 16 | 17 | static int timer_compare(struct timer *a, struct timer *b) { 18 | // There can be multiple timers with the same alarm time 19 | // We should allow entries with the same key (alarm_time) thus never return 20 | // '0' from this function. 21 | return a->alarm_time < b->alarm_time ? -1 : 1; 22 | } 23 | 24 | RB_PROTOTYPE(timer_list, timer, rbnode, timer_compare); 25 | RB_GENERATE(timer_list, timer, rbnode, timer_compare); 26 | 27 | static void timer_next_alarm_update(void) { 28 | if (RB_EMPTY(&timers_head)) { 29 | arch_timer_disable(); 30 | return; 31 | } 32 | 33 | struct timer *next_timer = RB_MIN(timer_list, &timers_head); 34 | if (next_timer->alarm_time != next_alarm_time) { 35 | arch_timer_set_alarm(next_timer->alarm_time); 36 | next_alarm_time = next_timer->alarm_time; 37 | 38 | // TODO: we should check that the time we set is in the future 39 | // Some timers (like HPET) do not work if the time is in the past 40 | } 41 | } 42 | 43 | struct timer *timer_add(time_t alarm_time, timer_callback callback, void *context) { 44 | struct timer *timer = kalloc(struct timer); 45 | timer->alarm_time = alarm_time; 46 | timer->callback = callback; 47 | timer->context = context; 48 | 49 | struct timer *existing = RB_INSERT(timer_list, &timers_head, timer); 50 | IFD SHOUT_IF(existing, "Found existing timer"); 51 | 52 | timer_next_alarm_update(); 53 | 54 | return timer; 55 | } 56 | 57 | void timer_change(struct timer *timer, time_t alarm_time) { 58 | if (timer->alarm_time == alarm_time) 59 | return; 60 | 61 | RB_REMOVE(timer_list, &timers_head, timer); 62 | timer->alarm_time = alarm_time; 63 | RB_INSERT(timer_list, &timers_head, timer); 64 | 65 | timer_next_alarm_update(); 66 | } 67 | 68 | void timer_delete(struct timer *timer) { 69 | RB_REMOVE(timer_list, &timers_head, timer); 70 | kfree(timer); 71 | 72 | timer_next_alarm_update(); 73 | } 74 | 75 | static void timer_irq_handler(UNUSED void *context) { 76 | while (true) { 77 | struct timer *timer = RB_MIN(timer_list, &timers_head); 78 | if (!timer) 79 | return; 80 | 81 | if (timer->alarm_time > time_now()) 82 | break; 83 | 84 | timer->callback(timer->context); 85 | RB_REMOVE(timer_list, &timers_head, timer); 86 | kfree(timer); 87 | } 88 | 89 | timer_next_alarm_update(); 90 | } 91 | 92 | void timer_system_init(void) { arch_timer_init(timer_irq_handler); } 93 | 94 | void sleep_us(uint64_t value) { 95 | time_t end = time_us_from_now(value); 96 | 97 | // TODO: check for overflow here and everywhere else where we compare with time_now() 98 | while (time_now() < end) 99 | ; 100 | } 101 | 102 | void sleep_ns(uint64_t value) { 103 | time_t end = time_ns_from_now(value); 104 | 105 | // TODO: check for overflow here and everywhere else where we compare with time_now() 106 | while (time_now() < end) 107 | ; 108 | } 109 | 110 | bool wait_for_clear(__mmio const uint32_t *reg, uint32_t mask, time_t timeout) { 111 | do { 112 | if (!(*reg & mask)) 113 | return false; 114 | sleep_us(10); 115 | } while (time_now() < timeout); 116 | return true; 117 | } 118 | 119 | bool wait_for_set(__mmio const uint32_t *reg, uint32_t mask, time_t timeout) { 120 | do { 121 | if (*reg & mask) 122 | return false; 123 | sleep_us(10); 124 | } while (time_now() < timeout); 125 | return true; 126 | } 127 | -------------------------------------------------------------------------------- /arch/x86/hpet.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "acpi.h" 4 | #include "arch_timer.h" 5 | #include "asan.h" 6 | #include "compiler.h" 7 | #include "cpu.h" 8 | #include "interrupt.h" 9 | #include "mmio.h" 10 | #include "mmu.h" 11 | #include "pci.h" 12 | #include "stdio.h" 13 | #include "timer.h" 14 | #include 15 | 16 | // HPET spec https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf 17 | 18 | // HPET registers 19 | #define REG_ID 0x0 20 | #define REG_CONFIG 0x10 21 | #define REG_INTR_STATUS 0x20 22 | #define REG_COUNTER_VALUE 0xf0 23 | 24 | // Timer-specific registers at base address == g_hpet_addr + 0x100 + 0x20 * N 25 | #define REG_TIMER_CONFIG 0x0 26 | #define REG_TIMER_COMPARATOR_VALUE 0x8 27 | #define REG_TIMER_FSB_IRQ_ROUTE 0x10 28 | 29 | // Bitfields for HPET_REG(REG_CONFIG) 30 | #define HPET_COUNTER_ENABLE BIT(0) 31 | #define HPET_LEGACY_REPLACEMENT BIT(1) 32 | 33 | // Bitfields for HPET_TIMER_REG(XXX, REG_TIMER_CONFIG) 34 | #define HPET_TIMER_INTR_EN BIT(2) 35 | #define HPET_TIMER_ROUTE_SFT 9 36 | #define HPET_TIMER_ROUTE (0 << HPET_TIMER_ROUTE_SFT) 37 | #define HPET_TIMER_FSB_EN BIT(14) // Send interrupt messages over FSB (it is MSI essentially) 38 | #define HPET_TIMER_FSB_SUPPORTED BIT(15) 39 | 40 | #define HPET_REG(reg) MMIO64(g_hpet_addr + reg) 41 | #define HPET_TIMER_REG(n, reg) MMIO64(g_hpet_addr + 0x100 + 0x20 * n + reg) 42 | 43 | #define HPET_TICK_SCALE 1000000000000000 // HPET reports tick period in femto-seconds 44 | uint32_t hpet_tick_per_ms; 45 | 46 | static uint64_t hpet_ticks(uint64_t msec) { return hpet_tick_per_ms * msec; } 47 | 48 | time_t time_now(void) { return HPET_REG(REG_COUNTER_VALUE); } 49 | time_t time_ns_from_now(uint64_t ns) { return time_now() + hpet_ticks(ns) / 1000000; } 50 | time_t time_us_from_now(uint64_t us) { return time_now() + hpet_ticks(us) / 1000; } 51 | time_t time_ms_from_now(uint64_t ms) { return time_now() + hpet_ticks(ms); } 52 | time_t time_sec_from_now(uint64_t sec) { return time_now() + hpet_ticks(1000 * sec); } 53 | time_t time_min_from_now(uint64_t min) { return time_now() + hpet_ticks(60 * 1000 * min); } 54 | time_t time_hours_from_now(uint64_t hours) { return time_now() + hpet_ticks(3600 * 1000 * hours); } 55 | time_t time_days_from_now(uint64_t days) { return time_now() + hpet_ticks(24 * 3600 * 1000 * days); } 56 | 57 | void arch_timer_init(event_handler_t irq_handler) { 58 | PANIC_IF(!g_hpet_addr, "HPET address is not initialized"); 59 | 60 | page_table_set_bit(g_hpet_addr, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITABLE | PAGE_CACHE_DISABLE, 61 | PAGE_PRESENT | PAGE_WRITABLE | PAGE_CACHE_DISABLE); 62 | asan_mark_memory_region(g_hpet_addr, PAGE_SIZE, ASAN_TAG_RW); 63 | 64 | uint64_t id = HPET_REG(REG_ID); 65 | uint64_t config = HPET_REG(REG_CONFIG); 66 | uint32_t hpet_clock_period = id >> 32; 67 | hpet_tick_per_ms = HPET_TICK_SCALE / hpet_clock_period / 1000; 68 | 69 | printf("HPET cap=0x%lx config=0x%lx ticks_per_ms=%d\n", id, config, hpet_tick_per_ms); 70 | 71 | uint64_t timercfg = HPET_TIMER_REG(0, REG_TIMER_CONFIG); 72 | if (timercfg & HPET_TIMER_FSB_SUPPORTED) { 73 | uint8_t vector = interrupt_register(irq_handler, NULL); 74 | 75 | uint32_t addr; 76 | uint16_t val; 77 | pci_msi_addr(vector, &addr, &val); 78 | HPET_TIMER_REG(0, REG_TIMER_FSB_IRQ_ROUTE) = ((uint64_t)addr << 32) | val; 79 | HPET_TIMER_REG(0, REG_TIMER_CONFIG) = timercfg | HPET_TIMER_FSB_EN; 80 | } else { 81 | // vmware does not support HPET FSB delivery method... 82 | HPET_REG(REG_CONFIG) |= HPET_LEGACY_REPLACEMENT; 83 | irq_register(IRQ_TIMER, irq_handler, NULL); 84 | } 85 | HPET_REG(REG_CONFIG) |= HPET_COUNTER_ENABLE; 86 | } 87 | 88 | // arch_timer_init need to be run first, otherwise result is undefined 89 | void arch_timer_set_alarm(time_t time) { 90 | // enable timer at comparator 0 91 | HPET_TIMER_REG(0, REG_TIMER_COMPARATOR_VALUE) = time; 92 | HPET_TIMER_REG(0, REG_TIMER_CONFIG) |= HPET_TIMER_INTR_EN; 93 | } 94 | 95 | void arch_timer_disable(void) { HPET_TIMER_REG(0, REG_TIMER_CONFIG) &= ~HPET_TIMER_INTR_EN; } 96 | -------------------------------------------------------------------------------- /include/compiler.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "config.h" 6 | 7 | #ifndef __ASSEMBLER__ 8 | typedef signed long int ssize_t; 9 | #endif /* ! __ASSEMBLER__ */ 10 | 11 | #define PRINTFLIKE(__fmt, __varargs) __attribute__((__format__(__printf__, __fmt, __varargs))) 12 | #define PACKED __attribute__((packed)) 13 | #define ALIGNED(x) __attribute__((aligned(x))) 14 | #define NORETURN __attribute__((noreturn)) 15 | #define NOINLINE __attribute__((noinline)) 16 | #define SECTION(s) __attribute__((section(s))) 17 | #define UNUSED __attribute__((unused)) 18 | #define USED __attribute__((used)) 19 | #define INIT_CODE SECTION(".init.text") 20 | #define INIT_DATA SECTION(".init.data") 21 | 22 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 23 | #define SIZEOF_FIELD(type, field) sizeof(((type *)NULL)->field) 24 | 25 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 26 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 27 | 28 | // rounds n up to multiple of d. d have to be power of 2. 29 | #define ROUND_UP(n, d) ((n + (__typeof__(n))d - 1) & ~((__typeof__(n))d - 1)) 30 | #define ROUND_DOWN(n, d) ((n) & ~((__typeof__(n))d - 1)) 31 | #define IS_ROUNDED(n, d) (((n) & ((__typeof__(n))d - 1)) == 0) 32 | 33 | #define DIV_ROUND_UP(n, d) ((n + (__typeof__(n))d - 1) / (__typeof__(n))(d)) 34 | 35 | #define ISPOW2(x) ((x) != 0 && ((x & (x - 1)) == 0)) 36 | 37 | // Number of leading zero bits 38 | #define CLZ(x) \ 39 | _Generic(x, int \ 40 | : __builtin_clz, unsigned int \ 41 | : __builtin_clz, long \ 42 | : __builtin_clzl, unsigned long \ 43 | : __builtin_clzl, long long \ 44 | : __builtin_clzll, unsigned long long \ 45 | : __builtin_clzll)(x) 46 | // One plus position of the least significant '1' bit 47 | #define FFS(x) \ 48 | _Generic(x, int \ 49 | : __builtin_ffs, unsigned int \ 50 | : __builtin_ffs, long \ 51 | : __builtin_ffsl, unsigned long \ 52 | : __builtin_ffsl, long long \ 53 | : __builtin_ffsll, unsigned long long \ 54 | : __builtin_ffsll)(x) 55 | // One plus position of the most significant '1' bit 56 | #define FLS(x) ((x) ? sizeof(x) * 8 - CLZ(x) : 0) 57 | // Integer log with base 2 58 | #define ILOG2(x) (FLS(x) - 1) 59 | // Integer log with base 2 rounding result up 60 | #define ILOG2_UP(x) FLS((x)-1) 61 | #define PAGE_ORDER(order) ((uint64_t)1 << (order)) 62 | 63 | // XXX: if we use __builtin_clz can compiler optimize it out? 64 | #define ROUND_UP_POW2(x) ((uint64_t)1 << ILOG2_UP(x)) 65 | 66 | #define BIT(x) ((uint64_t)1 << (x)) 67 | 68 | #define IFD if (IS_ENABLED(CONFIG_DEBUG)) 69 | #define IFV if (IS_ENABLED(CONFIG_DEBUG)) 70 | #define IFVV if (IS_ENABLED(CONFIG_DEBUG)) 71 | 72 | #ifdef CONFIG_SMP 73 | #define PERCPU _Thread_local 74 | #else 75 | #define PERCPU 76 | #endif 77 | 78 | // A marker macro 79 | #define __le __attribute__((scalar_storage_order("little-endian"))) 80 | #define __be __attribute__((scalar_storage_order("big-endian"))) 81 | #define __mmio volatile 82 | 83 | #define bswap(x) _Generic(x, uint16_t : __builtin_bswap16, uint32_t : __builtin_bswap32, uint64_t : __builtin_bswap64)(x) 84 | 85 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 86 | #define cpu_to_le(x) (x) 87 | #define le_to_cpu(x) (x) 88 | #define cpu_to_be(x) bswap(x) 89 | #define be_to_cpu(x) bswap(x) 90 | #else 91 | #define cpu_to_le(x) bswap(x) 92 | #define le_to_cpu(x) bswap(x) 93 | #define cpu_to_be(x) (x) 94 | #define be_to_cpu(x) (x) 95 | #endif 96 | 97 | #define COMPILER_BARRIER __asm__ volatile("" : : : "memory") 98 | 99 | #define container_of(ptr, type, member) \ 100 | ({ \ 101 | const void *__mptr = (void *)(ptr); \ 102 | ((type *)(__mptr - offsetof(type, member))); \ 103 | }) 104 | 105 | #define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, NAME, ...) NAME 106 | -------------------------------------------------------------------------------- /arch/x86/xapic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "acpi.h" 4 | #include "apic.h" 5 | #include "compiler.h" 6 | #include "interrupt.h" 7 | #include "mmio.h" 8 | #include "mmu.h" 9 | #include "stdio.h" 10 | #include "x86.h" 11 | #include 12 | 13 | #define REG_ID 0x20 // Local APIC ID 14 | #define REG_VERSION 0x30 // Local APIC Version 15 | #define REG_TPR 0x80 // Task Priority 16 | #define REG_EOI 0xb0 17 | #define REG_LOGIC_DEST 0xd0 // Loginc Destination 18 | #define REG_DEST_FORMAT 0xe0 // Destination Format 19 | #define REG_SPURIOUS_INT_VECTOR 0xf0 20 | #define REG_ICR_LOW 0x300 21 | #define REG_ICR_HIGH 0x310 22 | #define REG_LVT_TIMER 0x320 23 | #define REG_TIMER_INIT_COUNT 0x380 24 | #define REG_TIMER_CURRENT_COUNT 0x390 25 | #define REG_TIMER_DIVIDER 0x3e0 26 | 27 | #define APIC_ENABLE BIT(8) // set in REG_SPURIOUS_INT_VECTOR register 28 | 29 | #define TIMER_ONE_SHOT (0 << 17) 30 | #define TIMER_PERIODIC (1 << 17) 31 | #define TIMER_TSC_DEADLINE (2 << 17) 32 | 33 | #define ICR_DEST_NOSHORTHAND (0 << 18) 34 | #define ICR_DEST_SELF (1 << 18) 35 | #define ICR_DEST_ALLINCLUDINGSELF (2 << 18) 36 | #define ICR_DEST_ALLEXCLUDINGSELF (3 << 18) 37 | 38 | #define ICR_TRIGGER_LEVEL BIT(15) 39 | #define ICR_ASSERT BIT(14) 40 | #define ICR_DESTMODEL_LOGICAL BIT(11) 41 | 42 | #define ICR_MODE_FIXED (0b000 << 8) 43 | #define ICR_MODE_LOWESTPRIO (0b001 << 8) 44 | #define ICR_MODE_SMI (0b010 << 8) 45 | #define ICR_MODE_NMI (0b100 << 8) 46 | #define ICR_MODE_INIT (0b101 << 8) 47 | #define ICR_MODE_STARTUP (0b110 << 8) 48 | 49 | #define APIC_REG(reg) MMIO32(g_apic_addr + reg) 50 | 51 | UNUSED static void apic_timer_setup(void) { 52 | uint32_t eax, unused; 53 | x86_cpuid(CPUID_THERMAL_N_POWER, &eax, &unused, &unused, &unused); 54 | if (!(eax & BIT(2))) { 55 | printf("The device does not support constant frequency APIC timer"); 56 | return; 57 | } 58 | 59 | uint32_t tsc_denominator, tsc_numerator, crystal_freq; 60 | x86_cpuid(CPUID_TSC_FREQ, &tsc_denominator, &tsc_numerator, &crystal_freq, &unused); 61 | // printf("Crystal freq %ld HZ, tsc freq %ld HZ\n", crystal_freq, crystal_freq * tsc_numerator / tsc_denominator); 62 | 63 | APIC_REG(REG_TIMER_DIVIDER) = 0xa; // divider is 128 64 | APIC_REG(REG_TIMER_INIT_COUNT) = 30000000; // ~5 seconds delay on my 4GHz computer 65 | mb(); 66 | uint8_t INTERRUPT_TIMER = 100; // XXX: instead of hardcoding this number we need to use one from interrupt_register() 67 | APIC_REG(REG_LVT_TIMER) = INTERRUPT_TIMER | TIMER_PERIODIC; 68 | } 69 | 70 | void apic_init(void) { 71 | // according to x2apic spec section 2.3.2 MMIO address space size is 0xc00 72 | page_table_set_bit(g_apic_addr, 0x1000, PAGE_PRESENT | PAGE_WRITABLE, PAGE_PRESENT | PAGE_WRITABLE); 73 | 74 | uint64_t msr = x86_rdmsr(MSR_APIC_BASE); 75 | msr |= MSR_APIC_ENABLE; 76 | x86_wrmsr(MSR_APIC_BASE, msr); 77 | 78 | // Specify spurious intr vector and enable APIC 79 | APIC_REG(REG_SPURIOUS_INT_VECTOR) = INTERRUPT_APIC_SPURIOUS | APIC_ENABLE; 80 | 81 | // XXX: do we need followinig init code? 82 | // APIC_REG(REG_DEST_FORMAT) = 0xffffffff; // Flat mode 83 | // APIC_REG(REG_LOGIC_DEST) = 0x01000000; // All cpus use logical id 1 84 | // APIC_REG(REG_TPR) &= ~0xff; 85 | // enable APIC 86 | // APIC_REG(REG_SPURIOUS_INT_VECTOR) |= APIC_ENABLE; 87 | 88 | // apic_timer_setup(); 89 | } 90 | 91 | uint32_t apic_cpu_id(void) { return APIC_REG(REG_ID) >> 24; } 92 | 93 | void apic_interrupt_ack(void) { APIC_REG(REG_EOI) = 1; } 94 | 95 | void apic_ap_init(uint32_t apic_id) { 96 | PANIC_IF(apic_id >= 256, "xapic id must be 8 bits"); 97 | // First we write to high word 98 | APIC_REG(REG_ICR_HIGH) = (apic_id << 24); 99 | // writing to low word causes the IPI 100 | APIC_REG(REG_ICR_LOW) = ICR_ASSERT | ICR_MODE_INIT; 101 | }; 102 | 103 | void apic_ap_start(uint32_t apic_id, uint32_t address) { 104 | PANIC_IF(apic_id >= 256, "xapic id must be 8 bits"); 105 | 106 | uint8_t vector = address >> PAGE_SIZE_SFT; 107 | APIC_REG(REG_ICR_HIGH) = (apic_id << 24); 108 | APIC_REG(REG_ICR_LOW) = ICR_ASSERT | ICR_MODE_STARTUP | vector; 109 | } 110 | -------------------------------------------------------------------------------- /core/string.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "string.h" 4 | #include 5 | 6 | size_t strlen(const char *s) { 7 | size_t res = 0; 8 | while (*s++) { 9 | res++; 10 | } 11 | return res; 12 | } 13 | 14 | size_t strnlen(const char *s, size_t maxlen) { 15 | size_t res = 0; 16 | while (*s++ && maxlen-- > 0) { 17 | res++; 18 | } 19 | return res; 20 | } 21 | 22 | int strcmp(const char *s1, const char *s2) { 23 | for (; *s1 && *s2 && *s1 == *s2; s1++, s2++) 24 | ; 25 | return *s1 - *s2; 26 | } 27 | 28 | int strncmp(const char *s1, const char *s2, size_t n) { 29 | for (; *s1 && *s2 && n && *s1 == *s2; s1++, s2++, n--) 30 | ; 31 | 32 | return n == 0 ? 0 : *s1 - *s2; 33 | } 34 | 35 | /* Lookup table for digit values. '0' <= X <= 'z */ 36 | static const int8_t table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 37 | 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, 10, 38 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; 39 | 40 | int64_t strtonum(const char *nptr, const char **endptr, int base) { 41 | int64_t result = 0; 42 | bool negative = false; 43 | 44 | while (*nptr == ' ') { 45 | // skip leading spaces 46 | nptr++; 47 | } 48 | 49 | if (*nptr == '-') { 50 | negative = true; 51 | nptr++; 52 | } 53 | 54 | while (true) { 55 | char chr = *nptr++; 56 | if (chr < '0' || chr > 'z') { 57 | // out of range 58 | break; 59 | } 60 | int8_t digit = table[chr - '0']; 61 | if (digit >= base) 62 | break; // out of range 63 | 64 | result = base * result + digit; 65 | } 66 | 67 | if (negative) 68 | result = -result; 69 | 70 | if (endptr) 71 | *endptr = nptr; 72 | 73 | return result; 74 | } 75 | 76 | int isalpha(int c) { return ((unsigned)c | 32) - 'a' < 26; } 77 | 78 | int isprint(int c) { return (unsigned)c - 0x20 < 0x5f; } 79 | 80 | int isdigit(int c) { return (unsigned)c - '0' < 10; } 81 | 82 | int isxdigit(int c) { return isdigit(c) || ((unsigned)c | 32) - 'a' < 6; } 83 | 84 | int isspace(int c) { return c == ' ' || (unsigned)c - '\t' < 5; } 85 | 86 | char *strcpy(char *restrict dest, const char *restrict src) { 87 | const char *s = src; 88 | char *d = dest; 89 | while ((*d++ = *s++)) 90 | ; 91 | return dest; 92 | } 93 | 94 | char *strncpy(char *dest, const char *src, size_t n) { 95 | size_t i; 96 | 97 | for (i = 0; i < n && src[i] != '\0'; i++) 98 | dest[i] = src[i]; 99 | for (; i < n; i++) 100 | dest[i] = '\0'; 101 | 102 | return dest; 103 | } 104 | 105 | char *strcat(char *restrict dest, const char *restrict src) { 106 | strcpy(dest + strlen(dest), src); 107 | return dest; 108 | } 109 | 110 | char *strncat(char *restrict d, const char *restrict s, size_t n) { 111 | char *a = d; 112 | d += strlen(d); 113 | while (n && *s) 114 | n--, *d++ = *s++; 115 | *d++ = 0; 116 | return a; 117 | } 118 | 119 | char *strstr(const char *haystack, const char *needle) { 120 | if (!*needle) { 121 | return (char *)haystack; 122 | } 123 | 124 | for (; *haystack; haystack++) { 125 | // check that the first symbol of needle matches 126 | if (*haystack != *needle) { 127 | continue; 128 | } 129 | 130 | const char *a = haystack; 131 | const char *b = needle; 132 | 133 | while (true) { 134 | if (!*b) { 135 | return (char *)haystack; 136 | } 137 | if (*a++ != *b++) { 138 | break; 139 | } 140 | } 141 | } 142 | 143 | return NULL; 144 | } 145 | 146 | int toupper(int c) { 147 | if (islower(c)) 148 | return c & 0x5f; 149 | return c; 150 | } 151 | 152 | int tolower(int c) { 153 | if (isupper(c)) 154 | return c | 32; 155 | return c; 156 | } 157 | 158 | int isupper(int c) { return (unsigned)c - 'A' < 26; } 159 | 160 | int islower(int c) { return (unsigned)c - 'a' < 26; } 161 | -------------------------------------------------------------------------------- /arch/x86/microcode.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "microcode.h" 4 | 5 | #include "cpu.h" 6 | #include "intel-ucode.h" 7 | #include "shout.h" 8 | #include "x86.h" 9 | #include 10 | #include 11 | 12 | void microcode_load(void) { 13 | // Note that in case if Hyperthreading is enabled then microcode is shared between logical processors at the same core. 14 | // So we can either add tracking for updated cores OR simple let each logical core update the microcode. 15 | // Intel maual 8.7.11 states that core provides synchronization if multiple logical cores try to update the microcode. 16 | 17 | // find microcode 18 | uint32_t signature, unused; 19 | x86_cpuid(CPUID_FEATURES, &signature, &unused, &unused, &unused); 20 | 21 | if (bootstrap_cpu_id == current_cpu_id) 22 | IFVV printf("CPU signature is 0x%x\n", signature); 23 | 24 | uint64_t platform_id = x86_rdmsr(MSR_PLATFORM_ID); 25 | uint32_t processor_flags = 1 << ((platform_id >> 50) & 7); // extract flags [52:50] 26 | 27 | // iterate over all microcode files 28 | for (size_t j = 0; j < ARRAY_SIZE(BLOB_INTEL_UCODE); j++) { 29 | struct blob_record blob = BLOB_INTEL_UCODE[j]; 30 | 31 | // table 9-8 32 | struct PACKED ucode { 33 | uint32_t header_version; 34 | uint32_t update_revision; 35 | uint32_t date; 36 | uint32_t processor_signature; 37 | uint32_t checksum; 38 | uint32_t loader_revision; 39 | uint32_t processor_flags; 40 | uint32_t data_size; 41 | uint32_t total_size; 42 | uint8_t reserved1[12]; 43 | uint8_t data[0]; // actual ucode data 44 | } ALIGNED(16); 45 | BUILD_PANIC_IF(offsetof(struct ucode, data) != 48); 46 | 47 | const struct ucode *ucode = blob.data; 48 | PANIC_IF(!IS_ROUNDED((uintptr_t)ucode, 16)); // microcode data need to be aligned to 16 bytes 49 | 50 | // printf("ucode signature=%08x s=%08lx\n", ucode->processor_signature, signature); 51 | 52 | if (ucode->header_version != 1) 53 | continue; // we know how to handle ucode v1 only 54 | 55 | bool matches = false; 56 | if (ucode->processor_signature == signature && (ucode->processor_flags & processor_flags)) { 57 | matches = true; 58 | } else { 59 | // check if extended signature matches 60 | if (ucode->total_size > (ucode->data_size + 48)) { 61 | // XXX verify extended checksum 62 | 63 | uint32_t ext_signature_count = *(uint32_t *)((void *)ucode + ucode->data_size + 48); 64 | 65 | struct PACKED ext_signature { 66 | uint32_t processor_signature; 67 | uint32_t processor_flags; 68 | uint32_t checksum; 69 | }; 70 | 71 | struct ext_signature *ext = ((void *)ucode + ucode->data_size + 68); 72 | for (uint32_t i = 0; i < ext_signature_count; i++, ext++) { 73 | if (ext->processor_signature == signature && (ext->processor_flags & processor_flags)) { 74 | matches = true; 75 | break; 76 | } 77 | } 78 | } 79 | } 80 | 81 | if (matches) { 82 | if (bootstrap_cpu_id == current_cpu_id) 83 | IFVV printf("Updating processor microcode, signature 0x%x revision %d date %08x\n", signature, ucode->update_revision, 84 | ucode->date); 85 | 86 | // verify ucode checksum 87 | int words = 512; 88 | if (ucode->data_size) 89 | words = ucode->total_size / 4; 90 | uint32_t checksum = 0; 91 | uint32_t *ptr = (uint32_t *)ucode; 92 | for (int i = 0; i < words; i++, ptr++) 93 | checksum += *ptr; 94 | SHOUT_IF(checksum != 0, "ucode checksum does not match"); 95 | 96 | // Yay we've found ucode for our processor. Load microcode, see Intel manual section 9.11.1 97 | x86_wrmsr(MSR_BIOS_UPDT_TRIG, (uintptr_t)&ucode->data); 98 | 99 | // now verify that we loaded the microcode 100 | x86_wrmsr(MSR_BIOS_SIGN_ID, 0); 101 | x86_cpuid(CPUID_FEATURES, &unused, &unused, &unused, &unused); 102 | uint32_t new_revision = (x86_rdmsr(MSR_BIOS_SIGN_ID) >> 32); 103 | if (new_revision == ucode->update_revision) { 104 | IFVV printf("Microcode successfully updated\n"); 105 | break; 106 | } else { 107 | printf("Failed to load microcode, new revision (%d) does not match expected value (%d)\n", new_revision, 108 | ucode->update_revision); 109 | continue; 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/unit/allocator_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "acutest.h" 4 | #include "buddy.h" 5 | #include "slab.h" 6 | #include 7 | 8 | // Buddy allocator internal structures 9 | extern uint32_t buddy_root; 10 | extern size_t buddy_root_order; 11 | extern struct buddy *buddy_array; 12 | extern uint64_t *freebits; 13 | extern size_t freebits_size; 14 | 15 | #define BUDDY_PTR_ORDER(x) (((x)&0xff000000) >> 24) 16 | #define BUDDY_PTR_INDEX(ptr) (((ptr)&0xffffff)) 17 | 18 | void test_buddy(void) { 19 | const size_t malloc_size = 0x10000000; // 10 MiB 20 | void *area = aligned_alloc(malloc_size, malloc_size); 21 | TEST_CHECK(area != NULL); 22 | alloc_buddy_append((uintptr_t)area, (uintptr_t)area + malloc_size); 23 | 24 | TEST_CHECK((uintptr_t)area <= (uintptr_t)freebits && (uintptr_t)freebits < (uintptr_t)area + malloc_size); 25 | TEST_CHECK(freebits_size == 64); 26 | TEST_CHECK(freebits[0] == 0xffffffff00000000); 27 | 28 | // Part of the area been allocated to buddy_array thus maximum available order is one less that added order 29 | TEST_CHECK_(BUDDY_PTR_ORDER(buddy_root) == 27, "Expected buddy root order is 0x%x, got 0x%x", 27, BUDDY_PTR_ORDER(buddy_root)); 30 | 31 | void *ptr = alloc_buddy_allocate(27); 32 | TEST_CHECK(ptr != NULL); 33 | uintptr_t expected_ptr = (uintptr_t)area + malloc_size / 2; // second half of available area will be allocated for this buddy 34 | TEST_CHECK_(expected_ptr == (uintptr_t)ptr, "Expected address %p, got %p", expected_ptr, ptr); 35 | TEST_CHECK_(BUDDY_PTR_ORDER(buddy_root) == 26, "Expected buddy root order is 0x%x, got 0x%x", 26, BUDDY_PTR_ORDER(buddy_root)); 36 | 37 | // Second allocation should fail as we do not have enough space available 38 | TEST_CHECK(alloc_buddy_allocate(27) == NULL); 39 | 40 | // freeing buddy should increase available order back to 27 41 | alloc_buddy_free(ptr, 27); 42 | TEST_CHECK(BUDDY_PTR_ORDER(buddy_root) == 27); 43 | 44 | free(area); 45 | } 46 | 47 | void test_buddy_expanding_root_order(void) { 48 | const size_t malloc_size = 0x10000; 49 | void *area = aligned_alloc(malloc_size, malloc_size); 50 | TEST_CHECK(area != NULL); 51 | alloc_buddy_append((uintptr_t)area, (uintptr_t)area + malloc_size); 52 | TEST_CHECK_(BUDDY_PTR_ORDER(buddy_root) == 27, "Expected buddy free root order is 0x%x, got 0x%x", 27, BUDDY_PTR_ORDER(buddy_root)); 53 | TEST_CHECK_(buddy_root_order == 27, "Expected buddy root order is 0x%x, got 0x%x", 27, buddy_root_order); 54 | 55 | area = (uintptr_t)area << 1; 56 | alloc_buddy_append((uintptr_t)area, (uintptr_t)area + malloc_size); 57 | TEST_CHECK_(BUDDY_PTR_ORDER(buddy_root) == 27, "Expected buddy free root order is 0x%x, got 0x%x", 27, BUDDY_PTR_ORDER(buddy_root)); 58 | TEST_CHECK_(buddy_root_order == 28, "Expected buddy root order is 0x%x, got 0x%x", 28, buddy_root_order); 59 | 60 | area = (uintptr_t)area << 1; 61 | alloc_buddy_append((uintptr_t)area, (uintptr_t)area + malloc_size); 62 | TEST_CHECK_(BUDDY_PTR_ORDER(buddy_root) == 27, "Expected buddy free root order is 0x%x, got 0x%x", 27, BUDDY_PTR_ORDER(buddy_root)); 63 | TEST_CHECK_(buddy_root_order == 29, "Expected buddy root order is 0x%x, got 0x%x", 29, buddy_root_order); 64 | } 65 | 66 | // Slab allocator internal structures 67 | struct bucket { 68 | struct slab_head *slab_head; 69 | size_t total_capacity; // total number of elements that fit all the slabs 70 | size_t used; // number of elements used in this bucket 71 | size_t foreign_num; 72 | }; 73 | 74 | struct slab_head { 75 | void *slab; // area for objects 76 | uint32_t slab_order; // page order of the slab area 77 | uint32_t freeobj_size; // number of elements in freeobj 78 | struct slab_head *next; 79 | uint64_t freeobj[]; // bit field, bit per each object 80 | }; 81 | 82 | #define BUCKETS_NUM (32 * 1024 / 1024 + 116) 83 | extern __thread struct bucket buckets[BUCKETS_NUM]; 84 | 85 | void test_slab(void) { 86 | const size_t malloc_size = 0x10000000; // 10 MiB 87 | void *area = aligned_alloc(malloc_size, malloc_size); 88 | TEST_CHECK(area != NULL); 89 | alloc_buddy_append((uintptr_t)area, (uintptr_t)area + malloc_size); 90 | 91 | TEST_CHECK(buckets[1].used == 0); 92 | const size_t obj_size = 12; // 12-byte object 93 | const size_t obj_bucket = 1; // object size 12 belongs to bucket with index 1 94 | void *ptr = alloc_slab_allocate(obj_size); 95 | TEST_CHECK(ptr != NULL); 96 | TEST_CHECK(area <= ptr && ptr < area + malloc_size); 97 | 98 | TEST_CHECK(buckets[obj_bucket].used == 1); 99 | alloc_slab_free(ptr, obj_size); 100 | // Freeing the object should bring used count back to 0 101 | TEST_CHECK(buckets[obj_bucket].used == 0); 102 | 103 | free(area); 104 | } 105 | 106 | TEST_LIST = {{"buddy allocator", test_buddy}, 107 | {"buddy allocator expands root order", test_buddy_expanding_root_order}, 108 | {"slab allocator", test_slab}, 109 | {NULL, NULL}}; 110 | -------------------------------------------------------------------------------- /drv/virtio/virtio_mmio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Virtio platform device driver 5 | * 6 | * Copyright 2011, ARM Ltd. 7 | * 8 | * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007 9 | * 10 | * This header is BSD licensed so anyone can use the definitions to implement 11 | * compatible drivers/servers. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 3. Neither the name of IBM nor the names of its contributors 22 | * may be used to endorse or promote products derived from this software 23 | * without specific prior written permission. 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 | * SUCH DAMAGE. 35 | */ 36 | 37 | /* 38 | * Control registers 39 | */ 40 | 41 | /* Magic value ("virt" string) - Read Only */ 42 | #define VIRTIO_MMIO_MAGIC_VALUE 0x000 43 | 44 | /* Virtio device version - Read Only */ 45 | #define VIRTIO_MMIO_VERSION 0x004 46 | 47 | /* Virtio device ID - Read Only */ 48 | #define VIRTIO_MMIO_DEVICE_ID 0x008 49 | 50 | /* Virtio vendor ID - Read Only */ 51 | #define VIRTIO_MMIO_VENDOR_ID 0x00c 52 | 53 | /* Bitmask of the features supported by the device (host) 54 | * (32 bits per set) - Read Only */ 55 | #define VIRTIO_MMIO_DEVICE_FEATURES 0x010 56 | 57 | /* Device (host) features set selector - Write Only */ 58 | #define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 59 | 60 | /* Bitmask of features activated by the driver (guest) 61 | * (32 bits per set) - Write Only */ 62 | #define VIRTIO_MMIO_DRIVER_FEATURES 0x020 63 | 64 | /* Activated features set selector - Write Only */ 65 | #define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 66 | 67 | #ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */ 68 | 69 | /* Guest's memory page size in bytes - Write Only */ 70 | #define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 71 | 72 | #endif 73 | 74 | /* Queue selector - Write Only */ 75 | #define VIRTIO_MMIO_QUEUE_SEL 0x030 76 | 77 | /* Maximum size of the currently selected queue - Read Only */ 78 | #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 79 | 80 | /* Queue size for the currently selected queue - Write Only */ 81 | #define VIRTIO_MMIO_QUEUE_NUM 0x038 82 | 83 | #ifndef VIRTIO_MMIO_NO_LEGACY /* LEGACY DEVICES ONLY! */ 84 | 85 | /* Used Ring alignment for the currently selected queue - Write Only */ 86 | #define VIRTIO_MMIO_QUEUE_ALIGN 0x03c 87 | 88 | /* Guest's PFN for the currently selected queue - Read Write */ 89 | #define VIRTIO_MMIO_QUEUE_PFN 0x040 90 | 91 | #endif 92 | 93 | /* Ready bit for the currently selected queue - Read Write */ 94 | #define VIRTIO_MMIO_QUEUE_READY 0x044 95 | 96 | /* Queue notifier - Write Only */ 97 | #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 98 | 99 | /* Interrupt status - Read Only */ 100 | #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 101 | 102 | /* Interrupt acknowledge - Write Only */ 103 | #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 104 | 105 | /* Device status register - Read Write */ 106 | #define VIRTIO_MMIO_STATUS 0x070 107 | 108 | /* Selected queue's Descriptor Table address, 64 bits in two halves */ 109 | #define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 110 | #define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 111 | 112 | /* Selected queue's Available Ring address, 64 bits in two halves */ 113 | #define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 114 | #define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 115 | 116 | /* Selected queue's Used Ring address, 64 bits in two halves */ 117 | #define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 118 | #define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 119 | 120 | /* Configuration atomicity value */ 121 | #define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc 122 | 123 | /* The config space is defined by each driver as 124 | * the per-driver configuration space - Read Write */ 125 | #define VIRTIO_MMIO_CONFIG 0x100 126 | 127 | /* 128 | * Interrupt flags (re: interrupt status & acknowledge registers) 129 | */ 130 | 131 | #define VIRTIO_MMIO_INT_VRING (1 << 0) 132 | #define VIRTIO_MMIO_INT_CONFIG (1 << 1) 133 | -------------------------------------------------------------------------------- /include/shout.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #pragma once 4 | 5 | #include "compiler.h" 6 | #include "stdio.h" 7 | 8 | #define __SHOUT_0() \ 9 | do { \ 10 | printf("SHOUT at (%s:%d)\n", __FILE__, __LINE__); \ 11 | } while (0) 12 | #define __SHOUT_VARARG(msg, ...) \ 13 | do { \ 14 | printf("SHOUT at (%s:%d): " #msg "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ 15 | } while (0) 16 | #define SHOUT(...) \ 17 | GET_MACRO(_0, ##__VA_ARGS__, __SHOUT_VARARG, __SHOUT_VARARG, __SHOUT_VARARG, __SHOUT_VARARG, __SHOUT_VARARG, __SHOUT_VARARG, \ 18 | __SHOUT_0) \ 19 | (__VA_ARGS__) 20 | 21 | #define __SHOUT_IF_1(cond) \ 22 | do { \ 23 | if (cond) \ 24 | printf("SHOUT at (%s:%d): %s\n", __FILE__, __LINE__, #cond); \ 25 | } while (0) 26 | #define __SHOUT_IF_VARARG(cond, msg, ...) \ 27 | do { \ 28 | if (cond) \ 29 | printf("SHOUT at (%s:%d): %s " #msg "\n", __FILE__, __LINE__, #cond, ##__VA_ARGS__); \ 30 | } while (0) 31 | #define SHOUT_IF(...) \ 32 | GET_MACRO(_0, ##__VA_ARGS__, __SHOUT_IF_VARARG, __SHOUT_IF_VARARG, __SHOUT_IF_VARARG, __SHOUT_IF_VARARG, __SHOUT_IF_VARARG, \ 33 | __SHOUT_IF_1, void) \ 34 | (__VA_ARGS__) 35 | 36 | #define __PANIC_0() \ 37 | do { \ 38 | printf("PANIC at (%s:%d)\n", __FILE__, __LINE__); \ 39 | __builtin_trap(); \ 40 | } while (0) 41 | 42 | #define __PANIC_VARARG(msg, ...) \ 43 | do { \ 44 | printf("PANIC at (%s:%d): " #msg "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ 45 | __builtin_trap(); \ 46 | } while (0) 47 | #define PANIC(...) \ 48 | GET_MACRO(_0, ##__VA_ARGS__, __PANIC_VARARG, __PANIC_VARARG, __PANIC_VARARG, __PANIC_VARARG, __PANIC_VARARG, __PANIC_VARARG, \ 49 | __PANIC_0) \ 50 | (__VA_ARGS__) 51 | 52 | #define __PANIC_IF_1(cond) \ 53 | do { \ 54 | if (cond) { \ 55 | printf("PANIC at (%s:%d): %s\n", __FILE__, __LINE__, #cond); \ 56 | __builtin_trap(); \ 57 | } \ 58 | } while (0) 59 | #define __PANIC_IF_VARARG(cond, msg, ...) \ 60 | do { \ 61 | if (cond) { \ 62 | printf("PANIC at (%s:%d): %s " #msg "\n", __FILE__, __LINE__, #cond, ##__VA_ARGS__); \ 63 | __builtin_trap(); \ 64 | } \ 65 | } while (0) 66 | #define PANIC_IF(...) \ 67 | GET_MACRO(_0, ##__VA_ARGS__, __PANIC_IF_VARARG, __PANIC_IF_VARARG, __PANIC_IF_VARARG, __PANIC_IF_VARARG, __PANIC_IF_VARARG, \ 68 | __PANIC_IF_1, void) \ 69 | (__VA_ARGS__) 70 | 71 | #define __BUILD_PANIC_0 _Static_assert(false, "") 72 | #define __BUILD_PANIC_1(msg) _Static_assert(false, msg) 73 | #define __BUILD_PANIC_IF_1(cond) _Static_assert(!(cond), "") 74 | #define __BUILD_PANIC_IF_2(cond, msg) _Static_assert(!(cond), msg) 75 | 76 | #define BUILD_PANIC(...) GET_MACRO(_0, ##__VA_ARGS__, void, void, void, void, void, __BUILD_PANIC_1, __BUILD_PANIC_0)(__VA_ARGS__) 77 | #define BUILD_PANIC_IF(...) GET_MACRO(_0, ##__VA_ARGS__, void, void, void, void, __BUILD_PANIC_IF_2, __BUILD_PANIC_IF_1, void)(__VA_ARGS__) 78 | -------------------------------------------------------------------------------- /drv/net/intel_e1000e.regs.h: -------------------------------------------------------------------------------- 1 | // Generated by gen_regmap.rb ./drv/net/intel_e1000e.regs 2 | 3 | #pragma once 4 | 5 | // INTEL 82574 GbE NIC driver 6 | // http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf 7 | 8 | #define REG_CTRL 0x0 // Device control register, section 10.2.2.1 9 | #define CTRL_FULL_DUPLEX ((uint64_t)1 << 0) // 0 - Half duplex, 1 - Full duplex 10 | #define CTRL_MASTER_DISABLE ((uint64_t)1 << 2) 11 | #define CTRL_ASDE ((uint64_t)1 << 5) // Auto-Speed detection 12 | #define CTRL_SLU ((uint64_t)1 << 6) // Set link up 13 | #define CTRL_SPEED_MASK ((uint64_t)0x3 << 8) // Speed in Mb/s 14 | #define CTRL_SPEED_10 ((uint64_t)0 << 8) 15 | #define CTRL_SPEED_100 ((uint64_t)1 << 8) 16 | #define CTRL_SPEED_1000 ((uint64_t)2 << 8) 17 | #define CTRL_FRCSPD ((uint64_t)1 << 11) // Force speed 18 | #define CTRL_RST ((uint64_t)1 << 26) // Reset 19 | #define CTRL_PHY_RST ((uint64_t)1 << 31) 20 | 21 | #define REG_STATUS 0x8 // Status, R/O 22 | #define STATUS_FULL_DUPLEX ((uint64_t)1 << 0) 23 | #define STATUS_LINK_UP ((uint64_t)1 << 1) 24 | #define STATUS_TXOFF ((uint64_t)1 << 2) 25 | #define STATUS_SPEED_MASK ((uint64_t)0x3 << 6) 26 | #define STATUS_SPEED_10 ((uint64_t)0 << 6) 27 | #define STATUS_SPEED_100 ((uint64_t)1 << 6) 28 | #define STATUS_SPEED_1000 ((uint64_t)2 << 6) 29 | 30 | #define REG_MDIC 0x20 // MDI control (PHY access) 31 | #define MDIC_OP_MASK ((uint64_t)0x3 << 26) 32 | #define MDIC_OP_WRITE ((uint64_t)1 << 26) 33 | #define MDIC_OP_READ ((uint64_t)2 << 26) 34 | #define MDIC_R ((uint64_t)1 << 28) // Ready Bit 35 | #define MDIC_I ((uint64_t)1 << 29) // Interrupt Enable 36 | #define MDIC_E ((uint64_t)1 << 30) // Error 37 | 38 | #define INTR_LSC ((uint64_t)1 << 2) // Link Status Change 39 | #define INTR_RXT0 ((uint64_t)1 << 7) // Receiver Timer 40 | 41 | #define REG_ICR 0xc0 // Interrupt Cause Read 42 | 43 | #define REG_ICS 0xc8 // Interrupt Cause Set 44 | 45 | #define REG_IMS 0xd0 // Interrupt Mask Set 46 | 47 | #define REG_IMC 0xd8 // Interrupt Mask Clear 48 | 49 | #define REG_RXCSUM 0x5000 50 | 51 | #define REG_MTA 0x5200 // Multicast Table Array 52 | 53 | #define REG_RAL 0x5400 54 | 55 | #define REG_RAH 0x5404 56 | 57 | #define REG_RCTL 0x100 // Rx Control, section 10.2.5.1 58 | #define RCTL_RST ((uint64_t)1 << 0) // Rx Reset 59 | #define RCTL_EN ((uint64_t)1 << 1) // Rx Enable 60 | #define RCTL_SBP ((uint64_t)1 << 2) // Store Bad Packates 61 | #define RCTL_UPE ((uint64_t)1 << 3) // Unicast Promisc Enable 62 | #define RCTL_MPE ((uint64_t)1 << 4) // Multicast Promisc Enable 63 | #define RCTL_LPE ((uint64_t)1 << 5) // Long Packet Rx Enable (>1522 bytes) 64 | #define RCTL_LBM_MASK ((uint64_t)0x3 << 6) 65 | #define RCTL_LBM_NORMAL ((uint64_t)0 << 6) 66 | #define RCTL_LBM_MAC_LOOPBACK ((uint64_t)1 << 6) // Test mode 67 | #define RCTL_RDMTS_MASK ((uint64_t)0x3 << 8) // Receive Descriptor Minimum Threshold Size 68 | #define RCTL_MO_MASK ((uint64_t)0x3 << 12) // Multicast Offset 69 | #define RCTL_MO_36 ((uint64_t)0 << 12) // Multicast Filter Offset 36..47 70 | #define RCTL_MO_35 ((uint64_t)1 << 12) // Multicast Filter Offset 35..46 71 | #define RCTL_MO_34 ((uint64_t)2 << 12) // Multicast Filter Offset 34..45 72 | #define RCTL_MO_32 ((uint64_t)3 << 12) // Multicast Filter Offset 32..43 73 | #define RCTL_BAM ((uint64_t)1 << 15) // Rx Broadcast Packets Enable 74 | #define RCTL_BSIZE_MASK ((uint64_t)0x3 << 16) // Receive Buffer Size 75 | #define RCTL_BSIZE_2048 ((uint64_t)0 << 16) // Rx Buffer 2048 * (BSEX * 16) 76 | #define RCTL_BSIZE_1024 ((uint64_t)1 << 16) // Rx Buffer 1024 * (BSEX * 16) 77 | #define RCTL_BSIZE_512 ((uint64_t)2 << 16) // Rx Buffer 512 * (BSEX * 16) 78 | #define RCTL_BSIZE_256 ((uint64_t)3 << 16) // Rx Buffer 256 * (BSEX * 16) 79 | #define RCTL_DPF ((uint64_t)1 << 22) // Discard Pause Frames 80 | #define RCTL_PMCF ((uint64_t)1 << 23) // Pass MAC Control Frames 81 | #define RCTL_BSEX ((uint64_t)1 << 25) // Buffer Size Extension (x16) 82 | #define RCTL_SECRC ((uint64_t)1 << 26) // Strip CRC Field 83 | 84 | #define REG_RDBAL 0x2800 // Rx Descriptor Base Low 85 | 86 | #define REG_RDBAH 0x2804 // Rx Descriptor Base High 87 | 88 | #define REG_RDLEN 0x2808 // Rx Descriptor Length 89 | 90 | #define REG_RDH 0x2810 // Rx Descriptor Head 91 | 92 | #define REG_RDT 0x2818 // Rx Descriptor Tail 93 | 94 | #define REG_RXDCTL 0x2828 // Rx Descriptor Control 95 | #define RXDCTL_GRAN ((uint64_t)1 << 24) // Writeback granularity. 0 - Cache lines, 1 - Descriptors 96 | 97 | #define REG_TCTL 0x0400 // Tx Control 98 | #define TCTL_RST ((uint64_t)1 << 0) // Tx Reset 99 | #define TCTL_EN ((uint64_t)1 << 1) // Tx Enable 100 | #define TCTL_PSP ((uint64_t)1 << 3) // Pad Short Packets (to 64b) 101 | #define TCTL_SWXOFF ((uint64_t)1 << 22) // XOFF Tx (self-clearing) 102 | 103 | #define REG_TDBAL 0x3800 // Tx Descriptor Base Low 104 | 105 | #define REG_TDBAH 0x3804 // Tx Descriptor Base High 106 | 107 | #define REG_TDLEN 0x3808 // Tx Descriptor Length 108 | 109 | #define REG_TDH 0x3810 // Tx Descriptor Head 110 | 111 | #define REG_TDT 0x3818 // Tx Descriptor Tail 112 | 113 | #define REG_TXDCTL 0x3828 // Tx Descriptor Control 114 | #define TXDCTL_GRAN ((uint64_t)1 << 24) // Writeback granularity. 0 - Cache lines, 1 - Descriptors 115 | -------------------------------------------------------------------------------- /app/memcached/app.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "../../arch/x86/rtc.h" 4 | #include "blk.h" 5 | #include "init.h" 6 | #include "kalloc.h" 7 | #include "mem.h" 8 | #include "net/http_parser.h" 9 | #include "net/tcp.h" 10 | #include "stdio.h" 11 | #include "string.h" 12 | #include "timer.h" 13 | 14 | struct myserver_data { 15 | struct http_parser http_parser; 16 | struct http_parser_settings http_parser_settings; 17 | }; 18 | 19 | static int myserver_http_message_begin(UNUSED http_parser *parser) { return 0; } 20 | 21 | #define MYSERVER_HELLO_RESPONSE "Hello, world!" 22 | 23 | #define MYSERVER_HELLO_HTTP_RESPONSE \ 24 | "HTTP/1.1 200 OK\r\n" \ 25 | "Server: Unicycle Http Service\r\n" \ 26 | "Content-Length: 48\r\n" \ 27 | "Content-Type: text/html\r\n" \ 28 | "Connection: Closed\r\n" \ 29 | "\r\n" \ 30 | "" \ 31 | "" \ 32 | "

Hello, World!

" \ 33 | "" \ 34 | "" 35 | 36 | #define MYSERVER_FILE_NOT_FOUND \ 37 | "HTTP/1.1 404 Not Found\r\n" \ 38 | "Server: Unicycle Http Service\r\n" \ 39 | "Content-Length: 0\r\n" \ 40 | "Connection: Closed\r\n" \ 41 | "\r\n" 42 | 43 | static int myserver_http_on_url(http_parser *parser, const char *at, size_t length) { 44 | const char *response; 45 | size_t response_len; 46 | 47 | IFVV printf("myserver_http_on_url: %.*s\n", (int)length, at); 48 | if (!strncmp(at, "/", length)) { 49 | // If it the the main page request then return hello world 50 | response = MYSERVER_HELLO_HTTP_RESPONSE; 51 | response_len = sizeof(MYSERVER_HELLO_HTTP_RESPONSE) - 1; 52 | } else if (!strncmp(at, "/hello.txt", length)) { 53 | // If it the the main page request then return hello world 54 | response = MYSERVER_HELLO_RESPONSE; 55 | response_len = sizeof(MYSERVER_HELLO_RESPONSE) - 1; 56 | } else { 57 | // error 404 otherwise 58 | response = MYSERVER_FILE_NOT_FOUND; 59 | response_len = sizeof(MYSERVER_FILE_NOT_FOUND) - 1; 60 | } 61 | 62 | struct tcp_connection *conn = parser->data; 63 | 64 | // send HTTP response 65 | buffer_t *out = buffer_allocate(BUFFER_NET_SIZE, HDR_LEN_TCP); 66 | memcpy(out->pos, response, response_len); 67 | out->data_size += response_len; 68 | tcp_send(conn, out); 69 | return 0; 70 | } 71 | 72 | static int myserver_http_message_complete(UNUSED http_parser *parser) { return 0; } 73 | 74 | static bool myserver_accept(struct tcp_connection *conn) { 75 | IFVV printf("Accepting a TCP connection from " IPADDR_PRINT_FMT ":%u\n", IPADDR_PRINT_PARAMS(conn->remote_ip), conn->remote_port); 76 | 77 | struct myserver_data *data = kalloc(struct myserver_data); 78 | http_parser_init(&data->http_parser, HTTP_REQUEST); 79 | http_parser_settings_init(&data->http_parser_settings); 80 | 81 | data->http_parser_settings.on_message_begin = myserver_http_message_begin; 82 | data->http_parser_settings.on_url = myserver_http_on_url; 83 | data->http_parser_settings.on_message_complete = myserver_http_message_complete; 84 | data->http_parser.data = conn; 85 | 86 | conn->user_data = data; 87 | return true; 88 | } 89 | 90 | static void myserver_receive(struct tcp_connection *conn, buffer_t *buff) { 91 | struct myserver_data *data = conn->user_data; 92 | http_parser_execute(&data->http_parser, &data->http_parser_settings, buff->pos, buffer_data_available(buff)); 93 | buffer_free(buff); 94 | } 95 | 96 | static void myserver_finish(struct tcp_connection *conn) { 97 | IFVV printf("Finishing TCP connection from " IPADDR_PRINT_FMT ":%u\n", IPADDR_PRINT_PARAMS(conn->remote_ip), conn->remote_port); 98 | kfree((struct myserver_data *)conn->user_data); 99 | tcp_close(conn); 100 | } 101 | 102 | const struct tcp_ops myserver = { 103 | .accept = myserver_accept, 104 | .receive = myserver_receive, 105 | .finish = myserver_finish, 106 | }; 107 | 108 | #define MYSERVER_PORT 80 109 | 110 | static void myserver_bind(struct ip4if *ip4if) { tcp_bind(ip4if, MYSERVER_PORT, &myserver); } 111 | 112 | static void blk_op_complete(UNUSED struct blk_device *blk, void *data, enum blk_op_status status, UNUSED void *context) { 113 | printf("Blk write complete, status=%d\n", status); 114 | kfree_size(data, 512); 115 | } 116 | 117 | static void timer_fire(UNUSED void *data) { 118 | struct rtc_date date; 119 | rtc_read(&date); 120 | 121 | printf("Hello from timer #%ld. Today's date is %d-%d-%d %d-%d-%d\n", (uint64_t)data, date.year, date.month, date.day, date.hours, 122 | date.minutes, date.seconds); 123 | } 124 | 125 | void application_init(void) { 126 | ip4if_on_init(myserver_bind); 127 | timer_add(time_sec_from_now(8), timer_fire, (void *)1); 128 | timer_add(time_sec_from_now(2), timer_fire, (void *)2); 129 | timer_add(time_sec_from_now(2), timer_fire, (void *)3); 130 | 131 | struct blk_device *blk = blk_dev_get(0); 132 | 133 | if (blk) { 134 | void *data = kalloc_size(512); 135 | strcpy((char *)data, "Hello, world!"); 136 | blk_write(blk, data, 512, 1024, blk_op_complete, NULL); 137 | } else { 138 | printf("No block device found\n"); 139 | } 140 | 141 | printf("Memcached application has started\n"); 142 | } 143 | -------------------------------------------------------------------------------- /app/http-hello/app.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | #include "../../arch/x86/rtc.h" 4 | #include "blk.h" 5 | #include "init.h" 6 | #include "kalloc.h" 7 | #include "mem.h" 8 | #include "net/http_parser.h" 9 | #include "net/tcp.h" 10 | #include "stdio.h" 11 | #include "string.h" 12 | #include "timer.h" 13 | 14 | struct myserver_data { 15 | struct http_parser http_parser; 16 | struct http_parser_settings http_parser_settings; 17 | }; 18 | 19 | static int myserver_http_message_begin(UNUSED http_parser *parser) { return 0; } 20 | 21 | #define MYSERVER_HELLO_RESPONSE "Hello, world!" 22 | 23 | #define MYSERVER_HELLO_HTTP_RESPONSE \ 24 | "HTTP/1.1 200 OK\r\n" \ 25 | "Server: Unicycle Http Service\r\n" \ 26 | "Content-Length: 48\r\n" \ 27 | "Content-Type: text/html\r\n" \ 28 | "Connection: Closed\r\n" \ 29 | "\r\n" \ 30 | "" \ 31 | "" \ 32 | "

Hello, World!

" \ 33 | "" \ 34 | "" 35 | 36 | #define MYSERVER_FILE_NOT_FOUND \ 37 | "HTTP/1.1 404 Not Found\r\n" \ 38 | "Server: Unicycle Http Service\r\n" \ 39 | "Content-Length: 0\r\n" \ 40 | "Connection: Closed\r\n" \ 41 | "\r\n" 42 | 43 | static int myserver_http_on_url(http_parser *parser, const char *at, size_t length) { 44 | const char *response; 45 | size_t response_len; 46 | 47 | IFVV printf("myserver_http_on_url: %.*s\n", (int)length, at); 48 | if (!strncmp(at, "/", length)) { 49 | // If it the the main page request then return hello world 50 | response = MYSERVER_HELLO_HTTP_RESPONSE; 51 | response_len = sizeof(MYSERVER_HELLO_HTTP_RESPONSE) - 1; 52 | } else if (!strncmp(at, "/hello.txt", length)) { 53 | // If it the the main page request then return hello world 54 | response = MYSERVER_HELLO_RESPONSE; 55 | response_len = sizeof(MYSERVER_HELLO_RESPONSE) - 1; 56 | } else { 57 | // error 404 otherwise 58 | response = MYSERVER_FILE_NOT_FOUND; 59 | response_len = sizeof(MYSERVER_FILE_NOT_FOUND) - 1; 60 | } 61 | 62 | struct tcp_connection *conn = parser->data; 63 | 64 | // send HTTP response 65 | buffer_t *out = buffer_allocate(BUFFER_NET_SIZE, HDR_LEN_TCP); 66 | memcpy(out->pos, response, response_len); 67 | out->data_size += response_len; 68 | tcp_send(conn, out); 69 | return 0; 70 | } 71 | 72 | static int myserver_http_message_complete(UNUSED http_parser *parser) { return 0; } 73 | 74 | static bool myserver_accept(struct tcp_connection *conn) { 75 | IFVV printf("Accepting a TCP connection from " IPADDR_PRINT_FMT ":%u\n", IPADDR_PRINT_PARAMS(conn->remote_ip), conn->remote_port); 76 | 77 | struct myserver_data *data = kalloc(struct myserver_data); 78 | http_parser_init(&data->http_parser, HTTP_REQUEST); 79 | http_parser_settings_init(&data->http_parser_settings); 80 | 81 | data->http_parser_settings.on_message_begin = myserver_http_message_begin; 82 | data->http_parser_settings.on_url = myserver_http_on_url; 83 | data->http_parser_settings.on_message_complete = myserver_http_message_complete; 84 | data->http_parser.data = conn; 85 | 86 | conn->user_data = data; 87 | return true; 88 | } 89 | 90 | static void myserver_receive(struct tcp_connection *conn, buffer_t *buff) { 91 | struct myserver_data *data = conn->user_data; 92 | http_parser_execute(&data->http_parser, &data->http_parser_settings, buff->pos, buffer_data_available(buff)); 93 | buffer_free(buff); 94 | } 95 | 96 | static void myserver_finish(struct tcp_connection *conn) { 97 | IFVV printf("Finishing TCP connection from " IPADDR_PRINT_FMT ":%u\n", IPADDR_PRINT_PARAMS(conn->remote_ip), conn->remote_port); 98 | kfree((struct myserver_data *)conn->user_data); 99 | tcp_close(conn); 100 | } 101 | 102 | const struct tcp_ops myserver = { 103 | .accept = myserver_accept, 104 | .receive = myserver_receive, 105 | .finish = myserver_finish, 106 | }; 107 | 108 | #define MYSERVER_PORT 80 109 | 110 | static void myserver_bind(struct ip4if *ip4if) { tcp_bind(ip4if, MYSERVER_PORT, &myserver); } 111 | 112 | static void blk_op_complete(UNUSED struct blk_device *blk, void *data, enum blk_op_status status, UNUSED void *context) { 113 | printf("Blk write complete, status=%d\n", status); 114 | kfree_size(data, 512); 115 | } 116 | 117 | size_t counter = 0; 118 | 119 | static void timer_fire(UNUSED void *data) { 120 | struct rtc_date date = {}; 121 | rtc_read(&date); 122 | 123 | printf("Hello from timer #%ld. Today's date is %d-%d-%d %d-%d-%d\n", (uint64_t)data, date.year, date.month, date.day, date.hours, 124 | date.minutes, date.seconds); 125 | 126 | struct blk_device *blk = blk_dev_get(0); 127 | 128 | if (blk) { 129 | void *data = kalloc_size(512); 130 | strcpy((char *)data, "Hello, test world!"); 131 | blk_write(blk, data, 512, counter++, blk_op_complete, NULL); 132 | } else { 133 | printf("No block device found\n"); 134 | } 135 | } 136 | 137 | void application_init(void) { 138 | ip4if_on_init(myserver_bind); 139 | timer_add(time_sec_from_now(1), timer_fire, (void *)1); 140 | timer_add(time_sec_from_now(2), timer_fire, (void *)2); 141 | timer_add(time_sec_from_now(3), timer_fire, (void *)3); 142 | 143 | struct blk_device *blk = blk_dev_get(0); 144 | 145 | if (blk) { 146 | void *data = kalloc_size(512); 147 | strcpy((char *)data, "Hello, NCQ world!"); 148 | blk_write(blk, data, 512, counter++, blk_op_complete, NULL); 149 | } else { 150 | printf("No block device found\n"); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /scripts/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require "optparse" 4 | require "fileutils" 5 | require "etc" 6 | require "digest" 7 | 8 | DISKFILE = "disk.raw" 9 | 10 | def current_os 11 | case RUBY_PLATFORM 12 | when /linux/i 13 | :linux 14 | when /darwin/i 15 | :macosx 16 | else 17 | :unknown 18 | end 19 | end 20 | 21 | # returns the first argument that is enabled in the config file, nil otherwise 22 | def config_enabled(*options) 23 | config = File.readlines(".config") 24 | for option in options 25 | return option if config.any? { |l| l == "#{option}=y\n" } 26 | end 27 | 28 | return nil 29 | end 30 | 31 | options = {} 32 | OptionParser.new do |opts| 33 | opts.banner = "Runs built Unicycle application with QEMU emulator.\nUsage: run [options]" 34 | 35 | opts.on("-bNAME", "--bootloader=NAME", "Bootloader type") do |b| 36 | # currently supported 'uniboot' and 'uefi' 37 | options[:bootloader] = b 38 | end 39 | 40 | opts.on("-dNAME", "--disk=NAME", "Disk interface") do |n| 41 | options[:disk] = n 42 | end 43 | 44 | opts.on("-nNAME", "--network=NAME", "Network interface") do |n| 45 | options[:network] = n 46 | end 47 | 48 | opts.on("-hHOSTNAME", "--host=HOSTNAME", "Network host name") do |h| 49 | options[:host] = h 50 | end 51 | 52 | opts.on("", "--tap", "Use tup/tap network driver") do 53 | options[:network_tap] = true 54 | end 55 | 56 | opts.on("-v", "--verbose", "Verbose script output") do |v| 57 | options[:verbose] = v 58 | end 59 | end.parse! 60 | 61 | options[:disk] ||= "virtio" 62 | options[:network] ||= "e1000" 63 | options[:host] ||= "unicycle" 64 | options[:bootloader] ||= "uniboot" 65 | 66 | # qemu args 67 | args = [] 68 | 69 | cpu = "" 70 | case current_os 71 | when :linux 72 | cpu = "-enable-kvm -cpu host" 73 | when :macosx 74 | cpu = "-cpu max" 75 | else 76 | raise "Unknown OS" 77 | end 78 | 79 | args << cpu + ",+x2apic,+pdpe1gb" 80 | args << "-smp 4" 81 | args << "-serial stdio" 82 | args << "-gdb tcp::1234" 83 | args << "--no-reboot" 84 | args << "-display none" 85 | 86 | unless File.exists?(DISKFILE) 87 | `qemu-img create -f raw #{DISKFILE} 100M` 88 | end 89 | 90 | if options[:disk] != "none" 91 | disk = case options[:disk] 92 | when "virtio" 93 | "-device virtio-blk-pci,disable-legacy=on,drive=drive0" 94 | when "ahci" 95 | "-device ich9-ahci,id=ahci -device ide-drive,drive=drive0,bus=ahci.0" 96 | else 97 | raise "Expect -d (disk interface) one of the following values: none, virtio, ahci" 98 | end 99 | args << "-drive if=none,id=drive0,format=raw,file=#{DISKFILE} #{disk}" 100 | end 101 | 102 | user = Etc.getlogin 103 | 104 | if options[:network] == "none" 105 | args << "-net none" 106 | else 107 | mac = "" 108 | if options[:network_tap] 109 | sha1 = Digest::SHA1.new 110 | sha1 << options[:host] 111 | h = sha1.hexdigest 112 | mac = ",mac=52:54:00:#{h[0..1]}:#{h[2..3]}:#{h[4..5]}" 113 | end 114 | 115 | case options[:network] 116 | when "virtio" 117 | args << "-device virtio-net-pci,disable-legacy=on,netdev=net0#{mac}" 118 | when "e1000" 119 | args << "-device e1000,netdev=net0#{mac}" 120 | else 121 | raise "Expect -n (network interface) one of the following values: none, virtio, e1000" 122 | end 123 | 124 | args << "-object filter-dump,id=id,netdev=net0,file=out/unicycle.pcap" 125 | 126 | if options[:network_tap] 127 | if current_os == :macosx 128 | dev = "/dev/#{options[:host]}" 129 | stat = File.stat(dev) 130 | raise "To use qemu with networking on macOS, install the tun/tap driver:\nhttp://tuntaposx.sourceforge.net/download.xhtml" unless stat.chardev? 131 | raise "For networking #{dev} must be owned by #{user}. Please run:\n sudo chown #{user} #{dev}" unless stat.writable? 132 | args << "-netdev type=tap,ifname=#{options[:host]},script=no,downscript=no,id=net0" 133 | elsif current_os == :linux 134 | check = `tunctl -b -u #{user} -t #{options[:host]} 2>/dev/null` 135 | if check.strip != options[:host] 136 | puts "To use qemu with networking on Linux, configure tun/tap:" 137 | puts "Install 'uml-utilities' at Debian OR 'uml_utilities' at Arch" unless File.exists?("/usr/bin/tunctl") 138 | puts " sudo tunctl -u #{user} -t #{options[:host]}" 139 | puts " sudo ifconfig #{options[:host]} up" 140 | exit 1 141 | end 142 | args << " -netdev type=tap,ifname=#{options[:host]},script=no,downscript=no,id=net0" 143 | else 144 | raise "Unknown operation system" 145 | end 146 | else 147 | # Note we enable port forwarding 80->5555 148 | args << "-netdev user,id=net0,hostname=#{options[:host]},hostfwd=tcp::5555-:80" 149 | end 150 | end 151 | 152 | case options[:bootloader] 153 | when "uefi" 154 | # different distros have different path to uefi fw, check all possible locations 155 | fws = ["/usr/share/OVMF/OVMF_CODE.fd", "/usr/share/ovmf/x64/OVMF_CODE.fd"] 156 | uefi_fw = fws.find { |f| File.exists?(f) } 157 | raise "Cannot find uefi firmware in paths: #{fws}" unless uefi_fw 158 | 159 | # check bootloader is up-to-date 160 | if File.exists?("../bootloader/bootloader.efi") 161 | FileUtils.mkdir_p("out/EFI/BOOT/") 162 | FileUtils.cp("../bootloader/bootloader.efi", "out/EFI/BOOT/BOOTX64.EFI") 163 | else 164 | raise "Cannot find unicycle bootloader binary" unless File.exists?("out/EFI/BOOT/BOOTX64.EFI") 165 | end 166 | 167 | args << "--bios #{uefi_fw} -hda fat:rw:out" 168 | when "uniboot" 169 | # this boot protocol requires patches QEMU 170 | args << "-kernel out/app.elf -uniboot" 171 | else 172 | raise "Unknown bootloader option #{bootloader}" 173 | end 174 | 175 | cmd = "~/sources/qemu/build/x86_64-softmmu/qemu-system-x86_64 " + args.join(" ") 176 | puts cmd if options[:verbose] 177 | 178 | system(cmd) 179 | --------------------------------------------------------------------------------