From 9d5febdbb34f1ee1d0c42084bccabebf084e3be5 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Tue, 9 Jun 2020 11:38:48 +0100 Subject: [PATCH 01/16] Set up build system --- Makefile | 58 ++++++++++++++++++++++++++++++++++++++++++ tools/ld/raspi2.ld | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 Makefile create mode 100644 tools/ld/raspi2.ld diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d731ebd --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +MACHINE = raspi2 + +KERNEL_ELF = bin/porpoise.elf +KERNEL_BINARY = bin/sdcard/boot/kernel8.img + +PREFIX := aarch64-linux-gnu + +CXX = $(PREFIX)-g++ +CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE)\ + -ffreestanding -nostdlib -nodefaultlibs -nostartfiles -fno-exceptions -fno-rtti -fno-stack-protector \ + -O2\ + -std=c++17 -Wall -Wextra + +LDFLAGS = -Ttools/ld/$(MACHINE).ld + +LIBRARIES = -lgcc + +OBJCOPY = $(PREFIX)-objcopy + +EMU = qemu-system-aarch64 +EMUFLAGS = -M raspi2,usb=on -smp 4 -m 512 -display sdl -sdl -d guest_errors + +OBJECTS = $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/shared \( -name '*.cpp' -o -name '*.S' \))))\ + $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/$(MACHINE) \( -name '*.cpp' -o -name '*.S' \)))) + +obj/%.o: src/%.cpp + @echo "Compiling `basename $^`" + @mkdir -p `dirname $@` + @$(CXX) $(CXFLAGS) -c -o $@ $^ + +obj/%.o: src/%.S + @echo "Assembling `basename $^`" + @mkdir -p `dirname $@` + @$(CXX) $(CXFLAGS) -D__ASM_SOURCE__ -c -o $@ $^ + +all: $(KERNEL_BINARY) + +clean: + @rm -rf bin/* obj/* + +$(KERNEL_BINARY): $(KERNEL_ELF) + @echo "Converting to binary `basename $@`" + @mkdir -p `dirname $@` + @$(OBJCOPY) -O binary $< $@ + +$(KERNEL_ELF): $(OBJECTS) + @echo "Linking `basename $@`" + @mkdir -p `dirname $@` + @$(CXX) $(CXFLAGS) $(LDFLAGS) -o $@ $^ $(LIBRARIES) + +run-qemu: $(KERNEL_ELF) + @$(EMU) $(EMUFLAGS) -serial stdio -kernel $(KERNEL_ELF) + +debug-qemu: $(KERNEL_ELF) + @$(EMU) $(EMUFLAGS) -S -s & + gdb $(KERNEL_BINARY) -ex "target remote :1234" -kernel $(KERNEL_ELF) + +.PHONY: run-qemu debug-qemu diff --git a/tools/ld/raspi2.ld b/tools/ld/raspi2.ld new file mode 100644 index 0000000..41fc9f1 --- /dev/null +++ b/tools/ld/raspi2.ld @@ -0,0 +1,63 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x80000; + __kernel_start__ = .; + .text BLOCK(4K) : ALIGN(4K) { + __code_start__ = .; + *(.text.boot) + *(.text*) + *(.gnu.linkonce.t*) + __code_end__ = .; + } + .init_array : + { + crti.o(.init_array) + KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*))) + KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array)) + crtn.o(.init_array) + } + .fini_array : + { + crti.o(.fini_array) + KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*))) + KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array)) + crtn.o(.fini_array) + } + .data BLOCK(4K) : ALIGN(4K) { + __data_start__ = .; + *(.data) + *(.ctors*) + *(.dtors*) + *(.gnu.linkonce.d*) + *(.jcr) + __data_end__ = .; + } + .rodata BLOCK(4K) : ALIGN(4K) { + __rodata_start__ = .; + *(.rodata*) + *(.gnu.linkonce.r*) + __rodata_end__ = .; + } + .eh_frame BLOCK(4K) : ALIGN(4K) { + start_eh_frame = .; + __eh_frame_start__ = .; + *(.eh_frame) + *(.gnu.linkonce.e*) + QUAD(0) + __eh_frame_end__ = .; + } + .bss BLOCK(4K) : ALIGN(4K) { + __bss_start__ = .; + *(.bss) + *(.gnu.linkonce.b*) + *(COMMON) + *(.common) + *(.gnu.linkonce.c*) + __bss_end__ = .; + } + __kernel_end__ = .; + /DISCARD/ : { + *(.comment) + } +} From 123d4a4898929c6ad7d63b8760d7fa45c8b9bd9d Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Tue, 9 Jun 2020 15:26:35 +0100 Subject: [PATCH 02/16] Add boot code & logging to serial --- .gitignore | 4 +- Makefile | 40 ++- include/raspi/porpoise/boot-platform.hpp | 16 ++ include/raspi/porpoise/io/mailbox.hpp | 8 + include/raspi/porpoise/io/mmio.hpp | 53 ++++ include/shared/porpoise.hpp | 5 + include/shared/porpoise/abort.hpp | 15 ++ include/shared/porpoise/boot.hpp | 15 ++ include/shared/porpoise/io/logging.hpp | 4 + include/shared/porpoise/io/logging/log.hpp | 118 ++++++++ .../porpoise/io/logging/manipulators.hpp | 96 +++++++ .../porpoise/io/logging/sinks/serial-sink.hpp | 18 ++ include/shared/porpoise/io/uart.hpp | 24 ++ include/shared/porpoise/sync/atomic.hpp | 175 ++++++++++++ include/shared/porpoise/sync/basic-lock.hpp | 12 + include/shared/porpoise/sync/lock-guard.hpp | 19 ++ include/shared/porpoise/sync/spinlock.hpp | 25 ++ include/shared/porpoise/time/config.hpp | 18 ++ include/shared/porpoise/time/counters.hpp | 15 ++ include/shared/porpoise/time/delay.hpp | 16 ++ src/raspi/abort.cpp | 10 + src/raspi/boot/start-cpu.cpp | 34 +++ src/raspi/boot/start.S | 16 ++ src/raspi/io/mmio.cpp | 15 ++ src/raspi/io/uart.cpp | 99 +++++++ src/raspi/time/config.cpp | 17 ++ src/raspi/time/counters.cpp | 24 ++ src/raspi/time/delay.cpp | 23 ++ src/shared/abi/cxa-guard.cpp | 19 ++ src/shared/abi/cxa-pure-virtual.cpp | 6 + src/shared/boot/boot-kernel.cpp | 61 +++++ src/shared/boot/cpu-main.cpp | 11 + src/shared/io/logging/log.cpp | 252 ++++++++++++++++++ src/shared/io/logging/manipulators.cpp | 105 ++++++++ src/shared/io/logging/sinks/serial-sink.cpp | 34 +++ tools/ld/raspi3.ld | 63 +++++ 36 files changed, 1470 insertions(+), 15 deletions(-) create mode 100644 include/raspi/porpoise/boot-platform.hpp create mode 100644 include/raspi/porpoise/io/mailbox.hpp create mode 100644 include/raspi/porpoise/io/mmio.hpp create mode 100644 include/shared/porpoise.hpp create mode 100644 include/shared/porpoise/abort.hpp create mode 100644 include/shared/porpoise/boot.hpp create mode 100644 include/shared/porpoise/io/logging.hpp create mode 100644 include/shared/porpoise/io/logging/log.hpp create mode 100644 include/shared/porpoise/io/logging/manipulators.hpp create mode 100644 include/shared/porpoise/io/logging/sinks/serial-sink.hpp create mode 100644 include/shared/porpoise/io/uart.hpp create mode 100644 include/shared/porpoise/sync/atomic.hpp create mode 100644 include/shared/porpoise/sync/basic-lock.hpp create mode 100644 include/shared/porpoise/sync/lock-guard.hpp create mode 100644 include/shared/porpoise/sync/spinlock.hpp create mode 100644 include/shared/porpoise/time/config.hpp create mode 100644 include/shared/porpoise/time/counters.hpp create mode 100644 include/shared/porpoise/time/delay.hpp create mode 100644 src/raspi/abort.cpp create mode 100644 src/raspi/boot/start-cpu.cpp create mode 100644 src/raspi/boot/start.S create mode 100644 src/raspi/io/mmio.cpp create mode 100644 src/raspi/io/uart.cpp create mode 100644 src/raspi/time/config.cpp create mode 100644 src/raspi/time/counters.cpp create mode 100644 src/raspi/time/delay.cpp create mode 100644 src/shared/abi/cxa-guard.cpp create mode 100644 src/shared/abi/cxa-pure-virtual.cpp create mode 100644 src/shared/boot/boot-kernel.cpp create mode 100644 src/shared/boot/cpu-main.cpp create mode 100644 src/shared/io/logging/log.cpp create mode 100644 src/shared/io/logging/manipulators.cpp create mode 100644 src/shared/io/logging/sinks/serial-sink.cpp create mode 100644 tools/ld/raspi3.ld diff --git a/.gitignore b/.gitignore index 3afdc66..7b99e79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ bin/ obj/ -.vscode/ \ No newline at end of file +.vscode/ +tags +ctags diff --git a/Makefile b/Makefile index d731ebd..9ae83da 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -MACHINE = raspi2 +MACHINE_BASE = raspi +MACHINE_VERSION = 3 +MACHINE = $(MACHINE_BASE)$(MACHINE_VERSION) KERNEL_ELF = bin/porpoise.elf KERNEL_BINARY = bin/sdcard/boot/kernel8.img @@ -6,22 +8,29 @@ KERNEL_BINARY = bin/sdcard/boot/kernel8.img PREFIX := aarch64-linux-gnu CXX = $(PREFIX)-g++ -CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE)\ - -ffreestanding -nostdlib -nodefaultlibs -nostartfiles -fno-exceptions -fno-rtti -fno-stack-protector \ - -O2\ +CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE_BASE)\ + -D__$(MACHINE_BASE)__=$(MACHINE_VERSION)\ + -ffreestanding -nostdlib -fno-exceptions -fno-rtti -fno-stack-protector\ -std=c++17 -Wall -Wextra - LDFLAGS = -Ttools/ld/$(MACHINE).ld - LIBRARIES = -lgcc - OBJCOPY = $(PREFIX)-objcopy EMU = qemu-system-aarch64 -EMUFLAGS = -M raspi2,usb=on -smp 4 -m 512 -display sdl -sdl -d guest_errors +EMUFLAGS = -M $(MACHINE),usb=on -smp 4 -m 512 -d guest_errors -display none OBJECTS = $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/shared \( -name '*.cpp' -o -name '*.S' \))))\ - $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/$(MACHINE) \( -name '*.cpp' -o -name '*.S' \)))) + $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/$(MACHINE_BASE) \( -name '*.cpp' -o -name '*.S' \)))) + +ifeq ($(DEBUG),) +DEBUG=1 +endif + +ifeq ($(DEBUG),1) +CXFLAGS += -ggdb -O0 +else +CXFLAGS += -g0 -O3 +endif obj/%.o: src/%.cpp @echo "Compiling `basename $^`" @@ -36,7 +45,7 @@ obj/%.o: src/%.S all: $(KERNEL_BINARY) clean: - @rm -rf bin/* obj/* + @rm -rf obj/* $(KERNEL_BINARY) $(KERNEL_ELF) $(KERNEL_BINARY): $(KERNEL_ELF) @echo "Converting to binary `basename $@`" @@ -49,10 +58,13 @@ $(KERNEL_ELF): $(OBJECTS) @$(CXX) $(CXFLAGS) $(LDFLAGS) -o $@ $^ $(LIBRARIES) run-qemu: $(KERNEL_ELF) - @$(EMU) $(EMUFLAGS) -serial stdio -kernel $(KERNEL_ELF) + $(EMU) $(EMUFLAGS) -serial stdio -kernel $(KERNEL_ELF) + +mon-qemu: $(KERNEL_ELF) + $(EMU) $(EMUFLAGS) -monitor stdio -kernel $(KERNEL_ELF) debug-qemu: $(KERNEL_ELF) - @$(EMU) $(EMUFLAGS) -S -s & - gdb $(KERNEL_BINARY) -ex "target remote :1234" -kernel $(KERNEL_ELF) + $(EMU) $(EMUFLAGS) -kernel $(KERNEL_ELF) -S -s & + gdb-multiarch $(KERNEL_BINARY) -ex "target remote :1234" -ex "set architecture aarch64" -.PHONY: run-qemu debug-qemu +.PHONY: run-qemu debug-qemu $(KERNEL_BINARY) $(KERNEL_ELF) diff --git a/include/raspi/porpoise/boot-platform.hpp b/include/raspi/porpoise/boot-platform.hpp new file mode 100644 index 0000000..90e368a --- /dev/null +++ b/include/raspi/porpoise/boot-platform.hpp @@ -0,0 +1,16 @@ +#pragma once + +#if __raspi__ >= 3 +# define BOOT_ORIGIN 0x80000 +#else +# define BOOT_ORIGIN 0x8000 +#endif + +#ifndef __ASM_SOURCE__ +#include + +namespace porpoise { namespace boot { + extern "C" void boot_kernel(uint64_t dtb); +}} // porpoise::boot + +#endif // ! __ASM_SOURCE__ diff --git a/include/raspi/porpoise/io/mailbox.hpp b/include/raspi/porpoise/io/mailbox.hpp new file mode 100644 index 0000000..dbd634c --- /dev/null +++ b/include/raspi/porpoise/io/mailbox.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace porpoise { namespace io { + struct mailbox { + }; +}} // porpoise::io diff --git a/include/raspi/porpoise/io/mmio.hpp b/include/raspi/porpoise/io/mmio.hpp new file mode 100644 index 0000000..5510556 --- /dev/null +++ b/include/raspi/porpoise/io/mmio.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include + +namespace porpoise { namespace io { + struct mmio + { +#if __raspi__ == 0 || __raspi__ == 1 + static constexpr uintptr_t MMIO_BASE = 0x20000000; +#elif __raspi__ == 2 || __raspi__ == 3 + static constexpr uintptr_t MMIO_BASE = 0x3F000000; +#elif __raspi__ == 4 + static constexpr uintptr_t MMIO_BASE = 0xFE000000; +#else +#error Unknown/undefined Raspberry Pi version +#endif + static constexpr uintptr_t GPIO_BASE = MMIO_BASE + 0x200000; + static constexpr uintptr_t UART0_BASE = GPIO_BASE + 0x1000; + static constexpr uintptr_t MAILBOX_BASE = MMIO_BASE + 0xB880; + enum class reg : uintptr_t + { + GPIO_UP_DOWN = GPIO_BASE + 0x94, + GPIO_CLOCK0 = GPIO_BASE + 0x98, + UART0_DR = UART0_BASE + 0x00, + UART0_RSRECR = UART0_BASE + 0x04, + UART0_FR = UART0_BASE + 0x18, + UART0_ILPR = UART0_BASE + 0x20, + UART0_IBRD = UART0_BASE + 0x24, + UART0_FBRD = UART0_BASE + 0x28, + UART0_LCRH = UART0_BASE + 0x2C, + UART0_CR = UART0_BASE + 0x30, + UART0_IFLS = UART0_BASE + 0x34, + UART0_IMSC = UART0_BASE + 0x38, + UART0_RIS = UART0_BASE + 0x3C, + UART0_MIS = UART0_BASE + 0x40, + UART0_ICR = UART0_BASE + 0x44, + UART0_DMACR = UART0_BASE + 0x48, + UART0_ITCR = UART0_BASE + 0x80, + UART0_ITIP = UART0_BASE + 0x84, + UART0_ITOP = UART0_BASE + 0x88, + UART0_TDR = UART0_BASE + 0x8C, + MAILBOX_READ = MAILBOX_BASE + 0x00, + MAILBOX_STATUS = MAILBOX_BASE + 0x18, + MAILBOX_WRITE = MAILBOX_BASE + 0x20 + }; + + static void put(reg r, uint32_t value); + + static uint32_t get(reg r); + private: + mmio() = delete; + }; +}} // porpose::io diff --git a/include/shared/porpoise.hpp b/include/shared/porpoise.hpp new file mode 100644 index 0000000..1e1bc78 --- /dev/null +++ b/include/shared/porpoise.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +#define PORPOISE_UNUSED(X) ((void)(X)) diff --git a/include/shared/porpoise/abort.hpp b/include/shared/porpoise/abort.hpp new file mode 100644 index 0000000..f9fa2f2 --- /dev/null +++ b/include/shared/porpoise/abort.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define PORPOISE_ABORT(...) \ +do \ +{ \ + ::porpoise::io::logging::log::error() << __VA_ARGS__;\ + ::porpoise::abort(); \ +} while (0) + +namespace porpoise +{ + extern "C" void abort() __attribute__((noreturn)); +} diff --git a/include/shared/porpoise/boot.hpp b/include/shared/porpoise/boot.hpp new file mode 100644 index 0000000..52d9d99 --- /dev/null +++ b/include/shared/porpoise/boot.hpp @@ -0,0 +1,15 @@ +#pragma once + + +#include + +#ifndef __ASM_SOURCE__ +#include + +namespace porpoise { namespace boot { + void start_cpu(int id, void(* fn)()); + + void cpu_main(int id); +}} // porpoise::boot + +#endif // ! __ASM_SOURCE__ diff --git a/include/shared/porpoise/io/logging.hpp b/include/shared/porpoise/io/logging.hpp new file mode 100644 index 0000000..612a300 --- /dev/null +++ b/include/shared/porpoise/io/logging.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/include/shared/porpoise/io/logging/log.hpp b/include/shared/porpoise/io/logging/log.hpp new file mode 100644 index 0000000..1756440 --- /dev/null +++ b/include/shared/porpoise/io/logging/log.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include + +#include + +#define PORPOISE_LOG_TRACE(...) do {::porpoise::io::logging::log::trace() << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_DEBUG(...) do {::porpoise::io::logging::log::debug() << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_INFO(...) do {::porpoise::io::logging::log::info() << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_WARN(...) do {::porpoise::io::logging::log::warn() << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_ERROR(...) do {::porpoise::io::logging::log::error() << __VA_ARGS__;} while (0) + +namespace porpoise { namespace io { namespace logging { + /// Log level. + enum class log_level : uint8_t + { + trace, + debug, + info, + warn, + error + }; + + /// Log sink, e.g. serial device, a file or the screen. + struct log_sink + { + /// Emit a string verbatim if the log level is greater than or equal to the sink's log-level. + virtual void emit(log_level level, const char* event) = 0; + + /// Emit a character verbatim if the log level is greater than or equal to the sink's log-level. + virtual void emit(log_level level, char c) = 0; + }; + + struct log; + + struct manipulator + { + virtual void operator()(log& log_) = 0; + }; + + struct log + { + static constexpr int MAX_SINK = 16; + + static log trace(); + + static log debug(); + + static log info(); + + static log warn(); + + static log error(); + + static log_level minimum_level(); + + static void minimum_level(log_level next); + + static int num_sinks(); + + static bool add_sink(log_sink* sink); + + ~log(); + + void emit(char c); + + void emit(const char* string); + + void emit(bool value); + + void emit(intmax_t number); + + void emit(uintmax_t number); + + uint8_t field_width() const; + + char fill_char() const; + + uint8_t base() const; + + bool prefix() const; + + bool boolalpha() const; + + bool hexupper() const; + + void field_width(uint8_t next); + + void fill_char(char next); + + void base(uint8_t next); + + void prefix(bool next); + + void boolalpha(bool next); + + void hexupper(bool next); + protected: + log(log_level level); + + private: + static log_level _minimum_level; + static log_sink* _sinks[MAX_SINK]; + static int _num_sinks; + static sync::spinlock _lock; + + static log get(log_level level, const char* lvlstr); + + log_level _current_level; + uint8_t _field_width; + char _fill_char; + uint8_t _base; + bool _prefix : 1; + bool _boolalpha : 1; + bool _hexupper : 1; + }; + +}}} // porpoise::io::logging diff --git a/include/shared/porpoise/io/logging/manipulators.hpp b/include/shared/porpoise/io/logging/manipulators.hpp new file mode 100644 index 0000000..23b3231 --- /dev/null +++ b/include/shared/porpoise/io/logging/manipulators.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include + +namespace porpoise { namespace io { namespace logging { + struct reset : manipulator + { + void operator()(log& log_) + { + log_.base(10); + log_.prefix(false); + log_.hexupper(false); + log_.boolalpha(false); + log_.field_width(0); + log_.fill_char(' '); + } + }; + +#define MANIP_LIST(F) \ + F(set_width, uint8_t, field_width) \ + F(set_fill, char, fill_char) \ + F(set_base, uint8_t, base) \ + F(show_prefix, bool, prefix) \ + F(boolalpha, bool, boolalpha) \ + F(hexupper, bool, hexupper) + + struct set_width : manipulator + { + set_width(uint8_t value) : _value(value) {} + void operator()(log& log_) {log_.field_width(_value);} + private: + uint8_t _value; + }; + + struct set_fill : manipulator + { + set_fill(char value) : _value(value) {} + void operator()(log& log_) {log_.fill_char(_value);} + private: + char _value; + }; + + struct set_base : manipulator + { + set_base(uint8_t value) : _value(value) {} + void operator()(log& log_) {log_.base(_value);} + private: + uint8_t _value; + }; + + struct show_prefix : manipulator + { + show_prefix(bool value) : _value(value) {} + void operator()(log& log_) {log_.prefix(_value);} + private: + bool _value; + }; + + struct boolalpha : manipulator + { + boolalpha(bool value) : _value(value) {} + void operator()(log& log_) {log_.boolalpha(_value);} + private: + bool _value; + }; + + struct hexupper : manipulator + { + hexupper(bool value) : _value(value) {} + void operator()(log& log_) {log_.hexupper(_value);} + private: + bool _value; + }; +}}} // porpoise::io::logging + + +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, char c); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, const char* s); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int8_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int16_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int32_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int64_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint8_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint16_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint32_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint64_t number); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::reset manip); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::set_width manip); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::set_fill manip); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::set_base manip); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::show_prefix manip); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::boolalpha manip); +porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::hexupper manip); diff --git a/include/shared/porpoise/io/logging/sinks/serial-sink.hpp b/include/shared/porpoise/io/logging/sinks/serial-sink.hpp new file mode 100644 index 0000000..d6b24b2 --- /dev/null +++ b/include/shared/porpoise/io/logging/sinks/serial-sink.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace porpoise { namespace io { namespace logging { +struct serial_sink : log_sink +{ + serial_sink(uart* dev, log_level min_level); + + void emit(log_level level, const char* event); + + void emit(log_level level, char c); +private: + uart* _dev; + log_level _min_level; +}; +}}} // porpoise::io::logging diff --git a/include/shared/porpoise/io/uart.hpp b/include/shared/porpoise/io/uart.hpp new file mode 100644 index 0000000..b6e8486 --- /dev/null +++ b/include/shared/porpoise/io/uart.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace porpoise { namespace io { + struct uart { + enum class baud_rate + { + bd9600, + bd115200 + }; + + static uart* init(baud_rate baud); + + void put(uint8_t value); + + uint8_t get(); + private: + static uart _instance; + baud_rate _baud; + + uart() = default; + }; +}} // porpoise::io diff --git a/include/shared/porpoise/sync/atomic.hpp b/include/shared/porpoise/sync/atomic.hpp new file mode 100644 index 0000000..384ecca --- /dev/null +++ b/include/shared/porpoise/sync/atomic.hpp @@ -0,0 +1,175 @@ +#pragma once + +namespace porpoise { namespace sync { + /// Atomic flag. + struct atomic_flag { + /// Construct with initial value. + explicit atomic_flag(bool initial_value = false) : _value(initial_value) + {} + + /// Set the value and return the previous value. + bool test_and_set() volatile + { + return __sync_bool_compare_and_swap(&_value, false, true); + } + + /// Clear the value. + void clear() volatile + { + _value = 0; + } + private: + bool _value; + }; + + namespace internal { + template + struct atomic_base { + using value_type = T; + using reference = volatile T&; + using const_reference = const volatile T&; + using rvalue_reference = T&&; + + void store(const_reference value) volatile + { + _value = value; + } + + void store(rvalue_reference value) volatile + { + _value = value; + } + + reference load() volatile + { + return _value; + } + + const_reference load() const volatile + { + return _value; + } + + const_reference compare_and_swap(const_reference value, const_reference next) volatile + { + __sync_val_compare_and_swap(&_value, value, next); + } + + operator reference() volatile + { + return _value; + } + + operator const_reference() const volatile + { + return _value; + } + protected: + value_type _value; + }; + } + + /// Atomic wrapper. + template + struct atomic : public ::porpoise::sync::internal::atomic_base + { + using value_type = typename ::porpoise::sync::internal::atomic_base::value_type; + using reference = typename ::porpoise::sync::internal::atomic_base::reference; + using const_reference = typename ::porpoise::sync::internal::atomic_base::const_reference; + using rvalue_reference = typename ::porpoise::sync::internal::atomic_base::rvalue_reference; + + explicit atomic(const_reference value) { this->_value = value; } + + explicit atomic(rvalue_reference value) { this->_value = value; } + + atomic& operator=(const value_type& other) + { + this->store(other); + } + + atomic& operator=(const atomic& other) + { + this->store(other.load()); + } + }; + +#define NUMERIC_LIST(F) \ + F(char) \ + F(signed char) \ + F(signed int) \ + F(signed long) \ + F(signed long long) \ + F(unsigned char) \ + F(unsigned int) \ + F(unsigned long) \ + F(unsigned long long) + +#define ATOMIC_SPEC(TYPE) \ + template <> \ + struct atomic : public ::porpoise::sync::internal::atomic_base \ + { \ + using value_type = typename ::porpoise::sync::internal::atomic_base::value_type; \ + using reference = typename ::porpoise::sync::internal::atomic_base::reference; \ + using const_reference = typename ::porpoise::sync::internal::atomic_base::const_reference; \ + using rvalue_reference = typename ::porpoise::sync::internal::atomic_base::rvalue_reference;\ + explicit atomic(const_reference value) { this->_value = value; } \ + explicit atomic(rvalue_reference value) { this->_value = value; } \ + TYPE operator++() \ + { \ + return __sync_fetch_and_add(&_value, 1); \ + } \ + TYPE operator--() \ + { \ + return __sync_fetch_and_sub(&_value, 1); \ + } \ + }; + + NUMERIC_LIST(ATOMIC_SPEC) + +#undef NUMERIC_LIST +#undef ATOMIC_SPEC + + template + struct atomic : public ::porpoise::sync::internal::atomic_base + { + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using rvalue_reference = T&&; + + explicit atomic(pointer value) { this->_value = value; } + + const_reference operator*() const + { + return *(this->_value); + } + + reference operator*() + { + return *(this->_value); + } + + const_pointer operator->() const + { + return (this->_value); + } + + pointer operator->() + { + return (this->_value); + } + + pointer operator++() + { + return __sync_fetch_and_add(&(this->_value), 1); + } + + pointer operator--() + { + return __sync_fetch_and_sub(&(this->_value), 1); + } + }; + +}} // porpoise::sync \ No newline at end of file diff --git a/include/shared/porpoise/sync/basic-lock.hpp b/include/shared/porpoise/sync/basic-lock.hpp new file mode 100644 index 0000000..514064d --- /dev/null +++ b/include/shared/porpoise/sync/basic-lock.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace porpoise { namespace sync { + /// Interface implemented by locks. + struct basic_lock { + /// Acquire the lock, potentially a blocking call. + virtual bool acquire() = 0; + + /// Release the lock. + virtual void release() = 0; + }; +}} // porpoise::sync diff --git a/include/shared/porpoise/sync/lock-guard.hpp b/include/shared/porpoise/sync/lock-guard.hpp new file mode 100644 index 0000000..ea721d2 --- /dev/null +++ b/include/shared/porpoise/sync/lock-guard.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace porpoise { namespace sync { + struct lock_guard { + lock_guard(basic_lock& lock) : _lock(lock) + { + _lock.acquire(); + } + + virtual ~lock_guard() + { + _lock.release(); + } + private: + basic_lock& _lock; + }; +}} // porpoise::sync diff --git a/include/shared/porpoise/sync/spinlock.hpp b/include/shared/porpoise/sync/spinlock.hpp new file mode 100644 index 0000000..a9f8bb0 --- /dev/null +++ b/include/shared/porpoise/sync/spinlock.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace porpoise { namespace sync { + struct spinlock : public basic_lock { + bool acquire() final + { + while (_flag.test_and_set()) + { + // Do nothing. + } + + return true; + } + + void release() final + { + _flag.clear(); + } + private: + porpoise::sync::atomic_flag _flag; + }; +}} // porpoise::sync diff --git a/include/shared/porpoise/time/config.hpp b/include/shared/porpoise/time/config.hpp new file mode 100644 index 0000000..8a3de0f --- /dev/null +++ b/include/shared/porpoise/time/config.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace porpoise { namespace time { + struct config + { + static constexpr uintmax_t DEFAULT_CPU_HERTZ = 1000000000; // 1 GHz. + + static void cpu_hertz(uintmax_t next); + + static uintmax_t cpu_hertz(); + private: + static uintmax_t _cpu_hertz; + + config() = delete; + }; +}} // porpose::time diff --git a/include/shared/porpoise/time/counters.hpp b/include/shared/porpoise/time/counters.hpp new file mode 100644 index 0000000..b6852de --- /dev/null +++ b/include/shared/porpoise/time/counters.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace porpoise { namespace time { + struct counters { + static uintmax_t cycles(); + + static uintmax_t micros(); + + static uintmax_t millis(); + + counters() = delete; + }; +}} \ No newline at end of file diff --git a/include/shared/porpoise/time/delay.hpp b/include/shared/porpoise/time/delay.hpp new file mode 100644 index 0000000..441da5d --- /dev/null +++ b/include/shared/porpoise/time/delay.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace porpoise { namespace time { + struct delay + { + static void cycles(uintmax_t count); + + static void micros(uintmax_t count); + + static void millis(uintmax_t count); + private: + delay() = delete; + }; +}} // porpose::time diff --git a/src/raspi/abort.cpp b/src/raspi/abort.cpp new file mode 100644 index 0000000..1e94ebd --- /dev/null +++ b/src/raspi/abort.cpp @@ -0,0 +1,10 @@ +#include + +namespace porpoise +{ + extern "C" void abort() + { + while (true) + ; + } +} diff --git a/src/raspi/boot/start-cpu.cpp b/src/raspi/boot/start-cpu.cpp new file mode 100644 index 0000000..5c1d2e5 --- /dev/null +++ b/src/raspi/boot/start-cpu.cpp @@ -0,0 +1,34 @@ +#include + +#include +#include +#include + +using namespace porpoise::io::logging; + +namespace porpoise { namespace boot { + void start_cpu(int id, void(* fn)()) + { + auto source = reinterpret_cast(fn); + switch (id) + { + case 1: *reinterpret_cast(0xE0) = source; break; + case 2: *reinterpret_cast(0xE8) = source; break; + case 3: *reinterpret_cast(0xF0) = source; break; + default: + PORPOISE_ABORT( + "BUG: Got request run function " + << set_width(16) + << set_fill(0) + << set_base(16) + << show_prefix(true) + << source + << " on invalid CPU: " + << reset() + << id + << "; valid values are 1, 2 or 3\n" + ); + break; + } + } +}} // porpoise::boot diff --git a/src/raspi/boot/start.S b/src/raspi/boot/start.S new file mode 100644 index 0000000..79f7405 --- /dev/null +++ b/src/raspi/boot/start.S @@ -0,0 +1,16 @@ +#include + +.section ".text.boot" + +.globl _start + +.org BOOT_ORIGIN + +_start: + // x0 points to the DTB. + ldr x4, =_start + mov sp, x4 + bl boot_kernel +_exit: + wfe + b _exit diff --git a/src/raspi/io/mmio.cpp b/src/raspi/io/mmio.cpp new file mode 100644 index 0000000..77b96e2 --- /dev/null +++ b/src/raspi/io/mmio.cpp @@ -0,0 +1,15 @@ +#include + +#include + +namespace porpoise { namespace io { + void mmio::put(reg r, uint32_t value) + { + *reinterpret_cast(r) = value; + } + + uint32_t mmio::get(reg r) + { + return *reinterpret_cast(r); + } +}} // porpose::io diff --git a/src/raspi/io/uart.cpp b/src/raspi/io/uart.cpp new file mode 100644 index 0000000..17012fe --- /dev/null +++ b/src/raspi/io/uart.cpp @@ -0,0 +1,99 @@ +#include + +#include +#include +#include + +static constexpr uint32_t UART_CLOCK_RATE = 3000000; + +static volatile uint32_t __attribute__((aligned(16))) mailbox[9] = { + 9*4, + 0, + 0x38002, + 12, + 8, + 2, + UART_CLOCK_RATE, + 0, + 0 +}; + +namespace porpoise { namespace io { + using namespace porpoise::time; + + uart uart::_instance; + + uart* uart::init(baud_rate baud) + { + if (_instance._baud == baud) + { + return &_instance; + } + + // Disable UART0. + mmio::put(mmio::reg::UART0_CR, 0); + + // Set up pins 14 & 15. + mmio::put(mmio::reg::GPIO_UP_DOWN, 0); + delay::cycles(150); + mmio::put(mmio::reg::GPIO_CLOCK0, (1 << 14) | (1 << 15)); + delay::cycles(150); + mmio::put(mmio::reg::GPIO_CLOCK0, 0); + + // Clear pending interrupts. + mmio::put(mmio::reg::UART0_ICR, 0x7FF); + + // Ensure 3 MHz clock rate on Raspberry Pi 3+. + if (__raspi__ >= 3) + { + auto r = (reinterpret_cast(&mailbox) & ~0xF) | 8; + while (mmio::get(mmio::reg::MAILBOX_STATUS) & 0x80000000) + ; + mmio::put(mmio::reg::MAILBOX_WRITE, r); + while (mmio::get(mmio::reg::MAILBOX_STATUS) & 0x40000000 || mmio::get(mmio::reg::MAILBOX_READ) != r) + ; + } + + // Send divisor. + switch (baud) + { + case baud_rate::bd9600: + mmio::put(mmio::reg::UART0_IBRD, 19); + mmio::put(mmio::reg::UART0_FBRD, 35); + break; + case baud_rate::bd115200: + mmio::put(mmio::reg::UART0_IBRD, 1); + mmio::put(mmio::reg::UART0_FBRD, 40); + break; + default: + return nullptr; + } + + // Set 8-bit FIFO mode with 1 stop bit & no parity. + mmio::put(mmio::reg::UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); + + // Mask interrupts. + mmio::put( + mmio::reg::UART0_IMSC, + (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) + ); + + mmio::put(mmio::reg::UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); + + return &_instance; + } + + void uart::put(uint8_t value) + { + while (mmio::get(mmio::reg::UART0_FR) & (1 << 5)) + ; + mmio::put(mmio::reg::UART0_DR, value); + } + + uint8_t uart::get() + { + while (mmio::get(mmio::reg::UART0_FR) & (1 << 5)) + ; + return mmio::get(mmio::reg::UART0_DR); + } +}} // porpoise::io diff --git a/src/raspi/time/config.cpp b/src/raspi/time/config.cpp new file mode 100644 index 0000000..d7a58fd --- /dev/null +++ b/src/raspi/time/config.cpp @@ -0,0 +1,17 @@ +#include + +#include + +namespace porpoise { namespace time { + uintmax_t config::_cpu_hertz; + + void config::cpu_hertz(uintmax_t next) + { + _cpu_hertz = next; + } + + uintmax_t config::cpu_hertz() + { + return _cpu_hertz; + } +}} // porpose::time diff --git a/src/raspi/time/counters.cpp b/src/raspi/time/counters.cpp new file mode 100644 index 0000000..c0a1080 --- /dev/null +++ b/src/raspi/time/counters.cpp @@ -0,0 +1,24 @@ +#include +#include + +namespace porpoise { namespace time { + uintmax_t counters::cycles() + { + uint64_t count = 0xdeadbeef; + asm volatile( + "\tisb\n" + "\tmrs %0, pmccntr_el0\n":"=r"(count) + ); + return count; + } + + uintmax_t counters::micros() + { + return cycles()*config::cpu_hertz()/1000000; + } + + uintmax_t counters::millis() + { + return cycles()*config::cpu_hertz()/1000; + } +}} diff --git a/src/raspi/time/delay.cpp b/src/raspi/time/delay.cpp new file mode 100644 index 0000000..6a5f228 --- /dev/null +++ b/src/raspi/time/delay.cpp @@ -0,0 +1,23 @@ +#include +#include +#include + +namespace porpoise { namespace time { + void delay::cycles(uintmax_t count) + { + asm volatile( + "\t__delay_%=: subs %[count], %[count], #1\n\tbne __delay_%=\n" + :"=r"(count): [count]"0"(count) : "cc" + ); + } + + void delay::micros(uintmax_t count) + { + cycles(count*config::cpu_hertz()/1000000); + } + + void delay::millis(uintmax_t count) + { + cycles(count*config::cpu_hertz()/1000); + } +}} // porpose::time diff --git a/src/shared/abi/cxa-guard.cpp b/src/shared/abi/cxa-guard.cpp new file mode 100644 index 0000000..fd27b10 --- /dev/null +++ b/src/shared/abi/cxa-guard.cpp @@ -0,0 +1,19 @@ +#include + +__extension__ typedef int __guard __attribute__((mode(__DI__))); + +extern "C" int __cxa_guard_acquire(__guard* g) +{ + while (__sync_lock_test_and_set(g, 1)) {} + return 1; +} + +extern "C" void __cxa_guard_release(__guard* g) +{ + *g = 0; +} + +extern "C" void __cxa_guard_abort(__guard*) +{ + PORPOISE_ABORT("__cxa_guard_abort called"); +} diff --git a/src/shared/abi/cxa-pure-virtual.cpp b/src/shared/abi/cxa-pure-virtual.cpp new file mode 100644 index 0000000..9a99825 --- /dev/null +++ b/src/shared/abi/cxa-pure-virtual.cpp @@ -0,0 +1,6 @@ +#include + +extern "C" void __cxa_pure_virtual() +{ + PORPOISE_ABORT("Pure virtual method called"); +} diff --git a/src/shared/boot/boot-kernel.cpp b/src/shared/boot/boot-kernel.cpp new file mode 100644 index 0000000..5df80b7 --- /dev/null +++ b/src/shared/boot/boot-kernel.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace porpoise::io; +using namespace porpoise::io::logging; + +extern "C" void _init(); +extern "C" void _fini(); + +extern "C" uint8_t __bss_start__[]; +extern "C" uint8_t __bss_end__[]; + +static void clear_bss() +{ + auto p = reinterpret_cast(__bss_start__); + while (p < __bss_end__) + { + *p++ = 0; + } +} + +namespace porpoise { namespace boot { + using sync::atomic; + using time::delay; + + static atomic cpu_id(0); + + extern "C" void boot_kernel(uint64_t dtb_address) + { + PORPOISE_UNUSED(dtb_address); + auto me = cpu_id++; + if (me == 0) + { + clear_bss(); + _init(); + + static uart* uart0 = uart::init(uart::baud_rate::bd9600); + static serial_sink uart0_sink(uart0, log_level::trace); + log::add_sink(&uart0_sink); + } + else + { + delay::millis(1000); + } + + cpu_main(me); + + if (me == 0) + { + _fini(); + } + + while (true) + ; + } +}} // porpoise::boot \ No newline at end of file diff --git a/src/shared/boot/cpu-main.cpp b/src/shared/boot/cpu-main.cpp new file mode 100644 index 0000000..5b5c384 --- /dev/null +++ b/src/shared/boot/cpu-main.cpp @@ -0,0 +1,11 @@ +#include +#include + +using namespace porpoise::io::logging; + +namespace porpoise { namespace boot { + void cpu_main(int id) + { + PORPOISE_LOG_INFO("Hello from CPU " << id << "!"); + } +}} // porpoise::boot diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp new file mode 100644 index 0000000..53ddc08 --- /dev/null +++ b/src/shared/io/logging/log.cpp @@ -0,0 +1,252 @@ +#include +#include + +#include +#include + +namespace porpoise { namespace io { namespace logging { + log_level log::_minimum_level; + log_sink* log::_sinks[MAX_SINK]; + int log::_num_sinks; + sync::spinlock log::_lock; + + log log::get(log_level level, const char* lvlstr) + { + auto inst = log(level); + auto millis = time::counters::millis(); + auto secs = millis/1000; + millis -= secs*1000; + inst.emit(secs); + inst.emit('.'); + inst.emit(millis); + inst.emit(' '); + inst.emit(lvlstr); + inst.emit("] "); + return inst; + } + + log log::trace() + { + return get(log_level::trace, "trac"); + } + + log log::debug() + { + return get(log_level::debug, "debu"); + } + + log log::info() + { + return get(log_level::info, "info"); + } + + log log::warn() + { + return get(log_level::warn, "warn"); + } + + log log::error() + { + return get(log_level::error, "err]"); + } + + bool log::add_sink(log_sink* sink) + { + if (sink == nullptr) + { + return false; + } + + if (_num_sinks >= MAX_SINK) + { + return false; + } + + _sinks[_num_sinks++] = sink; + return true; + } + + void log::minimum_level(log_level next) + { + _minimum_level = next; + } + + log_level log::minimum_level() + { + return _minimum_level; + } + + int log::num_sinks() + { + return _num_sinks; + } + + log::log(log_level level) + : _current_level(level) + , _field_width(0) + , _fill_char(' ') + , _base(10) + , _prefix(false) + { + _lock.acquire(); + } + + log::~log() + { + emit("\r\n"); + _lock.release(); + } + + void log::emit(char c) + { + if (_current_level < _minimum_level) + { + return; + } + + for (auto i = 0; i < _num_sinks; i++) + { + _sinks[i]->emit(_current_level, c); + } + } + + void log::emit(const char* string) + { + if (_current_level < _minimum_level) + { + return; + } + + for (auto i = 0; i < _num_sinks; i++) + { + _sinks[i]->emit(_current_level, string); + } + } + + void log::emit(intmax_t number) + { + if (_current_level < _minimum_level) + { + return; + } + + if (number < 0) + { + emit('-'); + emit(static_cast(-number)); + } + else + { + emit(static_cast(number)); + } + } + + void log::emit(uintmax_t number) + { + if (_current_level < _minimum_level) + { + return; + } + + static constexpr unsigned DIGIT_MAX = sizeof(uintmax_t)*CHAR_BIT; + static char buffer[DIGIT_MAX]; + auto p = buffer; + auto q = buffer + DIGIT_MAX; + auto alpha = _hexupper ? 'A' : 'a'; + if (number == 0) + { + *p++ = '0'; + } + else + { + do + { + auto r = number%_base; + if (r < 10) + { + *p++ = '0' + r; + } + else + { + *p++ = alpha + r - 10; + } + } while (number /= _base && p < q); + } + + auto count = p - buffer; + while (count > _field_width) + { + emit(_fill_char); + } + + while (p >= buffer) + { + emit(*p--); + } + } + + uint8_t log::field_width() const + { + return _field_width; + } + + char log::fill_char() const + { + return _fill_char; + } + + uint8_t log::base() const + { + return _base; + } + + bool log::prefix() const + { + return _prefix; + } + + bool log::boolalpha() const + { + return _boolalpha; + } + + bool log::hexupper() const + { + return _hexupper; + } + + void log::field_width(uint8_t next) + { + _field_width = next; + } + + void log::fill_char(char next) + { + _fill_char = next; + } + + void log::base(uint8_t next) + { + if (next < 2) + { + return; + } + + _base = next; + } + + void log::prefix(bool next) + { + _prefix = next; + } + + void log::boolalpha(bool next) + { + _boolalpha = next; + } + + void log::hexupper(bool next) + { + _hexupper = next; + } +}}} // porpoise::io::logging diff --git a/src/shared/io/logging/manipulators.cpp b/src/shared/io/logging/manipulators.cpp new file mode 100644 index 0000000..961a74d --- /dev/null +++ b/src/shared/io/logging/manipulators.cpp @@ -0,0 +1,105 @@ +#include + +using namespace porpoise::io::logging; + +log operator<<(log log_, char c) +{ + log_.emit(c); + return log_; +} + +log operator<<(log log_, const char* s) +{ + log_.emit(s); + return log_; +} + +log operator<<(log log_, int8_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, int16_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, int32_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, int64_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, uint8_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, uint16_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, uint32_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, uint64_t number) +{ + log_.emit(static_cast(number)); + return log_; +} + +log operator<<(log log_, reset manip) +{ + manip(log_); + return log_; +} + +log operator<<(log log_, set_width manip) +{ + manip(log_); + return log_; +} + +log operator<<(log log_, set_fill manip) +{ + manip(log_); + return log_; +} + +log operator<<(log log_, set_base manip) +{ + manip(log_); + return log_; +} + +log operator<<(log log_, show_prefix manip) +{ + manip(log_); + return log_; +} + +log operator<<(log log_, boolalpha manip) +{ + manip(log_); + return log_; +} + +log operator<<(log log_, hexupper manip) +{ + manip(log_); + return log_; +} diff --git a/src/shared/io/logging/sinks/serial-sink.cpp b/src/shared/io/logging/sinks/serial-sink.cpp new file mode 100644 index 0000000..abeeda8 --- /dev/null +++ b/src/shared/io/logging/sinks/serial-sink.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +namespace porpoise { namespace io { namespace logging { + serial_sink::serial_sink(uart* dev, log_level min_level) + : _dev(dev) + , _min_level(min_level) + { + } + + void serial_sink::emit(log_level level, const char* event) + { + if (level < _min_level) + { + return; + } + + while (*event) + { + _dev->put(*event++); + } + } + + void serial_sink::emit(log_level level, char c) + { + if (level < _min_level) + { + return; + } + + _dev->put(c); + } +}}} // porpoise::io::logging diff --git a/tools/ld/raspi3.ld b/tools/ld/raspi3.ld new file mode 100644 index 0000000..41fc9f1 --- /dev/null +++ b/tools/ld/raspi3.ld @@ -0,0 +1,63 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x80000; + __kernel_start__ = .; + .text BLOCK(4K) : ALIGN(4K) { + __code_start__ = .; + *(.text.boot) + *(.text*) + *(.gnu.linkonce.t*) + __code_end__ = .; + } + .init_array : + { + crti.o(.init_array) + KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*))) + KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array)) + crtn.o(.init_array) + } + .fini_array : + { + crti.o(.fini_array) + KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*))) + KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array)) + crtn.o(.fini_array) + } + .data BLOCK(4K) : ALIGN(4K) { + __data_start__ = .; + *(.data) + *(.ctors*) + *(.dtors*) + *(.gnu.linkonce.d*) + *(.jcr) + __data_end__ = .; + } + .rodata BLOCK(4K) : ALIGN(4K) { + __rodata_start__ = .; + *(.rodata*) + *(.gnu.linkonce.r*) + __rodata_end__ = .; + } + .eh_frame BLOCK(4K) : ALIGN(4K) { + start_eh_frame = .; + __eh_frame_start__ = .; + *(.eh_frame) + *(.gnu.linkonce.e*) + QUAD(0) + __eh_frame_end__ = .; + } + .bss BLOCK(4K) : ALIGN(4K) { + __bss_start__ = .; + *(.bss) + *(.gnu.linkonce.b*) + *(COMMON) + *(.common) + *(.gnu.linkonce.c*) + __bss_end__ = .; + } + __kernel_end__ = .; + /DISCARD/ : { + *(.comment) + } +} From d9b2f3635b9281aecbd0b3367203c13f669f8a29 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Tue, 9 Jun 2020 22:38:57 +0100 Subject: [PATCH 03/16] Implement new & delete --- include/shared/porpoise/heap.hpp | 20 +++++++++++++ src/shared/heap.cpp | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 include/shared/porpoise/heap.hpp create mode 100644 src/shared/heap.cpp diff --git a/include/shared/porpoise/heap.hpp b/include/shared/porpoise/heap.hpp new file mode 100644 index 0000000..f30bb99 --- /dev/null +++ b/include/shared/porpoise/heap.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace porpoise { + struct heap + { + static void init(); + + static void* allocate(size_t bytes); + + static void deallocate(void* ptr); + private: + static constexpr uintptr_t TOP = 0x3b3fffff; + static uintptr_t curr; + + heap() = delete; + }; +} diff --git a/src/shared/heap.cpp b/src/shared/heap.cpp new file mode 100644 index 0000000..bb82cba --- /dev/null +++ b/src/shared/heap.cpp @@ -0,0 +1,49 @@ +#include +#include + +#include +#include + +extern uint8_t __kernel_end__[]; + +namespace porpoise { + uintptr_t heap::curr; + + void heap::init() + { + curr = reinterpret_cast(__kernel_end__); + } + + void* heap::allocate(size_t bytes) + { + curr += curr & ~(sizeof(uint64_t) - 1); + auto p = curr; + curr += bytes; + return reinterpret_cast(p); + } + + void heap::deallocate(void* ptr) + { + PORPOISE_UNUSED(ptr); + } +} + +void* operator new(size_t size) +{ + return porpoise::heap::allocate(size); +} + +void* operator new(size_t size, void* ptr) +{ + return ptr; +} + +void operator delete(void* ptr) +{ + porpoise::heap::deallocate(ptr); +} + +void operator delete(void* ptr, size_t size) +{ + porpoise::heap::deallocate(ptr); +} From 3b33abd6b9bd2fded49a550e64f1637cc65f1db3 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 09:07:21 +0100 Subject: [PATCH 04/16] Create timespan type --- include/shared/porpoise/time/config.hpp | 18 ------ include/shared/porpoise/time/counters.hpp | 15 ----- include/shared/porpoise/time/delay.hpp | 12 +--- include/shared/porpoise/time/timespan.hpp | 32 +++++++++++ src/raspi/io/uart.cpp | 4 +- src/raspi/time/config.cpp | 17 ------ src/raspi/time/counters.cpp | 24 -------- src/raspi/time/delay.cpp | 14 +---- src/raspi/time/timespan.cpp | 67 +++++++++++++++++++++++ src/shared/boot/boot-kernel.cpp | 4 +- src/shared/io/logging/log.cpp | 6 +- 11 files changed, 112 insertions(+), 101 deletions(-) delete mode 100644 include/shared/porpoise/time/config.hpp delete mode 100644 include/shared/porpoise/time/counters.hpp create mode 100644 include/shared/porpoise/time/timespan.hpp delete mode 100644 src/raspi/time/config.cpp delete mode 100644 src/raspi/time/counters.cpp create mode 100644 src/raspi/time/timespan.cpp diff --git a/include/shared/porpoise/time/config.hpp b/include/shared/porpoise/time/config.hpp deleted file mode 100644 index 8a3de0f..0000000 --- a/include/shared/porpoise/time/config.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -namespace porpoise { namespace time { - struct config - { - static constexpr uintmax_t DEFAULT_CPU_HERTZ = 1000000000; // 1 GHz. - - static void cpu_hertz(uintmax_t next); - - static uintmax_t cpu_hertz(); - private: - static uintmax_t _cpu_hertz; - - config() = delete; - }; -}} // porpose::time diff --git a/include/shared/porpoise/time/counters.hpp b/include/shared/porpoise/time/counters.hpp deleted file mode 100644 index b6852de..0000000 --- a/include/shared/porpoise/time/counters.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace porpoise { namespace time { - struct counters { - static uintmax_t cycles(); - - static uintmax_t micros(); - - static uintmax_t millis(); - - counters() = delete; - }; -}} \ No newline at end of file diff --git a/include/shared/porpoise/time/delay.hpp b/include/shared/porpoise/time/delay.hpp index 441da5d..6f017dd 100644 --- a/include/shared/porpoise/time/delay.hpp +++ b/include/shared/porpoise/time/delay.hpp @@ -1,16 +1,8 @@ #pragma once #include +#include namespace porpoise { namespace time { - struct delay - { - static void cycles(uintmax_t count); - - static void micros(uintmax_t count); - - static void millis(uintmax_t count); - private: - delay() = delete; - }; + void delay(const timespan& t); }} // porpose::time diff --git a/include/shared/porpoise/time/timespan.hpp b/include/shared/porpoise/time/timespan.hpp new file mode 100644 index 0000000..bc5d4dd --- /dev/null +++ b/include/shared/porpoise/time/timespan.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace porpoise { namespace time { + struct timespan { + static constexpr uintmax_t DEFAULT_CPU_HERTZ = 1000000000; // 1 GHz. + + static void cpu_hertz(uintmax_t next); + + static uintmax_t cpu_hertz(); + + static timespan get_program_counter(); + + static timespan cycles(uintmax_t value); + + static timespan micros(uintmax_t value); + + static timespan millis(uintmax_t value); + + timespan(uintmax_t cycles); + + uintmax_t cycles() const; + + uintmax_t micros() const; + + uintmax_t millis() const; + private: + static uintmax_t _cpu_hertz; + uintmax_t _cycles; + }; +}} // porpoise::time diff --git a/src/raspi/io/uart.cpp b/src/raspi/io/uart.cpp index 17012fe..a0b11a2 100644 --- a/src/raspi/io/uart.cpp +++ b/src/raspi/io/uart.cpp @@ -35,9 +35,9 @@ namespace porpoise { namespace io { // Set up pins 14 & 15. mmio::put(mmio::reg::GPIO_UP_DOWN, 0); - delay::cycles(150); + delay(timespan::cycles(150)); mmio::put(mmio::reg::GPIO_CLOCK0, (1 << 14) | (1 << 15)); - delay::cycles(150); + delay(timespan::cycles(150)); mmio::put(mmio::reg::GPIO_CLOCK0, 0); // Clear pending interrupts. diff --git a/src/raspi/time/config.cpp b/src/raspi/time/config.cpp deleted file mode 100644 index d7a58fd..0000000 --- a/src/raspi/time/config.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include - -namespace porpoise { namespace time { - uintmax_t config::_cpu_hertz; - - void config::cpu_hertz(uintmax_t next) - { - _cpu_hertz = next; - } - - uintmax_t config::cpu_hertz() - { - return _cpu_hertz; - } -}} // porpose::time diff --git a/src/raspi/time/counters.cpp b/src/raspi/time/counters.cpp deleted file mode 100644 index c0a1080..0000000 --- a/src/raspi/time/counters.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -namespace porpoise { namespace time { - uintmax_t counters::cycles() - { - uint64_t count = 0xdeadbeef; - asm volatile( - "\tisb\n" - "\tmrs %0, pmccntr_el0\n":"=r"(count) - ); - return count; - } - - uintmax_t counters::micros() - { - return cycles()*config::cpu_hertz()/1000000; - } - - uintmax_t counters::millis() - { - return cycles()*config::cpu_hertz()/1000; - } -}} diff --git a/src/raspi/time/delay.cpp b/src/raspi/time/delay.cpp index 6a5f228..7f029b8 100644 --- a/src/raspi/time/delay.cpp +++ b/src/raspi/time/delay.cpp @@ -1,23 +1,13 @@ #include -#include #include namespace porpoise { namespace time { - void delay::cycles(uintmax_t count) + void delay(const timespan& t) { + auto count = t.cycles(); asm volatile( "\t__delay_%=: subs %[count], %[count], #1\n\tbne __delay_%=\n" :"=r"(count): [count]"0"(count) : "cc" ); } - - void delay::micros(uintmax_t count) - { - cycles(count*config::cpu_hertz()/1000000); - } - - void delay::millis(uintmax_t count) - { - cycles(count*config::cpu_hertz()/1000); - } }} // porpose::time diff --git a/src/raspi/time/timespan.cpp b/src/raspi/time/timespan.cpp new file mode 100644 index 0000000..bd85a61 --- /dev/null +++ b/src/raspi/time/timespan.cpp @@ -0,0 +1,67 @@ +#include + +#define MICROS_TO_CYCLES(MICROS, HERTZ) ((MICROS)*(HERTZ)/1000000) + +#define MILLIS_TO_CYCLES(MILLIS, HERTZ) ((MILLIS)*(HERTZ)/1000) + +#define CYCLES_TO_MICROS(CYCLES, HERTZ) ((CYCLES)*1000000/(HERTZ)) + +#define CYCLES_TO_MILLIS(CYCLES, HERTZ) ((CYCLES)*1000/(HERTZ)) + +namespace porpoise { namespace time { + uintmax_t timespan::_cpu_hertz = DEFAULT_CPU_HERTZ; + + void timespan::cpu_hertz(uintmax_t next) + { + _cpu_hertz = next; + } + + uintmax_t timespan::cpu_hertz() + { + return _cpu_hertz; + } + + timespan timespan::get_program_counter() + { + uint64_t count = 0xdeadbeef; + asm volatile( + "\tisb\n" + "\tmrs %0, pmccntr_el0\n":"=r"(count) + ); + return cycles(count); + } + + timespan timespan::cycles(uintmax_t value) + { + return timespan(value); + } + + timespan timespan::micros(uintmax_t value) + { + return timespan(MICROS_TO_CYCLES(value, _cpu_hertz)); + } + + timespan timespan::millis(uintmax_t value) + { + return timespan(MILLIS_TO_CYCLES(value, _cpu_hertz)); + } + + timespan::timespan(uintmax_t cycles) : _cycles(cycles) + { + } + + uintmax_t timespan::cycles() const + { + return _cycles; + } + + uintmax_t timespan::micros() const + { + return CYCLES_TO_MICROS(_cycles, _cpu_hertz); + } + + uintmax_t timespan::millis() const + { + return CYCLES_TO_MILLIS(_cycles, _cpu_hertz); + } +}} // porpoise::time diff --git a/src/shared/boot/boot-kernel.cpp b/src/shared/boot/boot-kernel.cpp index 5df80b7..a4710b0 100644 --- a/src/shared/boot/boot-kernel.cpp +++ b/src/shared/boot/boot-kernel.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include using namespace porpoise::io; @@ -27,6 +28,7 @@ static void clear_bss() namespace porpoise { namespace boot { using sync::atomic; using time::delay; + using time::timespan; static atomic cpu_id(0); @@ -45,7 +47,7 @@ namespace porpoise { namespace boot { } else { - delay::millis(1000); + delay(timespan::millis(1000)); } cpu_main(me); diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp index 53ddc08..e542899 100644 --- a/src/shared/io/logging/log.cpp +++ b/src/shared/io/logging/log.cpp @@ -2,9 +2,11 @@ #include #include -#include +#include namespace porpoise { namespace io { namespace logging { + using time::timespan; + log_level log::_minimum_level; log_sink* log::_sinks[MAX_SINK]; int log::_num_sinks; @@ -13,7 +15,7 @@ namespace porpoise { namespace io { namespace logging { log log::get(log_level level, const char* lvlstr) { auto inst = log(level); - auto millis = time::counters::millis(); + auto millis = timespan::get_program_counter().millis(); auto secs = millis/1000; millis -= secs*1000; inst.emit(secs); From c47fe195394b2d6feaf0931018da82006f65cb2b Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 09:08:21 +0100 Subject: [PATCH 05/16] Use references in log manipulators --- include/shared/porpoise/abort.hpp | 11 +++--- include/shared/porpoise/io/logging/log.hpp | 10 +++--- .../porpoise/io/logging/manipulators.hpp | 34 +++++++++---------- src/shared/io/logging/manipulators.cpp | 34 +++++++++---------- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/include/shared/porpoise/abort.hpp b/include/shared/porpoise/abort.hpp index f9fa2f2..9e45e2f 100644 --- a/include/shared/porpoise/abort.hpp +++ b/include/shared/porpoise/abort.hpp @@ -2,11 +2,12 @@ #include -#define PORPOISE_ABORT(...) \ -do \ -{ \ - ::porpoise::io::logging::log::error() << __VA_ARGS__;\ - ::porpoise::abort(); \ +#define PORPOISE_ABORT(...) \ +do \ +{ \ + auto __log = ::porpoise::io::logging::log::error();\ + __log << __VA_ARGS__; \ + ::porpoise::abort(); \ } while (0) namespace porpoise diff --git a/include/shared/porpoise/io/logging/log.hpp b/include/shared/porpoise/io/logging/log.hpp index 1756440..ae603da 100644 --- a/include/shared/porpoise/io/logging/log.hpp +++ b/include/shared/porpoise/io/logging/log.hpp @@ -4,11 +4,11 @@ #include -#define PORPOISE_LOG_TRACE(...) do {::porpoise::io::logging::log::trace() << __VA_ARGS__;} while (0) -#define PORPOISE_LOG_DEBUG(...) do {::porpoise::io::logging::log::debug() << __VA_ARGS__;} while (0) -#define PORPOISE_LOG_INFO(...) do {::porpoise::io::logging::log::info() << __VA_ARGS__;} while (0) -#define PORPOISE_LOG_WARN(...) do {::porpoise::io::logging::log::warn() << __VA_ARGS__;} while (0) -#define PORPOISE_LOG_ERROR(...) do {::porpoise::io::logging::log::error() << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_TRACE(...) do {auto __log = ::porpoise::io::logging::log::trace(); __log << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_DEBUG(...) do {auto __log = ::porpoise::io::logging::log::debug(); __log << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_INFO(...) do {auto __log = ::porpoise::io::logging::log::info(); __log << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_WARN(...) do {auto __log = ::porpoise::io::logging::log::warn(); __log << __VA_ARGS__;} while (0) +#define PORPOISE_LOG_ERROR(...) do {auto __log = ::porpoise::io::logging::log::error(); __log << __VA_ARGS__;} while (0) namespace porpoise { namespace io { namespace logging { /// Log level. diff --git a/include/shared/porpoise/io/logging/manipulators.hpp b/include/shared/porpoise/io/logging/manipulators.hpp index 23b3231..2728a20 100644 --- a/include/shared/porpoise/io/logging/manipulators.hpp +++ b/include/shared/porpoise/io/logging/manipulators.hpp @@ -77,20 +77,20 @@ namespace porpoise { namespace io { namespace logging { }}} // porpoise::io::logging -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, char c); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, const char* s); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int8_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int16_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int32_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, int64_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint8_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint16_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint32_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, uint64_t number); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::reset manip); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::set_width manip); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::set_fill manip); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::set_base manip); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::show_prefix manip); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::boolalpha manip); -porpoise::io::logging::log operator<<(porpoise::io::logging::log log_, porpoise::io::logging::hexupper manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, char c); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, const char* s); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, int8_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, int16_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, int32_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, int64_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, uint8_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, uint16_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, uint32_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, uint64_t number); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::reset manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::set_width manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::set_fill manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::set_base manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::show_prefix manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::boolalpha manip); +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, porpoise::io::logging::hexupper manip); diff --git a/src/shared/io/logging/manipulators.cpp b/src/shared/io/logging/manipulators.cpp index 961a74d..45282f3 100644 --- a/src/shared/io/logging/manipulators.cpp +++ b/src/shared/io/logging/manipulators.cpp @@ -2,103 +2,103 @@ using namespace porpoise::io::logging; -log operator<<(log log_, char c) +log& operator<<(log& log_, char c) { log_.emit(c); return log_; } -log operator<<(log log_, const char* s) +log& operator<<(log& log_, const char* s) { log_.emit(s); return log_; } -log operator<<(log log_, int8_t number) +log& operator<<(log& log_, int8_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, int16_t number) +log& operator<<(log& log_, int16_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, int32_t number) +log& operator<<(log& log_, int32_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, int64_t number) +log& operator<<(log& log_, int64_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, uint8_t number) +log& operator<<(log& log_, uint8_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, uint16_t number) +log& operator<<(log& log_, uint16_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, uint32_t number) +log& operator<<(log& log_, uint32_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, uint64_t number) +log& operator<<(log& log_, uint64_t number) { log_.emit(static_cast(number)); return log_; } -log operator<<(log log_, reset manip) +log& operator<<(log& log_, reset manip) { manip(log_); return log_; } -log operator<<(log log_, set_width manip) +log& operator<<(log& log_, set_width manip) { manip(log_); return log_; } -log operator<<(log log_, set_fill manip) +log& operator<<(log& log_, set_fill manip) { manip(log_); return log_; } -log operator<<(log log_, set_base manip) +log& operator<<(log& log_, set_base manip) { manip(log_); return log_; } -log operator<<(log log_, show_prefix manip) +log& operator<<(log& log_, show_prefix manip) { manip(log_); return log_; } -log operator<<(log log_, boolalpha manip) +log& operator<<(log& log_, boolalpha manip) { manip(log_); return log_; } -log operator<<(log log_, hexupper manip) +log& operator<<(log& log_, hexupper manip) { manip(log_); return log_; From eca480d006422d08087ff26c8ff260ecd904b5ae Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 17:22:01 +0100 Subject: [PATCH 06/16] Use stack per core & switch __sync builtins to __atomic builtins --- Makefile | 6 +- include/raspi/porpoise/boot-platform.hpp | 2 +- include/shared/lib/string.hpp | 6 ++ include/shared/porpoise/io/logging/log.hpp | 36 +++++--- include/shared/porpoise/sync/atomic.hpp | 87 ++++++++++++------- include/shared/porpoise/sync/spinlock.hpp | 2 +- src/raspi/boot/start.S | 27 ++++-- src/shared/boot/boot-kernel.cpp | 76 +++++++++++------ src/shared/heap.cpp | 2 + src/shared/io/logging/log.cpp | 99 +++++++++++++++++----- src/shared/lib/string.cpp | 12 +++ tools/ld/raspi2.ld | 2 +- tools/ld/raspi3.ld | 2 +- 13 files changed, 260 insertions(+), 99 deletions(-) create mode 100644 include/shared/lib/string.hpp create mode 100644 src/shared/lib/string.cpp diff --git a/Makefile b/Makefile index 9ae83da..5994a03 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ KERNEL_BINARY = bin/sdcard/boot/kernel8.img PREFIX := aarch64-linux-gnu CXX = $(PREFIX)-g++ -CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE_BASE)\ +CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE_BASE) -Iinclude/shared/lib -Iinclude/$(MACHINE_BASE)/lib\ -D__$(MACHINE_BASE)__=$(MACHINE_VERSION)\ -ffreestanding -nostdlib -fno-exceptions -fno-rtti -fno-stack-protector\ -std=c++17 -Wall -Wextra @@ -64,7 +64,7 @@ mon-qemu: $(KERNEL_ELF) $(EMU) $(EMUFLAGS) -monitor stdio -kernel $(KERNEL_ELF) debug-qemu: $(KERNEL_ELF) - $(EMU) $(EMUFLAGS) -kernel $(KERNEL_ELF) -S -s & - gdb-multiarch $(KERNEL_BINARY) -ex "target remote :1234" -ex "set architecture aarch64" + $(EMU) $(EMUFLAGS) -kernel $(KERNEL_ELF) -s -S & + gdb-multiarch -s $(KERNEL_ELF) -q -ex "set architecture aarch64" -ex "b _start" -ex "target remote :1234" .PHONY: run-qemu debug-qemu $(KERNEL_BINARY) $(KERNEL_ELF) diff --git a/include/raspi/porpoise/boot-platform.hpp b/include/raspi/porpoise/boot-platform.hpp index 90e368a..e699388 100644 --- a/include/raspi/porpoise/boot-platform.hpp +++ b/include/raspi/porpoise/boot-platform.hpp @@ -10,7 +10,7 @@ #include namespace porpoise { namespace boot { - extern "C" void boot_kernel(uint64_t dtb); + extern "C" void boot_kernel(uint64_t dtb, uint64_t id) __attribute__((noreturn)); }} // porpoise::boot #endif // ! __ASM_SOURCE__ diff --git a/include/shared/lib/string.hpp b/include/shared/lib/string.hpp new file mode 100644 index 0000000..26b5de3 --- /dev/null +++ b/include/shared/lib/string.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +extern "C" size_t strlen(const char* s); diff --git a/include/shared/porpoise/io/logging/log.hpp b/include/shared/porpoise/io/logging/log.hpp index ae603da..a4a6290 100644 --- a/include/shared/porpoise/io/logging/log.hpp +++ b/include/shared/porpoise/io/logging/log.hpp @@ -2,6 +2,7 @@ #include +#include #include #define PORPOISE_LOG_TRACE(...) do {auto __log = ::porpoise::io::logging::log::trace(); __log << __VA_ARGS__;} while (0) @@ -60,6 +61,10 @@ namespace porpoise { namespace io { namespace logging { static bool add_sink(log_sink* sink); + log(log&& other); + + void operator=(log&& other); + ~log(); void emit(char c); @@ -95,24 +100,33 @@ namespace porpoise { namespace io { namespace logging { void boolalpha(bool next); void hexupper(bool next); + + log(const log&) = delete; + + void operator=(const log&) = delete; protected: log(log_level level); private: - static log_level _minimum_level; - static log_sink* _sinks[MAX_SINK]; - static int _num_sinks; - static sync::spinlock _lock; + static log_level _minimum_level; + static log_sink* _sinks[MAX_SINK]; + static int _num_sinks; + static sync::spinlock _lock; static log get(log_level level, const char* lvlstr); - log_level _current_level; - uint8_t _field_width; - char _fill_char; - uint8_t _base; - bool _prefix : 1; - bool _boolalpha : 1; - bool _hexupper : 1; + log_level _current_level; + uint8_t _field_width; + char _fill_char; + uint8_t _base; + bool _prefix : 1; + bool _boolalpha : 1; + bool _hexupper : 1; + sync::atomic _moved; + + void emit_all(char c); + + void emit_all(const char* s); }; }}} // porpoise::io::logging diff --git a/include/shared/porpoise/sync/atomic.hpp b/include/shared/porpoise/sync/atomic.hpp index 384ecca..8f511bc 100644 --- a/include/shared/porpoise/sync/atomic.hpp +++ b/include/shared/porpoise/sync/atomic.hpp @@ -1,6 +1,16 @@ #pragma once namespace porpoise { namespace sync { + enum class memory_order + { + seq_cst = __ATOMIC_SEQ_CST, + acq_rel = __ATOMIC_ACQ_REL, + release = __ATOMIC_RELEASE, + acquire = __ATOMIC_ACQUIRE, + consume = __ATOMIC_CONSUME, + relaxed = __ATOMIC_RELAXED + }; + /// Atomic flag. struct atomic_flag { /// Construct with initial value. @@ -8,15 +18,15 @@ namespace porpoise { namespace sync { {} /// Set the value and return the previous value. - bool test_and_set() volatile + bool test_and_set(memory_order order = memory_order::seq_cst) volatile { - return __sync_bool_compare_and_swap(&_value, false, true); + return __atomic_test_and_set(&_value, static_cast(order)); } /// Clear the value. - void clear() volatile + void clear(memory_order order = memory_order::seq_cst) volatile { - _value = 0; + __atomic_clear(&_value, static_cast(order)); } private: bool _value; @@ -30,39 +40,36 @@ namespace porpoise { namespace sync { using const_reference = const volatile T&; using rvalue_reference = T&&; - void store(const_reference value) volatile - { - _value = value; - } - - void store(rvalue_reference value) volatile + void store(const_reference next, memory_order order = memory_order::seq_cst) volatile { - _value = value; + __atomic_store(&_value, &next, static_cast(order)); } - reference load() volatile + void store(rvalue_reference next, memory_order order = memory_order::seq_cst) volatile { - return _value; + __atomic_store(&_value, &next, static_cast(order)); } - const_reference load() const volatile + value_type load(memory_order order = memory_order::seq_cst) const volatile { - return _value; + value_type loaded; + __atomic_load(&_value, &loaded, static_cast(order)); + return loaded; } - const_reference compare_and_swap(const_reference value, const_reference next) volatile + value_type compare_and_swap( + const_reference expect, + const_reference next, + memory_order order = memory_order::seq_cst) volatile { - __sync_val_compare_and_swap(&_value, value, next); + value_type tmp = expect; + __atomic_compare_exchange(&_value, &tmp, &next, false, static_cast(order)); + return tmp; } - operator reference() volatile + operator value_type() volatile { - return _value; - } - - operator const_reference() const volatile - { - return _value; + return load(); } protected: value_type _value; @@ -85,11 +92,13 @@ namespace porpoise { namespace sync { atomic& operator=(const value_type& other) { this->store(other); + return *this; } atomic& operator=(const atomic& other) { this->store(other.load()); + return *this; } }; @@ -112,15 +121,23 @@ namespace porpoise { namespace sync { using reference = typename ::porpoise::sync::internal::atomic_base::reference; \ using const_reference = typename ::porpoise::sync::internal::atomic_base::const_reference; \ using rvalue_reference = typename ::porpoise::sync::internal::atomic_base::rvalue_reference;\ - explicit atomic(const_reference value) { this->_value = value; } \ - explicit atomic(rvalue_reference value) { this->_value = value; } \ + explicit atomic(const_reference value) { this->_value = value; } \ + explicit atomic(rvalue_reference value) { this->_value = value; } \ TYPE operator++() \ { \ - return __sync_fetch_and_add(&_value, 1); \ + return __atomic_fetch_add(&_value, 1, static_cast(memory_order::seq_cst)); \ } \ TYPE operator--() \ { \ - return __sync_fetch_and_sub(&_value, 1); \ + return __atomic_fetch_sub(&_value, 1, static_cast(memory_order::seq_cst)); \ + } \ + TYPE operator++(int) \ + { \ + return __atomic_add_fetch(&_value, 1, static_cast(memory_order::seq_cst)); \ + } \ + TYPE operator--(int) \ + { \ + return __atomic_sub_fetch(&_value, 1, static_cast(memory_order::seq_cst)); \ } \ }; @@ -163,12 +180,22 @@ namespace porpoise { namespace sync { pointer operator++() { - return __sync_fetch_and_add(&(this->_value), 1); + return __atomic_fetch_add(&(this->_value), 1, static_cast(memory_order::seq_cst)); } pointer operator--() { - return __sync_fetch_and_sub(&(this->_value), 1); + return __atomic_fetch_sub(&(this->_value), 1, static_cast(memory_order::seq_cst)); + } + + pointer operator++(int) + { + return __atomic_add_fetch(&(this->_value), 1, static_cast(memory_order::seq_cst)); + } + + pointer operator--(int) + { + return __atomic_sub_fetch(&(this->_value), 1, static_cast(memory_order::seq_cst)); } }; diff --git a/include/shared/porpoise/sync/spinlock.hpp b/include/shared/porpoise/sync/spinlock.hpp index a9f8bb0..abae87c 100644 --- a/include/shared/porpoise/sync/spinlock.hpp +++ b/include/shared/porpoise/sync/spinlock.hpp @@ -20,6 +20,6 @@ namespace porpoise { namespace sync { _flag.clear(); } private: - porpoise::sync::atomic_flag _flag; + atomic_flag _flag; }; }} // porpoise::sync diff --git a/src/raspi/boot/start.S b/src/raspi/boot/start.S index 79f7405..4757218 100644 --- a/src/raspi/boot/start.S +++ b/src/raspi/boot/start.S @@ -4,13 +4,26 @@ .globl _start +.equ STACK_SIZE, 0x2000 + .org BOOT_ORIGIN _start: - // x0 points to the DTB. - ldr x4, =_start - mov sp, x4 - bl boot_kernel -_exit: - wfe - b _exit + // Get the CPU ID from the MPIDR + mrs x1, mpidr_el1 + and x1, x1, #3 + + mov x2, BOOT_ORIGIN + + // For CPU 0, boot immediately with sp=BOOT_ORIGIN, x0=DTB and x1=0. +0: cbnz x1, N + mov sp, x2 + b boot_kernel + + // For CPU 1-3, set stack pointer to `BOOT_ORIGIN - STACK_SIZE*cpu` giving each CPU an 8 kiB stack. +N: mov x3, STACK_SIZE + mul x3, x3, x1 // x3 = STACK_SIZE*cpu + sub x2, x2, x3 // x2 = BOOT_ORIGIN - STACK_SIZE*cpu + mov sp, x2 + b boot_kernel + diff --git a/src/shared/boot/boot-kernel.cpp b/src/shared/boot/boot-kernel.cpp index a4710b0..3d81691 100644 --- a/src/shared/boot/boot-kernel.cpp +++ b/src/shared/boot/boot-kernel.cpp @@ -10,51 +10,77 @@ using namespace porpoise::io; using namespace porpoise::io::logging; +extern "C" void _start(); extern "C" void _init(); extern "C" void _fini(); extern "C" uint8_t __bss_start__[]; extern "C" uint8_t __bss_end__[]; -static void clear_bss() -{ - auto p = reinterpret_cast(__bss_start__); - while (p < __bss_end__) - { - *p++ = 0; - } -} - namespace porpoise { namespace boot { using sync::atomic; using time::delay; using time::timespan; - static atomic cpu_id(0); + static atomic cpu0_init_complete(false); - extern "C" void boot_kernel(uint64_t dtb_address) + static void clear_bss() { - PORPOISE_UNUSED(dtb_address); - auto me = cpu_id++; - if (me == 0) + auto p = reinterpret_cast(__bss_start__); + while (p < __bss_end__) { - clear_bss(); - _init(); - - static uart* uart0 = uart::init(uart::baud_rate::bd9600); - static serial_sink uart0_sink(uart0, log_level::trace); - log::add_sink(&uart0_sink); + *p++ = 0; } - else + } + + // Ensure the other 3 CPUs run. + static void ensure_other_cpus() + { + start_cpu(1, _start); + start_cpu(2, _start); + start_cpu(3, _start); + } + + static void start_cpu_0(uint64_t dtb_address) + { + PORPOISE_UNUSED(dtb_address); + clear_bss(); + _init(); + + static uart* uart0 = uart::init(uart::baud_rate::bd9600); + static serial_sink uart0_sink(uart0, log_level::trace); + log::add_sink(&uart0_sink); + + cpu0_init_complete.store(true); + + ensure_other_cpus(); + + cpu_main(0); + + _fini(); + } + + static void start_cpu(int id) + { + // Wait for CPU 0 initialisation. + while (!cpu0_init_complete) { - delay(timespan::millis(1000)); + delay(timespan::millis(100)); } - cpu_main(me); + cpu_main(id); + } - if (me == 0) + extern "C" void boot_kernel(uint64_t dtb_address, uint64_t id) + { + PORPOISE_UNUSED(dtb_address); + if (id == 0) + { + start_cpu_0(dtb_address); + } + else { - _fini(); + start_cpu(id); } while (true) diff --git a/src/shared/heap.cpp b/src/shared/heap.cpp index bb82cba..8c3f98e 100644 --- a/src/shared/heap.cpp +++ b/src/shared/heap.cpp @@ -35,6 +35,7 @@ void* operator new(size_t size) void* operator new(size_t size, void* ptr) { + PORPOISE_UNUSED(size); return ptr; } @@ -45,5 +46,6 @@ void operator delete(void* ptr) void operator delete(void* ptr, size_t size) { + PORPOISE_UNUSED(size); porpoise::heap::deallocate(ptr); } diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp index e542899..6105c31 100644 --- a/src/shared/io/logging/log.cpp +++ b/src/shared/io/logging/log.cpp @@ -1,11 +1,15 @@ #include #include +#include + #include #include namespace porpoise { namespace io { namespace logging { using time::timespan; + using sync::spinlock; + using sync::atomic_flag; log_level log::_minimum_level; log_sink* log::_sinks[MAX_SINK]; @@ -18,23 +22,29 @@ namespace porpoise { namespace io { namespace logging { auto millis = timespan::get_program_counter().millis(); auto secs = millis/1000; millis -= secs*1000; - inst.emit(secs); - inst.emit('.'); - inst.emit(millis); - inst.emit(' '); - inst.emit(lvlstr); - inst.emit("] "); + inst << set_width(3) + << secs + << reset() + << '.' + << set_width(3) + << set_fill('0') + << millis + << reset() + << ' ' + << lvlstr + << "] " + ; return inst; } log log::trace() { - return get(log_level::trace, "trac"); + return get(log_level::trace, "trace"); } log log::debug() { - return get(log_level::debug, "debu"); + return get(log_level::debug, "debug"); } log log::info() @@ -49,7 +59,7 @@ namespace porpoise { namespace io { namespace logging { log log::error() { - return get(log_level::error, "err]"); + return get(log_level::error, "error"); } bool log::add_sink(log_sink* sink) @@ -89,14 +99,61 @@ namespace porpoise { namespace io { namespace logging { , _fill_char(' ') , _base(10) , _prefix(false) + , _hexupper(false) + , _moved(false) { _lock.acquire(); } + log::log(log&& other) + : _current_level(other._current_level) + , _field_width(other._field_width) + , _fill_char(other._fill_char) + , _base(other._base) + , _prefix(other._prefix) + , _boolalpha(other._boolalpha) + , _hexupper(other._hexupper) + , _moved(other._moved.load()) + { + other._moved.store(true); + } + + void log::operator=(log&& other) + { + _current_level = other._current_level; + _field_width = other._field_width; + _fill_char = other._fill_char; + _base = other._base; + _prefix = other._prefix; + _boolalpha = other._boolalpha; + _hexupper = other._hexupper; + _moved = other._moved; + other._moved.store(true); + } + log::~log() { - emit("\r\n"); - _lock.release(); + if (!_moved) + { + emit("\r\n"); + _lock.release(); + } + } + + void log::emit_all(char c) + { + for (auto i = 0; i < _num_sinks; i++) + { + _sinks[i]->emit(_current_level, c); + } + } + + void log::emit_all(const char* s) + { + for (auto i = 0; i < _num_sinks; i++) + { + _sinks[i]->emit(_current_level, s); + } } void log::emit(char c) @@ -106,10 +163,12 @@ namespace porpoise { namespace io { namespace logging { return; } - for (auto i = 0; i < _num_sinks; i++) + for (auto j = 1; j < _field_width; j++) { - _sinks[i]->emit(_current_level, c); + emit_all(_fill_char); } + + emit_all(c); } void log::emit(const char* string) @@ -119,10 +178,12 @@ namespace porpoise { namespace io { namespace logging { return; } - for (auto i = 0; i < _num_sinks; i++) + for (auto j = strlen(string); j < _field_width; j++) { - _sinks[i]->emit(_current_level, string); + emit_all(_fill_char); } + + emit_all(string); } void log::emit(intmax_t number) @@ -134,7 +195,7 @@ namespace porpoise { namespace io { namespace logging { if (number < 0) { - emit('-'); + emit_all('-'); emit(static_cast(-number)); } else @@ -176,14 +237,14 @@ namespace porpoise { namespace io { namespace logging { } auto count = p - buffer; - while (count > _field_width) + while (count++ < _field_width) { - emit(_fill_char); + emit_all(_fill_char); } while (p >= buffer) { - emit(*p--); + emit_all(*p--); } } diff --git a/src/shared/lib/string.cpp b/src/shared/lib/string.cpp new file mode 100644 index 0000000..5177607 --- /dev/null +++ b/src/shared/lib/string.cpp @@ -0,0 +1,12 @@ +#include + +size_t strlen(const char* s) +{ + auto p = s; + while (*s) + { + s++; + } + + return static_cast(s - p); +} diff --git a/tools/ld/raspi2.ld b/tools/ld/raspi2.ld index 41fc9f1..9126c4d 100644 --- a/tools/ld/raspi2.ld +++ b/tools/ld/raspi2.ld @@ -1,7 +1,7 @@ ENTRY(_start) SECTIONS { - . = 0x80000; + . = 0x8000; __kernel_start__ = .; .text BLOCK(4K) : ALIGN(4K) { __code_start__ = .; diff --git a/tools/ld/raspi3.ld b/tools/ld/raspi3.ld index 41fc9f1..981d0c2 100644 --- a/tools/ld/raspi3.ld +++ b/tools/ld/raspi3.ld @@ -5,7 +5,7 @@ SECTIONS __kernel_start__ = .; .text BLOCK(4K) : ALIGN(4K) { __code_start__ = .; - *(.text.boot) + KEEP(*(.text.boot)) *(.text*) *(.gnu.linkonce.t*) __code_end__ = .; From 2157ec770e84a82c3e67750ef5de144c6358cc03 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 17:55:38 +0100 Subject: [PATCH 07/16] Guard heap::allocate, allow arbitrary alignment, and handle OOM --- include/shared/porpoise/heap.hpp | 17 +++++++++++++++-- src/shared/heap.cpp | 25 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/include/shared/porpoise/heap.hpp b/include/shared/porpoise/heap.hpp index f30bb99..ad65735 100644 --- a/include/shared/porpoise/heap.hpp +++ b/include/shared/porpoise/heap.hpp @@ -3,17 +3,30 @@ #include #include +#include + namespace porpoise { struct heap { + enum class oom_behaviour + { + abort, + null + }; + static void init(); - static void* allocate(size_t bytes); + static void* allocate( + size_t bytes, + size_t alignment = sizeof(uint64_t), + oom_behaviour behaviour = oom_behaviour::abort + ); static void deallocate(void* ptr); private: static constexpr uintptr_t TOP = 0x3b3fffff; - static uintptr_t curr; + static uintptr_t _curr; + static sync::spinlock _lock; heap() = delete; }; diff --git a/src/shared/heap.cpp b/src/shared/heap.cpp index 8c3f98e..aadcbbe 100644 --- a/src/shared/heap.cpp +++ b/src/shared/heap.cpp @@ -3,22 +3,35 @@ #include #include +#include extern uint8_t __kernel_end__[]; namespace porpoise { - uintptr_t heap::curr; + uintptr_t heap::_curr; void heap::init() { - curr = reinterpret_cast(__kernel_end__); + _curr = reinterpret_cast(__kernel_end__); } - void* heap::allocate(size_t bytes) + void* heap::allocate(size_t bytes, size_t alignment, oom_behaviour behaviour) { - curr += curr & ~(sizeof(uint64_t) - 1); - auto p = curr; - curr += bytes; + sync::lock_guard guard(_lock); + + _curr += _curr & ~(alignment - 1); + if (_curr + bytes > TOP) + { + if (behaviour == oom_behaviour::abort) + { + PORPOISE_ABORT("Out of memory"); + } + + return nullptr; + } + + auto p = _curr; + _curr += bytes; return reinterpret_cast(p); } From fbbd3f55dc9c496793e9b2da91dc03221f379913 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 18:12:42 +0100 Subject: [PATCH 08/16] Fix log::emit(uintmax_t) looping too many times --- src/shared/boot/boot-kernel.cpp | 10 ++++---- src/shared/boot/cpu-main.cpp | 2 +- src/shared/heap.cpp | 2 ++ src/shared/io/logging/log.cpp | 42 +++++++++++++-------------------- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/shared/boot/boot-kernel.cpp b/src/shared/boot/boot-kernel.cpp index 3d81691..268c81f 100644 --- a/src/shared/boot/boot-kernel.cpp +++ b/src/shared/boot/boot-kernel.cpp @@ -47,14 +47,14 @@ namespace porpoise { namespace boot { clear_bss(); _init(); - static uart* uart0 = uart::init(uart::baud_rate::bd9600); + static uart* uart0 = uart::init(uart::baud_rate::bd115200); static serial_sink uart0_sink(uart0, log_level::trace); log::add_sink(&uart0_sink); - cpu0_init_complete.store(true); - ensure_other_cpus(); + cpu0_init_complete.store(true); + cpu_main(0); _fini(); @@ -65,7 +65,7 @@ namespace porpoise { namespace boot { // Wait for CPU 0 initialisation. while (!cpu0_init_complete) { - delay(timespan::millis(100)); + delay(timespan::millis(1)); } cpu_main(id); @@ -86,4 +86,4 @@ namespace porpoise { namespace boot { while (true) ; } -}} // porpoise::boot \ No newline at end of file +}} // porpoise::boot diff --git a/src/shared/boot/cpu-main.cpp b/src/shared/boot/cpu-main.cpp index 5b5c384..f9e9705 100644 --- a/src/shared/boot/cpu-main.cpp +++ b/src/shared/boot/cpu-main.cpp @@ -6,6 +6,6 @@ using namespace porpoise::io::logging; namespace porpoise { namespace boot { void cpu_main(int id) { - PORPOISE_LOG_INFO("Hello from CPU " << id << "!"); + PORPOISE_LOG_INFO("Hello from CPU " << id << '!'); } }} // porpoise::boot diff --git a/src/shared/heap.cpp b/src/shared/heap.cpp index aadcbbe..edd4efe 100644 --- a/src/shared/heap.cpp +++ b/src/shared/heap.cpp @@ -4,11 +4,13 @@ #include #include #include +#include extern uint8_t __kernel_end__[]; namespace porpoise { uintptr_t heap::_curr; + sync::spinlock heap::_lock; void heap::init() { diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp index 6105c31..a63fcdd 100644 --- a/src/shared/io/logging/log.cpp +++ b/src/shared/io/logging/log.cpp @@ -22,9 +22,7 @@ namespace porpoise { namespace io { namespace logging { auto millis = timespan::get_program_counter().millis(); auto secs = millis/1000; millis -= secs*1000; - inst << set_width(3) - << secs - << reset() + inst << secs << '.' << set_width(3) << set_fill('0') @@ -212,39 +210,33 @@ namespace porpoise { namespace io { namespace logging { } static constexpr unsigned DIGIT_MAX = sizeof(uintmax_t)*CHAR_BIT; - static char buffer[DIGIT_MAX]; + char buffer[DIGIT_MAX]; auto p = buffer; auto q = buffer + DIGIT_MAX; auto alpha = _hexupper ? 'A' : 'a'; - if (number == 0) - { - *p++ = '0'; - } - else + do { - do + auto r = number%_base; + if (r < 10) { - auto r = number%_base; - if (r < 10) - { - *p++ = '0' + r; - } - else - { - *p++ = alpha + r - 10; - } - } while (number /= _base && p < q); - } + *p++ = '0' + r; + } + else + { + *p++ = alpha + r - 10; + } + + number /= _base; + } while (number && p < q); - auto count = p - buffer; - while (count++ < _field_width) + for (auto count = p - buffer; count < _field_width; count++) { emit_all(_fill_char); } - while (p >= buffer) + while (p-- > buffer) { - emit_all(*p--); + emit_all(*p); } } From 7a7e620db6a9d15e8ff51f6d62585c70f418f1e9 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 18:30:03 +0100 Subject: [PATCH 09/16] Minor string change --- src/raspi/boot/start-cpu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raspi/boot/start-cpu.cpp b/src/raspi/boot/start-cpu.cpp index 5c1d2e5..479479f 100644 --- a/src/raspi/boot/start-cpu.cpp +++ b/src/raspi/boot/start-cpu.cpp @@ -17,7 +17,7 @@ namespace porpoise { namespace boot { case 3: *reinterpret_cast(0xF0) = source; break; default: PORPOISE_ABORT( - "BUG: Got request run function " + "BUG: Received request to run function " << set_width(16) << set_fill(0) << set_base(16) From 63ffb98dac2253bca312bbc258bedd6d5a4db2cc Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 18:36:04 +0100 Subject: [PATCH 10/16] Add operator<<(log, void*) overload --- .../porpoise/io/logging/manipulators.hpp | 10 +------- src/raspi/boot/start-cpu.cpp | 7 +----- src/shared/io/logging/manipulators.cpp | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/shared/porpoise/io/logging/manipulators.hpp b/include/shared/porpoise/io/logging/manipulators.hpp index 2728a20..f6f270d 100644 --- a/include/shared/porpoise/io/logging/manipulators.hpp +++ b/include/shared/porpoise/io/logging/manipulators.hpp @@ -19,14 +19,6 @@ namespace porpoise { namespace io { namespace logging { } }; -#define MANIP_LIST(F) \ - F(set_width, uint8_t, field_width) \ - F(set_fill, char, fill_char) \ - F(set_base, uint8_t, base) \ - F(show_prefix, bool, prefix) \ - F(boolalpha, bool, boolalpha) \ - F(hexupper, bool, hexupper) - struct set_width : manipulator { set_width(uint8_t value) : _value(value) {} @@ -76,7 +68,7 @@ namespace porpoise { namespace io { namespace logging { }; }}} // porpoise::io::logging - +porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, void*); porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, char c); porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, const char* s); porpoise::io::logging::log& operator<<(porpoise::io::logging::log& log_, int8_t number); diff --git a/src/raspi/boot/start-cpu.cpp b/src/raspi/boot/start-cpu.cpp index 479479f..c065c92 100644 --- a/src/raspi/boot/start-cpu.cpp +++ b/src/raspi/boot/start-cpu.cpp @@ -18,13 +18,8 @@ namespace porpoise { namespace boot { default: PORPOISE_ABORT( "BUG: Received request to run function " - << set_width(16) - << set_fill(0) - << set_base(16) - << show_prefix(true) - << source + << reinterpret_cast(source) << " on invalid CPU: " - << reset() << id << "; valid values are 1, 2 or 3\n" ); diff --git a/src/shared/io/logging/manipulators.cpp b/src/shared/io/logging/manipulators.cpp index 45282f3..88b6914 100644 --- a/src/shared/io/logging/manipulators.cpp +++ b/src/shared/io/logging/manipulators.cpp @@ -103,3 +103,27 @@ log& operator<<(log& log_, hexupper manip) manip(log_); return log_; } + +log& operator<<(log& log_, void* p) +{ + auto old_field_width = log_.field_width(); + auto old_fill_char = log_.fill_char(); + auto old_base = log_.base(); + auto old_prefix = log_.prefix(); + auto old_boolalpha = log_.boolalpha(); + auto old_hexupper = log_.hexupper(); + log_ << set_width(2*sizeof(p)) + << set_fill('0') + << hexupper(true) + << set_base(16) + << show_prefix(true) + << reinterpret_cast(p) + ; + log_.field_width(old_field_width); + log_.fill_char(old_fill_char); + log_.base(old_base); + log_.prefix(old_prefix); + log_.boolalpha(old_boolalpha); + log_.hexupper(old_hexupper); + return log_; +} From 83581fe9310acfd78e96399b537f225ded9e8421 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 19:00:10 +0100 Subject: [PATCH 11/16] Set CPU frequency --- src/raspi/time/timespan.cpp | 9 +++++++++ src/shared/boot/cpu-main.cpp | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/raspi/time/timespan.cpp b/src/raspi/time/timespan.cpp index bd85a61..131b2ce 100644 --- a/src/raspi/time/timespan.cpp +++ b/src/raspi/time/timespan.cpp @@ -9,6 +9,15 @@ #define CYCLES_TO_MILLIS(CYCLES, HERTZ) ((CYCLES)*1000/(HERTZ)) namespace porpoise { namespace time { + struct hertz_setter + { + hertz_setter() + { + uint32_t freq; + timespan::cpu_hertz(freq); + } + } set_hertz; + uintmax_t timespan::_cpu_hertz = DEFAULT_CPU_HERTZ; void timespan::cpu_hertz(uintmax_t next) diff --git a/src/shared/boot/cpu-main.cpp b/src/shared/boot/cpu-main.cpp index f9e9705..802a58b 100644 --- a/src/shared/boot/cpu-main.cpp +++ b/src/shared/boot/cpu-main.cpp @@ -1,11 +1,12 @@ #include #include +#include using namespace porpoise::io::logging; namespace porpoise { namespace boot { void cpu_main(int id) { - PORPOISE_LOG_INFO("Hello from CPU " << id << '!'); + PORPOISE_LOG_INFO("Hello from CPU " << id << " (" << time::timespan::cpu_hertz()/1000000 << "MHz)"); } }} // porpoise::boot From fde10dfff1ea85068132b618b779fd7586d6700a Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 20:13:02 +0100 Subject: [PATCH 12/16] Ensure ctors & dtors are called, set CPU frequency --- Makefile | 7 ++-- src/raspi/time/timespan.cpp | 13 ++++--- src/shared/abi/crti.cpp | 26 ++++++++++++++ src/shared/abi/crtn.cpp | 4 +++ src/shared/boot/boot-kernel.cpp | 4 ++- tools/ld/raspi2.ld | 63 --------------------------------- tools/ld/raspi3.ld | 22 ++++-------- 7 files changed, 49 insertions(+), 90 deletions(-) create mode 100644 src/shared/abi/crti.cpp create mode 100644 src/shared/abi/crtn.cpp delete mode 100644 tools/ld/raspi2.ld diff --git a/Makefile b/Makefile index 5994a03..b371fd5 100644 --- a/Makefile +++ b/Makefile @@ -12,12 +12,13 @@ CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE_BASE) -Iinclude/shared/lib -Iin -D__$(MACHINE_BASE)__=$(MACHINE_VERSION)\ -ffreestanding -nostdlib -fno-exceptions -fno-rtti -fno-stack-protector\ -std=c++17 -Wall -Wextra +LD = $(PREFIX)-ld LDFLAGS = -Ttools/ld/$(MACHINE).ld -LIBRARIES = -lgcc +LIBRARIES = $(shell $(CXX) $(CXFLAGS) --print-file-name=libgcc.a) OBJCOPY = $(PREFIX)-objcopy EMU = qemu-system-aarch64 -EMUFLAGS = -M $(MACHINE),usb=on -smp 4 -m 512 -d guest_errors -display none +EMUFLAGS = -M $(MACHINE),usb=on -smp 4 -m 1G -d guest_errors -display none OBJECTS = $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/shared \( -name '*.cpp' -o -name '*.S' \))))\ $(patsubst src/%.S,obj/%.o,$(patsubst src/%.cpp,obj/%.o,$(shell find src/$(MACHINE_BASE) \( -name '*.cpp' -o -name '*.S' \)))) @@ -55,7 +56,7 @@ $(KERNEL_BINARY): $(KERNEL_ELF) $(KERNEL_ELF): $(OBJECTS) @echo "Linking `basename $@`" @mkdir -p `dirname $@` - @$(CXX) $(CXFLAGS) $(LDFLAGS) -o $@ $^ $(LIBRARIES) + @$(LD) $(LDFLAGS) -o $@ $^ $(LIBRARIES) run-qemu: $(KERNEL_ELF) $(EMU) $(EMUFLAGS) -serial stdio -kernel $(KERNEL_ELF) diff --git a/src/raspi/time/timespan.cpp b/src/raspi/time/timespan.cpp index 131b2ce..c55bd44 100644 --- a/src/raspi/time/timespan.cpp +++ b/src/raspi/time/timespan.cpp @@ -9,19 +9,18 @@ #define CYCLES_TO_MILLIS(CYCLES, HERTZ) ((CYCLES)*1000/(HERTZ)) namespace porpoise { namespace time { - struct hertz_setter + uintmax_t timespan::_cpu_hertz = DEFAULT_CPU_HERTZ; + + void timespan::cpu_hertz(uintmax_t next) { - hertz_setter() + if (next == 0) { uint32_t freq; + asm volatile("mrs %0, cntfrq_el0":"=r"(freq)); timespan::cpu_hertz(freq); + return; } - } set_hertz; - uintmax_t timespan::_cpu_hertz = DEFAULT_CPU_HERTZ; - - void timespan::cpu_hertz(uintmax_t next) - { _cpu_hertz = next; } diff --git a/src/shared/abi/crti.cpp b/src/shared/abi/crti.cpp new file mode 100644 index 0000000..b411421 --- /dev/null +++ b/src/shared/abi/crti.cpp @@ -0,0 +1,26 @@ +using fn_ptr = void(*)(); + +extern "C" fn_ptr _init_array_start[]; +extern "C" fn_ptr _init_array_end[]; + +extern "C" fn_ptr _fini_array_start[]; +extern "C" fn_ptr _fini_array_end[]; + +extern "C" void _init(void) +{ + for (auto fn = _init_array_start; fn < _init_array_end; fn++) + { + (*fn)(); + } +} + +extern "C" void _fini(void) +{ + for (auto fn = _fini_array_start; fn < _fini_array_end; fn++) + { + (*fn)(); + } +} + +fn_ptr _init_array_start[0] __attribute__((used, section(".init_array"), aligned(sizeof(fn_ptr)))) = {}; +fn_ptr _fini_array_start[0] __attribute__((used, section(".fini_array"), aligned(sizeof(fn_ptr)))) = {}; diff --git a/src/shared/abi/crtn.cpp b/src/shared/abi/crtn.cpp new file mode 100644 index 0000000..4a167a9 --- /dev/null +++ b/src/shared/abi/crtn.cpp @@ -0,0 +1,4 @@ +using fn_ptr = void(*)(); + +fn_ptr _init_array_end[0] __attribute__((used, section(".init_array"), aligned(sizeof(fn_ptr)))) = {}; +fn_ptr _fini_array_end[0] __attribute__((used, section(".fini_array"), aligned(sizeof(fn_ptr)))) = {}; diff --git a/src/shared/boot/boot-kernel.cpp b/src/shared/boot/boot-kernel.cpp index 268c81f..27c6af6 100644 --- a/src/shared/boot/boot-kernel.cpp +++ b/src/shared/boot/boot-kernel.cpp @@ -47,6 +47,8 @@ namespace porpoise { namespace boot { clear_bss(); _init(); + timespan::cpu_hertz(0); + static uart* uart0 = uart::init(uart::baud_rate::bd115200); static serial_sink uart0_sink(uart0, log_level::trace); log::add_sink(&uart0_sink); @@ -63,7 +65,7 @@ namespace porpoise { namespace boot { static void start_cpu(int id) { // Wait for CPU 0 initialisation. - while (!cpu0_init_complete) + while (!cpu0_init_complete.load()) { delay(timespan::millis(1)); } diff --git a/tools/ld/raspi2.ld b/tools/ld/raspi2.ld deleted file mode 100644 index 9126c4d..0000000 --- a/tools/ld/raspi2.ld +++ /dev/null @@ -1,63 +0,0 @@ -ENTRY(_start) -SECTIONS -{ - . = 0x8000; - __kernel_start__ = .; - .text BLOCK(4K) : ALIGN(4K) { - __code_start__ = .; - *(.text.boot) - *(.text*) - *(.gnu.linkonce.t*) - __code_end__ = .; - } - .init_array : - { - crti.o(.init_array) - KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*))) - KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array)) - crtn.o(.init_array) - } - .fini_array : - { - crti.o(.fini_array) - KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*))) - KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array)) - crtn.o(.fini_array) - } - .data BLOCK(4K) : ALIGN(4K) { - __data_start__ = .; - *(.data) - *(.ctors*) - *(.dtors*) - *(.gnu.linkonce.d*) - *(.jcr) - __data_end__ = .; - } - .rodata BLOCK(4K) : ALIGN(4K) { - __rodata_start__ = .; - *(.rodata*) - *(.gnu.linkonce.r*) - __rodata_end__ = .; - } - .eh_frame BLOCK(4K) : ALIGN(4K) { - start_eh_frame = .; - __eh_frame_start__ = .; - *(.eh_frame) - *(.gnu.linkonce.e*) - QUAD(0) - __eh_frame_end__ = .; - } - .bss BLOCK(4K) : ALIGN(4K) { - __bss_start__ = .; - *(.bss) - *(.gnu.linkonce.b*) - *(COMMON) - *(.common) - *(.gnu.linkonce.c*) - __bss_end__ = .; - } - __kernel_end__ = .; - /DISCARD/ : { - *(.comment) - } -} diff --git a/tools/ld/raspi3.ld b/tools/ld/raspi3.ld index 981d0c2..8e9a95e 100644 --- a/tools/ld/raspi3.ld +++ b/tools/ld/raspi3.ld @@ -8,27 +8,13 @@ SECTIONS KEEP(*(.text.boot)) *(.text*) *(.gnu.linkonce.t*) + *(.init) + *(.fini) __code_end__ = .; } - .init_array : - { - crti.o(.init_array) - KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*))) - KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array)) - crtn.o(.init_array) - } - .fini_array : - { - crti.o(.fini_array) - KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*))) - KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array)) - crtn.o(.fini_array) - } .data BLOCK(4K) : ALIGN(4K) { __data_start__ = .; *(.data) - *(.ctors*) - *(.dtors*) *(.gnu.linkonce.d*) *(.jcr) __data_end__ = .; @@ -37,6 +23,10 @@ SECTIONS __rodata_start__ = .; *(.rodata*) *(.gnu.linkonce.r*) + *(.init_array) + *(.ctors*) + *(.fini_array) + *(.dtors*) __rodata_end__ = .; } .eh_frame BLOCK(4K) : ALIGN(4K) { From d4a08eb095f91949ee4603e3a9bbd97f7fcd69b4 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 10 Jun 2020 21:15:07 +0100 Subject: [PATCH 13/16] Fix a bug in log which allowed emitting from a moved instance --- src/shared/io/logging/log.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp index a63fcdd..4522d73 100644 --- a/src/shared/io/logging/log.cpp +++ b/src/shared/io/logging/log.cpp @@ -3,6 +3,7 @@ #include +#include #include #include @@ -131,7 +132,7 @@ namespace porpoise { namespace io { namespace logging { log::~log() { - if (!_moved) + if (!_moved.load()) { emit("\r\n"); _lock.release(); @@ -156,6 +157,11 @@ namespace porpoise { namespace io { namespace logging { void log::emit(char c) { + if (_moved.load()) + { + PORPOISE_ABORT("Can't emit from a moved instance of log"); + } + if (_current_level < _minimum_level) { return; @@ -171,6 +177,11 @@ namespace porpoise { namespace io { namespace logging { void log::emit(const char* string) { + if (_moved.load()) + { + PORPOISE_ABORT("Can't emit from a moved instance of log"); + } + if (_current_level < _minimum_level) { return; @@ -186,6 +197,11 @@ namespace porpoise { namespace io { namespace logging { void log::emit(intmax_t number) { + if (_moved.load()) + { + PORPOISE_ABORT("Can't emit from a moved instance of log"); + } + if (_current_level < _minimum_level) { return; @@ -204,6 +220,11 @@ namespace porpoise { namespace io { namespace logging { void log::emit(uintmax_t number) { + if (_moved.load()) + { + PORPOISE_ABORT("Can't emit from a moved instance of log"); + } + if (_current_level < _minimum_level) { return; From ae728757c11e9cb535f4559a7800b6df95a983c3 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Thu, 11 Jun 2020 09:18:59 +0100 Subject: [PATCH 14/16] Add semaphore & a log_internal_state object to reduce copying when log instances are passed by rvalue reference --- Makefile | 49 ++-- include/shared/lib/move.hpp | 11 + include/shared/porpoise/heap.hpp | 21 +- include/shared/porpoise/io/logging/log.hpp | 57 +++-- .../porpoise/io/logging/manipulators.hpp | 26 +-- .../porpoise/io/logging/sinks/serial-sink.hpp | 4 +- include/shared/porpoise/sync/atomic.hpp | 18 +- include/shared/porpoise/sync/basic-lock.hpp | 5 + include/shared/porpoise/sync/lock-guard.hpp | 27 ++- include/shared/porpoise/sync/semaphore.hpp | 34 +++ include/shared/porpoise/sync/spinlock.hpp | 30 +-- include/shared/porpoise/time/timespan.hpp | 2 +- src/raspi/boot/start.S | 5 + src/shared/abi/crti.cpp | 22 +- src/shared/heap.cpp | 45 +++- src/shared/io/logging/log.cpp | 212 ++++++++++-------- src/shared/sync/lock-guard.cpp | 33 +++ src/shared/sync/semaphore.cpp | 46 ++++ src/shared/sync/spinlock.cpp | 43 ++++ tools/ld/raspi3.ld | 1 + 20 files changed, 493 insertions(+), 198 deletions(-) create mode 100644 include/shared/lib/move.hpp create mode 100644 include/shared/porpoise/sync/semaphore.hpp create mode 100644 src/shared/sync/lock-guard.cpp create mode 100644 src/shared/sync/semaphore.cpp create mode 100644 src/shared/sync/spinlock.cpp diff --git a/Makefile b/Makefile index b371fd5..5600ec5 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,18 @@ +DEBUG := 1 + MACHINE_BASE = raspi MACHINE_VERSION = 3 MACHINE = $(MACHINE_BASE)$(MACHINE_VERSION) KERNEL_ELF = bin/porpoise.elf +KERNEL_DEBUG = bin/porpoise.debug KERNEL_BINARY = bin/sdcard/boot/kernel8.img PREFIX := aarch64-linux-gnu CXX = $(PREFIX)-g++ -CXFLAGS = -Iinclude/shared -Iinclude/$(MACHINE_BASE) -Iinclude/shared/lib -Iinclude/$(MACHINE_BASE)/lib\ +INCLUDES = -Iinclude/shared -Iinclude/$(MACHINE_BASE) -Iinclude/shared/lib -Iinclude/$(MACHINE_BASE)/lib +CXFLAGS = $(INCLUDES)\ -D__$(MACHINE_BASE)__=$(MACHINE_VERSION)\ -ffreestanding -nostdlib -fno-exceptions -fno-rtti -fno-stack-protector\ -std=c++17 -Wall -Wextra @@ -34,38 +38,43 @@ CXFLAGS += -g0 -O3 endif obj/%.o: src/%.cpp - @echo "Compiling `basename $^`" + @echo "<<< `basename $^`" @mkdir -p `dirname $@` - @$(CXX) $(CXFLAGS) -c -o $@ $^ + @$(CXX) $(CXFLAGS) -S -o $@.S $^ + @$(CXX) $(CXFLAGS) -c -o $@ $@.S obj/%.o: src/%.S - @echo "Assembling `basename $^`" + @echo "<<< `basename $^`" @mkdir -p `dirname $@` @$(CXX) $(CXFLAGS) -D__ASM_SOURCE__ -c -o $@ $^ all: $(KERNEL_BINARY) clean: - @rm -rf obj/* $(KERNEL_BINARY) $(KERNEL_ELF) - -$(KERNEL_BINARY): $(KERNEL_ELF) - @echo "Converting to binary `basename $@`" - @mkdir -p `dirname $@` - @$(OBJCOPY) -O binary $< $@ + @rm -rf obj/* $(KERNEL_BINARY) $(KERNEL_ELF) $(KERNEL_DEBUG) -$(KERNEL_ELF): $(OBJECTS) - @echo "Linking `basename $@`" +$(KERNEL_BINARY): $(OBJECTS) @mkdir -p `dirname $@` - @$(LD) $(LDFLAGS) -o $@ $^ $(LIBRARIES) - -run-qemu: $(KERNEL_ELF) + @echo ">>> `basename $(KERNEL_ELF)`" + @$(LD) $(LDFLAGS) -o $(KERNEL_ELF) $^ $(LIBRARIES) + @ + @echo ">>> `basename $(KERNEL_DEBUG)`" + @cp $(KERNEL_ELF) $(KERNEL_DEBUG) + @$(OBJCOPY) --strip-all $(KERNEL_ELF) + @ + @echo ">>> `basename $@`" + @$(OBJCOPY) -O binary $(KERNEL_ELF) $@ + +run-qemu: $(KERNEL_BINARY) $(EMU) $(EMUFLAGS) -serial stdio -kernel $(KERNEL_ELF) -mon-qemu: $(KERNEL_ELF) +mon-qemu: $(KERNEL_BINARY) $(EMU) $(EMUFLAGS) -monitor stdio -kernel $(KERNEL_ELF) -debug-qemu: $(KERNEL_ELF) - $(EMU) $(EMUFLAGS) -kernel $(KERNEL_ELF) -s -S & - gdb-multiarch -s $(KERNEL_ELF) -q -ex "set architecture aarch64" -ex "b _start" -ex "target remote :1234" +debug-qemu: $(KERNEL_BINARY) + $(EMU) $(EMUFLAGS) -kernel $(KERNEL_ELF) -s -S -monitor stdio + +check: + @cppcheck --quiet --enable=all $(INCLUDES) `find src -name "*.cpp"` `find include -name "*.hpp"` 2>&1 | grep -v "never used" -.PHONY: run-qemu debug-qemu $(KERNEL_BINARY) $(KERNEL_ELF) +.PHONY: run-qemu debug-qemu $(KERNEL_BINARY) diff --git a/include/shared/lib/move.hpp b/include/shared/lib/move.hpp new file mode 100644 index 0000000..aca3b8f --- /dev/null +++ b/include/shared/lib/move.hpp @@ -0,0 +1,11 @@ +#pragma once + +template struct remove_reference {typedef T type;}; +template struct remove_reference {typedef T type;}; +template struct remove_reference {typedef T type;}; + +template +typename remove_reference::type&& move(T&& t) +{ + return static_cast::type&&>(t); +} diff --git a/include/shared/porpoise/heap.hpp b/include/shared/porpoise/heap.hpp index ad65735..789455b 100644 --- a/include/shared/porpoise/heap.hpp +++ b/include/shared/porpoise/heap.hpp @@ -11,15 +11,30 @@ namespace porpoise { enum class oom_behaviour { abort, - null + return_null, + default_ = abort }; + static constexpr size_t DEFAULT_ALIGNMENT = sizeof(uint64_t); + static void init(); + static void* allocate(size_t bytes); + + static void* allocate( + size_t bytes, + size_t alignment + ); + + static void* allocate( + size_t bytes, + oom_behaviour behaviour + ); + static void* allocate( size_t bytes, - size_t alignment = sizeof(uint64_t), - oom_behaviour behaviour = oom_behaviour::abort + size_t alignment, + oom_behaviour behaviour ); static void deallocate(void* ptr); diff --git a/include/shared/porpoise/io/logging/log.hpp b/include/shared/porpoise/io/logging/log.hpp index a4a6290..945e4b9 100644 --- a/include/shared/porpoise/io/logging/log.hpp +++ b/include/shared/porpoise/io/logging/log.hpp @@ -1,9 +1,9 @@ #pragma once #include +#include -#include -#include +#include #define PORPOISE_LOG_TRACE(...) do {auto __log = ::porpoise::io::logging::log::trace(); __log << __VA_ARGS__;} while (0) #define PORPOISE_LOG_DEBUG(...) do {auto __log = ::porpoise::io::logging::log::debug(); __log << __VA_ARGS__;} while (0) @@ -65,7 +65,7 @@ namespace porpoise { namespace io { namespace logging { void operator=(log&& other); - ~log(); + virtual ~log(); void emit(char c); @@ -77,6 +77,8 @@ namespace porpoise { namespace io { namespace logging { void emit(uintmax_t number); + void flush(); + uint8_t field_width() const; char fill_char() const; @@ -105,28 +107,43 @@ namespace porpoise { namespace io { namespace logging { void operator=(const log&) = delete; protected: - log(log_level level); + explicit log(log_level level); private: - static log_level _minimum_level; - static log_sink* _sinks[MAX_SINK]; - static int _num_sinks; - static sync::spinlock _lock; + static log_level _minimum_level; + static log_sink* _sinks[MAX_SINK]; + static int _num_sinks; + static sync::spinlock _sink_lock; static log get(log_level level, const char* lvlstr); - log_level _current_level; - uint8_t _field_width; - char _fill_char; - uint8_t _base; - bool _prefix : 1; - bool _boolalpha : 1; - bool _hexupper : 1; - sync::atomic _moved; - - void emit_all(char c); - - void emit_all(const char* s); + struct log_internal_state + { + log_level current_level; + uint8_t field_width; + char fill_char; + uint8_t base; + bool prefix; + bool boolalpha; + bool hexupper; + + explicit log_internal_state(log_level level) + : current_level(level) + , field_width(0) + , fill_char(' ') + , base(10) + , prefix(false) + , boolalpha(false) + , hexupper(false) + { + } + } * _state; + + bool is_active_instance() const; + + void internal_emit(char c); + + void internal_emit(const char* s); }; }}} // porpoise::io::logging diff --git a/include/shared/porpoise/io/logging/manipulators.hpp b/include/shared/porpoise/io/logging/manipulators.hpp index f6f270d..e9044ba 100644 --- a/include/shared/porpoise/io/logging/manipulators.hpp +++ b/include/shared/porpoise/io/logging/manipulators.hpp @@ -8,7 +8,7 @@ namespace porpoise { namespace io { namespace logging { struct reset : manipulator { - void operator()(log& log_) + void operator()(log& log_) override { log_.base(10); log_.prefix(false); @@ -21,48 +21,48 @@ namespace porpoise { namespace io { namespace logging { struct set_width : manipulator { - set_width(uint8_t value) : _value(value) {} - void operator()(log& log_) {log_.field_width(_value);} + explicit set_width(uint8_t value) : _value(value) {} + void operator()(log& log_) override {log_.field_width(_value);} private: uint8_t _value; }; struct set_fill : manipulator { - set_fill(char value) : _value(value) {} - void operator()(log& log_) {log_.fill_char(_value);} + explicit set_fill(char value) : _value(value) {} + void operator()(log& log_) override {log_.fill_char(_value);} private: char _value; }; struct set_base : manipulator { - set_base(uint8_t value) : _value(value) {} - void operator()(log& log_) {log_.base(_value);} + explicit set_base(uint8_t value) : _value(value) {} + void operator()(log& log_) override {log_.base(_value);} private: uint8_t _value; }; struct show_prefix : manipulator { - show_prefix(bool value) : _value(value) {} - void operator()(log& log_) {log_.prefix(_value);} + explicit show_prefix(bool value) : _value(value) {} + void operator()(log& log_) override {log_.prefix(_value);} private: bool _value; }; struct boolalpha : manipulator { - boolalpha(bool value) : _value(value) {} - void operator()(log& log_) {log_.boolalpha(_value);} + explicit boolalpha(bool value) : _value(value) {} + void operator()(log& log_) override {log_.boolalpha(_value);} private: bool _value; }; struct hexupper : manipulator { - hexupper(bool value) : _value(value) {} - void operator()(log& log_) {log_.hexupper(_value);} + explicit hexupper(bool value) : _value(value) {} + void operator()(log& log_) override {log_.hexupper(_value);} private: bool _value; }; diff --git a/include/shared/porpoise/io/logging/sinks/serial-sink.hpp b/include/shared/porpoise/io/logging/sinks/serial-sink.hpp index d6b24b2..7a7276c 100644 --- a/include/shared/porpoise/io/logging/sinks/serial-sink.hpp +++ b/include/shared/porpoise/io/logging/sinks/serial-sink.hpp @@ -8,9 +8,9 @@ struct serial_sink : log_sink { serial_sink(uart* dev, log_level min_level); - void emit(log_level level, const char* event); + void emit(log_level level, const char* event) override; - void emit(log_level level, char c); + void emit(log_level level, char c) override; private: uart* _dev; log_level _min_level; diff --git a/include/shared/porpoise/sync/atomic.hpp b/include/shared/porpoise/sync/atomic.hpp index 8f511bc..5441068 100644 --- a/include/shared/porpoise/sync/atomic.hpp +++ b/include/shared/porpoise/sync/atomic.hpp @@ -78,7 +78,7 @@ namespace porpoise { namespace sync { /// Atomic wrapper. template - struct atomic : public ::porpoise::sync::internal::atomic_base + struct atomic : ::porpoise::sync::internal::atomic_base { using value_type = typename ::porpoise::sync::internal::atomic_base::value_type; using reference = typename ::porpoise::sync::internal::atomic_base::reference; @@ -88,18 +88,6 @@ namespace porpoise { namespace sync { explicit atomic(const_reference value) { this->_value = value; } explicit atomic(rvalue_reference value) { this->_value = value; } - - atomic& operator=(const value_type& other) - { - this->store(other); - return *this; - } - - atomic& operator=(const atomic& other) - { - this->store(other.load()); - return *this; - } }; #define NUMERIC_LIST(F) \ @@ -139,6 +127,10 @@ namespace porpoise { namespace sync { { \ return __atomic_sub_fetch(&_value, 1, static_cast(memory_order::seq_cst)); \ } \ + void operator=(const value_type& other) \ + { \ + this->store(other); \ + } \ }; NUMERIC_LIST(ATOMIC_SPEC) diff --git a/include/shared/porpoise/sync/basic-lock.hpp b/include/shared/porpoise/sync/basic-lock.hpp index 514064d..431e649 100644 --- a/include/shared/porpoise/sync/basic-lock.hpp +++ b/include/shared/porpoise/sync/basic-lock.hpp @@ -8,5 +8,10 @@ namespace porpoise { namespace sync { /// Release the lock. virtual void release() = 0; + + basic_lock(basic_lock&) = delete; + void operator=(basic_lock&) = delete; + protected: + basic_lock() = default; }; }} // porpoise::sync diff --git a/include/shared/porpoise/sync/lock-guard.hpp b/include/shared/porpoise/sync/lock-guard.hpp index ea721d2..07bcebd 100644 --- a/include/shared/porpoise/sync/lock-guard.hpp +++ b/include/shared/porpoise/sync/lock-guard.hpp @@ -1,19 +1,26 @@ #pragma once +#include + +#include + +#include #include +#include namespace porpoise { namespace sync { struct lock_guard { - lock_guard(basic_lock& lock) : _lock(lock) - { - _lock.acquire(); - } - - virtual ~lock_guard() - { - _lock.release(); - } + explicit lock_guard(spinlock& lock); + + virtual ~lock_guard(); + + lock_guard(lock_guard&& other); + + void operator=(lock_guard&& other); + + lock_guard(lock_guard& other) = delete; + void operator=(lock_guard& other) = delete; private: - basic_lock& _lock; + spinlock* _lock; }; }} // porpoise::sync diff --git a/include/shared/porpoise/sync/semaphore.hpp b/include/shared/porpoise/sync/semaphore.hpp new file mode 100644 index 0000000..8daf6cb --- /dev/null +++ b/include/shared/porpoise/sync/semaphore.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace porpoise { namespace sync { + /// A semaphore/atomic counter. + struct semaphore { + static constexpr int UNBOUNDED = -1; + + /// Construct from maximum count. + semaphore(int max = UNBOUNDED); + + semaphore(const semaphore&) = delete; + + /// Acquire the semaphore/increment the counter and return the count *after* the increment. If the current + /// count is >= the maximum, block until it is lower. If max < 0, never block. + int acquire(); + + /// Increment the counter without acquiring it. Use this with max=1 to implement a semaphore and reference count + /// in one. + int increment(); + + /// Release the semaphore/decrement the counter and return the count *after* the decrement. + int release(); + + /// Return the current count. + int count() const; + private: + int _max; + spinlock _lock; + int _count; + }; +}} \ No newline at end of file diff --git a/include/shared/porpoise/sync/spinlock.hpp b/include/shared/porpoise/sync/spinlock.hpp index abae87c..15ff3d1 100644 --- a/include/shared/porpoise/sync/spinlock.hpp +++ b/include/shared/porpoise/sync/spinlock.hpp @@ -5,21 +5,21 @@ namespace porpoise { namespace sync { struct spinlock : public basic_lock { - bool acquire() final - { - while (_flag.test_and_set()) - { - // Do nothing. - } - - return true; - } - - void release() final - { - _flag.clear(); - } + spinlock(); + + spinlock(spinlock&&); + + void operator=(spinlock&&); + + bool acquire() override; + + void release() override; + + spinlock(spinlock&) = delete; + + void operator=(spinlock&) = delete; private: - atomic_flag _flag; + atomic_flag _lock; + atomic _moved; }; }} // porpoise::sync diff --git a/include/shared/porpoise/time/timespan.hpp b/include/shared/porpoise/time/timespan.hpp index bc5d4dd..0c26eb3 100644 --- a/include/shared/porpoise/time/timespan.hpp +++ b/include/shared/porpoise/time/timespan.hpp @@ -18,7 +18,7 @@ namespace porpoise { namespace time { static timespan millis(uintmax_t value); - timespan(uintmax_t cycles); + explicit timespan(uintmax_t cycles); uintmax_t cycles() const; diff --git a/src/raspi/boot/start.S b/src/raspi/boot/start.S index 4757218..7218719 100644 --- a/src/raspi/boot/start.S +++ b/src/raspi/boot/start.S @@ -13,6 +13,8 @@ _start: mrs x1, mpidr_el1 and x1, x1, #3 + cbnz x1, _hang + mov x2, BOOT_ORIGIN // For CPU 0, boot immediately with sp=BOOT_ORIGIN, x0=DTB and x1=0. @@ -27,3 +29,6 @@ N: mov x3, STACK_SIZE mov sp, x2 b boot_kernel +_hang: + b _hang + diff --git a/src/shared/abi/crti.cpp b/src/shared/abi/crti.cpp index b411421..43e0ad8 100644 --- a/src/shared/abi/crti.cpp +++ b/src/shared/abi/crti.cpp @@ -1,3 +1,5 @@ +#include + using fn_ptr = void(*)(); extern "C" fn_ptr _init_array_start[]; @@ -6,21 +8,25 @@ extern "C" fn_ptr _init_array_end[]; extern "C" fn_ptr _fini_array_start[]; extern "C" fn_ptr _fini_array_end[]; -extern "C" void _init(void) +extern "C" void _init() { - for (auto fn = _init_array_start; fn < _init_array_end; fn++) + auto p = reinterpret_cast(_init_array_start); + auto q = reinterpret_cast(_init_array_end); + for (; p < q; p++) { - (*fn)(); + (*reinterpret_cast(p))(); } } -extern "C" void _fini(void) +extern "C" void _fini() { - for (auto fn = _fini_array_start; fn < _fini_array_end; fn++) + auto p = reinterpret_cast(_fini_array_start); + auto q = reinterpret_cast(_fini_array_end); + for (; p < q; p++) { - (*fn)(); + (*reinterpret_cast(p))(); } } -fn_ptr _init_array_start[0] __attribute__((used, section(".init_array"), aligned(sizeof(fn_ptr)))) = {}; -fn_ptr _fini_array_start[0] __attribute__((used, section(".fini_array"), aligned(sizeof(fn_ptr)))) = {}; +fn_ptr _init_array_start[] __attribute__((used, section(".init_array"), aligned(sizeof(fn_ptr)))) = {}; +fn_ptr _fini_array_start[] __attribute__((used, section(".fini_array"), aligned(sizeof(fn_ptr)))) = {}; diff --git a/src/shared/heap.cpp b/src/shared/heap.cpp index edd4efe..a90c442 100644 --- a/src/shared/heap.cpp +++ b/src/shared/heap.cpp @@ -9,17 +9,40 @@ extern uint8_t __kernel_end__[]; namespace porpoise { + using namespace sync; + uintptr_t heap::_curr; - sync::spinlock heap::_lock; + spinlock heap::_lock; void heap::init() { _curr = reinterpret_cast(__kernel_end__); } + void* heap::allocate(size_t bytes) + { + return allocate(bytes, DEFAULT_ALIGNMENT, oom_behaviour::default_); + } + + void* heap::allocate( + size_t bytes, + size_t alignment + ) + { + return allocate(bytes, alignment, oom_behaviour::default_); + } + + void* heap::allocate( + size_t bytes, + oom_behaviour behaviour + ) + { + return allocate(bytes, DEFAULT_ALIGNMENT, behaviour); + } + void* heap::allocate(size_t bytes, size_t alignment, oom_behaviour behaviour) { - sync::lock_guard guard(_lock); + lock_guard guard(_lock); _curr += _curr & ~(alignment - 1); if (_curr + bytes > TOP) @@ -29,9 +52,11 @@ namespace porpoise { PORPOISE_ABORT("Out of memory"); } + PORPOISE_LOG_WARN("Out of memory"); return nullptr; } + PORPOISE_LOG_TRACE("Allocated " << bytes << "bytes"); auto p = _curr; _curr += bytes; return reinterpret_cast(p); @@ -48,6 +73,11 @@ void* operator new(size_t size) return porpoise::heap::allocate(size); } +void* operator new[](size_t size) +{ + return porpoise::heap::allocate(size); +} + void* operator new(size_t size, void* ptr) { PORPOISE_UNUSED(size); @@ -59,6 +89,17 @@ void operator delete(void* ptr) porpoise::heap::deallocate(ptr); } +void operator delete[](void* ptr) +{ + porpoise::heap::deallocate(ptr); +} + +void operator delete[](void* ptr, size_t size) +{ + PORPOISE_UNUSED(size); + porpoise::heap::deallocate(ptr); +} + void operator delete(void* ptr, size_t size) { PORPOISE_UNUSED(size); diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp index 4522d73..65dc69f 100644 --- a/src/shared/io/logging/log.cpp +++ b/src/shared/io/logging/log.cpp @@ -1,25 +1,28 @@ -#include #include +#include #include #include +#include #include #include +#include +#include namespace porpoise { namespace io { namespace logging { - using time::timespan; - using sync::spinlock; - using sync::atomic_flag; + using namespace time; + using namespace sync; - log_level log::_minimum_level; - log_sink* log::_sinks[MAX_SINK]; - int log::_num_sinks; - sync::spinlock log::_lock; + log_level log::_minimum_level; + log_sink* log::_sinks[MAX_SINK]; + int log::_num_sinks; + spinlock log::_sink_lock; log log::get(log_level level, const char* lvlstr) { - auto inst = log(level); + _sink_lock.acquire(); + log inst(level); auto millis = timespan::get_program_counter().millis(); auto secs = millis/1000; millis -= secs*1000; @@ -68,6 +71,7 @@ namespace porpoise { namespace io { namespace logging { return false; } + lock_guard guard(_sink_lock); if (_num_sinks >= MAX_SINK) { return false; @@ -93,123 +97,94 @@ namespace porpoise { namespace io { namespace logging { } log::log(log_level level) - : _current_level(level) - , _field_width(0) - , _fill_char(' ') - , _base(10) - , _prefix(false) - , _hexupper(false) - , _moved(false) { - _lock.acquire(); + _state = new log_internal_state(level); } log::log(log&& other) - : _current_level(other._current_level) - , _field_width(other._field_width) - , _fill_char(other._fill_char) - , _base(other._base) - , _prefix(other._prefix) - , _boolalpha(other._boolalpha) - , _hexupper(other._hexupper) - , _moved(other._moved.load()) { - other._moved.store(true); + _state = other._state; + other._state = nullptr; } void log::operator=(log&& other) { - _current_level = other._current_level; - _field_width = other._field_width; - _fill_char = other._fill_char; - _base = other._base; - _prefix = other._prefix; - _boolalpha = other._boolalpha; - _hexupper = other._hexupper; - _moved = other._moved; - other._moved.store(true); + _state = other._state; + other._state = nullptr; + } + + bool log::is_active_instance() const + { + return _state != nullptr; } log::~log() { - if (!_moved.load()) + if (is_active_instance()) { - emit("\r\n"); - _lock.release(); + internal_emit("\r\n"); + delete _state; + _state = nullptr; + _sink_lock.release(); } } - void log::emit_all(char c) + void log::internal_emit(const char* s) { for (auto i = 0; i < _num_sinks; i++) { - _sinks[i]->emit(_current_level, c); + _sinks[i]->emit(_state->current_level, s); } } - void log::emit_all(const char* s) + void log::internal_emit(char c) { for (auto i = 0; i < _num_sinks; i++) { - _sinks[i]->emit(_current_level, s); + _sinks[i]->emit(_state->current_level, c); } } void log::emit(char c) { - if (_moved.load()) - { - PORPOISE_ABORT("Can't emit from a moved instance of log"); - } - - if (_current_level < _minimum_level) + if (!is_active_instance() || _state->current_level < _minimum_level) { return; } - for (auto j = 1; j < _field_width; j++) + for (auto j = 1; j < _state->field_width; j++) { - emit_all(_fill_char); + internal_emit(_state->fill_char); } - emit_all(c); + internal_emit(c); } void log::emit(const char* string) { - if (_moved.load()) - { - PORPOISE_ABORT("Can't emit from a moved instance of log"); - } - - if (_current_level < _minimum_level) + if (!is_active_instance() || _state->current_level < _minimum_level) { return; } - for (auto j = strlen(string); j < _field_width; j++) + for (auto j = 1; j < _state->field_width; j++) { - emit_all(_fill_char); + internal_emit(_state->fill_char); } - emit_all(string); + internal_emit(string); } void log::emit(intmax_t number) { - if (_moved.load()) - { - PORPOISE_ABORT("Can't emit from a moved instance of log"); - } - - if (_current_level < _minimum_level) + if (!is_active_instance() || _state->current_level < _minimum_level) { return; } if (number < 0) { - emit_all('-'); + internal_emit('-'); emit(static_cast(-number)); } else @@ -220,12 +195,7 @@ namespace porpoise { namespace io { namespace logging { void log::emit(uintmax_t number) { - if (_moved.load()) - { - PORPOISE_ABORT("Can't emit from a moved instance of log"); - } - - if (_current_level < _minimum_level) + if (!is_active_instance() || _state->current_level < _minimum_level) { return; } @@ -234,10 +204,10 @@ namespace porpoise { namespace io { namespace logging { char buffer[DIGIT_MAX]; auto p = buffer; auto q = buffer + DIGIT_MAX; - auto alpha = _hexupper ? 'A' : 'a'; + auto alpha = _state->hexupper ? 'A' : 'a'; do { - auto r = number%_base; + auto r = number%_state->base; if (r < 10) { *p++ = '0' + r; @@ -247,82 +217,142 @@ namespace porpoise { namespace io { namespace logging { *p++ = alpha + r - 10; } - number /= _base; + number /= _state->base; } while (number && p < q); - for (auto count = p - buffer; count < _field_width; count++) + for (auto count = p - buffer; count < _state->field_width; count++) { - emit_all(_fill_char); + internal_emit(_state->fill_char); } while (p-- > buffer) { - emit_all(*p); + internal_emit(*p); } } uint8_t log::field_width() const { - return _field_width; + if (!is_active_instance()) + { + return 0; + } + + return _state->field_width; } char log::fill_char() const { - return _fill_char; + if (!is_active_instance()) + { + return 0; + } + + return _state->fill_char; } uint8_t log::base() const { - return _base; + if (!is_active_instance()) + { + return 0; + } + + return _state->base; } bool log::prefix() const { - return _prefix; + if (!is_active_instance()) + { + return false; + } + + return _state->prefix; } bool log::boolalpha() const { - return _boolalpha; + if (!is_active_instance()) + { + return false; + } + + return _state->boolalpha; } bool log::hexupper() const { - return _hexupper; + if (!is_active_instance()) + { + return false; + } + + return _state->hexupper; } void log::field_width(uint8_t next) { - _field_width = next; + if (!is_active_instance()) + { + return; + } + + _state->field_width = next; } void log::fill_char(char next) { - _fill_char = next; + if (!is_active_instance()) + { + return; + } + + _state->fill_char = next; } void log::base(uint8_t next) { + if (!is_active_instance()) + { + return; + } + if (next < 2) { return; } - _base = next; + _state->base = next; } void log::prefix(bool next) { - _prefix = next; + if (!is_active_instance()) + { + return; + } + + _state->prefix = next; } void log::boolalpha(bool next) { - _boolalpha = next; + if (!is_active_instance()) + { + return; + } + + _state->boolalpha = next; } void log::hexupper(bool next) { - _hexupper = next; + if (!is_active_instance()) + { + return; + } + + _state->hexupper = next; } }}} // porpoise::io::logging diff --git a/src/shared/sync/lock-guard.cpp b/src/shared/sync/lock-guard.cpp new file mode 100644 index 0000000..1913b52 --- /dev/null +++ b/src/shared/sync/lock-guard.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +namespace porpoise { namespace sync { + lock_guard::lock_guard(spinlock& lock) : _lock(&lock) + { + _lock->acquire(); + } + + lock_guard::~lock_guard() + { + if (_lock != nullptr) + { + _lock->release(); + } + } + + lock_guard::lock_guard(lock_guard&& other) + { + _lock = other._lock; + other._lock = nullptr; + } + + void lock_guard::operator=(lock_guard&& other) + { + _lock = other._lock; + other._lock = nullptr; + } +}} // porpoise::sync diff --git a/src/shared/sync/semaphore.cpp b/src/shared/sync/semaphore.cpp new file mode 100644 index 0000000..0694c4f --- /dev/null +++ b/src/shared/sync/semaphore.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +namespace porpoise { namespace sync { + semaphore::semaphore(int max) + : _max(max) + , _count(0) + { + } + + int semaphore::acquire() + { + while (true) + { + lock_guard guard(_lock); + if (_max < 0 || _count < _max) + { + return ++_count; + } + } + } + + int semaphore::increment() + { + lock_guard guard(_lock); + return ++_count; + } + + int semaphore::release() + { + lock_guard guard(_lock); + _count--; + if (_count < 0) + { + _count = 0; + } + + return _count; + } + + int semaphore::count() const + { + return _count; + } +}} // porpoise::sync diff --git a/src/shared/sync/spinlock.cpp b/src/shared/sync/spinlock.cpp new file mode 100644 index 0000000..e5b89e6 --- /dev/null +++ b/src/shared/sync/spinlock.cpp @@ -0,0 +1,43 @@ +#include + +namespace porpoise { namespace sync { + spinlock::spinlock() : _moved(false) + {} + + spinlock::spinlock(spinlock&& other) + : _lock(other._lock) + , _moved(other._moved) + { + other._moved.store(true); + } + + void spinlock::operator=(spinlock&& other) + { + _moved = other._moved; + _lock = other._lock; + other._moved.store(true); + } + + bool spinlock::acquire() + { + if (_moved) + { + return false; + } + + while (_lock.test_and_set()) + { + // Do nothing. + } + + return true; + } + + void spinlock::release() + { + if (!_moved) + { + _lock.clear(); + } + } +}} // porpoise::sync diff --git a/tools/ld/raspi3.ld b/tools/ld/raspi3.ld index 8e9a95e..a173e42 100644 --- a/tools/ld/raspi3.ld +++ b/tools/ld/raspi3.ld @@ -15,6 +15,7 @@ SECTIONS .data BLOCK(4K) : ALIGN(4K) { __data_start__ = .; *(.data) + *(.data.*) *(.gnu.linkonce.d*) *(.jcr) __data_end__ = .; From e00f8ab2ef3c841b5ed00ece62965e8dd3e0e172 Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Sat, 20 Jun 2020 00:35:15 +0100 Subject: [PATCH 15/16] Fix lock bug & get global constructors working --- include/shared/porpoise/io/logging/log.hpp | 2 +- include/shared/porpoise/sync/lock-guard.hpp | 4 ++-- include/shared/porpoise/sync/spinlock.hpp | 6 ++--- src/raspi/boot/start.S | 2 -- src/raspi/time/timespan.cpp | 19 +++++++++------- src/shared/abi/crti.cpp | 24 ++++++++++---------- src/shared/boot/boot-kernel.cpp | 4 ++-- src/shared/heap.cpp | 3 +-- src/shared/io/logging/log.cpp | 5 ++--- src/shared/sync/lock-guard.cpp | 19 ++-------------- tools/ld/raspi3.ld | 25 ++++++++++++--------- 11 files changed, 51 insertions(+), 62 deletions(-) diff --git a/include/shared/porpoise/io/logging/log.hpp b/include/shared/porpoise/io/logging/log.hpp index 945e4b9..556082f 100644 --- a/include/shared/porpoise/io/logging/log.hpp +++ b/include/shared/porpoise/io/logging/log.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #define PORPOISE_LOG_TRACE(...) do {auto __log = ::porpoise::io::logging::log::trace(); __log << __VA_ARGS__;} while (0) #define PORPOISE_LOG_DEBUG(...) do {auto __log = ::porpoise::io::logging::log::debug(); __log << __VA_ARGS__;} while (0) diff --git a/include/shared/porpoise/sync/lock-guard.hpp b/include/shared/porpoise/sync/lock-guard.hpp index 07bcebd..71fb335 100644 --- a/include/shared/porpoise/sync/lock-guard.hpp +++ b/include/shared/porpoise/sync/lock-guard.hpp @@ -10,7 +10,7 @@ namespace porpoise { namespace sync { struct lock_guard { - explicit lock_guard(spinlock& lock); + explicit lock_guard(basic_lock& lock); virtual ~lock_guard(); @@ -21,6 +21,6 @@ namespace porpoise { namespace sync { lock_guard(lock_guard& other) = delete; void operator=(lock_guard& other) = delete; private: - spinlock* _lock; + basic_lock* _lock; }; }} // porpoise::sync diff --git a/include/shared/porpoise/sync/spinlock.hpp b/include/shared/porpoise/sync/spinlock.hpp index 15ff3d1..528483f 100644 --- a/include/shared/porpoise/sync/spinlock.hpp +++ b/include/shared/porpoise/sync/spinlock.hpp @@ -4,16 +4,16 @@ #include namespace porpoise { namespace sync { - struct spinlock : public basic_lock { + struct spinlock : basic_lock { spinlock(); spinlock(spinlock&&); void operator=(spinlock&&); - bool acquire() override; + virtual bool acquire() override; - void release() override; + virtual void release() override; spinlock(spinlock&) = delete; diff --git a/src/raspi/boot/start.S b/src/raspi/boot/start.S index 7218719..7450f28 100644 --- a/src/raspi/boot/start.S +++ b/src/raspi/boot/start.S @@ -13,7 +13,6 @@ _start: mrs x1, mpidr_el1 and x1, x1, #3 - cbnz x1, _hang mov x2, BOOT_ORIGIN @@ -31,4 +30,3 @@ N: mov x3, STACK_SIZE _hang: b _hang - diff --git a/src/raspi/time/timespan.cpp b/src/raspi/time/timespan.cpp index c55bd44..7315d53 100644 --- a/src/raspi/time/timespan.cpp +++ b/src/raspi/time/timespan.cpp @@ -11,16 +11,19 @@ namespace porpoise { namespace time { uintmax_t timespan::_cpu_hertz = DEFAULT_CPU_HERTZ; + namespace internal { + static struct hertz_initializer { + hertz_initializer() + { + uint32_t freq = 0x600DC0DE; + asm volatile("mrs %0, cntfrq_el0":"=r"(freq)); + timespan::cpu_hertz(freq); + } + } instance; + } + void timespan::cpu_hertz(uintmax_t next) { - if (next == 0) - { - uint32_t freq; - asm volatile("mrs %0, cntfrq_el0":"=r"(freq)); - timespan::cpu_hertz(freq); - return; - } - _cpu_hertz = next; } diff --git a/src/shared/abi/crti.cpp b/src/shared/abi/crti.cpp index 43e0ad8..8012a0d 100644 --- a/src/shared/abi/crti.cpp +++ b/src/shared/abi/crti.cpp @@ -2,31 +2,31 @@ using fn_ptr = void(*)(); -extern "C" fn_ptr _init_array_start[]; -extern "C" fn_ptr _init_array_end[]; +extern "C" fn_ptr __init_array_start__[]; +extern "C" fn_ptr __init_array_end__[]; -extern "C" fn_ptr _fini_array_start[]; -extern "C" fn_ptr _fini_array_end[]; +extern "C" fn_ptr __fini_array_start__[]; +extern "C" fn_ptr __fini_array_end__[]; extern "C" void _init() { - auto p = reinterpret_cast(_init_array_start); - auto q = reinterpret_cast(_init_array_end); + auto p = __init_array_start__; + auto q = __init_array_end__; for (; p < q; p++) { - (*reinterpret_cast(p))(); + (*p)(); } } extern "C" void _fini() { - auto p = reinterpret_cast(_fini_array_start); - auto q = reinterpret_cast(_fini_array_end); + auto p = __fini_array_start__; + auto q = __fini_array_end__; for (; p < q; p++) { - (*reinterpret_cast(p))(); + (*p)(); } } -fn_ptr _init_array_start[] __attribute__((used, section(".init_array"), aligned(sizeof(fn_ptr)))) = {}; -fn_ptr _fini_array_start[] __attribute__((used, section(".fini_array"), aligned(sizeof(fn_ptr)))) = {}; +fn_ptr __init_array_start__[] __attribute__((used, section(".init_array"), aligned(sizeof(fn_ptr)))) = {}; +fn_ptr __fini_array_start__[] __attribute__((used, section(".fini_array"), aligned(sizeof(fn_ptr)))) = {}; diff --git a/src/shared/boot/boot-kernel.cpp b/src/shared/boot/boot-kernel.cpp index 27c6af6..b7591d3 100644 --- a/src/shared/boot/boot-kernel.cpp +++ b/src/shared/boot/boot-kernel.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -47,8 +48,7 @@ namespace porpoise { namespace boot { clear_bss(); _init(); - timespan::cpu_hertz(0); - + heap::init(); static uart* uart0 = uart::init(uart::baud_rate::bd115200); static serial_sink uart0_sink(uart0, log_level::trace); log::add_sink(&uart0_sink); diff --git a/src/shared/heap.cpp b/src/shared/heap.cpp index a90c442..47fa037 100644 --- a/src/shared/heap.cpp +++ b/src/shared/heap.cpp @@ -43,7 +43,6 @@ namespace porpoise { void* heap::allocate(size_t bytes, size_t alignment, oom_behaviour behaviour) { lock_guard guard(_lock); - _curr += _curr & ~(alignment - 1); if (_curr + bytes > TOP) { @@ -56,7 +55,7 @@ namespace porpoise { return nullptr; } - PORPOISE_LOG_TRACE("Allocated " << bytes << "bytes"); + // PORPOISE_LOG_TRACE("Allocated " << bytes << "bytes"); auto p = _curr; _curr += bytes; return reinterpret_cast(p); diff --git a/src/shared/io/logging/log.cpp b/src/shared/io/logging/log.cpp index 65dc69f..0cf601f 100644 --- a/src/shared/io/logging/log.cpp +++ b/src/shared/io/logging/log.cpp @@ -66,12 +66,12 @@ namespace porpoise { namespace io { namespace logging { bool log::add_sink(log_sink* sink) { + lock_guard guard(_sink_lock); if (sink == nullptr) { return false; } - lock_guard guard(_sink_lock); if (_num_sinks >= MAX_SINK) { return false; @@ -96,9 +96,8 @@ namespace porpoise { namespace io { namespace logging { return _num_sinks; } - log::log(log_level level) + log::log(log_level level) : _state(new log_internal_state(level)) { - _state = new log_internal_state(level); } log::log(log&& other) diff --git a/src/shared/sync/lock-guard.cpp b/src/shared/sync/lock-guard.cpp index 1913b52..e91a706 100644 --- a/src/shared/sync/lock-guard.cpp +++ b/src/shared/sync/lock-guard.cpp @@ -6,28 +6,13 @@ #include namespace porpoise { namespace sync { - lock_guard::lock_guard(spinlock& lock) : _lock(&lock) + lock_guard::lock_guard(basic_lock& lock) : _lock(&lock) { _lock->acquire(); } lock_guard::~lock_guard() { - if (_lock != nullptr) - { - _lock->release(); - } - } - - lock_guard::lock_guard(lock_guard&& other) - { - _lock = other._lock; - other._lock = nullptr; - } - - void lock_guard::operator=(lock_guard&& other) - { - _lock = other._lock; - other._lock = nullptr; + _lock->release(); } }} // porpoise::sync diff --git a/tools/ld/raspi3.ld b/tools/ld/raspi3.ld index a173e42..d481aff 100644 --- a/tools/ld/raspi3.ld +++ b/tools/ld/raspi3.ld @@ -3,16 +3,25 @@ SECTIONS { . = 0x80000; __kernel_start__ = .; - .text BLOCK(4K) : ALIGN(4K) { + .text : ALIGN(16) { __code_start__ = .; KEEP(*(.text.boot)) + *(.fini) *(.text*) *(.gnu.linkonce.t*) + __code_end__ = .; + } + .init_fini : ALIGN(16) { *(.init) + __init_array_start__ = .; + *(.init_array) + __init_array_end__ = .; *(.fini) - __code_end__ = .; + __fini_array_start__ = .; + *(.fini_array) + __fini_array_end__ = .; } - .data BLOCK(4K) : ALIGN(4K) { + .data : ALIGN(16) { __data_start__ = .; *(.data) *(.data.*) @@ -20,17 +29,13 @@ SECTIONS *(.jcr) __data_end__ = .; } - .rodata BLOCK(4K) : ALIGN(4K) { + .rodata : ALIGN(16) { __rodata_start__ = .; *(.rodata*) *(.gnu.linkonce.r*) - *(.init_array) - *(.ctors*) - *(.fini_array) - *(.dtors*) __rodata_end__ = .; } - .eh_frame BLOCK(4K) : ALIGN(4K) { + .eh_frame : ALIGN(16) { start_eh_frame = .; __eh_frame_start__ = .; *(.eh_frame) @@ -38,7 +43,7 @@ SECTIONS QUAD(0) __eh_frame_end__ = .; } - .bss BLOCK(4K) : ALIGN(4K) { + .bss : ALIGN(16) { __bss_start__ = .; *(.bss) *(.gnu.linkonce.b*) From 1ccf60778413c018578810a3a7032937e7f5e07f Mon Sep 17 00:00:00 2001 From: Chris Swinchatt Date: Wed, 1 Jul 2020 11:55:09 +0100 Subject: [PATCH 16/16] Use sudo for calls to apt-get --- .github/workflows/c-cpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 2c37094..91ea34d 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -13,7 +13,7 @@ jobs: steps: - name: install compiler & emulator - run: apt update && apt install g++-aarch64-linux-gnu qemu-system-arm + run: sudo apt-get update && sudo apt-get install g++-aarch64-linux-gnu qemu-system-arm - uses: actions/checkout@v2 - name: run tests run: make test