├── .dockerignore ├── .gitignore ├── Assembler00_CallingFunctions └── README.md ├── Assembler00_Faculty ├── build.sh ├── faculty.S └── linker.ld ├── Assembler00_Registers └── README.md ├── Dockerfile ├── Lesson00_Debugging └── README.md ├── Lesson00_Interrupts_32bit ├── Makefile ├── README.md ├── kernel.ld └── main.s ├── Lesson00_Interrupts_32bit_C ├── README.md ├── armc-013-cstartup.c ├── armc-013-start.S ├── armc-013.c ├── build.sh ├── linker.ld ├── rpi-armtimer.c ├── rpi-armtimer.h ├── rpi-base.h ├── rpi-gpio.c ├── rpi-gpio.h ├── rpi-interrupts-controller.c ├── rpi-interrupts.c ├── rpi-interrupts.h ├── rpi-systimer.c └── rpi-systimer.h ├── Lesson00_LED_ON ├── LICENSE ├── Makefile ├── README.md ├── kernel.ld ├── kernel.list └── source │ ├── main.s │ └── main.s_LED_ON ├── Lesson00_RasperryPiBoards └── README.md ├── Lesson00_Toolchains └── README.md ├── Lesson00_UART_32bit ├── Makefile ├── README.md ├── kernel.ld └── main.s ├── Lesson00_UART_32bit_C ├── README.md ├── boot.s ├── boot.s.64bit ├── kernel.c └── linker.ld ├── Lesson00_UART_EchoLoop_32bit ├── Makefile ├── README.md ├── kernel.ld └── main.s ├── Lesson00_newlib └── README.md ├── Lesson01_Assembler ├── Makefile ├── README.md ├── link.ld └── start.S ├── Lesson02_C_64bit ├── Makefile ├── README.md ├── kernel.c ├── link.ld └── start.s ├── Lesson03_printf_64bit ├── Makefile ├── README.md ├── kernel.c ├── link.ld └── start.s ├── Lesson04_Heap └── README.md ├── Lesson04_VirtualMemory ├── README.md └── resources │ └── FirstLevelLookup.png ├── Lesson06_Multitasking └── README.md ├── LessonX1_FileSystem └── README.md ├── LessonX2_ELF_Loader └── README.md ├── LessonX3_C_Standard_Library └── README.md ├── LessonXX_Bluetooth └── README.md ├── LessonXX_Ethernet_TCPIP └── README.md ├── LessonXX_JavaScript └── README.md ├── LessonXX_USB └── README.md ├── LessonXX_libusb └── README.md ├── README.md ├── doc ├── Docker │ └── README.md ├── Environment │ └── README.md ├── GCC_Assembler_ARM │ └── README.md ├── USB │ ├── 46725298.pdf │ ├── 7218409.pdf │ ├── a10_54018.pdf │ ├── hid1_11.pdf │ ├── usb_20_20190524.zip │ └── usb_20_20190524 │ │ ├── 2002_05_28_errata.pdf │ │ ├── 5V Short Circuit Withstand ECN.pdf │ │ ├── Connect Timing ECN.pdf │ │ ├── Device Capacitance ECN.pdf │ │ ├── Disconnect_Supplement_HSIC_v1.0_18Sep2013.pdf │ │ ├── ECN for Core Spec TEST_MODE selector values.pdf │ │ ├── HSIC_ECN_21May2012.pdf │ │ ├── High Speed Inter-Chip_1_0 final.pdf │ │ ├── Inter-Chip USB Supplement to the USB 2.0 Specification_1.0.pdf │ │ ├── InterfaceAssociationDescriptor_ecn.pdf │ │ ├── Micro-USB_final │ │ ├── Clarification on the Chamfer on USB 2 0 Micro Connectors ECN .pdf │ │ ├── Maximum Unmating Force Value Definition to USB 2 0 Micro Connectors ECN.pdf │ │ ├── Micro-USB Cables and Connectors ADOPTERS AGREEMENT - Device Class Spec.pdf │ │ ├── Micro-USB ECN for Cable R3.pdf │ │ ├── Micro-USB_1_01.pdf │ │ ├── MicroUSB ECN.pdf │ │ ├── XV-4687C ECN Proposal for Micro USB.pdf │ │ └── XV-4687C_Supplement Quad cable R1.pdf │ │ ├── Suspend Current ECN.pdf │ │ ├── USB 2 0 Material Change ECN.pdf │ │ ├── USB 2.0 ECN VBUS Max Limit.pdf │ │ ├── USB 2.0 Phase-Locked SOFs ECN.pdf │ │ ├── USB 2_0 Adopters Agreement_final_021411.pdf │ │ ├── USB OTG and Embedded Host │ │ ├── USB OTG ECN │ │ │ └── USB OTGEH ECN Maximum Vbus Voltage OTGEH2.0.1.1A.001.pdf │ │ ├── USB OTG v2.0_ Amendment final_052609.pdf │ │ └── USB_OTG_and_EH_2-0-version 1_1a.pdf │ │ ├── USB2-LPM-Errata-final.pdf │ │ ├── USB2_LinkPowerMangement_ECN[final].pdf │ │ ├── UnicodeECN.pdf │ │ ├── ecn1-usb20-miniB-revd.pdf │ │ ├── errata-092800-1207001.pdf │ │ ├── resistor_ecn.pdf │ │ ├── rnd_chmfr_ECNFINAL.pdf │ │ └── usb_20.pdf ├── arm_spec │ ├── 00002306A.pdf │ ├── 01120a.pdf │ ├── BCM2835-ARM-Peripherals.pdf │ ├── DAI0527A_baremetal_boot_code_for_ARMv8_A_processors.pdf │ ├── DDI01001.pdf │ ├── DDI0301H_arm1176jzfs_r0p7_trm.pdf │ ├── DDI0406C_arm_architecture_reference_manual.pdf │ ├── DDI0487Fc_armv8_arm.pdf │ ├── DEN0024A_v8_architecture_PG.pdf │ ├── arm-instructionset.pdf │ ├── armv6.pdf │ ├── evb9512user-468043.pdf │ ├── lan9512.pdf │ └── lan9514_lan9514i databook rev. 1.2 (03-01-12).pdf ├── armv6_instruction_set │ └── README.md ├── git │ └── README.md ├── lan9500_linux_1.02.05.tar.gz └── timers │ └── README.md ├── gcc_as_formatter ├── .classpath ├── .gitignore ├── .project ├── .settings │ ├── org.eclipse.core.resources.prefs │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.m2e.core.prefs ├── pom.xml └── src │ ├── main │ ├── antlr4 │ │ └── gccas │ │ │ └── gcc_as.g4 │ └── java │ │ └── asm │ │ ├── Main.java │ │ └── gccas │ │ ├── OutputListener.java │ │ ├── TreeListener.java │ │ └── node │ │ ├── DirectiveNode.java │ │ ├── InstructionNode.java │ │ ├── Node.java │ │ ├── ParameterListNode.java │ │ └── PreprocessorNode.java │ └── test │ └── resources │ ├── SmartStart32.S │ ├── test1.S │ ├── test2.S │ └── test3.S ├── input01 ├── LICENSE ├── Makefile ├── kernel.ld └── source │ ├── drawing.s │ ├── font.bin │ ├── frameBuffer.s │ ├── gpio.s │ ├── keyboard.s │ ├── mailbox.s │ ├── main.s │ ├── maths.s │ ├── random.s │ ├── systemTimer.s │ ├── tags.s │ ├── text.s │ └── uart.s └── resources ├── ARMWebSiteDocumentDownload.png ├── RegisterUseInArmProcedureCallStandard.png ├── Screenshot 2021-06-03 at 10.40.45.png ├── Screenshot 2021-06-03 at 10.41.40.png ├── Screenshot 2021-06-03 at 11.51.01.png ├── Screenshot 2021-06-03 at 11.51.19.png ├── Screenshot 2021-06-03 at 11.51.23.png ├── Screenshot 2021-06-13 at 13.00.05.png ├── Screenshot 2021-07-07 at 12.02.32.png ├── Screenshot 2021-07-07 at 12.02.39.png ├── Screenshot 2021-07-13 at 15.53.11.png ├── config.txt.original_RPi_ModelBplus ├── ld-linux-aarch64.so.1 └── libc.so.6 /.dockerignore: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # kernel elf and images 2 | *.elf 3 | *.img 4 | *.map 5 | *.list 6 | 7 | # Prerequisites 8 | *.d 9 | 10 | # Compiled Object files 11 | *.slo 12 | *.lo 13 | *.o 14 | *.obj 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Compiled Dynamic libraries 21 | *.so 22 | *.dylib 23 | *.dll 24 | 25 | # Fortran module files 26 | *.mod 27 | *.smod 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | 40 | # folders 41 | /rpi3b-bare-metal/* 42 | /raspberrypi/* 43 | /arm-tutorial-rpi/* -------------------------------------------------------------------------------- /Assembler00_CallingFunctions/README.md: -------------------------------------------------------------------------------- 1 | # Calling Functions / Routines 2 | 3 | https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/how-to-call-a-function-from-arm-assembler 4 | 5 | Routine and Function are used synonymous. 6 | 7 | Functions allow to group instructions together that fulfill a recuring functionality. The functionality can be reused more easily or even moved into a library for reuse. Also functions help with organizing code. When choosing a descriptive function name, the code also gets some basic documentation. 8 | 9 | The branch with link (bl) instruction jumps to a label and saves the current address, that is currently stored in the programm counter register (r15) into the link register (r14). 10 | 11 | The inverse operation to bl is to copy the value from the link register (r14) back into the program counter register (r15) which effectively returns from the function and makes the CPU pick up after the function call. 12 | 13 | ``` 14 | ... 15 | bl routine_1 16 | ... 17 | 18 | routine_1: 19 | ... 20 | 21 | // return from the function 22 | mov pc, lr 23 | ``` 24 | 25 | This basic principle has to be extended. 26 | 27 | It has to cover 28 | 29 | - Passing paramters to the function (scratch registers r0-r3 and r12) 30 | - Returning a value from a function call (return value) 31 | - Storing and restoring of preserve registers (r4 - r11). 32 | - Nested calls to functions within functions (Recursion is a special case of nesting) which means storing the return address and restoring it after the nested function call. 33 | - How to call functions from C code. 34 | 35 | # Nested Calls 36 | 37 | Setting up a stack 38 | 39 | # Call a assembler function from C code 40 | 41 | ## In assembly 42 | 43 | 1. Define the function 44 | 45 | ``` 46 | /* Check if another character can be written and if the fifo is empty, write the character. 47 | The character is expected to be stored in r2. 48 | */ 49 | writeCharacter: 50 | ldr r3, [r1, #0x18] 51 | and r3, r3, #0x20 52 | cmp r3, #0 53 | bne writeCharacter 54 | str r2, [r1, #0x0000] /* str r2, [r1, #0x0000] actually writes a character to the UART */ 55 | 56 | /* return to caller. r15 is the programm counter. r14 is the link register */ 57 | mov pc, lr 58 | ``` 59 | 60 | 2. Define the function as .globl or .global so it can be seen from C code 61 | 62 | ``` 63 | .globl writeCharacter 64 | ``` 65 | 66 | ## In the C code 67 | 68 | 1. define the function prototype as extern: 69 | extern void writeCharacter(); 70 | 71 | 2. Call the function in C 72 | writeCharacter(); 73 | 74 | 3. link all object files together 75 | 76 | # Passing parameters to assembler function from C functions 77 | 78 | https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/function-parameters-on-32-bit-arm 79 | 80 | When a function defined in assembler is called from C code, the supplied parameters are inserted into the registers 81 | r0, r1, r2, r3 82 | 83 | what happens if there are more than 4 parameters? 84 | 85 | That means a function call like this: 86 | 87 | ``` 88 | writeCharacter('a', 'a', 'a'); 89 | ``` 90 | 91 | puts the character 'a' into r0, r1 and r2. r3 is unchanged and still contains the value it did contain before. 92 | 93 | # Returning values from assembler to C functions 94 | 95 | The return value is expected in r0. 96 | 97 | Example: 98 | 99 | ``` 100 | _get_stack_pointer: 101 | // Return the stack pointer value 102 | str sp, [sp] 103 | ldr r0, [sp] 104 | 105 | // return from the function 106 | mov pc, lr 107 | ``` 108 | -------------------------------------------------------------------------------- /Assembler00_Faculty/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function delete { 4 | rm -f *.o 5 | rm -f kernel.elf 6 | rm -f kernel.img 7 | rm -f kernel7.img 8 | rm -f kernel.list 9 | rm -f kernel.map 10 | } 11 | 12 | function compile { 13 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -c faculty.S -o faculty.o 14 | 15 | #arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c armc-013.c -o armc-013.o 16 | 17 | arm-none-eabi-gcc -T linker.ld -o kernel.elf -ffreestanding -O2 -nostdlib faculty.o -lgcc 18 | 19 | arm-none-eabi-objcopy kernel.elf -O binary kernel.img 20 | } 21 | 22 | case "$1" in 23 | -c | --clean | --clear) 24 | delete 25 | ;; 26 | 27 | * ) 28 | delete 29 | compile 30 | ;; 31 | esac 32 | -------------------------------------------------------------------------------- /Assembler00_Faculty/faculty.S: -------------------------------------------------------------------------------- 1 | .section ".text.startup" 2 | 3 | .globl _start 4 | _start: 5 | 6 | /* set the stack pointer */ 7 | ldr sp, =0x7000 8 | 9 | /* init serial */ 10 | bl initUart 11 | 12 | /* set parameter 1, the value to compute the faculty for */ 13 | mov r0, #0x03 14 | 15 | bl faculty 16 | 17 | /* r0 contains the result */ 18 | 19 | /* Convert the integer in r0 to the ASCII code of the character representing that number by adding 0x30 */ 20 | add r0, r0, #0x30 21 | 22 | end_of_app: 23 | /* output character stored in r2 */ 24 | mov r2, r0 25 | bl writeCharacter 26 | 27 | /* output H 28 | mov r2, #0x48 29 | bl writeCharacter*/ 30 | 31 | /* output space */ 32 | mov r2, #0x20 33 | bl writeCharacter 34 | 35 | bl delay 36 | 37 | bal end_of_app 38 | 39 | 40 | 41 | 42 | faculty: 43 | 44 | push {lr} 45 | 46 | cmp r0, #0x00 47 | bne continue_faculty 48 | mov r0, #0x01 49 | b return_faculty 50 | 51 | continue_faculty: 52 | push {r0} 53 | sub r0, r0, #0x01 54 | bl faculty 55 | mov r1, r0 56 | 57 | pop {r0} 58 | 59 | mul r0, r0, r1 60 | 61 | return_faculty: 62 | pop {lr} 63 | /* return to caller. r15 is the programm counter. r14 is the link register */ 64 | mov pc, lr 65 | 66 | 67 | 68 | 69 | /* Make the system wait */ 70 | delay: 71 | mov r2, #0 72 | wait: 73 | add r2, r2, #1 74 | cmp r2, #0x00400000 75 | bne wait 76 | 77 | /* return to caller. r15 is the programm counter. r14 is the link register */ 78 | mov pc, lr 79 | 80 | 81 | 82 | 83 | /* Check if another character can be written and if the fifo is empty, write the character */ 84 | /* write value from r2 to the serial connection */ 85 | writeCharacter: 86 | /* r0 is GPIO base, 0x20200000 */ 87 | mov r0, #0x20000000 88 | mov r1, #0x00200000 89 | orr r0, r0, r1 90 | 91 | /* r1 is UART base */ 92 | orr r1, r0, #0x1000 93 | 94 | /* Check if another character can be written */ 95 | ldr r3, [r1, #0x18] 96 | and r3, r3, #0x20 97 | cmp r3, #0 98 | bne writeCharacter 99 | 100 | /* str r2, [r1, #0x0000] actually writes a character to the UART */ 101 | str r2, [r1, #0x0000] 102 | 103 | /* return to caller. r15 is the programm counter. r14 is the link register */ 104 | mov pc, lr 105 | 106 | 107 | 108 | 109 | /* set up the hardware that can transfer data over a serial connection */ 110 | initUart: 111 | /* r0 is GPIO base, 0x20200000 */ 112 | mov r0, #0x20000000 113 | mov r1, #0x00200000 114 | orr r0, r0, r1 115 | 116 | /* r1 is UART base */ 117 | orr r1, r0, #0x1000 118 | 119 | /* disable UART by writing 0 to the UART_CR register */ 120 | mov r2, #0x00000000 121 | str r2, [r1, #0x30] 122 | 123 | /* write ggpud, pud = pull up / down */ 124 | /* Disable pull up/down for all GPIO pins */ 125 | /* 0x94 = 10010100 */ 126 | str r2, [r0, #0x94] 127 | 128 | /* nested function call, save link register */ 129 | mov r11, lr 130 | /* wait */ 131 | bl delay 132 | /* nested function call, restore link register */ 133 | mov lr, r11 134 | 135 | /* Write 1<<14 | 1<<15 = 0xC000 into the GPPUDCLK0 register. */ 136 | mov r2, #0xC000 137 | /* 0x98 = 10011000 */ 138 | str r2, [r0, #0x98] 139 | 140 | /* nested function call, save link register */ 141 | mov r11, lr 142 | /* wait */ 143 | bl delay 144 | /* nested function call, restore link register */ 145 | mov lr, r11 146 | 147 | /* write 0 into GPPUDCLK0. Write 0 to GPPUDCLK0 to make it take effect*/ 148 | mov r2, #0x00 149 | str r2, [r0, #0x98] 150 | 151 | /* clear interrupts */ 152 | /* */ 153 | /* write 0x07ff == 0111 1111 1111 into UART0_ICR (= UART0_BASE + offset 0x44) */ 154 | /* UART0_ICR = interrupt clear register */ 155 | /* 0x07ff clears all interrupts */ 156 | mov r2, #0x0700 157 | orr r2, r2, #0x00ff 158 | str r2, [r1, #0x44] 159 | 160 | /* Set the baud rate */ 161 | /* */ 162 | /* write a 1 into UART0_IBRD (= UART0_BASE + offset 0x24) */ 163 | /* IBRD = Integral Baud Rate Definition */ 164 | /* Set integer & fractional part of baud rate */ 165 | /* Divider = UART_CLOCK/(16 * Baud) */ 166 | /* Fraction part register = (Fractional part * 64) + 0.5 */ 167 | /* UART_CLOCK = 3000000; Baud = 115200. */ 168 | /* Divider = 3000000 / (16 * 115200) = 1.627 = ~1. */ 169 | /* Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. */ 170 | mov r2, #1 171 | str r2, [r1, #0x24] 172 | 173 | /* Set the baud rate */ 174 | /* */ 175 | /* write a 40 into UART0_FBRD (= UART0_BASE + offset 0x28) */ 176 | /* FBRD = Fractional Baud Rate Definition */ 177 | mov r2, #40 178 | str r2, [r1, #0x28] 179 | 180 | /* enable 8 bit data transmission (1 stop bit, no parity) */ 181 | /* */ 182 | /* UARTC_LCRH = Line Control Register */ 183 | /* write 70 0100 0110 = into UARTC_LCRH (= UART0_BASE + offset 0x2C) */ 184 | mov r2, #0x60 185 | str r2, [r1, #0x2C] 186 | 187 | /* mask all interrupts, means disables or ignores all interrupts */ 188 | /* */ 189 | /* write 0x07F2 into UART0_IMSC (= UART0_BASE + offset 0x38) */ 190 | /* 0x07F2 = 0111 1111 0010 = (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) */ 191 | mov r2, #0x0700 192 | orr r2, r2, #0x00F2 193 | str r2, [r1, #0x38] 194 | 195 | /* enable UART0 */ 196 | /* */ 197 | /* write 0x0301 = 0011 0000 0001 into UART0_CR (= UART0_BASE + offset 0x30) */ 198 | /* bit 0 = enable/disable */ 199 | /* bit 8 = enable receive */ 200 | /* bit 9 = enable transmit */ 201 | mov r2, #0x0300 202 | orr r2, r2, #0x0001 203 | str r2, [r1, #0x0030] 204 | 205 | /* return to caller. r15 is the programm counter. r14 is the link register */ 206 | mov pc, lr 207 | -------------------------------------------------------------------------------- /Assembler00_Faculty/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | /* Starts at LOADER_ADDR. */ 6 | /* For AArch64, use . = 0x80000; */ 7 | . = 0x8000; 8 | __start = .; 9 | __text_start = .; 10 | .text : 11 | { 12 | KEEP(*(.text.startup)) 13 | *(.text) 14 | } 15 | . = ALIGN(4096); /* align to page size */ 16 | __text_end = .; 17 | 18 | __rodata_start = .; 19 | .rodata : 20 | { 21 | *(.rodata) 22 | } 23 | . = ALIGN(4096); /* align to page size */ 24 | __rodata_end = .; 25 | 26 | __data_start = .; 27 | .data : 28 | { 29 | *(.data) 30 | } 31 | . = ALIGN(4096); /* align to page size */ 32 | __data_end = .; 33 | 34 | __bss_start = .; 35 | .bss : 36 | { 37 | bss = .; 38 | *(.bss) 39 | } 40 | . = ALIGN(4096); /* align to page size */ 41 | __bss_end = .; 42 | __end = .; 43 | } 44 | __bss_size = (__bss_end - __bss_start)>>3; -------------------------------------------------------------------------------- /Assembler00_Registers/README.md: -------------------------------------------------------------------------------- 1 | # Registers 2 | 3 | | Register Name | Alias | Description | Use in ARM Procedure Call Standard | 4 | | ------------- | ----- | -------------- | ------------------------------------------------------------------------------- | 5 | | R0 | | | Scratch Register. Used to pass parameters. Will be overwritten by subroutines. | 6 | | R1 | | | Scratch Register. Used to pass parameters. Will be overwritten by subroutines. | 7 | | R2 | | | Scratch Register. Used to pass parameters. Will be overwritten by subroutines. | 8 | | R3 | | | Scratch Register. Used to pass parameters. Will be overwritten by subroutines. | 9 | | R4 | | | Preserve Register. Stack before using. Restore before returning. | 10 | | R5 | | | Preserve Register. Stack before using. Restore before returning. | 11 | | R6 | | | Preserve Register. Stack before using. Restore before returning. | 12 | | R7 | | | Preserve Register. Stack before using. Restore before returning. | 13 | | R8 | | | Preserve Register. Stack before using. Restore before returning. | 14 | | R9 | | | Preserve Register. Stack before using. Restore before returning. | 15 | | R10 | | | Preserve Register. Stack before using. Restore before returning. | 16 | | R11 | | | Preserve Register. Stack before using. Restore before returning. | 17 | | R12 | | | Scratch Register. Used to pass parameters. Will be overwritten by subroutines. | 18 | | R13 | SP | StackPointer | | 19 | | R14 | LR | LinkRegister | Set by BL or BLX on entry of routine. Overwritten by further use of BL and BLX. | 20 | | R15 | PC | ProgramCounter | | 21 | | R16 | CPSR | StatusRegister | | 22 | 23 | # Scratch Registers in the ARM Procedure Call Standard 24 | 25 | r0-r3 and r12 are so called scratch registers in the ARM Procedure Call Standard. The calling routine has to be prepared to loose the contents of those registers when calling a routine because the convention is that those register will not be stored and restored by the caller. The caller is free to use those registers and to override the values without taking an precautions. 26 | 27 | # Preserve Registers in the ARM Procedure Call Standard 28 | 29 | r4-r11 are so called preserve registers in the ARM Procedure Call Standard. The Callee has to stack/save and restore them. The convention says that a callee will keep all values in the preserve registers in tact. This means a caller can be assured that there is no data lost during a function call when the data is stored in one of the preserve registers. 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | RUN apt-get update 3 | 4 | # Enabling source repositories and downloading the sources for qemu 5 | # 6 | # backup the original file 7 | RUN cp /etc/apt/sources.list /etc/apt/sources.list.backup 8 | # enable the source repositories by uncommenting the existing lines 9 | RUN sed -i 's/# deb-src/deb-src/g' /etc/apt/sources.list 10 | # update the cache 11 | RUN apt-get update 12 | # install build dependencies for qemu 13 | RUN DEBIAN_FRONTEND=noninteractive apt-get -yq build-dep qemu 14 | 15 | # install git, vim 16 | RUN DEBIAN_FRONTEND=noninteractive apt-get -yq install git 17 | RUN DEBIAN_FRONTEND=noninteractive apt-get -yq install vim 18 | 19 | # download the qemu sources and build qemu with arm support 20 | WORKDIR tmp 21 | RUN git clone https://github.com/qemu/qemu.git 22 | RUN pwd 23 | RUN ls -la 24 | WORKDIR qemu 25 | RUN pwd 26 | RUN ls -la 27 | RUN git checkout v5.0.1 28 | RUN git submodule init 29 | RUN git submodule update --recursive 30 | RUN ./configure --target-list=aarch64-softmmu 31 | RUN make 32 | 33 | WORKDIR / 34 | 35 | RUN DEBIAN_FRONTEND=noninteractive apt-get -yq install libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi libncurses5-dev build-essential bison flex libssl-dev bc 36 | 37 | # install The ARM toolchain 38 | RUN DEBIAN_FRONTEND=noninteractive apt-get -yq install gcc-aarch64-linux-gnu 39 | -------------------------------------------------------------------------------- /Lesson00_Debugging/README.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | ## Links 4 | 5 | - https://github.com/naums/PiOS 6 | - https://github.com/dwelch67/raspberrypi/tree/master/armjtag 7 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # makefile 3 | # by Alex Chadwick 4 | # 5 | # A makefile script for generation of raspberry pi kernel images. 6 | ############################################################################### 7 | 8 | # The toolchain to use. 9 | # arm-none-eabi works, but there does exist arm-bcm2708-linux-gnueabi. 10 | ARMGNU ?= arm-none-eabi 11 | 12 | # The intermediate directory for compiled object files. 13 | BUILD = build/ 14 | 15 | # The directory in which source files are stored. 16 | SOURCE = ./ 17 | 18 | # The name of the output file to generate. 19 | TARGET = kernel.img 20 | 21 | # The name of the assembler listing file to generate. 22 | LIST = kernel.list 23 | 24 | # The name of the map file to generate. 25 | MAP = kernel.map 26 | 27 | # The name of the linker script to use. 28 | LINKER = kernel.ld 29 | 30 | # The names of all object files that must be generated. Deduced from the 31 | # assembly code files in source. 32 | OBJECTS := $(patsubst $(SOURCE)%.s,$(BUILD)%.o,$(wildcard $(SOURCE)*.s)) 33 | 34 | # Rule to make everything. 35 | all: $(TARGET) $(LIST) 36 | 37 | # Rule to remake everything. Does not include clean. 38 | rebuild: all 39 | 40 | # Rule to make the listing file. 41 | $(LIST) : $(BUILD)output.elf 42 | $(ARMGNU)-objdump -d $(BUILD)output.elf > $(LIST) 43 | 44 | # Rule to make the image file. 45 | $(TARGET) : $(BUILD)output.elf 46 | $(ARMGNU)-objcopy $(BUILD)output.elf -O binary $(TARGET) 47 | 48 | # Rule to make the elf file. 49 | $(BUILD)output.elf : $(OBJECTS) $(LINKER) 50 | $(ARMGNU)-ld --no-undefined $(OBJECTS) -Map $(MAP) -o $(BUILD)output.elf -T $(LINKER) 51 | 52 | # Rule to make the object files. 53 | $(BUILD)%.o: $(SOURCE)%.s $(BUILD) 54 | $(ARMGNU)-as -I $(SOURCE) $< -o $@ 55 | 56 | $(BUILD): 57 | mkdir $@ 58 | 59 | # Rule to clean files. 60 | clean : 61 | -rm -rf $(BUILD) 62 | -rm -f $(TARGET) 63 | -rm -f $(LIST) 64 | -rm -f $(MAP) -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit/kernel.ld: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * kernel.ld 3 | * by Alex Chadwick 4 | * 5 | * A linker script for generation of raspberry pi kernel images. 6 | ******************************************************************************/ 7 | 8 | SECTIONS { 9 | /* 10 | * First and formost we need the .init section, containing the code to 11 | * be run first. We allow room for the ATAGs and stack and conform to 12 | * the bootloader's expectation by putting this code at 0x8000. 13 | */ 14 | .init 0x8000 : { 15 | *(.init) 16 | } 17 | 18 | /* 19 | * Next we put the rest of the code. 20 | */ 21 | .text : { 22 | *(.text) 23 | } 24 | 25 | /* 26 | * Next we put the data. 27 | */ 28 | .data : { 29 | *(.data) 30 | } 31 | 32 | /* 33 | * Finally comes everything else. A fun trick here is to put all other 34 | * sections into this section, which will be discarded by default. 35 | */ 36 | /DISCARD/ : { 37 | *(*) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thewbi/armos/1e3140a6135bd9abcd94f18fa2000bd2d4f58c21/Lesson00_Interrupts_32bit_C/README.md -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/armc-013-cstartup.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | extern int __bss_start; 12 | extern int __bss_end; 13 | 14 | extern void kernel_main(unsigned int r0, unsigned int r1, unsigned int atags); 15 | // extern void writeCharacter(char a, char b, char c); 16 | // extern void delay(); 17 | 18 | void _cstartup(unsigned int r0, unsigned int r1, unsigned int r2) 19 | { 20 | int* bss = &__bss_start; 21 | int* bss_end = &__bss_end; 22 | 23 | /* 24 | Clear the BSS section 25 | 26 | See http://en.wikipedia.org/wiki/.bss for further information on the 27 | BSS section 28 | 29 | See https://sourceware.org/newlib/libc.html#Stubs for further 30 | information on the c-library stubs 31 | */ 32 | while (bss < bss_end) { 33 | *bss++ = 0; 34 | } 35 | 36 | // while (1) { 37 | // writeCharacter('a', 'a', 'a'); 38 | // delay(); 39 | // } 40 | 41 | /* We should never return from main ... */ 42 | kernel_main(r0, r1, r2); 43 | 44 | /* ... but if we do, safely trap here */ 45 | while (1) 46 | { 47 | /* EMPTY! */ 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/armc-013.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | /* 11 | Interrupts example, show how to use the interrupt controller and to load 12 | the vector table at runtime. 13 | */ 14 | 15 | //#include 16 | //#include 17 | 18 | //#define RPI0 19 | 20 | //#include "gic-400.h" 21 | 22 | #include "rpi-gpio.h" 23 | #include "rpi-armtimer.h" 24 | #include "rpi-systimer.h" 25 | #include "rpi-interrupts.h" 26 | 27 | extern void _enable_interrupts(void); 28 | //extern void writeCharacter(char a, char b, char c); 29 | extern void delay(); 30 | 31 | /** Main function - we'll never return from here */ 32 | void kernel_main( unsigned int r0, unsigned int r1, unsigned int atags ) 33 | { 34 | /* Write 1 to the LED init nibble in the Function Select GPIO 35 | peripheral register to enable LED pin as an output */ 36 | 37 | // LED_GPIO defined in rpi-gpio.h to the value 16 which is the LED GPIO on the RPI0 38 | // RPI_SetGpioPinFunction defined in rpi-gpio.h 39 | RPI_SetGpioPinFunction(LED_GPIO, FS_OUTPUT); 40 | 41 | // setting the LED pin high turns the LED off. 42 | RPI_SetGpioHi(LED_GPIO); 43 | 44 | // defined in rpi-gpio.h 45 | LED_OFF(); 46 | 47 | // //while (1) { 48 | // for (int i = 0; i < 5; i++) { 49 | // writeCharacter('b', 'b', 'b'); 50 | 51 | // LED_ON(); 52 | // delay(); 53 | 54 | // LED_OFF(); 55 | // delay(); 56 | // } 57 | // //} 58 | 59 | 60 | 61 | // for (int i = 0; i < 5; i++) { 62 | // writeCharacter('c', 'c', 'c'); 63 | 64 | // LED_ON(); 65 | // delay(); 66 | 67 | // LED_OFF(); 68 | // delay(); 69 | // } 70 | 71 | #ifdef RPI4 72 | gic400_init(0xFF840000UL); 73 | #endif 74 | 75 | // defined in rpi-interrupts-controller.c 76 | RPI_EnableARMTimerInterrupt(); 77 | 78 | // for (int i = 0; i < 5; i++) { 79 | // writeCharacter('d', 'd', 'd'); 80 | 81 | // LED_ON(); 82 | // delay(); 83 | 84 | // LED_OFF(); 85 | // delay(); 86 | // } 87 | 88 | /* Setup the system timer interrupt 89 | Timer frequency = Clk/256 * 0x400 90 | 91 | NOTE: If the system decides to alter the clock, the frequency of these 92 | interrupts will also change. The system timer remains consistent. 93 | 94 | What is Clk ? 95 | */ 96 | // #if defined ( RPI4 ) 97 | // RPI_GetArmTimer()->Load = 0x4000; 98 | // #else 99 | // // Set a value into the load register of the ARM timer. 100 | // // The value in the load register is copied to the timer 101 | // // value register on write or when the value in the timer value register was decremented to 0 102 | // RPI_GetArmTimer()->Load = 0x400; 103 | // #endif 104 | 105 | RPI_GetArmTimer()->PreDivider = 0x7D; 106 | 107 | //RPI_GetArmTimer()->Load = 0x000F4240; 108 | 109 | // 15 seconds 110 | //RPI_GetArmTimer()->Load = 0x0000F424; 111 | 112 | //RPI_GetArmTimer()->Load = 0x1E46; 113 | RPI_GetArmTimer()->Load = 0x1E467F; 114 | 115 | // little bit slower than every second 116 | // 0x1000 = 4096d 117 | //RPI_GetArmTimer()->Load = 0x00001000; 118 | 119 | 120 | //RPI_GetArmTimer()->Load = 0x00000800; 121 | 122 | // twice as fast as 0x00000800 123 | //RPI_GetArmTimer()->Load = 0x00000400; 124 | 125 | // twice as fast as 0x00000400 126 | //RPI_GetArmTimer()->Load = 0x00000200; 127 | 128 | // for (int i = 0; i < 5; i++) { 129 | // writeCharacter('e', 'e', 'e'); 130 | 131 | // LED_ON(); 132 | // delay(); 133 | 134 | // LED_OFF(); 135 | // delay(); 136 | // } 137 | 138 | /* Setup the ARM Timer */ 139 | /* Write a value into the control register of the ARM timer */ 140 | RPI_GetArmTimer()->Control = 141 | RPI_ARMTIMER_CTRL_23BIT | 142 | RPI_ARMTIMER_CTRL_ENABLE | 143 | RPI_ARMTIMER_CTRL_INT_ENABLE | 144 | //RPI_ARMTIMER_CTRL_PRESCALE_256; 145 | RPI_ARMTIMER_CTRL_PRESCALE_1; 146 | 147 | /* Enable interrupts! */ 148 | /* Defined in armc-013-start.S */ 149 | _enable_interrupts(); 150 | 151 | // for (int i = 0; i < 5; i++) { 152 | // writeCharacter('f', 'f', 'f'); 153 | 154 | // LED_ON(); 155 | // delay(); 156 | 157 | // LED_OFF(); 158 | // delay(); 159 | // } 160 | 161 | /* Never exit as there is no OS to exit to! */ 162 | while(1) 163 | { 164 | 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #hlist=`ls *.h`; 4 | #${hlist} 5 | 6 | function delete { 7 | rm -f *.o 8 | rm -f kernel.elf 9 | rm -f kernel.img 10 | rm -f kernel7.img 11 | rm -f kernel.list 12 | rm -f kernel.map 13 | } 14 | 15 | function compile { 16 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -c armc-013-start.S -o armc-013-start.o 17 | 18 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c armc-013.c -o armc-013.o 19 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c armc-013-cstartup.c -o armc-013-cstartup.o 20 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c rpi-armtimer.c -o rpi-armtimer.o 21 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c rpi-gpio.c -o rpi-gpio.o 22 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c rpi-interrupts-controller.c -o rpi-interrupts-controller.o 23 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c rpi-interrupts.c -o rpi-interrupts.o 24 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -I "rpi-base.h, rpi-gpio.h, rpi-armtimer.h, rpi-interrupts.h, rpi-systimer.h" -c rpi-systimer.c -o rpi-systimer.o 25 | 26 | arm-none-eabi-gcc -T linker.ld -o kernel.elf -ffreestanding -O2 -nostdlib armc-013-start.o armc-013.o armc-013-cstartup.o rpi-armtimer.o rpi-gpio.o rpi-interrupts-controller.o rpi-interrupts.o rpi-systimer.o -lgcc 27 | 28 | arm-none-eabi-objcopy kernel.elf -O binary kernel.img 29 | } 30 | 31 | case "$1" in 32 | -c | --clean | --clear) 33 | delete 34 | ;; 35 | 36 | * ) 37 | delete 38 | compile 39 | ;; 40 | esac 41 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | /* Starts at LOADER_ADDR. */ 6 | /* For AArch64, use . = 0x80000; */ 7 | . = 0x8000; 8 | __start = .; 9 | __text_start = .; 10 | .text : 11 | { 12 | KEEP(*(.text.startup)) 13 | *(.text) 14 | } 15 | . = ALIGN(4096); /* align to page size */ 16 | __text_end = .; 17 | 18 | __rodata_start = .; 19 | .rodata : 20 | { 21 | *(.rodata) 22 | } 23 | . = ALIGN(4096); /* align to page size */ 24 | __rodata_end = .; 25 | 26 | __data_start = .; 27 | .data : 28 | { 29 | *(.data) 30 | } 31 | . = ALIGN(4096); /* align to page size */ 32 | __data_end = .; 33 | 34 | __bss_start = .; 35 | .bss : 36 | { 37 | bss = .; 38 | *(.bss) 39 | } 40 | . = ALIGN(4096); /* align to page size */ 41 | __bss_end = .; 42 | __end = .; 43 | } 44 | __bss_size = (__bss_end - __bss_start)>>3; -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-armtimer.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | //#include 12 | #include "rpi-armtimer.h" 13 | 14 | static rpi_arm_timer_t* rpiArmTimer = (rpi_arm_timer_t*)RPI_ARMTIMER_BASE; 15 | 16 | rpi_arm_timer_t* RPI_GetArmTimer(void) 17 | { 18 | return rpiArmTimer; 19 | } 20 | 21 | void RPI_ArmTimerInit(void) 22 | { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-armtimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | #ifndef RPI_ARMTIMER_H 12 | #define RPI_ARMTIMER_H 13 | 14 | //#include 15 | 16 | #include "rpi-base.h" 17 | 18 | /** @brief See the documentation for the ARM side timer (Section 14 of the 19 | BCM2835 Peripherals PDF) */ 20 | #define RPI_ARMTIMER_BASE ( PERIPHERAL_BASE + 0xB400UL ) 21 | 22 | /** @brief 0 : 16-bit counters - 1 : 23-bit counter */ 23 | #define RPI_ARMTIMER_CTRL_23BIT ( 1 << 1 ) 24 | 25 | #define RPI_ARMTIMER_CTRL_PRESCALE_1 ( 0 << 2 ) 26 | #define RPI_ARMTIMER_CTRL_PRESCALE_16 ( 1 << 2 ) 27 | #define RPI_ARMTIMER_CTRL_PRESCALE_256 ( 2 << 2 ) 28 | 29 | /** @brief 0 : Timer interrupt disabled - 1 : Timer interrupt enabled */ 30 | #define RPI_ARMTIMER_CTRL_INT_ENABLE ( 1 << 5 ) 31 | #define RPI_ARMTIMER_CTRL_INT_DISABLE ( 0 << 5 ) 32 | 33 | /** @brief 0 : Timer disabled - 1 : Timer enabled */ 34 | #define RPI_ARMTIMER_CTRL_ENABLE ( 1 << 7 ) 35 | #define RPI_ARMTIMER_CTRL_DISABLE ( 0 << 7 ) 36 | 37 | 38 | /** @brief Section 14.2 of the BCM2835 Peripherals documentation details 39 | the register layout for the ARM side timer */ 40 | typedef struct { 41 | 42 | /** The timer load register sets the time for the timer to count down. 43 | This value is loaded into the timer value register after the load 44 | register has been written or if the timer-value register has counted 45 | down to 0. */ 46 | volatile uint32_t Load; 47 | 48 | /** This register holds the current timer value and is counted down when 49 | the counter is running. It is counted down each timer clock until the 50 | value 0 is reached. Then the value register is re-loaded from the 51 | timer load register and the interrupt pending bit is set. The timer 52 | count down speed is set by the timer pre-divide register. */ 53 | volatile uint32_t Value; 54 | 55 | /** The standard SP804 timer control register consist of 8 bits but in the 56 | BCM implementation there are more control bits for the extra features. 57 | Control bits 0-7 are identical to the SP804 bits, albeit some 58 | functionality of the SP804 is not implemented. All new control bits 59 | start from bit 8 upwards. */ 60 | volatile uint32_t Control; 61 | 62 | /** The timer IRQ clear register is write only. When writing this register 63 | the interrupt-pending bit is cleared. When reading this register it 64 | returns 0x544D5241 which is the ASCII reversed value for "ARMT". */ 65 | volatile uint32_t IRQClear; 66 | 67 | /** The raw IRQ register is a read-only register. It shows the status of 68 | the interrupt pending bit. 0 : The interrupt pending bits is clear. 69 | 1 : The interrupt pending bit is set. 70 | 71 | The interrupt pending bits is set each time the value register is 72 | counted down to zero. The interrupt pending bit can not by itself 73 | generates interrupts. Interrupts can only be generated if the 74 | interrupt enable bit is set. */ 75 | volatile uint32_t RAWIRQ; 76 | 77 | /** The masked IRQ register is a read-only register. It shows the status 78 | of the interrupt signal. It is simply a logical AND of the interrupt 79 | pending bit and the interrupt enable bit. 0 : Interrupt line not 80 | asserted. 1 :Interrupt line is asserted, (the interrupt pending and 81 | the interrupt enable bit are set.) */ 82 | volatile uint32_t MaskedIRQ; 83 | 84 | /** This register is a copy of the timer load register. The difference is 85 | that a write to this register does not trigger an immediate reload of 86 | the timer value register. Instead the timer load register value is 87 | only accessed if the value register has finished counting down to 88 | zero. */ 89 | volatile uint32_t Reload; 90 | 91 | /** The Pre-divider register is not present in the SP804. The pre-divider 92 | register is 10 bits wide and can be written or read from. This 93 | register has been added as the SP804 expects a 1MHz clock which we do 94 | not have. Instead the pre-divider takes the APB clock and divides it 95 | down according to: 96 | 97 | timer_clock = apb_clock/(pre_divider+1) 98 | 99 | The reset value of this register is 0x7D so gives a divide by 126. */ 100 | volatile uint32_t PreDivider; 101 | 102 | /** The free running counter is not present in the SP804. The free running 103 | counter is a 32 bits wide read only register. The register is enabled 104 | by setting bit 9 of the Timer control register. The free running 105 | counter is incremented immediately after it is enabled. The timer can 106 | not be reset but when enabled, will always increment and roll-over. 107 | 108 | The free running counter is also running from the APB clock and has 109 | its own clock pre-divider controlled by bits 16-23 of the timer 110 | control register. 111 | 112 | This register will be halted too if bit 8 of the control register is 113 | set and the ARM is in Debug Halt mode. */ 114 | volatile uint32_t FreeRunningCounter; 115 | 116 | } rpi_arm_timer_t; 117 | 118 | extern rpi_arm_timer_t* RPI_GetArmTimer(void); 119 | extern void RPI_ArmTimerInit(void); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-base.h: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | #ifndef RPI_BASE_H 12 | #define RPI_BASE_H 13 | 14 | // for uint32_t, uint64_t 15 | #include 16 | 17 | #define RPI0 1 18 | 19 | //#if defined( RPI0 ) || defined( RPI1 ) 20 | #define PERIPHERAL_BASE 0x20000000UL 21 | //#elif defined( RPI2 ) || defined( RPI3 ) 22 | // #define PERIPHERAL_BASE 0x3F000000UL 23 | //#elif defined( RPI4 ) 24 | // #define PERIPHERAL_BASE 0xFE000000UL 25 | // #define GIC400_BASE (0xFF840000UL) 26 | //#else 27 | // #error Unknown RPI Model! 28 | //#endif 29 | 30 | /* System Frequencies From: 31 | https://www.raspberrypi.org/documentation/configuration/config-txt/overclocking.md */ 32 | //#if defined( RPI0 ) || defined( RPI3 ) 33 | //#define SYSFREQ 400000000UL 34 | //#elif defined( RPI1 ) || defined ( RPI2 ) 35 | #define SYSFREQ (250000000UL) 36 | //#elif defined( RPI4 ) 37 | //#define SYSFREQ 500000000UL 38 | //#else 39 | // #error Unknown RPI Model! 40 | //#endif 41 | 42 | typedef volatile uint32_t rpi_reg_rw_t; 43 | typedef volatile const uint32_t rpi_reg_ro_t; 44 | typedef volatile uint32_t rpi_reg_wo_t; 45 | 46 | typedef volatile uint64_t rpi_wreg_rw_t; 47 | typedef volatile const uint64_t rpi_wreg_ro_t; 48 | 49 | extern void writeCharacter(char a, char b, char c); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-gpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | //#include 12 | #include "rpi-gpio.h" 13 | 14 | static rpi_gpio_t* rpiGpio = (rpi_gpio_t*)RPI_GPIO_BASE; 15 | 16 | 17 | rpi_gpio_t* RPI_GetGpio(void) 18 | { 19 | return rpiGpio; 20 | } 21 | 22 | 23 | void RPI_SetGpioPinFunction( rpi_gpio_pin_t gpio, rpi_gpio_alt_function_t func ) 24 | { 25 | rpi_reg_rw_t* fsel_reg = &((rpi_reg_rw_t*)rpiGpio)[ gpio / 10 ]; 26 | 27 | rpi_reg_rw_t fsel_copy = *fsel_reg; 28 | fsel_copy &= ~( FS_MASK << ( ( gpio % 10 ) * 3 ) ); 29 | fsel_copy |= (func << ( ( gpio % 10 ) * 3 ) ); 30 | *fsel_reg = fsel_copy; 31 | } 32 | 33 | 34 | void RPI_SetGpioOutput( rpi_gpio_pin_t gpio ) 35 | { 36 | RPI_SetGpioPinFunction( gpio, FS_OUTPUT ); 37 | } 38 | 39 | 40 | void RPI_SetGpioInput( rpi_gpio_pin_t gpio ) 41 | { 42 | RPI_SetGpioPinFunction( gpio, FS_INPUT ); 43 | } 44 | 45 | 46 | rpi_gpio_value_t RPI_GetGpioValue( rpi_gpio_pin_t gpio ) 47 | { 48 | rpi_gpio_value_t result = RPI_IO_UNKNOWN; 49 | 50 | switch( gpio / 32 ) 51 | { 52 | case 0: 53 | result = rpiGpio->GPLEV0 & ( 1 << gpio ); 54 | break; 55 | 56 | case 1: 57 | result = rpiGpio->GPLEV1 & ( 1 << ( gpio - 32 ) ); 58 | break; 59 | 60 | default: 61 | break; 62 | } 63 | 64 | if( result != RPI_IO_UNKNOWN ) 65 | { 66 | if( result ) 67 | result = RPI_IO_HI; 68 | } 69 | 70 | return result; 71 | } 72 | 73 | 74 | void RPI_ToggleGpio( rpi_gpio_pin_t gpio ) 75 | { 76 | if( RPI_GetGpioValue( gpio ) ) 77 | RPI_SetGpioLo( gpio ); 78 | else 79 | RPI_SetGpioHi( gpio ); 80 | } 81 | 82 | 83 | void RPI_SetGpioHi( rpi_gpio_pin_t gpio ) 84 | { 85 | switch( gpio / 32 ) 86 | { 87 | case 0: 88 | rpiGpio->GPSET0 = ( 1 << gpio ); 89 | break; 90 | 91 | case 1: 92 | rpiGpio->GPSET1 = ( 1 << ( gpio - 32 ) ); 93 | break; 94 | 95 | default: 96 | break; 97 | } 98 | } 99 | 100 | 101 | void RPI_SetGpioLo( rpi_gpio_pin_t gpio ) 102 | { 103 | switch( gpio / 32 ) 104 | { 105 | case 0: 106 | rpiGpio->GPCLR0 = ( 1 << gpio ); 107 | break; 108 | 109 | case 1: 110 | rpiGpio->GPCLR1 = ( 1 << ( gpio - 32 ) ); 111 | break; 112 | 113 | default: 114 | break; 115 | } 116 | } 117 | 118 | 119 | void RPI_SetGpioValue( rpi_gpio_pin_t gpio, rpi_gpio_value_t value ) 120 | { 121 | if( ( value == RPI_IO_LO ) || ( value == RPI_IO_OFF ) ) 122 | RPI_SetGpioLo( gpio ); 123 | else if( ( value == RPI_IO_HI ) || ( value == RPI_IO_ON ) ) 124 | RPI_SetGpioHi( gpio ); 125 | } 126 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-interrupts-controller.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2020, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "rpi-armtimer.h" 15 | #include "rpi-base.h" 16 | #include "rpi-gpio.h" 17 | #include "rpi-interrupts.h" 18 | //#include "gic-400.h" 19 | 20 | /** @brief See Section 7.5 of the BCM2836 ARM Peripherals documentation, the base 21 | address of the controller is actually xxxxB000, but there is a 0x200 offset 22 | to the first addressable register for the interrupt controller, so offset the 23 | base to the first register */ 24 | #define RPI_INTERRUPT_CONTROLLER_BASE ( PERIPHERAL_BASE + 0xB200UL ) 25 | 26 | /** @brief Bits in the Enable_Basic_IRQs register to enable various interrupts. 27 | See the BCM2835 ARM Peripherals manual, section 7.5 */ 28 | #define RPI_BASIC_ARM_TIMER_IRQ (1 << 0) 29 | #define RPI_BASIC_ARM_MAILBOX_IRQ (1 << 1) 30 | #define RPI_BASIC_ARM_DOORBELL_0_IRQ (1 << 2) 31 | #define RPI_BASIC_ARM_DOORBELL_1_IRQ (1 << 3) 32 | #define RPI_BASIC_GPU_0_HALTED_IRQ (1 << 4) 33 | #define RPI_BASIC_GPU_1_HALTED_IRQ (1 << 5) 34 | #define RPI_BASIC_ACCESS_ERROR_1_IRQ (1 << 6) 35 | #define RPI_BASIC_ACCESS_ERROR_0_IRQ (1 << 7) 36 | 37 | 38 | extern void RPI_EnableGICInterrupts(void); 39 | 40 | /** @brief The interrupt controller memory mapped register set */ 41 | typedef struct { 42 | volatile uint32_t IRQ_basic_pending; 43 | volatile uint32_t IRQ_pending_1; 44 | volatile uint32_t IRQ_pending_2; 45 | volatile uint32_t FIQ_control; 46 | volatile uint32_t Enable_IRQs_1; 47 | volatile uint32_t Enable_IRQs_2; 48 | volatile uint32_t Enable_Basic_IRQs; 49 | volatile uint32_t Disable_IRQs_1; 50 | volatile uint32_t Disable_IRQs_2; 51 | volatile uint32_t Disable_Basic_IRQs; 52 | } rpi_irq_controller_t; 53 | 54 | 55 | /** @brief The BCM2835 Interupt controller peripheral at it's base address */ 56 | static rpi_irq_controller_t* rpiIRQController = 57 | (rpi_irq_controller_t*)RPI_INTERRUPT_CONTROLLER_BASE; 58 | 59 | 60 | /** 61 | @brief Return the IRQ Controller register set 62 | */ 63 | static rpi_irq_controller_t* RPI_GetIrqController( void ) 64 | { 65 | return rpiIRQController; 66 | } 67 | 68 | /* 69 | * Uses the interrupt controller and enables the timer irq. 70 | */ 71 | void RPI_EnableARMTimerInterrupt(void) 72 | { 73 | #ifdef RPI4 74 | // RPI_EnableGICInterrupts(); 75 | gic400_init(0xFF840000UL); 76 | #endif 77 | RPI_GetIrqController()->Enable_Basic_IRQs = RPI_BASIC_ARM_TIMER_IRQ; 78 | } 79 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-interrupts.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2020, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | //#include 12 | 13 | #include "rpi-armtimer.h" 14 | #include "rpi-base.h" 15 | #include "rpi-gpio.h" 16 | #include "rpi-interrupts.h" 17 | 18 | /** 19 | @brief The Reset vector interrupt handler 20 | 21 | This can never be called, since an ARM core reset would also reset the 22 | GPU and therefore cause the GPU to start running code again until 23 | the ARM is handed control at the end of boot loading 24 | */ 25 | void __attribute__((interrupt("ABORT"))) reset_vector(void) 26 | { 27 | 28 | } 29 | 30 | /** 31 | @brief The undefined instruction interrupt handler 32 | 33 | If an undefined intstruction is encountered, the CPU will start 34 | executing this function. Just trap here as a debug solution. 35 | */ 36 | void __attribute__((interrupt("UNDEF"))) undefined_instruction_vector(void) 37 | { 38 | while( 1 ) 39 | { 40 | /* Do Nothing! */ 41 | } 42 | } 43 | 44 | 45 | /** 46 | @brief The supervisor call interrupt handler 47 | 48 | The CPU will start executing this function. Just trap here as a debug 49 | solution. 50 | */ 51 | void __attribute__((interrupt("SWI"))) software_interrupt_vector(void) 52 | { 53 | while( 1 ) 54 | { 55 | /* Do Nothing! */ 56 | } 57 | } 58 | 59 | 60 | /** 61 | @brief The prefetch abort interrupt handler 62 | 63 | The CPU will start executing this function. Just trap here as a debug 64 | solution. 65 | */ 66 | void __attribute__((interrupt("ABORT"))) prefetch_abort_vector(void) 67 | { 68 | 69 | } 70 | 71 | 72 | /** 73 | @brief The Data Abort interrupt handler 74 | 75 | The CPU will start executing this function. Just trap here as a debug 76 | solution. 77 | */ 78 | void __attribute__((interrupt("ABORT"))) data_abort_vector(void) 79 | { 80 | 81 | } 82 | 83 | /** 84 | @brief The IRQ Interrupt handler 85 | 86 | This handler is run every time an interrupt source is triggered. It's 87 | up to the handler to determine the source of the interrupt and most 88 | importantly clear the interrupt flag so that the interrupt won't 89 | immediately put us back into the start of the handler again. 90 | */ 91 | void __attribute__((interrupt("IRQ"))) interrupt_vector(void) 92 | { 93 | static int lit = 0; 94 | 95 | if (RPI_GetArmTimer()->MaskedIRQ) { 96 | 97 | writeCharacter('g', 'g', 'g'); 98 | 99 | /* Clear the ARM Timer interrupt - it's the only interrupt we have 100 | enabled, so we want don't have to work out which interrupt source 101 | caused us to interrupt */ 102 | 103 | RPI_GetArmTimer()->IRQClear = 1; 104 | 105 | /* Flip the LED */ 106 | if (lit) 107 | { 108 | LED_OFF(); 109 | lit = 0; 110 | } 111 | else 112 | { 113 | LED_ON(); 114 | lit = 1; 115 | } 116 | } 117 | } 118 | 119 | 120 | /** 121 | @brief The FIQ Interrupt Handler 122 | 123 | The FIQ handler can only be allocated to one interrupt source. The FIQ has 124 | a full CPU shadow register set. Upon entry to this function the CPU 125 | switches to the shadow register set so that there is no need to save 126 | registers before using them in the interrupt. 127 | 128 | In C you can't see the difference between the IRQ and the FIQ interrupt 129 | handlers except for the FIQ knowing it's source of interrupt as there can 130 | only be one source, but the prologue and epilogue code is quite different. 131 | It's much faster on the FIQ interrupt handler. 132 | 133 | The prologue is the code that the compiler inserts at the start of the 134 | function, if you like, think of the opening curly brace of the function as 135 | being the prologue code. For the FIQ interrupt handler this is nearly 136 | empty because the CPU has switched to a fresh set of registers, there's 137 | nothing we need to save. 138 | 139 | The epilogue is the code that the compiler inserts at the end of the 140 | function, if you like, think of the closing curly brace of the function as 141 | being the epilogue code. For the FIQ interrupt handler this is nearly 142 | empty because the CPU has switched to a fresh set of registers and so has 143 | not altered the main set of registers. 144 | */ 145 | void __attribute__((interrupt("FIQ"))) fast_interrupt_vector(void) 146 | { 147 | 148 | } 149 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-interrupts.h: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | #ifndef RPI_INTERRUPTS_H 12 | #define RPI_INTERRUPTS_H 13 | 14 | //#include 15 | 16 | #define RPI0 1 17 | 18 | #include "rpi-base.h" 19 | 20 | // defined in rpi-interrupts-controller.c 21 | extern void RPI_EnableARMTimerInterrupt(void); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-systimer.c: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | //#include 12 | #include "rpi-systimer.h" 13 | 14 | static rpi_sys_timer_t* rpiSystemTimer = (rpi_sys_timer_t*)RPI_SYSTIMER_BASE; 15 | 16 | rpi_sys_timer_t* RPI_GetSystemTimer(void) 17 | { 18 | return rpiSystemTimer; 19 | } 20 | 21 | void RPI_WaitMicroSeconds( uint32_t us ) 22 | { 23 | volatile uint32_t ts = rpiSystemTimer->counter_lo; 24 | 25 | while( ( rpiSystemTimer->counter_lo - ts ) < us ) 26 | { 27 | /* BLANK */ 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Lesson00_Interrupts_32bit_C/rpi-systimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Part of the Raspberry-Pi Bare Metal Tutorials 3 | https://www.valvers.com/rpi/bare-metal/ 4 | Copyright (c) 2013-2018, Brian Sidebotham 5 | 6 | This software is licensed under the MIT License. 7 | Please see the LICENSE file included with this software. 8 | 9 | */ 10 | 11 | #ifndef RPI_SYSTIMER_H 12 | #define RPI_SYSTIMER_H 13 | 14 | //#include 15 | 16 | #define RPI0 1 17 | 18 | #include "rpi-base.h" 19 | 20 | #define RPI_SYSTIMER_BASE ( PERIPHERAL_BASE + 0x3000UL ) 21 | 22 | typedef struct { 23 | volatile uint32_t control_status; 24 | volatile uint32_t counter_lo; 25 | volatile uint32_t counter_hi; 26 | volatile uint32_t compare0; 27 | volatile uint32_t compare1; 28 | volatile uint32_t compare2; 29 | volatile uint32_t compare3; 30 | } rpi_sys_timer_t; 31 | 32 | 33 | extern rpi_sys_timer_t* RPI_GetSystemTimer(void); 34 | extern void RPI_WaitMicroSeconds( uint32_t us ); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Lesson00_LED_ON/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Alex Chadwick 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Lesson00_LED_ON/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # makefile 3 | # by Alex Chadwick 4 | # 5 | # A makefile script for generation of raspberry pi kernel images. 6 | ############################################################################### 7 | 8 | # The toolchain to use. arm-none-eabi works, but there does exist 9 | # arm-bcm2708-linux-gnueabi. 10 | ARMGNU ?= arm-none-eabi 11 | 12 | # The intermediate directory for compiled object files. 13 | BUILD = build/ 14 | 15 | # The directory in which source files are stored. 16 | SOURCE = source/ 17 | 18 | # The name of the output file to generate. 19 | TARGET = kernel.img 20 | 21 | # The name of the assembler listing file to generate. 22 | LIST = kernel.list 23 | 24 | # The name of the map file to generate. 25 | MAP = kernel.map 26 | 27 | # The name of the linker script to use. 28 | LINKER = kernel.ld 29 | 30 | # The names of all object files that must be generated. Deduced from the 31 | # assembly code files in source. 32 | OBJECTS := $(patsubst $(SOURCE)%.s,$(BUILD)%.o,$(wildcard $(SOURCE)*.s)) 33 | 34 | # Rule to make everything. 35 | all: $(TARGET) $(LIST) 36 | 37 | # Rule to remake everything. Does not include clean. 38 | rebuild: all 39 | 40 | # Rule to make the listing file. 41 | $(LIST) : $(BUILD)output.elf 42 | $(ARMGNU)-objdump -d $(BUILD)output.elf > $(LIST) 43 | 44 | # Rule to make the image file. 45 | $(TARGET) : $(BUILD)output.elf 46 | $(ARMGNU)-objcopy $(BUILD)output.elf -O binary $(TARGET) 47 | 48 | # Rule to make the elf file. 49 | $(BUILD)output.elf : $(OBJECTS) $(LINKER) 50 | $(ARMGNU)-ld --no-undefined $(OBJECTS) -Map $(MAP) -o $(BUILD)output.elf -T $(LINKER) 51 | 52 | # Rule to make the object files. 53 | $(BUILD)%.o: $(SOURCE)%.s $(BUILD) 54 | $(ARMGNU)-as -I $(SOURCE) $< -o $@ 55 | 56 | $(BUILD): 57 | mkdir $@ 58 | 59 | # Rule to clean files. 60 | clean : 61 | -rm -rf $(BUILD) 62 | -rm -f $(TARGET) 63 | -rm -f $(LIST) 64 | -rm -f $(MAP) -------------------------------------------------------------------------------- /Lesson00_LED_ON/README.md: -------------------------------------------------------------------------------- 1 | # Turning on a LED 2 | 3 | This code is taken from https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html 4 | 5 | It will not make a LED blink, instead it will turn an LED on. 6 | 7 | Another good explanation is given [here](https://www.youtube.com/watch?v=U9H7TmRt64A&list=PLRwVmtr-pp05PQDzfuOOo-eRskwHsONY0&index=4). 8 | 9 | The basic idea is that the GPIO controller on the Raspberry Pi has registers and those registers are memory mapped. Memory Mapped means those register are readable and writeable at addresses in the address space of the memory. 10 | 11 | The LED on the Raspbery Pi Board is tied to one of the pins of the GPIO controller. 12 | 13 | Turning the LED on and off basically means to turn the GPIO pin on and off. 14 | 15 | The GPIO controller is controlling the pins. Two steps are required: 16 | 17 | 1. The GPIO controller has to enable a pin 18 | 2. The GPIO controller has to write a 1 to a pin to turn it off or a 0 to turn a pin on (it is reversed, 1 means off, 0 means 0) 19 | 20 | ## Enabling a Pin 21 | 22 | There are 54 pins controller by the GPIO controller. 23 | 24 | To remember which pin is enabled or disabled, the GPIO controller uses 3 bits per pin. The GPIO controller uses 24 bytes to organize the 3 bit for all 54 pins. The math adds up: Per 4 byte = 32 bit, 10 pins can be managed with 2 bit unused. 24 bytes can manage 24 / 4 _ 10 = 6 _ 10 = 60 pins. 24 bit is enought to manage all 54 pins. 25 | 26 | The three bits for the 15th pin must be set to 001 to enable the pin. The 15th pin is the 6th pin in the second 4 byte group. The first four byte group manages pins 0 to 9. The second four byte group manages 10 - 19. Pin 15 is the 6th pin in the group 10 - 19. 27 | 28 | To compute the correct 4 byte value, use the value 1 and left shift it by 18d. 29 | The result is b1000000000000000000 = 262144d. If this value is written into the correct four byte group of the GPIO controller, the pin 15 is enabled. 30 | 31 | The pin 15 (= the LED) is contained in the second 4 byte block, that means it has an offset of #4 bytes. The offset has to be added to the address 0x20200000 (on the raspberry pi 1 only!) which is where the first four byte group for GPIO pins start. 0x20200000 is the address where the first register of the GPIO controller is mapped to memory. 32 | 33 | 0x20200000 + 4 is the address of the second register for the second four byte group for the pins 10 to 19. 34 | 35 | The task is to write the value 1 left shifted by 18 to the address 0x20200000 + 4. 36 | 37 | Here is the assembler code: 38 | 39 | ``` 40 | /* add a 1 to register r1, execute 18 left shifts on register r1 */ 41 | mov r1,#1 42 | lsl r1,#18 43 | 44 | /* store the GPIO controller's memory mapped address into r0 */ 45 | ldr r0,=0x20200000 46 | 47 | /* write to the GPIO controller. Write the value in r1 to r0 with a offset of 4. */ 48 | str r1,[r0,#4] 49 | ``` 50 | 51 | ## Toggling a pin 52 | 53 | After a pin is enabled, a value has to be assigned to the pin to either turn it on or off. 54 | 55 | Remember, turning pin 15 off makes the LED light up! Turning pin 15 on, make the LED turn off! 56 | 57 | Turning the 15th pin on is achieved by writing the value 1 << 16 to the offset #40 of the GPIO controller. 58 | Turning the 15th pin off is achieved by writing the value 1 << 16 to the offset #28 of the GPIO controller. 59 | 60 | Here is the assembler code: 61 | 62 | ``` 63 | /* store the GPIO controller's memory mapped address into r0 */ 64 | ldr r0,=0x20200000 65 | 66 | /* 67 | * Set the 16th bit of r1. 68 | */ 69 | mov r1,#1 70 | lsl r1,#16 71 | 72 | /* copy r1 to r0 offset #40 */ 73 | str r1,[r0,#40] 74 | 75 | /* copy r1 to r0 offset #40 */ 76 | str r1,[r0,#28] 77 | ``` 78 | 79 | ## Testing the kernel using QEmu 80 | 81 | https://blog.agchapman.com/using-qemu-to-emulate-a-raspberry-pi/ 82 | 83 | ``` 84 | apt update 85 | apt-cache search qemu 86 | apt-get install qemu-system-arm 87 | ``` 88 | 89 | Check which machines are supported by qemu, i.e. which systems it is able to emulate: 90 | 91 | ``` 92 | qemu-system-arm -machine help 93 | ``` 94 | 95 | Running for the Raspberry Pi 1 using the versatilepb machine. 96 | If you want to know what Qemu is doing, add the -d in_asm flag. 97 | 98 | ``` 99 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic -d in_asm 100 | ``` 101 | 102 | Running for the Raspberry Pi 2 using the raspi2 machine 103 | 104 | ``` 105 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M raspi2 -nographic -d in_asm 106 | ``` 107 | 108 | To terminate qemu once it is running: 109 | 110 | Ctrl+a x 111 | 112 | There is no output at all, because nothing is written anywhere in this first assembly program. 113 | 114 | ## Transfering the kernel.img onto a SD card 115 | 116 | An explanation on how to create a SD card is given [here](https://github.com/me-no-dev/BareMetalPi). 117 | 118 | The files that have to go on the SD Card are: 119 | 120 | - bootcode.bin 121 | - start.elf 122 | - either kernel.img or kernel7.img 123 | 124 | Note that the kernel image for Raspberry Pi 1 is called kernel.img and not kernel7.img or kernel8.img. 125 | The reason is that the Raspberry PI 1 reads a file called kernel.img. 126 | 127 | Later Raspberry Pi Versions read kernel7.img and kernel8.img. 128 | 129 | The files bootcode.bin and start.elf are taken from the official Raspberry Pi Github repostiory which 130 | contains precompiled versions of those files. You could copy those files from an existing SD card but if 131 | you have no existing SD Card, creating one just to replace kernel.img is a hassle. 132 | 133 | bootcode.bin and start.elf are contained here: 134 | https://github.com/raspberrypi/firmware/tree/master/boot 135 | 136 | Create a SD Card with 137 | 138 | - a single partition 139 | - format it with fat32 140 | - into the root folder, copy bootcode.bin, start.elf and kernel.img 141 | -------------------------------------------------------------------------------- /Lesson00_LED_ON/kernel.ld: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * kernel.ld 3 | * by Alex Chadwick 4 | * 5 | * A linker script for generation of raspberry pi kernel images. 6 | ******************************************************************************/ 7 | 8 | SECTIONS { 9 | /* 10 | * First and formost we need the .init section, containing the code to 11 | * be run first. We allow room for the ATAGs and stack and conform to 12 | * the bootloader's expectation by putting this code at 0x8000. 13 | */ 14 | .init 0x8000 : { 15 | *(.init) 16 | } 17 | 18 | /* 19 | * Next we put the rest of the code. 20 | */ 21 | .text : { 22 | *(.text) 23 | } 24 | 25 | /* 26 | * Next we put the data. 27 | */ 28 | .data : { 29 | *(.data) 30 | } 31 | 32 | /* 33 | * Finally comes everything else. A fun trick here is to put all other 34 | * sections into this section, which will be discarded by default. 35 | */ 36 | /DISCARD/ : { 37 | *(*) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lesson00_LED_ON/kernel.list: -------------------------------------------------------------------------------- 1 | 2 | build/output.elf: file format elf32-littlearm 3 | 4 | 5 | Disassembly of section .init: 6 | 7 | 00008000 <_start>: 8 | 8000: e59f003c ldr r0, [pc, #60] ; 8044 9 | 8004: e3a01001 mov r1, #1 10 | 8008: e1a01901 lsl r1, r1, #18 11 | 800c: e5801004 str r1, [r0, #4] 12 | 8010: e3a01001 mov r1, #1 13 | 8014: e1a01801 lsl r1, r1, #16 14 | 15 | 00008018 : 16 | 8018: e5801028 str r1, [r0, #40] ; 0x28 17 | 801c: e3a0283f mov r2, #4128768 ; 0x3f0000 18 | 19 | 00008020 : 20 | 8020: e2422001 sub r2, r2, #1 21 | 8024: e3520000 cmp r2, #0 22 | 8028: 1afffffc bne 8020 23 | 802c: e580101c str r1, [r0, #28] 24 | 8030: e3a0283f mov r2, #4128768 ; 0x3f0000 25 | 26 | 00008034 : 27 | 8034: e2422001 sub r2, r2, #1 28 | 8038: e3520000 cmp r2, #0 29 | 803c: 1afffffc bne 8034 30 | 8040: eafffff4 b 8018 31 | 8044: 20200000 .word 0x20200000 32 | -------------------------------------------------------------------------------- /Lesson00_LED_ON/source/main.s: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * main.s 3 | * by Alex Chadwick 4 | * 5 | * A sample assembly code implementation of the ok02 operating system, that 6 | * simply turns the OK LED on and off repeatedly. 7 | * Changes since OK01 are marked with NEW. 8 | ******************************************************************************/ 9 | 10 | /* 11 | * .section is a directive to our assembler telling it to place this code first. 12 | * .globl is a directive to our assembler, that tells it to export this symbol 13 | * to the elf file. Convention dictates that the symbol _start is used for the 14 | * entry point, so this all has the net effect of setting the entry point here. 15 | * Ultimately, this is useless as the elf itself is not used in the final 16 | * result, and so the entry point really doesn't matter, but it aids clarity, 17 | * allows simulators to run the elf, and also stops us getting a linker warning 18 | * about having no entry point. 19 | */ 20 | .section .init 21 | .globl _start 22 | _start: 23 | 24 | /* 25 | * This command loads the physical address of the GPIO region into r0. 26 | */ 27 | ldr r0, =0x20200000 28 | 29 | /* 30 | * Our register use is as follows: 31 | * r0=0x20200000 the address of the GPIO region. 32 | * r1=0x00040000 a number with bits 18-20 set to 001 to put into the GPIO 33 | * function select to enable output to GPIO 16. 34 | * 0x00040000 = 1 left shifted 18 times 35 | * 36 | * then 37 | * r1=0x00010000 a number with bit 16 high, so we can communicate with GPIO 16. 38 | * 0x00010000 = 1 left shifted 16 times 39 | */ 40 | 41 | // Create a number 42 | // 0x00040000 = 1 left shifted 18 times 43 | // a 1 shifted left 18 times creates the number 0x40000 44 | mov r1, #1 45 | lsl r1, #18 46 | 47 | /* 48 | * Set the GPIO function select. 49 | */ 50 | str r1, [r0, #4] 51 | 52 | /* 53 | * Create a number. 54 | * This number is used to control the GPIO 16 that is connected to the LED 55 | * 56 | * Set the 16th bit of r1. 57 | * 0x00010000 = 1 left shifted 16 times 58 | */ 59 | mov r1, #1 60 | lsl r1, #16 61 | 62 | /* NEW 63 | * Label the next line loop$ for the infinite looping 64 | */ 65 | loop$: 66 | 67 | /* 68 | * Set GPIO 16 to low, causing the LED to turn on. 69 | */ 70 | str r1, [r0, #40] 71 | 72 | /* NEW 73 | * Now, to create a delay, we busy the processor on a pointless quest to 74 | * decrement the number 0x3F0000 to 0! 75 | * 76 | * r2=0x003F0000 a number that will take a noticeable duration for the processor 77 | * to decrement to 0, allowing us to create a delay. 78 | */ 79 | mov r2, #0x3F0000 80 | wait1$: 81 | sub r2, #1 82 | cmp r2, #0 83 | bne wait1$ 84 | 85 | /* NEW 86 | * Set GPIO 16 to high, causing the LED to turn off. 87 | */ 88 | str r1, [r0, #28] 89 | 90 | /* NEW 91 | * Wait once more. 92 | */ 93 | mov r2, #0x3F0000 94 | wait2$: 95 | sub r2, #1 96 | cmp r2, #0 97 | bne wait2$ 98 | 99 | /* 100 | * Loop over this process forevermore 101 | */ 102 | b loop$ 103 | -------------------------------------------------------------------------------- /Lesson00_LED_ON/source/main.s_LED_ON: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * main.s 3 | * by Alex Chadwick 4 | * 5 | * A sample assembly code implementation of the ok01 operating system, that 6 | * simply turns on the OK LED. 7 | ******************************************************************************/ 8 | 9 | /* 10 | * .section is a directive to our assembler telling it to place this code first. 11 | * .globl is a directive to our assembler, that tells it to export this symbol 12 | * to the elf file. Convention dictates that the symbol _start is used for the 13 | * entry point, so this all has the net effect of setting the entry point here. 14 | * Ultimately, this is useless as the elf itself is not used in the final 15 | * result, and so the entry point really doesn't matter, but it aids clarity, 16 | * allows simulators to run the elf, and also stops us getting a linker warning 17 | * about having no entry point. 18 | */ 19 | .section .init 20 | .globl _start 21 | _start: 22 | 23 | /* 24 | * This command loads the physical address of the GPIO region into r0. 25 | */ 26 | ldr r0,=0x20200000 27 | 28 | /* 29 | * Our register use is as follows: 30 | * r0=0x20200000 the address of the GPIO region. 31 | * r1=0x00040000 a number with bits 18-20 set to 001 to put into the GPIO 32 | * function select to enable output to GPIO 16. 33 | * then 34 | * r1=0x00010000 a number with bit 16 high, so we can communicate with GPIO 16. 35 | */ 36 | mov r1,#1 37 | lsl r1,#18 38 | 39 | /* 40 | * Set the GPIO function select. 41 | */ 42 | str r1,[r0,#4] 43 | 44 | /* 45 | * Set the 16th bit of r1. 46 | */ 47 | mov r1,#1 48 | lsl r1,#16 49 | 50 | /* 51 | * Set GPIO 16 to low, causing the LED to turn on. 52 | */ 53 | str r1,[r0,#40] 54 | 55 | /* 56 | * Loop over this forevermore 57 | */ 58 | loop$: 59 | b loop$ 60 | -------------------------------------------------------------------------------- /Lesson00_RasperryPiBoards/README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Boards 2 | 3 | This lesson just contains introductory information about Raspberry Pi Boards and some terminology, there is no coding done in this lesson. There are many product names and it is easy to get confused. To bring some order to the chaos, this chapter describes the hardware and separates concepts from each other. 4 | 5 | ## Raspberry Pi Models 6 | 7 | There are several revisions of the Raspberry Pi. With each new revision the hardware changed. Programming on the hardware (bare metal) makes it necessary to know what hardware is present because assembler is used to talk to the hardware directly and this type of software development is inherently platform dependant. The hardware names can then be used to retrieve the correct documentation. 8 | 9 | https://en.wikipedia.org/wiki/Raspberry_Pi has a good overview of all models. 10 | 11 | It talks about 12 | 13 | - SoC 14 | - CPU 15 | - Instruction Set 16 | 17 | How do these terms relate to each other? 18 | 19 | A system on a chip (SoC) is pretty similar to a Microcontroller. A Microcontroller contains a CPU, Memory and other hardware components on a single chip. That means for a SoC there is a die of silicon which contains circuitry for a CPU, Memory and other components. 20 | 21 | The CPU is a IP (Intellectual property) of the company ARM (Achorn RISC Machines, Advanced RISC Machines, today just ARM) which licences the specifications out. ARM does not manufacture CPU themselves, instead they licence their IP to companies such as Broadcom, TexasInstruments or STM that actually do produce the ARM CPUs in silicon. 22 | 23 | The ARM processor is just one component in a SoC. The SoC on the Raspberry Pi is created by Broadcom. The Raspberry Pi Single Board Computer (SBC) is as large as a credit card and it is created by the Raspberry Pi Foundation. On that credit card sized SBC, the Broadcom SoC is placed below the memory chip! That means the SoC is very, very small and the terms Raspberry Pi SBC and SoC are not the same at all. 24 | 25 | Inside the Broadcom SoC there is an ARM CPU as mentioned already. This ARM CPU is different for differnet versions of the Raspberry Pi. 26 | 27 | ## ARM CPU Families. 28 | 29 | ARM produced the ARM11 family of processors. Earlier processor families where called ARM10, ARM9 and so forth. 30 | 31 | The ARM CPU ARM1176JZF-S belongs to the ARM11 family as can be seen by the first part (ARM11) of it's name. It has it's own [ARM1176JZF-S Technical Reference Manual](https://developer.arm.com/documentation/ddi0301/h/programmer-s-model/exceptions/exception-vectors) 32 | 33 | The early Raspberry Pi versions used a single ARM1176JZF-S CPU which is a 32bit CPU at 700 Mhz. (Just for your information, the ARM11 family also contains the CPUs ARM1136, ARM1156 (added Thumb2 instructions), ARM1176 (used on the early Raspberry Pi and added security extensions), ARM11MPCore (added multicore support)) 34 | 35 | ARM eventually released their Cortex lineup of CPUs with replaced ARM11 and ARM11 was discontinued. 36 | 37 | The Cortex family has three branches. The branches are 38 | 39 | - Cortex-M for embedded applications. (Cortex-M0, Cortex-M3, Cortex-M4, ...) 40 | - Cortex-A for end-user application grade software. (Cortex-A7, Cortex-A53, ...) 41 | - Cortex-R for real-time applications. 42 | 43 | The later Raspbery Pi use CPU from the Cortex-A processor branch. Later Raspberry Pi versions used four Cortex-A7 CPUs in their SoC and even later Raspberry Pi versions used four Cortex-A53 CPU in their SoC which makes the SoC, quad-core SoCs. 44 | 45 | ## Instruction Sets 46 | 47 | Now that the terms SoC and CPU have been established, the only term left is the term Instruction Set. 48 | The Instruction Set is important for the software development part of bare metal programming because it determines which assembler instructions are available and which effects those assembly instructions have. 49 | 50 | The instructions set used by the Raspberry Pi are 51 | 52 | - ARMv6 for the Raspberry Pi 1 53 | - ARMv7 for the Raspberry Pi 2 v1.1 54 | - ARMv8 for the Raspberry Pi 2 v1.2 55 | - ARMv8 for the Raspberry Pi 3 56 | - ARMv8 for the Raspberry Pi 4 57 | 58 | ARMv6 is 32 bit and it is used in the ARM11 family. 59 | ARMv7 is 32 bit and it is used in the Cortex family. 60 | ARMv8 is 32/64 bit and it is used in the Cortex family. 61 | 62 | ## Documentation 63 | 64 | For information about the Broadcom SoC look for the documentation section on the Raspberry Pi Foundation website. 65 | 66 | For information about the ARM1176JZF-S CPU, the Cortex-A CPUs and the ARM InstructionSets, consult the ARM website. 67 | 68 | There is the ARM (A)rchitectural (R)eference (M)anual also called ARM ARM. So if some article talks about (arm arm) you now know that they refer to the ARM Architectural Reference Manual. 69 | -------------------------------------------------------------------------------- /Lesson00_UART_32bit/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # makefile 3 | # by Alex Chadwick 4 | # 5 | # A makefile script for generation of raspberry pi kernel images. 6 | ############################################################################### 7 | 8 | # The toolchain to use. 9 | # arm-none-eabi works, but there does exist arm-bcm2708-linux-gnueabi. 10 | ARMGNU ?= arm-none-eabi 11 | 12 | # The intermediate directory for compiled object files. 13 | BUILD = build/ 14 | 15 | # The directory in which source files are stored. 16 | SOURCE = ./ 17 | 18 | # The name of the output file to generate. 19 | TARGET = kernel.img 20 | 21 | # The name of the assembler listing file to generate. 22 | LIST = kernel.list 23 | 24 | # The name of the map file to generate. 25 | MAP = kernel.map 26 | 27 | # The name of the linker script to use. 28 | LINKER = kernel.ld 29 | 30 | # The names of all object files that must be generated. Deduced from the 31 | # assembly code files in source. 32 | OBJECTS := $(patsubst $(SOURCE)%.s,$(BUILD)%.o,$(wildcard $(SOURCE)*.s)) 33 | 34 | # Rule to make everything. 35 | all: $(TARGET) $(LIST) 36 | 37 | # Rule to remake everything. Does not include clean. 38 | rebuild: all 39 | 40 | # Rule to make the listing file. 41 | $(LIST) : $(BUILD)kernel.elf 42 | $(ARMGNU)-objdump -d $(BUILD)kernel.elf > $(LIST) 43 | 44 | # Rule to make the image file. 45 | $(TARGET) : $(BUILD)kernel.elf 46 | $(ARMGNU)-objcopy $(BUILD)kernel.elf -O binary $(BUILD)$(TARGET) 47 | 48 | # Rule to make the elf file. 49 | $(BUILD)kernel.elf : $(OBJECTS) $(LINKER) 50 | $(ARMGNU)-ld --no-undefined $(OBJECTS) -Map $(MAP) -o $(BUILD)kernel.elf -T $(LINKER) 51 | 52 | # Rule to make the object files. 53 | $(BUILD)%.o: $(SOURCE)%.s $(BUILD) 54 | $(ARMGNU)-as -I $(SOURCE) $< -o $@ 55 | 56 | $(BUILD): 57 | mkdir $@ 58 | 59 | # Rule to clean files. 60 | clean : 61 | -rm -rf $(BUILD) 62 | -rm -f $(TARGET) 63 | -rm -f $(LIST) 64 | -rm -f $(MAP) 65 | 66 | run: 67 | qemu-system-arm -kernel $(BUILD)kernel.elf -cpu arm1176 -m 256 -M versatilepb -no-reboot -nodefaults -display none -serial stdio -------------------------------------------------------------------------------- /Lesson00_UART_32bit/kernel.ld: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * kernel.ld 3 | * by Alex Chadwick 4 | * 5 | * A linker script for generation of raspberry pi kernel images. 6 | ******************************************************************************/ 7 | 8 | SECTIONS { 9 | /* 10 | * First and formost we need the .init section, containing the code to 11 | * be run first. We allow room for the ATAGs and stack and conform to 12 | * the bootloader's expectation by putting this code at 0x8000. 13 | */ 14 | .init 0x8000 : { 15 | *(.init) 16 | } 17 | 18 | /* 19 | * Next we put the rest of the code. 20 | */ 21 | .text : { 22 | *(.text) 23 | } 24 | 25 | /* 26 | * Next we put the data. 27 | */ 28 | .data : { 29 | *(.data) 30 | } 31 | 32 | /* 33 | * Finally comes everything else. A fun trick here is to put all other 34 | * sections into this section, which will be discarded by default. 35 | */ 36 | /DISCARD/ : { 37 | *(*) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lesson00_UART_32bit_C/README.md: -------------------------------------------------------------------------------- 1 | rm boot.o 2 | rm kernel.o 3 | rm kernel.elf 4 | rm kernel.img 5 | rm kernel7.img 6 | rm kernel.list 7 | rm kernel.map 8 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -c boot.s -o boot.o 9 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra 10 | arm-none-eabi-gcc -T linker.ld -o kernel.elf -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc 11 | arm-none-eabi-objcopy kernel.elf -O binary kernel.img 12 | 13 | # assembler 14 | 15 | arm-none-eabi-as -c boot.S -o boot.o 16 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -c boot.s -o boot.o 17 | 18 | # C compoiler 19 | 20 | arm-none-eabi-gcc -ffreestanding -c kernel.c -o kernel.o -O2 -Wall -Wextra 21 | arm-none-eabi-gcc -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra 22 | 23 | # link 24 | 25 | arm-none-eabi-ld --no-undefined kernel.o -Map kernel.map -o kernel.elf -T linker.ld 26 | arm-none-eabi-gcc -T linker.ld -o kernel.elf -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc 27 | 28 | # remove elf, create image 29 | 30 | arm-none-eabi-objcopy kernel.elf -O binary kernel7.img 31 | arm-none-eabi-objcopy kernel.elf -O binary kernel.img 32 | 33 | arm-none-eabi-objdump -d kernel.elf > kernel.list 34 | 35 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -no-reboot -nodefaults -display none -serial stdio 36 | qemu-system-arm -kernel kernel.elf -cpu arm1176 -m 256 -M versatilepb -no-reboot -nodefaults -display none -serial stdio 37 | qemu-system-arm -kernel kernel.elf -cpu arm1176 -m 256 -M versatilepb -no-reboot -nodefaults -display none -serial none -serial stdio 38 | 39 | qemu-system-arm -m 256 -M raspi2 -display none -serial stdio -kernel kernel.elf 40 | qemu-system-arm -m 256 -M raspi2 -display none -serial stdio -kernel kernel7.img 41 | qemu-system-arm -m 256 -M raspi2 -nographic -kernel kernel.elf 42 | qemu-system-arm -m 256 -M raspi2 -nographic -kernel kernel7.img 43 | 44 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -no-reboot -nodefaults -display none -serial stdio 45 | 46 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -kernel kernel.img -m 1024 -M raspi3 -no-reboot -nodefaults -display none -serial stdio 47 | 48 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic -d in_asm 49 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic -serial null -monitor stdio 50 | qemu-system-arm -kernel kernel7.img -cpu arm1176 -m 256 -M versatilepb -nographic 51 | 52 | qemu-system-arm -kernel uart05.bin -cpu arm1176 -m 256 -M versatilepb -nographic -serial stdio 53 | qemu-system-arm -kernel uart05.bin -cpu arm1176 -m 256 -M versatilepb -display none -serial stdio 54 | 55 | qemu-system-arm -kernel kernel7.img -cpu arm1176 -m 256 -M raspi2 -nographic 56 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M raspi2 -nographic 57 | 58 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -m 1024 -M raspi3 -kernel kernel.img 59 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -m 1024 -M raspi3 -kernel kernel.img -serial stdio 60 | 61 | qemu-system-arm -machine help 62 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic 63 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic -serial null 64 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic -monitor stdio 65 | 66 | qemu-system-arm: -serial stdio: could not connect serial device to character backend 'stdio' 67 | 68 | qemu-system-arm -kernel uart01.bin -cpu arm1176 -m 256 -M versatilepb -nographic 69 | qemu-system-arm -kernel uart02.bin -cpu arm1176 -m 256 -M versatilepb -nographic 70 | 71 | https://www.raspberrypi.org/forums/viewtopic.php?t=142531 72 | 73 | https://stackoverflow.com/questions/60552355/qemu-baremetal-emulation-how-to-view-uart-output 74 | -s is a shortcut for -gdb tcp::1234 75 | -S means freeze CPU at startup 76 | 77 | https://stackoverflow.com/questions/48135954/bare-metal-arm-raspberry-pi-qemu-strange-behavior-with-floating-point-division 78 | 79 | brew install minicom 80 | 81 | sudo minicom --device /dev/tty.SLAB_USBtoUART 82 | 83 | Esc-Q to quit, Esc-O for options, etc 84 | 85 | brew install screen 86 | 87 | sudo screen /dev/tty.SLAB_USBtoUART 115200 88 | 89 | ls /dev/tty\* 90 | -------------------------------------------------------------------------------- /Lesson00_UART_32bit_C/boot.s: -------------------------------------------------------------------------------- 1 | // AArch32 mode 2 | 3 | // To keep this in the first portion of the binary. 4 | .section ".text.boot" 5 | 6 | // Make _start global. 7 | .globl _start 8 | .org 0x8000 9 | // Entry point for the kernel. 10 | // r15 -> should begin execution at 0x8000. 11 | // r0 -> 0x00000000 12 | // r1 -> 0x00000C42 - machine id 13 | // r2 -> 0x00000100 - start of ATAGS 14 | // preserve these registers as argument for kernel_main 15 | _start: 16 | // Setup the stack. 17 | mov sp, #0x8000 18 | 19 | // Clear out bss. 20 | ldr r4, =__bss_start 21 | ldr r9, =__bss_end 22 | mov r5, #0 23 | mov r6, #0 24 | mov r7, #0 25 | mov r8, #0 26 | b 2f 27 | 28 | 1: 29 | // store multiple at r4. 30 | stmia r4!, {r5-r8} 31 | 32 | // If we are still below bss_end, loop. 33 | 2: 34 | cmp r4, r9 35 | blo 1b 36 | 37 | // Call kernel_main 38 | ldr r3, =kernel_main 39 | blx r3 40 | 41 | // halt 42 | halt: 43 | wfe 44 | b halt 45 | -------------------------------------------------------------------------------- /Lesson00_UART_32bit_C/boot.s.64bit: -------------------------------------------------------------------------------- 1 | // AArch64 mode 2 | 3 | // To keep this in the first portion of the binary. 4 | .section ".text.boot" 5 | 6 | // Make _start global. 7 | .globl _start 8 | 9 | .org 0x80000 10 | //org 0x8000 11 | // Entry point for the kernel. Registers are not defined as in AArch32. 12 | _start: 13 | // read cpu id, stop slave cores 14 | mrs x1, mpidr_el1 15 | and x1, x1, #3 16 | cbz x1, 2f 17 | // cpu id > 0, stop 18 | 1: wfe 19 | b 1b 20 | 2: // cpu id == 0 21 | 22 | // set stack before our code 23 | ldr x1, =_start 24 | mov sp, x1 25 | 26 | // clear bss 27 | ldr x1, =__bss_start 28 | ldr w2, =__bss_size 29 | 3: cbz w2, 4f 30 | str xzr, [x1], #8 31 | sub w2, w2, #1 32 | cbnz w2, 3b 33 | 34 | // jump to C code, should not return 35 | 4: bl kernel_main 36 | // for failsafe, halt this core too 37 | b 1b -------------------------------------------------------------------------------- /Lesson00_UART_32bit_C/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | /* Starts at LOADER_ADDR. */ 6 | . = 0x8000; 7 | /* For AArch64, use . = 0x80000; */ 8 | __start = .; 9 | __text_start = .; 10 | .text : 11 | { 12 | KEEP(*(.text.boot)) 13 | *(.text) 14 | } 15 | . = ALIGN(4096); /* align to page size */ 16 | __text_end = .; 17 | 18 | __rodata_start = .; 19 | .rodata : 20 | { 21 | *(.rodata) 22 | } 23 | . = ALIGN(4096); /* align to page size */ 24 | __rodata_end = .; 25 | 26 | __data_start = .; 27 | .data : 28 | { 29 | *(.data) 30 | } 31 | . = ALIGN(4096); /* align to page size */ 32 | __data_end = .; 33 | 34 | __bss_start = .; 35 | .bss : 36 | { 37 | bss = .; 38 | *(.bss) 39 | } 40 | . = ALIGN(4096); /* align to page size */ 41 | __bss_end = .; 42 | __bss_size = __bss_end - __bss_start; 43 | __end = .; 44 | } -------------------------------------------------------------------------------- /Lesson00_UART_EchoLoop_32bit/Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # makefile 3 | # by Alex Chadwick 4 | # 5 | # A makefile script for generation of raspberry pi kernel images. 6 | ############################################################################### 7 | 8 | # The toolchain to use. 9 | # arm-none-eabi works, but there does exist arm-bcm2708-linux-gnueabi. 10 | ARMGNU ?= arm-none-eabi 11 | 12 | # The intermediate directory for compiled object files. 13 | BUILD = build/ 14 | 15 | # The directory in which source files are stored. 16 | SOURCE = ./ 17 | 18 | # The name of the output file to generate. 19 | TARGET = kernel.img 20 | 21 | # The name of the assembler listing file to generate. 22 | LIST = kernel.list 23 | 24 | # The name of the map file to generate. 25 | MAP = kernel.map 26 | 27 | # The name of the linker script to use. 28 | LINKER = kernel.ld 29 | 30 | # The names of all object files that must be generated. Deduced from the 31 | # assembly code files in source. 32 | OBJECTS := $(patsubst $(SOURCE)%.s,$(BUILD)%.o,$(wildcard $(SOURCE)*.s)) 33 | 34 | # Rule to make everything. 35 | all: $(TARGET) $(LIST) 36 | 37 | # Rule to remake everything. Does not include clean. 38 | rebuild: all 39 | 40 | # Rule to make the listing file. 41 | $(LIST) : $(BUILD)output.elf 42 | $(ARMGNU)-objdump -d $(BUILD)output.elf > $(LIST) 43 | 44 | # Rule to make the image file. 45 | $(TARGET) : $(BUILD)output.elf 46 | $(ARMGNU)-objcopy $(BUILD)output.elf -O binary $(TARGET) 47 | 48 | # Rule to make the elf file. 49 | $(BUILD)output.elf : $(OBJECTS) $(LINKER) 50 | $(ARMGNU)-ld --no-undefined $(OBJECTS) -Map $(MAP) -o $(BUILD)output.elf -T $(LINKER) 51 | 52 | # Rule to make the object files. 53 | $(BUILD)%.o: $(SOURCE)%.s $(BUILD) 54 | $(ARMGNU)-as -I $(SOURCE) $< -o $@ 55 | 56 | $(BUILD): 57 | mkdir $@ 58 | 59 | # Rule to clean files. 60 | clean : 61 | -rm -rf $(BUILD) 62 | -rm -f $(TARGET) 63 | -rm -f $(LIST) 64 | -rm -f $(MAP) -------------------------------------------------------------------------------- /Lesson00_UART_EchoLoop_32bit/kernel.ld: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * kernel.ld 3 | * by Alex Chadwick 4 | * 5 | * A linker script for generation of raspberry pi kernel images. 6 | ******************************************************************************/ 7 | 8 | SECTIONS { 9 | /* 10 | * First and formost we need the .init section, containing the code to 11 | * be run first. We allow room for the ATAGs and stack and conform to 12 | * the bootloader's expectation by putting this code at 0x8000. 13 | */ 14 | .init 0x8000 : { 15 | *(.init) 16 | } 17 | 18 | /* 19 | * Next we put the rest of the code. 20 | */ 21 | .text : { 22 | *(.text) 23 | } 24 | 25 | /* 26 | * Next we put the data. 27 | */ 28 | .data : { 29 | *(.data) 30 | } 31 | 32 | /* 33 | * Finally comes everything else. A fun trick here is to put all other 34 | * sections into this section, which will be discarded by default. 35 | */ 36 | /DISCARD/ : { 37 | *(*) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lesson00_UART_EchoLoop_32bit/main.s: -------------------------------------------------------------------------------- 1 | .section .init 2 | .globl _start 3 | _start: 4 | 5 | /* ldr reg,=val puts the number val into the register named reg. */ 6 | /* mov reg,#val puts the number val into the register named reg. */ 7 | /* str reg,[dest,#val] stores the number in reg at the address given by dest + val. */ 8 | /* mrs r0, cpsr copies the status register CPSR into a general purpose register */ 9 | /* msr cpsr, r0 copies the value in a general purpose register into the status register CPSR */ 10 | 11 | /* r0 is GPIO base, 0x20200000 */ 12 | mov r0, #0x20000000 13 | mov r1, #0x00200000 14 | orr r0, r0, r1 15 | 16 | /* r1 is UART base */ 17 | orr r1, r0, #0x1000 18 | 19 | /* disable UART by writing 0 to the UART_CR register */ 20 | mov r2, #0x00000000 21 | str r2, [r1, #0x30] 22 | 23 | /* write ggpud, pud = pull up / down */ 24 | /* Disable pull up/down for all GPIO pins */ 25 | /* 0x94 = 10010100 */ 26 | str r2, [r0, #0x94] 27 | 28 | /* wait */ 29 | bl delay 30 | 31 | /* Write 1<<14 | 1<<15 = 0xC000 into the GPPUDCLK0 register. */ 32 | mov r2, #0xC000 33 | /* 0x98 = 10011000 */ 34 | str r2, [r0, #0x98] 35 | 36 | /* wait */ 37 | bl delay 38 | 39 | /* write 0 into GPPUDCLK0. Write 0 to GPPUDCLK0 to make it take effect*/ 40 | mov r2, #0x00 41 | str r2, [r0, #0x98] 42 | 43 | /* clear interrupts */ 44 | /* */ 45 | /* write 0x07ff == 0111 1111 1111 into UART0_ICR (= UART0_BASE + offset 0x44) */ 46 | /* UART0_ICR = interrupt clear register */ 47 | /* 0x07ff clears all interrupts */ 48 | mov r2, #0x0700 49 | orr r2, r2, #0x00ff 50 | str r2, [r1, #0x44] 51 | 52 | /* Set the baud rate */ 53 | /* */ 54 | /* write a 1 into UART0_IBRD (= UART0_BASE + offset 0x24) */ 55 | /* IBRD = Integral Baud Rate Definition */ 56 | /* Set integer & fractional part of baud rate */ 57 | /* Divider = UART_CLOCK/(16 * Baud) */ 58 | /* Fraction part register = (Fractional part * 64) + 0.5 */ 59 | /* UART_CLOCK = 3000000; Baud = 115200. */ 60 | /* Divider = 3000000 / (16 * 115200) = 1.627 = ~1. */ 61 | /* Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. */ 62 | mov r2, #1 63 | str r2, [r1, #0x24] 64 | 65 | /* Set the baud rate */ 66 | /* */ 67 | /* write a 40 into UART0_FBRD (= UART0_BASE + offset 0x28) */ 68 | /* FBRD = Fractional Baud Rate Definition */ 69 | mov r2, #40 70 | str r2, [r1, #0x28] 71 | 72 | /* enable 8 bit data transmission (1 stop bit, no parity) */ 73 | /* */ 74 | /* UARTC_LCRH = Line Control Register */ 75 | /* write 70 0100 0110 = into UARTC_LCRH (= UART0_BASE + offset 0x2C) */ 76 | mov r2, #0x60 77 | str r2, [r1, #0x2C] 78 | 79 | /* mask all interrupts, means disables or ignores all interrupts */ 80 | /* */ 81 | /* write 0x07F2 into UART0_IMSC (= UART0_BASE + offset 0x38) */ 82 | /* 0x07F2 = 0111 1111 0010 = (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) */ 83 | mov r2, #0x0700 84 | orr r2, r2, #0x00F2 85 | str r2, [r1, #0x38] 86 | 87 | /* enable UART0 */ 88 | /* */ 89 | /* write 0x0301 = 0011 0000 0001 into UART0_CR (= UART0_BASE + offset 0x30) */ 90 | /* bit 0 = enable/disable */ 91 | /* bit 8 = enable receive */ 92 | /* bit 9 = enable transmit */ 93 | mov r2, #0x0300 94 | orr r2, r2, #0x0001 95 | str r2, [r1, #0x0030] 96 | 97 | loop: 98 | 99 | /* A = 0x41 */ 100 | /* B = 0x42 */ 101 | /* C = 0x43 */ 102 | /* D = 0x44 */ 103 | /* E = 0x45 */ 104 | /* F = 0x46 */ 105 | /* G = 0x47 */ 106 | /* H = 0x48 */ 107 | 108 | /* a = 0x61 */ 109 | /* b = 0x62 */ 110 | /* c = 0x63 */ 111 | /* ... */ 112 | /* l = 0x6C */ 113 | /* ... */ 114 | /* o = 0x6F */ 115 | 116 | /* 117 | mov r2, #0x48 118 | bl writeCharacter 119 | mov r2, #0x61 120 | bl writeCharacter 121 | mov r2, #0x6C 122 | bl writeCharacter 123 | mov r2, #0x6C 124 | bl writeCharacter 125 | mov r2, #0x6F 126 | bl writeCharacter 127 | */ 128 | 129 | bl readCharacter 130 | bl writeCharacter 131 | 132 | /* wait 133 | bl delay*/ 134 | 135 | bal loop 136 | 137 | delay: 138 | mov r2, #0 139 | wait: 140 | add r2, r2, #1 141 | cmp r2, #0x00400000 142 | bne wait 143 | mov r15, r14 /* return to caller. r15 is the programm counter. r14 is the link register */ 144 | 145 | /* Check if another character can be written and if the fifo is empty, write the character */ 146 | writeCharacter: 147 | ldr r3, [r1, #0x18] 148 | and r3, r3, #0x20 149 | cmp r3, #0x00 150 | bne writeCharacter 151 | str r2, [r1, #0x0000] /*str r2, [r1, #0x0000] actually writes the character in r2 to the UART */ 152 | mov r15, r14 /* return to caller. r15 is the programm counter. r14 is the link register */ 153 | 154 | /* Steve Halladay - https://www.youtube.com/watch?v=x3lzMfdlh2o&list=PLRwVmtr-pp05PQDzfuOOo-eRskwHsONY0&index=11 */ 155 | readCharacter: 156 | /* wait for the UART to receive a character */ 157 | ldr r7, [r1, #0x18] 158 | and r7, r7, #0x0010 159 | cmp r7, #0x00 160 | bne readCharacter 161 | /* get the character from the UART and store it into r2 */ 162 | ldr r2, [r1, #0x00] 163 | and r2, r2, #0x00FF /* Cut of leading byte because only the lower byte contains data */ 164 | mov r15, r14 /* return to caller. r15 is the programm counter. r14 is the link register */ 165 | -------------------------------------------------------------------------------- /Lesson00_newlib/README.md: -------------------------------------------------------------------------------- 1 | # newlib 2 | 3 | ## Links 4 | 5 | https://balau82.wordpress.com/2010/12/16/using-newlib-in-arm-bare-metal-programs/ 6 | -------------------------------------------------------------------------------- /Lesson01_Assembler/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2018 bzt (bztsrc@github) 3 | # 4 | # Permission is hereby granted, free of charge, to any person 5 | # obtaining a copy of this software and associated documentation 6 | # files (the "Software"), to deal in the Software without 7 | # restriction, including without limitation the rights to use, copy, 8 | # modify, merge, publish, distribute, sublicense, and/or sell copies 9 | # of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be 13 | # included in all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | # DEALINGS IN THE SOFTWARE. 23 | # 24 | # 25 | 26 | CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles 27 | 28 | all: clean kernel8.img 29 | 30 | start.o: start.S 31 | aarch64-linux-gnu-gcc $(CFLAGS) -c start.s -o start.o 32 | 33 | kernel8.img: start.o 34 | aarch64-linux-gnu-ld -nostdlib -nostartfiles start.o -T link.ld -o kernel8.elf 35 | aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img 36 | 37 | clean: 38 | rm kernel8.elf kernel8.img *.o >/dev/null 2>/dev/null || true 39 | 40 | run: 41 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -M raspi3 -kernel kernel8.img -d in_asm -------------------------------------------------------------------------------- /Lesson01_Assembler/link.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | . = 0x80000; 4 | 5 | .text : 6 | { 7 | *(.text) 8 | } 9 | } -------------------------------------------------------------------------------- /Lesson01_Assembler/start.S: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2018 bzt (bztsrc@github) 3 | # 4 | # Permission is hereby granted, free of charge, to any person 5 | # obtaining a copy of this software and associated documentation 6 | # files (the "Software"), to deal in the Software without 7 | # restriction, including without limitation the rights to use, copy, 8 | # modify, merge, publish, distribute, sublicense, and/or sell copies 9 | # of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be 13 | # included in all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | # DEALINGS IN THE SOFTWARE. 23 | # 24 | # 25 | 26 | .section ".text" 27 | 28 | .global _start 29 | 30 | _start: 31 | main: wfe 32 | b main -------------------------------------------------------------------------------- /Lesson02_C_64bit/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2018 bzt (bztsrc@github) 3 | # 4 | # Permission is hereby granted, free of charge, to any person 5 | # obtaining a copy of this software and associated documentation 6 | # files (the "Software"), to deal in the Software without 7 | # restriction, including without limitation the rights to use, copy, 8 | # modify, merge, publish, distribute, sublicense, and/or sell copies 9 | # of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be 13 | # included in all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | # DEALINGS IN THE SOFTWARE. 23 | # 24 | # 25 | 26 | SRCS = $(wildcard *.c) 27 | OBJS = $(SRCS:.c=.o) 28 | CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles 29 | 30 | all: clean kernel8.img 31 | 32 | start.o: start.S 33 | aarch64-linux-gnu-gcc $(CFLAGS) -c start.s -o start.o 34 | 35 | %.o: %.c 36 | aarch64-linux-gnu-gcc $(CFLAGS) -c $< -o $@ 37 | 38 | kernel8.img: start.o $(OBJS) 39 | aarch64-linux-gnu-ld -nostdlib -nostartfiles start.o $(OBJS) -T link.ld -o kernel8.elf 40 | aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img 41 | 42 | clean: 43 | rm kernel8.elf kernel8.img *.o >/dev/null 2>/dev/null || true 44 | 45 | run: 46 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -M raspi3 -kernel kernel8.img -d in_asm -------------------------------------------------------------------------------- /Lesson02_C_64bit/kernel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 bzt (bztsrc@github) 3 | * 4 | * Permission is hereby granted, free of charge, to any person 5 | * obtaining a copy of this software and associated documentation 6 | * files (the "Software"), to deal in the Software without 7 | * restriction, including without limitation the rights to use, copy, 8 | * modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | void main() 27 | { 28 | while(1); 29 | } -------------------------------------------------------------------------------- /Lesson02_C_64bit/link.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 bzt (bztsrc@github) 3 | * 4 | * Permission is hereby granted, free of charge, to any person 5 | * obtaining a copy of this software and associated documentation 6 | * files (the "Software"), to deal in the Software without 7 | * restriction, including without limitation the rights to use, copy, 8 | * modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | * 24 | */ 25 | 26 | SECTIONS 27 | { 28 | . = 0x80000; 29 | 30 | .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } 31 | 32 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } 33 | 34 | PROVIDE(_data = .); 35 | .data : { *(.data .data.* .gnu.linkonce.d*) } 36 | 37 | .bss (NOLOAD) : { 38 | . = ALIGN(16); 39 | __bss_start = .; 40 | *(.bss .bss.*) 41 | *(COMMON) 42 | __bss_end = .; 43 | } 44 | 45 | _end = .; 46 | 47 | /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } 48 | } 49 | __bss_size = (__bss_end - __bss_start)>>3; -------------------------------------------------------------------------------- /Lesson02_C_64bit/start.s: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2018 bzt (bztsrc@github) 3 | // 4 | // Permission is hereby granted, free of charge, to any person 5 | // obtaining a copy of this software and associated documentation 6 | // files (the "Software"), to deal in the Software without 7 | // restriction, including without limitation the rights to use, copy, 8 | // modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | // DEALINGS IN THE SOFTWARE. 23 | // 24 | 25 | .section ".text.boot" 26 | 27 | .global _start 28 | 29 | _start: 30 | // read cpu id, stop slave cores 31 | mrs x1, mpidr_el1 32 | and x1, x1, #3 33 | cbz x1, 2f 34 | // cpu id > 0, stop 35 | 1: wfe 36 | b 1b 37 | 2: // cpu id == 0 38 | 39 | // set stack before our code 40 | ldr x1, =_start 41 | mov sp, x1 42 | 43 | // clear bss 44 | ldr x1, =__bss_start 45 | ldr w2, =__bss_size 46 | 3: cbz w2, 4f 47 | str xzr, [x1], #8 48 | sub w2, w2, #1 49 | cbnz w2, 3b 50 | 51 | // jump to C code, should not return 52 | 4: bl main 53 | // for failsafe, halt this core too 54 | b 1b -------------------------------------------------------------------------------- /Lesson03_printf_64bit/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2018 bzt (bztsrc@github) 3 | # 4 | # Permission is hereby granted, free of charge, to any person 5 | # obtaining a copy of this software and associated documentation 6 | # files (the "Software"), to deal in the Software without 7 | # restriction, including without limitation the rights to use, copy, 8 | # modify, merge, publish, distribute, sublicense, and/or sell copies 9 | # of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be 13 | # included in all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | # DEALINGS IN THE SOFTWARE. 23 | # 24 | # 25 | 26 | SRCS = $(wildcard *.c) 27 | OBJS = $(SRCS:.c=.o) 28 | CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles 29 | 30 | all: clean kernel8.img 31 | 32 | start.o: start.S 33 | aarch64-linux-gnu-gcc $(CFLAGS) -c start.S -o start.o 34 | 35 | %.o: %.c 36 | aarch64-linux-gnu-gcc -I/usr/lib/gcc-cross/aarch64-linux-gnu/9/include $(CFLAGS) -c $< -o $@ 37 | 38 | kernel8.img: start.o $(OBJS) 39 | aarch64-linux-gnu-ld -nostdlib -nostartfiles start.o $(OBJS) -T link.ld -o kernel8.elf 40 | #aarch64-linux-gnu-ld -nostdlib -nostartfiles start.o $(OBJS) -T link.ld -o kernel.elf 41 | aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img 42 | #aarch64-linux-gnu-objcopy -O binary kernel.elf kernel.img 43 | 44 | clean: 45 | rm kernel.elf kernel.img kernel8.elf kernel8.img *.o >/dev/null 2>/dev/null || true 46 | 47 | run: 48 | # QEmu options: https://wiki.gentoo.org/wiki/QEMU/Options 49 | #/tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -M raspi3 -kernel kernel8.img -d in_asm 50 | #/tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -m 1024 -M raspi3 -kernel kernel8.img 51 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -display none -serial stdio -m 1024 -M raspi3 -kernel kernel8.img 52 | # for Raspberry Pi 1 53 | #qemu-system-arm -kernel kernel8.img -cpu arm1176 -m 256 -M versatilepb -nographic -------------------------------------------------------------------------------- /Lesson03_printf_64bit/README.md: -------------------------------------------------------------------------------- 1 | # printf 2 | 3 | ## Links 4 | 5 | https://wiki.osdev.org/Raspberry_Pi_Bare_Bones 6 | 7 | ## UART (Universal Asynchronous Receiver Transmitter) 8 | 9 | The goal of this section is to send character output over UART so that the OS can print onto the console. 10 | 11 | A hello world output should be printed. 12 | 13 | NOTE: For Qemu only UART 0 works! Qemu will onyl output if you write to UART 0! 14 | 15 | /home/raspi3-tutorial/05_uart0 16 | 17 | ./gcc-arm-none-eabi-X-XXXX-XX-update/bin/arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -c boot.S -o boot.o 18 | ./gcc-arm-none-eabi-X-XXXX-XX-update/bin/arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra 19 | ./gcc-arm-none-eabi-X-XXXX-XX-update/bin/arm-none-eabi-gcc -T linker.ld -o myos.elf -ffreestanding -O2 -nostdlib boot.o kernel.o 20 | 21 | THIS WORKS AFTER COMPILING AND INSTALLING THE GNU ARM GNU EABI TOOLCHAIN 22 | 23 | rm start.o kernel.o myos.elf kernel8.elf kernel8.img 24 | /usr/bin/arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -c start.S -o start.o 25 | /usr/bin/arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra 26 | /usr/bin/arm-none-eabi-gcc -T link.ld -o kernel8.elf -ffreestanding -O2 -nostdlib start.o kernel.o 27 | 28 | /usr/bin/arm-none-eabi-gcc --target=aarch64-arm-none-eabi -mcpu=cortex-a8 -fpic -ffreestanding -c start.S -o start.o 29 | /usr/bin/arm-none-eabi-gcc -mcpu=cortex-a8 -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra 30 | /usr/bin/arm-none-eabi-gcc -T link.ld -o kernel8.elf -ffreestanding -O2 -nostdlib start.o kernel.o 31 | 32 | THIS WORKS: 33 | First install 34 | 35 | ``` 36 | $ apt-get install gcc-aarch64-linux-gnu 37 | ``` 38 | 39 | /usr/bin/aarch64-linux-gnu-gcc -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -c start.S -o start.o 40 | THIS WORKS: 41 | aarch64-linux-gnu-gcc -I/usr/lib/gcc-cross/aarch64-linux-gnu/9/include -std=gnu99 -Wall -Wextra -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -c kernel.c -o kernel.o 42 | THIS WORKS: 43 | /usr/bin/aarch64-linux-gnu-gcc -T link.ld -o kernel8.elf -ffreestanding -O2 -nostdlib start.o kernel.o 44 | 45 | aarch64-linux-gnu-objcopy -O binary myos.elf kernel8.img 46 | THIS WORKS: 47 | aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img 48 | 49 | qemu-system-arm -m 256 -M raspi2 -serial stdio -kernel myos.elf 50 | 51 | THIS WORKS: 52 | /tmp/qemu/aarch64-softmmu/qemu-system-aarch64 -nographic -M raspi3 -kernel kernel8.img 53 | 54 | apt-get remove qemu-system 55 | apt-get autoremove qemu-system 56 | apt-get autoremove --purge qemu-system 57 | apt-get install qemu-system 58 | 59 | qemu-system-arm -kernel kernel.img -cpu arm1176 -m 256 -M versatilepb -nographic 60 | 61 | /home/download/gcc-arm-none-eabi-9-2020-q2-update/bin 62 | 63 | /home/download/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -c start.S -o start.o 64 | 65 | /home/download/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-gcc -mcpu=cortex-a7 -fpic -ffreestanding -std=gnu99 -c kernel.c -o kernel.o -O2 -Wall -Wextra 66 | 67 | /home/download/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-gcc -T linker.ld -o myos.elf -ffreestanding -O2 -nostdlib start.o kernel.o 68 | 69 | rm /lib/ld-linux-aarch64.so.1 70 | rm /lib64/libc.so.6 71 | 72 | apt remove binutils-arm-none-eabi gcc-arm-none-eabi libnewlib-arm-none-eabi 73 | 74 | apt install software-properties-common 75 | 76 | add-apt-repository "deb [arch=armhf,arm64,powerpc] http://ports.ubuntu.com/ focal main" 77 | 78 | #dpkg --add-architecture powerpc 79 | 80 | dpkg --add-architecture arm64 81 | dpkg --add-architecture armhf 82 | apt-get install libc6:arm64 libc6:armhf libc6:powerpc 83 | 84 | apt-get install libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi libncurses5-dev build-essential bison flex libssl-dev bc 85 | 86 | apt-get install gcc-arm-none-eabi binutils-arm-none-eabi gdb-arm-none-eabi openocd 87 | 88 | export PATH=\$PATH:/home/download/gcc-arm-none-eabi-9-2020-q2-update/bin/ 89 | 90 | /home/download/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-gcc 91 | 92 | /home/download/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-gcc 93 | 94 | ERROR: 95 | /lib/ld-linux-aarch64.so.1: No such file or directory 96 | 97 | SOLUTION: 98 | /lib/ld-linux-aarch64.so.1 is contained in the libc package for the arm64 architecture 99 | 100 | dpkg --add-architecture arm64 101 | apt-get install libc6:arm64 102 | 103 | ERROR: 104 | The following packages have unmet dependencies: 105 | libc6:arm64 : Depends: libgcc-s1:arm64 but it is not going to be installed 106 | E: Unable to correct problems, you have held broken packages. 107 | -------------------------------------------------------------------------------- /Lesson03_printf_64bit/kernel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // board type, raspi0 = 0, raspi1 = 1, raspi2 == 2, raspi3 == 3, raspi4 == 4 5 | int raspi = 3; 6 | //int raspi = 0; 7 | 8 | static inline void mmio_write(uint32_t reg, uint32_t data) 9 | { 10 | *(volatile uint32_t*)reg = data; 11 | } 12 | 13 | static inline uint32_t mmio_read(uint32_t reg) 14 | { 15 | return *(volatile uint32_t*)reg; 16 | } 17 | 18 | // Loop times in a way that the compiler won't optimize away 19 | static inline void delay(int32_t count) 20 | { 21 | asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" 22 | : "=r"(count): [count]"0"(count) : "cc"); 23 | } 24 | 25 | enum 26 | { 27 | // // The MMIO area base address 28 | // switch (raspi) { 29 | 30 | // case 0: 31 | // case 1: MMIO_BASE = 0x20000000; break; // for raspi 1, raspi zero etc. 32 | 33 | // case 2: 34 | // case 3: MMIO_BASE = 0x3F000000; break; // for raspi 2 & 3 35 | 36 | // case 4: MMIO_BASE = 0xFE000000; break; // for raspi 4 37 | 38 | // default: MMIO_BASE = 0x20000000; break; // for raspi 1, raspi zero etc. 39 | // } 40 | 41 | // The MMIO area base address 42 | MMIO_BASE = 0x3F000000, 43 | //MMIO_BASE = 0x20000000, 44 | //0x101f1000 45 | 46 | // the GPIO registers base address. 47 | GPIO_BASE = MMIO_BASE + 0x200000, 48 | 49 | GPPUD = (GPIO_BASE + 0x94), 50 | GPPUDCLK0 = (GPIO_BASE + 0x98), 51 | 52 | // the base address for UART. 53 | UART0_BASE = GPIO_BASE + 0x1000, 54 | 55 | UART0_DR = (UART0_BASE + 0x00), 56 | UART0_RSRECR = (UART0_BASE + 0x04), 57 | UART0_FR = (UART0_BASE + 0x18), 58 | UART0_ILPR = (UART0_BASE + 0x20), 59 | UART0_IBRD = (UART0_BASE + 0x24), 60 | UART0_FBRD = (UART0_BASE + 0x28), 61 | UART0_LCRH = (UART0_BASE + 0x2C), 62 | UART0_CR = (UART0_BASE + 0x30), 63 | UART0_IFLS = (UART0_BASE + 0x34), 64 | UART0_IMSC = (UART0_BASE + 0x38), 65 | UART0_RIS = (UART0_BASE + 0x3C), 66 | UART0_MIS = (UART0_BASE + 0x40), 67 | UART0_ICR = (UART0_BASE + 0x44), 68 | UART0_DMACR = (UART0_BASE + 0x48), 69 | UART0_ITCR = (UART0_BASE + 0x80), 70 | UART0_ITIP = (UART0_BASE + 0x84), 71 | UART0_ITOP = (UART0_BASE + 0x88), 72 | UART0_TDR = (UART0_BASE + 0x8C), 73 | }; 74 | 75 | void uart_init() 76 | { 77 | // disable UART0 78 | // the UART0_CR register is the UART controll register 79 | // the bit 0 will disable UART0 if the value is 0 80 | mmio_write(UART0_CR, 0x00000000); 81 | 82 | // setup the GPIO pin 14 and 15 83 | // Disable pull up/down for all GPIO pins 84 | mmio_write(GPPUD, 0x00000000); 85 | 86 | // delay for 150 cycles 87 | delay(150); 88 | 89 | // disable pull up/down for pin 14 and 15 90 | mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); 91 | 92 | // delay for 150 cycles 93 | delay(150); 94 | 95 | // write 0 to GPPUDCLK0 to make it take effect 96 | mmio_write(GPPUDCLK0, 0x00000000); 97 | 98 | // clear pending interrupts 99 | // ICR = Interrupt Clear register 100 | // 0x7FF clears all interrupts see the Perpherals Manual 101 | mmio_write(UART0_ICR, 0x7FF); 102 | 103 | // setting the baud rate is done by computing an integral and fractional part 104 | // and then writing those two values into the IBRD and FBRD registers respectively 105 | // 106 | // set integer & fractional part of baud rate 107 | // Divider = UART_CLOCK/(16 * Baud) 108 | // Fraction part register = (Fractional part * 64) + 0.5 109 | // Baud = 115200. 110 | 111 | // for Raspi3 and 4 the UART_CLOCK is system-clock dependent by default. 112 | // Set it to 3Mhz so that we can consistently set the baud rate 113 | 114 | // divider = 3000000 / (16 * 115200) = 1.627 = ~1 115 | // the divider 3000000 is not documented and is taken from the Linux kernel source code 116 | mmio_write(UART0_IBRD, 1); 117 | 118 | // fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40 119 | mmio_write(UART0_FBRD, 40); 120 | 121 | // enable FIFO & 8 bit data transmission (1 stop bit, no parity) 122 | // LCRH = Line Control Register. Enables FIFO and 123 | mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); 124 | 125 | // Mask all interrupts (= turn them off) 126 | // IMSC = interrupt mask, mask of or disables interrupts 127 | mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | 128 | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)); 129 | 130 | // enable UART0, receive & transfer part of UART 131 | mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9)); 132 | } 133 | 134 | void uart_putc(unsigned char c) 135 | { 136 | while ( mmio_read(UART0_FR) & (1 << 5) ) 137 | { 138 | // nop 139 | } 140 | mmio_write(UART0_DR, c); 141 | } 142 | 143 | unsigned char uart_getc() 144 | { 145 | while ( mmio_read(UART0_FR) & (1 << 4) ) 146 | { 147 | // nop 148 | } 149 | return mmio_read(UART0_DR); 150 | } 151 | 152 | void uart_puts(const char* str) 153 | { 154 | for (size_t i = 0; str[i] != '\0'; i ++) { 155 | uart_putc((unsigned char)str[i]); 156 | } 157 | } 158 | 159 | void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) 160 | { 161 | (void) r0; 162 | (void) r1; 163 | (void) atags; 164 | 165 | uart_init(); 166 | while (1) { 167 | uart_puts("Garbage!\r\n"); 168 | } 169 | 170 | while (1) { 171 | uart_putc(uart_getc()); 172 | uart_putc('\n'); 173 | } 174 | } -------------------------------------------------------------------------------- /Lesson03_printf_64bit/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | /* Starts at LOADER_ADDR. */ 6 | /**/. = 0x80000; 7 | /*. = 0x8000; */ 8 | __start = .; 9 | __text_start = .; 10 | .text : 11 | { 12 | KEEP(*(.text.boot)) 13 | *(.text) 14 | } 15 | . = ALIGN(4096); /* align to page size */ 16 | __text_end = .; 17 | 18 | __rodata_start = .; 19 | .rodata : 20 | { 21 | *(.rodata) 22 | } 23 | . = ALIGN(4096); /* align to page size */ 24 | __rodata_end = .; 25 | 26 | __data_start = .; 27 | .data : 28 | { 29 | *(.data) 30 | } 31 | . = ALIGN(4096); /* align to page size */ 32 | __data_end = .; 33 | 34 | __bss_start = .; 35 | .bss : 36 | { 37 | bss = .; 38 | *(.bss) 39 | } 40 | . = ALIGN(4096); /* align to page size */ 41 | __bss_end = .; 42 | __end = .; 43 | } 44 | __bss_size = (__bss_end - __bss_start)>>3; -------------------------------------------------------------------------------- /Lesson03_printf_64bit/start.s: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2018 bzt (bztsrc@github) 3 | // 4 | // Permission is hereby granted, free of charge, to any person 5 | // obtaining a copy of this software and associated documentation 6 | // files (the "Software"), to deal in the Software without 7 | // restriction, including without limitation the rights to use, copy, 8 | // modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | // DEALINGS IN THE SOFTWARE. 23 | // 24 | 25 | .section ".text.boot" 26 | 27 | .global _start 28 | 29 | _start: 30 | // read cpu id, stop slave cores 31 | mrs x1, mpidr_el1 32 | and x1, x1, #3 33 | cbz x1, 2f 34 | // cpu id > 0, stop 35 | 1: wfe 36 | b 1b 37 | 2: // cpu id == 0 38 | 39 | // set stack before our code 40 | ldr x1, =_start 41 | mov sp, x1 42 | 43 | // clear bss 44 | ldr x1, =__bss_start 45 | ldr w2, =__bss_size 46 | 3: cbz w2, 4f 47 | str xzr, [x1], #8 48 | sub w2, w2, #1 49 | cbnz w2, 3b 50 | 51 | // jump to C code, should not return 52 | 4: bl kernel_main 53 | // for failsafe, halt this core too 54 | b 1b -------------------------------------------------------------------------------- /Lesson04_Heap/README.md: -------------------------------------------------------------------------------- 1 | # Heap 2 | 3 | A heap is a datastructure that is used to manage memory. 4 | 5 | Virtual memory also is a feature that allows you to manage memory. So that definition is too simple. 6 | 7 | The difference between virtual memory and a heap is granularity. Virtual memory manages pages in blocks of 8 | usually 4KB, 16KB or 64KB. A heap allows an application or the kernel itself to allocate very small or very large 9 | amounts of memory. A application could create an array of three integers which would be 24 bytes in size. This 10 | is a very small amount of memory compared to a 4KB block. Hence the Heap safes memory. The heap can be used with 11 | or without underlying virtual memory. 12 | 13 | A heap will grow and shrink. If the application reserves more and more memory it has to request a new block 14 | from the operating system so it can extend into that block. If the heap grows it moves its upper bound. The upper 15 | bound is usually called the break. The operation that the heap calls to request new memory is called brk() to 16 | denate that the break is moved. 17 | -------------------------------------------------------------------------------- /Lesson04_VirtualMemory/resources/FirstLevelLookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thewbi/armos/1e3140a6135bd9abcd94f18fa2000bd2d4f58c21/Lesson04_VirtualMemory/resources/FirstLevelLookup.png -------------------------------------------------------------------------------- /Lesson06_Multitasking/README.md: -------------------------------------------------------------------------------- 1 | # Multitasking 2 | 3 | ## Links 4 | 5 | - https://s-matyukevich.github.io/raspberry-pi-os/docs/lesson04/rpi-os.html 6 | - https://github.com/LdB-ECM/Raspberry-Pi 7 | - https://github.com/danielbathke/PIqueno/blob/master/scheduler.c 8 | 9 | ## Introduction 10 | 11 | Running two processes/applications at the same time is called multitasking. 12 | 13 | In cooperative multitasking, also known as non-preemptive multitasking, a process gives up control of the processor so other processes can execute. The downside is that an badly written or malisious application can hog the CPU and it cannot be forced to hand over control starving all other processes and the operating system itself. Aside from actively hogging the CPU, a flawed application can execute an endless loop and then never hand over control. 14 | 15 | In preemptive multitasking, control over the CPU is actively taken away from a process and given to another process. 16 | A timer is set to a certain frequency. It will cause an interrupt at that frequency. The interrupt handler calls the scheduler. The scheduler is implemented using one of the myriad of existing scheduling algorithms to decide which process is executed next. The current process is then interrupted, the control is given to the process that the scheduler did determine. 17 | 18 | See Chapter 2.5.3 of [1] "... A Real-Time Operating System (RTOS) can be used to handle the task scheduling (Figure 2.11). An RTOS allows multiple processes to be executed concurrently, by dividing the processor's time into time slots and allocating the time slots to the processes that require services. A timer is needed to handle the timekeeping for the RTOS, and at the end of each time slot, the timer generates a timer interrupt, which triggers the task scheduler and decides if context switching should be carried out. If yes, the current executing process is suspended and the processor executes another process." 19 | 20 | ## Multitasking in ARM 21 | 22 | How to setup the interrupt handler 23 | Simple scheduling algorithm 24 | 25 | When the interrupt occurs, the handler is called. 26 | The handler will 27 | 28 | - save CPU state (= CPSR status register) in the process structure, so should the process be continued later, the processor state can be taken from the process structure and written back into the CPU 29 | - save the process's stack 30 | - save the processes pages from the MMU page tables into the process structure, so the page table can be restored later. Another option is to only store the address to the page tables and let the page table reside in memory. 31 | - It will call the scheduler to determine the next process to run 32 | - Restore the processes page table and set them into the MMU. If the process structure only stores the address of the process's page table, that base address has to be set into the base address register of CP15. 33 | - It will restore that process's state (= restore CPSR status register) 34 | - It will restore that process's stack 35 | - continue the CPU on the new process 36 | 37 | https://developer.arm.com/documentation/den0024/a/The-Memory-Management-Unit/Context-switching 38 | 39 | # Glossary 40 | 41 | [1] "The Definitive Guide to ARM CORTEX-M3 ARM CORTEX-M4 Processors, 3rd Edition by Joseph Yiu" 42 | 43 | # Next Steps 44 | 45 | - Try out the code from https://github.com/danielbathke/PIqueno/blob/master/scheduler.c 46 | - Connect the scheduler to the ARM/CPU Timer 47 | -------------------------------------------------------------------------------- /LessonX1_FileSystem/README.md: -------------------------------------------------------------------------------- 1 | # File Systems 2 | 3 | Is needed to put an executable .elf program on. 4 | 5 | ## Links 6 | 7 | - http://www.simplyembedded.org/archives/filesystems-with-the-raspberry-pi/ 8 | -------------------------------------------------------------------------------- /LessonX2_ELF_Loader/README.md: -------------------------------------------------------------------------------- 1 | Is required to execute .elf applications written for the OS in their own process. 2 | -------------------------------------------------------------------------------- /LessonX3_C_Standard_Library/README.md: -------------------------------------------------------------------------------- 1 | Is required so that a .elf application can use OS system calls in a unified way. 2 | If the header files of the standard CLib are supported on the OS, C applications can be developed in a platform independant manner. They can be tested on another OS and then be compiled on the custom OS. 3 | 4 | https://de.wikipedia.org/wiki/C-Standard-Bibliothek 5 | 6 | In jeder standardkonformen betriebssystemgestützten Implementierung (hosted environment) von C muss die C-Standard-Bibliothek in vollem Umfang vorhanden sein. Hingegen müssen freistehende Umgebungen (freestanding environment), wie man sie beispielsweise im Embedded-Bereich häufig antrifft, nur eine festgelegte Untermenge der Standardbibliothek anbieten, um standardkonform zu sein. 7 | -------------------------------------------------------------------------------- /LessonXX_Bluetooth/README.md: -------------------------------------------------------------------------------- 1 | # Bluetooth 2 | 3 | ## Links 4 | 5 | - https://isometimes.github.io/rpi4-osdev/part8-breakout-ble/ 6 | -------------------------------------------------------------------------------- /LessonXX_JavaScript/README.md: -------------------------------------------------------------------------------- 1 | \*\* Version Numbering and Naming 2 | 3 | https://www.w3schools.com/js/js_versions.asp 4 | 5 | > JavaScript was invented by Brendan Eich in 1995, and became an ECMA standard in 1997. ECMAScript is the official name of the language. ECMAScript versions have been abbreviated to ES1, ES2, ES3, ES5, and ES6. From 2015 ECMAScript is named by year (ECMAScript 2015). 6 | 7 | The ECMA standard is called ECMA-262. So every javascript version is a version within the ECMA-262 standard. 8 | 9 | The table on https://www.w3schools.com/js/js_versions.asp shows the relationship between ESX names and the ECMAScript yyyy names: 10 | 11 | ES1 ECMAScript 1 (1997) First edition 12 | ES2 ECMAScript 2 (1998) Editorial changes 13 | ES3 ECMAScript 3 (1999) Added regular expressions Added try/catch 14 | ES4 ECMAScript 4 Never released 15 | ES5 ECMAScript 5 (2009) 16 | ES6 (ECMAScript 2015, ECMAScript 2016, ECMAScript 2017 and ECMAScript 2018) 17 | 18 | \*\* What is 'Standard ECMA-262 5.1 Edition' from June 2011 ? 19 | 20 | http://www.ecma-international.org/ecma-262/5.1/ 21 | 22 | Antlr4 Grammar for 'Standard ECMA-262 5.1 Edition' 23 | 24 | https://github.com/bkiers/ecmascript-parser (7 years since last commit) 25 | https://github.com/antlr/grammars-v4/tree/master/javascript/ecmascript (11 month since last commit) 26 | 27 | According to https://developer.mozilla.org/de/docs/Web/JavaScript 28 | 29 | > Der Standard für JavaScript ist ECMAScript. Stand 2012 unterstützen alle modernen Browser ECMAScript 5.1 vollständig. Ältere Browser unterstützen mindestens ECMAScript 3. Am 17. Juni 2015 veröffentlichte ECMA International die sechste Version von ECMAScript, welche offiziell ECMAScript 2015 genannt wird und initial mit ECMAScript 6 oder ES6 referenziert wurde. 30 | 31 | \*\* What is the latest version? 32 | 33 | https://www.ecma-international.org/publications/standards/Ecma-262.htm 34 | ECMAScript® 2020 Language Specification 35 | 11th edition (June 2020) 36 | 37 | \*\* Conformance Testing 38 | 39 | https://v8.dev/blog/modern-javascript 40 | 41 | the Test262 test suite : https://github.com/tc39/test262 42 | Kangax compatibility table : 43 | -------------------------------------------------------------------------------- /doc/Docker/README.md: -------------------------------------------------------------------------------- 1 | ## Docker 2 | 3 | To develop on Linux with qemu on any Host-OS (Linux, Windows, Mac) Docker is used. 4 | This chapter explains how to setup and use Docker. 5 | 6 | This course uses docker to run a Linux on any Host system. Linux is required because 7 | the ARM toolchain on Linux is able to compile freestanding code which means code 8 | that uses no libraries or other dependencies and can be run on the hardware. 9 | The Linux that is run inside Docker will be used to compile the code of your custom OS. 10 | 11 | Running a Linux on Windows or Mac is surprisingly easy using Docker. Docker is a 12 | infrastructure that makes the concept of Infrastructure as Code available to everyone. 13 | People write Dockerfiles that contain instructions on how to set up an operating system. 14 | You can build on the work on others to derive your own Dockerfile from an existing 15 | Dockerfile and add features to the OS. 16 | 17 | Once you have your environment described as a Dockerfile (Infrastructure as Code) you 18 | let Docker execute the Dockerfile. It will create a container. You can start one or more 19 | of those containers on your host OS. The Containers have individual file systems and can 20 | mount folders from the host OS. The containers can connect to other infrastructure via 21 | TCP/IP and other protocols as they are first class Operating Systems inside. 22 | 23 | Ultimately you can open a command line into the OS inside the running Docker container and 24 | you can execute Linux commands. This course places the source code inside a folder on the 25 | Host OS where you will edit those files. Then the Docker Container mounts that folder and 26 | compiles the code inside itself on the Linux using the ARM toolchain. This yields a kernel 27 | image which you can then transfer on a SD Card or run in an emulator such as qemu. 28 | 29 | ### Installing Docker on Linux 30 | 31 | TODO 32 | 33 | ### Installing Docker on Windows 34 | 35 | Download and install Docker Desktop from https://www.docker.com/ 36 | 37 | ### Installing Docker on MacOS 38 | 39 | Download and install Docker Desktop from https://www.docker.com/ 40 | -------------------------------------------------------------------------------- /doc/GCC_Assembler_ARM/README.md: -------------------------------------------------------------------------------- 1 | # GCC as 2 | 3 | The GNU Compiler Collection Assembler is described here: https://sourceware.org/binutils/docs/as/ 4 | 5 | It contains "ARM Dependent Features" described in chapter 9 (https://sourceware.org/binutils/docs/as/ARM_002dDependent.html#ARM_002dDependent) 6 | 7 | The GCC as is used as part of the toolchain that was used for these tutorials. 8 | 9 | ## Selecting ARM or THUMB instruction sets 10 | 11 | .arm or .code 32 selects the ARM instruction set. 12 | 13 | .thumb or .code 16 selects the THUMB instruction set. The THUMB instruction set is less performant but more dense to save space. 14 | 15 | ## Assembler Syntax 16 | 17 | ### Links 18 | 19 | https://sourceware.org/binutils/docs/as/ 20 | https://sourceware.org/binutils/docs/as/Syntax.html 21 | 22 | ### Introduction 23 | 24 | There are several different syntaxes depending on which toolchain is used. 25 | The two most common are the ARM syntax and the GCC syntax. 26 | 27 | The syntaxes are not compatible, i.e. a GCC tool will not be able to understand the ARM syntax and vice versa. 28 | 29 | This document will describe the GCC syntax. 30 | 31 | ### Comments in GCC as 32 | 33 | https://sourceware.org/binutils/docs/as/Comments.html#Comments states that there are single and multiline comments. Both are reduced to a single space. 34 | 35 | Multiline comments are defined to be /\* ... \*/. 36 | 37 | Single line comments are started by one or more characters. Which character or characters are used to start a single line comment depends on the target. 38 | 39 | I have seen 40 | 41 | - semicolon (;) 42 | - double slash (//) 43 | - semicolon followd by the at sign (;@) 44 | - single at sign (@) 45 | 46 | ## Deal with 47 | 48 | ; ... 49 | ;@ ... 50 | // ... 51 | #define 52 | .equ 53 | .section 54 | .balign 55 | .section "name", "aw" 56 | label: ; 57 | .globl