diff --git a/exercises/lesson02/2/szediwy/Makefile b/exercises/lesson02/2/szediwy/Makefile new file mode 100644 index 00000000..97057d6a --- /dev/null +++ b/exercises/lesson02/2/szediwy/Makefile @@ -0,0 +1,34 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +.PHONY: clean + +all : kernel8.img + +clean : + rm -rf $(BUILD_DIR) *.img + @echo "clean: [SUCCESS]" + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel8.elf -O binary kernel8.img diff --git a/exercises/lesson02/2/szediwy/build.bat b/exercises/lesson02/2/szediwy/build.bat new file mode 100755 index 00000000..26eb1b5e --- /dev/null +++ b/exercises/lesson02/2/szediwy/build.bat @@ -0,0 +1 @@ +docker run --rm -v %cd%:/app -w /app smatyukevich/raspberry-pi-os-builder make %1 diff --git a/exercises/lesson02/2/szediwy/build.sh b/exercises/lesson02/2/szediwy/build.sh new file mode 100755 index 00000000..56cbff29 --- /dev/null +++ b/exercises/lesson02/2/szediwy/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --rm -v $(pwd):/app -w /app smatyukevich/raspberry-pi-os-builder make $1 diff --git a/exercises/lesson02/2/szediwy/include/custom_printf.h b/exercises/lesson02/2/szediwy/include/custom_printf.h new file mode 100644 index 00000000..984e49f3 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/custom_printf.h @@ -0,0 +1,108 @@ +/* +File: printf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is really just two files: 'printf.h' and 'printf.c'. + +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. + +I've found them so usefull in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and sprintf. + +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. + +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf you need to supply your own character output function, +something like : + + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } + +Before you can call printf you need to initialize it to use your +character output function with something like: + + init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +void test (char c); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/entry.h b/exercises/lesson02/2/szediwy/include/entry.h new file mode 100644 index 00000000..7844b7f2 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/entry.h @@ -0,0 +1,26 @@ +#ifndef _ENTRY_H +#define _ENTRY_H + +#define S_FRAME_SIZE 256 // size of all saved registers + +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +#endif \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/irq.h b/exercises/lesson02/2/szediwy/include/irq.h new file mode 100644 index 00000000..c0617d53 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/irq.h @@ -0,0 +1,10 @@ +#ifndef _IRQ_H +#define _IRQ_H + +void enable_interrupt_controller( void ); + +void irq_vector_init( void ); +void enable_irq( void ); +void disable_irq( void ); + +#endif /*_IRQ_H */ \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/mini_uart.h b/exercises/lesson02/2/szediwy/include/mini_uart.h new file mode 100644 index 00000000..f7f30ab2 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/mini_uart.h @@ -0,0 +1,10 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void putc(void *p, char c); + +#endif /*_MINI_UART_H */ diff --git a/exercises/lesson02/2/szediwy/include/mm.h b/exercises/lesson02/2/szediwy/include/mm.h new file mode 100644 index 00000000..9d05242d --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (2 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/exercises/lesson02/2/szediwy/include/peripherals/base.h b/exercises/lesson02/2/szediwy/include/peripherals/base.h new file mode 100644 index 00000000..3ab57c09 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/peripherals/base.h @@ -0,0 +1,20 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +// #define PBASE 0x3F000000 +// So a peripheral described in this document as being at legacy address 0x7Enn_nnnn is available in the 35-bit address +// space at 0x4_7Enn_nnnn, and visible to the ARM at 0x0_FEnn_nnnn if Low Peripheral mode is enabled. +// 0x7E000000 (legacy) -> 0x4_7E00_0000 (35-bit) -> 0x0_FE00_0000 (low peripheral) +#define PBASE 0xFE000000 + +// The base address of the GIC-400 is 0x4c0040000. Note that, unlike other peripheral addresses in this document, this is an +// ARM-only address and not a legacy master address. If Low Peripheral mode is enabled this base address becomes +// 0xff840000. +// The GIC-400 is configured with "NUM_CPUS=4" and "NUM_SPIS=192". For full register details, please refer to the ARM +// GIC-400 documentation on the ARM Developer website. +#define GIC_BASE 0xFF840000 + +// The ARMC register base address is 0x7e00b000 -> 0x4_7E00B000 -> 0x0FE00B000 +#define ARMC_BASE 0x0FE00B000 + +#endif /*_P_BASE_H */ diff --git a/exercises/lesson02/2/szediwy/include/peripherals/gpio.h b/exercises/lesson02/2/szediwy/include/peripherals/gpio.h new file mode 100644 index 00000000..717c2462 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/peripherals/gpio.h @@ -0,0 +1,20 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL1 (PBASE+0x00200004) +// #define GPSET0 (PBASE+0x0020001C) +// #define GPCLR0 (PBASE+0x00200028) +// #define GPPUD (PBASE+0x00200094) +// #define GPPUDCLK0 (PBASE+0x00200098) +#define GPIO_PUP_PDN_CNTRL_REG0 (PBASE+0x002000E4) +#define UART0_DR (PBASE+0x00201000) +#define UART0_FR (PBASE+0x00201018) +#define UART0_IBRD (PBASE+0x00201024) +#define UART0_FBRD (PBASE+0x00201028) +#define UART0_LCRH (PBASE+0x0020102C) +#define UART0_CR (PBASE+0x00201030) +#define UART0_IMSC (PBASE+0x00201038) + +#endif /*_P_GPIO_H */ diff --git a/exercises/lesson02/2/szediwy/include/peripherals/irq.h b/exercises/lesson02/2/szediwy/include/peripherals/irq.h new file mode 100644 index 00000000..7072cea0 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/peripherals/irq.h @@ -0,0 +1,23 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + +#define IRQ_PENDING_0_CORE_0 (PBASE+0x0000B200) +#define IRQ_PENDING_1_CORE_0 (PBASE+0x0000B204) +#define IRQ_PENDING_2_CORE_0 (PBASE+0x0000B208) +//#define FIQ_CONTROL (PBASE+0x0000B20C) +#define ENABLE_IRQS_0_CORE_0 (PBASE+0x0000B210) +#define ENABLE_IRQS_1_CORE_0 (PBASE+0x0000B214) +#define ENABLE_IRQS_2_CORE_0 (PBASE+0x0000B218) + +#define DISABLE_IRQS_0_CORE_0 (PBASE+0x0000B220) +#define DISABLE_IRQS_1_CORE_0 (PBASE+0x0000B224) +#define DISABLE_IRQS_2_CORE_0 (PBASE+0x0000B228) + +#define SYSTEM_TIMER_IRQ_0 (1 << 0) +#define SYSTEM_TIMER_IRQ_1 (1 << 1) +#define SYSTEM_TIMER_IRQ_2 (1 << 2) +#define SYSTEM_TIMER_IRQ_3 (1 << 3) + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/peripherals/mini_uart.h b/exercises/lesson02/2/szediwy/include/peripherals/mini_uart.h new file mode 100644 index 00000000..af95a3be --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE+0x00215004) +#define AUX_MU_IO_REG (PBASE+0x00215040) +#define AUX_MU_IER_REG (PBASE+0x00215044) +#define AUX_MU_IIR_REG (PBASE+0x00215048) +#define AUX_MU_LCR_REG (PBASE+0x0021504C) +#define AUX_MU_MCR_REG (PBASE+0x00215050) +#define AUX_MU_LSR_REG (PBASE+0x00215054) +#define AUX_MU_MSR_REG (PBASE+0x00215058) +#define AUX_MU_SCRATCH (PBASE+0x0021505C) +#define AUX_MU_CNTL_REG (PBASE+0x00215060) +#define AUX_MU_STAT_REG (PBASE+0x00215064) +#define AUX_MU_BAUD_REG (PBASE+0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/exercises/lesson02/2/szediwy/include/peripherals/timer.h b/exercises/lesson02/2/szediwy/include/peripherals/timer.h new file mode 100644 index 00000000..c0f4e8be --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/peripherals/timer.h @@ -0,0 +1,19 @@ +#ifndef _P_TIMER_H +#define _P_TIMER_H + +#include "peripherals/base.h" + +#define TIMER_CS (PBASE+0x00003000) +#define TIMER_CLO (PBASE+0x00003004) +#define TIMER_CHI (PBASE+0x00003008) +#define TIMER_C0 (PBASE+0x0000300C) +#define TIMER_C1 (PBASE+0x00003010) +#define TIMER_C2 (PBASE+0x00003014) +#define TIMER_C3 (PBASE+0x00003018) + +#define TIMER_CS_M0 (1 << 0) +#define TIMER_CS_M1 (1 << 1) +#define TIMER_CS_M2 (1 << 2) +#define TIMER_CS_M3 (1 << 3) + +#endif /*_P_TIMER_H */ \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/sysregs.h b/exercises/lesson02/2/szediwy/include/sysregs.h new file mode 100644 index 00000000..39067ebb --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/sysregs.h @@ -0,0 +1,55 @@ +#ifndef _SYSREGS_H +#define _SYSREGS_H + +// *************************************** +// SCTLR_EL1, System Control Register (EL1), Page 2654 of AArch64-Reference-Manual. +// *************************************** + +#define SCTLR_RESERVED (3 << 28) | (3 << 22) | (1 << 20) | (1 << 11) +#define SCTLR_EE_LITTLE_ENDIAN (0 << 25) +#define SCTLR_EOE_LITTLE_ENDIAN (0 << 24) +#define SCTLR_I_CACHE_DISABLED (0 << 12) +#define SCTLR_D_CACHE_DISABLED (0 << 2) +#define SCTLR_MMU_DISABLED (0 << 0) +#define SCTLR_MMU_ENABLED (1 << 0) + +#define SCTLR_VALUE_MMU_DISABLED (SCTLR_RESERVED | SCTLR_EE_LITTLE_ENDIAN | SCTLR_I_CACHE_DISABLED | SCTLR_D_CACHE_DISABLED | SCTLR_MMU_DISABLED) + +// *************************************** +// HCR_EL2, Hypervisor Configuration Register (EL2), Page 2487 of AArch64-Reference-Manual. +// *************************************** + +#define HCR_RW (1 << 31) +#define HCR_VALUE HCR_RW + +// *************************************** +// SCR_EL3, Secure Configuration Register (EL3), Page 2648 of AArch64-Reference-Manual. +// *************************************** + +#define SCR_RESERVED (3 << 4) +#define SCR_RW (1 << 10) +#define SCR_NS (1 << 0) +#define SCR_VALUE (SCR_RESERVED | SCR_RW | SCR_NS) + +// *************************************** +// SPSR_EL3, Saved Program Status Register (EL3) Page 389 of AArch64-Reference-Manual. +// *************************************** + +#define SPSR_MASK_ALL (7 << 6) +#define SPSR_EL1h (5 << 0) +#define SPSR_VALUE (SPSR_MASK_ALL | SPSR_EL1h) + +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') + +#define CPACR_EL1_MASK (1 << 28 | 3 << 20 | 3 << 16) + +#endif \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/timer.h b/exercises/lesson02/2/szediwy/include/timer.h new file mode 100644 index 00000000..a888b107 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/timer.h @@ -0,0 +1,7 @@ +#ifndef _TIMER_H +#define _TIMER_H + +void timer_init ( void ); +void handle_timer_irq ( void ); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/include/utils.h b/exercises/lesson02/2/szediwy/include/utils.h new file mode 100644 index 00000000..5b301011 --- /dev/null +++ b/exercises/lesson02/2/szediwy/include/utils.h @@ -0,0 +1,9 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay(unsigned long); +extern void put32(unsigned long, unsigned int); +extern unsigned int get32(unsigned long); +extern unsigned int get_el(); + +#endif /*_BOOT_H */ diff --git a/exercises/lesson02/2/szediwy/src/boot.S b/exercises/lesson02/2/szediwy/src/boot.S new file mode 100644 index 00000000..49e398da --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/boot.S @@ -0,0 +1,148 @@ +#include "mm.h" +#include "sysregs.h" + +.section ".text.boot" + +.globl _start +_start: + mrs x0, mpidr_el1 + and x0, x0, #0x3 + cbz x0, init_bss + /* If processor id is not 0 then pending lock processor + * (wait for `sev` instruction) + */ + wfe + b master + +proc_hang: + b proc_hang + +init_bss: + adr x0, bss_begin + adr x1, bss_end + sub x1, x1, x0 + bl memzero + + sev + + /***********************************************************************/ + /* Enable the other cores + link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/arm64/booting.rst?h=v5.3#n255 + + The boot loader is expected to enter the kernel on each CPU in the + following manner: + + - The primary CPU must jump directly to the first instruction of the + kernel image. The device tree blob passed by this CPU must contain + an 'enable-method' property for each cpu node. The supported + enable-methods are described below. + + It is expected that the bootloader will generate these device tree + properties and insert them into the blob prior to kernel entry. + + - CPUs with a "spin-table" enable-method must have a 'cpu-release-addr' + property in their cpu node. This property identifies a + naturally-aligned 64-bit zero-initalised memory location. + + These CPUs should spin outside of the kernel in a reserved area of + memory (communicated to the kernel by a /memreserve/ region in the + device tree) polling their cpu-release-addr location, which must be + contained in the reserved region. A wfe instruction may be inserted + to reduce the overhead of the busy-loop and a sev will be issued by + the primary CPU. When a read of the location pointed to by the + cpu-release-addr returns a non-zero value, the CPU must jump to this + value. The value will be written as a single 64-bit little-endian + value, so CPUs must convert the read value to their native endianness + before jumping to it. + + - CPUs with a "psci" enable method should remain outside of + the kernel (i.e. outside of the regions of memory described to the + kernel in the memory node, or in a reserved area of memory described + to the kernel by a /memreserve/ region in the device tree). The + kernel will issue CPU_ON calls as described in ARM document number ARM + DEN 0022A ("Power State Coordination Interface System Software on ARM + processors") to bring CPUs into the kernel. + + The device tree should contain a 'psci' node, as described in + Documentation/devicetree/bindings/arm/psci.yaml. + + - Secondary CPU general-purpose register settings + x0 = 0 (reserved for future use) + x1 = 0 (reserved for future use) + x2 = 0 (reserved for future use) + x3 = 0 (reserved for future use) + */ + + /* cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <0>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000d8>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <1>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000e0>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <2>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000e8>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <3>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000f0>; + }; */ + /****************************************************/ + mov x0, #0 + adr x0, configure_el1 + + mov x1, #0xe0 + str x0, [x1] + mov x1, #0xe8 + str x0, [x1] + mov x1, #0xf0 + str x0, [x1] + +configure_el1: + ldr x0, =SCTLR_VALUE_MMU_DISABLED + msr sctlr_el1, x0 + + ldr x0, =HCR_VALUE + msr hcr_el2, x0 + + + ldr x0, =CPACR_EL1_MASK + msr cpacr_el1, x0 + + ldr x0, =SPSR_VALUE + msr spsr_el2, x0 + + adr x0, master + msr elr_el2, x0 + + eret + +master: + mrs x0, mpidr_el1 + and x0, x0, #0x3 + + mov x1, #SECTION_SIZE + mul x1, x1, x0 + add x1, x1, #LOW_MEMORY + mov sp, x1 + + bl kernel_main + b proc_hang + diff --git a/exercises/lesson02/2/szediwy/src/config.txt b/exercises/lesson02/2/szediwy/src/config.txt new file mode 100644 index 00000000..51e25029 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/config.txt @@ -0,0 +1,4 @@ +arm_64bit=1 +enable_uart=1 +uart_2ndstage=1 +disable_commandline_tags=1 \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/custom_printf.c b/exercises/lesson02/2/szediwy/src/custom_printf.c new file mode 100644 index 00000000..a4b62218 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/custom_printf.c @@ -0,0 +1,249 @@ +/* +File: printf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "custom_printf.h" + +typedef void (*putcf)(void *, char); +static putcf stdout_putf; +static void *stdout_putp; + +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + while (num / d >= base) + d *= base; + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void li2a(long num, char *bf) +{ + if (num < 0) + { + num = -num; + *bf++ = '-'; + } + uli2a(num, 10, 0, bf); +} + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + while (num / d >= base) + d *= base; + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void i2a(int num, char *bf) +{ + if (num < 0) + { + num = -num; + *bf++ = '-'; + } + ui2a(num, 10, 0, bf); +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return -1; +} + +static char a2i(char ch, char **src, int base, int *nump) +{ + char *p = *src; + int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) + { + if (digit > base) + break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, int n, char z, char *bf) +{ + char fc = z ? '0' : ' '; + char ch; + char *p = bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp, fc); + while ((ch = *bf++)) + putf(putp, ch); +} + +void tfp_format(void *putp, putcf putf, char *fmt, va_list va) +{ + + char bf[12]; + + char ch; + + while ((ch = *(fmt++))) + { + if (ch != '%') + { + putf(putp, ch); + } + else + { + char lz = 0; +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; +#endif + int w = 0; + ch = *(fmt++); + if (ch == '0') + { + ch = *(fmt++); + lz = 1; + } + if (ch >= '0' && ch <= '9') + { + ch = a2i(ch, &fmt, 10, &w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') + { + ch = *(fmt++); + lng = 1; + } +#endif + switch (ch) + { + case 0: + goto abort; + case 'u': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 10, 0, bf); + else +#endif + ui2a(va_arg(va, unsigned int), 10, 0, bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'd': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int), bf); + else +#endif + i2a(va_arg(va, int), bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'x': + case 'X': +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 16, (ch == 'X'), bf); + else +#endif + ui2a(va_arg(va, unsigned int), 16, (ch == 'X'), bf); + putchw(putp, putf, w, lz, bf); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + putchw(putp, putf, w, 0, va_arg(va, char *)); + break; + case '%': + putf(putp, ch); + default: + break; + } + } + } +abort:; +} + +void init_printf(void *putp, void (*putf)(void *, char)) +{ + stdout_putf = putf; + stdout_putp = putp; +} + +void tfp_printf(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(stdout_putp, stdout_putf, fmt, va); + va_end(va); +} + +static void putcp(void *p, char c) +{ + *(*((char **)p))++ = c; +} + +void tfp_sprintf(char *s, char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(&s, putcp, fmt, va); + putcp(&s, 0); + va_end(va); +} \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/entry.S b/exercises/lesson02/2/szediwy/src/entry.S new file mode 100644 index 00000000..eefd711a --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/entry.S @@ -0,0 +1,136 @@ +#include "entry.h" + + .macro handle_invalid_entry type + kernel_entry + mov x0, #\type + mrs x1, esr_el1 + mrs x2, elr_el1 + bl show_invalid_entry_message + b err_hang + .endm + + .macro ventry label + .align 7 + b \label + .endm + + .macro kernel_entry + sub sp, sp, #S_FRAME_SIZE + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + str x30, [sp, #16 * 15] + .endm + + .macro kernel_exit + ldp x0, x1, [sp, #16 * 0] + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] + ldp x8, x9, [sp, #16 * 4] + ldp x10, x11, [sp, #16 * 5] + ldp x12, x13, [sp, #16 * 6] + ldp x14, x15, [sp, #16 * 7] + ldp x16, x17, [sp, #16 * 8] + ldp x18, x19, [sp, #16 * 9] + ldp x20, x21, [sp, #16 * 10] + ldp x22, x23, [sp, #16 * 11] + ldp x24, x25, [sp, #16 * 12] + ldp x26, x27, [sp, #16 * 13] + ldp x28, x29, [sp, #16 * 14] + ldr x30, [sp, #16 * 15] + add sp, sp, #S_FRAME_SIZE + eret + .endm + + +/* + * Exception vectors. + */ +.align 11 +.globl vectors +vectors: + ventry sync_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + ventry sync_invalid_el1h // Synchronous EL1h + ventry el1_irq // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_el1h // Error EL1h + + ventry sync_invalid_el0_64 // Synchronous 64-bit EL0 + ventry irq_invalid_el0_64 // IRQ 64-bit EL0 + ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + +sync_invalid_el1t: + handle_invalid_entry SYNC_INVALID_EL1t + +irq_invalid_el1t: + handle_invalid_entry IRQ_INVALID_EL1t + +fiq_invalid_el1t: + handle_invalid_entry FIQ_INVALID_EL1t + +error_invalid_el1t: + handle_invalid_entry ERROR_INVALID_EL1t + +sync_invalid_el1h: + handle_invalid_entry SYNC_INVALID_EL1h + +fiq_invalid_el1h: + handle_invalid_entry FIQ_INVALID_EL1h + +error_invalid_el1h: + handle_invalid_entry ERROR_INVALID_EL1h + +sync_invalid_el0_64: + handle_invalid_entry SYNC_INVALID_EL0_64 + +irq_invalid_el0_64: + handle_invalid_entry IRQ_INVALID_EL0_64 + +fiq_invalid_el0_64: + handle_invalid_entry FIQ_INVALID_EL0_64 + +error_invalid_el0_64: + handle_invalid_entry ERROR_INVALID_EL0_64 + +sync_invalid_el0_32: + handle_invalid_entry SYNC_INVALID_EL0_32 + +irq_invalid_el0_32: + handle_invalid_entry IRQ_INVALID_EL0_32 + +fiq_invalid_el0_32: + handle_invalid_entry FIQ_INVALID_EL0_32 + +error_invalid_el0_32: + handle_invalid_entry ERROR_INVALID_EL0_32 + +el1_irq: + kernel_entry + bl handle_irq + kernel_exit + +.globl err_hang +err_hang: b err_hang \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/irq.S b/exercises/lesson02/2/szediwy/src/irq.S new file mode 100644 index 00000000..5df4b031 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/irq.S @@ -0,0 +1,15 @@ +.globl irq_vector_init +irq_vector_init: + adr x0, vectors // load VBAR_EL1 with virtual + msr vbar_el1, x0 // vector table address + ret + +.globl enable_irq +enable_irq: + msr daifclr, #2 + ret + +.globl disable_irq +disable_irq: + msr daifset, #2 + ret \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/irq.c b/exercises/lesson02/2/szediwy/src/irq.c new file mode 100644 index 00000000..c8607086 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/irq.c @@ -0,0 +1,49 @@ +#include "utils.h" +#include "printf.h" +#include "timer.h" +#include "entry.h" +#include "peripherals/irq.h" + +const char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; + +void enable_interrupt_controller() +{ + put32(ENABLE_IRQS_0_CORE_0, SYSTEM_TIMER_IRQ_1); +} + +void show_invalid_entry_message(int type, unsigned long esr, unsigned long address) +{ + printf("%s, ESR: %x, address: %x\r\n", entry_error_messages[type], esr, address); +} + +void handle_irq(void) +{ + unsigned int irq = get32(IRQ_PENDING_0_CORE_0); + switch (irq) { + case (SYSTEM_TIMER_IRQ_1): + handle_timer_irq(); + break; + default: + printf("Unknown pending irq: %x\r\n", irq); + } +} \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/kernel.c b/exercises/lesson02/2/szediwy/src/kernel.c new file mode 100644 index 00000000..1060d2c6 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/kernel.c @@ -0,0 +1,39 @@ +#include "custom_printf.h" +#include "timer.h" +#include "utils.h" +#include "mini_uart.h" +#include "irq.h" + +void kernel_main(unsigned long processor_index) +{ + static unsigned int current_processor_index = 0; + + if (processor_index == 0) + { + uart_init(); + init_printf(0, putc); + irq_vector_init(); + timer_init(); + enable_interrupt_controller(); + enable_irq(); + } + + while (processor_index != current_processor_index) + ; + + int exception_level = get_el(); + printf("{CPU: %d, Exception level: %d} \r\n", processor_index, exception_level); + + current_processor_index++; + + if (processor_index == 0) + { + // if current_processor_index == 4 then all processors send message + while (current_processor_index != 4) + ; + for (;;) + { + uart_send(uart_recv()); + } + } +} \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/linker.ld b/exercises/lesson02/2/szediwy/src/linker.ld new file mode 100644 index 00000000..3af650df --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/linker.ld @@ -0,0 +1,12 @@ +SECTIONS +{ + . = 0x80000; + .text.boot : { *(.text.boot) } + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + . = ALIGN(0x8); + bss_begin = .; + .bss : { *(.bss*) } + bss_end = .; +} diff --git a/exercises/lesson02/2/szediwy/src/mini_uart.c b/exercises/lesson02/2/szediwy/src/mini_uart.c new file mode 100644 index 00000000..c0032fff --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/mini_uart.c @@ -0,0 +1,73 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" + +void uart_send(char c) +{ + while (get32(UART0_FR) & (1 << 5)) + { + } + put32(UART0_DR, c); +} + +char uart_recv(void) +{ + while (get32(UART0_FR) & (1 << 4)) + { + } + return (get32(UART0_DR) & 0xFF); +} + +void uart_send_string(char *str) +{ + for (int i = 0; str[i] != '\0'; i++) + { + uart_send((char)str[i]); + } +} + +void uart_init(void) +{ + + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 4 << 12; // set alt0 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 4 << 15; // set alt0 for gpio15 + put32(GPFSEL1, selector); + + unsigned int pullRegister; + pullRegister = get32(GPIO_PUP_PDN_CNTRL_REG0); + pullRegister &= ~(3 << 30); + pullRegister &= ~(3 << 28); + put32(GPIO_PUP_PDN_CNTRL_REG0, pullRegister); + + //first disable uart + put32(UART0_CR, 0); + put32(UART0_IMSC, 0); + + //from ../adkaster/src/uart.c + // Assume 48MHz UART Reference Clock (Standard) + // Calculate UART clock divider per datasheet + // BAUDDIV = (FUARTCLK/(16 Baud rate)) + // Note: We get 6 bits of fraction for the baud div + // 48000000/(16 * 115200) = 3000000/115200 = 26.0416666... + // Integer part = 26 :) + // From http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0183g/I54603.html + // we want floor(0.04166666.. * 64 + 0.5) = 3 + put32(UART0_IBRD, 26); + put32(UART0_FBRD, 3); + + //little endian: 0111|0000 => 8 bits and enable fifos + put32(UART0_LCRH, 7 << 4); + //little endian: 0011|0000|0001 => enable: rx tx uart + put32(UART0_CR, (1 << 9) | (1 << 8) | (1 << 0)); +} + +// This function is required by printf function +void putc(void* p, char c) +{ + uart_send(c); +} \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/mm.S b/exercises/lesson02/2/szediwy/src/mm.S new file mode 100644 index 00000000..1bd32ff3 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/exercises/lesson02/2/szediwy/src/timer.c b/exercises/lesson02/2/szediwy/src/timer.c new file mode 100644 index 00000000..ec77e9fb --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/timer.c @@ -0,0 +1,21 @@ +#include "utils.h" +#include "custom_printf.h" +#include "peripherals/timer.h" + +const unsigned int interval = 200000; +unsigned int curVal = 0; + +void timer_init ( void ) +{ + curVal = get32(TIMER_CLO); + curVal += interval; + put32(TIMER_C1, curVal); +} + +void handle_timer_irq( void ) +{ + curVal += interval; + put32(TIMER_C1, curVal); + put32(TIMER_CS, TIMER_CS_M1); + printf("Timer interrupt received\n\r"); +} \ No newline at end of file diff --git a/exercises/lesson02/2/szediwy/src/utils.S b/exercises/lesson02/2/szediwy/src/utils.S new file mode 100644 index 00000000..f6db63c5 --- /dev/null +++ b/exercises/lesson02/2/szediwy/src/utils.S @@ -0,0 +1,21 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl get_el +get_el: + mrs x0, CurrentEL + lsr x0, x0, #2 + ret \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/Makefile b/exercises/lesson03/1/szediwy/Makefile new file mode 100644 index 00000000..97057d6a --- /dev/null +++ b/exercises/lesson03/1/szediwy/Makefile @@ -0,0 +1,34 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +.PHONY: clean + +all : kernel8.img + +clean : + rm -rf $(BUILD_DIR) *.img + @echo "clean: [SUCCESS]" + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel8.elf -O binary kernel8.img diff --git a/exercises/lesson03/1/szediwy/README.md b/exercises/lesson03/1/szediwy/README.md new file mode 100644 index 00000000..40f6b964 --- /dev/null +++ b/exercises/lesson03/1/szediwy/README.md @@ -0,0 +1,3 @@ +# Readme + +Solved the issue for the Raspberry Pi 4B. The solution has both timer active. The system timer 1 and the local timer. diff --git a/exercises/lesson03/1/szediwy/build.bat b/exercises/lesson03/1/szediwy/build.bat new file mode 100755 index 00000000..26eb1b5e --- /dev/null +++ b/exercises/lesson03/1/szediwy/build.bat @@ -0,0 +1 @@ +docker run --rm -v %cd%:/app -w /app smatyukevich/raspberry-pi-os-builder make %1 diff --git a/exercises/lesson03/1/szediwy/build.sh b/exercises/lesson03/1/szediwy/build.sh new file mode 100755 index 00000000..56cbff29 --- /dev/null +++ b/exercises/lesson03/1/szediwy/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --rm -v $(pwd):/app -w /app smatyukevich/raspberry-pi-os-builder make $1 diff --git a/exercises/lesson03/1/szediwy/include/custom_printf.h b/exercises/lesson03/1/szediwy/include/custom_printf.h new file mode 100644 index 00000000..984e49f3 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/custom_printf.h @@ -0,0 +1,108 @@ +/* +File: printf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is really just two files: 'printf.h' and 'printf.c'. + +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. + +I've found them so usefull in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and sprintf. + +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. + +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf you need to supply your own character output function, +something like : + + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } + +Before you can call printf you need to initialize it to use your +character output function with something like: + + init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +void test (char c); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/entry.h b/exercises/lesson03/1/szediwy/include/entry.h new file mode 100644 index 00000000..7844b7f2 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/entry.h @@ -0,0 +1,26 @@ +#ifndef _ENTRY_H +#define _ENTRY_H + +#define S_FRAME_SIZE 256 // size of all saved registers + +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +#endif \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/irq.h b/exercises/lesson03/1/szediwy/include/irq.h new file mode 100644 index 00000000..69e22b88 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/irq.h @@ -0,0 +1,13 @@ +#ifndef _IRQ_H +#define _IRQ_H + +void enable_interrupt_controller( void ); + +void enable_interrupt(unsigned int irq); +void assign_target(unsigned int irq, unsigned int cpu); + +void irq_vector_init( void ); +void enable_irq( void ); +void disable_irq( void ); + +#endif /*_IRQ_H */ \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/mini_uart.h b/exercises/lesson03/1/szediwy/include/mini_uart.h new file mode 100644 index 00000000..f7f30ab2 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/mini_uart.h @@ -0,0 +1,10 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void putc(void *p, char c); + +#endif /*_MINI_UART_H */ diff --git a/exercises/lesson03/1/szediwy/include/mm.h b/exercises/lesson03/1/szediwy/include/mm.h new file mode 100644 index 00000000..9d05242d --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (2 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/exercises/lesson03/1/szediwy/include/peripherals/base.h b/exercises/lesson03/1/szediwy/include/peripherals/base.h new file mode 100644 index 00000000..96af006c --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/peripherals/base.h @@ -0,0 +1,22 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +// #define PBASE 0x3F000000 +// So a peripheral described in this document as being at legacy address 0x7Enn_nnnn is available in the 35-bit address +// space at 0x4_7Enn_nnnn, and visible to the ARM at 0x0_FEnn_nnnn if Low Peripheral mode is enabled. +// 0x7E000000 (legacy) -> 0x4_7E00_0000 (35-bit) -> 0x0_FE00_0000 (low peripheral) +#define PBASE 0xFE000000 + +// The base address of the GIC-400 is 0x4c0040000. Note that, unlike other peripheral addresses in this document, this is an +// ARM-only address and not a legacy master address. If Low Peripheral mode is enabled this base address becomes +// 0xff840000. +// The GIC-400 is configured with "NUM_CPUS=4" and "NUM_SPIS=192". For full register details, please refer to the ARM +// GIC-400 documentation on the ARM Developer website. +#define GIC_BASE 0xFF840000 + +#define ARM_LOCAL_BASE 0xFF800000 + +// The ARMC register base address is 0x7e00b000 -> 0x4_7E00B000 -> 0x0FE00B000 +#define ARMC_BASE 0x0FE00B000 + +#endif /*_P_BASE_H */ diff --git a/exercises/lesson03/1/szediwy/include/peripherals/gpio.h b/exercises/lesson03/1/szediwy/include/peripherals/gpio.h new file mode 100644 index 00000000..717c2462 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/peripherals/gpio.h @@ -0,0 +1,20 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL1 (PBASE+0x00200004) +// #define GPSET0 (PBASE+0x0020001C) +// #define GPCLR0 (PBASE+0x00200028) +// #define GPPUD (PBASE+0x00200094) +// #define GPPUDCLK0 (PBASE+0x00200098) +#define GPIO_PUP_PDN_CNTRL_REG0 (PBASE+0x002000E4) +#define UART0_DR (PBASE+0x00201000) +#define UART0_FR (PBASE+0x00201018) +#define UART0_IBRD (PBASE+0x00201024) +#define UART0_FBRD (PBASE+0x00201028) +#define UART0_LCRH (PBASE+0x0020102C) +#define UART0_CR (PBASE+0x00201030) +#define UART0_IMSC (PBASE+0x00201038) + +#endif /*_P_GPIO_H */ diff --git a/exercises/lesson03/1/szediwy/include/peripherals/irq.h b/exercises/lesson03/1/szediwy/include/peripherals/irq.h new file mode 100644 index 00000000..7f85ef51 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/peripherals/irq.h @@ -0,0 +1,48 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + + + +#define GIC_DIST_BASE (GIC_BASE+0x00001000) +#define GIC_CPU_BASE (GIC_BASE+0x00002000) + + + +// For interrupt ID m, when DIV and MOD are the integer division and modulo operations: +// • the corresponding GICD_ISENABLER number, n, is given by n = m DIV 32 +// • the offset of the required GICD_ISENABLER is (0x100 + (4*n)) +// • the bit number of the required Set-enable bit in this register is m MOD 32. +// +// VC Timer 1 = 1 => 97 / 32 = 3 => offset 0x10C +// +#define GIC_ENABLE_IRQ_BASE (GIC_DIST_BASE+0x00000100) +#define GIC_ENABLE_IRQ_3 (GIC_DIST_BASE+0x0000010C) + +#define GICC_IAR (GIC_CPU_BASE+0x0000000C) +#define GICC_EOIR (GIC_CPU_BASE+0x00000010) + +// For interrupt ID m, when DIV and MOD are the integer division and modulo operations: +// • the corresponding GICD_ITARGETSRn number, n, is given by n = m DIV 4 +// • the offset of the required GICD_ITARGETSR is (0x800 + (4*n)) +// • the byte offset of the required Priority field in this register is m MOD 4, where: +// — byte offset 0 refers to register bits [7:0] +// — byte offset 1 refers to register bits [15:8] +// — byte offset 2 refers to register bits [23:16] +// — byte offset 3 refers to register bits [31:24]. +#define GIC_IRQ_TARGET_BASE (GIC_DIST_BASE+0x00000800) + +// 97 / 4 => n = 24 +// 0x800 + 4*24 +// 97 % 4 => m = 1 +// byte offset = 1<<8 +#define GIC_IRQ_TARGET_24 (GIC_DIST_BASE+0x00000860) + +#define LOCAL_TIMER_IRQ (0x35) // Local timer IRQ +#define SYSTEM_TIMER_IRQ_0 (0x60) // VC IRQ 0 +#define SYSTEM_TIMER_IRQ_1 (0x61) // VC IRQ 1 +#define SYSTEM_TIMER_IRQ_2 (0x62) // VC IRQ 2 +#define SYSTEM_TIMER_IRQ_3 (0x63) // VC IRQ 3 + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/peripherals/mini_uart.h b/exercises/lesson03/1/szediwy/include/peripherals/mini_uart.h new file mode 100644 index 00000000..af95a3be --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/peripherals/mini_uart.h @@ -0,0 +1,19 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE+0x00215004) +#define AUX_MU_IO_REG (PBASE+0x00215040) +#define AUX_MU_IER_REG (PBASE+0x00215044) +#define AUX_MU_IIR_REG (PBASE+0x00215048) +#define AUX_MU_LCR_REG (PBASE+0x0021504C) +#define AUX_MU_MCR_REG (PBASE+0x00215050) +#define AUX_MU_LSR_REG (PBASE+0x00215054) +#define AUX_MU_MSR_REG (PBASE+0x00215058) +#define AUX_MU_SCRATCH (PBASE+0x0021505C) +#define AUX_MU_CNTL_REG (PBASE+0x00215060) +#define AUX_MU_STAT_REG (PBASE+0x00215064) +#define AUX_MU_BAUD_REG (PBASE+0x00215068) + +#endif /*_P_MINI_UART_H */ diff --git a/exercises/lesson03/1/szediwy/include/peripherals/timer.h b/exercises/lesson03/1/szediwy/include/peripherals/timer.h new file mode 100644 index 00000000..19cb556b --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/peripherals/timer.h @@ -0,0 +1,32 @@ +#ifndef _P_TIMER_H +#define _P_TIMER_H + +#include "peripherals/base.h" + +#define TIMER_CS (PBASE+0x00003000) +#define TIMER_CLO (PBASE+0x00003004) +#define TIMER_CHI (PBASE+0x00003008) +#define TIMER_C0 (PBASE+0x0000300C) +#define TIMER_C1 (PBASE+0x00003010) +#define TIMER_C2 (PBASE+0x00003014) +#define TIMER_C3 (PBASE+0x00003018) + +#define TIMER_CS_M0 (1 << 0) +#define TIMER_CS_M1 (1 << 1) +#define TIMER_CS_M2 (1 << 2) +#define TIMER_CS_M3 (1 << 3) + +// Local Timer Configuration. +// A free-running secondary timer is provided that can generate an interrupt each time it crosses zero. When it is +// enabled, the timer is decremented on each edge (positive or negative) of the crystal reference clock. It is +// automatically reloaded with the TIMER_TIMEOUT value when it reaches zero and then continues to decrement. +// Routing of the timer interrupt is controlled by the PERI_IRQ_ROUTE0 register +#define LOCAL_TIMER_CONTROL (ARM_LOCAL_BASE + 0x34) + +// Local Timer Interrupt Control +#define LOCAL_TIMER_IRQ (ARM_LOCAL_BASE + 0x38) + +// 54 MHz +#define LOCAL_TIMER_FREQ (54000000) + +#endif /*_P_TIMER_H */ \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/sysregs.h b/exercises/lesson03/1/szediwy/include/sysregs.h new file mode 100644 index 00000000..b116dd46 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/sysregs.h @@ -0,0 +1,56 @@ +#ifndef _SYSREGS_H +#define _SYSREGS_H + +// *************************************** +// SCTLR_EL1, System Control Register (EL1), Page 2654 of AArch64-Reference-Manual. +// *************************************** + +#define SCTLR_RESERVED (3 << 28) | (3 << 22) | (1 << 20) | (1 << 11) +#define SCTLR_EE_LITTLE_ENDIAN (0 << 25) +#define SCTLR_EOE_LITTLE_ENDIAN (0 << 24) +#define SCTLR_I_CACHE_DISABLED (0 << 12) +#define SCTLR_D_CACHE_DISABLED (0 << 2) +#define SCTLR_MMU_DISABLED (0 << 0) +#define SCTLR_MMU_ENABLED (1 << 0) + + +#define SCTLR_VALUE_MMU_DISABLED (SCTLR_RESERVED | SCTLR_EE_LITTLE_ENDIAN | SCTLR_I_CACHE_DISABLED | SCTLR_D_CACHE_DISABLED | SCTLR_MMU_DISABLED) + +// *************************************** +// HCR_EL2, Hypervisor Configuration Register (EL2), Page 2487 of AArch64-Reference-Manual. +// *************************************** + +#define HCR_RW (1 << 31) +#define HCR_VALUE HCR_RW + +// *************************************** +// SCR_EL3, Secure Configuration Register (EL3), Page 2648 of AArch64-Reference-Manual. +// *************************************** + +#define SCR_RESERVED (3 << 4) +#define SCR_RW (1 << 10) +#define SCR_NS (1 << 0) +#define SCR_VALUE (SCR_RESERVED | SCR_RW | SCR_NS) + +// *************************************** +// SPSR_EL3, Saved Program Status Register (EL3) Page 389 of AArch64-Reference-Manual. +// *************************************** + +#define SPSR_MASK_ALL (7 << 6) +#define SPSR_EL1h (5 << 0) +#define SPSR_VALUE (SPSR_MASK_ALL | SPSR_EL1h) + +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') + +#define CPACR_EL1_MASK (0 << 28 | 3 << 20 | 3 << 16) + +#endif \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/timer.h b/exercises/lesson03/1/szediwy/include/timer.h new file mode 100644 index 00000000..41d3a927 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/timer.h @@ -0,0 +1,10 @@ +#ifndef _TIMER_H +#define _TIMER_H + +void timer_init ( void ); +void handle_timer_irq ( void ); + +void local_timer_init (void); +void handle_local_timer_irq (void); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/include/utils.h b/exercises/lesson03/1/szediwy/include/utils.h new file mode 100644 index 00000000..5b301011 --- /dev/null +++ b/exercises/lesson03/1/szediwy/include/utils.h @@ -0,0 +1,9 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay(unsigned long); +extern void put32(unsigned long, unsigned int); +extern unsigned int get32(unsigned long); +extern unsigned int get_el(); + +#endif /*_BOOT_H */ diff --git a/exercises/lesson03/1/szediwy/src/boot.S b/exercises/lesson03/1/szediwy/src/boot.S new file mode 100644 index 00000000..49e398da --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/boot.S @@ -0,0 +1,148 @@ +#include "mm.h" +#include "sysregs.h" + +.section ".text.boot" + +.globl _start +_start: + mrs x0, mpidr_el1 + and x0, x0, #0x3 + cbz x0, init_bss + /* If processor id is not 0 then pending lock processor + * (wait for `sev` instruction) + */ + wfe + b master + +proc_hang: + b proc_hang + +init_bss: + adr x0, bss_begin + adr x1, bss_end + sub x1, x1, x0 + bl memzero + + sev + + /***********************************************************************/ + /* Enable the other cores + link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/arm64/booting.rst?h=v5.3#n255 + + The boot loader is expected to enter the kernel on each CPU in the + following manner: + + - The primary CPU must jump directly to the first instruction of the + kernel image. The device tree blob passed by this CPU must contain + an 'enable-method' property for each cpu node. The supported + enable-methods are described below. + + It is expected that the bootloader will generate these device tree + properties and insert them into the blob prior to kernel entry. + + - CPUs with a "spin-table" enable-method must have a 'cpu-release-addr' + property in their cpu node. This property identifies a + naturally-aligned 64-bit zero-initalised memory location. + + These CPUs should spin outside of the kernel in a reserved area of + memory (communicated to the kernel by a /memreserve/ region in the + device tree) polling their cpu-release-addr location, which must be + contained in the reserved region. A wfe instruction may be inserted + to reduce the overhead of the busy-loop and a sev will be issued by + the primary CPU. When a read of the location pointed to by the + cpu-release-addr returns a non-zero value, the CPU must jump to this + value. The value will be written as a single 64-bit little-endian + value, so CPUs must convert the read value to their native endianness + before jumping to it. + + - CPUs with a "psci" enable method should remain outside of + the kernel (i.e. outside of the regions of memory described to the + kernel in the memory node, or in a reserved area of memory described + to the kernel by a /memreserve/ region in the device tree). The + kernel will issue CPU_ON calls as described in ARM document number ARM + DEN 0022A ("Power State Coordination Interface System Software on ARM + processors") to bring CPUs into the kernel. + + The device tree should contain a 'psci' node, as described in + Documentation/devicetree/bindings/arm/psci.yaml. + + - Secondary CPU general-purpose register settings + x0 = 0 (reserved for future use) + x1 = 0 (reserved for future use) + x2 = 0 (reserved for future use) + x3 = 0 (reserved for future use) + */ + + /* cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <0>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000d8>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <1>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000e0>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <2>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000e8>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <3>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000f0>; + }; */ + /****************************************************/ + mov x0, #0 + adr x0, configure_el1 + + mov x1, #0xe0 + str x0, [x1] + mov x1, #0xe8 + str x0, [x1] + mov x1, #0xf0 + str x0, [x1] + +configure_el1: + ldr x0, =SCTLR_VALUE_MMU_DISABLED + msr sctlr_el1, x0 + + ldr x0, =HCR_VALUE + msr hcr_el2, x0 + + + ldr x0, =CPACR_EL1_MASK + msr cpacr_el1, x0 + + ldr x0, =SPSR_VALUE + msr spsr_el2, x0 + + adr x0, master + msr elr_el2, x0 + + eret + +master: + mrs x0, mpidr_el1 + and x0, x0, #0x3 + + mov x1, #SECTION_SIZE + mul x1, x1, x0 + add x1, x1, #LOW_MEMORY + mov sp, x1 + + bl kernel_main + b proc_hang + diff --git a/exercises/lesson03/1/szediwy/src/config.txt b/exercises/lesson03/1/szediwy/src/config.txt new file mode 100644 index 00000000..51e25029 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/config.txt @@ -0,0 +1,4 @@ +arm_64bit=1 +enable_uart=1 +uart_2ndstage=1 +disable_commandline_tags=1 \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/custom_printf.c b/exercises/lesson03/1/szediwy/src/custom_printf.c new file mode 100644 index 00000000..a4b62218 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/custom_printf.c @@ -0,0 +1,249 @@ +/* +File: printf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "custom_printf.h" + +typedef void (*putcf)(void *, char); +static putcf stdout_putf; +static void *stdout_putp; + +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + while (num / d >= base) + d *= base; + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void li2a(long num, char *bf) +{ + if (num < 0) + { + num = -num; + *bf++ = '-'; + } + uli2a(num, 10, 0, bf); +} + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + while (num / d >= base) + d *= base; + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void i2a(int num, char *bf) +{ + if (num < 0) + { + num = -num; + *bf++ = '-'; + } + ui2a(num, 10, 0, bf); +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return -1; +} + +static char a2i(char ch, char **src, int base, int *nump) +{ + char *p = *src; + int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) + { + if (digit > base) + break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, int n, char z, char *bf) +{ + char fc = z ? '0' : ' '; + char ch; + char *p = bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp, fc); + while ((ch = *bf++)) + putf(putp, ch); +} + +void tfp_format(void *putp, putcf putf, char *fmt, va_list va) +{ + + char bf[12]; + + char ch; + + while ((ch = *(fmt++))) + { + if (ch != '%') + { + putf(putp, ch); + } + else + { + char lz = 0; +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; +#endif + int w = 0; + ch = *(fmt++); + if (ch == '0') + { + ch = *(fmt++); + lz = 1; + } + if (ch >= '0' && ch <= '9') + { + ch = a2i(ch, &fmt, 10, &w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') + { + ch = *(fmt++); + lng = 1; + } +#endif + switch (ch) + { + case 0: + goto abort; + case 'u': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 10, 0, bf); + else +#endif + ui2a(va_arg(va, unsigned int), 10, 0, bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'd': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int), bf); + else +#endif + i2a(va_arg(va, int), bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'x': + case 'X': +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 16, (ch == 'X'), bf); + else +#endif + ui2a(va_arg(va, unsigned int), 16, (ch == 'X'), bf); + putchw(putp, putf, w, lz, bf); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + putchw(putp, putf, w, 0, va_arg(va, char *)); + break; + case '%': + putf(putp, ch); + default: + break; + } + } + } +abort:; +} + +void init_printf(void *putp, void (*putf)(void *, char)) +{ + stdout_putf = putf; + stdout_putp = putp; +} + +void tfp_printf(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(stdout_putp, stdout_putf, fmt, va); + va_end(va); +} + +static void putcp(void *p, char c) +{ + *(*((char **)p))++ = c; +} + +void tfp_sprintf(char *s, char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(&s, putcp, fmt, va); + putcp(&s, 0); + va_end(va); +} \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/debug.c b/exercises/lesson03/1/szediwy/src/debug.c new file mode 100644 index 00000000..e4941fdb --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/debug.c @@ -0,0 +1,21 @@ +#include "custom_printf.h" + +void d0(void) { + printf("#0"); +} + +void d1(void) { + printf("#1"); +} + +void d2(void) { + printf("#2"); +} + +void d3(void) { + printf("#3"); +} + +void d4(void) { + printf("#4"); +} \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/entry.S b/exercises/lesson03/1/szediwy/src/entry.S new file mode 100644 index 00000000..288f8917 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/entry.S @@ -0,0 +1,136 @@ +#include "entry.h" + +.macro handle_invalid_entry type + kernel_entry + mov x0, #\type + mrs x1, esr_el1 + mrs x2, elr_el1 + bl show_invalid_entry_message + b err_hang +.endm + +.macro ventry label + .align 7 + b \label +.endm + +.macro kernel_entry + sub sp, sp, #S_FRAME_SIZE + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + str x30, [sp, #16 * 15] +.endm + +.macro kernel_exit + ldp x0, x1, [sp, #16 * 0] + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] + ldp x8, x9, [sp, #16 * 4] + ldp x10, x11, [sp, #16 * 5] + ldp x12, x13, [sp, #16 * 6] + ldp x14, x15, [sp, #16 * 7] + ldp x16, x17, [sp, #16 * 8] + ldp x18, x19, [sp, #16 * 9] + ldp x20, x21, [sp, #16 * 10] + ldp x22, x23, [sp, #16 * 11] + ldp x24, x25, [sp, #16 * 12] + ldp x26, x27, [sp, #16 * 13] + ldp x28, x29, [sp, #16 * 14] + ldr x30, [sp, #16 * 15] + add sp, sp, #S_FRAME_SIZE + eret +.endm + + +/* + * Exception vectors. + */ +.align 11 +.globl vectors +vectors: + ventry sync_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + ventry sync_invalid_el1h // Synchronous EL1h + ventry el1_irq // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_el1h // Error EL1h + + ventry sync_invalid_el0_64 // Synchronous 64-bit EL0 + ventry irq_invalid_el0_64 // IRQ 64-bit EL0 + ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + +sync_invalid_el1t: + handle_invalid_entry SYNC_INVALID_EL1t + +irq_invalid_el1t: + handle_invalid_entry IRQ_INVALID_EL1t + +fiq_invalid_el1t: + handle_invalid_entry FIQ_INVALID_EL1t + +error_invalid_el1t: + handle_invalid_entry ERROR_INVALID_EL1t + +sync_invalid_el1h: + handle_invalid_entry SYNC_INVALID_EL1h + +fiq_invalid_el1h: + handle_invalid_entry FIQ_INVALID_EL1h + +error_invalid_el1h: + handle_invalid_entry ERROR_INVALID_EL1h + +sync_invalid_el0_64: + handle_invalid_entry SYNC_INVALID_EL0_64 + +irq_invalid_el0_64: + handle_invalid_entry IRQ_INVALID_EL0_64 + +fiq_invalid_el0_64: + handle_invalid_entry FIQ_INVALID_EL0_64 + +error_invalid_el0_64: + handle_invalid_entry ERROR_INVALID_EL0_64 + +sync_invalid_el0_32: + handle_invalid_entry SYNC_INVALID_EL0_32 + +irq_invalid_el0_32: + handle_invalid_entry IRQ_INVALID_EL0_32 + +fiq_invalid_el0_32: + handle_invalid_entry FIQ_INVALID_EL0_32 + +error_invalid_el0_32: + handle_invalid_entry ERROR_INVALID_EL0_32 + +el1_irq: + kernel_entry + bl handle_irq + kernel_exit + +.globl err_hang +err_hang: b err_hang \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/irq.S b/exercises/lesson03/1/szediwy/src/irq.S new file mode 100644 index 00000000..f6e830cb --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/irq.S @@ -0,0 +1,15 @@ +.globl irq_vector_init +irq_vector_init: + adr x0, vectors // load VBAR_EL1 with virtual + msr vbar_el1, x0 + ret + +.globl enable_irq +enable_irq: + msr daifclr, #0b0010 + ret + +.globl disable_irq +disable_irq: + msr daifset, #0b0010 + ret \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/irq.c b/exercises/lesson03/1/szediwy/src/irq.c new file mode 100644 index 00000000..3363f0f0 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/irq.c @@ -0,0 +1,100 @@ +#include "utils.h" +#include "custom_printf.h" +#include "timer.h" +#include "entry.h" +#include "peripherals/irq.h" +#include "sysregs.h" + +const char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; + +void enable_interrupt(unsigned int irq) { + + // For interrupt ID m, when DIV and MOD are the integer division and modulo operations: + // • the corresponding GICD_ISENABLER number, n, is given by n = m DIV 32 + // • the offset of the required GICD_ISENABLER is (0x100 + (4*n)) + // • the bit number of the required Set-enable bit in this register is m MOD 32. + + unsigned int n = irq / 32; + unsigned int offset = irq % 32; + unsigned int enableRegister = GIC_ENABLE_IRQ_BASE + (4*n); + + put32(enableRegister, 1 << offset); +} + +void assign_target(unsigned int irq, unsigned int cpu) { + // For interrupt ID m, when DIV and MOD are the integer division and modulo operations: + // • the corresponding GICD_ITARGETSRn number, n, is given by n = m DIV 4 + // • the offset of the required GICD_ITARGETSR is (0x800 + (4*n)) + // • the byte offset of the required Priority field in this register is m MOD 4, where: + // — byte offset 0 refers to register bits [7:0] + // — byte offset 1 refers to register bits [15:8] + // — byte offset 2 refers to register bits [23:16] + // — byte offset 3 refers to register bits [31:24]. + + unsigned int n = irq / 4; + unsigned int offset = irq % 4; + unsigned int targetRegister = GIC_IRQ_TARGET_BASE + (4*n); + + // Currently we only enter the target CPU 0 + put32(targetRegister, get32(targetRegister) | (1 << (offset*8))); +} + + +void enable_interrupt_controller() { + assign_target(SYSTEM_TIMER_IRQ_1, 0); + enable_interrupt(SYSTEM_TIMER_IRQ_1); + + assign_target(LOCAL_TIMER_IRQ, 0); + enable_interrupt(LOCAL_TIMER_IRQ); +} + +void show_invalid_entry_message(int type, unsigned long esr, unsigned long address) +{ + printf("%s, ESR: %x, address: %x\r\n", entry_error_messages[type], esr, address); +} + +void handle_irq(void) +{ + // NOTE (ARM® Generic Interrupt Controller - Architecture version 2.0): + // For compatibility with possible extensions to the GIC architecture specification, ARM recommends that software + // preserves the entire register value read from the GICC_IAR when it acknowledges the interrupt, and uses that entire + // value for its corresponding write to the GICC_EOIR. + unsigned int irqAckRegister = get32(GICC_IAR); + + unsigned int irq = irqAckRegister & 0x2FF; // bit [9:0] -> interrupt ID + switch (irq) { + case (SYSTEM_TIMER_IRQ_1): + handle_timer_irq(); + put32(GICC_EOIR, irqAckRegister); + break; + case (LOCAL_TIMER_IRQ): + handle_local_timer_irq(); + put32(GICC_EOIR, irqAckRegister); + break; + default: + printf("Unknown pending irq: %x\r\n", irq); + } + + + +} \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/kernel.c b/exercises/lesson03/1/szediwy/src/kernel.c new file mode 100644 index 00000000..eabd8ce5 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/kernel.c @@ -0,0 +1,45 @@ +#include "custom_printf.h" +#include "timer.h" +#include "utils.h" +#include "mini_uart.h" +#include "irq.h" + +void kernel_main(unsigned long processor_index) +{ + static unsigned int current_processor_index = 0; + + if (processor_index == 0) + { + uart_init(); + init_printf(0, putc); + printf("irq_vector_init\r\n"); + irq_vector_init(); + printf("timer_init\r\n"); + timer_init(); + printf("local_timer_init\r\n"); + local_timer_init(); + printf("enable_interrupt_controller\r\n"); + enable_interrupt_controller(); + printf("enable_irq\r\n"); + enable_irq(); + } + + while (processor_index != current_processor_index) + ; + + int exception_level = get_el(); + printf("{CPU: %d, Exception level: %d}\r\n", processor_index, exception_level); + + current_processor_index++; + + if (processor_index == 0) + { + // if current_processor_index == 4 then all processors send message + while (current_processor_index != 4) + ; + for (;;) + { + uart_send(uart_recv()); + } + } +} \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/linker.ld b/exercises/lesson03/1/szediwy/src/linker.ld new file mode 100644 index 00000000..3af650df --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/linker.ld @@ -0,0 +1,12 @@ +SECTIONS +{ + . = 0x80000; + .text.boot : { *(.text.boot) } + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + . = ALIGN(0x8); + bss_begin = .; + .bss : { *(.bss*) } + bss_end = .; +} diff --git a/exercises/lesson03/1/szediwy/src/mini_uart.c b/exercises/lesson03/1/szediwy/src/mini_uart.c new file mode 100644 index 00000000..c0032fff --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/mini_uart.c @@ -0,0 +1,73 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" + +void uart_send(char c) +{ + while (get32(UART0_FR) & (1 << 5)) + { + } + put32(UART0_DR, c); +} + +char uart_recv(void) +{ + while (get32(UART0_FR) & (1 << 4)) + { + } + return (get32(UART0_DR) & 0xFF); +} + +void uart_send_string(char *str) +{ + for (int i = 0; str[i] != '\0'; i++) + { + uart_send((char)str[i]); + } +} + +void uart_init(void) +{ + + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 4 << 12; // set alt0 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 4 << 15; // set alt0 for gpio15 + put32(GPFSEL1, selector); + + unsigned int pullRegister; + pullRegister = get32(GPIO_PUP_PDN_CNTRL_REG0); + pullRegister &= ~(3 << 30); + pullRegister &= ~(3 << 28); + put32(GPIO_PUP_PDN_CNTRL_REG0, pullRegister); + + //first disable uart + put32(UART0_CR, 0); + put32(UART0_IMSC, 0); + + //from ../adkaster/src/uart.c + // Assume 48MHz UART Reference Clock (Standard) + // Calculate UART clock divider per datasheet + // BAUDDIV = (FUARTCLK/(16 Baud rate)) + // Note: We get 6 bits of fraction for the baud div + // 48000000/(16 * 115200) = 3000000/115200 = 26.0416666... + // Integer part = 26 :) + // From http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0183g/I54603.html + // we want floor(0.04166666.. * 64 + 0.5) = 3 + put32(UART0_IBRD, 26); + put32(UART0_FBRD, 3); + + //little endian: 0111|0000 => 8 bits and enable fifos + put32(UART0_LCRH, 7 << 4); + //little endian: 0011|0000|0001 => enable: rx tx uart + put32(UART0_CR, (1 << 9) | (1 << 8) | (1 << 0)); +} + +// This function is required by printf function +void putc(void* p, char c) +{ + uart_send(c); +} \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/mm.S b/exercises/lesson03/1/szediwy/src/mm.S new file mode 100644 index 00000000..1bd32ff3 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/exercises/lesson03/1/szediwy/src/timer.c b/exercises/lesson03/1/szediwy/src/timer.c new file mode 100644 index 00000000..a5b6865d --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/timer.c @@ -0,0 +1,32 @@ +#include "utils.h" +#include "custom_printf.h" +#include "peripherals/timer.h" + +const unsigned int interval = 200000; +unsigned int curVal = 0; + +void timer_init ( void ) +{ + curVal = get32(TIMER_CLO); + curVal += interval; + put32(TIMER_C1, curVal); +} + +void handle_timer_irq( void ) +{ + curVal += interval; + put32(TIMER_C1, curVal); + put32(TIMER_CS, TIMER_CS_M1); + printf("Timer interrupt received\n\r"); +} + +void handle_local_timer_irq( void ) +{ + printf("LOCAL_TIMER_IRQ received\n\r"); + put32(LOCAL_TIMER_IRQ, (3<<30)); +} + +void local_timer_init (void) { + // Enable the timer + put32(LOCAL_TIMER_CONTROL, (3<<28) | (3*LOCAL_TIMER_FREQ)); +} \ No newline at end of file diff --git a/exercises/lesson03/1/szediwy/src/utils.S b/exercises/lesson03/1/szediwy/src/utils.S new file mode 100644 index 00000000..f6db63c5 --- /dev/null +++ b/exercises/lesson03/1/szediwy/src/utils.S @@ -0,0 +1,21 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl get_el +get_el: + mrs x0, CurrentEL + lsr x0, x0, #2 + ret \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/Makefile b/exercises/lesson03/2/szediwy/Makefile new file mode 100644 index 00000000..97057d6a --- /dev/null +++ b/exercises/lesson03/2/szediwy/Makefile @@ -0,0 +1,34 @@ +ARMGNU ?= aarch64-linux-gnu + +COPS = -Wall -nostdlib -nostartfiles -ffreestanding -Iinclude +ASMOPS = -Iinclude + +BUILD_DIR = build +SRC_DIR = src + +.PHONY: clean + +all : kernel8.img + +clean : + rm -rf $(BUILD_DIR) *.img + @echo "clean: [SUCCESS]" + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + mkdir -p $(@D) + $(ARMGNU)-gcc $(COPS) -MMD -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMOPS) -MMD -c $< -o $@ + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) + +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +kernel8.img: $(SRC_DIR)/linker.ld $(OBJ_FILES) + $(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel8.elf $(OBJ_FILES) + $(ARMGNU)-objcopy $(BUILD_DIR)/kernel8.elf -O binary kernel8.img diff --git a/exercises/lesson03/2/szediwy/README.md b/exercises/lesson03/2/szediwy/README.md new file mode 100644 index 00000000..40f6b964 --- /dev/null +++ b/exercises/lesson03/2/szediwy/README.md @@ -0,0 +1,3 @@ +# Readme + +Solved the issue for the Raspberry Pi 4B. The solution has both timer active. The system timer 1 and the local timer. diff --git a/exercises/lesson03/2/szediwy/build.bat b/exercises/lesson03/2/szediwy/build.bat new file mode 100755 index 00000000..26eb1b5e --- /dev/null +++ b/exercises/lesson03/2/szediwy/build.bat @@ -0,0 +1 @@ +docker run --rm -v %cd%:/app -w /app smatyukevich/raspberry-pi-os-builder make %1 diff --git a/exercises/lesson03/2/szediwy/build.sh b/exercises/lesson03/2/szediwy/build.sh new file mode 100755 index 00000000..56cbff29 --- /dev/null +++ b/exercises/lesson03/2/szediwy/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --rm -v $(pwd):/app -w /app smatyukevich/raspberry-pi-os-builder make $1 diff --git a/exercises/lesson03/2/szediwy/include/custom_printf.h b/exercises/lesson03/2/szediwy/include/custom_printf.h new file mode 100644 index 00000000..0ae8ac36 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/custom_printf.h @@ -0,0 +1,119 @@ +/* +File: printf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is really just two files: 'printf.h' and 'printf.c'. + +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. + +I've found them so usefull in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and sprintf. + +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. + +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf you need to supply your own character output function, +something like : + + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } + +Before you can call printf you need to initialize it to use your +character output function with something like: + + init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +void test (char c); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0') + +#endif \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/entry.h b/exercises/lesson03/2/szediwy/include/entry.h new file mode 100644 index 00000000..7844b7f2 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/entry.h @@ -0,0 +1,26 @@ +#ifndef _ENTRY_H +#define _ENTRY_H + +#define S_FRAME_SIZE 256 // size of all saved registers + +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +#endif \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/irq.h b/exercises/lesson03/2/szediwy/include/irq.h new file mode 100644 index 00000000..69e22b88 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/irq.h @@ -0,0 +1,13 @@ +#ifndef _IRQ_H +#define _IRQ_H + +void enable_interrupt_controller( void ); + +void enable_interrupt(unsigned int irq); +void assign_target(unsigned int irq, unsigned int cpu); + +void irq_vector_init( void ); +void enable_irq( void ); +void disable_irq( void ); + +#endif /*_IRQ_H */ \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/mini_uart.h b/exercises/lesson03/2/szediwy/include/mini_uart.h new file mode 100644 index 00000000..2c61fd97 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/mini_uart.h @@ -0,0 +1,12 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +void uart_init(void); +char uart_recv(void); +void uart_send(char c); +void uart_send_string(char *str); +void putc(void *p, char c); + +void handle_uart_interrupt(void ); + +#endif /*_MINI_UART_H */ diff --git a/exercises/lesson03/2/szediwy/include/mm.h b/exercises/lesson03/2/szediwy/include/mm.h new file mode 100644 index 00000000..9d05242d --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/mm.h @@ -0,0 +1,19 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SHIFT 12 +#define TABLE_SHIFT 9 +#define SECTION_SHIFT (PAGE_SHIFT + TABLE_SHIFT) + +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define SECTION_SIZE (1 << SECTION_SHIFT) + +#define LOW_MEMORY (2 * SECTION_SIZE) + +#ifndef __ASSEMBLER__ + +void memzero(unsigned long src, unsigned long n); + +#endif + +#endif /*_MM_H */ diff --git a/exercises/lesson03/2/szediwy/include/peripherals/base.h b/exercises/lesson03/2/szediwy/include/peripherals/base.h new file mode 100644 index 00000000..96af006c --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/peripherals/base.h @@ -0,0 +1,22 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +// #define PBASE 0x3F000000 +// So a peripheral described in this document as being at legacy address 0x7Enn_nnnn is available in the 35-bit address +// space at 0x4_7Enn_nnnn, and visible to the ARM at 0x0_FEnn_nnnn if Low Peripheral mode is enabled. +// 0x7E000000 (legacy) -> 0x4_7E00_0000 (35-bit) -> 0x0_FE00_0000 (low peripheral) +#define PBASE 0xFE000000 + +// The base address of the GIC-400 is 0x4c0040000. Note that, unlike other peripheral addresses in this document, this is an +// ARM-only address and not a legacy master address. If Low Peripheral mode is enabled this base address becomes +// 0xff840000. +// The GIC-400 is configured with "NUM_CPUS=4" and "NUM_SPIS=192". For full register details, please refer to the ARM +// GIC-400 documentation on the ARM Developer website. +#define GIC_BASE 0xFF840000 + +#define ARM_LOCAL_BASE 0xFF800000 + +// The ARMC register base address is 0x7e00b000 -> 0x4_7E00B000 -> 0x0FE00B000 +#define ARMC_BASE 0x0FE00B000 + +#endif /*_P_BASE_H */ diff --git a/exercises/lesson03/2/szediwy/include/peripherals/gpio.h b/exercises/lesson03/2/szediwy/include/peripherals/gpio.h new file mode 100644 index 00000000..fdc5c54f --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/peripherals/gpio.h @@ -0,0 +1,13 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "peripherals/base.h" + +#define GPFSEL1 (PBASE+0x00200004) +// #define GPSET0 (PBASE+0x0020001C) +// #define GPCLR0 (PBASE+0x00200028) +// #define GPPUD (PBASE+0x00200094) +// #define GPPUDCLK0 (PBASE+0x00200098) +#define GPIO_PUP_PDN_CNTRL_REG0 (PBASE+0x002000E4) + +#endif /*_P_GPIO_H */ diff --git a/exercises/lesson03/2/szediwy/include/peripherals/irq.h b/exercises/lesson03/2/szediwy/include/peripherals/irq.h new file mode 100644 index 00000000..3ffbfb8d --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/peripherals/irq.h @@ -0,0 +1,52 @@ +#ifndef _P_IRQ_H +#define _P_IRQ_H + +#include "peripherals/base.h" + + + +#define GIC_DIST_BASE (GIC_BASE+0x00001000) +#define GIC_CPU_BASE (GIC_BASE+0x00002000) + + + +// For interrupt ID m, when DIV and MOD are the integer division and modulo operations: +// • the corresponding GICD_ISENABLER number, n, is given by n = m DIV 32 +// • the offset of the required GICD_ISENABLER is (0x100 + (4*n)) +// • the bit number of the required Set-enable bit in this register is m MOD 32. +// +// VC Timer 1 = 1 => 97 / 32 = 3 => offset 0x10C +// +#define GIC_ENABLE_IRQ_BASE (GIC_DIST_BASE+0x00000100) +#define GIC_ENABLE_IRQ_3 (GIC_DIST_BASE+0x0000010C) + +#define GICC_IAR (GIC_CPU_BASE+0x0000000C) +#define GICC_EOIR (GIC_CPU_BASE+0x00000010) + +// For interrupt ID m, when DIV and MOD are the integer division and modulo operations: +// • the corresponding GICD_ITARGETSRn number, n, is given by n = m DIV 4 +// • the offset of the required GICD_ITARGETSR is (0x800 + (4*n)) +// • the byte offset of the required Priority field in this register is m MOD 4, where: +// — byte offset 0 refers to register bits [7:0] +// — byte offset 1 refers to register bits [15:8] +// — byte offset 2 refers to register bits [23:16] +// — byte offset 3 refers to register bits [31:24]. +#define GIC_IRQ_TARGET_BASE (GIC_DIST_BASE+0x00000800) + +// 97 / 4 => n = 24 +// 0x800 + 4*24 +// 97 % 4 => m = 1 +// byte offset = 1<<8 +#define GIC_IRQ_TARGET_24 (GIC_DIST_BASE+0x00000860) + +#define LOCAL_TIMER_IRQ (0x35) // Local timer IRQ + +#define SYSTEM_TIMER_IRQ_0 (0x60) // VC IRQ 0 +#define SYSTEM_TIMER_IRQ_1 (0x61) // VC IRQ 1 +#define SYSTEM_TIMER_IRQ_2 (0x62) // VC IRQ 2 +#define SYSTEM_TIMER_IRQ_3 (0x63) // VC IRQ 3 + +// +#define UART_IRQ (0x99) // VC IRQ 57 + +#endif /*_P_IRQ_H */ \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/peripherals/mini_uart.h b/exercises/lesson03/2/szediwy/include/peripherals/mini_uart.h new file mode 100644 index 00000000..177dfc17 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/peripherals/mini_uart.h @@ -0,0 +1,80 @@ +#ifndef _P_MINI_UART_H +#define _P_MINI_UART_H + +#include "peripherals/base.h" + +#define AUX_ENABLES (PBASE + 0x00215004) +#define AUX_MU_IO_REG (PBASE + 0x00215040) +#define AUX_MU_IER_REG (PBASE + 0x00215044) +#define AUX_MU_IIR_REG (PBASE + 0x00215048) +#define AUX_MU_LCR_REG (PBASE + 0x0021504C) +#define AUX_MU_MCR_REG (PBASE + 0x00215050) +#define AUX_MU_LSR_REG (PBASE + 0x00215054) +#define AUX_MU_MSR_REG (PBASE + 0x00215058) +#define AUX_MU_SCRATCH (PBASE + 0x0021505C) +#define AUX_MU_CNTL_REG (PBASE + 0x00215060) +#define AUX_MU_STAT_REG (PBASE + 0x00215064) +#define AUX_MU_BAUD_REG (PBASE + 0x00215068) + +#define PACTL_CS_REG (PBASE + 0x00204E00) + +#define UART0_BASE (PBASE + 0x00201000) +#define UART2_BASE (PBASE + 0x00201400) +#define UART3_BASE (PBASE + 0x00201600) +#define UART4_BASE (PBASE + 0x00201800) +#define UART5_BASE (PBASE + 0x00201a00) + +#define UART_DR_OFFSET 0x0 +#define UART_FR_OFFSET 0x18 +#define UART_IBRD_OFFSET 0x24 +#define UART_FBRD_OFFSET 0x28 +#define UART_LCRH_OFFSET 0x2C +#define UART_CR_OFFSET 0x30 + +// The UART_IFLS Register is the interrupt FIFO level select register. +// You can use this register to define the FIFO level that triggers the +// assertion of the combined interrupt signal. +// +// The interrupts are generated based on a transition through a level rather +// than being based on the level. That is, the interrupts are generated when +// the fill level progresses through the trigger level. The bits are reset +// so that the trigger level is when the FIFOs are at the half-way mark. +#define UART_IFLS_OFFSET 0x34 + +// Setting the appropriate mask bit HIGH enables the interrupt. +#define UART_IMSC_OFFSET 0x38 + +// Masked Interrupt Status Register +// The UARTMIS Register is the masked interrupt status register. It is a read-only register. +// This register returns the current masked status value of the corresponding interrupt. A +// write has no effect. +// All the bits except for the modem status interrupt bits (bits 3 to 0) are cleared to 0 when +// reset. The modem status interrupt bits are undefined after reset. Table 3-16 lists the +// register bit assignments. +#define UART_MIS_OFFSET 0x40 + +// The UART_ICR Register is the interrupt clear register. +#define UART_ICR_OFFSET 0x44 + +// The receive interrupt changes state when one of the following events occurs: +// • If the FIFOs are enabled and the receive FIFO reaches the programmed trigger +// level. When this happens, the receive interrupt is asserted HIGH. The receive +// interrupt is cleared by reading data from the receive FIFO until it becomes less +// than the trigger level, or by clearing the interrupt. +// • If the FIFOs are disabled (have a depth of one location) and data is received +// thereby filling the location, the receive interrupt is asserted HIGH. The receive +// interrupt is cleared by performing a single read of the receive FIFO, or by clearing +// the interrupt. +#define UART_IRQ_RXIM_MASK (1 << 4) + +// The receive timeout interrupt is asserted when the receive FIFO is not empty, and no +// more data is received during a 32-bit period. The receive timeout interrupt is cleared +// either when the FIFO becomes empty through reading all the data (or by reading the +// holding register), or when a 1 is written to the corresponding bit of the Interrupt Clear +// Register, UARTICR on page 3-21. +// TODO: 32-bit period - 32 cycles of UART clock? +#define UART_IRQ_RTIM_MASK (1 << 6) + +#define UART_RX_MASK (1 << 4) + +#endif /*_P_MINI_UART_H */ diff --git a/exercises/lesson03/2/szediwy/include/peripherals/timer.h b/exercises/lesson03/2/szediwy/include/peripherals/timer.h new file mode 100644 index 00000000..19cb556b --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/peripherals/timer.h @@ -0,0 +1,32 @@ +#ifndef _P_TIMER_H +#define _P_TIMER_H + +#include "peripherals/base.h" + +#define TIMER_CS (PBASE+0x00003000) +#define TIMER_CLO (PBASE+0x00003004) +#define TIMER_CHI (PBASE+0x00003008) +#define TIMER_C0 (PBASE+0x0000300C) +#define TIMER_C1 (PBASE+0x00003010) +#define TIMER_C2 (PBASE+0x00003014) +#define TIMER_C3 (PBASE+0x00003018) + +#define TIMER_CS_M0 (1 << 0) +#define TIMER_CS_M1 (1 << 1) +#define TIMER_CS_M2 (1 << 2) +#define TIMER_CS_M3 (1 << 3) + +// Local Timer Configuration. +// A free-running secondary timer is provided that can generate an interrupt each time it crosses zero. When it is +// enabled, the timer is decremented on each edge (positive or negative) of the crystal reference clock. It is +// automatically reloaded with the TIMER_TIMEOUT value when it reaches zero and then continues to decrement. +// Routing of the timer interrupt is controlled by the PERI_IRQ_ROUTE0 register +#define LOCAL_TIMER_CONTROL (ARM_LOCAL_BASE + 0x34) + +// Local Timer Interrupt Control +#define LOCAL_TIMER_IRQ (ARM_LOCAL_BASE + 0x38) + +// 54 MHz +#define LOCAL_TIMER_FREQ (54000000) + +#endif /*_P_TIMER_H */ \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/sysregs.h b/exercises/lesson03/2/szediwy/include/sysregs.h new file mode 100644 index 00000000..82b233e1 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/sysregs.h @@ -0,0 +1,45 @@ +#ifndef _SYSREGS_H +#define _SYSREGS_H + +// *************************************** +// SCTLR_EL1, System Control Register (EL1), Page 2654 of AArch64-Reference-Manual. +// *************************************** + +#define SCTLR_RESERVED (3 << 28) | (3 << 22) | (1 << 20) | (1 << 11) +#define SCTLR_EE_LITTLE_ENDIAN (0 << 25) +#define SCTLR_EOE_LITTLE_ENDIAN (0 << 24) +#define SCTLR_I_CACHE_DISABLED (0 << 12) +#define SCTLR_D_CACHE_DISABLED (0 << 2) +#define SCTLR_MMU_DISABLED (0 << 0) +#define SCTLR_MMU_ENABLED (1 << 0) + + +#define SCTLR_VALUE_MMU_DISABLED (SCTLR_RESERVED | SCTLR_EE_LITTLE_ENDIAN | SCTLR_I_CACHE_DISABLED | SCTLR_D_CACHE_DISABLED | SCTLR_MMU_DISABLED) + +// *************************************** +// HCR_EL2, Hypervisor Configuration Register (EL2), Page 2487 of AArch64-Reference-Manual. +// *************************************** + +#define HCR_RW (1 << 31) +#define HCR_VALUE HCR_RW + +// *************************************** +// SCR_EL3, Secure Configuration Register (EL3), Page 2648 of AArch64-Reference-Manual. +// *************************************** + +#define SCR_RESERVED (3 << 4) +#define SCR_RW (1 << 10) +#define SCR_NS (1 << 0) +#define SCR_VALUE (SCR_RESERVED | SCR_RW | SCR_NS) + +// *************************************** +// SPSR_EL3, Saved Program Status Register (EL3) Page 389 of AArch64-Reference-Manual. +// *************************************** + +#define SPSR_MASK_ALL (7 << 6) +#define SPSR_EL1h (5 << 0) +#define SPSR_VALUE (SPSR_MASK_ALL | SPSR_EL1h) + +#define CPACR_EL1_MASK (0 << 28 | 3 << 20 | 3 << 16) + +#endif \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/timer.h b/exercises/lesson03/2/szediwy/include/timer.h new file mode 100644 index 00000000..41d3a927 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/timer.h @@ -0,0 +1,10 @@ +#ifndef _TIMER_H +#define _TIMER_H + +void timer_init ( void ); +void handle_timer_irq ( void ); + +void local_timer_init (void); +void handle_local_timer_irq (void); + +#endif /*_TIMER_H */ \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/include/utils.h b/exercises/lesson03/2/szediwy/include/utils.h new file mode 100644 index 00000000..5b301011 --- /dev/null +++ b/exercises/lesson03/2/szediwy/include/utils.h @@ -0,0 +1,9 @@ +#ifndef _BOOT_H +#define _BOOT_H + +extern void delay(unsigned long); +extern void put32(unsigned long, unsigned int); +extern unsigned int get32(unsigned long); +extern unsigned int get_el(); + +#endif /*_BOOT_H */ diff --git a/exercises/lesson03/2/szediwy/src/boot.S b/exercises/lesson03/2/szediwy/src/boot.S new file mode 100644 index 00000000..49e398da --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/boot.S @@ -0,0 +1,148 @@ +#include "mm.h" +#include "sysregs.h" + +.section ".text.boot" + +.globl _start +_start: + mrs x0, mpidr_el1 + and x0, x0, #0x3 + cbz x0, init_bss + /* If processor id is not 0 then pending lock processor + * (wait for `sev` instruction) + */ + wfe + b master + +proc_hang: + b proc_hang + +init_bss: + adr x0, bss_begin + adr x1, bss_end + sub x1, x1, x0 + bl memzero + + sev + + /***********************************************************************/ + /* Enable the other cores + link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/arm64/booting.rst?h=v5.3#n255 + + The boot loader is expected to enter the kernel on each CPU in the + following manner: + + - The primary CPU must jump directly to the first instruction of the + kernel image. The device tree blob passed by this CPU must contain + an 'enable-method' property for each cpu node. The supported + enable-methods are described below. + + It is expected that the bootloader will generate these device tree + properties and insert them into the blob prior to kernel entry. + + - CPUs with a "spin-table" enable-method must have a 'cpu-release-addr' + property in their cpu node. This property identifies a + naturally-aligned 64-bit zero-initalised memory location. + + These CPUs should spin outside of the kernel in a reserved area of + memory (communicated to the kernel by a /memreserve/ region in the + device tree) polling their cpu-release-addr location, which must be + contained in the reserved region. A wfe instruction may be inserted + to reduce the overhead of the busy-loop and a sev will be issued by + the primary CPU. When a read of the location pointed to by the + cpu-release-addr returns a non-zero value, the CPU must jump to this + value. The value will be written as a single 64-bit little-endian + value, so CPUs must convert the read value to their native endianness + before jumping to it. + + - CPUs with a "psci" enable method should remain outside of + the kernel (i.e. outside of the regions of memory described to the + kernel in the memory node, or in a reserved area of memory described + to the kernel by a /memreserve/ region in the device tree). The + kernel will issue CPU_ON calls as described in ARM document number ARM + DEN 0022A ("Power State Coordination Interface System Software on ARM + processors") to bring CPUs into the kernel. + + The device tree should contain a 'psci' node, as described in + Documentation/devicetree/bindings/arm/psci.yaml. + + - Secondary CPU general-purpose register settings + x0 = 0 (reserved for future use) + x1 = 0 (reserved for future use) + x2 = 0 (reserved for future use) + x3 = 0 (reserved for future use) + */ + + /* cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <0>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000d8>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <1>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000e0>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <2>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000e8>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a72"; + reg = <3>; + enable-method = "spin-table"; + cpu-release-addr = <0x0 0x000000f0>; + }; */ + /****************************************************/ + mov x0, #0 + adr x0, configure_el1 + + mov x1, #0xe0 + str x0, [x1] + mov x1, #0xe8 + str x0, [x1] + mov x1, #0xf0 + str x0, [x1] + +configure_el1: + ldr x0, =SCTLR_VALUE_MMU_DISABLED + msr sctlr_el1, x0 + + ldr x0, =HCR_VALUE + msr hcr_el2, x0 + + + ldr x0, =CPACR_EL1_MASK + msr cpacr_el1, x0 + + ldr x0, =SPSR_VALUE + msr spsr_el2, x0 + + adr x0, master + msr elr_el2, x0 + + eret + +master: + mrs x0, mpidr_el1 + and x0, x0, #0x3 + + mov x1, #SECTION_SIZE + mul x1, x1, x0 + add x1, x1, #LOW_MEMORY + mov sp, x1 + + bl kernel_main + b proc_hang + diff --git a/exercises/lesson03/2/szediwy/src/config.txt b/exercises/lesson03/2/szediwy/src/config.txt new file mode 100644 index 00000000..51e25029 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/config.txt @@ -0,0 +1,4 @@ +arm_64bit=1 +enable_uart=1 +uart_2ndstage=1 +disable_commandline_tags=1 \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/custom_printf.c b/exercises/lesson03/2/szediwy/src/custom_printf.c new file mode 100644 index 00000000..a4b62218 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/custom_printf.c @@ -0,0 +1,249 @@ +/* +File: printf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "custom_printf.h" + +typedef void (*putcf)(void *, char); +static putcf stdout_putf; +static void *stdout_putp; + +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + while (num / d >= base) + d *= base; + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void li2a(long num, char *bf) +{ + if (num < 0) + { + num = -num; + *bf++ = '-'; + } + uli2a(num, 10, 0, bf); +} + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + while (num / d >= base) + d *= base; + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void i2a(int num, char *bf) +{ + if (num < 0) + { + num = -num; + *bf++ = '-'; + } + ui2a(num, 10, 0, bf); +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return -1; +} + +static char a2i(char ch, char **src, int base, int *nump) +{ + char *p = *src; + int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) + { + if (digit > base) + break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, int n, char z, char *bf) +{ + char fc = z ? '0' : ' '; + char ch; + char *p = bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp, fc); + while ((ch = *bf++)) + putf(putp, ch); +} + +void tfp_format(void *putp, putcf putf, char *fmt, va_list va) +{ + + char bf[12]; + + char ch; + + while ((ch = *(fmt++))) + { + if (ch != '%') + { + putf(putp, ch); + } + else + { + char lz = 0; +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; +#endif + int w = 0; + ch = *(fmt++); + if (ch == '0') + { + ch = *(fmt++); + lz = 1; + } + if (ch >= '0' && ch <= '9') + { + ch = a2i(ch, &fmt, 10, &w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') + { + ch = *(fmt++); + lng = 1; + } +#endif + switch (ch) + { + case 0: + goto abort; + case 'u': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 10, 0, bf); + else +#endif + ui2a(va_arg(va, unsigned int), 10, 0, bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'd': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int), bf); + else +#endif + i2a(va_arg(va, int), bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'x': + case 'X': +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 16, (ch == 'X'), bf); + else +#endif + ui2a(va_arg(va, unsigned int), 16, (ch == 'X'), bf); + putchw(putp, putf, w, lz, bf); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + putchw(putp, putf, w, 0, va_arg(va, char *)); + break; + case '%': + putf(putp, ch); + default: + break; + } + } + } +abort:; +} + +void init_printf(void *putp, void (*putf)(void *, char)) +{ + stdout_putf = putf; + stdout_putp = putp; +} + +void tfp_printf(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(stdout_putp, stdout_putf, fmt, va); + va_end(va); +} + +static void putcp(void *p, char c) +{ + *(*((char **)p))++ = c; +} + +void tfp_sprintf(char *s, char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(&s, putcp, fmt, va); + putcp(&s, 0); + va_end(va); +} \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/debug.c b/exercises/lesson03/2/szediwy/src/debug.c new file mode 100644 index 00000000..e4941fdb --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/debug.c @@ -0,0 +1,21 @@ +#include "custom_printf.h" + +void d0(void) { + printf("#0"); +} + +void d1(void) { + printf("#1"); +} + +void d2(void) { + printf("#2"); +} + +void d3(void) { + printf("#3"); +} + +void d4(void) { + printf("#4"); +} \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/entry.S b/exercises/lesson03/2/szediwy/src/entry.S new file mode 100644 index 00000000..288f8917 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/entry.S @@ -0,0 +1,136 @@ +#include "entry.h" + +.macro handle_invalid_entry type + kernel_entry + mov x0, #\type + mrs x1, esr_el1 + mrs x2, elr_el1 + bl show_invalid_entry_message + b err_hang +.endm + +.macro ventry label + .align 7 + b \label +.endm + +.macro kernel_entry + sub sp, sp, #S_FRAME_SIZE + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + str x30, [sp, #16 * 15] +.endm + +.macro kernel_exit + ldp x0, x1, [sp, #16 * 0] + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] + ldp x8, x9, [sp, #16 * 4] + ldp x10, x11, [sp, #16 * 5] + ldp x12, x13, [sp, #16 * 6] + ldp x14, x15, [sp, #16 * 7] + ldp x16, x17, [sp, #16 * 8] + ldp x18, x19, [sp, #16 * 9] + ldp x20, x21, [sp, #16 * 10] + ldp x22, x23, [sp, #16 * 11] + ldp x24, x25, [sp, #16 * 12] + ldp x26, x27, [sp, #16 * 13] + ldp x28, x29, [sp, #16 * 14] + ldr x30, [sp, #16 * 15] + add sp, sp, #S_FRAME_SIZE + eret +.endm + + +/* + * Exception vectors. + */ +.align 11 +.globl vectors +vectors: + ventry sync_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + ventry sync_invalid_el1h // Synchronous EL1h + ventry el1_irq // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_el1h // Error EL1h + + ventry sync_invalid_el0_64 // Synchronous 64-bit EL0 + ventry irq_invalid_el0_64 // IRQ 64-bit EL0 + ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + +sync_invalid_el1t: + handle_invalid_entry SYNC_INVALID_EL1t + +irq_invalid_el1t: + handle_invalid_entry IRQ_INVALID_EL1t + +fiq_invalid_el1t: + handle_invalid_entry FIQ_INVALID_EL1t + +error_invalid_el1t: + handle_invalid_entry ERROR_INVALID_EL1t + +sync_invalid_el1h: + handle_invalid_entry SYNC_INVALID_EL1h + +fiq_invalid_el1h: + handle_invalid_entry FIQ_INVALID_EL1h + +error_invalid_el1h: + handle_invalid_entry ERROR_INVALID_EL1h + +sync_invalid_el0_64: + handle_invalid_entry SYNC_INVALID_EL0_64 + +irq_invalid_el0_64: + handle_invalid_entry IRQ_INVALID_EL0_64 + +fiq_invalid_el0_64: + handle_invalid_entry FIQ_INVALID_EL0_64 + +error_invalid_el0_64: + handle_invalid_entry ERROR_INVALID_EL0_64 + +sync_invalid_el0_32: + handle_invalid_entry SYNC_INVALID_EL0_32 + +irq_invalid_el0_32: + handle_invalid_entry IRQ_INVALID_EL0_32 + +fiq_invalid_el0_32: + handle_invalid_entry FIQ_INVALID_EL0_32 + +error_invalid_el0_32: + handle_invalid_entry ERROR_INVALID_EL0_32 + +el1_irq: + kernel_entry + bl handle_irq + kernel_exit + +.globl err_hang +err_hang: b err_hang \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/irq.S b/exercises/lesson03/2/szediwy/src/irq.S new file mode 100644 index 00000000..f6e830cb --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/irq.S @@ -0,0 +1,15 @@ +.globl irq_vector_init +irq_vector_init: + adr x0, vectors // load VBAR_EL1 with virtual + msr vbar_el1, x0 + ret + +.globl enable_irq +enable_irq: + msr daifclr, #0b0010 + ret + +.globl disable_irq +disable_irq: + msr daifset, #0b0010 + ret \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/irq.c b/exercises/lesson03/2/szediwy/src/irq.c new file mode 100644 index 00000000..407d4835 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/irq.c @@ -0,0 +1,108 @@ +#include "utils.h" +#include "custom_printf.h" +#include "timer.h" +#include "entry.h" +#include "peripherals/irq.h" +#include "sysregs.h" +#include "mini_uart.h" + +const char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; + +void enable_interrupt(unsigned int irq) { + + // For interrupt ID m, when DIV and MOD are the integer division and modulo operations: + // • the corresponding GICD_ISENABLER number, n, is given by n = m DIV 32 + // • the offset of the required GICD_ISENABLER is (0x100 + (4*n)) + // • the bit number of the required Set-enable bit in this register is m MOD 32. + + unsigned int n = irq / 32; + unsigned int offset = irq % 32; + unsigned int enableRegister = GIC_ENABLE_IRQ_BASE + (4*n); + + put32(enableRegister, 1 << offset); +} + +void assign_target(unsigned int irq, unsigned int cpu) { + // For interrupt ID m, when DIV and MOD are the integer division and modulo operations: + // • the corresponding GICD_ITARGETSRn number, n, is given by n = m DIV 4 + // • the offset of the required GICD_ITARGETSR is (0x800 + (4*n)) + // • the byte offset of the required Priority field in this register is m MOD 4, where: + // — byte offset 0 refers to register bits [7:0] + // — byte offset 1 refers to register bits [15:8] + // — byte offset 2 refers to register bits [23:16] + // — byte offset 3 refers to register bits [31:24]. + + unsigned int n = irq / 4; + unsigned int offset = irq % 4; + unsigned int targetRegister = GIC_IRQ_TARGET_BASE + (4*n); + + // Currently we only enter the target CPU 0 + put32(targetRegister, get32(targetRegister) | (1 << (offset*8))); +} + + +void enable_interrupt_controller() { + // assign_target(SYSTEM_TIMER_IRQ_1, 0); + // enable_interrupt(SYSTEM_TIMER_IRQ_1); + + // assign_target(LOCAL_TIMER_IRQ, 0); + // enable_interrupt(LOCAL_TIMER_IRQ); + + assign_target(UART_IRQ, 0); + enable_interrupt(UART_IRQ); +} + +void show_invalid_entry_message(int type, unsigned long esr, unsigned long address) +{ + printf("%s, ESR: %x, address: %x\r\n", entry_error_messages[type], esr, address); +} + +void handle_irq(void) +{ + // NOTE (ARM® Generic Interrupt Controller - Architecture version 2.0): + // For compatibility with possible extensions to the GIC architecture specification, ARM recommends that software + // preserves the entire register value read from the GICC_IAR when it acknowledges the interrupt, and uses that entire + // value for its corresponding write to the GICC_EOIR. + unsigned int irqAckRegister = get32(GICC_IAR); + + unsigned int irq = irqAckRegister & 0x2FF; // bit [9:0] -> interrupt ID + switch (irq) { + case (SYSTEM_TIMER_IRQ_1): + handle_timer_irq(); + put32(GICC_EOIR, irqAckRegister); + break; + case (LOCAL_TIMER_IRQ): + handle_local_timer_irq(); + put32(GICC_EOIR, irqAckRegister); + break; + case (UART_IRQ): + handle_uart_interrupt(); + put32(GICC_EOIR, irqAckRegister); + break; + default: + printf("Unknown pending irq: 0x%x\r\n", irq); + } + + + +} \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/kernel.c b/exercises/lesson03/2/szediwy/src/kernel.c new file mode 100644 index 00000000..0d7827df --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/kernel.c @@ -0,0 +1,45 @@ +#include "custom_printf.h" +#include "timer.h" +#include "utils.h" +#include "mini_uart.h" +#include "irq.h" + +void kernel_main(unsigned long processor_index) +{ + static unsigned int current_processor_index = 0; + + if (processor_index == 0) + { + uart_init(); + init_printf(0, putc); + printf("irq_vector_init\r\n"); + irq_vector_init(); + // printf("timer_init\r\n"); + // timer_init(); + // printf("local_timer_init\r\n"); + // local_timer_init(); + printf("enable_interrupt_controller\r\n"); + enable_interrupt_controller(); + printf("enable_irq\r\n"); + enable_irq(); + } + + while (processor_index != current_processor_index) + ; + + int exception_level = get_el(); + printf("{CPU: %d, Exception level: %d}\r\n", processor_index, exception_level); + + current_processor_index++; + + if (processor_index == 0) + { + // if current_processor_index == 4 then all processors send message + while (current_processor_index != 4) + ; + for (;;) + { + // uart_send(uart_recv()); + } + } +} \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/linker.ld b/exercises/lesson03/2/szediwy/src/linker.ld new file mode 100644 index 00000000..3af650df --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/linker.ld @@ -0,0 +1,12 @@ +SECTIONS +{ + . = 0x80000; + .text.boot : { *(.text.boot) } + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + . = ALIGN(0x8); + bss_begin = .; + .bss : { *(.bss*) } + bss_end = .; +} diff --git a/exercises/lesson03/2/szediwy/src/mini_uart.c b/exercises/lesson03/2/szediwy/src/mini_uart.c new file mode 100644 index 00000000..a450ab30 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/mini_uart.c @@ -0,0 +1,134 @@ +#include "utils.h" +#include "peripherals/mini_uart.h" +#include "peripherals/gpio.h" +#include "custom_printf.h" + +void uart_send(char c) +{ + while (get32(UART0_BASE + UART_FR_OFFSET) & (1 << 5)) + { + } + put32(UART0_BASE + UART_DR_OFFSET, c); +} + +char uart_recv(void) +{ + while (get32(UART0_BASE + UART_FR_OFFSET) & UART_RX_MASK) + { + } + return (get32(UART0_BASE + UART_DR_OFFSET) & 0xFF); +} + +void uart_send_string(char *str) +{ + for (int i = 0; str[i] != '\0'; i++) + { + uart_send((char)str[i]); + } +} + +void uart_init(void) +{ + + unsigned int selector; + + selector = get32(GPFSEL1); + selector &= ~(7 << 12); // clean gpio14 + selector |= 4 << 12; // set alt0 for gpio14 + selector &= ~(7 << 15); // clean gpio15 + selector |= 4 << 15; // set alt0 for gpio15 + put32(GPFSEL1, selector); + + unsigned int pullRegister; + pullRegister = get32(GPIO_PUP_PDN_CNTRL_REG0); + pullRegister &= ~(3 << 30); + pullRegister &= ~(3 << 28); + put32(GPIO_PUP_PDN_CNTRL_REG0, pullRegister); + + // first disable uart + put32(UART0_BASE + UART_CR_OFFSET, 0); + + // unmask all interrupts -> disable them + put32(UART0_BASE + UART_IMSC_OFFSET, 0); + + //from ../adkaster/src/uart.c + // Assume 48MHz UART Reference Clock (Standard) + // Calculate UART clock divider per datasheet + // BAUDDIV = (FUARTCLK/(16 Baud rate)) + // Note: We get 6 bits of fraction for the baud div + // 48000000/(16 * 115200) = 3000000/115200 = 26.0416666... + // Integer part = 26 :) + // From http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0183g/I54603.html + // we want floor(0.04166666.. * 64 + 0.5) = 3 + put32(UART0_BASE + UART_IBRD_OFFSET, 26); + put32(UART0_BASE + UART_FBRD_OFFSET, 3); + + //0111|0000 => 8 bits and enable fifos + put32(UART0_BASE + UART_LCRH_OFFSET, 7 << 4); + + // if the fifo queue is enabled set the lowest levels to trigger an interrupt. + put32(UART0_BASE + UART_IFLS_OFFSET, 0); + + // 0011|0000|0001 => enable: rx tx uart + put32(UART0_BASE + UART_CR_OFFSET, (1 << 9) | (1 << 8) | (1 << 0)); + + // enable the RX and RX timeout interrupt + put32(UART0_BASE + UART_IMSC_OFFSET, (UART_IRQ_RXIM_MASK | UART_IRQ_RTIM_MASK)); +} + +// This function is required by printf function +void putc(void *p, char c) +{ + uart_send(c); +} + +void handle_uart_interrupt(void) +{ + + // Which UART was the interrupt source + // UART5 IRQ bit 16 + // UART4 IRQ bit 17 + // UART3 IRQ bit 18 + // UART2 IRQ bit 19 + // UART0 IRQ bit 20 + unsigned int uart = get32(PACTL_CS_REG) & (0b11111 << 16); + switch (uart) + { + case 1 << 20: + uart = 0; + break; + case 1 << 19: + uart = 2; + break; + case 1 << 18: + uart = 3; + break; + case 1 << 17: + uart = 4; + break; + case 1 << 16: + uart = 5; + break; + default: + uart = -1; + } + + static unsigned int uart_bases[] = {UART0_BASE, -1, UART2_BASE, UART3_BASE, UART4_BASE, UART5_BASE}; + + unsigned int base = uart_bases[uart]; + + + // Read the exact interrupt type from the masked interrupt status register + // and check whether it is a RX + unsigned int uart_irq_status = get32(base + UART_MIS_OFFSET); + + printf("Detected interrupt 0x%x for UART%d at 0x%x: ", uart_irq_status, uart, base); + + if (uart_irq_status & (UART_IRQ_RXIM_MASK | UART_IRQ_RTIM_MASK)) + { + uart_send(uart_recv()); + } + + // Clear the interrupt + put32(base + UART_ICR_OFFSET, uart_irq_status); +} diff --git a/exercises/lesson03/2/szediwy/src/mm.S b/exercises/lesson03/2/szediwy/src/mm.S new file mode 100644 index 00000000..1bd32ff3 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/mm.S @@ -0,0 +1,6 @@ +.globl memzero +memzero: + str xzr, [x0], #8 + subs x1, x1, #8 + b.gt memzero + ret diff --git a/exercises/lesson03/2/szediwy/src/timer.c b/exercises/lesson03/2/szediwy/src/timer.c new file mode 100644 index 00000000..a5b6865d --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/timer.c @@ -0,0 +1,32 @@ +#include "utils.h" +#include "custom_printf.h" +#include "peripherals/timer.h" + +const unsigned int interval = 200000; +unsigned int curVal = 0; + +void timer_init ( void ) +{ + curVal = get32(TIMER_CLO); + curVal += interval; + put32(TIMER_C1, curVal); +} + +void handle_timer_irq( void ) +{ + curVal += interval; + put32(TIMER_C1, curVal); + put32(TIMER_CS, TIMER_CS_M1); + printf("Timer interrupt received\n\r"); +} + +void handle_local_timer_irq( void ) +{ + printf("LOCAL_TIMER_IRQ received\n\r"); + put32(LOCAL_TIMER_IRQ, (3<<30)); +} + +void local_timer_init (void) { + // Enable the timer + put32(LOCAL_TIMER_CONTROL, (3<<28) | (3*LOCAL_TIMER_FREQ)); +} \ No newline at end of file diff --git a/exercises/lesson03/2/szediwy/src/utils.S b/exercises/lesson03/2/szediwy/src/utils.S new file mode 100644 index 00000000..f6db63c5 --- /dev/null +++ b/exercises/lesson03/2/szediwy/src/utils.S @@ -0,0 +1,21 @@ +.globl put32 +put32: + str w1,[x0] + ret + +.globl get32 +get32: + ldr w0,[x0] + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl get_el +get_el: + mrs x0, CurrentEL + lsr x0, x0, #2 + ret \ No newline at end of file