diff --git a/Lab4/.vscode/settings.json b/Lab4/.vscode/settings.json new file mode 100644 index 000000000..efafeb2f6 --- /dev/null +++ b/Lab4/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "utils.h": "c" + } +} \ No newline at end of file diff --git a/Lab4/Makefile b/Lab4/Makefile new file mode 100644 index 000000000..b84ea729b --- /dev/null +++ b/Lab4/Makefile @@ -0,0 +1,35 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +CFLAGS = -Iinclude -Iperipherals -fno-stack-protector -g +ASFLAGS = +BUILD_DIR = build + +# Define peripherals source files +PERIPHERALS_SRC_C = $(wildcard peripherals/*.c) +PERIPHERALS_SRC_S = $(wildcard peripherals/*.S) +PERIPHERALS_OBJS = $(PERIPHERALS_SRC_C:%.c=$(BUILD_DIR)/%.o) $(PERIPHERALS_SRC_S:%.S=$(BUILD_DIR)/%.o) + +# Target for peripherals +$(BUILD_DIR)/peripherals/%.o: peripherals/%.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/peripherals/%.o: peripherals/%.S + @mkdir -p $(@D) + $(AS) $(ASFLAGS) $< -o $@ + +# all: peripherals bootloader kernel +all: peripherals kernel bootloader + +peripherals: $(PERIPHERALS_OBJS) + +kernel: + $(MAKE) -C kernel BUILD_DIR=../$(BUILD_DIR) + +bootloader: + $(MAKE) -C bootloader BUILD_DIR=../$(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR) + +.PHONY: all peripherals kernel bootloader clean diff --git a/Lab4/README b/Lab4/README new file mode 100644 index 000000000..328e42a5a --- /dev/null +++ b/Lab4/README @@ -0,0 +1,47 @@ +The cpio archive format collects any number of files, directories, and +other file system objects (symbolic links, device nodes, etc.) into a +single stream of bytes. + + +CPIO New ASCII Format + +The "new" ASCII format uses 8-byte hexadecimal fields for all numbers +and separates device numbers into separate fields for major and minor +numbers. + +struct cpio_newc_header { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +}; + +Except as specified below, the fields here match those specified for +the new binary format above. + +magic The string "070701". + +check This field is always set to zero by writers and ignored by + readers. See the next section for more details. + +The pathname is followed by NUL bytes so that the total size of the +fixed header plus pathname is a multiple of four. Likewise, the file +data is padded to a multiple of four bytes. Note that this format sup- +ports only 4 gigabyte files (unlike the older ASCII format, which sup- +ports 8 gigabyte files). + +In this format, hardlinked files are handled by setting the filesize to +zero for each entry except the first one that appears in the archive. + +create a cpio archive: + find . | cpio -o -H newc > ../initramfs.cpio \ No newline at end of file diff --git a/Lab4/a.out b/Lab4/a.out new file mode 100755 index 000000000..4d8ce7207 Binary files /dev/null and b/Lab4/a.out differ diff --git a/Lab4/archive/bcm2710-rpi-3-b-plus.dtb b/Lab4/archive/bcm2710-rpi-3-b-plus.dtb new file mode 100755 index 000000000..c83b0817e Binary files /dev/null and b/Lab4/archive/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab4/archive/initramfs.cpio b/Lab4/archive/initramfs.cpio new file mode 100644 index 000000000..efc1bc727 Binary files /dev/null and b/Lab4/archive/initramfs.cpio differ diff --git a/Lab4/archive/rootfs/dir1/sub_dir1/test b/Lab4/archive/rootfs/dir1/sub_dir1/test new file mode 100644 index 000000000..793354cac --- /dev/null +++ b/Lab4/archive/rootfs/dir1/sub_dir1/test @@ -0,0 +1,2 @@ +This is test! +Newline. \ No newline at end of file diff --git a/Lab4/bcm2710-rpi-3-b-plus.dtb b/Lab4/bcm2710-rpi-3-b-plus.dtb new file mode 100755 index 000000000..c83b0817e Binary files /dev/null and b/Lab4/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab4/bootloader.zip b/Lab4/bootloader.zip new file mode 100644 index 000000000..9e81161b3 Binary files /dev/null and b/Lab4/bootloader.zip differ diff --git a/Lab4/bootloader/Makefile b/Lab4/bootloader/Makefile new file mode 100644 index 000000000..f28fa218c --- /dev/null +++ b/Lab4/bootloader/Makefile @@ -0,0 +1,33 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -I../include -I../peripherals -fno-stack-protector -g +ASFLAGS = + +# Adjust these paths as necessary +SRC_C = $(wildcard *.c) +SRC_S = $(wildcard *.S) +OBJS = $(SRC_C:%.c=$(BUILD_DIR)/bootloader/%.o) $(SRC_S:%.S=$(BUILD_DIR)/bootloader/%.o) + +# Include peripherals objects +PERIPHERALS_OBJS = $(wildcard $(BUILD_DIR)/peripherals/*.o) +LINKER_SCRIPT = linker.ld +BOOTLOADER_ELF = $(BUILD_DIR)/bootloader/bootloader.elf +BOOTLOADER_IMG = $(BUILD_DIR)/bootloader/bootloader.img + +all: $(OBJS) bootloader + +$(BUILD_DIR)/bootloader/%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/bootloader/%.o: %.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +bootloader: $(OBJS) $(PERIPHERALS_OBJS) + $(LD) -T $(LINKER_SCRIPT) -o $(BOOTLOADER_ELF) $^ + $(OBJCOPY) -O binary $(BOOTLOADER_ELF) $(BOOTLOADER_IMG) + +.PHONY: all bootloader diff --git a/Lab4/bootloader/boot.S b/Lab4/bootloader/boot.S new file mode 100644 index 000000000..f6703d5f3 --- /dev/null +++ b/Lab4/bootloader/boot.S @@ -0,0 +1,69 @@ +#include "../peripherals/mm.h" + +.section ".text.boot" + +.global _start + +_start: + // The initial state of the registers x0, x1, x2, and x3 upon startup holds specific information + // passed by the firmware/bootloader. + // x0: The 64-bit machine model number (from the Device Tree Blob, or DTB). This is an identifier + // for the hardware model the software is running on, which can be used to adjust behaviors or + // operations for different hardware variants if necessary. + mov x10, x0 + // x1: The physical address of the Device Tree Blob (DTB) in memory. The DTB is a binary + // representation of the device tree, which describes the hardware components of the system in a + // tree-like structure. This includes information about the processor, memory, peripherals, and + // other hardware components. The operating system or bootloader can parse the DTB to dynamically + // discover the hardware configuration of the system. + mov x11, x1 + // x2: Reserved or unused in the standard Raspberry Pi boot process. Its value may not be defined + // or may be specific to certain configurations or future use. It's often safe to assume this register + // does not hold information critical to the initial boot process unless specified by new documentation + // or specific boot configurations. + mov x12, x2 + // x3: Reserved or unused, similar to x2. Its purpose is dependent on the booting firmware or specific + // use cases and is generally not used in the standard boot process for Raspberry Pi. + mov x13, x3 + +// Relocate the boatloader(0x80000) to another location(0x60000) to avoid overlapping with kernel. +self_relocate: + ldr x2, =0x60000 + adr x0, __bootloader_start + adr x1, __bootloader_end + sub x1, x1, x0 + +relocate: + ldr x3, [x0], #8 + str x3, [x2], #8 + subs x1, x1, #8 + b.gt relocate + + +// Initialize the bss section. +clear_bss: + // Initialize stack pointer. + mov sp, #LOW_MEMORY + adr x0, __bss_start + adr x1, __bss_end + sub x1, x1, x0 + +// Iterate through the bss section and initialize it with zeros. +memzero: + // Stores the value from the zero register(xzr) to the memory location pointed to + // by x0. After storing the bytes, increment x0 by 8 bytes, since it's a 64-bit + // architecture. + str xzr, [x0], #8 + + // x1 contains the size of bss that needs to be zeroed out. subs also updates + // the condition flags based on the result. + subs x1, x1, #8 + + // It's a conditional branch that jumps back to memzero label if the condition + // flags indicate that the result of the previous "subs" was greater than zero. + b.gt memzero + bl bootloader_main - 0x20000 + +// This step shouldn't be reached, define it just in case. +proc_hang: + b proc_hang diff --git a/Lab4/bootloader/bootloader.c b/Lab4/bootloader/bootloader.c new file mode 100644 index 000000000..f147a4938 --- /dev/null +++ b/Lab4/bootloader/bootloader.c @@ -0,0 +1,52 @@ +#include "../peripherals/mini_uart.h" +#include "bootloader.h" +#include "../peripherals/utils.h" + +void bootloader_main(unsigned long address) { + // Location to load the kernel. + char* kernel = (char *)0x80000; + + // Store the size of the kernel image. + unsigned int size = 0; + + // Setup mini uart. + uart_init(); + uart_send_string("\r\nBooting...\r\n"); + uart_send_string("Kernel image size: "); + uart_send_string("0x"); + size = get_kernel_size(); + uart_send_uint(size); + uart_send_string(" bytes\r\n"); + + while (size--) { + // uart_send_string("Loading Kernel\r\n"); + *kernel++ = uart_recv(); + } + + uart_send_string("Kernel loaded.\r\n"); + + // Give uart enough time to send "Kernel loaded." message. + delay(5000); + + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + "mov x30, 0x80000;" + "ret;" + ); + +} + +unsigned int get_kernel_size(void) { + unsigned int size = 0; + + // The uart transfers 4 bytes at a time, while the uart_recv() reads 1 byte every time. + for (int i = 0; i < 4; i++) { + char c = uart_recv(); + size |= ((unsigned int)c & 0xFF) << (i * 8); + } + + return size; +} \ No newline at end of file diff --git a/Lab4/bootloader/bootloader.h b/Lab4/bootloader/bootloader.h new file mode 100644 index 000000000..a325b2fba --- /dev/null +++ b/Lab4/bootloader/bootloader.h @@ -0,0 +1,7 @@ +#ifndef _BOOTLOADER_H_ +#define _BOOTLOADER_H_ + +void bootloader_main(unsigned long address); +unsigned int get_kernel_size(void); + +#endif \ No newline at end of file diff --git a/Lab4/bootloader/linker.ld b/Lab4/bootloader/linker.ld new file mode 100644 index 000000000..003530818 --- /dev/null +++ b/Lab4/bootloader/linker.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + . = 0x80000; + __bootloader_start = .; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + # Here I used 16 because some machines support 4-byte, 8-byte, and 16-byte. The bootloader should + # be as fast as possible, so here I used 16. The kernel, on the other hand, uses 8-byte for maximum + # compatibility and performance. + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + . = ALIGN(8); + __bootloader_end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} \ No newline at end of file diff --git a/Lab4/firmware/bootcode.bin b/Lab4/firmware/bootcode.bin new file mode 100755 index 000000000..9e831a273 Binary files /dev/null and b/Lab4/firmware/bootcode.bin differ diff --git a/Lab4/firmware/config.txt b/Lab4/firmware/config.txt new file mode 100644 index 000000000..9c55ac4a4 --- /dev/null +++ b/Lab4/firmware/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x30000000 \ No newline at end of file diff --git a/Lab4/firmware/fixup.dat b/Lab4/firmware/fixup.dat new file mode 100755 index 000000000..2ff966820 Binary files /dev/null and b/Lab4/firmware/fixup.dat differ diff --git a/Lab4/firmware/start.elf b/Lab4/firmware/start.elf new file mode 100755 index 000000000..f5d78d670 Binary files /dev/null and b/Lab4/firmware/start.elf differ diff --git a/Lab4/initramfs.cpio b/Lab4/initramfs.cpio new file mode 100644 index 000000000..f7c9425e1 Binary files /dev/null and b/Lab4/initramfs.cpio differ diff --git a/Lab4/kernel/Makefile b/Lab4/kernel/Makefile new file mode 100644 index 000000000..e660743ff --- /dev/null +++ b/Lab4/kernel/Makefile @@ -0,0 +1,33 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -I../include -I../peripherals -fno-stack-protector -g -O0 +ASFLAGS = + +# Adjust these paths as necessary +SRC_C = $(wildcard *.c) +SRC_S = $(wildcard *.S) +OBJS = $(SRC_C:%.c=$(BUILD_DIR)/kernel/%.o) $(SRC_S:%.S=$(BUILD_DIR)/kernel/%.o) + +# Include peripherals objects +PERIPHERALS_OBJS = $(wildcard $(BUILD_DIR)/peripherals/*.o) +LINKER_SCRIPT = linker.ld +KERNEL8_ELF = $(BUILD_DIR)/kernel/kernel8.elf +KERNEL8_IMG = $(BUILD_DIR)/kernel/kernel8.img + +all: $(OBJS) kernel + +$(BUILD_DIR)/kernel/%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/kernel/%.o: %.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +kernel: $(OBJS) $(PERIPHERALS_OBJS) + $(LD) -T $(LINKER_SCRIPT) -o $(KERNEL8_ELF) $^ + $(OBJCOPY) -O binary $(KERNEL8_ELF) $(KERNEL8_IMG) + +.PHONY: all kernel diff --git a/Lab4/kernel/alloc.c b/Lab4/kernel/alloc.c new file mode 100644 index 000000000..f22768e93 --- /dev/null +++ b/Lab4/kernel/alloc.c @@ -0,0 +1,73 @@ +#include "alloc.h" +#include "mini_uart.h" +#include + +// Initialize the heap pointer. +void init_heap() { + // uart_send_string("Heap start\r\n"); + // uart_send_uint((uintptr_t)heap); + // uart_send_string("\r\n"); + heap_ptr = (unsigned char *)ALIGN((uintptr_t)heap, ALIGNMENT); + // uart_send_string("After heap alignment\r\n"); + // uart_send_uint((uintptr_t)heap_ptr); + // uart_send_string("\r\n"); +} + +// A simple allocater for continuous space. No handler was implemented, so the user +// must be careful when using the allocated memory space. No warnings will be given if +// accessing memory space not allocated. +// For example, int* table = malloc(sizeof(int) * 8), table[8] = 6. 8 exceeds allocated +// memory. +void* simple_malloc(size_t size) { + heap_ptr = (unsigned char *)ALIGN((uintptr_t)heap_ptr, ALIGNMENT); + + if (heap_ptr + size > heap + HEAP_SIZE) { + // Used all of heap's memory. + uart_send_string("Heap memory exhausted! Failed to allocate memory :(\r\n"); + return NULL; + } + + // uart_send_string("ptr address\r\n"); + // uart_send_uint((uintptr_t)heap_ptr); + // uart_send_string("\r\n"); + + void* ptr = heap_ptr; + + heap_ptr += size; + // uart_send_string("ptr address\r\n"); + // uart_send_uint((uintptr_t)ptr); + // uart_send_string("\r\n"); + + return ptr; +} + +static char* current_heap_ptr; +extern char __heap_start; +extern char __heap_end; + +void init_heap2() { + current_heap_ptr = &__heap_start; +} + +void* malloc(size_t size) { + current_heap_ptr = (unsigned char *)ALIGN((uintptr_t)current_heap_ptr, ALIGNMENT); + + // uart_send_string("__heap_end: "); + // uart_send_uint((uintptr_t)&__heap_end); + // uart_send_string("\r\n"); + + // uart_send_string("current_heap_ptr: "); + // uart_send_uint((uintptr_t)current_heap_ptr); + // uart_send_string("\r\n"); + + if (current_heap_ptr + size > &__heap_end) { + uart_send_string("Heap out of memory!\r\n"); + return NULL; + } + + void* ptr = current_heap_ptr; + + current_heap_ptr += size; + + return ptr; +} \ No newline at end of file diff --git a/Lab4/kernel/alloc.h b/Lab4/kernel/alloc.h new file mode 100644 index 000000000..500bead92 --- /dev/null +++ b/Lab4/kernel/alloc.h @@ -0,0 +1,19 @@ +#ifndef _ALLOC_H_ +#define _ALLOC_H_ + +#include "../peripherals/utils.h" +#include + +#define HEAP_SIZE 1024 * 1024 * 16 // Define 16MB heap size. +#define ALIGNMENT 8 // Align the heap pointer to the multiple of 8 before allocating. +#define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) + +static unsigned char heap[HEAP_SIZE]; +static unsigned char* heap_ptr; + +void init_heap(); +void* simple_malloc(size_t size); +void init_heap2(); +void* malloc(size_t size); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/boot.S b/Lab4/kernel/boot.S new file mode 100644 index 000000000..373d0d4c6 --- /dev/null +++ b/Lab4/kernel/boot.S @@ -0,0 +1,104 @@ +#include "../peripherals/mm.h" + +.section ".text.boot" + +.globl _start +_start: + // mrs: Move the contents of a special register to a general-purpose register. + // Reads the Multiprocessor Affinity Register (MPIDR_EL1) into register x0. + // This register contains the processor's unique ID, which is used to identify the core on which the code is running. + mrs x0, mpidr_el1 + + // Performs a bitwise AND operation on x0 with 0xFF to isolate the lower 8 bits + // of the MPIDR_EL1 register, which represent the processor ID. + and x0, x0, #0xFF + + // cbz: Compare and branch on zero + // If x0 is zero, it indicates the primary or master CPU and branches to the "master" label if true. + // If x0 is not zero(indicating secondary CPUs), it doesn't branch. + cbz x0, master + + // This label followed by an instruction creates an infinite loop, effectively hanging or stopping + // any secondary CPU cores from proceeding further. This ensures that only the primary core continues + // execution beyond this point. + b proc_hang + +master: + // Transit exception level before running kernel code.(EL2 -> EL1) + // Has to transit from the start. + bl el2_to_el1 + + + // Initialize stack pointer. + // Align to 16-byte boundary. + ldr x0, =0xC298390 + mov sp, x0 + + // Calculate the size of the bss section(stores uninitialized data) + // adr loads the address of the label into the register. + ldr x0, =bss_begin + ldr x1, =bss_end + sub x1, x1, x0 + bl memzero + + // Device tree address has been moved from x0 to x10 within the bootloader. + mov x0, x10 + bl kernel_main + b proc_hang + +// Initialize the bss section. +memzero: + // Stores the value from the zero register(xzr) to the memory location pointed to + // by x0. After storing the bytes, increment x0 by 8 bytes, since it's a 64-bit + // architecture. + str xzr, [x0], #8 + + // x1 contains the size of bss that needs to be zeroed out. subs also updates + // the condition flags based on the result. + subs x1, x1, #8 + + // It's a conditional branch that jumps back to memzero label if the condition + // flags indicate that the result of the previous subs was greater than zero. + b.gt memzero + ret + +// Change the exception level from EL2 to EL1 for the kernel.(By default, RPi's CPU runs +// in EL2 mode after booting) +el2_to_el1: + // EL1 uses aarch64 + // Consult https://developer.arm.com/documentation/ddi0601/2021-06/AArch64-Registers/HCR-EL2--Hypervisor-Configuration-Register?lang=en + // for setting the register. + mov x0, (1 << 31) + + // msr: Move the contents of a general-purpose register into the specified special register. + // The hcr_el2(Hypervisor Configuration Register) provides configuration controls for virtualization. + // It's used in hypervisor mode(EL2) to control various aspects of system behavior when executing lower + // exception levels. + msr hcr_el2, x0 + + // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/SPSR-EL2--Saved-Program-Status-Register--EL2- + // Set SPSR_EL2(Saved Program Status Register) + // EL1h (SPSel = 1) with interrupt disabled + // [3:0] 0b0101: EL1 with SP_EL1 (EL1h) -> AArch64 Exception level and selected Stack Pointer. + // [6]: FIQ interrupt mask. + // [7]: IRQ interrupt mask. + // [8]: SError interrupt mask. + // [9]: Debug exception mask. + mov x0, 0x3C5 + + // SPSR_EL2 holds the saved processor state when an exception is taken to EL2. Set the spsr_el2 register + // to ensure correct PSTATE when returning to EL1. + msr spsr_el2, x0 + + // Set ELR_EL2(Exception Link Register) from LR(Link Register) + // Holds the return address when executing an exception return operation(eret). + msr elr_el2, lr + + eret // return to EL1 + +// Prevents other CPU cores from proceeding any further. +proc_hang: + b proc_hang + +.section .data +low_stack_top: .word 0x1295B00 diff --git a/Lab4/kernel/buddy_system.c b/Lab4/kernel/buddy_system.c new file mode 100644 index 000000000..834e4f7be --- /dev/null +++ b/Lab4/kernel/buddy_system.c @@ -0,0 +1,497 @@ +#include "buddy_system.h" +#include "alloc.h" +#include "../peripherals/utils.h" +#include "../peripherals/mini_uart.h" + +list_header* free_block_list[FREE_BLOCK_LIST_SIZE]; + +// Store the info of allocated memory. +allocate_info* alloc_mem_list[MAX_ALLOC]; + +// Store the info of reserved memory. +reserve_mem_list* rsv_mem_head; + +// Size of free memory available. +uint64_t free_mem_size; + +void init_frame_freelist(void) { + free_mem_size = FREE_MEM_END_ADDR - FREE_MEM_BASE_ADDR; + if (free_mem_size < get_chunk_size(FREE_BLOCK_LIST_SIZE - 1)) { + uart_send_string("Not enough free memory! Increase free memory size or lower maximum block size!\r\n"); + return; + } + + // Initialize list headers. + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + list_header* new_header = (list_header *)malloc(sizeof(list_header)); + + new_header->next_block = NULL; + + free_block_list[i] = new_header; + } + + // Initialize the list storing allocated frames. + for (int i = 0; i < MAX_ALLOC; i++) { + alloc_mem_list[i] = NULL; + } + + // Initialize the empty list with maximum chunk size(free_list_tail). + int chunk_cnt = free_mem_size / get_chunk_size(FREE_BLOCK_LIST_SIZE - 1); + + free_block* cur_block; + for (int i = 0; i < chunk_cnt; i++) { + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + + if (i == 0) { + new_block->ind = 0; + new_block->next = NULL; + new_block->prev = new_block; + free_block_list[FREE_BLOCK_LIST_SIZE - 1]->next_block = new_block; + cur_block = new_block; + } else { + new_block->ind = cur_block->ind + power(2, FREE_BLOCK_LIST_SIZE - 1); + new_block->next = NULL; + new_block->prev = cur_block; + cur_block->next = new_block; + cur_block = cur_block->next; + } + } + +} + +allocate_info* allocate_frame(uint64_t request_size) { + free_block* allocate_block = NULL; + allocate_info* info = NULL; + + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + + // List empty. + if (free_block_list[i]->next_block == NULL) { + continue; + } + + // Each chunk size in current list. + uint64_t cur_chunk_size = get_chunk_size(i); + + if (request_size <= cur_chunk_size) { + info = (allocate_info *)malloc(sizeof(allocate_info)); + + // The starting block of the returned list. + allocate_block = free_block_list[i]->next_block; + info->start_frame = allocate_block->ind; + info->last_frame = info->start_frame + (cur_chunk_size / BLOCK_SIZE) - 1; + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Allocate Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + uart_send_string("Allocate block of memory: "); + uart_send_int(cur_chunk_size / 1024); + uart_send_string(" KB, starting from frame "); + uart_send_int(allocate_block->ind); + uart_send_string("\r\n\r\n"); + + + free_block_list[i]->next_block = allocate_block->next; + + if (free_block_list[i]->next_block != NULL) { + free_block_list[i]->next_block->prev = free_block_list[i]->next_block; + } + + // Calculate the remaining memory of allocated block. + uint64_t rem_mem = cur_chunk_size - request_size; + + // The last chunk still has memory left. + if (rem_mem > 0) { + info->last_frame = release_block(allocate_block->ind, cur_chunk_size, request_size); + } + + insert_alloc_list(info); + + break; + } + } + + check_list(); + + return info; +} + +// Put back remaining block after allocating and return the index of the last allocated block. +int release_block(int start_ind, uint64_t chunk_size, uint64_t used_mem) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Release Remaining Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + while (chunk_size > BLOCK_SIZE) { + // The last half is free to put back. + if (chunk_size / 2 >= used_mem) { + int exp = find_chunk_exp(chunk_size / 2); + int ind = start_ind + (chunk_size / 2) / BLOCK_SIZE; + + uart_send_string("Release "); + uart_send_int(chunk_size / 2 / 1024); + uart_send_string(" KB memory, starting from frame "); + uart_send_int(ind); + uart_send_string("\r\n"); + + free_block* cur_block = free_block_list[exp]->next_block; + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + + new_block->ind = ind; + new_block->prev = new_block; + new_block->next = NULL; + + // Insert the last half into free list. + if (cur_block == NULL) { + free_block_list[exp]->next_block = new_block; + } else { + while (cur_block != NULL) { + // Insert free chunk in increasing order. + if (cur_block->ind > ind) { + // New block should be head of list. + if (cur_block->prev == cur_block) { + free_block_list[exp]->next_block = new_block; + new_block->next = cur_block; + cur_block->prev = new_block; + } else { + cur_block->prev->next = new_block; + new_block->prev = cur_block->prev; + new_block->next = cur_block; + cur_block->prev = new_block; + } + break; + // New block should be inserted at the tail. + } else if (cur_block->next == NULL) { + cur_block->next = new_block; + new_block->prev = cur_block; + break; + } + cur_block = cur_block->next; + } + } + + chunk_size /= 2; + + } else { + chunk_size /= 2; + used_mem -= chunk_size; + start_ind += (chunk_size / BLOCK_SIZE); + } + } + + uart_send_string("\r\n"); + + return start_ind; +} + +// Release the block and put it back to the free list, also updating the allocated +// memory list. +void free_allocated_mem(int start_ind) { + int start_frame; + int last_frame; + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Free Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] != NULL && alloc_mem_list[i]->start_frame == start_ind) { + start_frame = alloc_mem_list[i]->start_frame; + last_frame = alloc_mem_list[i]->last_frame; + alloc_mem_list[i] = NULL; + break; + } + + if (i == MAX_ALLOC - 1) { + uart_send_string("Allocated memory not found!\r\n"); + return; + } + } + + unsigned long size = (last_frame - start_frame + 1) * (BLOCK_SIZE / 1024); + + while (size > 0) { + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + if (size >= get_chunk_size(i) / 1024 && size < get_chunk_size(i + 1) / 1024) { + uart_send_string("start_frame: "); + uart_send_int(start_frame); + uart_send_string("\r\n"); + uart_send_string("list_index: "); + uart_send_int(i); + uart_send_string("\r\n"); + merge_buddy(start_frame, i); + size -= get_chunk_size(i) / 1024; + start_frame += (get_chunk_size(i) / BLOCK_SIZE); + } + } + } + + +} + +void insert_alloc_list(allocate_info* info) { + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] == NULL) { + alloc_mem_list[i] = info; + + return; + } + } + + uart_send_string("Maximum allocation reached! Failed to allocated memory!\r\n"); +} + +void show_alloc_list2(void) { + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Allocated Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] != NULL) { + uart_send_string("Allocated frames, starting from frame "); + uart_send_int(alloc_mem_list[i]->start_frame); + uart_send_string(" to frame "); + uart_send_int(alloc_mem_list[i]->last_frame); + uart_send_string("\r\n"); + } + } + uart_send_string("\r\n"); +} + +uint64_t get_chunk_size(int list_ind) { + return BLOCK_SIZE * power(2, list_ind); +} + +int find_chunk_exp(uint64_t size) { + int tmp = size / BLOCK_SIZE; + int exp = 0; + + while (tmp > 0) { + exp++; + tmp /= 2; + } + + exp--; + + return exp; +} + +int find_bud(int ind, int exp) { + return ind ^ (1 << exp); +} + +void merge_buddy(int ind, int free_list_ind) { + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("In merge_buddy\r\n"); + uart_send_string("---------------------------------------\r\n"); + + uart_send_string("Finding buddy of block "); + uart_send_int(ind); + uart_send_string(" in list "); + uart_send_int(free_list_ind); + uart_send_string("\r\n"); + + free_block* cur_block = free_block_list[free_list_ind]->next_block; + + // Maximum size, don't merge. + if (free_list_ind == FREE_BLOCK_LIST_SIZE - 1) { + uart_send_string("Maximum block size, no need to merge.\r\n"); + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + new_block->ind = ind; + if (cur_block == NULL) { + new_block->next = NULL; + new_block->prev = new_block; + free_block_list[free_list_ind]->next_block = new_block; + } else { + while (cur_block != NULL) { + if (cur_block->ind > ind) { + // cur_block is head. + if (cur_block->prev = cur_block) { + free_block_list[free_list_ind]->next_block = new_block; + new_block->next = cur_block; + cur_block->prev = new_block; + } else { + cur_block->prev->next = new_block; + new_block->prev = cur_block->prev; + new_block->next = cur_block; + cur_block->prev = new_block; + } + break; + } + cur_block = cur_block->next; + } + } + + return; + } + + int buddy_ind = find_bud(ind, free_list_ind); + + // Find whether buddy exists. + while (cur_block != NULL) { + if (cur_block->ind == buddy_ind) { + uart_send_string("Buddy found ---> Block "); + uart_send_int(buddy_ind); + uart_send_string("\r\n"); + // Remove the block from the current list and merge it to larger memory list. + // If the block is the head. + if (cur_block->prev == cur_block) { + free_block_list[free_list_ind]->next_block = cur_block->next; + if (cur_block->next != NULL) { + cur_block->next->prev = cur_block->next; + } + // Block is tail. + } else if (cur_block->next == NULL) { + cur_block->prev->next = NULL; + // Block is at the middle + } else { + cur_block->prev->next = cur_block->next; + cur_block->next->prev = cur_block->prev; + } + + if (buddy_ind > ind) { + merge_buddy(ind, free_list_ind + 1); + } else { + merge_buddy(buddy_ind, free_list_ind + 1); + } + + return; + } + cur_block = cur_block->next; + } + + uart_send_string("No buddy found!\r\n"); + + // No buddy is found, so insert the free block to current list. + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + new_block->ind = ind; + new_block->next = NULL; + new_block->prev = new_block; + + cur_block = free_block_list[free_list_ind]->next_block; + if (cur_block == NULL) { + free_block_list[free_list_ind]->next_block = new_block; + } else { + while (cur_block != NULL) { + if (cur_block->ind > new_block->ind) { + // cur_block is head. + if (cur_block->prev = cur_block) { + cur_block->prev = new_block; + new_block->next = cur_block; + free_block_list[free_list_ind]->next_block = new_block; + } else { + cur_block->prev->next = new_block; + new_block->prev = cur_block->prev; + new_block->next = cur_block; + cur_block->prev = new_block; + } + break; + } else if (cur_block->next == NULL) { + cur_block->next = new_block; + new_block->prev = cur_block; + break; + } + + cur_block = cur_block->next; + } + } +} + +void check_list(void) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Free Frame List Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + uart_send_string("List: "); + uart_send_int(i); + uart_send_string("\r\n"); + + free_block* head = free_block_list[i]->next_block; + uart_send_string("Free block index: "); + while (i != FREE_BLOCK_LIST_SIZE - 1 && head != NULL) { + uart_send_int(head->ind); + uart_send_string(" "); + head = head->next; + } + + if (i == FREE_BLOCK_LIST_SIZE - 1) { + // Only print ten index or the output will be too much. + for (int i = 0; i < 10; i++) { + uart_send_int(head->ind); + uart_send_string(" "); + head = head->next; + } + } + uart_send_string("\r\n"); + + } + uart_send_string("\r\n"); +} + +void init_rsv_mem_list(void) { + rsv_mem_head = NULL; +} + +// Reserve the memory block.(From start to end) +/* +void memory_reserve(uint64_t start, uint64_t end) { + int start_ind = start / BLOCK_SIZE; + int end_ind = end / BLOCK_SIZE; + + reserve_mem_list* new = (reserve_mem_list *)malloc(sizeof(reserve_mem_list)); + allocate_info* info = (allocate_info *)malloc(sizeof(allocate_info)); + info->start_frame = start_ind; + info->last_frame = end_ind; + new->info = info; + new->next = NULL; + new->prev = NULL; + + // The list should be in acsending order according to starting frame index. + if (rsv_mem_head == NULL) { + rsv_mem_head = new; + } else { + reserve_mem_list* cur = rsv_mem_head; + + while (cur != NULL) { + if (new->info->start_frame < cur->info->start_frame) { + if (cur == rsv_mem_head) { + new->next = cur; + cur->prev = new; + rsv_mem_head = new; + } else { + cur->prev->next = new; + new->prev = cur->prev; + new->next = cur; + cur->prev = new; + } + break; + } else if (cur->next == NULL) { + cur->next = new; + new->prev = cur; + break; + } + + cur = cur->next; + } + } + + + reserve_mem_list* cur = rsv_mem_head; + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Reserved Memory\r\n"); + uart_send_string("---------------------------------------\r\n"); + + while (cur != NULL) { + uart_send_string("frame "); + uart_send_int(cur->info->start_frame); + uart_send_string(" ~ "); + uart_send_string("frame "); + uart_send_int(cur->info->last_frame); + uart_send_string("\r\n"); + cur = cur->next; + } +} +*/ \ No newline at end of file diff --git a/Lab4/kernel/buddy_system.h b/Lab4/kernel/buddy_system.h new file mode 100644 index 000000000..8436be6a5 --- /dev/null +++ b/Lab4/kernel/buddy_system.h @@ -0,0 +1,74 @@ +#ifndef _BUDDY_SYSTEM_H_ +#define _BUDDY_SYSTEM_H_ + +#include + +// 0x10000000 ~ 0x1FFFFFFF +// #define FREE_MEM_BASE_ADDR 0x10000000 + +// Total available memory from 0x0 to 0x3C000000. +#define FREE_MEM_BASE_ADDR 0x0 +#define FREE_MEM_END_ADDR 0x3C000000 + +/* + * Total free memory size: 256 MB + * Memory size has to be 2's exponent, otherwise the initialization will fail. + */ +//#define FREE_MEM_SIZE (1 << 16) * BLOCK_SIZE +// One block -> 4KB +#define BLOCK_SIZE 4096 + +// 7 Linked lists -> 4, 8, 16, 32, 64, 128, 256 KB +#define FREE_BLOCK_LIST_SIZE 7 + +// Maximum allocation allowed. +#define MAX_ALLOC 10 + +// Size of free memory available. +extern uint64_t free_mem_size; + +typedef struct _free_block { + // Block index. + int ind; + struct _free_block* next; + struct _free_block* prev; +} free_block; + +typedef struct _list_header { + // Exponent of current list. + int exp; + free_block* next_block; +} list_header; + +typedef struct _allocate_info { + // Stores the index of first frame allocated. + int start_frame; + // Store the number of frame pages allocated. + int last_frame; +} allocate_info; + +typedef struct _reserved_mem { + allocate_info* info; + struct _reserved_mem* next; + struct _reserved_mem* prev; +} reserve_mem_list; + +extern allocate_info* alloc_mem_list[MAX_ALLOC]; +extern reserve_mem_list* rsv_mem_head; + +void init_frame_freelist(void); +allocate_info* allocate_frame(uint64_t request_size); +uint64_t get_chunk_size(int list_ind); +int release_block(int start_ind, uint64_t chunk_size, uint64_t used_mem); +int find_chunk_exp(uint64_t size); +void check_list(void); +// After allocated frames for a request, save the info. +void insert_alloc_list(allocate_info* info); +// Free the allocated memory depending on the starting frame. +void remove_alloc_list(int start_ind); +void free_allocated_mem(int start_ind); +void show_alloc_list2(void); +void merge_buddy(int ind, int free_list_ind); +// void memory_reserve(uint64_t start, uint64_t end); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/buddy_system_2.c b/Lab4/kernel/buddy_system_2.c new file mode 100644 index 000000000..c8596cecf --- /dev/null +++ b/Lab4/kernel/buddy_system_2.c @@ -0,0 +1,471 @@ +#include "buddy_system_2.h" +#include "alloc.h" +#include "../peripherals/utils.h" +#include "../peripherals/mini_uart.h" + +entry_t** frame_arr; +list_header_t* free_list[FREE_LIST_SIZE]; +alloc_header_t* alloc_frame_list[MAX_ALLOC_CNT]; + +void init_buddy_system(void) { + uint64_t tot_mem_size = FREE_MEM_END_ADDR - FREE_MEM_BASE_ADDR; + int frame_cnt = tot_mem_size / FRAME_SIZE; + uart_send_string("frame_cnt: "); + uart_send_int(frame_cnt); + uart_send_string("\r\n"); + + for (int i = 0; i < MAX_ALLOC_CNT; i++) { + alloc_header_t* header = (alloc_header_t *)malloc(sizeof(alloc_header_t)); + header->head = NULL; + header->tail = NULL; + header->status = -1; + alloc_frame_list[i] = header; + } + + // Check whether memory is divisible by maximum frame size. + if (tot_mem_size % (FRAME_SIZE * power(2, FREE_LIST_SIZE - 1)) != 0) { + uart_send_string("Free memory not divisible by maximum frame size!\r\n"); + uart_send_string("Buddy system initialization failed!\r\n"); + return; + } + + frame_arr = (entry_t **)malloc(sizeof(entry_t *) * frame_cnt); + + for (int i = 0; i < FREE_LIST_SIZE; i++) { + list_header_t* header = (list_header_t *)malloc(sizeof(list_header_t)); + header->head = NULL; + header->tail = NULL; + free_list[i] = header; + } + + int largest_page_size = power(2, FREE_LIST_SIZE - 1); + entry_t* cur_entry; + + for (int i = 0; i < frame_cnt; i++) { + entry_t* new_entry = (entry_t *)malloc(sizeof(entry_t)); + new_entry->slot_size = SLOT_NOT_ALLOCATED; + new_entry->status = FREE_FRAME; + new_entry->next = NULL; + new_entry->prev = NULL; + new_entry->next_slot = NULL; + new_entry->prev_slot = NULL; + new_entry->ind = i; + frame_arr[i] = new_entry; + } + + // Reserve spin tables for multicore boot. + memory_reserve(0x0, 0x1000); + // Reserve kernel image. + memory_reserve(0x80000, 0xC298390); + // Reserve cpio. + memory_reserve(0x10000000, 0x10000400); + // Reserve dtb. + memory_reserve(0x2EFF7600, 0x2EFFFFDC); + + for (int i = 0; i < frame_cnt; i++) { + int start_frame = i; + int contiguous_frames = 0; + + while (i < frame_cnt && frame_arr[i]->status != USED_FRAME) { + contiguous_frames++; + i++; + + if (i % 64 == 63) { + if (frame_arr[i]->status != USED_FRAME) { + contiguous_frames++; + } + break; + } + } + + + + if (contiguous_frames > 0) { + insert_freelist(start_frame, contiguous_frames); + } + } + + + + + /* + // Frame index indicating the beginning of the largest continuous block.(256KB) + if (i % largest_page_size == 0) { + new_entry->status = FREE_LIST_SIZE - 1; + new_entry->prev = new_entry; + new_entry->next = NULL; + + // Head and tail points to the same entry if only one exists. + if (free_list[FREE_LIST_SIZE - 1]->head == NULL) { + free_list[FREE_LIST_SIZE - 1]->head = new_entry; + free_list[FREE_LIST_SIZE - 1]->tail = new_entry; + cur_entry = new_entry; + } else { + cur_entry->next = new_entry; + new_entry->prev = cur_entry; + cur_entry = cur_entry->next; + free_list[FREE_LIST_SIZE - 1]->tail = new_entry; + } + } else { + new_entry->status = FREE_FRAME; + new_entry->next = NULL; + new_entry->prev = NULL; + } + */ + + show_free_list(); +} + +void insert_freelist(int start_frame, int contiguous_frames) { + + while (contiguous_frames > 0) { + // The start frame index has to be divisible by 2^list_index to ensure alignment. + // 256 KB. + if (start_frame % (1 << 6) == 0 && contiguous_frames >= (1 << 6)) { + frame_arr[start_frame]->status = 6; + // 128 KB. + } else if (start_frame % (1 << 5) == 0 && contiguous_frames >= (1 << 5)) { + frame_arr[start_frame]->status = 5; + // 64 KB. + } else if (start_frame % (1 << 4) == 0 && contiguous_frames >= (1 << 4)) { + frame_arr[start_frame]->status = 4; + // 32 KB. + } else if (start_frame % (1 << 3) == 0 && contiguous_frames >= (1 << 3)) { + frame_arr[start_frame]->status = 3; + // 16 KB. + } else if (start_frame % (1 << 2) == 0 && contiguous_frames >= (1 << 2)) { + frame_arr[start_frame]->status = 2; + // 8 KB. + } else if (start_frame % (1 << 1) == 0 && contiguous_frames >= (1 << 1)) { + frame_arr[start_frame]->status = 1; + // 4 KB. + } else if (start_frame % (1 << 0) == 0 && contiguous_frames >= (1 << 0)) { + frame_arr[start_frame]->status = 0; + } + + if (free_list[frame_arr[start_frame]->status]->tail == NULL) { + free_list[frame_arr[start_frame]->status]->head = frame_arr[start_frame]; + free_list[frame_arr[start_frame]->status]->tail = frame_arr[start_frame]; + frame_arr[start_frame]->prev = frame_arr[start_frame]; + frame_arr[start_frame]->next = NULL; + } else { + free_list[frame_arr[start_frame]->status]->tail->next = frame_arr[start_frame]; + frame_arr[start_frame]->prev = free_list[frame_arr[start_frame]->status]->tail; + frame_arr[start_frame]->next = NULL; + free_list[frame_arr[start_frame]->status]->tail = frame_arr[start_frame]; + } + + contiguous_frames -= (1 << frame_arr[start_frame]->status); + start_frame += (1 << frame_arr[start_frame]->status); + } +} + + + +// Request in bytes. +// Return the index of alloc_frame_list array for the allocation. +int alloc_page(uint64_t request_size) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Allocate Page\r\n"); + uart_send_string("---------------------------------------\r\n"); + + if (request_size > power(2, FREE_LIST_SIZE - 1) * FRAME_SIZE) { + uart_send_string("Request exceed maximum page size! Allocation failed!\r\n"); + return -1; + } + + // Find the smallest page required. + int list_ind = -1; + + for (int i = 0; i < FREE_LIST_SIZE; i++) { + if (power(2, i) * FRAME_SIZE >= request_size) { + // The list has free memory. + if (free_list[i]->head != NULL) { + list_ind = i; + break; + } + } + } + + uart_send_string("Allocate page of size "); + uart_send_int(power(2, list_ind) * 4); + uart_send_string(" KB\r\n"); + + if (list_ind == -1) { + uart_send_string("No available memory in buddy system!\r\n"); + return -1; + } + + entry_t* cur_entry = free_list[list_ind]->head; + + // Remove current entry from free list. + if (free_list[list_ind]->head->next != NULL) { + free_list[list_ind]->head = free_list[list_ind]->head->next; + } else { + free_list[list_ind]->head = NULL; + free_list[list_ind]->tail = NULL; + } + + // Release redundant memory. + if (list_ind != 0) { + uint64_t prev_list_size = power(2, list_ind - 1) * FRAME_SIZE; + + // Allocated memory is much larger than required. Put back the last half until + // remaining memory is of correct size. + while (prev_list_size >= request_size) { + // Split the list starting from frame . + int split_ind = cur_entry->ind + power(2, list_ind - 1); + + // Insert into the end of free list. + if (free_list[list_ind - 1]->tail == NULL) { + free_list[list_ind - 1]->head = frame_arr[split_ind]; + free_list[list_ind - 1]->tail = free_list[list_ind - 1]->head; + + frame_arr[split_ind]->prev = frame_arr[split_ind]; + frame_arr[split_ind]->next = NULL; + } else { + free_list[list_ind - 1]->tail->next = frame_arr[split_ind]; + frame_arr[split_ind]->prev = free_list[list_ind - 1]->tail; + frame_arr[split_ind]->next = NULL; + free_list[list_ind - 1]->tail = frame_arr[split_ind]; + } + + // Update frame array. + frame_arr[split_ind]->status = list_ind - 1; + uart_send_string("split_ind: "); + uart_send_int(split_ind); + uart_send_string("\r\n"); + + uart_send_string("f_split_ind: "); + uart_send_int(frame_arr[split_ind]->status); + uart_send_string("\r\n"); + list_ind--; + + if (list_ind <= 0) { + break; + } + + prev_list_size = power(2, list_ind - 1) * FRAME_SIZE; + } + + show_free_list(); + + cur_entry->status = list_ind; + // uart_send_string("cur_entry->status: "); + // uart_send_int(cur_entry->status); + // uart_send_string("\r\n"); + } + + // Record allocated info. + int alloc_ind; + for (alloc_ind = 0; alloc_ind < MAX_ALLOC_CNT; alloc_ind++) { + if (alloc_frame_list[alloc_ind]->head == NULL) { + alloc_frame_list[alloc_ind]->head = cur_entry; + alloc_frame_list[alloc_ind]->tail = frame_arr[cur_entry->ind + power(2, list_ind) - 1]; + alloc_frame_list[alloc_ind]->status = cur_entry->status; + break; + } + } + + if (alloc_ind == MAX_ALLOC_CNT) { + uart_send_string("Maximum allocation reached! Unable to allocate new frames!\r\n"); + return -1; + } + + // Update the status of allocated frames. + uart_send_string("\r\ncur_entry->ind: "); + uart_send_int(cur_entry->ind); + uart_send_string("\r\n"); + uart_send_string("cur_entry->status: "); + uart_send_int(power(2, cur_entry->status)); + uart_send_string("\r\n"); + for (int i = alloc_frame_list[alloc_ind]->head->ind; i <= alloc_frame_list[alloc_ind]->tail->ind; i++) { + frame_arr[i]->status = USED_FRAME; + } + + return alloc_ind; +} + +void free_page(int start_frame_ind) { + + // uart_send_string("\r\n---------------------------------------\r\n"); + // uart_send_string("Free Page\r\n"); + // uart_send_string("---------------------------------------\r\n"); + + entry_t* start_frame = NULL; + entry_t* last_frame = NULL; + int alloc_status; + + for (int i = 0; i < MAX_ALLOC_CNT; i++) { + if (alloc_frame_list[i]->head->ind == start_frame_ind) { + start_frame = alloc_frame_list[i]->head; + last_frame = alloc_frame_list[i]->tail; + alloc_status = alloc_frame_list[i]->status; + alloc_frame_list[i]->head = NULL; + alloc_frame_list[i]->tail = NULL; + alloc_frame_list[i]->status = -1; + break; + } + } + + if (start_frame == NULL) { + uart_send_string("Frame not allocated! Free page failed!\r\n"); + return; + } + + start_frame->status = alloc_status; + + // uart_send_string("start_frame->ind: "); + // uart_send_int(start_frame->ind); + // uart_send_string("\r\n"); + + // uart_send_string("alloc_status: "); + // uart_send_int(alloc_status); + // uart_send_string("\r\n"); + + int buddy_ind = find_buddy(start_frame->ind, alloc_status); + // uart_send_string("buddy_ind: "); + // uart_send_int(buddy_ind); + // uart_send_string("\r\n"); + + // uart_send_string("alloc_status: "); + // uart_send_int(frame_arr[buddy_ind]->status); + // uart_send_string("\r\n"); + + // If buddy exists, merge them and move on to the next level. + // Keep merging until reaching maximum list size or no buddy found. + while (frame_arr[buddy_ind]->status == alloc_status && alloc_status < FREE_LIST_SIZE - 1) { + // uart_send_string("In while\r\n"); + // uart_send_string("alloc_status: "); + // uart_send_int(alloc_status); + // uart_send_string("\r\n"); + + // Update free list. + entry_t* buddy = frame_arr[buddy_ind]; + + // Buddy is at the head of the list. + if (buddy->prev == buddy) { + // Only page in the list. + if (buddy->next == NULL) { + free_list[alloc_status]->head = NULL; + free_list[alloc_status]->tail = NULL; + } else { + free_list[alloc_status]->head = buddy->next; + free_list[alloc_status]->head->prev = free_list[alloc_status]->head; + } + } else { + if (buddy->next == NULL) { + free_list[alloc_status]->tail = free_list[alloc_status]->tail->prev; + free_list[alloc_status]->tail->next = NULL; + } else { + buddy->next->prev = buddy->prev; + buddy->prev->next = buddy->next; + } + } + buddy->next = NULL; + buddy->prev = NULL; + start_frame->next = NULL; + start_frame->prev = NULL; + + int merge_block_start = (start_frame->ind < buddy_ind) ? start_frame->ind : buddy_ind; + start_frame = frame_arr[merge_block_start]; + + // Update the frame status to free. + // uart_send_string("merge_block_start: "); + // uart_send_int(merge_block_start); + // uart_send_string("\r\n"); + + // uart_send_string("frame_arr[merge_block_start]->status: "); + // uart_send_int(frame_arr[merge_block_start]->status); + // uart_send_string("\r\n"); + + frame_arr[merge_block_start]->status = alloc_status + 1; + for (int i = frame_arr[merge_block_start]->ind + 1; i < frame_arr[merge_block_start]->ind + power(2, alloc_status + 1); i++) { + frame_arr[i]->status = FREE_FRAME; + } + buddy_ind = find_buddy(frame_arr[merge_block_start]->ind, alloc_status + 1); + + // uart_send_string("buddy_ind: "); + // uart_send_int(buddy_ind); + // uart_send_string("\r\n"); + // uart_send_string("Buddy_status: "); + // uart_send_int(frame_arr[buddy_ind]->status); + // uart_send_string("\r\n"); + alloc_status++; + } + + // Insert newly freed frames into free list. + //alloc_status--; + if (free_list[alloc_status]->tail == NULL) { + free_list[alloc_status]->head = start_frame; + free_list[alloc_status]->tail = start_frame; + start_frame->prev = start_frame; + start_frame->next = NULL; + } else { + free_list[alloc_status]->tail->next = start_frame; + start_frame->prev = free_list[alloc_status]->tail; + start_frame->next = NULL; + free_list[alloc_status]->tail = start_frame; + } + +} + +int find_buddy(int ind, int exp) { + return ind ^ (1 << exp); +} + +void memory_reserve(uint64_t start, uint64_t end) { + int start_frame; + int end_frame; + + start_frame = (start - FREE_MEM_BASE_ADDR) / FRAME_SIZE; + end_frame = (end - FREE_MEM_BASE_ADDR) / FRAME_SIZE; + + for (int i = start_frame; i <= end_frame; i++) { + frame_arr[i]->status = USED_FRAME; + } +} + +void show_free_list(void) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Free Memory List\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < FREE_LIST_SIZE; i++) { + uart_send_string("Current list -> "); + uart_send_int(i); + uart_send_string(": "); + entry_t* cur = free_list[i]->head; + + int cnt = 0; + while (cur != NULL && cnt <= 10) { + uart_send_int(cur->ind); + uart_send_string(" "); + cur = cur->next; + cnt++; + } + uart_send_string("\r\n"); + } + uart_send_int(free_list[FREE_LIST_SIZE - 1]->tail->prev->ind); + uart_send_string(" "); + uart_send_int(free_list[FREE_LIST_SIZE - 1]->tail->ind); + uart_send_string(" "); +} + +void show_alloc_list(void) { + for (int i = 0; i < MAX_ALLOC_CNT; i++) { + if (alloc_frame_list[i]->head != NULL) { + int cur_frame = alloc_frame_list[i]->head->ind; + int end_frame = alloc_frame_list[i]->tail->ind; + + for (; cur_frame <= end_frame; cur_frame++) { + uart_send_int(frame_arr[cur_frame]->ind); + uart_send_string(" "); + } + + uart_send_string("\r\n"); + } + } +} \ No newline at end of file diff --git a/Lab4/kernel/buddy_system_2.h b/Lab4/kernel/buddy_system_2.h new file mode 100644 index 000000000..90811dc70 --- /dev/null +++ b/Lab4/kernel/buddy_system_2.h @@ -0,0 +1,78 @@ +#ifndef _BUDDY_SYSTEM_2_ +#define _BUDDY_SYSTEM_2_ + +#include + +#define FREE_MEM_BASE_ADDR 0x0 +#define FREE_MEM_END_ADDR 0x3C000000 + +// Each frame is 4KB. +#define FRAME_SIZE 4096 + +// List stores page frames of size 4KB, 8KB, 16KB, 32KB, 64KB, 128KB, 256KB. +#define FREE_LIST_SIZE 7 + +// Maximum allocation from the buddy system permitted. +#define MAX_ALLOC_CNT 10 + +// Status of the frame. +// FREE_FRAME -> Frame is free but belong to a larger contiguous page. +#define FREE_FRAME 10 +// USED_FRAME -> Allocated frame. Unusable. +#define USED_FRAME 11 + +// 'A' -> 101, 'B' -> 102, 'C' -> 103 +// At most 64 frames will be allocated(256 KB) +#define SLOT_64B 'A' +#define SLOT_128B 'B' +#define SLOT_256B 'C' +#define SLOT_NOT_ALLOCATED 'D' + +// Reserve memory + + +typedef struct entry { + short status; + int ind; + struct entry* next; + struct entry* prev; + + // Linked list for dynamic allocator. + // Storing allocated slots. + // Ex: slot 0 is used -> FFFF FFFF FFFF FFF0(In hexadecimal) + uint64_t bitmap; + // Determine the slot size. + char slot_size; + // Remaining slot count. + short slot_cnt; + struct entry* next_slot; + struct entry* prev_slot; +} entry_t; + +typedef struct list_header { + // Points to head entry and tail entry of the list. + entry_t* head; + entry_t* tail; +} list_header_t; + +typedef struct alloc_header { + // Points to head entry and tail entry of the list. + entry_t* head; + entry_t* tail; + // Size of allocated block. + short status; +} alloc_header_t; + +extern alloc_header_t* alloc_frame_list[MAX_ALLOC_CNT]; +extern entry_t** frame_arr; + +void memory_reserve(uint64_t start, uint64_t end); +void insert_freelist(int start_frame, int contiguous_frames); +void init_buddy_system(void); +int alloc_page(uint64_t request_size); +void free_page(int start_frame_ind); +void show_free_list(void); +void show_alloc_list(void); +int find_buddy(int ind, int exp); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/device_tree.c b/Lab4/kernel/device_tree.c new file mode 100644 index 000000000..d39cdc4cc --- /dev/null +++ b/Lab4/kernel/device_tree.c @@ -0,0 +1,208 @@ +#include "device_tree.h" +#include "../peripherals/mini_uart.h" +#include "initramdisk.h" + +DTBHeader myHeader; + +// For listing all the child node names of the reserved memory node. +short rvmem_child_cnt; + +// Initialize the header struct. +// return 0 for success, 1 for error. +int fdt_traverse(fdt_node_callback_t callback, uintptr_t dtb_address) { + uint32_t* dtb = (uint32_t *)dtb_address; + + rvmem_child_cnt = 0; + + // Parse the DTB header fields. + // __builtin_bswap32() is a GCC built-in function to convert to the system's endianness.(RPi uses little-endian) + myHeader.magic = __builtin_bswap32(*dtb++); + if ((myHeader.magic - 0xd00dfeed) != 0) { + uart_send_string("DTB magic number doesn't match!\r\n"); + return 1; + } + myHeader.totalsize = __builtin_bswap32(*dtb++); + myHeader.off_dt_struct = __builtin_bswap32(*dtb++); + myHeader.off_dt_strings = __builtin_bswap32(*dtb++); + myHeader.off_mem_rsvmap = __builtin_bswap32(*dtb++); + myHeader.version = __builtin_bswap32(*dtb++); + myHeader.last_comp_version = __builtin_bswap32(*dtb++); + myHeader.boot_cpuid_phys = __builtin_bswap32(*dtb++); + myHeader.size_dt_strings = __builtin_bswap32(*dtb++); + myHeader.size_dt_struct = __builtin_bswap32(*dtb++); + + if (parse_struct(callback, dtb_address) != 0) { + uart_send_string("Error parsing devicetree!\r\n"); + } +} + +// Parse the entire structure and call corresponding callback functions to perform +// certain operations. +int parse_struct(fdt_node_callback_t callback, uintptr_t dtb_start_addr) { + + // Point to the beginning of structure block. + uint32_t* cur_ptr = (uint32_t *)(dtb_start_addr + myHeader.off_dt_struct); + + for (;;) { + uint32_t token = __builtin_bswap32(*cur_ptr++); + + switch (token) { + case FDT_BEGIN_NODE: + // uart_send_string("In FDT_BEGIN_NODE!\r\n"); + // The node name consists of 31 characters at most, with the root node an empty string. + char name[32]; + // Reset name array. + for (int i = 0; i < 31; i++) { + name[i] = '\0'; + } + + char* name_ptr = (char *)cur_ptr; + + // Root node. + if (*name_ptr == '\0') { + name[0] = '/'; + name_ptr++; + // All other nodes has names. + } else { + for (int i = 0; *name_ptr != '\0'; i++, name_ptr++) { + name[i] = *name_ptr; + } + // Ignore the null terminator. + name_ptr++; + } + + cur_ptr = (uint32_t *)ALIGN((uintptr_t)name_ptr, ALIGN_STRUCT_BLOCK); + + callback(token, name, NULL, (uintptr_t)cur_ptr); + break; + case FDT_END_NODE: + break; + case FDT_PROP: + uint32_t len = __builtin_bswap32(*(uint32_t *)cur_ptr++); + uint32_t nameoff = __builtin_bswap32(*(uint32_t *)cur_ptr++); + + // Point to the address storing the property name within the strings block. + char* prop_name_ptr = (char *)(dtb_start_addr + myHeader.off_dt_strings + nameoff); + char prop_name[32]; + for (int i = 0; i < 32; i++) { + prop_name[i] = '\0'; + } + + for (int i = 0; *prop_name_ptr != '\0'; i++, prop_name_ptr++) { + prop_name[i] = *prop_name_ptr; + } + + // Pass the length of the property's value and the pointer to that value + // into callback function for extracting value for that property. + callback(token, prop_name, &len, (uintptr_t)cur_ptr); + + // Property's value is given as a byte string of length len(In bytes). + // cur_ptr is an uint32 pointer, so when it increments by 1, it's + // actually incrementing 4 bytes. + if (len % 4 == 0) { + cur_ptr += (len / 4); + } else { + cur_ptr += (len / 4 + 1); + } + + cur_ptr = (uint32_t *)ALIGN((uintptr_t)cur_ptr, ALIGN_STRUCT_BLOCK); + + break; + case FDT_NOP: + continue; + break; + case FDT_END: + return 0; + default: + break; + } + + } +} + +void print_tree(int type, const char* name, void* data, uintptr_t cur_ptr) { + switch (type) { + case FDT_BEGIN_NODE: + uart_send_string("Node name: "); + uart_send_string("\r\n"); + uart_send_string((char *)name); + uart_send_string("\r\n"); + break; + + // Also print the property value. + case FDT_PROP: + uart_send_string("Property: "); + uart_send_string((char *)name); + uart_send_string("\r\n"); + // Value: or + if (!strcmp(name, "compatible") || !strcmp(name, "model")) { + char* val_ptr = (char *)cur_ptr; + int len = *(int *)data; + uart_send_string(" "); + for (int i = 0; i < len; i++) { + if (*val_ptr == '\0') { + if (i != len - 1) { + uart_send_string("; "); + } else { + uart_send(';'); + } + val_ptr++; + + } else { + uart_send(*val_ptr++); + } + } + + uart_send_string("\r\n"); + } + break; + default: + break; + } +} + +void get_initrd_address(int type, const char* name, void* data, uintptr_t cur_ptr) { + // Address of initramfs is stored within the property name "linux,initrd-start". + if (type == FDT_PROP) { + if (strcmp(name, "linux,initrd-start") == 0) { + uint32_t* val_ptr = (uint32_t *)cur_ptr; + initramdisk_main(__builtin_bswap32(*val_ptr)); + uart_send_string("initramdisk addr: "); + uart_send_uint(__builtin_bswap32(*val_ptr)); + uart_send_string("\r\n"); + } + } +} + +int parse_reserved_memory(uintptr_t mem_rsvmap_addr) { + Mem_Reserve_Entry* entry = (Mem_Reserve_Entry *)mem_rsvmap_addr; + + while (__builtin_bswap64(entry->address) != 0 || __builtin_bswap64(entry->size) != 0) { + uart_send_string("Memory reserve address: "); + uart_send_int(__builtin_bswap64(entry->address)); + uart_send_string(" ~ "); + uart_send_int(__builtin_bswap64(entry->address) + __builtin_bswap64(entry->size)); + uart_send_string("\r\n"); + entry++; + } + + uart_send_string("End of parsing memory reservation block\r\n"); + return 0; +} + +void parse_rvmem_child(int type, const char* name, void* data, uintptr_t cur_ptr) { + + if (type == FDT_BEGIN_NODE) { + if (strcmp(name, "reserved-memory") == 0) { + rvmem_child_cnt++; + } else if (rvmem_child_cnt > 0) { + uart_send_string("Reserved-memory device: "); + uart_send_string((char *)name); + uart_send_string("\r\n"); + } + } else if (type == FDT_END_NODE) { + if (rvmem_child_cnt > 0) { + rvmem_child_cnt--; + } + } +} \ No newline at end of file diff --git a/Lab4/kernel/device_tree.h b/Lab4/kernel/device_tree.h new file mode 100644 index 000000000..2cdc18d27 --- /dev/null +++ b/Lab4/kernel/device_tree.h @@ -0,0 +1,68 @@ +#ifndef _DT_H_ +#define _DT_H_ + +#include "alloc.h" + +#define ALIGN_MEMORY_BLOCK 8 +#define ALIGN_STRUCT_BLOCK 4 +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +typedef struct _header_block { + uint32_t magic; // Contain value 0xd00dfeed(big-endian) + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +} DTBHeader; + +typedef struct _mem_block { + uint64_t address; + uint64_t size; + // Use a singly-linked list for the memory reservation blocks. + struct _mem_block* nextNode; +} DTBMemory; + +typedef struct _struct_prop { + uint32_t len; + uint32_t nameoff; + char* prop_name; + // The data type of val is defined according to its property. + void* val; + // Singly linked list for property list + struct _struct_prop* next; +} Prop; + +typedef struct _struct_node { + // Number of characters ranged from 1~31 + char node_name[32]; + Prop* prop_node_head; + struct _struct_node* next_node; + struct _stucr_node* next_child_node; +} Node; + +typedef struct _mem_reserve_entry { + uint64_t address; + uint64_t size; +} Mem_Reserve_Entry; + +extern DTBHeader myHeader; + +// Define a callback function type for processing nodes. +typedef void (*fdt_node_callback_t)(int type, const char* name, void* data, uintptr_t cur_ptr); +int fdt_traverse(fdt_node_callback_t callback, uintptr_t dtb_address); +int parse_struct(fdt_node_callback_t callback, uintptr_t dtb_start_addr); +void print_tree(int type, const char* name, void* data, uintptr_t cur_ptr); +void get_initrd_address(int type, const char* name, void* data, uintptr_t cur_ptr); +int parse_reserved_memory(uintptr_t mem_rsvmap_addr); +void parse_rvmem_child(int type, const char* name, void* data, uintptr_t cur_ptr); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/dynamic_alloc.c b/Lab4/kernel/dynamic_alloc.c new file mode 100644 index 000000000..050a99e2b --- /dev/null +++ b/Lab4/kernel/dynamic_alloc.c @@ -0,0 +1,242 @@ +#include "dynamic_alloc.h" +#include "alloc.h" +#include "mini_uart.h" + +slab_cache_t* slab_cache; + +// Total frames from 0x0 to 0x3C000000 -> 245697 frames +void init_dynamic_alloc(void) { + init_buddy_system(); + + // The cache contains lists of slabs of size 64B, 128B, 256B. + slab_cache = (slab_cache_t *)malloc(sizeof(slab_cache_t) * 3); + for (int i = 0; i < 3; i++) { + slab_cache[i].head = NULL; + slab_cache[i].tail = NULL; + } +} + +uint64_t dynamic_alloc(uint64_t request) { + // Request larger than 256 bytes, allocate all frames. + if (request > 256) { + // Get the index of alloc_frame_list. + int alloc_ind = alloc_page(request); + + uart_send_string("Allocated frames "); + for (int i = alloc_frame_list[alloc_ind]->head->ind; i <= alloc_frame_list[alloc_ind]->tail->ind; i++) { + // For contiguous allocated frames, the slot_size records the offset between the current frame + // and the head. + frame_arr[i]->slot_size = i - alloc_frame_list[alloc_ind]->head->ind; + frame_arr[i]->slot_cnt = 0; + uart_send_int(i); + uart_send_string(" "); + } + uart_send_string("\r\n"); + + return alloc_frame_list[alloc_ind]->head->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR; + } else { + // For determining which slot the request belong to. + int slab_cache_ind; + int slot_cnt; + int slot_size; + if (request > 128) { + slab_cache_ind = 2; + slot_cnt = 16; + slot_size = 256; + } else if (request > 64) { + slab_cache_ind = 1; + slot_cnt = 32; + slot_size = 128; + } else { + slab_cache_ind = 0; + slot_cnt = 64; + slot_size = 64; + } + + // Allocated slabs exist. + if (slab_cache[slab_cache_ind].head != NULL) { + entry_t* cur_entry = slab_cache[slab_cache_ind].head; + + // Find a slab with empty slots. + while (cur_entry != NULL && cur_entry->slot_cnt <= 0) + cur_entry = cur_entry->next_slot; + + // Find empty slot. + if (cur_entry != NULL) { + short free_slot_ind = 0; + + for (; free_slot_ind < slot_cnt; free_slot_ind++) { + if (cur_entry->bitmap & (1 << free_slot_ind)) { + // Mark the slot as used. + cur_entry->bitmap &= ~(1 << free_slot_ind); + cur_entry->slot_cnt--; + break; + } + } + + uart_send_string("Allocated slot "); + uart_send_int(free_slot_ind); + uart_send_string(" in frame "); + uart_send_int(cur_entry->ind); + uart_send_string("\r\n"); + + return cur_entry->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR + free_slot_ind * slot_size; + } else { + // Allocate a new page if no slot remains. + int alloc_ind = alloc_page(request); + cur_entry = alloc_frame_list[alloc_ind]->head; + + if (slot_size == 256) { + cur_entry->bitmap = 0xFFFF; + cur_entry->slot_cnt = 16; + cur_entry->slot_size = SLOT_256B; + } else if (slot_size == 128) { + cur_entry->bitmap = 0xFFFFFFFF; + cur_entry->slot_cnt = 32; + cur_entry->slot_size = SLOT_128B; + } else { + cur_entry->bitmap = 0xFFFFFFFFFFFFFFFF; + cur_entry->slot_cnt = 64; + cur_entry->slot_size = SLOT_64B; + } + + cur_entry->bitmap &= ~(0x1); + cur_entry->slot_cnt--; + + // Insert into slab cache. + slab_cache[slab_cache_ind].tail->next = cur_entry; + cur_entry->prev_slot = slab_cache[slab_cache_ind].tail; + cur_entry->next_slot = NULL; + slab_cache[slab_cache_ind].tail = cur_entry; + + uart_send_string("Allocated slot "); + uart_send_int(0); + uart_send_string(" in frame "); + uart_send_int(cur_entry->ind); + uart_send_string("\r\n"); + + return cur_entry->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR; + } + // No allocated page exists in the cache. + } else { + int alloc_ind = alloc_page(request); + entry_t* cur_entry = alloc_frame_list[alloc_ind]->head; + cur_entry->prev_slot = cur_entry; + cur_entry->next_slot = NULL; + + slab_cache[slab_cache_ind].head = cur_entry; + slab_cache[slab_cache_ind].tail = cur_entry; + + if (slot_size == 256) { + cur_entry->bitmap = 0xFFFF; + cur_entry->slot_cnt = 16; + cur_entry->slot_size = SLOT_256B; + } else if (slot_size == 128) { + cur_entry->bitmap = 0xFFFFFFFF; + cur_entry->slot_cnt = 32; + cur_entry->slot_size = SLOT_128B; + } else { + cur_entry->bitmap = 0xFFFFFFFFFFFFFFFF; + cur_entry->slot_cnt = 64; + cur_entry->slot_size = SLOT_64B; + } + + cur_entry->bitmap &= ~(0x1); + cur_entry->slot_cnt--; + + uart_send_string("Allocated slot "); + uart_send_int(0); + uart_send_string(" in frame "); + uart_send_int(cur_entry->ind); + uart_send_string("\r\n"); + + return cur_entry->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR; + } + } +} + +void dynamic_free(uint64_t addr) { + // Find the frame it belongs to. + int frame_ind = (addr - FREE_MEM_BASE_ADDR) / FRAME_SIZE; + + if (frame_arr[frame_ind]->slot_size == SLOT_64B || frame_arr[frame_ind]->slot_size == SLOT_128B || frame_arr[frame_ind]->slot_size == SLOT_256B) { + int slot_size; + int slab_cache_ind; + + if (frame_arr[frame_ind]->slot_size == SLOT_64B) { + slab_cache_ind = 0; + slot_size = 64; + } else if (frame_arr[frame_ind]->slot_size == SLOT_128B) { + slab_cache_ind = 1; + slot_size = 128; + } else if (frame_arr[frame_ind]->slot_size == SLOT_256B) { + slab_cache_ind = 2; + slot_size = 256; + } + + int slot_ind = (addr - (frame_ind * FRAME_SIZE + FREE_MEM_BASE_ADDR)) / slot_size; + // The slot is free already. + if (((1 << slot_ind) & frame_arr[frame_ind]->bitmap) == (1 << slot_ind)) { + uart_send_string("Slot "); + uart_send_int(slot_ind); + uart_send_string(" in frame "); + uart_send_int(frame_ind); + uart_send_string(" is freed already!\r\n"); + return; + } + + frame_arr[frame_ind]->bitmap |= (1 << slot_ind); + frame_arr[frame_ind]->slot_cnt++; + + uart_send_string("Freeing slot "); + uart_send_int(slot_ind); + uart_send_string(" in frame "); + uart_send_int(frame_ind); + uart_send_string("\r\n"); + uart_send_string("Remaining slots: "); + uart_send_int(frame_arr[frame_ind]->slot_cnt); + uart_send_string("\r\n"); + + // Free the page if all allocated slots are freed. + if (frame_arr[frame_ind]->slot_cnt == slot_size) { + frame_arr[frame_ind]->slot_size = SLOT_NOT_ALLOCATED; + free_page(frame_ind); + + // Discard the frame in slab cache. + entry_t* cur_entry = slab_cache[slab_cache_ind].head; + + while (cur_entry != NULL && cur_entry->ind != frame_ind) { + cur_entry = cur_entry->next_slot; + } + + // If current frame is at the head of cache. + if (cur_entry->prev_slot == cur_entry) { + slab_cache[slab_cache_ind].head = cur_entry->next_slot; + if (slab_cache[slab_cache_ind].head == NULL) { + slab_cache[slab_cache_ind].tail = NULL; + } else { + slab_cache[slab_cache_ind].head->prev_slot = slab_cache[slab_cache_ind].head; + } + // At the end or middle of cache. + } else { + if (cur_entry->next_slot == NULL) { + slab_cache[slab_cache_ind].tail = cur_entry->prev_slot; + slab_cache[slab_cache_ind].tail->next = NULL; + } else { + cur_entry->prev_slot->next_slot = cur_entry->next_slot; + cur_entry->next_slot->prev_slot = cur_entry->prev_slot; + } + } + cur_entry->next_slot = NULL; + cur_entry->prev_slot = NULL; + } + } else if (frame_arr[frame_ind]->slot_size == SLOT_NOT_ALLOCATED) { + uart_send_string("Memory 0x"); + uart_send_int(addr); + uart_send_string(" not allocated!\r\n"); + // Contiguous frames to free. + } else { + int head = frame_ind - frame_arr[frame_ind]->slot_size; + free_page(head); + } +} \ No newline at end of file diff --git a/Lab4/kernel/dynamic_alloc.h b/Lab4/kernel/dynamic_alloc.h new file mode 100644 index 000000000..78024cf21 --- /dev/null +++ b/Lab4/kernel/dynamic_alloc.h @@ -0,0 +1,19 @@ +#ifndef _DYNAMIC_ALLOC_H_ +#define _DYNAMIC_ALLOC_H_ + +#include "buddy_system_2.h" + +#define USED_SLOT 'U' +#define FREE_SLOT 'F' + +typedef struct _slab_cache { + entry_t* head; + entry_t* tail; +} slab_cache_t; + +void startup_allocator(void); +void init_dynamic_alloc(void); +uint64_t dynamic_alloc(uint64_t request); +void dynamic_free(uint64_t addr); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/entry.S b/Lab4/kernel/entry.S new file mode 100644 index 000000000..eb61a7cb6 --- /dev/null +++ b/Lab4/kernel/entry.S @@ -0,0 +1,182 @@ +#include "entry.h" + +.macro handle_invalid_entry type +mov x0, #\type +mrs x1, esr_el1 +mrs x2, elr_el1 +bl show_invalid_entry_message +b err_hang +.endm + +/* + * Vector table entries. + * All exception vectors should be located at offset 0x80 bytes one from another. + */ +.macro ventry label +.align 7 +b \label +.endm + +/* + * Save current register state before exception handling. + */ +.macro save_state + // 31 GPR + spsr_el1 + elr_el1, each register is 8 bytes(64-bit) + sub sp, sp, 34 * 8 + // stp(Store Pair of registers) + stp x0, x1, [sp, 16 * 0] + stp x2, x3, [sp, 16 * 1] + stp x4, x5, [sp, 16 * 2] + stp x6, x7, [sp, 16 * 3] + stp x8, x9, [sp, 16 * 4] + stp x10, x11, [sp, 16 * 5] + stp x12, x13, [sp, 16 * 6] + stp x14, x15, [sp, 16 * 7] + stp x16, x17, [sp, 16 * 8] + stp x18, x19, [sp, 16 * 9] + stp x20, x21, [sp, 16 * 10] + stp x22, x23, [sp, 16 * 11] + stp x24, x25, [sp, 16 * 12] + stp x26, x27, [sp, 16 * 13] + stp x28, x29, [sp, 16 * 14] + mrs x0, spsr_el1 + stp x30, x0, [sp, 16 * 15] + mrs x0, elr_el1 + str x0, [sp, 16 * 16] + ldp x0, x1, [sp, 16 * 0] +.endm + +/* + * Load current register state after exception handling. + */ +.macro load_state + ldp x0, x1, [sp, 16 * 0] + ldp x2, x3, [sp, 16 * 1] + ldp x4, x5, [sp, 16 * 2] + ldp x6, x7, [sp, 16 * 3] + ldp x8, x9, [sp, 16 * 4] + ldp x10, x11, [sp, 16 * 5] + ldp x12, x13, [sp, 16 * 6] + ldp x14, x15, [sp, 16 * 7] + ldp x16, x17, [sp, 16 * 8] + ldp x18, x19, [sp, 16 * 9] + ldp x20, x21, [sp, 16 * 10] + ldp x22, x23, [sp, 16 * 11] + ldp x24, x25, [sp, 16 * 12] + ldp x26, x27, [sp, 16 * 13] + ldp x28, x29, [sp, 16 * 14] + ldp x30, x0, [sp, 16 * 15] + msr spsr_el1, x0 + ldr x0, [sp, 16 * 16] + msr elr_el1, x0 + ldp x0, x1, [sp, 16 * 0] + add sp, sp, 34 * 8 +.endm + +/* + * Setup interrupt vector table. + * Vector table should be aligned to 0x800. + */ + .align 11 + .global vectors + vectors: + /* + * EL1t Exception is taken from EL1 while stack pointer was shared with EL0. + * This happens when SPSel register holds the value 0. In our lab, we won't be using this + * exception. Including all 16 handlers for easier debugging if anything goes wrong. + */ + ventry sync_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + /* + * EL1h Exception is taken from EL1 at the time when dedicated stack pointer was allocated + * for EL1. This means that SPSel holds the value 1 and this is the mode that we are currently using. + */ + ventry sync_el1h // Synchronous EL1h + ventry irq_el1h // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_el1h // Error EL1h + + /* + * EL0_64 Exception is taken from EL0 executing in 64-bit mode. + */ + ventry sync_el0_64 // Synchronous 64-bit EL0 + ventry irq_el0_64 // IRQ 64-bit EL0 + ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + /* + * EL0_32 Exception is taken from EL0 executing in 32-bit mode.(Won't be using it) + */ + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + +sync_invalid_el1t: + handle_invalid_entry SYNC_INVALID_EL1t + +irq_invalid_el1t: + handle_invalid_entry IRQ_INVALID_EL1t + +fiq_invalid_el1t: + handle_invalid_entry FIQ_INVALID_EL1t + +error_invalid_el1t: + handle_invalid_entry ERROR_INVALID_EL1t + +fiq_invalid_el1h: + handle_invalid_entry FIQ_INVALID_EL1h + +error_invalid_el1h: + handle_invalid_entry ERROR_INVALID_EL1h + +fiq_invalid_el0_64: + handle_invalid_entry FIQ_INVALID_EL0_64 + +error_invalid_el0_64: + handle_invalid_entry ERROR_INVALID_EL0_64 + +sync_invalid_el0_32: + handle_invalid_entry SYNC_INVALID_EL0_32 + +irq_invalid_el0_32: + handle_invalid_entry IRQ_INVALID_EL0_32 + +fiq_invalid_el0_32: + handle_invalid_entry FIQ_INVALID_EL0_32 + +error_invalid_el0_32: + handle_invalid_entry ERROR_INVALID_EL0_32 + +// Jump to synchronous exception handler. +sync_el0_64: + save_state + bl handle_sync_el0_64 + load_state + eret + +// Jump to interrupt exception handler. +irq_el0_64: + save_state + bl handle_irq_el0_64 + load_state + eret + +sync_el1h: + save_state + bl handle_sync_el1h + load_state + eret + +irq_el1h: + save_state + mov x0, sp + bl handle_irq_el1h + load_state + eret + +.global err_hang +err_hang: b err_hang \ No newline at end of file diff --git a/Lab4/kernel/entry.h b/Lab4/kernel/entry.h new file mode 100644 index 000000000..8c9ce5d56 --- /dev/null +++ b/Lab4/kernel/entry.h @@ -0,0 +1,24 @@ +#ifndef _ENTRY_H_ +#define _ENTRY_H_ + +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +#endif \ No newline at end of file diff --git a/Lab4/kernel/exception.c b/Lab4/kernel/exception.c new file mode 100644 index 000000000..b96e1d0f5 --- /dev/null +++ b/Lab4/kernel/exception.c @@ -0,0 +1,213 @@ +#include "../peripherals/mini_uart.h" +#include "timer.h" +#include "irq.h" +#include "task.h" +#include "alloc.h" + +const char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; + +void show_invalid_entry_message(int type, unsigned long esr, unsigned long address) { + uart_send_string((char *)entry_error_messages[type]); + uart_send_string(", ESR: "); + uart_send_uint(esr); + uart_send_string(", address: "); + uart_send_uint(address); + uart_send_string("\r\n"); +} + +void handle_sync_el0_64(void) { + unsigned long spsr_el1, elr_el1, esr_el1; + + asm volatile ( + "mrs %0, spsr_el1;" + "mrs %1, elr_el1;" + "mrs %2, esr_el1;" + + // Output operand. + : "=r" (spsr_el1), "=r" (elr_el1), "=r" (esr_el1) + : + // Clobber list. + : "x0", "x1", "x2" + ); + + uart_send_string("In handler: handle_sync_el0_64\r\n"); + uart_send_string("spsr_el1: "); + uart_send_uint(spsr_el1); + uart_send_string("\r\n"); + uart_send_string("elr_el1: "); + uart_send_uint(elr_el1); + uart_send_string("\r\n"); + + /* + * For assembly user code, it showed 0x56000000. + * [31:26] 0b010101 -> SVC instruction execution in AArch64 state. + */ + + uart_send_string("esr_el1: "); + uart_send_uint(esr_el1); + uart_send_string("\r\n"); + + //set_timer_expire(3); +} + +void handle_irq_el0_64(void) { + unsigned long spsr_el1, elr_el1, esr_el1, sp_el0; + + asm volatile ( + "mrs %0, spsr_el1;" + "mrs %1, elr_el1;" + "mrs %2, esr_el1;" + "mrs %3, sp_el0;" + + // Output operand. + : "=r" (spsr_el1), "=r" (elr_el1), "=r" (esr_el1), "=r" (sp_el0) + : + // Clobber list. + : "x0", "x1", "x2" + ); + + uart_send_string("In handler: handle_irq_el0_64\r\n"); + uart_send_string("spsr_el1: "); + uart_send_uint(spsr_el1); + uart_send_string("\r\n"); + uart_send_string("elr_el1: "); + uart_send_uint(elr_el1); + uart_send_string("\r\n"); + uart_send_string("sp_el0: "); + uart_send_uint(sp_el0); + uart_send_string("\r\n"); + /* + * For assembly user code, it showed 0x56000000. + * [31:26] 0b010101 -> SVC instruction execution in AArch64 state. + */ + + uart_send_string("esr_el1: "); + uart_send_uint(esr_el1); + uart_send_string("\r\n"); + + // For checking whether timer caused the interrupt. + unsigned long cntp_ctl; + static int cnt = 0; + asm volatile("mrs %0, cntp_ctl_el0" : "=r" (cntp_ctl)); + + // Timer interrupt is pending + if (cntp_ctl & (1 << 2)) { + uart_send_string("Timer interrupt detected "); + uart_send_uint(cnt++); + uart_send_string(".\r\n"); + + // Reset the timer. + set_timer_expire(2); + + + } +} + +void handle_irq_el1h(uint64_t sp) { + disable_el1_interrupt(); + + uart_send_string("\r\nIn handler: handle_irq_el1h\r\n"); + uart_send_string("SP: "); + uart_send_uint(sp); + uart_send_string("\r\n"); + + task* t = (task *)simple_malloc(sizeof(task)); + t->sp = sp; + // On manual P.9 + // If AUX_IRQ[0] is set to 1, mini UART has an interrupt pending. + if (*AUX_IRQ & 0x1) { + uart_send_string("Uart interrupt\r\n"); + + t->intr_func = handle_uart_interrupt; + t->arg = NULL; + t->priority = 1; + enqueue_task(t); + enable_el1_interrupt(); + + // handle_uart_interrupt(); + } + + // Check whether timer expires. + int timer_intr; + asm volatile ( + "mrs %0, cntp_ctl_el0;" + + : "=r" (timer_intr) + : + : + ); + + // cntp_ctl_el0[2] = 1 -> timer condition met. This bit is read-only, cannot be modified. + // The bit will be cleared when a new expired time is set. + if (timer_intr & 0x4) { + uart_send_string("Timer interrupt\r\n"); + clear_timer_intr(); + // t->intr_func = handle_timer_intr; + // t->arg = NULL; + // t->priority = 0; + + // enqueue_task(t); + // enable_el1_interrupt(); + // execute_task(t); + + handle_timer_intr(NULL); + } + + + +} + +void handle_sync_el1h(void) { + + //disable_el1_interrupt(); + // uart_send_string("In handler: handle_sync_el1h\r\n"); + unsigned long spsr_el1, elr_el1, esr_el1, sp_el0; + + asm volatile ( + "mrs %0, spsr_el1;" + "mrs %1, elr_el1;" + "mrs %2, esr_el1;" + "mrs %3, sp_el0;" + + // Output operand. + : "=r" (spsr_el1), "=r" (elr_el1), "=r" (esr_el1), "=r" (sp_el0) + : + // Clobber list. + : "x0", "x1", "x2" + ); + + // uart_send_string("spsr_el1: "); + // uart_send_uint(spsr_el1); + // uart_send_string("\r\n"); + // uart_send_string("elr_el1: "); + // uart_send_uint(elr_el1); + // uart_send_string("\r\n"); + // uart_send_string("esr_el1: "); + // uart_send_uint(esr_el1); + // uart_send_string("\r\n"); + // uart_send_string("sp_el0: "); + // uart_send_uint(sp_el0); + // uart_send_string("\r\n"); + //enable_el1_interrupt(); + +} \ No newline at end of file diff --git a/Lab4/kernel/exception.h b/Lab4/kernel/exception.h new file mode 100644 index 000000000..bdd3d4ea0 --- /dev/null +++ b/Lab4/kernel/exception.h @@ -0,0 +1,6 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + + + +#endif \ No newline at end of file diff --git a/Lab4/kernel/initramdisk.c b/Lab4/kernel/initramdisk.c new file mode 100644 index 000000000..31416cdcf --- /dev/null +++ b/Lab4/kernel/initramdisk.c @@ -0,0 +1,312 @@ +#include "initramdisk.h" +#include "../peripherals/mini_uart.h" +#include "../peripherals/utils.h" +#include + +struct Directory* rootDir; +// Save the current user location. +struct Directory* userDir; + +char* cpio; + +// Read the cpio file and construct the simple filesystem. +void initramdisk_main(uintptr_t cpio_addr) { + // Retrieve the cpio address from parsing the device tree blob. + cpio = (char *)cpio_addr; + uart_send_string("cpio start address: "); + uart_send_uint((uintptr_t)cpio); + uart_send_string("\r\n"); + + // Parse info for root directory. + rootDir = allocateDirectory(); + rootDir->dHeader = parse_cpio_header(); + rootDir->nDirs = 0; + rootDir->nFiles = 0; + rootDir->emptyDirInd = 0; + rootDir->emptyFileInd = 0; + // Root directory has no parent. + rootDir->parent = NULL; + userDir = rootDir; + + for (int i = 0; i < rootDir->dHeader->nameSize; i++, cpio++) { + rootDir->dName[i] = *cpio; + } + + // uart_send_string("Print root directory: "); + // uart_send_string(rootDir->dName); + // uart_send_string("\r\n"); + + // Align to 4-byte boundary. + if (((unsigned long)cpio & 3)) { + unsigned long pad = 4 - ((unsigned long)cpio & 3); + cpio += pad; + } + + // Common pattern for directories is '4' at the beginning of the 'mode' value in octal + // representation(040xxx) + // if (rootDir.dHeader.mode & 040000) + // uart_send_string("Directory!\r\n"); + + // Parse the entire cpio. + while (1) { + struct Directory* curDir = rootDir; + struct CpioHeader* header = parse_cpio_header(); + + char end[15]; + for (int i = 0; i < 11; i++) { + end[i] = *(cpio + i); + } + + if (strcmp(end, "TRAILER!!!") == 0) { + // uart_send_string("End of cpio!\r\n"); + break; + } + + // New directory path. + char path[64]; + // Records number of nested directories storing the file or directory. + int nested_dir_num = 0; + + // The null character is included in the nameSize. + for (int i = 0; i < header->nameSize; i++, cpio++) { + path[i] = *cpio; + if (path[i] == '/') + nested_dir_num++; + } + + // Align to 4-byte boundary. + if (((unsigned long)cpio & 3)) { + unsigned long pad = 4 - ((unsigned long)cpio & 3); + cpio += pad; + } + + // Navigate to the directory the new directory or new file is located. + int pathInd = 0; + + // Enter if not located in root directory. + while (nested_dir_num > 0) { + char nextDir[64]; + + int i = 0; + for (; path[pathInd] != '/'; i++, pathInd++) { + nextDir[i] = path[pathInd]; + } + nextDir[i] = '\0'; + pathInd++; + + for (int i = 0; i < curDir->nDirs; i++) { + // Check which directory to navigate to. + if (strcmp(nextDir, curDir->dirs[i]->dName) == 0) { + curDir = curDir->dirs[i]; + nested_dir_num--; + break; + } + } + } + + char name[64]; + // Read the new directory(file) name. + for (int i = 0; pathInd < header->nameSize; i++, pathInd++) { + name[i] = path[pathInd]; + } + + // New directory. + if (header->mode & 040000) { + // Initialize new directory. + struct Directory* newDir = allocateDirectory(); + newDir->dHeader = header; + newDir->emptyDirInd = 0; + newDir->emptyFileInd = 0; + newDir->nDirs = 0; + newDir->nFiles = 0; + newDir->parent = curDir; + strcpy(newDir->dName, name); + + // Link the new directory with its parent directory. + curDir->nDirs++; + curDir->dirs[curDir->emptyDirInd++] = newDir; + + // New file. + } else if (header->mode & 0100000) { + // Initialize new file. + struct File* newFile = allocateFile(); + newFile->fileSize = header->fileSize; + newFile->fHeader = header; + newFile->parent = curDir; + strcpy(newFile->fName, name); + + for (int i = 0; i < header->fileSize; i++, cpio++) { + newFile->fData[i] = *cpio; + } + + // Link the new file with its parent directory. + curDir->nFiles++; + curDir->files[curDir->emptyFileInd++] = newFile; + + // Align to 4-byte boundary. + if (((unsigned long)cpio & 3)) { + unsigned long pad = 4 - ((unsigned long)cpio & 3); + cpio += pad; + } + } + } +} + +struct CpioHeader* parse_cpio_header() { + // uart_send_string("In parse cpio header\r\n"); + struct CpioHeader* header = allocateHeader(); + char tmp[10]; + // Record the length of tmp. + int len = 8; + + // Read magic number from cpio. + for (int i = 0; i < 6; i++) { + tmp[i] = *cpio++; + } + tmp[6] = '\0'; + + if (strcmp(tmp, "070701") != 0) { + uart_send_string("Cpio not in new Ascii format!\r\n"); + } + + // Read inode number from cpio. + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->inode = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->mode = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->uid = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->guid = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->nlink = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->mtime = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->fileSize = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->devMaj = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->devMin = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->rdevMaj = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->rdevMin = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->nameSize = strHex2Int(tmp, len); + // uart_send_uint(header->nameSize); + // uart_send_string("\r\n"); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->check = strHex2Int(tmp, len); + + // uart_send_string("Exit\r\n"); + return header; +} + +struct File* allocateFile() { + if (filePoolIndex < MAX_FILES) + return &filePool[filePoolIndex++]; + else { + uart_send_string("Maximum file count reached! Failed to create new file!\r\n"); + return NULL; + } +} + +struct Directory* allocateDirectory() { + if (directoryPoolIndex < MAX_DIRECTORIES) + return &directoryPool[directoryPoolIndex++]; + else { + uart_send_string("Maximum directory count reached! Failed to create new directory!\r\n"); + return NULL; + } +} + +struct CpioHeader* allocateHeader() { + if (headerPoolIndex < MAX_HEADERS) + return &headerPool[headerPoolIndex++]; + else { + uart_send_string("Maximum header count reached! Failed to create new header!\r\n"); + return NULL; + } +} + + +void show_current_dir_content() { + for (int i = 0; i < userDir->nDirs; i++) { + uart_send_string(userDir->dirs[i]->dName); + uart_send_string("\r\n"); + } + + for (int i = 0; i < userDir->nFiles; i++) { + uart_send_string(userDir->files[i]->fName); + uart_send_string("\r\n"); + } +} + +void change_directory(char* path) { + for (int i = 0; i < userDir->nDirs; i++) { + if (strcmp(path, userDir->dirs[i]->dName) == 0) { + userDir = userDir->dirs[i]; + return; + } + } + + uart_send_string("No such directory!\r\n"); +} + +void print_content(char* fileName) { + for (int i = 0; i < userDir->emptyFileInd; i++) { + if (strcmp(fileName, userDir->files[i]->fName) == 0) { + for (unsigned int j = 0; j < userDir->files[i]->fileSize; j++) { + if (userDir->files[i]->fData[j] == '\n') { + uart_send_string("\r\n"); + } else { + uart_send(userDir->files[i]->fData[j]); + } + } + uart_send_string("\r\n"); + return; + } + } + + uart_send_string("File doesn't exist!\r\n"); +} + diff --git a/Lab4/kernel/initramdisk.h b/Lab4/kernel/initramdisk.h new file mode 100644 index 000000000..a36cda092 --- /dev/null +++ b/Lab4/kernel/initramdisk.h @@ -0,0 +1,87 @@ +#ifndef _INITRAMDISK_H_ +#define _INITRAMDISK_H_ + +#define MAX_HEADERS 10 +#define MAX_DIRECTORIES 10 +#define MAX_FILES 10 +#define MAX_NAME_LENGTH 64 +#define MAX_FILE_SIZE 512 + +struct CpioHeader { + unsigned int inode; // inode number + unsigned int mode; // File mode + unsigned int uid; // User ID + unsigned int guid; // Group ID + unsigned int nlink; // Number of links + unsigned int mtime; // Modification time + unsigned int fileSize; // Size of file + unsigned int devMaj; // Major number of device + unsigned int devMin; // Minor number of device + unsigned int rdevMaj; // Major number of a character or block device file + unsigned int rdevMin; // Minor number of a character or block device file + unsigned int nameSize; // Length of name including the trailing NUL + unsigned int check; // Checksum +}; + +struct File { + struct CpioHeader* fHeader; + + // Maximum size for a file name is 64 bytes. + char fName[64]; + + // Maximum Data for a file name is 512 bytes. + char fData[MAX_FILE_SIZE]; + + unsigned int fileSize; + + // Stores the parent directory. + struct Directory* parent; +}; + +struct Directory { + struct CpioHeader* dHeader; + + // Maximum size for a directory name is 64 bytes. + char dName[64]; + + // Keep track of the number of files inside that directory. + int nFiles; + // Keep track of the number of directories inside that directory. + int nDirs; + // Keep track of current empty directory index. + int emptyDirInd; + // Keep track of current empty file index. + int emptyFileInd; + + // Stores the parent directory. + struct Directory* parent; + // Define the maximum number of directories and files stored within a directory. + struct Directory* dirs[MAX_DIRECTORIES]; + struct File* files[10]; +}; + +// Static memory pool for headers. +static struct CpioHeader headerPool[MAX_HEADERS]; +static int headerPoolIndex = 0; + +// Static memory pool for directories. +static struct Directory directoryPool[MAX_DIRECTORIES]; +static int directoryPoolIndex = 0; + +// Static memory pool for files. +static struct File filePool[MAX_FILES]; +static int filePoolIndex = 0; + +extern struct Directory* rootDir; +extern struct Directory* userDir; + +void initramdisk_main(); +struct CpioHeader* parse_cpio_header(); +struct Directory* allocateDirectory(); +struct File* allocateFile(); +struct CpioHeader* allocateHeader(); +void show_current_dir_content(); +void change_directory(char* path); +void print_content(char* arg); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/irq.c b/Lab4/kernel/irq.c new file mode 100644 index 000000000..9a7211f49 --- /dev/null +++ b/Lab4/kernel/irq.c @@ -0,0 +1,21 @@ +#include "irq.h" + +void irq_vector_init(void) { + asm volatile ( + "adr x0, vectors;" + "msr vbar_el1, x0;" + "ret;" + ); +} + +void enable_el1_interrupt(void) { + asm volatile( + "msr DAIFClr, 0xf;" + ); +} + +void disable_el1_interrupt(void) { + asm volatile( + "msr DAIFSet, 0xf;" + ); +} \ No newline at end of file diff --git a/Lab4/kernel/irq.h b/Lab4/kernel/irq.h new file mode 100644 index 000000000..88e9ea2f9 --- /dev/null +++ b/Lab4/kernel/irq.h @@ -0,0 +1,13 @@ +#ifndef _IRQ_H_ +#define _IRQ_H_ + +#include + +void irq_vector_init(void); +void enable_el1_interrupt(void); +void disable_el1_interrupt(void); +void handle_sync_el0_64(void); +void irq_el0_64(void); +void handle_sync_el1h(void); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/irq_asm.S b/Lab4/kernel/irq_asm.S new file mode 100644 index 000000000..1108f10d2 --- /dev/null +++ b/Lab4/kernel/irq_asm.S @@ -0,0 +1,6 @@ +// load VBAR_EL1 with virtual vector table address +//.global irq_vector_init +//irq_vector_init: +// adr x0, vectors +// msr vbar_el1, x0 +// ret diff --git a/Lab4/kernel/kernel.c b/Lab4/kernel/kernel.c new file mode 100644 index 000000000..dd42a0b93 --- /dev/null +++ b/Lab4/kernel/kernel.c @@ -0,0 +1,73 @@ +#include "../peripherals/mini_uart.h" +#include "shell.h" +#include "initramdisk.h" +#include "device_tree.h" +#include "irq.h" +#include "timer.h" +#include "task.h" +#include "dynamic_alloc.h" +#include "buddy_system_2.h" + + +/* + * Use 0x1000_0000 ~ 0x2000_0000 for page frame allocator. + */ +void kernel_main(uintptr_t dtb_address) +{ + /* + asm volatile ( + // CurrentEL register(64-bit) stores the current exception level. + // bits[3:2] + // 0b00 -> EL0 + // 0b01 -> EL1 + // 0b10 -> EL2 + // 0b11 -> EL3 + "mrs x0, CurrentEL;" + ); + */ +/* + // Test asynchronous UART. + irq_vector_init(); + enable_el1_interrupt(); + enable_uart_interrupt(); + + // Infinite loop for testing asynchronous UART + while(1) {} +*/ + + + enable_el1_interrupt(); + + uart_send_string("Time after booting: "); + uart_send_uint(get_current_time()); + uart_send_string(" seconds\r\n"); + + irq_vector_init(); + core_timer_enable(); + init_heap(); + init_heap2(); + // init_frame_freelist(); + init_task_queue(); + // init_buddy_system(); + init_dynamic_alloc(); + //init_memory_pool(); + fdt_traverse(get_initrd_address, dtb_address); + if (parse_reserved_memory(dtb_address + myHeader.off_mem_rsvmap) != 0) { + uart_send_string("Error parsing memory reservation block!\r\n"); + } + // fdt_traverse(parse_rvmem_child, dtb_address); + + // Check preemption. + // add_timer(create_loop, NULL, 2); + // add_timer(exit_loop, NULL, 5); + + // add_timer(delay_loop, NULL, 2); + // char* message = (char *)simple_malloc(sizeof(char) * 10); + // strcpy(message, "Timeout\r\n"); + // add_timer(print_timeout_message, (char *)message, 4); + + //while(1); + //fdt_traverse(print_tree, dtb_address); + shell(); + +} diff --git a/Lab4/kernel/linker.ld b/Lab4/kernel/linker.ld new file mode 100644 index 000000000..7efac520d --- /dev/null +++ b/Lab4/kernel/linker.ld @@ -0,0 +1,26 @@ +# Define 32MB for heap. +HEAP_SIZE = 0x2000000; + +STACK_PTR = 0x2295B88; + +SECTIONS +{ + . = 0x80000; + .text.boot : { *(.text.boot) } + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + # Use NOLOAD in .bss ensures that this section is allocated space in the memory layout without + # attempting to load any pre-initialized data from the binary. + .bss (NOLOAD) : { + . = ALIGN(0x8); + bss_begin = .; + *(.bss*) + bss_end = .; + } + + . = ALIGN(0x8); + __heap_start = .; + . = . + HEAP_SIZE; + __heap_end = .; +} \ No newline at end of file diff --git a/Lab4/kernel/memory_alloc.c b/Lab4/kernel/memory_alloc.c new file mode 100644 index 000000000..4a4265f1e --- /dev/null +++ b/Lab4/kernel/memory_alloc.c @@ -0,0 +1,325 @@ +#include "memory_alloc.h" +#include "alloc.h" +#include "mini_uart.h" + +// mpool[i] stores the corresponding information for frame[i]. +mem_pool* mpool; + +void init_memory_pool(void) { + + mpool = (mem_pool *)malloc(sizeof(mem_pool) * (free_mem_size / BLOCK_SIZE)); + + // for (uint64_t i = 0; i < 10; i++) { + // mpool[i].allocate_all = 0; + // mpool[i]._32B_available = 8; + // mpool[i]._64B_available = 4; + // mpool[i]._128B_available = 4; + // mpool[i]._256B_available = 4; + // mpool[i]._512B_available = 2; + // mpool[i]._1024B_available = 1; + + // for (int j = 0; j < 8; j++) { + // mpool[i]._32B[j].free = 1; + + // if (j < 1) { + // mpool[i]._1024B[j].free = 1; + // } + + // if (j < 2) { + // mpool[i]._512B[j].free = 1; + // } + + // // 64, 128, 256 bytes only have 4 slots. + // if (j < 4) { + // mpool[i]._64B[j].free = 1; + // mpool[i]._128B[j].free = 1; + // mpool[i]._256B[j].free = 1; + // } + // } + // } +} + + +uint64_t dynamic_malloc(uint64_t request_size) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Dynamic Allocator\r\n"); + uart_send_string("---------------------------------------\r\n"); + + + // If the request size is larger than 1KB, allocate entire frame(or several frames) for the request. + if (request_size > 1024) { + allocate_info* info = allocate_frame(request_size); + + if (info == NULL) { + uart_send_string("Request exceeded largest frame size!\r\n"); + return -1; + } + + uart_send_string("Allocated frames: "); + + for (int i = info->start_frame; i <= info->last_frame; i++) { + uart_send_int(i); + uart_send_string(" "); + mpool[i].allocate_all = 1; + } + + uart_send_string("for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return info->start_frame * 4096 + FREE_MEM_BASE_ADDR; + } else { + // Check allocated memory for remaining slots available. + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] != NULL) { + for (int j = alloc_mem_list[i]->start_frame; j <= alloc_mem_list[i]->last_frame; j++) { + // The entire frame(or several frames) is allocated for a single request. + if (mpool[j].allocate_all) { + break; + } + + if (request_size <= 32) { + // Current frame has 32 byte slot left. + if (mpool[j]._32B_available > 0) { + mpool[j]._32B_available--; + for (int s = 0; s < 8; s++) { + if (mpool[j]._32B[s].free) { + mpool[j]._32B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + 32 * s; + } + } + } else { + continue; + } + } else if (request_size > 32 && request_size <= 64) { + if (mpool[j]._64B_available > 0) { + mpool[j]._64B_available--; + for (int s = 0; s < 4; s++) { + if (mpool[j]._64B[s].free) { + mpool[j]._64B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 8); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * s); + } + } + } else { + continue; + } + } else if (request_size > 64 && request_size <= 128) { + if (mpool[j]._128B_available > 0) { + mpool[j]._128B_available--; + for (int s = 0; s < 4; s++) { + if (mpool[j]._128B[s].free) { + mpool[j]._128B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 12); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * s); + } + } + } else { + continue; + } + } else if (request_size > 128 && request_size <= 256) { + if (mpool[j]._256B_available > 0) { + mpool[j]._256B_available--; + for (int s = 0; s < 4; s++) { + if (mpool[j]._256B[s].free) { + mpool[j]._256B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 16); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * s); + } + } + } else { + continue; + } + } else if (request_size > 256 && request_size <= 512) { + if (mpool[j]._512B_available > 0) { + mpool[j]._512B_available--; + for (int s = 0; s < 2; s++) { + if (mpool[j]._512B[s].free) { + mpool[j]._512B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 20); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4) + (512 * s); + } + } + } else { + continue; + } + } else if (request_size > 512 && request_size <= 1024) { + if (mpool[j]._1024B_available > 0) { + mpool[j]._1024B_available--; + + if (mpool[j]._1024B[0].free) { + mpool[j]._1024B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(22); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4) + (512 * 2); + } + + } else { + continue; + } + } + } + } + } + + // No remaining slots available. Allocate new frame. + allocate_info* info = allocate_frame(request_size); + + // Initialize memory pool for new frame. + mpool[info->start_frame].allocate_all = 0; + mpool[info->start_frame]._32B_available = 8; + mpool[info->start_frame]._64B_available = 4; + mpool[info->start_frame]._128B_available = 4; + mpool[info->start_frame]._256B_available = 4; + mpool[info->start_frame]._512B_available = 2; + mpool[info->start_frame]._1024B_available = 1; + + for (int j = 0; j < 8; j++) { + mpool[info->start_frame]._32B[j].free = 1; + + if (j < 1) { + mpool[info->start_frame]._1024B[j].free = 1; + } + + if (j < 2) { + mpool[info->start_frame]._512B[j].free = 1; + } + + // 64, 128, 256 bytes only have 4 slots. + if (j < 4) { + mpool[info->start_frame]._64B[j].free = 1; + mpool[info->start_frame]._128B[j].free = 1; + mpool[info->start_frame]._256B[j].free = 1; + } + } + + if (request_size <= 32) { + mpool[info->start_frame]._32B_available--; + mpool[info->start_frame]._32B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(0); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return info->start_frame * 4096 + FREE_MEM_BASE_ADDR; + } else if (request_size > 32 && request_size <= 64) { + mpool[info->start_frame]._64B_available--; + mpool[info->start_frame]._64B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(8); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8); + } else if (request_size > 64 && request_size <= 128) { + mpool[info->start_frame]._128B_available--; + mpool[info->start_frame]._128B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(12); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4); + } else if (request_size > 128 && request_size <= 256) { + mpool[info->start_frame]._256B_available--; + mpool[info->start_frame]._256B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(16); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4); + } else if (request_size > 256 && request_size <= 512) { + mpool[info->start_frame]._512B_available--; + mpool[info->start_frame]._512B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(20); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4); + } else if (request_size > 512 && request_size <= 1024) { + mpool[info->start_frame]._1024B_available--; + mpool[info->start_frame]._1024B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(22); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4) + (512 * 2); + } + } +} \ No newline at end of file diff --git a/Lab4/kernel/memory_alloc.h b/Lab4/kernel/memory_alloc.h new file mode 100644 index 000000000..8b87c1c60 --- /dev/null +++ b/Lab4/kernel/memory_alloc.h @@ -0,0 +1,55 @@ +#ifndef _MEMORY_ALLOC_H_ +#define _MEMORY_ALLOC_H_ + +#include "buddy_system.h" + +// 0x10000000 ~ 0x1FFFFFFF +// #define FREE_MEM_BASE_ADDR 0x10000000 + +typedef struct _slot { + short free; +} slot; + +/* + * Stores the slots for each frame(4 KB). + * Each frame consists of: + * 32B -> 8 slots + * 64B -> 4 slots + * 128B -> 4 slots + * 256B -> 4 slots + * 512B -> 2 slots + * 1024B -> 1 slot + */ +typedef struct _mem_pool { + // Remaining available slots for each size. + short _32B_available; + short _64B_available; + short _128B_available; + short _256B_available; + short _512B_available; + short _1024B_available; + + // The entire block is allocated for one single request. + short allocate_all; + + /* + * slot index within the frame. Calculate the physical address of the slot with the index. + * 32B x 8 -> slot 0 ~ 7 + * 64B x 4 -> slot 8 ~ 11 + * 128B x 4 -> slot 12 ~ 15 + * 256B x 4 -> slot 16 ~ 19 + * 512B x 2 -> slot 20 ~ 21 + * 1024B x 1 -> slot 22 + */ + slot _32B[8]; + slot _64B[4]; + slot _128B[4]; + slot _256B[4]; + slot _512B[2]; + slot _1024B[1]; +} mem_pool; + +void init_memory_pool(void); +uint64_t dynamic_malloc(uint64_t request_size); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/reboot.c b/Lab4/kernel/reboot.c new file mode 100644 index 000000000..bb5d4eed5 --- /dev/null +++ b/Lab4/kernel/reboot.c @@ -0,0 +1,16 @@ +#include "reboot.h" + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab4/kernel/reboot.h b/Lab4/kernel/reboot.h new file mode 100644 index 000000000..590bb40d7 --- /dev/null +++ b/Lab4/kernel/reboot.h @@ -0,0 +1,12 @@ +#ifndef _REBOOT_H_ +#define _REBOOT_H_ + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/shell.c b/Lab4/kernel/shell.c new file mode 100644 index 000000000..f779e5f71 --- /dev/null +++ b/Lab4/kernel/shell.c @@ -0,0 +1,411 @@ +#include "../peripherals/mini_uart.h" +#include "shell.h" +#include "../peripherals/utils.h" +#include "../peripherals/mailbox.h" +#include "reboot.h" +#include "initramdisk.h" +#include "user.h" +#include "timer.h" +#include "buddy_system_2.h" +#include "dynamic_alloc.h" + +// List of commands. +char cmd_list[COMMAND_COUNT][SHELL_BUF_MAXSIZE] = { + "help", + "hello", + "reboot", + "board-revision", + "get-memory-info", + "ls", + "cd", + "cat", + "loaduser", + "setTimeout", + "bootTime", + "allocate-page", + "free-page", + "show-allocated-page", + "freelist", + // Dynamic allocator + "dalloc", + "dfree", + "rsv_mem" +}; + +// If 0, command is valid, otherwise invalid. +int cmd_status; + +void reset_buf(char* buf) { + for (int i = 0; i < SHELL_BUF_MAXSIZE; i++) { + buf[i] = 0; + } +} + +// Parse the entire command list to check which command the user inputs. +void handle_command(char* cmd) { + // Iterate through all existing commands within the entire command list. + int cmd_list_ind = 0; + // Iterate through the cmd buffer and parse commands, flags, arguments etc. + int cmd_ind = 0; + + // Store the arguments for commands. + char arg[MAX_ARG_COUNT][MAX_ARG_SIZE]; + + // Store the main command. + char command[10]; + + // Parse the command. + for (; cmd_ind < SHELL_BUF_MAXSIZE; cmd_ind++) { + if (cmd[cmd_ind] == ' ' || cmd[cmd_ind] == '\0') { + command[cmd_ind++] = '\0'; + break; + } + + command[cmd_ind] = cmd[cmd_ind]; + } + + // Record the number of arguments. + int arg_cnt = 0; + int arg_ind = 0; + + // Ends until strlen(cmd) + 1 is for capturing '\0'. + for (; cmd_ind < strlen(cmd) + 1; cmd_ind++) { + // End of an argument. + if (cmd[cmd_ind] == ' ' || cmd[cmd_ind] == '\0') { + arg[arg_cnt][arg_ind] = '\0'; + arg_cnt++; + arg_ind = 0; + + if (cmd[cmd_ind] == '\0') + break; + } else { + arg[arg_cnt][arg_ind] = cmd[cmd_ind]; + arg_ind++; + } + } + + // Check corresponding command within the command list. + for (; cmd_list_ind < COMMAND_COUNT; cmd_list_ind++) { + if (strcmp(cmd_list[cmd_list_ind], command) == 0) + break; + } + + // If no command was found inside command list. + if (cmd_list_ind >= COMMAND_COUNT) { + uart_send_string("Undefined command!\r\n"); + return; + } + + switch(cmd_list_ind) { + // "help" command. + case 0: + uart_send_string("\r\n"); + uart_send_string(" help : print this help menu\r\n"); + uart_send_string(" hello : print Hello World!\r\n"); + uart_send_string(" reboot : reboot the device\r\n"); + uart_send_string(" board-revision : print board revision\r\n"); + uart_send_string(" get-memory-info : print ARM memory base address and size\r\n"); + uart_send_string(" ls : print files in current directory\r\n"); + uart_send_string(" cd : cd [dir], change the shell working directory\r\n"); + uart_send_string(" loaduser : loaduser [file], load a user program from initial ramdisk to execute.\r\n"); + uart_send_string("\r\n"); + break; + // print "hello". + case 1: + uart_send_string("Hello World!\r\n"); + break; + // Reboot RPi3. + case 2: + reset(1000); + break; + // Print board revisiion. + case 3: + get_board_revision(); + uart_send_string("\r\n"); + break; + // Print ARM memory base address and size. + case 4: + get_arm_memory(); + uart_send_string("\r\n"); + break; + // "ls" command. + case 5: + show_current_dir_content(); + break; + // "cd" command. + case 6: + if (arg_cnt > 1) { + uart_send_string(" cd: too many arguments\r\n"); + break; + } + change_directory(arg[0]); + break; + // "cat" command. + case 7: + for (int i = 0; i < arg_cnt; i++) { + print_content(arg[i]); + } + break; + // Load a user program within initramfs. + case 8: + if (arg_cnt > 1) { + uart_send_string(" loaduser: too many arguments\r\n"); + break; + } + int i = 0; + for (; i < userDir->nFiles; i++) { + // Find the specified file in the current directory. + if (strcmp(userDir->files[i]->fName, arg[0]) == 0) { + // Send the address of the file into the function. + load_user_prog((uintptr_t)userDir->files[i]); + break; + } + } + + if (i == userDir->nFiles) { + uart_send_string(" loaduser: file not found.\r\n"); + } + + break; + // Set timeout. + case 9: + + if (arg_cnt > 2) { + uart_send_string(" setTimeout: too many arguments\r\n"); + break; + } else if (arg_cnt < 2) { + uart_send_string(" setTimeout: not enough arguments\r\n"); + break; + } + + int time = str2Int(arg[1], strlen(arg[1])); + add_timer(print_timeout_message, (void *)arg[0], time); + + + // Test preemption. + // int time = str2Int(arg[1], strlen(arg[1])); + // if (arg[2][0] == '0') { + // uart_send_string("Add exit loop\r\n"); + // add_timer(exit_loop, (void *)arg[0], time); + // } else if (arg[2][0] == '1') { + // uart_send_string("Add create loop\r\n"); + // add_timer(create_loop, (void *)arg[0], time); + // } + + break; + // Check the time(in secs) after booting and set the next timeout to 2 seconds. + case 10: + if (arg_cnt > 0) { + uart_send_string(" bootTime: too many arguments\r\n"); + break; + } + + int cur_time = get_current_time(); + uart_send_string("Time after booting: "); + uart_send_uint(cur_time); + uart_send_string("\r\n"); + char message[30]; + strcpy(message, "Timeout after booting\r\n"); + add_timer(print_timeout_message, message, 2); + break; + // Allocate page frames. + case 11: + if (arg_cnt > 1) { + uart_send_string(" allocate-mem: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" allocate-mem: not enough arguments\r\n"); + break; + } + uint64_t size = str2Int(arg[0], strlen(arg[0])); + uart_send_string("size: "); + uart_send_int(size); + uart_send_string("\r\n"); + + // allocate_info* info = allocate_frame(size * 1024); + int ind = alloc_page(size * 1024); + + uart_send_string("Allocate frames for "); + uart_send_int(size); + uart_send_string(" KB\r\n"); + + if (ind == -1) { + uart_send_string("allocate-mem failed!\r\n"); + break; + } + + uart_send_string("Allocated frames: "); + for (int i = alloc_frame_list[ind]->head->ind; i <= alloc_frame_list[ind]->tail->ind; i++) { + uart_send_int(i); + uart_send_string(" "); + } + uart_send_string("\r\n"); + + break; + case 12: + if (arg_cnt > 1) { + uart_send_string(" free-page: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" free-page: not enough arguments\r\n"); + break; + } + int frame = str2Int(arg[0], strlen(arg[0])); + free_page(frame); + break; + case 13: + show_alloc_list(); + break; + case 14: + if (arg_cnt > 0) { + uart_send_string(" freelist: too many arguments\r\n"); + break; + } + show_free_list(); + break; + case 15: + if (arg_cnt > 1) { + uart_send_string(" dalloc: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" dalloc: not enough arguments\r\n"); + break; + } + + size = str2Int(arg[0], strlen(arg[0])); + uint64_t allocated_addr = dynamic_alloc(size); + uart_send_string("Allocated physical address: 0x"); + uart_send_uint(allocated_addr); + uart_send_string("\r\n"); + break; + // Check reserved memory. + case 16: + if (arg_cnt > 1) { + uart_send_string(" dfree: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" dfree: not enough arguments\r\n"); + break; + } + + uint64_t free_addr = str2Int(arg[0], strlen(arg[0])); + dynamic_free(free_addr); + + break; + case 17: + if (arg_cnt > 2) { + uart_send_string(" rsv_mem: too many arguments\r\n"); + break; + } else if (arg_cnt < 2) { + uart_send_string(" rsv_mem: not enough arguments\r\n"); + break; + } + + uint64_t start = str2Int(arg[0], strlen(arg[0])); + uint64_t end = str2Int(arg[1], strlen(arg[1])); + // memory_reserve(start, end); + + default: + break; + } + +} + +void shell() { + + // Buffer to store commands. + char cmd[SHELL_BUF_MAXSIZE]; + + // Acts as a pointer to the cmd buffer. + int pos = 0; + + // To avoid printing command prompt when pressing enter with empty input. + int press_backspace = 0; + int press_arrow_key = 0; + int press_space = 0; + + uart_send_string("Welcome to Josh's mini OS!\r\n"); + + // Wait for user input. + while (1) { + // Clear buffer for new input. + reset_buf(cmd); + pos = 0; + + // Ready to fetch input command from the user. + if (!press_backspace && !press_arrow_key && !press_space) + uart_send_string("josh@raspberrypi:~ $ "); + cmd_status = VALID_COMMAND; + char c = uart_recv(); + + // If a backspace character is entered. On M3 mac, 0x7F(Delete) is sent when backspace is pressed. + // On some other configurations, 0x08(backspace) might be sent. + if (c == 0x7F || c == 0x08) { + press_backspace = 1; + continue; + // User pressed enter with no input.(Enter: 0xA, Carriage return: 0xD) + } else if (c == 0xA || c == 0xD) { + uart_send_string("\r\n"); + continue; + // User pressed left arrow with no input. Arrow keys pass multi-bytes. "0xE0" and "0x4B". + } else if (c == 0xE0 || c == 0x8B) { + // uart_send_string("In\r\n"); + press_arrow_key = 1; + continue; + } + + // if (press_arrow_key) { + // if (c == 0x4B) { + // uart_send_string("In2\r\n"); + // continue; + // } + // } + + press_backspace = 0; + press_arrow_key = 0; + press_space = 0; + // Print the user input on the screen. + uart_send(c); + + // Discard all leading white spaces. + if (c == ' ') { + press_space = 1; + continue; + } + + // Fetch all characters the user inputs. + while ((c != '\n') && (c != '\r')) { + if (c == 0x7F || c == 0x08) { + if (pos) { + cmd[pos--] = '\0'; + uart_send('\b'); + uart_send(' '); + uart_send('\b'); + } + } else { + cmd[pos++] = c; + + // Print error message if user inputs too many characters. + if (pos >= SHELL_BUF_MAXSIZE) { + cmd_status = COMMAND_EXCEED_SIZELIMIT; + reset_buf(cmd); + pos = 0; + uart_send_string("Exceed input command size!\r\n"); + break; + } + } + c = uart_recv(); + uart_send(c); + } + + uart_send_string("\r\n"); + + if (cmd_status != VALID_COMMAND) + continue; + + // Add an ending string character to the input command. + cmd[pos] = '\0'; + + handle_command(cmd); + } +} \ No newline at end of file diff --git a/Lab4/kernel/shell.h b/Lab4/kernel/shell.h new file mode 100644 index 000000000..c18665292 --- /dev/null +++ b/Lab4/kernel/shell.h @@ -0,0 +1,14 @@ +#ifndef _SHELL_H_ +#define _SHELL_H_ + +#define COMMAND_COUNT 18 +#define SHELL_BUF_MAXSIZE 256 +#define MAX_ARG_COUNT 5 +#define MAX_ARG_SIZE 16 + +#define VALID_COMMAND 0 +#define COMMAND_EXCEED_SIZELIMIT 1 + +void shell(); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/task.c b/Lab4/kernel/task.c new file mode 100644 index 000000000..e602a8747 --- /dev/null +++ b/Lab4/kernel/task.c @@ -0,0 +1,136 @@ +#include "task.h" +#include "alloc.h" +#include "irq.h" +#include "entry.h" +#include "../peripherals/mini_uart.h" + +task_queue* t_priority_queue[MIN_PRIORITY + 1]; +int current_priority; + +void init_task_queue(void) { + for (int i = 0; i <= MIN_PRIORITY; i++) { + t_priority_queue[i] = (task_queue *)simple_malloc(sizeof(task_queue)); + t_priority_queue[i]->front = NULL; + t_priority_queue[i]->end = NULL; + } + current_priority = 10; +} + +// Insert the task into queue. +void enqueue_task(task* t) { + // The priority queue is empty initially. + if (t_priority_queue[t->priority]->front == NULL) { + t_priority_queue[t->priority]->front = t; + t_priority_queue[t->priority]->end = t; + } else { + t_priority_queue[t->priority]->end->next = t; + t_priority_queue[t->priority]->end = t_priority_queue[t->priority]->end->next; + } + t->next = NULL; +} + +// Dequeue the task with highest priority. +task* dequeue_task(void) { + for (int i = 0; i <= MIN_PRIORITY; i++) { + if (t_priority_queue[i]->front != NULL) { + task* tmp = t_priority_queue[i]->front; + t_priority_queue[i]->front = t_priority_queue[i]->front->next; + if (t_priority_queue[i]->front == NULL) { + t_priority_queue[i]->end = NULL; + } + + return tmp; + } + } + + return NULL; +} + +void execute_task() { + disable_el1_interrupt(); + // Iterate through the task queues and execute the tasks from high priority to + // low priority. + task* t = NULL; + for (int i = 0; i <= MIN_PRIORITY; i++) { + if (t_priority_queue[i]->front != NULL) { + if (t_priority_queue[i]->front->priority < current_priority) { + t = dequeue_task(); + break; + } + } + } + + if (t != NULL) { + // If the task has higher priority, execute it first. + int prev = current_priority; + current_priority = t->priority; + enable_el1_interrupt(); + t->intr_func(t->arg); + current_priority = prev; + // Check the queue recursively until no task is left. + execute_task(); + } + enable_el1_interrupt(); +} + +// Store the context of current task. +void save_context(task* t) { + asm volatile ( + // stp(Store Pair of registers) + "stp x0, x1, [%0, 16 * 0];" + "stp x2, x3, [%0, 16 * 1];" + "stp x4, x5, [%0, 16 * 2];" + "stp x6, x7, [%0, 16 * 3];" + "stp x8, x9, [%0, 16 * 4];" + "stp x10, x11, [%0, 16 * 5];" + "stp x12, x13, [%0, 16 * 6];" + "stp x14, x15, [%0, 16 * 7];" + "stp x16, x17, [%0, 16 * 8];" + "stp x18, x19, [%0, 16 * 9];" + "stp x20, x21, [%0, 16 * 10];" + "stp x22, x23, [%0, 16 * 11];" + "stp x24, x25, [%0, 16 * 12];" + "stp x26, x27, [%0, 16 * 13];" + "stp x28, x29, [%0, 16 * 14];" + "mrs x0, spsr_el1;" + "stp x30, x0, [%0, 16 * 15];" + "mrs x0, elr_el1;" + "str x0, [%0, 16 * 16];" + "ldp x0, x1, [%0, 16 * 0];" + + // Output + : + // Input + : "r" (t->sp) + : + ); +} + +void load_context(task* t) { + asm volatile ( + "ldp x0, x1, [%0, 16 * 0];" + "ldp x2, x3, [%0, 16 * 1];" + "ldp x4, x5, [%0, 16 * 2];" + "ldp x6, x7, [%0, 16 * 3];" + "ldp x8, x9, [%0, 16 * 4];" + "ldp x10, x11, [%0, 16 * 5];" + "ldp x12, x13, [%0, 16 * 6];" + "ldp x14, x15, [%0, 16 * 7];" + "ldp x16, x17, [%0, 16 * 8];" + "ldp x18, x19, [%0, 16 * 9];" + "ldp x20, x21, [%0, 16 * 10];" + "ldp x22, x23, [%0, 16 * 11];" + "ldp x24, x25, [%0, 16 * 12];" + "ldp x26, x27, [%0, 16 * 13];" + "ldp x28, x29, [%0, 16 * 14];" + "ldp x30, x0, [%0, 16 * 15];" + "msr spsr_el1, x0;" + "ldr x0, [%0, 16 * 16];" + "msr elr_el1, x0;" + "ldp x0, x1, [%0, 16 * 0];" + + : + : "r" (t->sp) + : + ); +} \ No newline at end of file diff --git a/Lab4/kernel/task.h b/Lab4/kernel/task.h new file mode 100644 index 000000000..f8b6e0425 --- /dev/null +++ b/Lab4/kernel/task.h @@ -0,0 +1,39 @@ +#ifndef _TASK_H_ +#define _TASK_H_ + +#include + +// There can be maximum of 10 priorities(0 ~ 9). +#define MIN_PRIORITY 9 + +// Function pointer for the handler function to be executed. +typedef void(*task_func_t)(void *); + +typedef struct _task { + task_func_t intr_func; + // Argument for interrupt function. + void* arg; + int priority; + struct _task* next; + // Stores the stack pointer of the current task to keep track in case of nested interrupt. + uint64_t sp; +} task; + +typedef struct _task_queue { + task* front; + task* end; +} task_queue; + +// Front -> highest priority +// End -> Lowest priority +extern task_queue* priority_q[MIN_PRIORITY + 1]; + +void save_context(task* t); +void load_context(task* t); + +void init_task_queue(void); +void enqueue_task(task* t); +task* dequeue_task(void); +void execute_task(); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/timer.c b/Lab4/kernel/timer.c new file mode 100644 index 000000000..c82f824a4 --- /dev/null +++ b/Lab4/kernel/timer.c @@ -0,0 +1,221 @@ +#include "timer.h" +#include "alloc.h" +#include "mini_uart.h" +#include "task.h" +#include "irq.h" + +#define DEFAULT_TIMER 0xFFF + +// #define CORE0_TIMER_IRQ_CTRL 0x40000040 +TQueue* q_head; + +void core_timer_enable(void) { + // Initialize time queue. + q_head = NULL; + + // Set interrupt interval to a very large time initially. + int interval = 0xFFFFFFFF; + + asm volatile( + "mov x0, 1;" + // Control register for the physical timer on the current core. + "msr cntp_ctl_el0, x0;" // enable + + // cntfrq_el0 holds the frequency in Hz at which the system + // counter increments. + "mrs x0, cntfrq_el0;" + //"mul x0, x0, %0;" + //"msr cntp_tval_el0, x0;" // set expired time + "mov x0, 2;" + "ldr x1, =0x40000040;" + + // Stores the value from "x0"(which is the "w0" register, the 32-bit view of "x0"). + // Writing 2 into the register unmasks the timer interrupt. + "str x0, [x1];" // unmask timer interrupt + + : + : "r" (interval) + : + + ); +} + +unsigned long get_current_time(void) { + long long current_time; + long long freq; + + asm volatile( + "mrs x0, cntpct_el0;" + "mov %0, x0;" + "mrs x1, cntfrq_el0;" + "mov %1, x1;" + + // Output operands. + : "=r" (current_time), "=r" (freq) + // No input operands. + : + // Clobbered registers. + : "x0", "x1" + ); + + return (unsigned long)(current_time / freq); +} + +void set_timer_expire(long sec) { + + asm volatile( + "mrs x0, cntfrq_el0;" + "mul x0, x0, %0;" + "msr cntp_tval_el0, x0;" + + // Output operand. + : + // Input operand. + : "r" (sec) + // Clobbered registers. + : "x0" + ); +} + +int p = 1; + +void handle_timer_intr(void* data) { + disable_el1_interrupt(); + uart_send_string("Timer invoked!\r\n"); + + TQueue* cur = q_head; + if (cur == NULL) { + uart_send_string("Timer queue empty!\r\n"); + // Check which event should be invoked. + } else { + unsigned int cur_time = get_current_time(); + + // Check which event expired. + while (cur != NULL) { + if (cur->e.invoked_t <= cur_time) { + task* t = (task *)simple_malloc(sizeof(task)); + uart_send_string("Command executed time: "); + uart_send_uint(cur->e.enter_t); + uart_send_string("\r\n"); + uart_send_string("Event message: "); + + t->intr_func = cur->e.callback; + t->arg = cur->e.arg; + t->priority = p++; + enqueue_task(t); + uart_send_string("Current time: "); + uart_send_uint(cur_time); + uart_send_string("\r\n"); + + cur = cur->next; + } else { + break; + } + } + + + q_head = cur; + } + + update_timer(); + execute_task(); +} + +// Register the callback function into the timer queue. +int add_timer(one_shot_timer_callback_t callback, void* arg, unsigned int expire) { + // Allocate memory for the argument in case it was erased before executing the interrupt. + char* copy_arg = simple_malloc(sizeof(char) * strlen((char *)arg)); + strcpy(copy_arg, (char *)arg); + + TQueue* new = (TQueue *)simple_malloc(sizeof(TQueue)); + new->next = NULL; + new->e.enter_t = get_current_time(); + new->e.invoked_t = new->e.enter_t + expire; + new->e.callback = callback; + new->e.arg = copy_arg; + + // Queue is empty. + if (q_head == NULL) { + q_head = new; + set_timer_expire(expire); + } else { + TQueue* cur = q_head; + + // Sorted queue from smallest invoked time to largest + // to avoid inaccuracies when multiple timers are active. + if (cur->e.invoked_t > new->e.invoked_t) { + new->next = cur; + q_head = new; + } else { + while (cur->next != NULL) { + if (cur->next->e.invoked_t <= new->e.invoked_t) { + cur = cur->next; + } else { + break; + } + } + new->next = cur->next; + cur->next = new; + } + + update_timer(); + } +} + +// Check the invoked time of first element within the queue. +// If queue is empty, reset the timer to 20 seconds. +void update_timer(void) { + unsigned int cur_time = get_current_time(); + + if (q_head == NULL) { + set_timer_expire(DEFAULT_TIMER); + } else { + // Expire immediately if the event is overdue. + if (q_head->e.invoked_t <= cur_time) { + set_timer_expire(0); + } else { + set_timer_expire(q_head->e.invoked_t - cur_time); + } + } +} + +void clear_timer_intr(void) { + unsigned long sec = 0xFFF; + + asm volatile ( + "mrs x0, cntfrq_el0;" + "mul x0, x0, %0;" + "msr cntp_tval_el0, x0;" + + // Output operand. + : + // Input operand. + : "r" (sec) + // Clobbered registers. + : "x0" + ); +} + +void print_timeout_message(void* message) { + uart_send_string((char *)message); + uart_send_string("\r\n"); +} + + +static volatile int flag = 1; +void create_loop(void* data) { + uart_send_string("In create loop\r\n"); + while (flag); + uart_send_string("Exit create loop \r\n"); +} + +void exit_loop(void* data) { + uart_send_string("In exit loop\r\n"); + flag = 0; +} + +void delay_loop(void* data) { + // delay(10000); + uart_send_string("In delay loop\r\n"); + while (1); +} \ No newline at end of file diff --git a/Lab4/kernel/timer.h b/Lab4/kernel/timer.h new file mode 100644 index 000000000..a0ea956ee --- /dev/null +++ b/Lab4/kernel/timer.h @@ -0,0 +1,41 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +// Define a callback function for one-shot timer. +typedef void (*one_shot_timer_callback_t)(void* arg); + +typedef struct _event { + // Enter queue time of the event. + unsigned int enter_t; + + // The actual time it expires. + unsigned int invoked_t; + + // Arguments passed to the callback function. + void* arg; + + // Callback function to be invoked after timer expires. + one_shot_timer_callback_t callback; +} Event; + +// Singly linked list for the timer queue. +typedef struct _timer_queue { + Event e; + struct _timer_queue* next; +} TQueue; + +void core_timer_enable(void); +unsigned long get_current_time(void); +void set_timer_expire(long sec); +void handle_timer_intr(void* data); +void clear_timer_intr(void); + +int add_timer(one_shot_timer_callback_t callback, void* arg, unsigned int expire); +void update_timer(void); + +void print_timeout_message(void* message); +void create_loop(void* data); +void exit_loop(void* data); +void delay_loop(void* data); + +#endif \ No newline at end of file diff --git a/Lab4/kernel/timer_asm.S b/Lab4/kernel/timer_asm.S new file mode 100644 index 000000000..1a289d697 --- /dev/null +++ b/Lab4/kernel/timer_asm.S @@ -0,0 +1,23 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 + +core_timer_enable: + mov x0, 1 + // Control register for the physical timer on the current core. + msr cntp_ctl_el0, x0 // enable + + // cntfrq_el0 holds the frequency in Hz at which the system + // counter increments. + mrs x0, cntfrq_el0 + msr cntp_tval_el0, x0 // set expired time + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + + // Stores the value from "x0"(which is the "w0" register, the 32-bit view of "x0"). + // Writing 2 into the register unmasks the timer interrupt. + str w0, [x1] // unmask timer interrupt + + +// Set the timer to expire a second later. +core_timer_handler: + mrs x0, cntfrq_el0 + msr cntp_tval_el0, x0 \ No newline at end of file diff --git a/Lab4/kernel/user.c b/Lab4/kernel/user.c new file mode 100644 index 000000000..793cd8a8d --- /dev/null +++ b/Lab4/kernel/user.c @@ -0,0 +1,56 @@ +#include "initramdisk.h" +#include "user.h" +#include "../peripherals/mini_uart.h" +#include "../peripherals/mm.h" + +#define USER_STACK_SIZE 0x2000 + +int load_user_prog(uintptr_t file_addr) { + uintptr_t user_start = 0x100000; + uintptr_t user_sp = user_start + USER_STACK_SIZE; + + // Load the file content into memory. + struct File* file = (struct File *)file_addr; + unsigned int fileSize = file->fileSize; + + // Write the user program into the address specified by the linker. + char* userProg = (char *)0x100000; + + int i = 0; + while (fileSize--) { + uart_send(file->fData[i]); + *userProg++ = file->fData[i++]; + } + + uart_send_string("Print file size\r\n"); + uart_send_uint((uintptr_t)file); + uart_send_string("\r\n"); + uart_send_uint(file->fileSize); + uart_send_string("\r\n"); + + + // EL1 to EL0. + asm volatile ( + // Set SPSR_EL1 to 0x3C0 to disable interrupts and set appropriate state for EL0. + // [9:6]DAIF -> Enable interrupt in user program.(I -> 0) + "mov x0, 0x340;" + "msr spsr_el1, x0;" + + // Set ELR_EL1 to the start address of the user program. + "mov x1, %0;" + "msr elr_el1, x1;" + + // Set SP_EL0 to a valid stack pointer for the user program. + "mov x2, %1;" + "msr sp_el0, x2;" + "eret;" + + // No output operands. + : + // Input operands. + : "r" (user_start), "r" (user_sp) + // List of clobbered registers.(Tells the compiler which registers the assembly code modifies or "clobbers.") + : "x0", "x1", "x2" + ); + +} \ No newline at end of file diff --git a/Lab4/kernel/user.h b/Lab4/kernel/user.h new file mode 100644 index 000000000..a1228ab19 --- /dev/null +++ b/Lab4/kernel/user.h @@ -0,0 +1,8 @@ +#ifndef _USER_H_ +#define _USER_H_ + +#include + +int load_user_prog(uintptr_t file_addr); + +#endif \ No newline at end of file diff --git a/Lab4/output.dts b/Lab4/output.dts new file mode 100644 index 000000000..31c44b4ae --- /dev/null +++ b/Lab4/output.dts @@ -0,0 +1,1565 @@ +/dts-v1/; + +/memreserve/ 0x0000000000000000 0x0000000000001000; +/ { + compatible = "raspberrypi,3-model-b-plus\0brcm,bcm2837"; + model = "Raspberry Pi 3 Model B+"; + #address-cells = <0x01>; + #size-cells = <0x01>; + interrupt-parent = <0x01>; + + aliases { + serial0 = "/soc/serial@7e215040"; + serial1 = "/soc/serial@7e201000"; + aux = "/soc/aux@7e215000"; + sound = "/soc/sound"; + soc = "/soc"; + dma = "/soc/dma-controller@7e007000"; + intc = "/soc/interrupt-controller@7e00b200"; + watchdog = "/soc/watchdog@7e100000"; + random = "/soc/rng@7e104000"; + mailbox = "/soc/mailbox@7e00b880"; + gpio = "/soc/gpio@7e200000"; + uart0 = "/soc/serial@7e201000"; + uart1 = "/soc/serial@7e215040"; + sdhost = "/soc/mmc@7e202000"; + mmc = "/soc/mmc@7e300000"; + mmc1 = "/soc/mmcnr@7e300000"; + mmc0 = "/soc/mmc@7e202000"; + i2s = "/soc/i2s@7e203000"; + i2c0 = "/soc/i2c0mux/i2c@0"; + i2c1 = "/soc/i2c@7e804000"; + i2c10 = "/soc/i2c0mux/i2c@1"; + i2c = "/soc/i2c@7e804000"; + spi0 = "/soc/spi@7e204000"; + spi1 = "/soc/spi@7e215080"; + spi2 = "/soc/spi@7e2150c0"; + usb = "/soc/usb@7e980000"; + leds = "/leds"; + fb = "/soc/fb"; + thermal = "/soc/thermal@7e212000"; + axiperf = "/soc/axiperf"; + i2c2 = "/soc/i2c@7e805000"; + ethernet0 = "/soc/usb@7e980000/usb-port@1/usb-port@1/ethernet@1"; + bluetooth = "/soc/serial@7e201000/bluetooth"; + phandle = <0x32>; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0"; + phandle = <0x36>; + }; + + reserved-memory { + #address-cells = <0x01>; + #size-cells = <0x01>; + ranges; + phandle = <0x3a>; + + linux,cma { + compatible = "shared-dma-pool"; + size = <0x4000000>; + reusable; + linux,cma-default; + phandle = <0x3b>; + }; + }; + + thermal-zones { + + cpu-thermal { + polling-delay-passive = <0x00>; + polling-delay = <0x3e8>; + thermal-sensors = <0x02>; + coefficients = <0xfffffde6 0x64960>; + phandle = <0x3c>; + + trips { + phandle = <0x3d>; + + cpu-crit { + temperature = <0x1adb0>; + hysteresis = <0x00>; + type = "critical"; + }; + }; + + cooling-maps { + phandle = <0x3e>; + }; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <0x01>; + #size-cells = <0x01>; + ranges = <0x7e000000 0x3f000000 0x1000000 0x40000000 0x40000000 0x1000>; + dma-ranges = <0xc0000000 0x00 0x3f000000 0x7e000000 0x3f000000 0x1000000>; + phandle = <0x3f>; + + timer@7e003000 { + compatible = "brcm,bcm2835-system-timer"; + reg = <0x7e003000 0x1000>; + interrupts = <0x01 0x00 0x01 0x01 0x01 0x02 0x01 0x03>; + clock-frequency = <0xf4240>; + status = "disabled"; + phandle = <0x40>; + }; + + txp@7e004000 { + compatible = "brcm,bcm2835-txp"; + reg = <0x7e004000 0x20>; + interrupts = <0x01 0x0b>; + status = "disabled"; + phandle = <0x41>; + }; + + cprman@7e101000 { + compatible = "brcm,bcm2835-cprman"; + #clock-cells = <0x01>; + reg = <0x7e101000 0x2000>; + clocks = <0x03 0x04 0x00 0x04 0x01 0x04 0x02 0x05 0x00 0x05 0x01 0x05 0x02>; + firmware = <0x06>; + phandle = <0x08>; + }; + + mailbox@7e00b880 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0x00 0x01>; + #mbox-cells = <0x00>; + phandle = <0x1f>; + }; + + gpio@7e200000 { + compatible = "brcm,bcm2835-gpio"; + reg = <0x7e200000 0xb4>; + interrupts = <0x02 0x11 0x02 0x12>; + gpio-controller; + #gpio-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x02>; + gpio-ranges = <0x07 0x00 0x00 0x36>; + gpio-line-names = "ID_SDA\0ID_SCL\0GPIO2\0GPIO3\0GPIO4\0GPIO5\0GPIO6\0GPIO7\0GPIO8\0GPIO9\0GPIO10\0GPIO11\0GPIO12\0GPIO13\0GPIO14\0GPIO15\0GPIO16\0GPIO17\0GPIO18\0GPIO19\0GPIO20\0GPIO21\0GPIO22\0GPIO23\0GPIO24\0GPIO25\0GPIO26\0GPIO27\0HDMI_HPD_N\0STATUS_LED_G\0CTS0\0RTS0\0TXD0\0RXD0\0SD1_CLK\0SD1_CMD\0SD1_DATA0\0SD1_DATA1\0SD1_DATA2\0SD1_DATA3\0PWM0_OUT\0PWM1_OUT\0ETH_CLK\0WIFI_CLK\0SDA0\0SCL0\0SMPS_SCL\0SMPS_SDA\0SD_CLK_R\0SD_CMD_R\0SD_DATA0_R\0SD_DATA1_R\0SD_DATA2_R\0SD_DATA3_R"; + phandle = <0x07>; + + dpi-gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b>; + brcm,function = <0x06>; + phandle = <0x42>; + }; + + emmc-gpio22 { + brcm,pins = <0x16 0x17 0x18 0x19 0x1a 0x1b>; + brcm,function = <0x07>; + phandle = <0x43>; + }; + + emmc-gpio34 { + brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02 0x02 0x02 0x02 0x02>; + phandle = <0x44>; + }; + + emmc-gpio48 { + brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; + brcm,function = <0x07>; + phandle = <0x14>; + }; + + gpclk0-gpio4 { + brcm,pins = <0x04>; + brcm,function = <0x04>; + phandle = <0x45>; + }; + + gpclk1-gpio5 { + brcm,pins = <0x05>; + brcm,function = <0x04>; + phandle = <0x46>; + }; + + gpclk1-gpio42 { + brcm,pins = <0x2a>; + brcm,function = <0x04>; + phandle = <0x47>; + }; + + gpclk1-gpio44 { + brcm,pins = <0x2c>; + brcm,function = <0x04>; + phandle = <0x48>; + }; + + gpclk2-gpio6 { + brcm,pins = <0x06>; + brcm,function = <0x04>; + phandle = <0x49>; + }; + + gpclk2-gpio43 { + brcm,pins = <0x2b>; + brcm,function = <0x04>; + brcm,pull = <0x00>; + phandle = <0x4a>; + }; + + i2c0if-gpio0 { + brcm,pins = <0x00 0x01>; + brcm,function = <0x04>; + phandle = <0x1d>; + }; + + i2c0if-gpio28 { + brcm,pins = <0x1c 0x1d>; + brcm,function = <0x04>; + phandle = <0x4b>; + }; + + i2c0if-gpio44 { + brcm,pins = <0x2c 0x2d>; + brcm,function = <0x05>; + phandle = <0x1e>; + }; + + i2c1-gpio2 { + brcm,pins = <0x02 0x03>; + brcm,function = <0x04>; + phandle = <0x4c>; + }; + + i2c1-gpio44 { + brcm,pins = <0x2c 0x2d>; + brcm,function = <0x06>; + phandle = <0x4d>; + }; + + jtag-gpio22 { + brcm,pins = <0x16 0x17 0x18 0x19 0x1a 0x1b>; + brcm,function = <0x03>; + phandle = <0x4e>; + }; + + pcm-gpio18 { + brcm,pins = <0x12 0x13 0x14 0x15>; + brcm,function = <0x04>; + phandle = <0x4f>; + }; + + pcm-gpio28 { + brcm,pins = <0x1c 0x1d 0x1e 0x1f>; + brcm,function = <0x06>; + phandle = <0x50>; + }; + + sdhost-gpio48 { + brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; + brcm,function = <0x04>; + phandle = <0x0d>; + }; + + spi0-gpio7 { + brcm,pins = <0x07 0x08 0x09 0x0a 0x0b>; + brcm,function = <0x04>; + phandle = <0x51>; + }; + + spi0-gpio35 { + brcm,pins = <0x23 0x24 0x25 0x26 0x27>; + brcm,function = <0x04>; + phandle = <0x52>; + }; + + spi1-gpio16 { + brcm,pins = <0x10 0x11 0x12 0x13 0x14 0x15>; + brcm,function = <0x03>; + phandle = <0x53>; + }; + + spi2-gpio40 { + brcm,pins = <0x28 0x29 0x2a 0x2b 0x2c 0x2d>; + brcm,function = <0x03>; + phandle = <0x54>; + }; + + uart0-gpio14 { + brcm,pins = <0x0e 0x0f>; + brcm,function = <0x04>; + phandle = <0x55>; + }; + + uart0-ctsrts-gpio16 { + brcm,pins = <0x10 0x11>; + brcm,function = <0x07>; + phandle = <0x56>; + }; + + uart0-ctsrts-gpio30 { + brcm,pins = <0x1e 0x1f>; + brcm,function = <0x07>; + brcm,pull = <0x02 0x00>; + phandle = <0x57>; + }; + + uart0-gpio32 { + brcm,pins = <0x20 0x21>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02>; + phandle = <0x58>; + }; + + uart0-gpio36 { + brcm,pins = <0x24 0x25>; + brcm,function = <0x06>; + phandle = <0x59>; + }; + + uart0-ctsrts-gpio38 { + brcm,pins = <0x26 0x27>; + brcm,function = <0x06>; + phandle = <0x5a>; + }; + + uart1-gpio14 { + brcm,pins = <0x0e 0x0f>; + brcm,function = <0x02>; + phandle = <0x5b>; + }; + + uart1-ctsrts-gpio16 { + brcm,pins = <0x10 0x11>; + brcm,function = <0x02>; + phandle = <0x5c>; + }; + + uart1-gpio32 { + brcm,pins = <0x20 0x21>; + brcm,function = <0x02>; + phandle = <0x5d>; + }; + + uart1-ctsrts-gpio30 { + brcm,pins = <0x1e 0x1f>; + brcm,function = <0x02>; + phandle = <0x5e>; + }; + + uart1-gpio40 { + brcm,pins = <0x28 0x29>; + brcm,function = <0x02>; + phandle = <0x5f>; + }; + + uart1-ctsrts-gpio42 { + brcm,pins = <0x2a 0x2b>; + brcm,function = <0x02>; + phandle = <0x60>; + }; + + i2c-slave-gpio18 { + brcm,pins = <0x12 0x13 0x14 0x15>; + brcm,function = <0x07>; + phandle = <0x61>; + }; + + jtag-gpio4 { + brcm,pins = <0x04 0x05 0x06 0x0c 0x0d>; + brcm,function = <0x02>; + phandle = <0x62>; + }; + + pwm0-gpio12 { + brcm,pins = <0x0c>; + brcm,function = <0x04>; + phandle = <0x63>; + }; + + pwm0-gpio18 { + brcm,pins = <0x12>; + brcm,function = <0x02>; + phandle = <0x64>; + }; + + pwm0-gpio40 { + brcm,pins = <0x28>; + brcm,function = <0x04>; + phandle = <0x65>; + }; + + pwm1-gpio13 { + brcm,pins = <0x0d>; + brcm,function = <0x04>; + phandle = <0x66>; + }; + + pwm1-gpio19 { + brcm,pins = <0x13>; + brcm,function = <0x02>; + phandle = <0x67>; + }; + + pwm1-gpio41 { + brcm,pins = <0x29>; + brcm,function = <0x04>; + phandle = <0x68>; + }; + + pwm1-gpio45 { + brcm,pins = <0x2d>; + brcm,function = <0x04>; + phandle = <0x69>; + }; + + dpi_18bit_cpadhi_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>; + brcm,function = <0x06>; + brcm,pull = <0x00>; + phandle = <0x6a>; + }; + + dpi_18bit_cpadhi_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>; + brcm,function = <0x06>; + phandle = <0x6b>; + }; + + dpi_18bit_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>; + brcm,function = <0x06>; + phandle = <0x6c>; + }; + + dpi_18bit_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>; + brcm,function = <0x06>; + phandle = <0x6d>; + }; + + dpi_16bit_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>; + brcm,function = <0x06>; + phandle = <0x6e>; + }; + + dpi_16bit_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>; + brcm,function = <0x06>; + phandle = <0x6f>; + }; + + dpi_16bit_cpadhi_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>; + brcm,function = <0x06>; + phandle = <0x70>; + }; + + dpi_16bit_cpadhi_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>; + brcm,function = <0x06>; + phandle = <0x71>; + }; + + gpioout { + brcm,pins = <0x06>; + brcm,function = <0x01>; + phandle = <0x72>; + }; + + alt0 { + brcm,pins = <0x04 0x05 0x07 0x08 0x09 0x0a 0x0b>; + brcm,function = <0x04>; + phandle = <0x73>; + }; + + spi0_pins { + brcm,pins = <0x09 0x0a 0x0b>; + brcm,function = <0x04>; + phandle = <0x0f>; + }; + + spi0_cs_pins { + brcm,pins = <0x08 0x07>; + brcm,function = <0x01>; + phandle = <0x10>; + }; + + i2c0 { + brcm,pins = <0x00 0x01>; + brcm,function = <0x04>; + phandle = <0x74>; + }; + + i2c1 { + brcm,pins = <0x02 0x03>; + brcm,function = <0x04>; + phandle = <0x15>; + }; + + i2s { + brcm,pins = <0x12 0x13 0x14 0x15>; + brcm,function = <0x04>; + phandle = <0x0e>; + }; + + sdio_pins { + brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02 0x02 0x02 0x02 0x02>; + phandle = <0x1b>; + }; + + bt_pins { + brcm,pins = <0x2b>; + brcm,function = <0x04>; + brcm,pull = <0x00>; + phandle = <0x0a>; + }; + + uart0_pins { + brcm,pins = <0x20 0x21>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02>; + phandle = <0x09>; + }; + + uart1_pins { + brcm,pins; + brcm,function; + brcm,pull; + phandle = <0x13>; + }; + + uart1_bt_pins { + brcm,pins = <0x20 0x21 0x1e 0x1f>; + brcm,function = <0x02>; + brcm,pull = <0x00 0x02 0x02 0x00>; + phandle = <0x75>; + }; + + audio_pins { + brcm,pins = <0x28 0x29>; + brcm,function = <0x04>; + brcm,pull = <0x00>; + phandle = <0x20>; + }; + }; + + serial@7e201000 { + compatible = "arm,pl011\0arm,primecell"; + reg = <0x7e201000 0x200>; + interrupts = <0x02 0x19>; + clocks = <0x08 0x13 0x08 0x14>; + clock-names = "uartclk\0apb_pclk"; + arm,primecell-periphid = <0x241011>; + cts-event-workaround; + skip-init; + pinctrl-names = "default"; + pinctrl-0 = <0x09 0x0a>; + status = "okay"; + phandle = <0x26>; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + max-speed = <0x2dc6c0>; + shutdown-gpios = <0x0b 0x00 0x00>; + local-bd-address = [00 00 00 00 00 00]; + fallback-bd-address; + status = "okay"; + phandle = <0x34>; + }; + }; + + mmc@7e202000 { + compatible = "brcm,bcm2835-sdhost"; + reg = <0x7e202000 0x100>; + interrupts = <0x02 0x18>; + clocks = <0x08 0x14>; + status = "okay"; + dmas = <0x0c 0x2000000d>; + dma-names = "rx-tx"; + bus-width = <0x04>; + brcm,overclock-50 = <0x00>; + brcm,pio-limit = <0x01>; + firmware = <0x06>; + pinctrl-names = "default"; + pinctrl-0 = <0x0d>; + phandle = <0x2e>; + }; + + i2s@7e203000 { + compatible = "brcm,bcm2835-i2s"; + reg = <0x7e203000 0x24>; + clocks = <0x08 0x1f>; + status = "disabled"; + dmas = <0x0c 0x02 0x0c 0x03>; + dma-names = "tx\0rx"; + #sound-dai-cells = <0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x0e>; + phandle = <0x28>; + }; + + spi@7e204000 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7e204000 0x200>; + interrupts = <0x02 0x16>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + dmas = <0x0c 0x06 0x0c 0x07>; + dma-names = "tx\0rx"; + pinctrl-names = "default"; + pinctrl-0 = <0x0f 0x10>; + cs-gpios = <0x07 0x08 0x01 0x07 0x07 0x01>; + phandle = <0x29>; + + spidev@0 { + compatible = "spidev"; + reg = <0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + spi-max-frequency = <0x7735940>; + phandle = <0x76>; + }; + + spidev@1 { + compatible = "spidev"; + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + spi-max-frequency = <0x7735940>; + phandle = <0x77>; + }; + }; + + i2c@7e205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x200>; + interrupts = <0x02 0x15>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + clock-frequency = <0x186a0>; + phandle = <0x1c>; + }; + + dpi@7e208000 { + compatible = "brcm,bcm2835-dpi"; + reg = <0x7e208000 0x8c>; + clocks = <0x08 0x14 0x08 0x2c>; + clock-names = "core\0pixel"; + status = "disabled"; + phandle = <0x78>; + }; + + dsi@7e209000 { + compatible = "brcm,bcm2835-dsi0"; + reg = <0x7e209000 0x78>; + interrupts = <0x02 0x04>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + clocks = <0x08 0x20 0x08 0x2f 0x08 0x31>; + clock-names = "phy\0escape\0pixel"; + clock-output-names = "dsi0_byte\0dsi0_ddr2\0dsi0_ddr"; + status = "disabled"; + power-domains = <0x11 0x11>; + phandle = <0x04>; + }; + + aux@7e215000 { + compatible = "brcm,bcm2835-aux"; + #clock-cells = <0x01>; + reg = <0x7e215000 0x08>; + clocks = <0x08 0x14>; + phandle = <0x12>; + }; + + serial@7e215040 { + compatible = "brcm,bcm2835-aux-uart"; + reg = <0x7e215040 0x40>; + interrupts = <0x01 0x1d>; + clocks = <0x12 0x00>; + status = "okay"; + skip-init; + pinctrl-names = "default"; + pinctrl-0 = <0x13>; + phandle = <0x27>; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + max-speed = <0x38400>; + shutdown-gpios = <0x0b 0x00 0x00>; + local-bd-address = [00 00 00 00 00 00]; + fallback-bd-address; + status = "disabled"; + phandle = <0x35>; + }; + }; + + spi@7e215080 { + compatible = "brcm,bcm2835-aux-spi"; + reg = <0x7e215080 0x40>; + interrupts = <0x01 0x1d>; + clocks = <0x12 0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + phandle = <0x79>; + }; + + spi@7e2150c0 { + compatible = "brcm,bcm2835-aux-spi"; + reg = <0x7e2150c0 0x40>; + interrupts = <0x01 0x1d>; + clocks = <0x12 0x02>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + phandle = <0x7a>; + }; + + pwm@7e20c000 { + compatible = "brcm,bcm2835-pwm"; + reg = <0x7e20c000 0x28>; + clocks = <0x08 0x1e>; + assigned-clocks = <0x08 0x1e>; + assigned-clock-rates = <0x2faf080>; + #pwm-cells = <0x03>; + status = "disabled"; + phandle = <0x7b>; + }; + + mmc@7e300000 { + compatible = "brcm,bcm2835-mmc\0brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <0x02 0x1e>; + clocks = <0x08 0x1c>; + status = "disabled"; + dmas = <0x0c 0x0b>; + dma-names = "rx-tx"; + brcm,overclock-50 = <0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x14>; + bus-width = <0x04>; + phandle = <0x2f>; + }; + + hvs@7e400000 { + compatible = "brcm,bcm2835-hvs"; + reg = <0x7e400000 0x6000>; + interrupts = <0x02 0x01>; + status = "disabled"; + phandle = <0x7c>; + }; + + dsi@7e700000 { + compatible = "brcm,bcm2835-dsi1"; + reg = <0x7e700000 0x8c>; + interrupts = <0x02 0x0c>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + clocks = <0x08 0x23 0x08 0x30 0x08 0x32>; + clock-names = "phy\0escape\0pixel"; + clock-output-names = "dsi1_byte\0dsi1_ddr2\0dsi1_ddr"; + status = "disabled"; + power-domains = <0x11 0x12>; + phandle = <0x05>; + }; + + i2c@7e804000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e804000 0x1000>; + interrupts = <0x02 0x15>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + pinctrl-names = "default"; + pinctrl-0 = <0x15>; + clock-frequency = <0x186a0>; + phandle = <0x2b>; + }; + + usb@7e980000 { + compatible = "brcm,bcm2708-usb"; + reg = <0x7e980000 0x10000 0x7e006000 0x1000>; + interrupts = <0x01 0x09 0x02 0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + clocks = <0x16>; + clock-names = "otg"; + phys = <0x17>; + phy-names = "usb2-phy"; + interrupt-names = "usb\0soft"; + power-domains = <0x11 0x06>; + phandle = <0x7d>; + + usb-port@1 { + compatible = "usb424,2514"; + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + usb-port@1 { + compatible = "usb424,2514"; + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + ethernet@1 { + compatible = "usb424,7800"; + reg = <0x01>; + phandle = <0x7e>; + + mdio { + #address-cells = <0x01>; + #size-cells = <0x00>; + + ethernet-phy@1 { + reg = <0x01>; + microchip,led-modes = <0x01 0x06>; + microchip,eee-enabled; + microchip,tx-lpi-timer = <0x258>; + microchip,downshift-after = <0x02>; + phandle = <0x39>; + }; + }; + }; + }; + }; + }; + + dma-controller@7e007000 { + compatible = "brcm,bcm2835-dma"; + reg = <0x7e007000 0xf00>; + interrupts = <0x01 0x10 0x01 0x11 0x01 0x12 0x01 0x13 0x01 0x14 0x01 0x15 0x01 0x16 0x01 0x17 0x01 0x18 0x01 0x19 0x01 0x1a 0x01 0x1b 0x01 0x1b 0x01 0x1b 0x01 0x1b 0x01 0x1c>; + interrupt-names = "dma0\0dma1\0dma2\0dma3\0dma4\0dma5\0dma6\0dma7\0dma8\0dma9\0dma10\0dma11\0dma12\0dma13\0dma14\0dma-shared-all"; + #dma-cells = <0x01>; + brcm,dma-channel-mask = <0x7f35>; + phandle = <0x0c>; + }; + + interrupt-controller@7e00b200 { + compatible = "brcm,bcm2836-armctrl-ic"; + reg = <0x7e00b200 0x200>; + interrupt-controller; + #interrupt-cells = <0x02>; + interrupt-parent = <0x18>; + interrupts = <0x08 0x04>; + phandle = <0x01>; + }; + + watchdog@7e100000 { + compatible = "brcm,bcm2835-pm\0brcm,bcm2835-pm-wdt"; + #power-domain-cells = <0x01>; + #reset-cells = <0x01>; + reg = <0x7e100000 0x114 0x7e00a000 0x24>; + reg-names = "pm\0asb"; + clocks = <0x08 0x15 0x08 0x1d 0x08 0x17 0x08 0x16>; + clock-names = "v3d\0peri_image\0h264\0isp"; + system-power-controller; + phandle = <0x2c>; + }; + + rng@7e104000 { + compatible = "brcm,bcm2835-rng"; + reg = <0x7e104000 0x10>; + interrupts = <0x02 0x1d>; + phandle = <0x2d>; + }; + + pixelvalve@7e206000 { + compatible = "brcm,bcm2835-pixelvalve0"; + reg = <0x7e206000 0x100>; + interrupts = <0x02 0x0d>; + status = "disabled"; + phandle = <0x7f>; + }; + + pixelvalve@7e207000 { + compatible = "brcm,bcm2835-pixelvalve1"; + reg = <0x7e207000 0x100>; + interrupts = <0x02 0x0e>; + status = "disabled"; + phandle = <0x80>; + }; + + thermal@7e212000 { + compatible = "brcm,bcm2837-thermal"; + reg = <0x7e212000 0x08>; + clocks = <0x08 0x1b>; + #thermal-sensor-cells = <0x00>; + status = "okay"; + phandle = <0x02>; + }; + + i2c@7e805000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e805000 0x1000>; + interrupts = <0x02 0x15>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + clock-frequency = <0x186a0>; + phandle = <0x1a>; + }; + + vec@7e806000 { + compatible = "brcm,bcm2835-vec"; + reg = <0x7e806000 0x1000>; + clocks = <0x19 0x0f>; + interrupts = <0x02 0x1b>; + status = "disabled"; + power-domains = <0x11 0x07>; + phandle = <0x81>; + }; + + pixelvalve@7e807000 { + compatible = "brcm,bcm2835-pixelvalve2"; + reg = <0x7e807000 0x100>; + interrupts = <0x02 0x0a>; + status = "disabled"; + phandle = <0x82>; + }; + + hdmi@7e902000 { + compatible = "brcm,bcm2835-hdmi"; + reg = <0x7e902000 0x600 0x7e808000 0x100>; + interrupts = <0x02 0x08 0x02 0x09>; + ddc = <0x1a>; + clocks = <0x19 0x09 0x19 0x0d>; + clock-names = "pixel\0hdmi"; + dmas = <0x0c 0x9000011>; + dma-names = "audio-rx"; + status = "disabled"; + reg-names = "hdmi\0hd"; + power-domains = <0x11 0x05>; + hpd-gpios = <0x07 0x1c 0x01>; + phandle = <0x33>; + }; + + v3d@7ec00000 { + compatible = "brcm,vc4-v3d"; + reg = <0x7ec00000 0x1000>; + interrupts = <0x01 0x0a>; + power-domains = <0x11 0x0a>; + status = "disabled"; + phandle = <0x83>; + }; + + gpu { + compatible = "brcm,bcm2835-vc4"; + status = "disabled"; + raspberrypi,firmware = <0x06>; + phandle = <0x84>; + }; + + local_intc@40000000 { + compatible = "brcm,bcm2836-l1-intc"; + reg = <0x40000000 0x100>; + interrupt-controller; + #interrupt-cells = <0x02>; + interrupt-parent = <0x18>; + phandle = <0x18>; + }; + + mmcnr@7e300000 { + compatible = "brcm,bcm2835-mmc\0brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <0x02 0x1e>; + clocks = <0x08 0x1c>; + dmas = <0x0c 0x0b>; + dma-names = "rx-tx"; + brcm,overclock-50 = <0x00>; + non-removable; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <0x1b>; + bus-width = <0x04>; + #address-cells = <0x01>; + #size-cells = <0x00>; + phandle = <0x30>; + + wifi@1 { + reg = <0x01>; + compatible = "brcm,bcm4329-fmac"; + phandle = <0x85>; + }; + }; + + firmwarekms@7e600000 { + compatible = "raspberrypi,rpi-firmware-kms"; + reg = <0x7e600000 0x100>; + interrupts = <0x02 0x10>; + brcm,firmware = <0x06>; + status = "disabled"; + phandle = <0x86>; + }; + + smi@7e600000 { + compatible = "brcm,bcm2835-smi"; + reg = <0x7e600000 0x100>; + interrupts = <0x02 0x10>; + clocks = <0x08 0x2a>; + assigned-clocks = <0x08 0x2a>; + assigned-clock-rates = <0x7735940>; + dmas = <0x0c 0x04>; + dma-names = "rx-tx"; + status = "disabled"; + phandle = <0x87>; + }; + + csi@7e800000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e800000 0x800 0x7e802000 0x04>; + interrupts = <0x02 0x06>; + clocks = <0x08 0x2d 0x19 0x04>; + clock-names = "lp\0vpu"; + power-domains = <0x11 0x0c>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + status = "disabled"; + phandle = <0x88>; + }; + + csi@7e801000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e801000 0x800 0x7e802004 0x04>; + interrupts = <0x02 0x07>; + clocks = <0x08 0x2e 0x19 0x04>; + clock-names = "lp\0vpu"; + power-domains = <0x11 0x0d>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + status = "disabled"; + brcm,num-data-lanes = <0x02>; + phandle = <0x89>; + }; + + axiperf { + compatible = "brcm,bcm2835-axiperf"; + reg = <0x7e009800 0x100 0x7ee08000 0x100>; + firmware = <0x06>; + status = "disabled"; + phandle = <0x31>; + }; + + i2c0mux { + compatible = "i2c-mux-pinctrl"; + #address-cells = <0x01>; + #size-cells = <0x00>; + i2c-parent = <0x1c>; + status = "disabled"; + pinctrl-names = "i2c0\0i2c_csi_dsi"; + pinctrl-0 = <0x1d>; + pinctrl-1 = <0x1e>; + phandle = <0x2a>; + + i2c@0 { + reg = <0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + phandle = <0x8a>; + }; + + i2c@1 { + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + phandle = <0x8b>; + }; + }; + + firmware { + compatible = "raspberrypi,bcm2835-firmware\0simple-mfd"; + #address-cells = <0x01>; + #size-cells = <0x01>; + mboxes = <0x1f>; + dma-ranges; + phandle = <0x06>; + + clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <0x01>; + phandle = <0x19>; + }; + + vcio { + compatible = "raspberrypi,vcio"; + phandle = <0x8c>; + }; + + expgpio { + compatible = "raspberrypi,firmware-gpio"; + gpio-controller; + #gpio-cells = <0x02>; + gpio-line-names = "BT_ON\0WL_ON\0PWR_LED_R\0LAN_RUN\0NC\0CAM_GPIO0\0CAM_GPIO1\0NC"; + status = "okay"; + phandle = <0x0b>; + }; + }; + + power { + compatible = "raspberrypi,bcm2835-power"; + firmware = <0x06>; + #power-domain-cells = <0x01>; + phandle = <0x11>; + }; + + mailbox@7e00b840 { + compatible = "brcm,bcm2836-vchiq\0brcm,bcm2835-vchiq"; + reg = <0x7e00b840 0x3c>; + interrupts = <0x00 0x02>; + pinctrl-names = "default"; + pinctrl-0 = <0x20>; + phandle = <0x8d>; + }; + + gpiomem { + compatible = "brcm,bcm2835-gpiomem"; + reg = <0x7e200000 0x1000>; + }; + + fb { + compatible = "brcm,bcm2708-fb"; + firmware = <0x06>; + status = "okay"; + phandle = <0x8e>; + }; + + sound { + status = "disabled"; + phandle = <0x8f>; + }; + }; + + clocks { + + clk-osc { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-output-names = "osc"; + clock-frequency = <0x124f800>; + phandle = <0x03>; + }; + + clk-usb { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-output-names = "otg"; + clock-frequency = <0x1c9c3800>; + phandle = <0x16>; + }; + }; + + phy { + compatible = "usb-nop-xceiv"; + #phy-cells = <0x00>; + phandle = <0x17>; + }; + + arm-pmu { + compatible = "arm,cortex-a53-pmu\0arm,cortex-a7-pmu"; + interrupt-parent = <0x18>; + interrupts = <0x09 0x04>; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupt-parent = <0x18>; + interrupts = <0x00 0x04 0x01 0x04 0x03 0x04 0x02 0x04>; + always-on; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + enable-method = "brcm,bcm2836-smp"; + phandle = <0x90>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x00>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xd8>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x22>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x01>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xe0>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x23>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x02>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xe8>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x24>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x03>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xf0>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x25>; + }; + + l2-cache0 { + compatible = "cache"; + cache-unified; + cache-size = <0x80000>; + cache-line-size = <0x40>; + cache-sets = <0x200>; + cache-level = <0x02>; + phandle = <0x21>; + }; + }; + + cam1_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam1-reg"; + enable-active-high; + status = "okay"; + gpio = <0x0b 0x05 0x00>; + phandle = <0x91>; + }; + + cam1_clk { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + status = "disabled"; + phandle = <0x92>; + }; + + cam0_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam0-reg"; + enable-active-high; + status = "disabled"; + phandle = <0x93>; + }; + + cam0_clk { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + status = "disabled"; + phandle = <0x94>; + }; + + cam_dummy_reg { + compatible = "regulator-fixed"; + regulator-name = "cam-dummy-reg"; + status = "okay"; + phandle = <0x95>; + }; + + __overrides__ { + cam0-pwdn-ctrl; + cam0-pwdn; + cam0-led-ctrl; + cam0-led; + arm_freq = "\0\0\0\"clock-frequency:0\0\0\0\0#clock-frequency:0\0\0\0\0$clock-frequency:0\0\0\0\0%clock-frequency:0"; + cache_line_size; + uart0 = "\0\0\0&status"; + uart1 = "\0\0\0'status"; + i2s = "\0\0\0(status"; + spi = "\0\0\0)status"; + i2c0 = [00 00 00 1c 73 74 61 74 75 73 00 00 00 00 2a 73 74 61 74 75 73 00]; + i2c1 = "\0\0\0+status"; + i2c = "\0\0\0+status"; + i2c_arm = "\0\0\0+status"; + i2c_vc = [00 00 00 1c 73 74 61 74 75 73 00 00 00 00 2a 73 74 61 74 75 73 00]; + i2c0_baudrate = [00 00 00 1c 63 6c 6f 63 6b 2d 66 72 65 71 75 65 6e 63 79 3a 30 00]; + i2c1_baudrate = "\0\0\0+clock-frequency:0"; + i2c_baudrate = "\0\0\0+clock-frequency:0"; + i2c_arm_baudrate = "\0\0\0+clock-frequency:0"; + i2c_vc_baudrate = [00 00 00 1c 63 6c 6f 63 6b 2d 66 72 65 71 75 65 6e 63 79 3a 30 00]; + watchdog = "\0\0\0,status"; + random = "\0\0\0-status"; + sd_overclock = "\0\0\0.brcm,overclock-50:0"; + sd_force_pio = "\0\0\0.brcm,force-pio?"; + sd_pio_limit = "\0\0\0.brcm,pio-limit:0"; + sd_debug = "\0\0\0.brcm,debug"; + sdio_overclock = "\0\0\0/brcm,overclock-50:0\0\0\0\00brcm,overclock-50:0"; + axiperf = "\0\0\01status"; + drm_fb0_vc4 = "\0\0\02drm-fb0=\0/soc/gpu"; + drm_fb1_vc4 = "\0\0\02drm-fb1=\0/soc/gpu"; + drm_fb2_vc4 = "\0\0\02drm-fb2=\0/soc/gpu"; + hdmi = "\0\0\03status"; + i2c2_iknowwhatimdoing = [00 00 00 1a 73 74 61 74 75 73 00]; + i2c2_baudrate = [00 00 00 1a 63 6c 6f 63 6b 2d 66 72 65 71 75 65 6e 63 79 3a 30 00]; + sd = "\0\0\0.status"; + sd_poll_once = "\0\0\0.non-removable?"; + bdaddr = "\0\0\04local-bd-address[\0\0\0\04fallback-bd-address?=0\0\0\0\05local-bd-address[\0\0\0\05fallback-bd-address?=0"; + krnbt = "\0\0\04status"; + krnbt_baudrate = "\0\0\04max-speed:0\0\0\0\05max-speed:0"; + audio = "\0\0\06bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}"; + act_led_gpio = "\0\0\07gpios:4"; + act_led_activelow = "\0\0\07gpios:8"; + act_led_trigger = "\0\0\07linux,default-trigger"; + pwr_led_gpio = "\0\0\08gpios:4"; + pwr_led_activelow = "\0\0\08gpios:8"; + pwr_led_trigger = "\0\0\08linux,default-trigger"; + eee = "\0\0\09microchip,eee-enabled?"; + tx_lpi_timer = "\0\0\09microchip,tx-lpi-timer:0"; + eth_led0 = "\0\0\09microchip,led-modes:0"; + eth_led1 = "\0\0\09microchip,led-modes:4"; + eth_downshift_after = "\0\0\09microchip,downshift-after:0"; + eth_max_speed = "\0\0\09max-speed:0"; + }; + + fixedregulator_3v3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <0x325aa0>; + regulator-min-microvolt = <0x325aa0>; + regulator-name = "3v3"; + phandle = <0x96>; + }; + + fixedregulator_5v0 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <0x4c4b40>; + regulator-min-microvolt = <0x4c4b40>; + regulator-name = "5v0"; + phandle = <0x97>; + }; + + memory@0 { + device_type = "memory"; + reg = <0x00 0x00>; + }; + + leds { + compatible = "gpio-leds"; + phandle = <0x98>; + + led-act { + label = "ACT"; + default-state = "off"; + linux,default-trigger = "mmc0"; + gpios = <0x07 0x1d 0x00>; + phandle = <0x37>; + }; + + led-pwr { + label = "PWR"; + gpios = <0x0b 0x02 0x01>; + default-state = "off"; + linux,default-trigger = "default-on"; + phandle = <0x38>; + }; + }; + + __symbols__ { + aliases = "/aliases"; + chosen = "/chosen"; + rmem = "/reserved-memory"; + cma = "/reserved-memory/linux,cma"; + cpu_thermal = "/thermal-zones/cpu-thermal"; + thermal_trips = "/thermal-zones/cpu-thermal/trips"; + cooling_maps = "/thermal-zones/cpu-thermal/cooling-maps"; + soc = "/soc"; + system_timer = "/soc/timer@7e003000"; + txp = "/soc/txp@7e004000"; + clocks = "/soc/cprman@7e101000"; + mailbox = "/soc/mailbox@7e00b880"; + gpio = "/soc/gpio@7e200000"; + dpi_gpio0 = "/soc/gpio@7e200000/dpi-gpio0"; + emmc_gpio22 = "/soc/gpio@7e200000/emmc-gpio22"; + emmc_gpio34 = "/soc/gpio@7e200000/emmc-gpio34"; + emmc_gpio48 = "/soc/gpio@7e200000/emmc-gpio48"; + gpclk0_gpio4 = "/soc/gpio@7e200000/gpclk0-gpio4"; + gpclk1_gpio5 = "/soc/gpio@7e200000/gpclk1-gpio5"; + gpclk1_gpio42 = "/soc/gpio@7e200000/gpclk1-gpio42"; + gpclk1_gpio44 = "/soc/gpio@7e200000/gpclk1-gpio44"; + gpclk2_gpio6 = "/soc/gpio@7e200000/gpclk2-gpio6"; + gpclk2_gpio43 = "/soc/gpio@7e200000/gpclk2-gpio43"; + i2c0_gpio0 = "/soc/gpio@7e200000/i2c0if-gpio0"; + i2c0_gpio28 = "/soc/gpio@7e200000/i2c0if-gpio28"; + i2c0_gpio44 = "/soc/gpio@7e200000/i2c0if-gpio44"; + i2c1_gpio2 = "/soc/gpio@7e200000/i2c1-gpio2"; + i2c1_gpio44 = "/soc/gpio@7e200000/i2c1-gpio44"; + jtag_gpio22 = "/soc/gpio@7e200000/jtag-gpio22"; + pcm_gpio18 = "/soc/gpio@7e200000/pcm-gpio18"; + pcm_gpio28 = "/soc/gpio@7e200000/pcm-gpio28"; + sdhost_gpio48 = "/soc/gpio@7e200000/sdhost-gpio48"; + spi0_gpio7 = "/soc/gpio@7e200000/spi0-gpio7"; + spi0_gpio35 = "/soc/gpio@7e200000/spi0-gpio35"; + spi1_gpio16 = "/soc/gpio@7e200000/spi1-gpio16"; + spi2_gpio40 = "/soc/gpio@7e200000/spi2-gpio40"; + uart0_gpio14 = "/soc/gpio@7e200000/uart0-gpio14"; + uart0_ctsrts_gpio16 = "/soc/gpio@7e200000/uart0-ctsrts-gpio16"; + uart0_ctsrts_gpio30 = "/soc/gpio@7e200000/uart0-ctsrts-gpio30"; + uart0_gpio32 = "/soc/gpio@7e200000/uart0-gpio32"; + uart0_gpio36 = "/soc/gpio@7e200000/uart0-gpio36"; + uart0_ctsrts_gpio38 = "/soc/gpio@7e200000/uart0-ctsrts-gpio38"; + uart1_gpio14 = "/soc/gpio@7e200000/uart1-gpio14"; + uart1_ctsrts_gpio16 = "/soc/gpio@7e200000/uart1-ctsrts-gpio16"; + uart1_gpio32 = "/soc/gpio@7e200000/uart1-gpio32"; + uart1_ctsrts_gpio30 = "/soc/gpio@7e200000/uart1-ctsrts-gpio30"; + uart1_gpio40 = "/soc/gpio@7e200000/uart1-gpio40"; + uart1_ctsrts_gpio42 = "/soc/gpio@7e200000/uart1-ctsrts-gpio42"; + i2c_slave_gpio18 = "/soc/gpio@7e200000/i2c-slave-gpio18"; + jtag_gpio4 = "/soc/gpio@7e200000/jtag-gpio4"; + pwm0_gpio12 = "/soc/gpio@7e200000/pwm0-gpio12"; + pwm0_gpio18 = "/soc/gpio@7e200000/pwm0-gpio18"; + pwm0_gpio40 = "/soc/gpio@7e200000/pwm0-gpio40"; + pwm1_gpio13 = "/soc/gpio@7e200000/pwm1-gpio13"; + pwm1_gpio19 = "/soc/gpio@7e200000/pwm1-gpio19"; + pwm1_gpio41 = "/soc/gpio@7e200000/pwm1-gpio41"; + pwm1_gpio45 = "/soc/gpio@7e200000/pwm1-gpio45"; + dpi_18bit_cpadhi_gpio0 = "/soc/gpio@7e200000/dpi_18bit_cpadhi_gpio0"; + dpi_18bit_cpadhi_gpio2 = "/soc/gpio@7e200000/dpi_18bit_cpadhi_gpio2"; + dpi_18bit_gpio0 = "/soc/gpio@7e200000/dpi_18bit_gpio0"; + dpi_18bit_gpio2 = "/soc/gpio@7e200000/dpi_18bit_gpio2"; + dpi_16bit_gpio0 = "/soc/gpio@7e200000/dpi_16bit_gpio0"; + dpi_16bit_gpio2 = "/soc/gpio@7e200000/dpi_16bit_gpio2"; + dpi_16bit_cpadhi_gpio0 = "/soc/gpio@7e200000/dpi_16bit_cpadhi_gpio0"; + dpi_16bit_cpadhi_gpio2 = "/soc/gpio@7e200000/dpi_16bit_cpadhi_gpio2"; + gpioout = "/soc/gpio@7e200000/gpioout"; + alt0 = "/soc/gpio@7e200000/alt0"; + spi0_pins = "/soc/gpio@7e200000/spi0_pins"; + spi0_cs_pins = "/soc/gpio@7e200000/spi0_cs_pins"; + i2c0_pins = "/soc/gpio@7e200000/i2c0"; + i2c1_pins = "/soc/gpio@7e200000/i2c1"; + i2s_pins = "/soc/gpio@7e200000/i2s"; + sdio_pins = "/soc/gpio@7e200000/sdio_pins"; + bt_pins = "/soc/gpio@7e200000/bt_pins"; + uart0_pins = "/soc/gpio@7e200000/uart0_pins"; + uart1_pins = "/soc/gpio@7e200000/uart1_pins"; + uart1_bt_pins = "/soc/gpio@7e200000/uart1_bt_pins"; + audio_pins = "/soc/gpio@7e200000/audio_pins"; + uart0 = "/soc/serial@7e201000"; + bt = "/soc/serial@7e201000/bluetooth"; + sdhost = "/soc/mmc@7e202000"; + i2s_clk_consumer = "/soc/i2s@7e203000"; + i2s_clk_producer = "/soc/i2s@7e203000"; + i2s = "/soc/i2s@7e203000"; + spi0 = "/soc/spi@7e204000"; + spi = "/soc/spi@7e204000"; + spidev0 = "/soc/spi@7e204000/spidev@0"; + spidev1 = "/soc/spi@7e204000/spidev@1"; + i2c0if = "/soc/i2c@7e205000"; + dpi = "/soc/dpi@7e208000"; + dsi0 = "/soc/dsi@7e209000"; + aux = "/soc/aux@7e215000"; + uart1 = "/soc/serial@7e215040"; + minibt = "/soc/serial@7e215040/bluetooth"; + spi1 = "/soc/spi@7e215080"; + spi2 = "/soc/spi@7e2150c0"; + pwm = "/soc/pwm@7e20c000"; + mmc = "/soc/mmc@7e300000"; + sdhci = "/soc/mmc@7e300000"; + hvs = "/soc/hvs@7e400000"; + dsi1 = "/soc/dsi@7e700000"; + i2c_arm = "/soc/i2c@7e804000"; + i2c1 = "/soc/i2c@7e804000"; + usb = "/soc/usb@7e980000"; + ethernet = "/soc/usb@7e980000/usb-port@1/usb-port@1/ethernet@1"; + eth_phy = "/soc/usb@7e980000/usb-port@1/usb-port@1/ethernet@1/mdio/ethernet-phy@1"; + dma = "/soc/dma-controller@7e007000"; + intc = "/soc/interrupt-controller@7e00b200"; + watchdog = "/soc/watchdog@7e100000"; + pm = "/soc/watchdog@7e100000"; + random = "/soc/rng@7e104000"; + pixelvalve0 = "/soc/pixelvalve@7e206000"; + pixelvalve1 = "/soc/pixelvalve@7e207000"; + thermal = "/soc/thermal@7e212000"; + i2c2 = "/soc/i2c@7e805000"; + vec = "/soc/vec@7e806000"; + pixelvalve2 = "/soc/pixelvalve@7e807000"; + hdmi = "/soc/hdmi@7e902000"; + v3d = "/soc/v3d@7ec00000"; + vc4 = "/soc/gpu"; + local_intc = "/soc/local_intc@40000000"; + mmcnr = "/soc/mmcnr@7e300000"; + brcmf = "/soc/mmcnr@7e300000/wifi@1"; + firmwarekms = "/soc/firmwarekms@7e600000"; + smi = "/soc/smi@7e600000"; + csi0 = "/soc/csi@7e800000"; + csi1 = "/soc/csi@7e801000"; + axiperf = "/soc/axiperf"; + i2c0mux = "/soc/i2c0mux"; + i2c_csi_dsi0 = "/soc/i2c0mux/i2c@0"; + i2c_vc = "/soc/i2c0mux/i2c@0"; + i2c0 = "/soc/i2c0mux/i2c@0"; + i2c_csi_dsi = "/soc/i2c0mux/i2c@1"; + firmware = "/soc/firmware"; + firmware_clocks = "/soc/firmware/clocks"; + vcio = "/soc/firmware/vcio"; + expgpio = "/soc/firmware/expgpio"; + power = "/soc/power"; + vchiq = "/soc/mailbox@7e00b840"; + fb = "/soc/fb"; + sound = "/soc/sound"; + clk_osc = "/clocks/clk-osc"; + clk_usb = "/clocks/clk-usb"; + usbphy = "/phy"; + cpus = "/cpus"; + cpu0 = "/cpus/cpu@0"; + cpu1 = "/cpus/cpu@1"; + cpu2 = "/cpus/cpu@2"; + cpu3 = "/cpus/cpu@3"; + l2 = "/cpus/l2-cache0"; + cam1_reg = "/cam1_regulator"; + cam1_clk = "/cam1_clk"; + cam0_regulator = "/cam0_regulator"; + cam0_clk = "/cam0_clk"; + cam0_reg = "/cam_dummy_reg"; + cam_dummy_reg = "/cam_dummy_reg"; + vdd_3v3_reg = "/fixedregulator_3v3"; + vdd_5v0_reg = "/fixedregulator_5v0"; + leds = "/leds"; + led_act = "/leds/led-act"; + led_pwr = "/leds/led-pwr"; + }; +}; diff --git a/Lab4/peripherals/base.h b/Lab4/peripherals/base.h new file mode 100644 index 000000000..50281ad8b --- /dev/null +++ b/Lab4/peripherals/base.h @@ -0,0 +1,7 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +// RPi3B+ reserves the memory above address 0x3F000000 for devices. +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab4/peripherals/gpio.h b/Lab4/peripherals/gpio.h new file mode 100644 index 000000000..6f26401f2 --- /dev/null +++ b/Lab4/peripherals/gpio.h @@ -0,0 +1,13 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "base.h" + +// GPIO14 and GPIO15 are controlled by GPFSEL1(0x3F200004) +#define GPFSEL1 ((volatile unsigned int *)(PBASE+0x00200004)) +#define GPSET0 ((volatile unsigned int *)(PBASE+0x0020001C)) +#define GPCLR0 ((volatile unsigned int *)(PBASE+0x00200028)) +#define GPPUD ((volatile unsigned int *)(PBASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(PBASE+0x00200098)) + +#endif /*_P_GPIO_H */ diff --git a/Lab4/peripherals/mailbox.c b/Lab4/peripherals/mailbox.c new file mode 100644 index 000000000..149e3d652 --- /dev/null +++ b/Lab4/peripherals/mailbox.c @@ -0,0 +1,113 @@ +#include "mailbox.h" +#include "mini_uart.h" + +// Check messageBuffer status. +volatile unsigned int* const mailboxStatus = MAILBOX_STATUS; +volatile unsigned int* const mailboxRead = MAILBOX_READ; +volatile unsigned int* const mailboxWrite = MAILBOX_WRITE; + +// Message buffer must be 16-byte aligned. +unsigned int messageBuffer[8] __attribute__((aligned(16))); + + +void mailbox_write(unsigned int channel, unsigned int data) { + // Wait for the mailbox to become not full. + while (*mailboxStatus & MAILBOX_FULL); + // Write the data (with channel) to the mailbox. + *mailboxWrite = data | (channel & 0xF); +} + +// Nothing is written in mailbox for channel 8. However, when using other channels this value might have its +// uses. +unsigned int mailbox_read(unsigned int channel) { + unsigned int data; + + do { + // If mailbox isn't empty, read it. + while (*mailboxStatus & MAILBOX_EMPTY); + data = *mailboxRead; + + } while (channel != (data & 0xF)); // Check whether the response is meant for the channel. + + return (data & 0xFFFFFFF0); +} + +void get_board_revision() { + messageBuffer[0] = sizeof(messageBuffer); // buffer size in bytes + messageBuffer[1] = MAILBOX_REQUEST; + // tags begin + // Tag identifier: Specifies the type of request or information being queried or configured. + messageBuffer[2] = MAILBOX_TAG_GETBOARD; + + // Value buffer size: Indicates the size of the value buffer that follows, specifying how much space is allocated + // for the data related to this tag. It includes space for both the request data (if any) and + // the response data. + // Allocates 4 bytes, since board revision number is a 32-bit value. + messageBuffer[3] = 4; + + // Request/Response Size: Specifies the size of the request data(which may be 0 if no request data is needed), + // Upon processing the message, the GPU updates this field to reflect the size of the response data it has placed + // in the value buffer. + messageBuffer[4] = TAG_REQUEST_CODE; + + // Value Buffer: Contains the actual data for the request or response. The content and format of this data vary + // depending on the tag identifier. For a request, this might be parameters or configuration data; for a response, + // it's the information being returned by the GPU. + messageBuffer[5] = 0; + // tags end + // A zero value marks the end of the tags in the message. This signals to the GPU that there are no more tags to + // process in this message. + messageBuffer[6] = MAILBOX_TAG_END; + messageBuffer[7] = 0; // Unused for requesting board revision number. + + // Here we're passing the ADDRESS of messageBuffer, not the actual data stored in the buffer! + // 0xC0000000 is for bypassing the CPU cache, ensuring that the mailbox message is READ DIRECTLY from memory by the GPU. + mailbox_write(MAILBOX_CH_PROP, (unsigned int)(unsigned long)messageBuffer | 0xC0000000); + + // Wait until the messageBuffer is available. + while (messageBuffer[1] != MAILBOX_RESPONSE_SUCCESS); + + uart_send_string("Board revision: "); + uart_send_uint(messageBuffer[5]); // it should be 0xa020d3 for rpi3 b+ +} + +void get_arm_memory() { + messageBuffer[0] = sizeof(messageBuffer); // buffer size in bytes + messageBuffer[1] = MAILBOX_REQUEST; + // tags begin + // Tag identifier: Specifies the type of request or information being queried or configured. + messageBuffer[2] = MAILBOX_TAG_GETARMMEM; + + // Value buffer size: Indicates the size of the value buffer that follows, specifying how much space is allocated + // for the data related to this tag. It includes space for both the request data (if any) and + // the response data. + // Allocates 8 bytes, containing both base address and size. + messageBuffer[3] = 8; + + // Request/Response Size: Specifies the size of the request data(which may be 0 if no request data is needed), + // Upon processing the message, the GPU updates this field to reflect the size of the response data it has placed + // in the value buffer. + messageBuffer[4] = TAG_REQUEST_CODE; + + // Value Buffer: Contains the actual data for the request or response. The content and format of this data vary + // depending on the tag identifier. For a request, this might be parameters or configuration data; for a response, + // it's the information being returned by the GPU. + messageBuffer[5] = 0; + messageBuffer[6] = 0; + // tags end + // A zero value marks the end of the tags in the message. This signals to the GPU that there are no more tags to + // process in this message. + messageBuffer[7] = MAILBOX_TAG_END; + + // Here we're passing the ADDRESS of messageBuffer, not the actual data stored in the buffer! + // 0xC0000000 is for bypassing the CPU cache, ensuring that the mailbox message is READ DIRECTLY from memory by the GPU. + mailbox_write(MAILBOX_CH_PROP, (unsigned int)(unsigned long)messageBuffer | 0xC0000000); + // Wait until the messageBuffer is available. + while (messageBuffer[1] != MAILBOX_RESPONSE_SUCCESS); + + uart_send_string("ARM base memory address: 0x"); + uart_send_uint(messageBuffer[5]); + uart_send_string("\r\n"); + uart_send_string("ARM memory size: 0x"); + uart_send_uint(messageBuffer[6]); +} \ No newline at end of file diff --git a/Lab4/peripherals/mailbox.h b/Lab4/peripherals/mailbox.h new file mode 100644 index 000000000..8510e943e --- /dev/null +++ b/Lab4/peripherals/mailbox.h @@ -0,0 +1,41 @@ +#ifndef _MAILBOX_H_ +#define _MAILBOX_H_ + +#include "base.h" + +#define MAILBOX_REQUEST 0 + +/* channels */ +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +/* tags */ +#define TAG_REQUEST_CODE 0x00000000 +#define MAILBOX_TAG_GETBOARD 0x00010002 +#define MAILBOX_TAG_GETSERIAL 0x00010004 +#define MAILBOX_TAG_GETARMMEM 0x00010005 +#define MAILBOX_TAG_END 0x00000000 + +#define MAILBOX_BASE PBASE + 0xb880 +#define MAILBOX_READ ((volatile unsigned int*)MAILBOX_BASE) +#define MAILBOX_POLL ((volatile unsigned int*)(MAILBOX_BASE + 0x10)) +#define MAILBOX_SENDER ((volatile unsigned int*)(MAILBOX_BASE + 0x14)) +#define MAILBOX_STATUS ((volatile unsigned int*)(MAILBOX_BASE + 0x18)) +#define MAILBOX_CONFIG ((volatile unsigned int*)(MAILBOX_BASE + 0x1C)) +#define MAILBOX_WRITE ((volatile unsigned int*)(MAILBOX_BASE + 0x20)) +#define MAILBOX_EMPTY 0x40000000 // empty flag is represented by bit 30. Bit 30 is set(1), the mailbox is empty. +#define MAILBOX_FULL 0x80000000 // full flag is represented by bit 31. Bit 31 is set(1), the mailbox is full. +#define MAILBOX_RESPONSE_SUCCESS 0x80000000 // mailbox writes this to the message buffer if response success. +#define MAILBOX_RESPONSE_FAIL 0x80000001 // mailbox writes this to the message buffer if response fail. + +void get_board_revision(); +void get_arm_memory(); + +#endif \ No newline at end of file diff --git a/Lab4/peripherals/mini_uart.c b/Lab4/peripherals/mini_uart.c new file mode 100644 index 000000000..292cce7fb --- /dev/null +++ b/Lab4/peripherals/mini_uart.c @@ -0,0 +1,258 @@ +#include "mm.h" +#include "gpio.h" +#include "mini_uart.h" +#include "utils.h" + +void uart_init (void) +{ + unsigned int selector; + + // Set the bits in register GPFSEL1 for gpio14 and gpio15, configuring them + // for the use of mini UART. + selector = *GPFSEL1; + selector &= ~(7u << 12); // clean gpio14 + selector |= 2u << 12; // set alt5 for gpio14 + selector &= ~(7u << 15); // clean gpio15 + selector |= 2u << 15; // set alt5 for gpio 15 + *GPFSEL1 = selector; + + // Switching the states of the pin. + // Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither + // to remove the current Pull-up/down). + *GPPUD = 0u; + + // Wait 150 cycles – this provides the required set-up time for the control signal. + // delay(150); + delay(150u); + + // Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to + // modify – NOTE only the pads which receive a clock will be modified, all others + // will retain their previous state. + *GPPUDCLK0 = (1u << 14) | (1u << 15); + delay(150); + + // Write to GPPUDCLK0/1 to remove the clock + *GPPUDCLK0 = 0u; + + // Enabling mini uart + *AUX_ENABLES = 1u; // Enable mini uart (this also enables access to its registers) + *AUX_MU_CNTL_REG = 0u; // Disable auto flow control and disable receiver and transmitter (for now) + *AUX_MU_IER_REG = 0u; // Disable receive and transmit interrupts + *AUX_MU_LCR_REG = 3u; // Enable 8 bit mode + *AUX_MU_MCR_REG = 0u; // Set RTS line to be always high + *AUX_MU_BAUD_REG = 270u; // Set baud rate to 115200(meaning the serial port is capable of transferring + // a maximum of 115200 bits per second) + // baudrate = (system_clk_freq = 250MHz) / (8 * (baudrate_reg + 1)) + *AUX_MU_IIR_REG = 6u; + *AUX_MU_CNTL_REG = 3u; //Finally, enable transmitter and receiver +} + +void uart_send (char c) +{ + // bit_5 == 1 -> writable + // 0x20 = 0000 0000 0010 0000 + while (!((*AUX_MU_LSR_REG) & 0x20)); + *AUX_MU_IO_REG = c; +} + +char uart_recv (void) +{ + // bit_0 == 1 -> readable + // 0x01 = 0000 0000 0000 0001 + while (!((*AUX_MU_LSR_REG) & 0x01)); + + return (*AUX_MU_IO_REG & 0xFF); +} + +void uart_send_string(char* str) +{ + for (int i = 0; str[i] != '\0'; i++) { + uart_send((char)str[i]); + } +} + +// Convert unsigned int to string and send it to uart. +void uart_send_uint(unsigned long data) { + + // 64-bit + char str[17]; + int isZero = 1; + for (int i = 0; i < 16; i++) { + // Take the LS 4 bits. + int value = data & 0xF; + str[15 - i] = int2char(value); + data = data >> 4; + } + + for (int i = 0; i < 16; i++) { + // Don't print the preceding zeros. + if (str[i] != '0') { + isZero = 0; + } + + if (!isZero) { + uart_send(str[i]); + } + } + + // If the value is zero. + if (isZero) + uart_send('0'); + // uart_send_string("\r\n"); +} + +void uart_send_int(unsigned long data) { + char str[30]; + int isZero = 1; + + for (int i = 0; i < 30; i++) { + // Convert to ascii code. '0' <-> 48 + str[30 - 1 - i] = (data % 10) + 48; + data /= 10; + } + + for (int i = 0; i < 30; i++) { + if (str[i] != '0') { + isZero = 0; + } + + if (!isZero) { + uart_send(str[i]); + } + } + + if (isZero) { + uart_send('0'); + } + +} + +void enable_uart_interrupt(void) { + /* + * Manual p.12 + * If AUX_MU_IER_REG[0] is set, receive interrupts are enabled. + * If AUX_MU_IER_REG[1] is set, transmit interrupts are enabled. + */ + + *AUX_MU_IER_REG |= 3u; + + // Enable second level interrupt controller. + volatile unsigned int* IRQ_ENABLE_S1 = (volatile unsigned int *)(PBASE + 0x0000B210); + *IRQ_ENABLE_S1 |= (1 << 29); +} + +void disable_uart_interrupt(void) { + + // Disable both receive and transmit interrupts. + *AUX_MU_IER_REG = 0u; +} + +// #define ASYNC_BUF_SIZE 256 +// volatile char rx_buf_async[ASYNC_BUF_SIZE]; +// volatile int rx_buf_head = 0; +// volatile int rx_buf_tail = 0; + +// volatile char tx_buf_async[ASYNC_BUF_SIZE]; +// volatile int tx_buf_head = 0; +// volatile int tx_buf_tail = 0; + +// void handle_uart_interrupt() { + +// // disable_uart_interrupt(); + +// // On manual P.13 +// // Receive interrupt.(Receiver holds valid byte) +// if (*AUX_MU_IIR_REG & 0x4) { + +// // Read the least 8-bits(1 byte, the length of char) in IO register. +// // Indicating data is sent from laptop. +// char c = *AUX_MU_IO_REG & 0xFF; +// int next = (rx_buf_tail + 1) % ASYNC_BUF_SIZE; + +// // Check buffer overflow. +// if (next != rx_buf_head) { +// rx_buf_async[rx_buf_tail] = c; +// rx_buf_tail = next; +// } +// } +// /* +// // Transmit interrupt.(Transmit register empty) +// else if ((*AUX_MU_IIR_REG & 0x2) && (tx_buf_head != tx_buf_tail)) { +// *AUX_MU_IO_REG = tx_buf_async[tx_buf_head]; +// tx_buf_head = (tx_buf_head + 1) % ASYNC_BUF_SIZE; +// } +// */ + +// // enable_uart_interrupt(); + +// } + +// int uart_recv_async(char* buf, int buf_size) { +// int bytes_read = 0; + +// // Write the data within the RX buffer into buf. +// // Continue writing until the RX buffer is empty or exceed buf_size. +// while ((rx_buf_head != rx_buf_tail) && (bytes_read < buf_size)) { +// buf[bytes_read++] = rx_buf_async[rx_buf_head]; +// rx_buf_head = (rx_buf_head + 1) % ASYNC_BUF_SIZE; +// } + +// return bytes_read; +// } + +// void uart_send_async(const char* data, int len) { +// for (int i = 0; i < len; i++) { +// int next = (tx_buf_tail + 1) % ASYNC_BUF_SIZE; + +// // Wait until Transmit buffer isn't full. +// if (next == tx_buf_head) { +// continue; +// } + +// tx_buf_async[tx_buf_tail] = data[i]; +// tx_buf_tail = next; +// } + +// if (tx_buf_head == tx_buf_tail) { +// // Disable TX interrupt if no data +// *AUX_MU_IER_REG &= ~0x2; +// } else { +// *AUX_MU_IER_REG |= 0x2; +// } +// } + +#define BUFFER_SIZE 256 +volatile char tx_buffer[BUFFER_SIZE]; +volatile unsigned int tx_head = 0; +volatile unsigned int tx_tail = 0; + +// UART interrupt handler +void handle_uart_interrupt(void* data) { + // Check if the interrupt is for a received character + if (*AUX_MU_IIR_REG & 0x4) { // Receiver holds a valid byte + char received_char = *AUX_MU_IO_REG & 0xFF; // Read the received byte + uart_send_async(received_char); // Echo it back immediately or queue + } + + // Transmit buffer management + if ((*AUX_MU_IIR_REG & 0x2) && (tx_head != tx_tail)) { // Transmit register empty + *AUX_MU_IO_REG = tx_buffer[tx_tail]; // Send next character + tx_tail = (tx_tail + 1) % BUFFER_SIZE; // Move tail forward + } +} + +// Queue a character for transmission or send immediately if ready +void uart_send_async(char c) { + // Attempt immediate transmission if UART is ready and buffer is empty + if ((*AUX_MU_LSR_REG & 0x20) && (tx_head == tx_tail)) { + *AUX_MU_IO_REG = c; // Transmit immediately + } else { + // Otherwise, queue the character + unsigned int next_head = (tx_head + 1) % BUFFER_SIZE; + if (next_head != tx_tail) { // Ensure the buffer isn't full + tx_buffer[tx_head] = c; + tx_head = next_head; + *AUX_MU_IER_REG |= 0x2; // Ensure transmit interrupts are enabled + } + } +} \ No newline at end of file diff --git a/Lab4/peripherals/mini_uart.h b/Lab4/peripherals/mini_uart.h new file mode 100644 index 000000000..de5194ff5 --- /dev/null +++ b/Lab4/peripherals/mini_uart.h @@ -0,0 +1,38 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +#include "base.h" + +#define AUX_IRQ ((volatile unsigned int*)(PBASE+0x00215000)) +#define AUX_ENABLES ((volatile unsigned int*)(PBASE+0x00215004)) +#define AUX_MU_IO_REG ((volatile unsigned int*)(PBASE+0x00215040)) // Used to write data to and read data from the uart FIFOs. +#define AUX_MU_IER_REG ((volatile unsigned int*)(PBASE+0x00215044)) // Used to enable interrupts. +#define AUX_MU_IIR_REG ((volatile unsigned int*)(PBASE+0x00215048)) // Shows the interrupt status. +#define AUX_MU_LCR_REG ((volatile unsigned int*)(PBASE+0x0021504C)) // Controls the line data format and gives access to the baudrate register. +#define AUX_MU_MCR_REG ((volatile unsigned int*)(PBASE+0x00215050)) // Controls the "modem" signals. +#define AUX_MU_LSR_REG ((volatile unsigned int*)(PBASE+0x00215054)) // Shows data status. +#define AUX_MU_MSR_REG ((volatile unsigned int*)(PBASE+0x00215058)) // Shows modem status. +#define AUX_MU_SCRATCH ((volatile unsigned int*)(PBASE+0x0021505C)) // Single byte storage. +#define AUX_MU_CNTL_REG ((volatile unsigned int*)(PBASE+0x00215060)) // Provides access to some extra useful and nice features not found on a normal 16550 uart. +#define AUX_MU_STAT_REG ((volatile unsigned int*)(PBASE+0x00215064)) // Provides a lot of useful information about the internal status of the mini uart not found on + // a normal 16550 uart. +#define AUX_MU_BAUD_REG ((volatile unsigned int*)(PBASE+0x00215068)) // Allows direct access to the 16-bit wide baudrate counter. + + + +void uart_init (void); +char uart_recv (void); +void uart_send (char c); +void uart_send_string(char* str); +// For hexadecimal. +void uart_send_uint(unsigned long data); +// For decimal. +void uart_send_int(unsigned long data); +void enable_uart_interrupt(void); +void disable_uart_interrupt(void); +void handle_uart_interrupt(void* data); +int uart_recv_async(char* buf, int buf_size); +// void uart_send_async(const char* data, int len); +void uart_send_async(char c); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab4/peripherals/mm.h b/Lab4/peripherals/mm.h new file mode 100644 index 000000000..7e4517348 --- /dev/null +++ b/Lab4/peripherals/mm.h @@ -0,0 +1,11 @@ +#ifndef _MM_H +#define _MM_H + +// Heap end address: 0x1195B88 +// Allocate 1MB for stack: 0x1195B88 + 0x100000 = 0x1295B88 +#define LOW_MEMORY 0x400000 // Place the bootloader sp at 0x400000 +#define USER_STACK (1 << 23) // sp for a user program.(Ranging from 0x800000 to 0x400000) + + + +#endif /*_MM_H */ \ No newline at end of file diff --git a/Lab4/peripherals/utils.c b/Lab4/peripherals/utils.c new file mode 100644 index 000000000..aaee9aec6 --- /dev/null +++ b/Lab4/peripherals/utils.c @@ -0,0 +1,132 @@ +#include "utils.h" + +int strcmp(const char* str1, const char* str2) { + for (; *str1 == *str2; str1++, str2++) { + if (*str1 == '\0') + return 0; + } + return (unsigned char)*str1 - (unsigned char)*str2; +} + +char* strcpy(char* destination, const char* source) { + int ind = 0; + for (; source[ind] != '\0'; ind++) { + destination[ind] = source[ind]; + } + destination[ind] = '\0'; + + return destination; +} + +// Convert integers to characters. +char int2char(int data) { + switch (data) { + case 10: + return 'A'; + case 11: + return 'B'; + case 12: + return 'C'; + case 13: + return 'D'; + case 14: + return 'E'; + case 15: + return 'F'; + default: + // ascii code of '0' is 48. + return data + 48; + } +} + +// Convert hexadecimal strings to unsigned integers. Pass in the string array and the length of +// the string. +unsigned int strHex2Int(char* str, int len) { + unsigned int tot = 0; + + for (int i = 0; i < len; i++) { + switch (str[i]) { + case 'A': + tot += 10 * power(16, len - i - 1); + break; + case 'B': + tot += 11 * power(16, len - i - 1); + break; + case 'C': + tot += 12 * power(16, len - i - 1); + break; + case 'D': + tot += 13 * power(16, len - i - 1); + break; + case 'E': + tot += 14 * power(16, len - i - 1); + break; + case 'F': + tot += 15 * power(16, len - i - 1); + break; + default: + // Ascii code for '0' is 48. + tot += (str[i] - 48) * power(16, len - i - 1); + } + } + + return tot; +} + +unsigned int str2Int(char* str, int len) { + unsigned int tot = 0; + + for (int i = 0; i < len; i++) { + switch (str[i]) { + case 'A': + tot += 10 * power(10, len - i - 1); + break; + case 'B': + tot += 11 * power(10, len - i - 1); + break; + case 'C': + tot += 12 * power(10, len - i - 1); + break; + case 'D': + tot += 13 * power(10, len - i - 1); + break; + case 'E': + tot += 14 * power(10, len - i - 1); + break; + case 'F': + tot += 15 * power(10, len - i - 1); + break; + default: + // Ascii code for '0' is 48. + tot += (str[i] - 48) * power(10, len - i - 1); + } + } + + return tot; +} + +// Calculating base to the power of p. +unsigned int power(unsigned int base, int p) { + unsigned int tot = 1; + for (int i = 0; i < p; i++) { + tot *= base; + } + return tot; +} + +long unsigned int strlen(const char* str) { + int i = 0; + while (str[i] != '\0') { + i++; + } + + return i; +} + +void delay(unsigned int clock) { + while (clock--) { + asm volatile ( + "nop;" + ); + } +} \ No newline at end of file diff --git a/Lab4/peripherals/utils.h b/Lab4/peripherals/utils.h new file mode 100644 index 000000000..2bcd845b2 --- /dev/null +++ b/Lab4/peripherals/utils.h @@ -0,0 +1,16 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include + +int strcmp(const char* str1, const char* str2); +char* uint_to_string(unsigned int data); +char int2char(int data); +void delay(unsigned int time); +unsigned int power(unsigned int base, int p); +unsigned int strHex2Int(char* str, int len); +unsigned int str2Int(char* str, int len); +char* strcpy(char* destination, const char* source); +long unsigned int strlen(const char* str); + +#endif \ No newline at end of file diff --git a/Lab4/rootfs/test1.txt b/Lab4/rootfs/test1.txt new file mode 100644 index 000000000..737c7c293 --- /dev/null +++ b/Lab4/rootfs/test1.txt @@ -0,0 +1 @@ +This is text1. \ No newline at end of file diff --git a/Lab4/rootfs/test2.txt b/Lab4/rootfs/test2.txt new file mode 100644 index 000000000..37626f1e1 --- /dev/null +++ b/Lab4/rootfs/test2.txt @@ -0,0 +1 @@ +This is text2. \ No newline at end of file diff --git a/Lab4/rootfs/test_user.img b/Lab4/rootfs/test_user.img new file mode 100755 index 000000000..1adf648a4 Binary files /dev/null and b/Lab4/rootfs/test_user.img differ diff --git a/Lab4/rootfs2/dir1/sub_dir1/test b/Lab4/rootfs2/dir1/sub_dir1/test new file mode 100644 index 000000000..793354cac --- /dev/null +++ b/Lab4/rootfs2/dir1/sub_dir1/test @@ -0,0 +1,2 @@ +This is test! +Newline. \ No newline at end of file diff --git a/Lab4/rootfs2/testRoot b/Lab4/rootfs2/testRoot new file mode 100644 index 000000000..d5fb86f42 --- /dev/null +++ b/Lab4/rootfs2/testRoot @@ -0,0 +1 @@ +This is testRoot. \ No newline at end of file diff --git a/Lab4/send_image.py b/Lab4/send_image.py new file mode 100644 index 000000000..6c7870c45 --- /dev/null +++ b/Lab4/send_image.py @@ -0,0 +1,40 @@ +import serial +import time +import os + +# Configuration +serial_port = '/dev/ttyUSB0' # Adjust this to your system's serial port +# serial_port = '/dev/tty.PL2303-USBtoUART1120' # Adjust this to your system's serial port +baud_rate = 115200 # Adjust baud rate as needed +kernel_image_path = 'build/kernel/kernel8.img' # Path to your kernel image + +# Function to send a file through serial +def send_file(file_path, port, baud): + try: + with serial.Serial(port, baud, timeout=1) as ser: + # Get file size + file_size = os.path.getsize(file_path) + print(f"File size: {file_size} bytes") + + # Send file size (4 bytes, little-endian format) + ser.write(file_size.to_bytes(4, byteorder='little')) + time.sleep(0.5) # Give the receiver a moment to process the size + + with open(file_path, 'rb') as file: + print("Sending file...") + while True: + chunk = file.read(1024) # Read in chunks of 1024 bytes + if not chunk: + break # End of file + ser.write(chunk) + time.sleep(0.02) # Short delay to ensure data is transmitted properly + print("File sent successfully.") + + except serial.SerialException as e: + print(f"Error: {e}") + except FileNotFoundError: + print(f"Error: File {file_path} not found.") + +# Main function +if __name__ == '__main__': + send_file(kernel_image_path, serial_port, baud_rate) \ No newline at end of file diff --git a/Lab4/test_user/Makefile b/Lab4/test_user/Makefile new file mode 100644 index 000000000..1d1087f80 --- /dev/null +++ b/Lab4/test_user/Makefile @@ -0,0 +1,36 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -I../include -I../peripherals -fno-stack-protector -g -O0 +ASFLAGS = + +# Adjust these paths as necessary +SRC_C = $(wildcard *.c) +SRC_S = $(wildcard *.S) +OBJS = $(SRC_C:%.c=%.o) $(SRC_S:%.S=%.o) + +# Include peripherals objects +PERIPHERALS_OBJS = $(wildcard $(BUILD_DIR)/peripherals/*.o) +LINKER_SCRIPT = linker.ld +KERNEL8_ELF = test_user.elf +KERNEL8_IMG = test_user.img + +all: $(OBJS) kernel + +%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +kernel: $(OBJS) $(PERIPHERALS_OBJS) + $(LD) -T $(LINKER_SCRIPT) -o $(KERNEL8_ELF) $^ + $(OBJCOPY) -O binary $(KERNEL8_ELF) $(KERNEL8_IMG) + +clean: + rm test_user.o test_user.elf test_user.img + +.PHONY: all kernel diff --git a/Lab4/test_user/linker.ld b/Lab4/test_user/linker.ld new file mode 100644 index 000000000..bb6c709cd --- /dev/null +++ b/Lab4/test_user/linker.ld @@ -0,0 +1,15 @@ +SECTIONS +{ + . = 0x500000; + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + # Use NOLOAD in .bss ensures that this section is allocated space in the memory layout without + # attempting to load any pre-initialized data from the binary. + .bss (NOLOAD) : { + . = ALIGN(0x8); + bss_begin = .; + *(.bss*) + bss_end = .; + } +} \ No newline at end of file diff --git a/Lab4/test_user/test_user.S b/Lab4/test_user/test_user.S new file mode 100644 index 000000000..ce7e53908 --- /dev/null +++ b/Lab4/test_user/test_user.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/Lab4/test_user/test_user.elf b/Lab4/test_user/test_user.elf new file mode 100755 index 000000000..2ff4f2991 Binary files /dev/null and b/Lab4/test_user/test_user.elf differ diff --git a/Lab4/test_user/test_user.img b/Lab4/test_user/test_user.img new file mode 100755 index 000000000..1adf648a4 Binary files /dev/null and b/Lab4/test_user/test_user.img differ diff --git a/Lab4/test_user/test_user.o b/Lab4/test_user/test_user.o new file mode 100644 index 000000000..92e44ca8a Binary files /dev/null and b/Lab4/test_user/test_user.o differ diff --git a/Lab5/.vscode/settings.json b/Lab5/.vscode/settings.json new file mode 100644 index 000000000..efafeb2f6 --- /dev/null +++ b/Lab5/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "utils.h": "c" + } +} \ No newline at end of file diff --git a/Lab5/DSLibrary/doubly_list.c b/Lab5/DSLibrary/doubly_list.c new file mode 100644 index 000000000..a6c128475 --- /dev/null +++ b/Lab5/DSLibrary/doubly_list.c @@ -0,0 +1,92 @@ +#include "doubly_list.h" +#include "../peripherals/utils.h" +#include "../kernel/dynamic_alloc.h" + +void init_list(doubly_linked_list_t* list) { + list->head = NULL; +} + +void insert_at_start(doubly_linked_list_node_t** head, void* data) { + doubly_linked_list_node_t* temp = (doubly_linked_list_node_t*)dynamic_alloc(sizeof(doubly_linked_list_node_t)); + if (!temp) return; // Handle allocation failure + + temp->data = data; + temp->next = *head; + temp->prev = NULL; + + if (*head != NULL) { + (*head)->prev = temp; + } + + *head = temp; +} + +void insert_at_end(doubly_linked_list_node_t** head, void* data) { + doubly_linked_list_node_t* temp = (doubly_linked_list_node_t*)dynamic_alloc(sizeof(doubly_linked_list_node_t)); + if (!temp) return; // Handle allocation failure + + temp->data = data; + temp->next = NULL; + + if (*head == NULL) { + temp->prev = NULL; + *head = temp; + return; + } + + doubly_linked_list_node_t* ptr = *head; + while (ptr->next != NULL) { + ptr = ptr->next; + } + + ptr->next = temp; + temp->prev = ptr; +} + +void insert_before(doubly_linked_list_node_t** head, doubly_linked_list_node_t* fixNode, void* data) { + if (!head || !fixNode) return; + + doubly_linked_list_node_t* temp = (doubly_linked_list_node_t*)dynamic_alloc(sizeof(doubly_linked_list_node_t)); + if (!temp) return; // Handle allocation failure + + temp->data = data; + + if (*head == fixNode) { + temp->next = *head; + (*head)->prev = temp; + *head = temp; + return; + } + + doubly_linked_list_node_t* ptr = *head; + while (ptr != NULL && ptr->next != fixNode) { + ptr = ptr->next; + } + + if (ptr == NULL) return; // fixNode not found in the list + + temp->next = fixNode; + temp->prev = ptr; + ptr->next = temp; + fixNode->prev = temp; +} + +void delete_node(doubly_linked_list_node_t** head, doubly_linked_list_node_t* nodeToDelete) { + if (*head == NULL || nodeToDelete == NULL) { + return; + } + + if (*head == nodeToDelete) { + *head = nodeToDelete->next; + } + + if (nodeToDelete->next != NULL) { + nodeToDelete->next->prev = nodeToDelete->prev; + } + + if (nodeToDelete->prev != NULL) { + nodeToDelete->prev->next = nodeToDelete->next; + } + + dynamic_free((uint64_t)nodeToDelete); +} diff --git a/Lab5/DSLibrary/doubly_list.h b/Lab5/DSLibrary/doubly_list.h new file mode 100644 index 000000000..c295cefd1 --- /dev/null +++ b/Lab5/DSLibrary/doubly_list.h @@ -0,0 +1,20 @@ +#ifndef _DOUBLY_LIST_H_ +#define _DOUBLY_LIST_H_ + +typedef struct doubly_linked_list_node { + struct doubly_linked_list_node* next; + struct doubly_linked_list_node* prev; + void* data; +} doubly_linked_list_node_t; + +typedef struct doubly_linked_list { + doubly_linked_list_node_t* head; +} doubly_linked_list_t; + +void init_list(doubly_linked_list_t* list); +void insert_at_start(doubly_linked_list_node_t** head, void* data); +void insert_at_end(doubly_linked_list_node_t** head, void* data); +void insert_before(doubly_linked_list_node_t **head, doubly_linked_list_node_t* fixNode, void* data); +void delete_node(doubly_linked_list_node_t **head, doubly_linked_list_node_t *nodeToDelete); + +#endif \ No newline at end of file diff --git a/Lab5/DSLibrary/queue.c b/Lab5/DSLibrary/queue.c new file mode 100644 index 000000000..aadfe8225 --- /dev/null +++ b/Lab5/DSLibrary/queue.c @@ -0,0 +1,33 @@ +#include "queue.h" + +void init_queue(Queue* q) { + init_list(&q->list); +} + +void enqueue(Queue* queue, void* data) { + insert_at_end(&queue->list.head, data); +} + +void* dequeue(Queue* queue) { + if (queue->list.head == NULL) { + return NULL; // Return NULL if the queue is empty. + } + + // Get the data from the head of the list to return it + void* data = queue->list.head->data; + + // Call delete_node to remove the head node properly + delete_node(&queue->list.head, queue->list.head); + + return data; // Return the data from the removed node +} + +int queue_element_count(Queue* queue) { + int cnt = 0; + doubly_linked_list_node_t* node = queue->list.head; + while (node != NULL) { + cnt++; + node = node->next; + } + return cnt; +} diff --git a/Lab5/DSLibrary/queue.h b/Lab5/DSLibrary/queue.h new file mode 100644 index 000000000..f346ae139 --- /dev/null +++ b/Lab5/DSLibrary/queue.h @@ -0,0 +1,16 @@ +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +#include +#include "doubly_list.h" + +typedef struct queue { + doubly_linked_list_t list; // Using the doubly linked list to store queue elements +} Queue; + +void init_queue(Queue* q); +void enqueue(Queue *queue, void *data); +void* dequeue(Queue *queue); +int queue_element_count(Queue* queue); + +#endif \ No newline at end of file diff --git a/Lab5/Makefile b/Lab5/Makefile new file mode 100644 index 000000000..38c22953c --- /dev/null +++ b/Lab5/Makefile @@ -0,0 +1,51 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +CFLAGS = -Iinclude -Iperipherals -IDSLibrary -fno-stack-protector -g +ASFLAGS = +BUILD_DIR = build + +# Define peripherals source files +PERIPHERALS_SRC_C = $(wildcard peripherals/*.c) +PERIPHERALS_SRC_S = $(wildcard peripherals/*.S) +PERIPHERALS_OBJS = $(PERIPHERALS_SRC_C:%.c=$(BUILD_DIR)/%.o) $(PERIPHERALS_SRC_S:%.S=$(BUILD_DIR)/%.o) + +# Define DSLibrary source files +DSLIBRARY_SRC_C = $(wildcard DSLibrary/*.c) +DSLIBRARY_SRC_S = $(wildcard DSLibrary/*.S) +DSLIBRARY_OBJS = $(DSLIBRARY_SRC_C:%.c=$(BUILD_DIR)/%.o) $(DSLIBRARY_SRC_S:%.S=$(BUILD_DIR)/%.o) + +# Target for peripherals +$(BUILD_DIR)/peripherals/%.o: peripherals/%.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/peripherals/%.o: peripherals/%.S + @mkdir -p $(@D) + $(AS) $(ASFLAGS) $< -o $@ + +# Target for DSLibrary +$(BUILD_DIR)/DSLibrary/%.o: DSLibrary/%.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/DSLibrary/%.o: DSLibrary/%.S + @mkdir -p $(@D) + $(AS) $(ASFLAGS) $< -o $@ + +# all: peripherals bootloader kernel +all: peripherals DSLibrary kernel bootloader + +peripherals: $(PERIPHERALS_OBJS) + +DSLibrary: $(DSLIBRARY_OBJS) + +kernel: + $(MAKE) -C kernel BUILD_DIR=../$(BUILD_DIR) + +bootloader: + $(MAKE) -C bootloader BUILD_DIR=../$(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR) + +.PHONY: all peripherals kernel bootloader clean diff --git a/Lab5/README b/Lab5/README new file mode 100644 index 000000000..328e42a5a --- /dev/null +++ b/Lab5/README @@ -0,0 +1,47 @@ +The cpio archive format collects any number of files, directories, and +other file system objects (symbolic links, device nodes, etc.) into a +single stream of bytes. + + +CPIO New ASCII Format + +The "new" ASCII format uses 8-byte hexadecimal fields for all numbers +and separates device numbers into separate fields for major and minor +numbers. + +struct cpio_newc_header { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +}; + +Except as specified below, the fields here match those specified for +the new binary format above. + +magic The string "070701". + +check This field is always set to zero by writers and ignored by + readers. See the next section for more details. + +The pathname is followed by NUL bytes so that the total size of the +fixed header plus pathname is a multiple of four. Likewise, the file +data is padded to a multiple of four bytes. Note that this format sup- +ports only 4 gigabyte files (unlike the older ASCII format, which sup- +ports 8 gigabyte files). + +In this format, hardlinked files are handled by setting the filesize to +zero for each entry except the first one that appears in the archive. + +create a cpio archive: + find . | cpio -o -H newc > ../initramfs.cpio \ No newline at end of file diff --git a/Lab5/a.out b/Lab5/a.out new file mode 100755 index 000000000..4d8ce7207 Binary files /dev/null and b/Lab5/a.out differ diff --git a/Lab5/archive/bcm2710-rpi-3-b-plus.dtb b/Lab5/archive/bcm2710-rpi-3-b-plus.dtb new file mode 100755 index 000000000..c83b0817e Binary files /dev/null and b/Lab5/archive/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab5/archive/initramfs.cpio b/Lab5/archive/initramfs.cpio new file mode 100644 index 000000000..efc1bc727 Binary files /dev/null and b/Lab5/archive/initramfs.cpio differ diff --git a/Lab5/archive/rootfs/dir1/sub_dir1/test b/Lab5/archive/rootfs/dir1/sub_dir1/test new file mode 100644 index 000000000..793354cac --- /dev/null +++ b/Lab5/archive/rootfs/dir1/sub_dir1/test @@ -0,0 +1,2 @@ +This is test! +Newline. \ No newline at end of file diff --git a/Lab5/bcm2710-rpi-3-b-plus.dtb b/Lab5/bcm2710-rpi-3-b-plus.dtb new file mode 100755 index 000000000..c83b0817e Binary files /dev/null and b/Lab5/bcm2710-rpi-3-b-plus.dtb differ diff --git a/Lab5/bootloader.zip b/Lab5/bootloader.zip new file mode 100644 index 000000000..9e81161b3 Binary files /dev/null and b/Lab5/bootloader.zip differ diff --git a/Lab5/bootloader/Makefile b/Lab5/bootloader/Makefile new file mode 100644 index 000000000..f28fa218c --- /dev/null +++ b/Lab5/bootloader/Makefile @@ -0,0 +1,33 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -I../include -I../peripherals -fno-stack-protector -g +ASFLAGS = + +# Adjust these paths as necessary +SRC_C = $(wildcard *.c) +SRC_S = $(wildcard *.S) +OBJS = $(SRC_C:%.c=$(BUILD_DIR)/bootloader/%.o) $(SRC_S:%.S=$(BUILD_DIR)/bootloader/%.o) + +# Include peripherals objects +PERIPHERALS_OBJS = $(wildcard $(BUILD_DIR)/peripherals/*.o) +LINKER_SCRIPT = linker.ld +BOOTLOADER_ELF = $(BUILD_DIR)/bootloader/bootloader.elf +BOOTLOADER_IMG = $(BUILD_DIR)/bootloader/bootloader.img + +all: $(OBJS) bootloader + +$(BUILD_DIR)/bootloader/%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/bootloader/%.o: %.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +bootloader: $(OBJS) $(PERIPHERALS_OBJS) + $(LD) -T $(LINKER_SCRIPT) -o $(BOOTLOADER_ELF) $^ + $(OBJCOPY) -O binary $(BOOTLOADER_ELF) $(BOOTLOADER_IMG) + +.PHONY: all bootloader diff --git a/Lab5/bootloader/boot.S b/Lab5/bootloader/boot.S new file mode 100644 index 000000000..f6703d5f3 --- /dev/null +++ b/Lab5/bootloader/boot.S @@ -0,0 +1,69 @@ +#include "../peripherals/mm.h" + +.section ".text.boot" + +.global _start + +_start: + // The initial state of the registers x0, x1, x2, and x3 upon startup holds specific information + // passed by the firmware/bootloader. + // x0: The 64-bit machine model number (from the Device Tree Blob, or DTB). This is an identifier + // for the hardware model the software is running on, which can be used to adjust behaviors or + // operations for different hardware variants if necessary. + mov x10, x0 + // x1: The physical address of the Device Tree Blob (DTB) in memory. The DTB is a binary + // representation of the device tree, which describes the hardware components of the system in a + // tree-like structure. This includes information about the processor, memory, peripherals, and + // other hardware components. The operating system or bootloader can parse the DTB to dynamically + // discover the hardware configuration of the system. + mov x11, x1 + // x2: Reserved or unused in the standard Raspberry Pi boot process. Its value may not be defined + // or may be specific to certain configurations or future use. It's often safe to assume this register + // does not hold information critical to the initial boot process unless specified by new documentation + // or specific boot configurations. + mov x12, x2 + // x3: Reserved or unused, similar to x2. Its purpose is dependent on the booting firmware or specific + // use cases and is generally not used in the standard boot process for Raspberry Pi. + mov x13, x3 + +// Relocate the boatloader(0x80000) to another location(0x60000) to avoid overlapping with kernel. +self_relocate: + ldr x2, =0x60000 + adr x0, __bootloader_start + adr x1, __bootloader_end + sub x1, x1, x0 + +relocate: + ldr x3, [x0], #8 + str x3, [x2], #8 + subs x1, x1, #8 + b.gt relocate + + +// Initialize the bss section. +clear_bss: + // Initialize stack pointer. + mov sp, #LOW_MEMORY + adr x0, __bss_start + adr x1, __bss_end + sub x1, x1, x0 + +// Iterate through the bss section and initialize it with zeros. +memzero: + // Stores the value from the zero register(xzr) to the memory location pointed to + // by x0. After storing the bytes, increment x0 by 8 bytes, since it's a 64-bit + // architecture. + str xzr, [x0], #8 + + // x1 contains the size of bss that needs to be zeroed out. subs also updates + // the condition flags based on the result. + subs x1, x1, #8 + + // It's a conditional branch that jumps back to memzero label if the condition + // flags indicate that the result of the previous "subs" was greater than zero. + b.gt memzero + bl bootloader_main - 0x20000 + +// This step shouldn't be reached, define it just in case. +proc_hang: + b proc_hang diff --git a/Lab5/bootloader/bootloader.c b/Lab5/bootloader/bootloader.c new file mode 100644 index 000000000..f147a4938 --- /dev/null +++ b/Lab5/bootloader/bootloader.c @@ -0,0 +1,52 @@ +#include "../peripherals/mini_uart.h" +#include "bootloader.h" +#include "../peripherals/utils.h" + +void bootloader_main(unsigned long address) { + // Location to load the kernel. + char* kernel = (char *)0x80000; + + // Store the size of the kernel image. + unsigned int size = 0; + + // Setup mini uart. + uart_init(); + uart_send_string("\r\nBooting...\r\n"); + uart_send_string("Kernel image size: "); + uart_send_string("0x"); + size = get_kernel_size(); + uart_send_uint(size); + uart_send_string(" bytes\r\n"); + + while (size--) { + // uart_send_string("Loading Kernel\r\n"); + *kernel++ = uart_recv(); + } + + uart_send_string("Kernel loaded.\r\n"); + + // Give uart enough time to send "Kernel loaded." message. + delay(5000); + + asm volatile( + "mov x0, x10;" + "mov x1, x11;" + "mov x2, x12;" + "mov x3, x13;" + "mov x30, 0x80000;" + "ret;" + ); + +} + +unsigned int get_kernel_size(void) { + unsigned int size = 0; + + // The uart transfers 4 bytes at a time, while the uart_recv() reads 1 byte every time. + for (int i = 0; i < 4; i++) { + char c = uart_recv(); + size |= ((unsigned int)c & 0xFF) << (i * 8); + } + + return size; +} \ No newline at end of file diff --git a/Lab5/bootloader/bootloader.h b/Lab5/bootloader/bootloader.h new file mode 100644 index 000000000..a325b2fba --- /dev/null +++ b/Lab5/bootloader/bootloader.h @@ -0,0 +1,7 @@ +#ifndef _BOOTLOADER_H_ +#define _BOOTLOADER_H_ + +void bootloader_main(unsigned long address); +unsigned int get_kernel_size(void); + +#endif \ No newline at end of file diff --git a/Lab5/bootloader/linker.ld b/Lab5/bootloader/linker.ld new file mode 100644 index 000000000..003530818 --- /dev/null +++ b/Lab5/bootloader/linker.ld @@ -0,0 +1,24 @@ +SECTIONS +{ + . = 0x80000; + __bootloader_start = .; + PROVIDE(_code = .); + .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } + PROVIDE(_data = .); + .data : { *(.data .data.* .gnu.linkonce.d*) } + .bss (NOLOAD) : { + # Here I used 16 because some machines support 4-byte, 8-byte, and 16-byte. The bootloader should + # be as fast as possible, so here I used 16. The kernel, on the other hand, uses 8-byte for maximum + # compatibility and performance. + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + . = ALIGN(8); + __bootloader_end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} \ No newline at end of file diff --git a/Lab5/firmware/bootcode.bin b/Lab5/firmware/bootcode.bin new file mode 100755 index 000000000..9e831a273 Binary files /dev/null and b/Lab5/firmware/bootcode.bin differ diff --git a/Lab5/firmware/config.txt b/Lab5/firmware/config.txt new file mode 100644 index 000000000..9c55ac4a4 --- /dev/null +++ b/Lab5/firmware/config.txt @@ -0,0 +1,3 @@ +kernel=bootloader.img +arm_64bit=1 +initramfs initramfs.cpio 0x30000000 \ No newline at end of file diff --git a/Lab5/firmware/fixup.dat b/Lab5/firmware/fixup.dat new file mode 100755 index 000000000..2ff966820 Binary files /dev/null and b/Lab5/firmware/fixup.dat differ diff --git a/Lab5/firmware/start.elf b/Lab5/firmware/start.elf new file mode 100755 index 000000000..f5d78d670 Binary files /dev/null and b/Lab5/firmware/start.elf differ diff --git a/Lab5/initramfs.cpio b/Lab5/initramfs.cpio new file mode 100644 index 000000000..f7c9425e1 Binary files /dev/null and b/Lab5/initramfs.cpio differ diff --git a/Lab5/kernel/Makefile b/Lab5/kernel/Makefile new file mode 100644 index 000000000..a5bd61dbd --- /dev/null +++ b/Lab5/kernel/Makefile @@ -0,0 +1,34 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -I../include -I../peripherals -I../DSLibrary -fno-stack-protector -g -O0 +ASFLAGS = + +# Adjust these paths as necessary +SRC_C = $(wildcard *.c) +SRC_S = $(wildcard *.S) +OBJS = $(SRC_C:%.c=$(BUILD_DIR)/kernel/%.o) $(SRC_S:%.S=$(BUILD_DIR)/kernel/%.o) + +# Include peripherals objects +PERIPHERALS_OBJS = $(wildcard $(BUILD_DIR)/peripherals/*.o) +DSLIBRARY_OBJS = $(wildcard $(BUILD_DIR)/DSLibrary/*.o) +LINKER_SCRIPT = linker.ld +KERNEL8_ELF = $(BUILD_DIR)/kernel/kernel8.elf +KERNEL8_IMG = $(BUILD_DIR)/kernel/kernel8.img + +all: $(OBJS) kernel + +$(BUILD_DIR)/kernel/%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/kernel/%.o: %.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +kernel: $(OBJS) $(PERIPHERALS_OBJS) $(DSLIBRARY_OBJS) + $(LD) -T $(LINKER_SCRIPT) -o $(KERNEL8_ELF) $^ + $(OBJCOPY) -O binary $(KERNEL8_ELF) $(KERNEL8_IMG) + +.PHONY: all kernel diff --git a/Lab5/kernel/alloc.c b/Lab5/kernel/alloc.c new file mode 100644 index 000000000..f22768e93 --- /dev/null +++ b/Lab5/kernel/alloc.c @@ -0,0 +1,73 @@ +#include "alloc.h" +#include "mini_uart.h" +#include + +// Initialize the heap pointer. +void init_heap() { + // uart_send_string("Heap start\r\n"); + // uart_send_uint((uintptr_t)heap); + // uart_send_string("\r\n"); + heap_ptr = (unsigned char *)ALIGN((uintptr_t)heap, ALIGNMENT); + // uart_send_string("After heap alignment\r\n"); + // uart_send_uint((uintptr_t)heap_ptr); + // uart_send_string("\r\n"); +} + +// A simple allocater for continuous space. No handler was implemented, so the user +// must be careful when using the allocated memory space. No warnings will be given if +// accessing memory space not allocated. +// For example, int* table = malloc(sizeof(int) * 8), table[8] = 6. 8 exceeds allocated +// memory. +void* simple_malloc(size_t size) { + heap_ptr = (unsigned char *)ALIGN((uintptr_t)heap_ptr, ALIGNMENT); + + if (heap_ptr + size > heap + HEAP_SIZE) { + // Used all of heap's memory. + uart_send_string("Heap memory exhausted! Failed to allocate memory :(\r\n"); + return NULL; + } + + // uart_send_string("ptr address\r\n"); + // uart_send_uint((uintptr_t)heap_ptr); + // uart_send_string("\r\n"); + + void* ptr = heap_ptr; + + heap_ptr += size; + // uart_send_string("ptr address\r\n"); + // uart_send_uint((uintptr_t)ptr); + // uart_send_string("\r\n"); + + return ptr; +} + +static char* current_heap_ptr; +extern char __heap_start; +extern char __heap_end; + +void init_heap2() { + current_heap_ptr = &__heap_start; +} + +void* malloc(size_t size) { + current_heap_ptr = (unsigned char *)ALIGN((uintptr_t)current_heap_ptr, ALIGNMENT); + + // uart_send_string("__heap_end: "); + // uart_send_uint((uintptr_t)&__heap_end); + // uart_send_string("\r\n"); + + // uart_send_string("current_heap_ptr: "); + // uart_send_uint((uintptr_t)current_heap_ptr); + // uart_send_string("\r\n"); + + if (current_heap_ptr + size > &__heap_end) { + uart_send_string("Heap out of memory!\r\n"); + return NULL; + } + + void* ptr = current_heap_ptr; + + current_heap_ptr += size; + + return ptr; +} \ No newline at end of file diff --git a/Lab5/kernel/alloc.h b/Lab5/kernel/alloc.h new file mode 100644 index 000000000..500bead92 --- /dev/null +++ b/Lab5/kernel/alloc.h @@ -0,0 +1,19 @@ +#ifndef _ALLOC_H_ +#define _ALLOC_H_ + +#include "../peripherals/utils.h" +#include + +#define HEAP_SIZE 1024 * 1024 * 16 // Define 16MB heap size. +#define ALIGNMENT 8 // Align the heap pointer to the multiple of 8 before allocating. +#define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) + +static unsigned char heap[HEAP_SIZE]; +static unsigned char* heap_ptr; + +void init_heap(); +void* simple_malloc(size_t size); +void init_heap2(); +void* malloc(size_t size); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/boot.S b/Lab5/kernel/boot.S new file mode 100644 index 000000000..373d0d4c6 --- /dev/null +++ b/Lab5/kernel/boot.S @@ -0,0 +1,104 @@ +#include "../peripherals/mm.h" + +.section ".text.boot" + +.globl _start +_start: + // mrs: Move the contents of a special register to a general-purpose register. + // Reads the Multiprocessor Affinity Register (MPIDR_EL1) into register x0. + // This register contains the processor's unique ID, which is used to identify the core on which the code is running. + mrs x0, mpidr_el1 + + // Performs a bitwise AND operation on x0 with 0xFF to isolate the lower 8 bits + // of the MPIDR_EL1 register, which represent the processor ID. + and x0, x0, #0xFF + + // cbz: Compare and branch on zero + // If x0 is zero, it indicates the primary or master CPU and branches to the "master" label if true. + // If x0 is not zero(indicating secondary CPUs), it doesn't branch. + cbz x0, master + + // This label followed by an instruction creates an infinite loop, effectively hanging or stopping + // any secondary CPU cores from proceeding further. This ensures that only the primary core continues + // execution beyond this point. + b proc_hang + +master: + // Transit exception level before running kernel code.(EL2 -> EL1) + // Has to transit from the start. + bl el2_to_el1 + + + // Initialize stack pointer. + // Align to 16-byte boundary. + ldr x0, =0xC298390 + mov sp, x0 + + // Calculate the size of the bss section(stores uninitialized data) + // adr loads the address of the label into the register. + ldr x0, =bss_begin + ldr x1, =bss_end + sub x1, x1, x0 + bl memzero + + // Device tree address has been moved from x0 to x10 within the bootloader. + mov x0, x10 + bl kernel_main + b proc_hang + +// Initialize the bss section. +memzero: + // Stores the value from the zero register(xzr) to the memory location pointed to + // by x0. After storing the bytes, increment x0 by 8 bytes, since it's a 64-bit + // architecture. + str xzr, [x0], #8 + + // x1 contains the size of bss that needs to be zeroed out. subs also updates + // the condition flags based on the result. + subs x1, x1, #8 + + // It's a conditional branch that jumps back to memzero label if the condition + // flags indicate that the result of the previous subs was greater than zero. + b.gt memzero + ret + +// Change the exception level from EL2 to EL1 for the kernel.(By default, RPi's CPU runs +// in EL2 mode after booting) +el2_to_el1: + // EL1 uses aarch64 + // Consult https://developer.arm.com/documentation/ddi0601/2021-06/AArch64-Registers/HCR-EL2--Hypervisor-Configuration-Register?lang=en + // for setting the register. + mov x0, (1 << 31) + + // msr: Move the contents of a general-purpose register into the specified special register. + // The hcr_el2(Hypervisor Configuration Register) provides configuration controls for virtualization. + // It's used in hypervisor mode(EL2) to control various aspects of system behavior when executing lower + // exception levels. + msr hcr_el2, x0 + + // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/SPSR-EL2--Saved-Program-Status-Register--EL2- + // Set SPSR_EL2(Saved Program Status Register) + // EL1h (SPSel = 1) with interrupt disabled + // [3:0] 0b0101: EL1 with SP_EL1 (EL1h) -> AArch64 Exception level and selected Stack Pointer. + // [6]: FIQ interrupt mask. + // [7]: IRQ interrupt mask. + // [8]: SError interrupt mask. + // [9]: Debug exception mask. + mov x0, 0x3C5 + + // SPSR_EL2 holds the saved processor state when an exception is taken to EL2. Set the spsr_el2 register + // to ensure correct PSTATE when returning to EL1. + msr spsr_el2, x0 + + // Set ELR_EL2(Exception Link Register) from LR(Link Register) + // Holds the return address when executing an exception return operation(eret). + msr elr_el2, lr + + eret // return to EL1 + +// Prevents other CPU cores from proceeding any further. +proc_hang: + b proc_hang + +.section .data +low_stack_top: .word 0x1295B00 diff --git a/Lab5/kernel/buddy_system.c b/Lab5/kernel/buddy_system.c new file mode 100644 index 000000000..834e4f7be --- /dev/null +++ b/Lab5/kernel/buddy_system.c @@ -0,0 +1,497 @@ +#include "buddy_system.h" +#include "alloc.h" +#include "../peripherals/utils.h" +#include "../peripherals/mini_uart.h" + +list_header* free_block_list[FREE_BLOCK_LIST_SIZE]; + +// Store the info of allocated memory. +allocate_info* alloc_mem_list[MAX_ALLOC]; + +// Store the info of reserved memory. +reserve_mem_list* rsv_mem_head; + +// Size of free memory available. +uint64_t free_mem_size; + +void init_frame_freelist(void) { + free_mem_size = FREE_MEM_END_ADDR - FREE_MEM_BASE_ADDR; + if (free_mem_size < get_chunk_size(FREE_BLOCK_LIST_SIZE - 1)) { + uart_send_string("Not enough free memory! Increase free memory size or lower maximum block size!\r\n"); + return; + } + + // Initialize list headers. + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + list_header* new_header = (list_header *)malloc(sizeof(list_header)); + + new_header->next_block = NULL; + + free_block_list[i] = new_header; + } + + // Initialize the list storing allocated frames. + for (int i = 0; i < MAX_ALLOC; i++) { + alloc_mem_list[i] = NULL; + } + + // Initialize the empty list with maximum chunk size(free_list_tail). + int chunk_cnt = free_mem_size / get_chunk_size(FREE_BLOCK_LIST_SIZE - 1); + + free_block* cur_block; + for (int i = 0; i < chunk_cnt; i++) { + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + + if (i == 0) { + new_block->ind = 0; + new_block->next = NULL; + new_block->prev = new_block; + free_block_list[FREE_BLOCK_LIST_SIZE - 1]->next_block = new_block; + cur_block = new_block; + } else { + new_block->ind = cur_block->ind + power(2, FREE_BLOCK_LIST_SIZE - 1); + new_block->next = NULL; + new_block->prev = cur_block; + cur_block->next = new_block; + cur_block = cur_block->next; + } + } + +} + +allocate_info* allocate_frame(uint64_t request_size) { + free_block* allocate_block = NULL; + allocate_info* info = NULL; + + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + + // List empty. + if (free_block_list[i]->next_block == NULL) { + continue; + } + + // Each chunk size in current list. + uint64_t cur_chunk_size = get_chunk_size(i); + + if (request_size <= cur_chunk_size) { + info = (allocate_info *)malloc(sizeof(allocate_info)); + + // The starting block of the returned list. + allocate_block = free_block_list[i]->next_block; + info->start_frame = allocate_block->ind; + info->last_frame = info->start_frame + (cur_chunk_size / BLOCK_SIZE) - 1; + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Allocate Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + uart_send_string("Allocate block of memory: "); + uart_send_int(cur_chunk_size / 1024); + uart_send_string(" KB, starting from frame "); + uart_send_int(allocate_block->ind); + uart_send_string("\r\n\r\n"); + + + free_block_list[i]->next_block = allocate_block->next; + + if (free_block_list[i]->next_block != NULL) { + free_block_list[i]->next_block->prev = free_block_list[i]->next_block; + } + + // Calculate the remaining memory of allocated block. + uint64_t rem_mem = cur_chunk_size - request_size; + + // The last chunk still has memory left. + if (rem_mem > 0) { + info->last_frame = release_block(allocate_block->ind, cur_chunk_size, request_size); + } + + insert_alloc_list(info); + + break; + } + } + + check_list(); + + return info; +} + +// Put back remaining block after allocating and return the index of the last allocated block. +int release_block(int start_ind, uint64_t chunk_size, uint64_t used_mem) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Release Remaining Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + while (chunk_size > BLOCK_SIZE) { + // The last half is free to put back. + if (chunk_size / 2 >= used_mem) { + int exp = find_chunk_exp(chunk_size / 2); + int ind = start_ind + (chunk_size / 2) / BLOCK_SIZE; + + uart_send_string("Release "); + uart_send_int(chunk_size / 2 / 1024); + uart_send_string(" KB memory, starting from frame "); + uart_send_int(ind); + uart_send_string("\r\n"); + + free_block* cur_block = free_block_list[exp]->next_block; + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + + new_block->ind = ind; + new_block->prev = new_block; + new_block->next = NULL; + + // Insert the last half into free list. + if (cur_block == NULL) { + free_block_list[exp]->next_block = new_block; + } else { + while (cur_block != NULL) { + // Insert free chunk in increasing order. + if (cur_block->ind > ind) { + // New block should be head of list. + if (cur_block->prev == cur_block) { + free_block_list[exp]->next_block = new_block; + new_block->next = cur_block; + cur_block->prev = new_block; + } else { + cur_block->prev->next = new_block; + new_block->prev = cur_block->prev; + new_block->next = cur_block; + cur_block->prev = new_block; + } + break; + // New block should be inserted at the tail. + } else if (cur_block->next == NULL) { + cur_block->next = new_block; + new_block->prev = cur_block; + break; + } + cur_block = cur_block->next; + } + } + + chunk_size /= 2; + + } else { + chunk_size /= 2; + used_mem -= chunk_size; + start_ind += (chunk_size / BLOCK_SIZE); + } + } + + uart_send_string("\r\n"); + + return start_ind; +} + +// Release the block and put it back to the free list, also updating the allocated +// memory list. +void free_allocated_mem(int start_ind) { + int start_frame; + int last_frame; + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Free Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] != NULL && alloc_mem_list[i]->start_frame == start_ind) { + start_frame = alloc_mem_list[i]->start_frame; + last_frame = alloc_mem_list[i]->last_frame; + alloc_mem_list[i] = NULL; + break; + } + + if (i == MAX_ALLOC - 1) { + uart_send_string("Allocated memory not found!\r\n"); + return; + } + } + + unsigned long size = (last_frame - start_frame + 1) * (BLOCK_SIZE / 1024); + + while (size > 0) { + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + if (size >= get_chunk_size(i) / 1024 && size < get_chunk_size(i + 1) / 1024) { + uart_send_string("start_frame: "); + uart_send_int(start_frame); + uart_send_string("\r\n"); + uart_send_string("list_index: "); + uart_send_int(i); + uart_send_string("\r\n"); + merge_buddy(start_frame, i); + size -= get_chunk_size(i) / 1024; + start_frame += (get_chunk_size(i) / BLOCK_SIZE); + } + } + } + + +} + +void insert_alloc_list(allocate_info* info) { + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] == NULL) { + alloc_mem_list[i] = info; + + return; + } + } + + uart_send_string("Maximum allocation reached! Failed to allocated memory!\r\n"); +} + +void show_alloc_list2(void) { + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Allocated Memory Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] != NULL) { + uart_send_string("Allocated frames, starting from frame "); + uart_send_int(alloc_mem_list[i]->start_frame); + uart_send_string(" to frame "); + uart_send_int(alloc_mem_list[i]->last_frame); + uart_send_string("\r\n"); + } + } + uart_send_string("\r\n"); +} + +uint64_t get_chunk_size(int list_ind) { + return BLOCK_SIZE * power(2, list_ind); +} + +int find_chunk_exp(uint64_t size) { + int tmp = size / BLOCK_SIZE; + int exp = 0; + + while (tmp > 0) { + exp++; + tmp /= 2; + } + + exp--; + + return exp; +} + +int find_bud(int ind, int exp) { + return ind ^ (1 << exp); +} + +void merge_buddy(int ind, int free_list_ind) { + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("In merge_buddy\r\n"); + uart_send_string("---------------------------------------\r\n"); + + uart_send_string("Finding buddy of block "); + uart_send_int(ind); + uart_send_string(" in list "); + uart_send_int(free_list_ind); + uart_send_string("\r\n"); + + free_block* cur_block = free_block_list[free_list_ind]->next_block; + + // Maximum size, don't merge. + if (free_list_ind == FREE_BLOCK_LIST_SIZE - 1) { + uart_send_string("Maximum block size, no need to merge.\r\n"); + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + new_block->ind = ind; + if (cur_block == NULL) { + new_block->next = NULL; + new_block->prev = new_block; + free_block_list[free_list_ind]->next_block = new_block; + } else { + while (cur_block != NULL) { + if (cur_block->ind > ind) { + // cur_block is head. + if (cur_block->prev = cur_block) { + free_block_list[free_list_ind]->next_block = new_block; + new_block->next = cur_block; + cur_block->prev = new_block; + } else { + cur_block->prev->next = new_block; + new_block->prev = cur_block->prev; + new_block->next = cur_block; + cur_block->prev = new_block; + } + break; + } + cur_block = cur_block->next; + } + } + + return; + } + + int buddy_ind = find_bud(ind, free_list_ind); + + // Find whether buddy exists. + while (cur_block != NULL) { + if (cur_block->ind == buddy_ind) { + uart_send_string("Buddy found ---> Block "); + uart_send_int(buddy_ind); + uart_send_string("\r\n"); + // Remove the block from the current list and merge it to larger memory list. + // If the block is the head. + if (cur_block->prev == cur_block) { + free_block_list[free_list_ind]->next_block = cur_block->next; + if (cur_block->next != NULL) { + cur_block->next->prev = cur_block->next; + } + // Block is tail. + } else if (cur_block->next == NULL) { + cur_block->prev->next = NULL; + // Block is at the middle + } else { + cur_block->prev->next = cur_block->next; + cur_block->next->prev = cur_block->prev; + } + + if (buddy_ind > ind) { + merge_buddy(ind, free_list_ind + 1); + } else { + merge_buddy(buddy_ind, free_list_ind + 1); + } + + return; + } + cur_block = cur_block->next; + } + + uart_send_string("No buddy found!\r\n"); + + // No buddy is found, so insert the free block to current list. + free_block* new_block = (free_block *)malloc(sizeof(free_block)); + new_block->ind = ind; + new_block->next = NULL; + new_block->prev = new_block; + + cur_block = free_block_list[free_list_ind]->next_block; + if (cur_block == NULL) { + free_block_list[free_list_ind]->next_block = new_block; + } else { + while (cur_block != NULL) { + if (cur_block->ind > new_block->ind) { + // cur_block is head. + if (cur_block->prev = cur_block) { + cur_block->prev = new_block; + new_block->next = cur_block; + free_block_list[free_list_ind]->next_block = new_block; + } else { + cur_block->prev->next = new_block; + new_block->prev = cur_block->prev; + new_block->next = cur_block; + cur_block->prev = new_block; + } + break; + } else if (cur_block->next == NULL) { + cur_block->next = new_block; + new_block->prev = cur_block; + break; + } + + cur_block = cur_block->next; + } + } +} + +void check_list(void) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Free Frame List Info\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < FREE_BLOCK_LIST_SIZE; i++) { + uart_send_string("List: "); + uart_send_int(i); + uart_send_string("\r\n"); + + free_block* head = free_block_list[i]->next_block; + uart_send_string("Free block index: "); + while (i != FREE_BLOCK_LIST_SIZE - 1 && head != NULL) { + uart_send_int(head->ind); + uart_send_string(" "); + head = head->next; + } + + if (i == FREE_BLOCK_LIST_SIZE - 1) { + // Only print ten index or the output will be too much. + for (int i = 0; i < 10; i++) { + uart_send_int(head->ind); + uart_send_string(" "); + head = head->next; + } + } + uart_send_string("\r\n"); + + } + uart_send_string("\r\n"); +} + +void init_rsv_mem_list(void) { + rsv_mem_head = NULL; +} + +// Reserve the memory block.(From start to end) +/* +void memory_reserve(uint64_t start, uint64_t end) { + int start_ind = start / BLOCK_SIZE; + int end_ind = end / BLOCK_SIZE; + + reserve_mem_list* new = (reserve_mem_list *)malloc(sizeof(reserve_mem_list)); + allocate_info* info = (allocate_info *)malloc(sizeof(allocate_info)); + info->start_frame = start_ind; + info->last_frame = end_ind; + new->info = info; + new->next = NULL; + new->prev = NULL; + + // The list should be in acsending order according to starting frame index. + if (rsv_mem_head == NULL) { + rsv_mem_head = new; + } else { + reserve_mem_list* cur = rsv_mem_head; + + while (cur != NULL) { + if (new->info->start_frame < cur->info->start_frame) { + if (cur == rsv_mem_head) { + new->next = cur; + cur->prev = new; + rsv_mem_head = new; + } else { + cur->prev->next = new; + new->prev = cur->prev; + new->next = cur; + cur->prev = new; + } + break; + } else if (cur->next == NULL) { + cur->next = new; + new->prev = cur; + break; + } + + cur = cur->next; + } + } + + + reserve_mem_list* cur = rsv_mem_head; + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Reserved Memory\r\n"); + uart_send_string("---------------------------------------\r\n"); + + while (cur != NULL) { + uart_send_string("frame "); + uart_send_int(cur->info->start_frame); + uart_send_string(" ~ "); + uart_send_string("frame "); + uart_send_int(cur->info->last_frame); + uart_send_string("\r\n"); + cur = cur->next; + } +} +*/ \ No newline at end of file diff --git a/Lab5/kernel/buddy_system.h b/Lab5/kernel/buddy_system.h new file mode 100644 index 000000000..8436be6a5 --- /dev/null +++ b/Lab5/kernel/buddy_system.h @@ -0,0 +1,74 @@ +#ifndef _BUDDY_SYSTEM_H_ +#define _BUDDY_SYSTEM_H_ + +#include + +// 0x10000000 ~ 0x1FFFFFFF +// #define FREE_MEM_BASE_ADDR 0x10000000 + +// Total available memory from 0x0 to 0x3C000000. +#define FREE_MEM_BASE_ADDR 0x0 +#define FREE_MEM_END_ADDR 0x3C000000 + +/* + * Total free memory size: 256 MB + * Memory size has to be 2's exponent, otherwise the initialization will fail. + */ +//#define FREE_MEM_SIZE (1 << 16) * BLOCK_SIZE +// One block -> 4KB +#define BLOCK_SIZE 4096 + +// 7 Linked lists -> 4, 8, 16, 32, 64, 128, 256 KB +#define FREE_BLOCK_LIST_SIZE 7 + +// Maximum allocation allowed. +#define MAX_ALLOC 10 + +// Size of free memory available. +extern uint64_t free_mem_size; + +typedef struct _free_block { + // Block index. + int ind; + struct _free_block* next; + struct _free_block* prev; +} free_block; + +typedef struct _list_header { + // Exponent of current list. + int exp; + free_block* next_block; +} list_header; + +typedef struct _allocate_info { + // Stores the index of first frame allocated. + int start_frame; + // Store the number of frame pages allocated. + int last_frame; +} allocate_info; + +typedef struct _reserved_mem { + allocate_info* info; + struct _reserved_mem* next; + struct _reserved_mem* prev; +} reserve_mem_list; + +extern allocate_info* alloc_mem_list[MAX_ALLOC]; +extern reserve_mem_list* rsv_mem_head; + +void init_frame_freelist(void); +allocate_info* allocate_frame(uint64_t request_size); +uint64_t get_chunk_size(int list_ind); +int release_block(int start_ind, uint64_t chunk_size, uint64_t used_mem); +int find_chunk_exp(uint64_t size); +void check_list(void); +// After allocated frames for a request, save the info. +void insert_alloc_list(allocate_info* info); +// Free the allocated memory depending on the starting frame. +void remove_alloc_list(int start_ind); +void free_allocated_mem(int start_ind); +void show_alloc_list2(void); +void merge_buddy(int ind, int free_list_ind); +// void memory_reserve(uint64_t start, uint64_t end); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/buddy_system_2.c b/Lab5/kernel/buddy_system_2.c new file mode 100644 index 000000000..9a3e95d11 --- /dev/null +++ b/Lab5/kernel/buddy_system_2.c @@ -0,0 +1,489 @@ +#include "buddy_system_2.h" +#include "alloc.h" +#include "../peripherals/utils.h" +#include "../peripherals/mini_uart.h" + +entry_t** frame_arr; +list_header_t* free_list[FREE_LIST_SIZE]; +alloc_header_t* alloc_frame_list[MAX_ALLOC_CNT]; + +void init_buddy_system(void) { + uint64_t tot_mem_size = FREE_MEM_END_ADDR - FREE_MEM_BASE_ADDR; + int frame_cnt = tot_mem_size / FRAME_SIZE; + uart_send_string("frame_cnt: "); + uart_send_int(frame_cnt); + uart_send_string("\r\n"); + + for (int i = 0; i < MAX_ALLOC_CNT; i++) { + alloc_header_t* header = (alloc_header_t *)malloc(sizeof(alloc_header_t)); + header->head = NULL; + header->tail = NULL; + header->status = -1; + alloc_frame_list[i] = header; + } + + // Check whether memory is divisible by maximum frame size. + if (tot_mem_size % (FRAME_SIZE * power(2, FREE_LIST_SIZE - 1)) != 0) { + uart_send_string("Free memory not divisible by maximum frame size!\r\n"); + uart_send_string("Buddy system initialization failed!\r\n"); + return; + } + + frame_arr = (entry_t **)malloc(sizeof(entry_t *) * frame_cnt); + + for (int i = 0; i < FREE_LIST_SIZE; i++) { + list_header_t* header = (list_header_t *)malloc(sizeof(list_header_t)); + header->head = NULL; + header->tail = NULL; + free_list[i] = header; + } + + int largest_page_size = power(2, FREE_LIST_SIZE - 1); + entry_t* cur_entry; + + for (int i = 0; i < frame_cnt; i++) { + entry_t* new_entry = (entry_t *)malloc(sizeof(entry_t)); + new_entry->slot_size = SLOT_NOT_ALLOCATED; + new_entry->status = FREE_FRAME; + new_entry->next = NULL; + new_entry->prev = NULL; + new_entry->next_slot = NULL; + new_entry->prev_slot = NULL; + new_entry->ind = i; + frame_arr[i] = new_entry; + } + + // Reserve spin tables for multicore boot. + memory_reserve(0x0, 0x1000); + // Reserve kernel image. + memory_reserve(0x80000, 0xC298390); + // Reserve cpio. + memory_reserve(0x10000000, 0x10000400); + // Reserve dtb. + memory_reserve(0x2EFF7600, 0x2EFFFFDC); + + + for (int i = 0; i < frame_cnt; i++) { + // uart_send_string("i: "); + // uart_send_int(i); + // uart_send_string("\r\n"); + + int start_frame = i; + int contiguous_frames = 0; + + while (i < frame_cnt && frame_arr[i]->status != USED_FRAME) { + // uart_send_string("In while: "); + // uart_send_int(i); + // uart_send_string("\r\n"); + contiguous_frames++; + i++; + + if (i % 64 == 63) { + if (frame_arr[i]->status != USED_FRAME) { + contiguous_frames++; + } + break; + } + } + + + + if (contiguous_frames > 0) { + insert_freelist(start_frame, contiguous_frames); + } + } + + + + + /* + // Frame index indicating the beginning of the largest continuous block.(256KB) + if (i % largest_page_size == 0) { + new_entry->status = FREE_LIST_SIZE - 1; + new_entry->prev = new_entry; + new_entry->next = NULL; + + // Head and tail points to the same entry if only one exists. + if (free_list[FREE_LIST_SIZE - 1]->head == NULL) { + free_list[FREE_LIST_SIZE - 1]->head = new_entry; + free_list[FREE_LIST_SIZE - 1]->tail = new_entry; + cur_entry = new_entry; + } else { + cur_entry->next = new_entry; + new_entry->prev = cur_entry; + cur_entry = cur_entry->next; + free_list[FREE_LIST_SIZE - 1]->tail = new_entry; + } + } else { + new_entry->status = FREE_FRAME; + new_entry->next = NULL; + new_entry->prev = NULL; + } + */ + + // show_free_list(); +} + +void insert_freelist(int start_frame, int contiguous_frames) { + + while (contiguous_frames > 0) { + // The start frame index has to be divisible by 2^list_index to ensure alignment. + // 256 KB. + if (start_frame % (1 << 6) == 0 && contiguous_frames >= (1 << 6)) { + frame_arr[start_frame]->status = 6; + // 128 KB. + } else if (start_frame % (1 << 5) == 0 && contiguous_frames >= (1 << 5)) { + frame_arr[start_frame]->status = 5; + // 64 KB. + } else if (start_frame % (1 << 4) == 0 && contiguous_frames >= (1 << 4)) { + frame_arr[start_frame]->status = 4; + // 32 KB. + } else if (start_frame % (1 << 3) == 0 && contiguous_frames >= (1 << 3)) { + frame_arr[start_frame]->status = 3; + // 16 KB. + } else if (start_frame % (1 << 2) == 0 && contiguous_frames >= (1 << 2)) { + frame_arr[start_frame]->status = 2; + // 8 KB. + } else if (start_frame % (1 << 1) == 0 && contiguous_frames >= (1 << 1)) { + frame_arr[start_frame]->status = 1; + // 4 KB. + } else if (start_frame % (1 << 0) == 0 && contiguous_frames >= (1 << 0)) { + frame_arr[start_frame]->status = 0; + } + + if (free_list[frame_arr[start_frame]->status]->tail == NULL) { + free_list[frame_arr[start_frame]->status]->head = frame_arr[start_frame]; + free_list[frame_arr[start_frame]->status]->tail = frame_arr[start_frame]; + frame_arr[start_frame]->prev = frame_arr[start_frame]; + frame_arr[start_frame]->next = NULL; + } else { + free_list[frame_arr[start_frame]->status]->tail->next = frame_arr[start_frame]; + frame_arr[start_frame]->prev = free_list[frame_arr[start_frame]->status]->tail; + frame_arr[start_frame]->next = NULL; + free_list[frame_arr[start_frame]->status]->tail = frame_arr[start_frame]; + } + + contiguous_frames -= (1 << frame_arr[start_frame]->status); + start_frame += (1 << frame_arr[start_frame]->status); + } +} + + + +// Request in bytes. +// Return the index of alloc_frame_list array for the allocation. +int alloc_page(uint64_t request_size) { + + // Uncomment for demo. + // uart_send_string("\r\n---------------------------------------\r\n"); + // uart_send_string("Allocate Page\r\n"); + // uart_send_string("---------------------------------------\r\n"); + + if (request_size > power(2, FREE_LIST_SIZE - 1) * FRAME_SIZE) { + uart_send_string("Request exceed maximum page size! Allocation failed!\r\n"); + return -1; + } + + // Find the smallest page required. + int list_ind = -1; + + for (int i = 0; i < FREE_LIST_SIZE; i++) { + if (power(2, i) * FRAME_SIZE >= request_size) { + // The list has free memory. + if (free_list[i]->head != NULL) { + list_ind = i; + break; + } + } + } + + // Uncomment for demo. + // uart_send_string("Allocate page of size "); + // uart_send_int(power(2, list_ind) * 4); + // uart_send_string(" KB\r\n"); + + if (list_ind == -1) { + uart_send_string("No available memory in buddy system!\r\n"); + return -1; + } + + entry_t* cur_entry = free_list[list_ind]->head; + + // Remove current entry from free list. + if (free_list[list_ind]->head->next != NULL) { + free_list[list_ind]->head = free_list[list_ind]->head->next; + } else { + free_list[list_ind]->head = NULL; + free_list[list_ind]->tail = NULL; + } + + // Release redundant memory. + if (list_ind != 0) { + uint64_t prev_list_size = power(2, list_ind - 1) * FRAME_SIZE; + + // Allocated memory is much larger than required. Put back the last half until + // remaining memory is of correct size. + while (prev_list_size >= request_size) { + // Split the list starting from frame . + int split_ind = cur_entry->ind + power(2, list_ind - 1); + + // Insert into the end of free list. + if (free_list[list_ind - 1]->tail == NULL) { + free_list[list_ind - 1]->head = frame_arr[split_ind]; + free_list[list_ind - 1]->tail = free_list[list_ind - 1]->head; + + frame_arr[split_ind]->prev = frame_arr[split_ind]; + frame_arr[split_ind]->next = NULL; + } else { + free_list[list_ind - 1]->tail->next = frame_arr[split_ind]; + frame_arr[split_ind]->prev = free_list[list_ind - 1]->tail; + frame_arr[split_ind]->next = NULL; + free_list[list_ind - 1]->tail = frame_arr[split_ind]; + } + + // Update frame array. + frame_arr[split_ind]->status = list_ind - 1; + // uart_send_string("split_ind: "); + // uart_send_int(split_ind); + // uart_send_string("\r\n"); + + // uart_send_string("f_split_ind: "); + // uart_send_int(frame_arr[split_ind]->status); + // uart_send_string("\r\n"); + list_ind--; + + if (list_ind <= 0) { + break; + } + + prev_list_size = power(2, list_ind - 1) * FRAME_SIZE; + } + + // show_free_list(); + + cur_entry->status = list_ind; + // uart_send_string("cur_entry->status: "); + // uart_send_int(cur_entry->status); + // uart_send_string("\r\n"); + } + + // Record allocated info. + int alloc_ind; + for (alloc_ind = 0; alloc_ind < MAX_ALLOC_CNT; alloc_ind++) { + if (alloc_frame_list[alloc_ind]->head == NULL) { + alloc_frame_list[alloc_ind]->head = cur_entry; + alloc_frame_list[alloc_ind]->tail = frame_arr[cur_entry->ind + power(2, list_ind) - 1]; + alloc_frame_list[alloc_ind]->status = cur_entry->status; + break; + } + } + + if (alloc_ind == MAX_ALLOC_CNT) { + uart_send_string("Maximum allocation reached! Unable to allocate new frames!\r\n"); + return -1; + } + + // Update the status of allocated frames. + // uart_send_string("\r\ncur_entry->ind: "); + // uart_send_int(cur_entry->ind); + // uart_send_string("\r\n"); + // uart_send_string("cur_entry->status: "); + // uart_send_int(power(2, cur_entry->status)); + // uart_send_string("\r\n"); + for (int i = alloc_frame_list[alloc_ind]->head->ind; i <= alloc_frame_list[alloc_ind]->tail->ind; i++) { + frame_arr[i]->status = USED_FRAME; + } + + return alloc_ind; +} + +void free_page(int start_frame_ind) { + + // uart_send_string("\r\n---------------------------------------\r\n"); + // uart_send_string("Free Page\r\n"); + // uart_send_string("---------------------------------------\r\n"); + + entry_t* start_frame = NULL; + entry_t* last_frame = NULL; + int alloc_status; + + for (int i = 0; i < MAX_ALLOC_CNT; i++) { + if (alloc_frame_list[i]->head->ind == start_frame_ind) { + start_frame = alloc_frame_list[i]->head; + last_frame = alloc_frame_list[i]->tail; + alloc_status = alloc_frame_list[i]->status; + alloc_frame_list[i]->head = NULL; + alloc_frame_list[i]->tail = NULL; + alloc_frame_list[i]->status = -1; + break; + } + } + + if (start_frame == NULL) { + uart_send_string("Frame not allocated! Free page failed!\r\n"); + return; + } + + start_frame->status = alloc_status; + + // uart_send_string("start_frame->ind: "); + // uart_send_int(start_frame->ind); + // uart_send_string("\r\n"); + + // uart_send_string("alloc_status: "); + // uart_send_int(alloc_status); + // uart_send_string("\r\n"); + + int buddy_ind = find_buddy(start_frame->ind, alloc_status); + // uart_send_string("buddy_ind: "); + // uart_send_int(buddy_ind); + // uart_send_string("\r\n"); + + // uart_send_string("alloc_status: "); + // uart_send_int(frame_arr[buddy_ind]->status); + // uart_send_string("\r\n"); + + // If buddy exists, merge them and move on to the next level. + // Keep merging until reaching maximum list size or no buddy found. + while (frame_arr[buddy_ind]->status == alloc_status && alloc_status < FREE_LIST_SIZE - 1) { + // uart_send_string("In while\r\n"); + // uart_send_string("alloc_status: "); + // uart_send_int(alloc_status); + // uart_send_string("\r\n"); + + // Update free list. + entry_t* buddy = frame_arr[buddy_ind]; + + // Buddy is at the head of the list. + if (buddy->prev == buddy) { + // Only page in the list. + if (buddy->next == NULL) { + free_list[alloc_status]->head = NULL; + free_list[alloc_status]->tail = NULL; + } else { + free_list[alloc_status]->head = buddy->next; + free_list[alloc_status]->head->prev = free_list[alloc_status]->head; + } + } else { + if (buddy->next == NULL) { + free_list[alloc_status]->tail = free_list[alloc_status]->tail->prev; + free_list[alloc_status]->tail->next = NULL; + } else { + buddy->next->prev = buddy->prev; + buddy->prev->next = buddy->next; + } + } + buddy->next = NULL; + buddy->prev = NULL; + start_frame->next = NULL; + start_frame->prev = NULL; + + int merge_block_start = (start_frame->ind < buddy_ind) ? start_frame->ind : buddy_ind; + start_frame = frame_arr[merge_block_start]; + + // Update the frame status to free. + // uart_send_string("merge_block_start: "); + // uart_send_int(merge_block_start); + // uart_send_string("\r\n"); + + // uart_send_string("frame_arr[merge_block_start]->status: "); + // uart_send_int(frame_arr[merge_block_start]->status); + // uart_send_string("\r\n"); + + frame_arr[merge_block_start]->status = alloc_status + 1; + for (int i = frame_arr[merge_block_start]->ind + 1; i < frame_arr[merge_block_start]->ind + power(2, alloc_status + 1); i++) { + frame_arr[i]->status = FREE_FRAME; + } + buddy_ind = find_buddy(frame_arr[merge_block_start]->ind, alloc_status + 1); + + // uart_send_string("buddy_ind: "); + // uart_send_int(buddy_ind); + // uart_send_string("\r\n"); + // uart_send_string("Buddy_status: "); + // uart_send_int(frame_arr[buddy_ind]->status); + // uart_send_string("\r\n"); + alloc_status++; + } + + // Insert newly freed frames into free list. + //alloc_status--; + if (free_list[alloc_status]->tail == NULL) { + free_list[alloc_status]->head = start_frame; + free_list[alloc_status]->tail = start_frame; + start_frame->prev = start_frame; + start_frame->next = NULL; + } else { + free_list[alloc_status]->tail->next = start_frame; + start_frame->prev = free_list[alloc_status]->tail; + start_frame->next = NULL; + free_list[alloc_status]->tail = start_frame; + } + +} + +int find_buddy(int ind, int exp) { + return ind ^ (1 << exp); +} + +void memory_reserve(uint64_t start, uint64_t end) { + int start_frame; + int end_frame; + + uart_send_string("start frame: "); + + start_frame = (start - FREE_MEM_BASE_ADDR) / FRAME_SIZE; + uart_send_int(start_frame); + uart_send_string("\r\n"); + + uart_send_string("end frame: "); + end_frame = (end - FREE_MEM_BASE_ADDR) / FRAME_SIZE; + uart_send_int(end_frame); + uart_send_string("\r\n"); + + for (int i = start_frame; i <= end_frame; i++) { + frame_arr[i]->status = USED_FRAME; + } +} + +void show_free_list(void) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Free Memory List\r\n"); + uart_send_string("---------------------------------------\r\n"); + + for (int i = 0; i < FREE_LIST_SIZE; i++) { + uart_send_string("Current list -> "); + uart_send_int(i); + uart_send_string(": "); + entry_t* cur = free_list[i]->head; + + int cnt = 0; + while (cur != NULL && cnt <= 10) { + uart_send_int(cur->ind); + uart_send_string(" "); + cur = cur->next; + cnt++; + } + uart_send_string("\r\n"); + } + uart_send_int(free_list[FREE_LIST_SIZE - 1]->tail->prev->ind); + uart_send_string(" "); + uart_send_int(free_list[FREE_LIST_SIZE - 1]->tail->ind); + uart_send_string(" "); +} + +void show_alloc_list(void) { + for (int i = 0; i < MAX_ALLOC_CNT; i++) { + if (alloc_frame_list[i]->head != NULL) { + int cur_frame = alloc_frame_list[i]->head->ind; + int end_frame = alloc_frame_list[i]->tail->ind; + + for (; cur_frame <= end_frame; cur_frame++) { + uart_send_int(frame_arr[cur_frame]->ind); + uart_send_string(" "); + } + + uart_send_string("\r\n"); + } + } +} \ No newline at end of file diff --git a/Lab5/kernel/buddy_system_2.h b/Lab5/kernel/buddy_system_2.h new file mode 100644 index 000000000..90811dc70 --- /dev/null +++ b/Lab5/kernel/buddy_system_2.h @@ -0,0 +1,78 @@ +#ifndef _BUDDY_SYSTEM_2_ +#define _BUDDY_SYSTEM_2_ + +#include + +#define FREE_MEM_BASE_ADDR 0x0 +#define FREE_MEM_END_ADDR 0x3C000000 + +// Each frame is 4KB. +#define FRAME_SIZE 4096 + +// List stores page frames of size 4KB, 8KB, 16KB, 32KB, 64KB, 128KB, 256KB. +#define FREE_LIST_SIZE 7 + +// Maximum allocation from the buddy system permitted. +#define MAX_ALLOC_CNT 10 + +// Status of the frame. +// FREE_FRAME -> Frame is free but belong to a larger contiguous page. +#define FREE_FRAME 10 +// USED_FRAME -> Allocated frame. Unusable. +#define USED_FRAME 11 + +// 'A' -> 101, 'B' -> 102, 'C' -> 103 +// At most 64 frames will be allocated(256 KB) +#define SLOT_64B 'A' +#define SLOT_128B 'B' +#define SLOT_256B 'C' +#define SLOT_NOT_ALLOCATED 'D' + +// Reserve memory + + +typedef struct entry { + short status; + int ind; + struct entry* next; + struct entry* prev; + + // Linked list for dynamic allocator. + // Storing allocated slots. + // Ex: slot 0 is used -> FFFF FFFF FFFF FFF0(In hexadecimal) + uint64_t bitmap; + // Determine the slot size. + char slot_size; + // Remaining slot count. + short slot_cnt; + struct entry* next_slot; + struct entry* prev_slot; +} entry_t; + +typedef struct list_header { + // Points to head entry and tail entry of the list. + entry_t* head; + entry_t* tail; +} list_header_t; + +typedef struct alloc_header { + // Points to head entry and tail entry of the list. + entry_t* head; + entry_t* tail; + // Size of allocated block. + short status; +} alloc_header_t; + +extern alloc_header_t* alloc_frame_list[MAX_ALLOC_CNT]; +extern entry_t** frame_arr; + +void memory_reserve(uint64_t start, uint64_t end); +void insert_freelist(int start_frame, int contiguous_frames); +void init_buddy_system(void); +int alloc_page(uint64_t request_size); +void free_page(int start_frame_ind); +void show_free_list(void); +void show_alloc_list(void); +int find_buddy(int ind, int exp); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/context_switch.S b/Lab5/kernel/context_switch.S new file mode 100644 index 000000000..514958efa --- /dev/null +++ b/Lab5/kernel/context_switch.S @@ -0,0 +1,26 @@ +.global switch_to +switch_to: + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + msr tpidr_el1, x1 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret \ No newline at end of file diff --git a/Lab5/kernel/device_tree.c b/Lab5/kernel/device_tree.c new file mode 100644 index 000000000..cce4c45f0 --- /dev/null +++ b/Lab5/kernel/device_tree.c @@ -0,0 +1,228 @@ +#include "device_tree.h" +#include "../peripherals/mini_uart.h" +#include "initramdisk.h" + +DTBHeader myHeader; + +// For listing all the child node names of the reserved memory node. +short rvmem_child_cnt; + +// Initialize the header struct. +// return 0 for success, 1 for error. +int fdt_traverse(fdt_node_callback_t callback, uintptr_t dtb_address) { + uint32_t* dtb = (uint32_t *)dtb_address; + + rvmem_child_cnt = 0; + + // Parse the DTB header fields. + // __builtin_bswap32() is a GCC built-in function to convert to the system's endianness.(RPi uses little-endian) + myHeader.magic = __builtin_bswap32(*dtb++); + if ((myHeader.magic - 0xd00dfeed) != 0) { + uart_send_string("DTB magic number doesn't match!\r\n"); + return 1; + } + myHeader.totalsize = __builtin_bswap32(*dtb++); + myHeader.off_dt_struct = __builtin_bswap32(*dtb++); + myHeader.off_dt_strings = __builtin_bswap32(*dtb++); + myHeader.off_mem_rsvmap = __builtin_bswap32(*dtb++); + myHeader.version = __builtin_bswap32(*dtb++); + myHeader.last_comp_version = __builtin_bswap32(*dtb++); + myHeader.boot_cpuid_phys = __builtin_bswap32(*dtb++); + myHeader.size_dt_strings = __builtin_bswap32(*dtb++); + myHeader.size_dt_struct = __builtin_bswap32(*dtb++); + + // uart_send_string("DTB start address: "); + // uart_send_uint(dtb_address); + // uart_send_string("\r\n"); + + // uart_send_string("DTB end address: "); + // uart_send_uint(dtb_address + myHeader.totalsize); + // uart_send_string("\r\n"); + + if (parse_struct(callback, dtb_address) != 0) { + uart_send_string("Error parsing devicetree!\r\n"); + } +} + +// Parse the entire structure and call corresponding callback functions to perform +// certain operations. +int parse_struct(fdt_node_callback_t callback, uintptr_t dtb_start_addr) { + + // Point to the beginning of structure block. + uint32_t* cur_ptr = (uint32_t *)(dtb_start_addr + myHeader.off_dt_struct); + + for (;;) { + uint32_t token = __builtin_bswap32(*cur_ptr++); + + switch (token) { + case FDT_BEGIN_NODE: + // uart_send_string("In FDT_BEGIN_NODE!\r\n"); + // The node name consists of 31 characters at most, with the root node an empty string. + char name[32]; + // Reset name array. + for (int i = 0; i < 31; i++) { + name[i] = '\0'; + } + + char* name_ptr = (char *)cur_ptr; + + // Root node. + if (*name_ptr == '\0') { + name[0] = '/'; + name_ptr++; + // All other nodes has names. + } else { + for (int i = 0; *name_ptr != '\0'; i++, name_ptr++) { + name[i] = *name_ptr; + } + // Ignore the null terminator. + name_ptr++; + } + + cur_ptr = (uint32_t *)ALIGN((uintptr_t)name_ptr, ALIGN_STRUCT_BLOCK); + + callback(token, name, NULL, (uintptr_t)cur_ptr); + break; + case FDT_END_NODE: + break; + case FDT_PROP: + uint32_t len = __builtin_bswap32(*(uint32_t *)cur_ptr++); + uint32_t nameoff = __builtin_bswap32(*(uint32_t *)cur_ptr++); + + // Point to the address storing the property name within the strings block. + char* prop_name_ptr = (char *)(dtb_start_addr + myHeader.off_dt_strings + nameoff); + char prop_name[32]; + for (int i = 0; i < 32; i++) { + prop_name[i] = '\0'; + } + + for (int i = 0; *prop_name_ptr != '\0'; i++, prop_name_ptr++) { + prop_name[i] = *prop_name_ptr; + } + + // Pass the length of the property's value and the pointer to that value + // into callback function for extracting value for that property. + callback(token, prop_name, &len, (uintptr_t)cur_ptr); + + // Property's value is given as a byte string of length len(In bytes). + // cur_ptr is an uint32 pointer, so when it increments by 1, it's + // actually incrementing 4 bytes. + if (len % 4 == 0) { + cur_ptr += (len / 4); + } else { + cur_ptr += (len / 4 + 1); + } + + cur_ptr = (uint32_t *)ALIGN((uintptr_t)cur_ptr, ALIGN_STRUCT_BLOCK); + + break; + case FDT_NOP: + continue; + break; + case FDT_END: + return 0; + default: + break; + } + + } +} + +void print_tree(int type, const char* name, void* data, uintptr_t cur_ptr) { + switch (type) { + case FDT_BEGIN_NODE: + uart_send_string("Node name: "); + uart_send_string("\r\n"); + uart_send_string((char *)name); + uart_send_string("\r\n"); + break; + + // Also print the property value. + case FDT_PROP: + uart_send_string("Property: "); + uart_send_string((char *)name); + uart_send_string("\r\n"); + // Value: or + if (!strcmp(name, "compatible") || !strcmp(name, "model")) { + char* val_ptr = (char *)cur_ptr; + int len = *(int *)data; + uart_send_string(" "); + for (int i = 0; i < len; i++) { + if (*val_ptr == '\0') { + if (i != len - 1) { + uart_send_string("; "); + } else { + uart_send(';'); + } + val_ptr++; + + } else { + uart_send(*val_ptr++); + } + } + + uart_send_string("\r\n"); + } + break; + default: + break; + } +} + +void get_initrd_address(int type, const char* name, void* data, uintptr_t cur_ptr) { + // Address of initramfs is stored within the property name "linux,initrd-start". + if (type == FDT_PROP) { + if (strcmp(name, "linux,initrd-start") == 0) { + uint32_t* val_ptr = (uint32_t *)cur_ptr; + initramdisk_main(__builtin_bswap32(*val_ptr)); + uart_send_string("initramdisk addr: "); + uart_send_uint(__builtin_bswap32(*val_ptr)); + uart_send_string("\r\n"); + } + } +} + +void get_initrd_end_address(int type, const char* name, void* data, uintptr_t cur_ptr) { + // Address of initramfs is stored within the property name "linux,initrd-start". + if (type == FDT_PROP) { + if (strcmp(name, "linux,initrd-end") == 0) { + uint32_t* val_ptr = (uint32_t *)cur_ptr; + uart_send_string("initramdisk end addr: "); + uart_send_uint(__builtin_bswap32(*val_ptr)); + uart_send_string("\r\n"); + } + } +} + +int parse_reserved_memory(uintptr_t mem_rsvmap_addr) { + Mem_Reserve_Entry* entry = (Mem_Reserve_Entry *)mem_rsvmap_addr; + + while (__builtin_bswap64(entry->address) != 0 || __builtin_bswap64(entry->size) != 0) { + uart_send_string("Memory reserve address: "); + uart_send_int(__builtin_bswap64(entry->address)); + uart_send_string(" ~ "); + uart_send_int(__builtin_bswap64(entry->address) + __builtin_bswap64(entry->size)); + uart_send_string("\r\n"); + entry++; + } + + uart_send_string("End of parsing memory reservation block\r\n"); + return 0; +} + +void parse_rvmem_child(int type, const char* name, void* data, uintptr_t cur_ptr) { + + if (type == FDT_BEGIN_NODE) { + if (strcmp(name, "reserved-memory") == 0) { + rvmem_child_cnt++; + } else if (rvmem_child_cnt > 0) { + uart_send_string("Reserved-memory device: "); + uart_send_string((char *)name); + uart_send_string("\r\n"); + } + } else if (type == FDT_END_NODE) { + if (rvmem_child_cnt > 0) { + rvmem_child_cnt--; + } + } +} \ No newline at end of file diff --git a/Lab5/kernel/device_tree.h b/Lab5/kernel/device_tree.h new file mode 100644 index 000000000..8071edb90 --- /dev/null +++ b/Lab5/kernel/device_tree.h @@ -0,0 +1,69 @@ +#ifndef _DT_H_ +#define _DT_H_ + +#include "alloc.h" + +#define ALIGN_MEMORY_BLOCK 8 +#define ALIGN_STRUCT_BLOCK 4 +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +typedef struct _header_block { + uint32_t magic; // Contain value 0xd00dfeed(big-endian) + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +} DTBHeader; + +typedef struct _mem_block { + uint64_t address; + uint64_t size; + // Use a singly-linked list for the memory reservation blocks. + struct _mem_block* nextNode; +} DTBMemory; + +typedef struct _struct_prop { + uint32_t len; + uint32_t nameoff; + char* prop_name; + // The data type of val is defined according to its property. + void* val; + // Singly linked list for property list + struct _struct_prop* next; +} Prop; + +typedef struct _struct_node { + // Number of characters ranged from 1~31 + char node_name[32]; + Prop* prop_node_head; + struct _struct_node* next_node; + struct _stucr_node* next_child_node; +} Node; + +typedef struct _mem_reserve_entry { + uint64_t address; + uint64_t size; +} Mem_Reserve_Entry; + +extern DTBHeader myHeader; + +// Define a callback function type for processing nodes. +typedef void (*fdt_node_callback_t)(int type, const char* name, void* data, uintptr_t cur_ptr); +int fdt_traverse(fdt_node_callback_t callback, uintptr_t dtb_address); +int parse_struct(fdt_node_callback_t callback, uintptr_t dtb_start_addr); +void print_tree(int type, const char* name, void* data, uintptr_t cur_ptr); +void get_initrd_address(int type, const char* name, void* data, uintptr_t cur_ptr); +void get_initrd_end_address(int type, const char* name, void* data, uintptr_t cur_ptr); +int parse_reserved_memory(uintptr_t mem_rsvmap_addr); +void parse_rvmem_child(int type, const char* name, void* data, uintptr_t cur_ptr); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/dynamic_alloc.c b/Lab5/kernel/dynamic_alloc.c new file mode 100644 index 000000000..a59712da4 --- /dev/null +++ b/Lab5/kernel/dynamic_alloc.c @@ -0,0 +1,251 @@ +#include "dynamic_alloc.h" +#include "alloc.h" +#include "mini_uart.h" + +slab_cache_t* slab_cache; + +// Total frames from 0x0 to 0x3C000000 -> 245697 frames +void init_dynamic_alloc(void) { + init_buddy_system(); + + // The cache contains lists of slabs of size 64B, 128B, 256B. + slab_cache = (slab_cache_t *)malloc(sizeof(slab_cache_t) * 3); + for (int i = 0; i < 3; i++) { + slab_cache[i].head = NULL; + slab_cache[i].tail = NULL; + } +} + +uint64_t dynamic_alloc(uint64_t request) { + // Request larger than 256 bytes, allocate all frames. + if (request > 256) { + // Get the index of alloc_frame_list. + int alloc_ind = alloc_page(request); + + // Uncomment for demo. + // uart_send_string("Allocated frames "); + for (int i = alloc_frame_list[alloc_ind]->head->ind; i <= alloc_frame_list[alloc_ind]->tail->ind; i++) { + // For contiguous allocated frames, the slot_size records the offset between the current frame + // and the head. + frame_arr[i]->slot_size = i - alloc_frame_list[alloc_ind]->head->ind; + frame_arr[i]->slot_cnt = 0; + + // Uncomment for demo. + // uart_send_int(i); + // uart_send_string(" "); + } + + // Uncomment for demo. + // uart_send_string("\r\n"); + + return alloc_frame_list[alloc_ind]->head->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR; + } else { + // For determining which slot the request belong to. + int slab_cache_ind; + int slot_cnt; + int slot_size; + if (request > 128) { + slab_cache_ind = 2; + slot_cnt = 16; + slot_size = 256; + } else if (request > 64) { + slab_cache_ind = 1; + slot_cnt = 32; + slot_size = 128; + } else { + slab_cache_ind = 0; + slot_cnt = 64; + slot_size = 64; + } + + // Allocated slabs exist. + if (slab_cache[slab_cache_ind].head != NULL) { + entry_t* cur_entry = slab_cache[slab_cache_ind].head; + + // Find a slab with empty slots. + while (cur_entry != NULL && cur_entry->slot_cnt <= 0) + cur_entry = cur_entry->next_slot; + + // Find empty slot. + if (cur_entry != NULL) { + short free_slot_ind = 0; + + for (; free_slot_ind < slot_cnt; free_slot_ind++) { + if (cur_entry->bitmap & (1 << free_slot_ind)) { + // Mark the slot as used. + cur_entry->bitmap &= ~(1 << free_slot_ind); + cur_entry->slot_cnt--; + break; + } + } + + // Uncomment for demo. + // uart_send_string("Allocated slot "); + // uart_send_int(free_slot_ind); + // uart_send_string(" in frame "); + // uart_send_int(cur_entry->ind); + // uart_send_string("\r\n"); + + return cur_entry->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR + free_slot_ind * slot_size; + } else { + // Allocate a new page if no slot remains. + int alloc_ind = alloc_page(request); + cur_entry = alloc_frame_list[alloc_ind]->head; + + if (slot_size == 256) { + cur_entry->bitmap = 0xFFFF; + cur_entry->slot_cnt = 16; + cur_entry->slot_size = SLOT_256B; + } else if (slot_size == 128) { + cur_entry->bitmap = 0xFFFFFFFF; + cur_entry->slot_cnt = 32; + cur_entry->slot_size = SLOT_128B; + } else { + cur_entry->bitmap = 0xFFFFFFFFFFFFFFFF; + cur_entry->slot_cnt = 64; + cur_entry->slot_size = SLOT_64B; + } + + cur_entry->bitmap &= ~(0x1); + cur_entry->slot_cnt--; + + // Insert into slab cache. + slab_cache[slab_cache_ind].tail->next = cur_entry; + cur_entry->prev_slot = slab_cache[slab_cache_ind].tail; + cur_entry->next_slot = NULL; + slab_cache[slab_cache_ind].tail = cur_entry; + + // Uncomment for demo. + // uart_send_string("Allocated slot "); + // uart_send_int(0); + // uart_send_string(" in frame "); + // uart_send_int(cur_entry->ind); + // uart_send_string("\r\n"); + + return cur_entry->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR; + } + // No allocated page exists in the cache. + } else { + int alloc_ind = alloc_page(request); + entry_t* cur_entry = alloc_frame_list[alloc_ind]->head; + cur_entry->prev_slot = cur_entry; + cur_entry->next_slot = NULL; + + slab_cache[slab_cache_ind].head = cur_entry; + slab_cache[slab_cache_ind].tail = cur_entry; + + if (slot_size == 256) { + cur_entry->bitmap = 0xFFFF; + cur_entry->slot_cnt = 16; + cur_entry->slot_size = SLOT_256B; + } else if (slot_size == 128) { + cur_entry->bitmap = 0xFFFFFFFF; + cur_entry->slot_cnt = 32; + cur_entry->slot_size = SLOT_128B; + } else { + cur_entry->bitmap = 0xFFFFFFFFFFFFFFFF; + cur_entry->slot_cnt = 64; + cur_entry->slot_size = SLOT_64B; + } + + cur_entry->bitmap &= ~(0x1); + cur_entry->slot_cnt--; + + // Uncomment for demo. + // uart_send_string("Allocated slot "); + // uart_send_int(0); + // uart_send_string(" in frame "); + // uart_send_int(cur_entry->ind); + // uart_send_string("\r\n"); + + return cur_entry->ind * FRAME_SIZE + FREE_MEM_BASE_ADDR; + } + } +} + +void dynamic_free(uint64_t addr) { + // Find the frame it belongs to. + int frame_ind = (addr - FREE_MEM_BASE_ADDR) / FRAME_SIZE; + + if (frame_arr[frame_ind]->slot_size == SLOT_64B || frame_arr[frame_ind]->slot_size == SLOT_128B || frame_arr[frame_ind]->slot_size == SLOT_256B) { + int slot_size; + int slab_cache_ind; + + if (frame_arr[frame_ind]->slot_size == SLOT_64B) { + slab_cache_ind = 0; + slot_size = 64; + } else if (frame_arr[frame_ind]->slot_size == SLOT_128B) { + slab_cache_ind = 1; + slot_size = 128; + } else if (frame_arr[frame_ind]->slot_size == SLOT_256B) { + slab_cache_ind = 2; + slot_size = 256; + } + + int slot_ind = (addr - (frame_ind * FRAME_SIZE + FREE_MEM_BASE_ADDR)) / slot_size; + // The slot is free already. + if (((1 << slot_ind) & frame_arr[frame_ind]->bitmap) == (1 << slot_ind)) { + uart_send_string("Slot "); + uart_send_int(slot_ind); + uart_send_string(" in frame "); + uart_send_int(frame_ind); + uart_send_string(" is freed already!\r\n"); + return; + } + + frame_arr[frame_ind]->bitmap |= (1 << slot_ind); + frame_arr[frame_ind]->slot_cnt++; + + // Uncomment for demo. + // uart_send_string("Freeing slot "); + // uart_send_int(slot_ind); + // uart_send_string(" in frame "); + // uart_send_int(frame_ind); + // uart_send_string("\r\n"); + // uart_send_string("Remaining slots: "); + // uart_send_int(frame_arr[frame_ind]->slot_cnt); + // uart_send_string("\r\n"); + + // Free the page if all allocated slots are freed. + if (frame_arr[frame_ind]->slot_cnt == slot_size) { + frame_arr[frame_ind]->slot_size = SLOT_NOT_ALLOCATED; + free_page(frame_ind); + + // Discard the frame in slab cache. + entry_t* cur_entry = slab_cache[slab_cache_ind].head; + + while (cur_entry != NULL && cur_entry->ind != frame_ind) { + cur_entry = cur_entry->next_slot; + } + + // If current frame is at the head of cache. + if (cur_entry->prev_slot == cur_entry) { + slab_cache[slab_cache_ind].head = cur_entry->next_slot; + if (slab_cache[slab_cache_ind].head == NULL) { + slab_cache[slab_cache_ind].tail = NULL; + } else { + slab_cache[slab_cache_ind].head->prev_slot = slab_cache[slab_cache_ind].head; + } + // At the end or middle of cache. + } else { + if (cur_entry->next_slot == NULL) { + slab_cache[slab_cache_ind].tail = cur_entry->prev_slot; + slab_cache[slab_cache_ind].tail->next = NULL; + } else { + cur_entry->prev_slot->next_slot = cur_entry->next_slot; + cur_entry->next_slot->prev_slot = cur_entry->prev_slot; + } + } + cur_entry->next_slot = NULL; + cur_entry->prev_slot = NULL; + } + } else if (frame_arr[frame_ind]->slot_size == SLOT_NOT_ALLOCATED) { + uart_send_string("Memory 0x"); + uart_send_int(addr); + uart_send_string(" not allocated!\r\n"); + // Contiguous frames to free. + } else { + int head = frame_ind - frame_arr[frame_ind]->slot_size; + free_page(head); + } +} \ No newline at end of file diff --git a/Lab5/kernel/dynamic_alloc.h b/Lab5/kernel/dynamic_alloc.h new file mode 100644 index 000000000..78024cf21 --- /dev/null +++ b/Lab5/kernel/dynamic_alloc.h @@ -0,0 +1,19 @@ +#ifndef _DYNAMIC_ALLOC_H_ +#define _DYNAMIC_ALLOC_H_ + +#include "buddy_system_2.h" + +#define USED_SLOT 'U' +#define FREE_SLOT 'F' + +typedef struct _slab_cache { + entry_t* head; + entry_t* tail; +} slab_cache_t; + +void startup_allocator(void); +void init_dynamic_alloc(void); +uint64_t dynamic_alloc(uint64_t request); +void dynamic_free(uint64_t addr); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/entry.S b/Lab5/kernel/entry.S new file mode 100644 index 000000000..eb61a7cb6 --- /dev/null +++ b/Lab5/kernel/entry.S @@ -0,0 +1,182 @@ +#include "entry.h" + +.macro handle_invalid_entry type +mov x0, #\type +mrs x1, esr_el1 +mrs x2, elr_el1 +bl show_invalid_entry_message +b err_hang +.endm + +/* + * Vector table entries. + * All exception vectors should be located at offset 0x80 bytes one from another. + */ +.macro ventry label +.align 7 +b \label +.endm + +/* + * Save current register state before exception handling. + */ +.macro save_state + // 31 GPR + spsr_el1 + elr_el1, each register is 8 bytes(64-bit) + sub sp, sp, 34 * 8 + // stp(Store Pair of registers) + stp x0, x1, [sp, 16 * 0] + stp x2, x3, [sp, 16 * 1] + stp x4, x5, [sp, 16 * 2] + stp x6, x7, [sp, 16 * 3] + stp x8, x9, [sp, 16 * 4] + stp x10, x11, [sp, 16 * 5] + stp x12, x13, [sp, 16 * 6] + stp x14, x15, [sp, 16 * 7] + stp x16, x17, [sp, 16 * 8] + stp x18, x19, [sp, 16 * 9] + stp x20, x21, [sp, 16 * 10] + stp x22, x23, [sp, 16 * 11] + stp x24, x25, [sp, 16 * 12] + stp x26, x27, [sp, 16 * 13] + stp x28, x29, [sp, 16 * 14] + mrs x0, spsr_el1 + stp x30, x0, [sp, 16 * 15] + mrs x0, elr_el1 + str x0, [sp, 16 * 16] + ldp x0, x1, [sp, 16 * 0] +.endm + +/* + * Load current register state after exception handling. + */ +.macro load_state + ldp x0, x1, [sp, 16 * 0] + ldp x2, x3, [sp, 16 * 1] + ldp x4, x5, [sp, 16 * 2] + ldp x6, x7, [sp, 16 * 3] + ldp x8, x9, [sp, 16 * 4] + ldp x10, x11, [sp, 16 * 5] + ldp x12, x13, [sp, 16 * 6] + ldp x14, x15, [sp, 16 * 7] + ldp x16, x17, [sp, 16 * 8] + ldp x18, x19, [sp, 16 * 9] + ldp x20, x21, [sp, 16 * 10] + ldp x22, x23, [sp, 16 * 11] + ldp x24, x25, [sp, 16 * 12] + ldp x26, x27, [sp, 16 * 13] + ldp x28, x29, [sp, 16 * 14] + ldp x30, x0, [sp, 16 * 15] + msr spsr_el1, x0 + ldr x0, [sp, 16 * 16] + msr elr_el1, x0 + ldp x0, x1, [sp, 16 * 0] + add sp, sp, 34 * 8 +.endm + +/* + * Setup interrupt vector table. + * Vector table should be aligned to 0x800. + */ + .align 11 + .global vectors + vectors: + /* + * EL1t Exception is taken from EL1 while stack pointer was shared with EL0. + * This happens when SPSel register holds the value 0. In our lab, we won't be using this + * exception. Including all 16 handlers for easier debugging if anything goes wrong. + */ + ventry sync_invalid_el1t // Synchronous EL1t + ventry irq_invalid_el1t // IRQ EL1t + ventry fiq_invalid_el1t // FIQ EL1t + ventry error_invalid_el1t // Error EL1t + + /* + * EL1h Exception is taken from EL1 at the time when dedicated stack pointer was allocated + * for EL1. This means that SPSel holds the value 1 and this is the mode that we are currently using. + */ + ventry sync_el1h // Synchronous EL1h + ventry irq_el1h // IRQ EL1h + ventry fiq_invalid_el1h // FIQ EL1h + ventry error_invalid_el1h // Error EL1h + + /* + * EL0_64 Exception is taken from EL0 executing in 64-bit mode. + */ + ventry sync_el0_64 // Synchronous 64-bit EL0 + ventry irq_el0_64 // IRQ 64-bit EL0 + ventry fiq_invalid_el0_64 // FIQ 64-bit EL0 + ventry error_invalid_el0_64 // Error 64-bit EL0 + + /* + * EL0_32 Exception is taken from EL0 executing in 32-bit mode.(Won't be using it) + */ + ventry sync_invalid_el0_32 // Synchronous 32-bit EL0 + ventry irq_invalid_el0_32 // IRQ 32-bit EL0 + ventry fiq_invalid_el0_32 // FIQ 32-bit EL0 + ventry error_invalid_el0_32 // Error 32-bit EL0 + +sync_invalid_el1t: + handle_invalid_entry SYNC_INVALID_EL1t + +irq_invalid_el1t: + handle_invalid_entry IRQ_INVALID_EL1t + +fiq_invalid_el1t: + handle_invalid_entry FIQ_INVALID_EL1t + +error_invalid_el1t: + handle_invalid_entry ERROR_INVALID_EL1t + +fiq_invalid_el1h: + handle_invalid_entry FIQ_INVALID_EL1h + +error_invalid_el1h: + handle_invalid_entry ERROR_INVALID_EL1h + +fiq_invalid_el0_64: + handle_invalid_entry FIQ_INVALID_EL0_64 + +error_invalid_el0_64: + handle_invalid_entry ERROR_INVALID_EL0_64 + +sync_invalid_el0_32: + handle_invalid_entry SYNC_INVALID_EL0_32 + +irq_invalid_el0_32: + handle_invalid_entry IRQ_INVALID_EL0_32 + +fiq_invalid_el0_32: + handle_invalid_entry FIQ_INVALID_EL0_32 + +error_invalid_el0_32: + handle_invalid_entry ERROR_INVALID_EL0_32 + +// Jump to synchronous exception handler. +sync_el0_64: + save_state + bl handle_sync_el0_64 + load_state + eret + +// Jump to interrupt exception handler. +irq_el0_64: + save_state + bl handle_irq_el0_64 + load_state + eret + +sync_el1h: + save_state + bl handle_sync_el1h + load_state + eret + +irq_el1h: + save_state + mov x0, sp + bl handle_irq_el1h + load_state + eret + +.global err_hang +err_hang: b err_hang \ No newline at end of file diff --git a/Lab5/kernel/entry.h b/Lab5/kernel/entry.h new file mode 100644 index 000000000..8c9ce5d56 --- /dev/null +++ b/Lab5/kernel/entry.h @@ -0,0 +1,24 @@ +#ifndef _ENTRY_H_ +#define _ENTRY_H_ + +#define SYNC_INVALID_EL1t 0 +#define IRQ_INVALID_EL1t 1 +#define FIQ_INVALID_EL1t 2 +#define ERROR_INVALID_EL1t 3 + +#define SYNC_INVALID_EL1h 4 +#define IRQ_INVALID_EL1h 5 +#define FIQ_INVALID_EL1h 6 +#define ERROR_INVALID_EL1h 7 + +#define SYNC_INVALID_EL0_64 8 +#define IRQ_INVALID_EL0_64 9 +#define FIQ_INVALID_EL0_64 10 +#define ERROR_INVALID_EL0_64 11 + +#define SYNC_INVALID_EL0_32 12 +#define IRQ_INVALID_EL0_32 13 +#define FIQ_INVALID_EL0_32 14 +#define ERROR_INVALID_EL0_32 15 + +#endif \ No newline at end of file diff --git a/Lab5/kernel/exception.c b/Lab5/kernel/exception.c new file mode 100644 index 000000000..8d73e3d91 --- /dev/null +++ b/Lab5/kernel/exception.c @@ -0,0 +1,205 @@ +#include "../peripherals/mini_uart.h" +#include "timer.h" +#include "irq.h" +#include "task.h" +#include "alloc.h" +#include "exception.h" +#include "syscall.h" + +const char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; + +// System call table. +static uint64_t (*syscall_table[])(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3) = { + [SYS_GETPID] = (void *)getpid, + [SYS_UART_READ] = (void *)uart_recv, + [SYS_UART_WRITE] = (void *)uart_send +}; + +void show_invalid_entry_message(int type, unsigned long esr, unsigned long address) { + uart_send_string((char *)entry_error_messages[type]); + uart_send_string(", ESR: "); + uart_send_uint(esr); + uart_send_string(", address: "); + uart_send_uint(address); + uart_send_string("\r\n"); +} + +void handle_sync_el0_64(trap_frame_t* tf) { + + /* + * For assembly user code, it showed 0x56000000. + * [31:26] 0b010101 -> SVC instruction execution in AArch64 state. + */ + + // Retrieve system call number from user process. Typically stored in x8 in arm. + int syscall = tf->x8; + + // Check whether the system call is valid. + if (syscall < sizeof(syscall_table) / sizeof(syscall_table[0]) && syscall_table[syscall]) { + // Send the arguments if required. + tf->x0 = syscall_table[syscall](tf->x0, tf->x1, tf->x2, tf->x3); + + } +} + +void handle_irq_el0_64(void) { + unsigned long spsr_el1, elr_el1, esr_el1, sp_el0; + + asm volatile ( + "mrs %0, spsr_el1;" + "mrs %1, elr_el1;" + "mrs %2, esr_el1;" + "mrs %3, sp_el0;" + + // Output operand. + : "=r" (spsr_el1), "=r" (elr_el1), "=r" (esr_el1), "=r" (sp_el0) + : + // Clobber list. + : "x0", "x1", "x2" + ); + + uart_send_string("In handler: handle_irq_el0_64\r\n"); + uart_send_string("spsr_el1: "); + uart_send_uint(spsr_el1); + uart_send_string("\r\n"); + uart_send_string("elr_el1: "); + uart_send_uint(elr_el1); + uart_send_string("\r\n"); + uart_send_string("sp_el0: "); + uart_send_uint(sp_el0); + uart_send_string("\r\n"); + /* + * For assembly user code, it showed 0x56000000. + * [31:26] 0b010101 -> SVC instruction execution in AArch64 state. + */ + + uart_send_string("esr_el1: "); + uart_send_uint(esr_el1); + uart_send_string("\r\n"); + + // For checking whether timer caused the interrupt. + unsigned long cntp_ctl; + static int cnt = 0; + asm volatile("mrs %0, cntp_ctl_el0" : "=r" (cntp_ctl)); + + // Timer interrupt is pending + if (cntp_ctl & (1 << 2)) { + uart_send_string("Timer interrupt detected "); + uart_send_uint(cnt++); + uart_send_string(".\r\n"); + + // Reset the timer. + set_timer_expire(2); + + + } +} + +void handle_irq_el1h(uint64_t sp) { + disable_el1_interrupt(); + + uart_send_string("\r\nIn handler: handle_irq_el1h\r\n"); + uart_send_string("SP: "); + uart_send_uint(sp); + uart_send_string("\r\n"); + + task* t = (task *)simple_malloc(sizeof(task)); + t->sp = sp; + // On manual P.9 + // If AUX_IRQ[0] is set to 1, mini UART has an interrupt pending. + if (*AUX_IRQ & 0x1) { + uart_send_string("Uart interrupt\r\n"); + + t->intr_func = handle_uart_interrupt; + t->arg = NULL; + t->priority = 1; + enqueue_task(t); + enable_el1_interrupt(); + + // handle_uart_interrupt(); + } + + // Check whether timer expires. + int timer_intr; + asm volatile ( + "mrs %0, cntp_ctl_el0;" + + : "=r" (timer_intr) + : + : + ); + + // cntp_ctl_el0[2] = 1 -> timer condition met. This bit is read-only, cannot be modified. + // The bit will be cleared when a new expired time is set. + if (timer_intr & 0x4) { + uart_send_string("Timer interrupt\r\n"); + clear_timer_intr(); + // t->intr_func = handle_timer_intr; + // t->arg = NULL; + // t->priority = 0; + + // enqueue_task(t); + // enable_el1_interrupt(); + // execute_task(t); + + handle_timer_intr(NULL); + } + + + +} + +void handle_sync_el1h(void) { + + //disable_el1_interrupt(); + // uart_send_string("In handler: handle_sync_el1h\r\n"); + unsigned long spsr_el1, elr_el1, esr_el1, sp_el0; + + asm volatile ( + "mrs %0, spsr_el1;" + "mrs %1, elr_el1;" + "mrs %2, esr_el1;" + "mrs %3, sp_el0;" + + // Output operand. + : "=r" (spsr_el1), "=r" (elr_el1), "=r" (esr_el1), "=r" (sp_el0) + : + // Clobber list. + : "x0", "x1", "x2" + ); + + // uart_send_string("spsr_el1: "); + // uart_send_uint(spsr_el1); + // uart_send_string("\r\n"); + // uart_send_string("elr_el1: "); + // uart_send_uint(elr_el1); + // uart_send_string("\r\n"); + // uart_send_string("esr_el1: "); + // uart_send_uint(esr_el1); + // uart_send_string("\r\n"); + // uart_send_string("sp_el0: "); + // uart_send_uint(sp_el0); + // uart_send_string("\r\n"); + //enable_el1_interrupt(); + +} \ No newline at end of file diff --git a/Lab5/kernel/exception.h b/Lab5/kernel/exception.h new file mode 100644 index 000000000..a1c0298d0 --- /dev/null +++ b/Lab5/kernel/exception.h @@ -0,0 +1,44 @@ +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +#include + +typedef struct trap_frame { + uint64_t x0; + uint64_t x1; + uint64_t x2; + uint64_t x3; + uint64_t x4; + uint64_t x5; + uint64_t x6; + uint64_t x7; + uint64_t x8; + uint64_t x9; + uint64_t x10; + uint64_t x11; + uint64_t x12; + uint64_t x13; + uint64_t x14; + uint64_t x15; + uint64_t x16; + uint64_t x17; + uint64_t x18; + uint64_t x19; + uint64_t x20; + uint64_t x21; + uint64_t x22; + uint64_t x23; + uint64_t x24; + uint64_t x25; + uint64_t x26; + uint64_t x27; + uint64_t x28; + uint64_t fp; + uint64_t lr; + uint64_t spsr_el1; + uint64_t elr_el1; + uint64_t sp_el0; +} trap_frame_t; + + +#endif \ No newline at end of file diff --git a/Lab5/kernel/initramdisk.c b/Lab5/kernel/initramdisk.c new file mode 100644 index 000000000..31416cdcf --- /dev/null +++ b/Lab5/kernel/initramdisk.c @@ -0,0 +1,312 @@ +#include "initramdisk.h" +#include "../peripherals/mini_uart.h" +#include "../peripherals/utils.h" +#include + +struct Directory* rootDir; +// Save the current user location. +struct Directory* userDir; + +char* cpio; + +// Read the cpio file and construct the simple filesystem. +void initramdisk_main(uintptr_t cpio_addr) { + // Retrieve the cpio address from parsing the device tree blob. + cpio = (char *)cpio_addr; + uart_send_string("cpio start address: "); + uart_send_uint((uintptr_t)cpio); + uart_send_string("\r\n"); + + // Parse info for root directory. + rootDir = allocateDirectory(); + rootDir->dHeader = parse_cpio_header(); + rootDir->nDirs = 0; + rootDir->nFiles = 0; + rootDir->emptyDirInd = 0; + rootDir->emptyFileInd = 0; + // Root directory has no parent. + rootDir->parent = NULL; + userDir = rootDir; + + for (int i = 0; i < rootDir->dHeader->nameSize; i++, cpio++) { + rootDir->dName[i] = *cpio; + } + + // uart_send_string("Print root directory: "); + // uart_send_string(rootDir->dName); + // uart_send_string("\r\n"); + + // Align to 4-byte boundary. + if (((unsigned long)cpio & 3)) { + unsigned long pad = 4 - ((unsigned long)cpio & 3); + cpio += pad; + } + + // Common pattern for directories is '4' at the beginning of the 'mode' value in octal + // representation(040xxx) + // if (rootDir.dHeader.mode & 040000) + // uart_send_string("Directory!\r\n"); + + // Parse the entire cpio. + while (1) { + struct Directory* curDir = rootDir; + struct CpioHeader* header = parse_cpio_header(); + + char end[15]; + for (int i = 0; i < 11; i++) { + end[i] = *(cpio + i); + } + + if (strcmp(end, "TRAILER!!!") == 0) { + // uart_send_string("End of cpio!\r\n"); + break; + } + + // New directory path. + char path[64]; + // Records number of nested directories storing the file or directory. + int nested_dir_num = 0; + + // The null character is included in the nameSize. + for (int i = 0; i < header->nameSize; i++, cpio++) { + path[i] = *cpio; + if (path[i] == '/') + nested_dir_num++; + } + + // Align to 4-byte boundary. + if (((unsigned long)cpio & 3)) { + unsigned long pad = 4 - ((unsigned long)cpio & 3); + cpio += pad; + } + + // Navigate to the directory the new directory or new file is located. + int pathInd = 0; + + // Enter if not located in root directory. + while (nested_dir_num > 0) { + char nextDir[64]; + + int i = 0; + for (; path[pathInd] != '/'; i++, pathInd++) { + nextDir[i] = path[pathInd]; + } + nextDir[i] = '\0'; + pathInd++; + + for (int i = 0; i < curDir->nDirs; i++) { + // Check which directory to navigate to. + if (strcmp(nextDir, curDir->dirs[i]->dName) == 0) { + curDir = curDir->dirs[i]; + nested_dir_num--; + break; + } + } + } + + char name[64]; + // Read the new directory(file) name. + for (int i = 0; pathInd < header->nameSize; i++, pathInd++) { + name[i] = path[pathInd]; + } + + // New directory. + if (header->mode & 040000) { + // Initialize new directory. + struct Directory* newDir = allocateDirectory(); + newDir->dHeader = header; + newDir->emptyDirInd = 0; + newDir->emptyFileInd = 0; + newDir->nDirs = 0; + newDir->nFiles = 0; + newDir->parent = curDir; + strcpy(newDir->dName, name); + + // Link the new directory with its parent directory. + curDir->nDirs++; + curDir->dirs[curDir->emptyDirInd++] = newDir; + + // New file. + } else if (header->mode & 0100000) { + // Initialize new file. + struct File* newFile = allocateFile(); + newFile->fileSize = header->fileSize; + newFile->fHeader = header; + newFile->parent = curDir; + strcpy(newFile->fName, name); + + for (int i = 0; i < header->fileSize; i++, cpio++) { + newFile->fData[i] = *cpio; + } + + // Link the new file with its parent directory. + curDir->nFiles++; + curDir->files[curDir->emptyFileInd++] = newFile; + + // Align to 4-byte boundary. + if (((unsigned long)cpio & 3)) { + unsigned long pad = 4 - ((unsigned long)cpio & 3); + cpio += pad; + } + } + } +} + +struct CpioHeader* parse_cpio_header() { + // uart_send_string("In parse cpio header\r\n"); + struct CpioHeader* header = allocateHeader(); + char tmp[10]; + // Record the length of tmp. + int len = 8; + + // Read magic number from cpio. + for (int i = 0; i < 6; i++) { + tmp[i] = *cpio++; + } + tmp[6] = '\0'; + + if (strcmp(tmp, "070701") != 0) { + uart_send_string("Cpio not in new Ascii format!\r\n"); + } + + // Read inode number from cpio. + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->inode = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->mode = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->uid = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->guid = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->nlink = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->mtime = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->fileSize = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->devMaj = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->devMin = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->rdevMaj = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->rdevMin = strHex2Int(tmp, len); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->nameSize = strHex2Int(tmp, len); + // uart_send_uint(header->nameSize); + // uart_send_string("\r\n"); + + for (int i = 0; i < 8; i++) { + tmp[i] = *cpio++; + } + header->check = strHex2Int(tmp, len); + + // uart_send_string("Exit\r\n"); + return header; +} + +struct File* allocateFile() { + if (filePoolIndex < MAX_FILES) + return &filePool[filePoolIndex++]; + else { + uart_send_string("Maximum file count reached! Failed to create new file!\r\n"); + return NULL; + } +} + +struct Directory* allocateDirectory() { + if (directoryPoolIndex < MAX_DIRECTORIES) + return &directoryPool[directoryPoolIndex++]; + else { + uart_send_string("Maximum directory count reached! Failed to create new directory!\r\n"); + return NULL; + } +} + +struct CpioHeader* allocateHeader() { + if (headerPoolIndex < MAX_HEADERS) + return &headerPool[headerPoolIndex++]; + else { + uart_send_string("Maximum header count reached! Failed to create new header!\r\n"); + return NULL; + } +} + + +void show_current_dir_content() { + for (int i = 0; i < userDir->nDirs; i++) { + uart_send_string(userDir->dirs[i]->dName); + uart_send_string("\r\n"); + } + + for (int i = 0; i < userDir->nFiles; i++) { + uart_send_string(userDir->files[i]->fName); + uart_send_string("\r\n"); + } +} + +void change_directory(char* path) { + for (int i = 0; i < userDir->nDirs; i++) { + if (strcmp(path, userDir->dirs[i]->dName) == 0) { + userDir = userDir->dirs[i]; + return; + } + } + + uart_send_string("No such directory!\r\n"); +} + +void print_content(char* fileName) { + for (int i = 0; i < userDir->emptyFileInd; i++) { + if (strcmp(fileName, userDir->files[i]->fName) == 0) { + for (unsigned int j = 0; j < userDir->files[i]->fileSize; j++) { + if (userDir->files[i]->fData[j] == '\n') { + uart_send_string("\r\n"); + } else { + uart_send(userDir->files[i]->fData[j]); + } + } + uart_send_string("\r\n"); + return; + } + } + + uart_send_string("File doesn't exist!\r\n"); +} + diff --git a/Lab5/kernel/initramdisk.h b/Lab5/kernel/initramdisk.h new file mode 100644 index 000000000..a36cda092 --- /dev/null +++ b/Lab5/kernel/initramdisk.h @@ -0,0 +1,87 @@ +#ifndef _INITRAMDISK_H_ +#define _INITRAMDISK_H_ + +#define MAX_HEADERS 10 +#define MAX_DIRECTORIES 10 +#define MAX_FILES 10 +#define MAX_NAME_LENGTH 64 +#define MAX_FILE_SIZE 512 + +struct CpioHeader { + unsigned int inode; // inode number + unsigned int mode; // File mode + unsigned int uid; // User ID + unsigned int guid; // Group ID + unsigned int nlink; // Number of links + unsigned int mtime; // Modification time + unsigned int fileSize; // Size of file + unsigned int devMaj; // Major number of device + unsigned int devMin; // Minor number of device + unsigned int rdevMaj; // Major number of a character or block device file + unsigned int rdevMin; // Minor number of a character or block device file + unsigned int nameSize; // Length of name including the trailing NUL + unsigned int check; // Checksum +}; + +struct File { + struct CpioHeader* fHeader; + + // Maximum size for a file name is 64 bytes. + char fName[64]; + + // Maximum Data for a file name is 512 bytes. + char fData[MAX_FILE_SIZE]; + + unsigned int fileSize; + + // Stores the parent directory. + struct Directory* parent; +}; + +struct Directory { + struct CpioHeader* dHeader; + + // Maximum size for a directory name is 64 bytes. + char dName[64]; + + // Keep track of the number of files inside that directory. + int nFiles; + // Keep track of the number of directories inside that directory. + int nDirs; + // Keep track of current empty directory index. + int emptyDirInd; + // Keep track of current empty file index. + int emptyFileInd; + + // Stores the parent directory. + struct Directory* parent; + // Define the maximum number of directories and files stored within a directory. + struct Directory* dirs[MAX_DIRECTORIES]; + struct File* files[10]; +}; + +// Static memory pool for headers. +static struct CpioHeader headerPool[MAX_HEADERS]; +static int headerPoolIndex = 0; + +// Static memory pool for directories. +static struct Directory directoryPool[MAX_DIRECTORIES]; +static int directoryPoolIndex = 0; + +// Static memory pool for files. +static struct File filePool[MAX_FILES]; +static int filePoolIndex = 0; + +extern struct Directory* rootDir; +extern struct Directory* userDir; + +void initramdisk_main(); +struct CpioHeader* parse_cpio_header(); +struct Directory* allocateDirectory(); +struct File* allocateFile(); +struct CpioHeader* allocateHeader(); +void show_current_dir_content(); +void change_directory(char* path); +void print_content(char* arg); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/irq.c b/Lab5/kernel/irq.c new file mode 100644 index 000000000..9a7211f49 --- /dev/null +++ b/Lab5/kernel/irq.c @@ -0,0 +1,21 @@ +#include "irq.h" + +void irq_vector_init(void) { + asm volatile ( + "adr x0, vectors;" + "msr vbar_el1, x0;" + "ret;" + ); +} + +void enable_el1_interrupt(void) { + asm volatile( + "msr DAIFClr, 0xf;" + ); +} + +void disable_el1_interrupt(void) { + asm volatile( + "msr DAIFSet, 0xf;" + ); +} \ No newline at end of file diff --git a/Lab5/kernel/irq.h b/Lab5/kernel/irq.h new file mode 100644 index 000000000..127857f98 --- /dev/null +++ b/Lab5/kernel/irq.h @@ -0,0 +1,14 @@ +#ifndef _IRQ_H_ +#define _IRQ_H_ + +#include +#include "exception.h" + +void irq_vector_init(void); +void enable_el1_interrupt(void); +void disable_el1_interrupt(void); +void handle_sync_el0_64(trap_frame_t* tf); +void irq_el0_64(void); +void handle_sync_el1h(void); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/kernel.c b/Lab5/kernel/kernel.c new file mode 100644 index 000000000..2134b3673 --- /dev/null +++ b/Lab5/kernel/kernel.c @@ -0,0 +1,66 @@ +#include "../peripherals/mini_uart.h" +#include "shell.h" +#include "initramdisk.h" +#include "device_tree.h" +#include "irq.h" +#include "timer.h" +#include "task.h" +#include "dynamic_alloc.h" +#include "buddy_system_2.h" +#include "thread.h" +#include "kernel.h" + + +/* + * Use 0x1000_0000 ~ 0x2000_0000 for page frame allocator. + */ +void kernel_main(uintptr_t dtb_address) +{ + /* + asm volatile ( + // CurrentEL register(64-bit) stores the current exception level. + // bits[3:2] + // 0b00 -> EL0 + // 0b01 -> EL1 + // 0b10 -> EL2 + // 0b11 -> EL3 + "mrs x0, CurrentEL;" + ); + */ +/* + // Test asynchronous UART. + irq_vector_init(); + enable_el1_interrupt(); + enable_uart_interrupt(); + + // Infinite loop for testing asynchronous UART + while(1) {} +*/ + + init_kernel(); + + enable_el1_interrupt(); + + uart_send_string("Time after booting: "); + uart_send_uint(get_current_time()); + uart_send_string(" seconds\r\n"); + + fdt_traverse(get_initrd_address, dtb_address); + fdt_traverse(get_initrd_end_address, dtb_address); + if (parse_reserved_memory(dtb_address + myHeader.off_mem_rsvmap) != 0) { + uart_send_string("Error parsing memory reservation block!\r\n"); + } + + shell(); +} + +void init_kernel(void) { + irq_vector_init(); + core_timer_enable(); + init_heap(); + init_heap2(); + init_task_queue(); + init_dynamic_alloc(); + init_thread_pool(); + init_scheduler(); +} diff --git a/Lab5/kernel/kernel.h b/Lab5/kernel/kernel.h new file mode 100644 index 000000000..4c904518a --- /dev/null +++ b/Lab5/kernel/kernel.h @@ -0,0 +1,6 @@ +#ifndef _KERNEL_H_ +#define _KERNEL_H_ + +void init_kernel(void); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/linker.ld b/Lab5/kernel/linker.ld new file mode 100644 index 000000000..7efac520d --- /dev/null +++ b/Lab5/kernel/linker.ld @@ -0,0 +1,26 @@ +# Define 32MB for heap. +HEAP_SIZE = 0x2000000; + +STACK_PTR = 0x2295B88; + +SECTIONS +{ + . = 0x80000; + .text.boot : { *(.text.boot) } + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + # Use NOLOAD in .bss ensures that this section is allocated space in the memory layout without + # attempting to load any pre-initialized data from the binary. + .bss (NOLOAD) : { + . = ALIGN(0x8); + bss_begin = .; + *(.bss*) + bss_end = .; + } + + . = ALIGN(0x8); + __heap_start = .; + . = . + HEAP_SIZE; + __heap_end = .; +} \ No newline at end of file diff --git a/Lab5/kernel/memory_alloc.c b/Lab5/kernel/memory_alloc.c new file mode 100644 index 000000000..4a4265f1e --- /dev/null +++ b/Lab5/kernel/memory_alloc.c @@ -0,0 +1,325 @@ +#include "memory_alloc.h" +#include "alloc.h" +#include "mini_uart.h" + +// mpool[i] stores the corresponding information for frame[i]. +mem_pool* mpool; + +void init_memory_pool(void) { + + mpool = (mem_pool *)malloc(sizeof(mem_pool) * (free_mem_size / BLOCK_SIZE)); + + // for (uint64_t i = 0; i < 10; i++) { + // mpool[i].allocate_all = 0; + // mpool[i]._32B_available = 8; + // mpool[i]._64B_available = 4; + // mpool[i]._128B_available = 4; + // mpool[i]._256B_available = 4; + // mpool[i]._512B_available = 2; + // mpool[i]._1024B_available = 1; + + // for (int j = 0; j < 8; j++) { + // mpool[i]._32B[j].free = 1; + + // if (j < 1) { + // mpool[i]._1024B[j].free = 1; + // } + + // if (j < 2) { + // mpool[i]._512B[j].free = 1; + // } + + // // 64, 128, 256 bytes only have 4 slots. + // if (j < 4) { + // mpool[i]._64B[j].free = 1; + // mpool[i]._128B[j].free = 1; + // mpool[i]._256B[j].free = 1; + // } + // } + // } +} + + +uint64_t dynamic_malloc(uint64_t request_size) { + + uart_send_string("\r\n---------------------------------------\r\n"); + uart_send_string("Dynamic Allocator\r\n"); + uart_send_string("---------------------------------------\r\n"); + + + // If the request size is larger than 1KB, allocate entire frame(or several frames) for the request. + if (request_size > 1024) { + allocate_info* info = allocate_frame(request_size); + + if (info == NULL) { + uart_send_string("Request exceeded largest frame size!\r\n"); + return -1; + } + + uart_send_string("Allocated frames: "); + + for (int i = info->start_frame; i <= info->last_frame; i++) { + uart_send_int(i); + uart_send_string(" "); + mpool[i].allocate_all = 1; + } + + uart_send_string("for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return info->start_frame * 4096 + FREE_MEM_BASE_ADDR; + } else { + // Check allocated memory for remaining slots available. + for (int i = 0; i < MAX_ALLOC; i++) { + if (alloc_mem_list[i] != NULL) { + for (int j = alloc_mem_list[i]->start_frame; j <= alloc_mem_list[i]->last_frame; j++) { + // The entire frame(or several frames) is allocated for a single request. + if (mpool[j].allocate_all) { + break; + } + + if (request_size <= 32) { + // Current frame has 32 byte slot left. + if (mpool[j]._32B_available > 0) { + mpool[j]._32B_available--; + for (int s = 0; s < 8; s++) { + if (mpool[j]._32B[s].free) { + mpool[j]._32B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + 32 * s; + } + } + } else { + continue; + } + } else if (request_size > 32 && request_size <= 64) { + if (mpool[j]._64B_available > 0) { + mpool[j]._64B_available--; + for (int s = 0; s < 4; s++) { + if (mpool[j]._64B[s].free) { + mpool[j]._64B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 8); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * s); + } + } + } else { + continue; + } + } else if (request_size > 64 && request_size <= 128) { + if (mpool[j]._128B_available > 0) { + mpool[j]._128B_available--; + for (int s = 0; s < 4; s++) { + if (mpool[j]._128B[s].free) { + mpool[j]._128B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 12); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * s); + } + } + } else { + continue; + } + } else if (request_size > 128 && request_size <= 256) { + if (mpool[j]._256B_available > 0) { + mpool[j]._256B_available--; + for (int s = 0; s < 4; s++) { + if (mpool[j]._256B[s].free) { + mpool[j]._256B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 16); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * s); + } + } + } else { + continue; + } + } else if (request_size > 256 && request_size <= 512) { + if (mpool[j]._512B_available > 0) { + mpool[j]._512B_available--; + for (int s = 0; s < 2; s++) { + if (mpool[j]._512B[s].free) { + mpool[j]._512B[s].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(s + 20); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4) + (512 * s); + } + } + } else { + continue; + } + } else if (request_size > 512 && request_size <= 1024) { + if (mpool[j]._1024B_available > 0) { + mpool[j]._1024B_available--; + + if (mpool[j]._1024B[0].free) { + mpool[j]._1024B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(22); + uart_send_string(" in frame "); + uart_send_int(j); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (j * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4) + (512 * 2); + } + + } else { + continue; + } + } + } + } + } + + // No remaining slots available. Allocate new frame. + allocate_info* info = allocate_frame(request_size); + + // Initialize memory pool for new frame. + mpool[info->start_frame].allocate_all = 0; + mpool[info->start_frame]._32B_available = 8; + mpool[info->start_frame]._64B_available = 4; + mpool[info->start_frame]._128B_available = 4; + mpool[info->start_frame]._256B_available = 4; + mpool[info->start_frame]._512B_available = 2; + mpool[info->start_frame]._1024B_available = 1; + + for (int j = 0; j < 8; j++) { + mpool[info->start_frame]._32B[j].free = 1; + + if (j < 1) { + mpool[info->start_frame]._1024B[j].free = 1; + } + + if (j < 2) { + mpool[info->start_frame]._512B[j].free = 1; + } + + // 64, 128, 256 bytes only have 4 slots. + if (j < 4) { + mpool[info->start_frame]._64B[j].free = 1; + mpool[info->start_frame]._128B[j].free = 1; + mpool[info->start_frame]._256B[j].free = 1; + } + } + + if (request_size <= 32) { + mpool[info->start_frame]._32B_available--; + mpool[info->start_frame]._32B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(0); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return info->start_frame * 4096 + FREE_MEM_BASE_ADDR; + } else if (request_size > 32 && request_size <= 64) { + mpool[info->start_frame]._64B_available--; + mpool[info->start_frame]._64B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(8); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8); + } else if (request_size > 64 && request_size <= 128) { + mpool[info->start_frame]._128B_available--; + mpool[info->start_frame]._128B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(12); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4); + } else if (request_size > 128 && request_size <= 256) { + mpool[info->start_frame]._256B_available--; + mpool[info->start_frame]._256B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(16); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4); + } else if (request_size > 256 && request_size <= 512) { + mpool[info->start_frame]._512B_available--; + mpool[info->start_frame]._512B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(20); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4); + } else if (request_size > 512 && request_size <= 1024) { + mpool[info->start_frame]._1024B_available--; + mpool[info->start_frame]._1024B[0].free = 0; + + uart_send_string("Allocated slot "); + uart_send_int(22); + uart_send_string(" in frame "); + uart_send_int(info->start_frame); + uart_send_string(" for request "); + uart_send_int(request_size); + uart_send_string(" bytes\r\n"); + + return (info->start_frame * 4096 + FREE_MEM_BASE_ADDR) + (32 * 8) + (64 * 4) + (128 * 4) + (256 * 4) + (512 * 2); + } + } +} \ No newline at end of file diff --git a/Lab5/kernel/memory_alloc.h b/Lab5/kernel/memory_alloc.h new file mode 100644 index 000000000..8b87c1c60 --- /dev/null +++ b/Lab5/kernel/memory_alloc.h @@ -0,0 +1,55 @@ +#ifndef _MEMORY_ALLOC_H_ +#define _MEMORY_ALLOC_H_ + +#include "buddy_system.h" + +// 0x10000000 ~ 0x1FFFFFFF +// #define FREE_MEM_BASE_ADDR 0x10000000 + +typedef struct _slot { + short free; +} slot; + +/* + * Stores the slots for each frame(4 KB). + * Each frame consists of: + * 32B -> 8 slots + * 64B -> 4 slots + * 128B -> 4 slots + * 256B -> 4 slots + * 512B -> 2 slots + * 1024B -> 1 slot + */ +typedef struct _mem_pool { + // Remaining available slots for each size. + short _32B_available; + short _64B_available; + short _128B_available; + short _256B_available; + short _512B_available; + short _1024B_available; + + // The entire block is allocated for one single request. + short allocate_all; + + /* + * slot index within the frame. Calculate the physical address of the slot with the index. + * 32B x 8 -> slot 0 ~ 7 + * 64B x 4 -> slot 8 ~ 11 + * 128B x 4 -> slot 12 ~ 15 + * 256B x 4 -> slot 16 ~ 19 + * 512B x 2 -> slot 20 ~ 21 + * 1024B x 1 -> slot 22 + */ + slot _32B[8]; + slot _64B[4]; + slot _128B[4]; + slot _256B[4]; + slot _512B[2]; + slot _1024B[1]; +} mem_pool; + +void init_memory_pool(void); +uint64_t dynamic_malloc(uint64_t request_size); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/reboot.c b/Lab5/kernel/reboot.c new file mode 100644 index 000000000..bb5d4eed5 --- /dev/null +++ b/Lab5/kernel/reboot.c @@ -0,0 +1,16 @@ +#include "reboot.h" + +void set(long addr, unsigned int value) { + volatile unsigned int* point = (unsigned int*)addr; + *point = value; +} + +void reset(int tick) { // reboot after watchdog timer expire + set(PM_RSTC, PM_PASSWORD | 0x20); // full reset + set(PM_WDOG, PM_PASSWORD | tick); // number of watchdog tick +} + +void cancel_reset() { + set(PM_RSTC, PM_PASSWORD | 0); // full reset + set(PM_WDOG, PM_PASSWORD | 0); // number of watchdog tick +} \ No newline at end of file diff --git a/Lab5/kernel/reboot.h b/Lab5/kernel/reboot.h new file mode 100644 index 000000000..590bb40d7 --- /dev/null +++ b/Lab5/kernel/reboot.h @@ -0,0 +1,12 @@ +#ifndef _REBOOT_H_ +#define _REBOOT_H_ + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC 0x3F10001c +#define PM_WDOG 0x3F100024 + +void set(long addr, unsigned int value); +void reset(int tick); +void cancel_reset(); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/shell.c b/Lab5/kernel/shell.c new file mode 100644 index 000000000..3edf4f137 --- /dev/null +++ b/Lab5/kernel/shell.c @@ -0,0 +1,435 @@ +#include "../peripherals/mini_uart.h" +#include "shell.h" +#include "../peripherals/utils.h" +#include "../peripherals/mailbox.h" +#include "reboot.h" +#include "initramdisk.h" +#include "user.h" +#include "timer.h" +#include "buddy_system_2.h" +#include "dynamic_alloc.h" +#include "thread.h" + +// List of commands. +char cmd_list[COMMAND_COUNT][SHELL_BUF_MAXSIZE] = { + "help", + "hello", + "reboot", + "board-revision", + "get-memory-info", + "ls", + "cd", + "cat", + "loaduser", + "setTimeout", + "bootTime", + "allocate-page", + "free-page", + "show-allocated-page", + "freelist", + // Dynamic allocator + "dalloc", + "dfree", + "rsv_mem", + "multithreading" +}; + +// If 0, command is valid, otherwise invalid. +int cmd_status; + +void reset_buf(char* buf) { + for (int i = 0; i < SHELL_BUF_MAXSIZE; i++) { + buf[i] = 0; + } +} + +// Parse the entire command list to check which command the user inputs. +void handle_command(char* cmd) { + // Iterate through all existing commands within the entire command list. + int cmd_list_ind = 0; + // Iterate through the cmd buffer and parse commands, flags, arguments etc. + int cmd_ind = 0; + + // Store the arguments for commands. + char arg[MAX_ARG_COUNT][MAX_ARG_SIZE]; + + // Store the main command. + char command[10]; + + // Parse the command. + for (; cmd_ind < SHELL_BUF_MAXSIZE; cmd_ind++) { + if (cmd[cmd_ind] == ' ' || cmd[cmd_ind] == '\0') { + command[cmd_ind++] = '\0'; + break; + } + + command[cmd_ind] = cmd[cmd_ind]; + } + + // Record the number of arguments. + int arg_cnt = 0; + int arg_ind = 0; + + // Ends until strlen(cmd) + 1 is for capturing '\0'. + for (; cmd_ind < strlen(cmd) + 1; cmd_ind++) { + // End of an argument. + if (cmd[cmd_ind] == ' ' || cmd[cmd_ind] == '\0') { + arg[arg_cnt][arg_ind] = '\0'; + arg_cnt++; + arg_ind = 0; + + if (cmd[cmd_ind] == '\0') + break; + } else { + arg[arg_cnt][arg_ind] = cmd[cmd_ind]; + arg_ind++; + } + } + + // Check corresponding command within the command list. + for (; cmd_list_ind < COMMAND_COUNT; cmd_list_ind++) { + if (strcmp(cmd_list[cmd_list_ind], command) == 0) + break; + } + + // If no command was found inside command list. + if (cmd_list_ind >= COMMAND_COUNT) { + uart_send_string("Undefined command!\r\n"); + return; + } + + switch(cmd_list_ind) { + // "help" command. + case 0: + uart_send_string("\r\n"); + uart_send_string(" help : print this help menu\r\n"); + uart_send_string(" hello : print Hello World!\r\n"); + uart_send_string(" reboot : reboot the device\r\n"); + uart_send_string(" board-revision : print board revision\r\n"); + uart_send_string(" get-memory-info : print ARM memory base address and size\r\n"); + uart_send_string(" ls : print files in current directory\r\n"); + uart_send_string(" cd : cd [dir], change the shell working directory\r\n"); + uart_send_string(" loaduser : loaduser [file], load a user program from initial ramdisk to execute.\r\n"); + uart_send_string("\r\n"); + break; + // print "hello". + case 1: + uart_send_string("Hello World!\r\n"); + break; + // Reboot RPi3. + case 2: + reset(1000); + break; + // Print board revisiion. + case 3: + get_board_revision(); + uart_send_string("\r\n"); + break; + // Print ARM memory base address and size. + case 4: + get_arm_memory(); + uart_send_string("\r\n"); + break; + // "ls" command. + case 5: + show_current_dir_content(); + break; + // "cd" command. + case 6: + if (arg_cnt > 1) { + uart_send_string(" cd: too many arguments\r\n"); + break; + } + change_directory(arg[0]); + break; + // "cat" command. + case 7: + for (int i = 0; i < arg_cnt; i++) { + print_content(arg[i]); + } + break; + // Load a user program within initramfs. + case 8: + if (arg_cnt > 1) { + uart_send_string(" loaduser: too many arguments\r\n"); + break; + } + int i = 0; + for (; i < userDir->nFiles; i++) { + // Find the specified file in the current directory. + if (strcmp(userDir->files[i]->fName, arg[0]) == 0) { + // Send the address of the file into the function. + load_user_prog((uintptr_t)userDir->files[i]); + break; + } + } + + if (i == userDir->nFiles) { + uart_send_string(" loaduser: file not found.\r\n"); + } + + break; + // Set timeout. + case 9: + + if (arg_cnt > 2) { + uart_send_string(" setTimeout: too many arguments\r\n"); + break; + } else if (arg_cnt < 2) { + uart_send_string(" setTimeout: not enough arguments\r\n"); + break; + } + + int time = str2Int(arg[1], strlen(arg[1])); + add_timer(print_timeout_message, (void *)arg[0], time); + + + // Test preemption. + // int time = str2Int(arg[1], strlen(arg[1])); + // if (arg[2][0] == '0') { + // uart_send_string("Add exit loop\r\n"); + // add_timer(exit_loop, (void *)arg[0], time); + // } else if (arg[2][0] == '1') { + // uart_send_string("Add create loop\r\n"); + // add_timer(create_loop, (void *)arg[0], time); + // } + + break; + // Check the time(in secs) after booting and set the next timeout to 2 seconds. + case 10: + if (arg_cnt > 0) { + uart_send_string(" bootTime: too many arguments\r\n"); + break; + } + + int cur_time = get_current_time(); + uart_send_string("Time after booting: "); + uart_send_uint(cur_time); + uart_send_string("\r\n"); + char message[30]; + strcpy(message, "Timeout after booting\r\n"); + add_timer(print_timeout_message, message, 2); + break; + // Allocate page frames. + case 11: + if (arg_cnt > 1) { + uart_send_string(" allocate-mem: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" allocate-mem: not enough arguments\r\n"); + break; + } + uint64_t size = str2Int(arg[0], strlen(arg[0])); + uart_send_string("size: "); + uart_send_int(size); + uart_send_string("\r\n"); + + // allocate_info* info = allocate_frame(size * 1024); + int ind = alloc_page(size * 1024); + + uart_send_string("Allocate frames for "); + uart_send_int(size); + uart_send_string(" KB\r\n"); + + if (ind == -1) { + uart_send_string("allocate-mem failed!\r\n"); + break; + } + + uart_send_string("Allocated frames: "); + for (int i = alloc_frame_list[ind]->head->ind; i <= alloc_frame_list[ind]->tail->ind; i++) { + uart_send_int(i); + uart_send_string(" "); + } + uart_send_string("\r\n"); + + break; + case 12: + if (arg_cnt > 1) { + uart_send_string(" free-page: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" free-page: not enough arguments\r\n"); + break; + } + int frame = str2Int(arg[0], strlen(arg[0])); + free_page(frame); + break; + case 13: + show_alloc_list(); + break; + case 14: + if (arg_cnt > 0) { + uart_send_string(" freelist: too many arguments\r\n"); + break; + } + show_free_list(); + break; + case 15: + if (arg_cnt > 1) { + uart_send_string(" dalloc: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" dalloc: not enough arguments\r\n"); + break; + } + + size = str2Int(arg[0], strlen(arg[0])); + uint64_t allocated_addr = dynamic_alloc(size); + uart_send_string("Allocated physical address: 0x"); + uart_send_uint(allocated_addr); + uart_send_string("\r\n"); + break; + // Check reserved memory. + case 16: + if (arg_cnt > 1) { + uart_send_string(" dfree: too many arguments\r\n"); + break; + } else if (arg_cnt < 1) { + uart_send_string(" dfree: not enough arguments\r\n"); + break; + } + + uint64_t free_addr = str2Int(arg[0], strlen(arg[0])); + dynamic_free(free_addr); + + break; + case 17: + if (arg_cnt > 2) { + uart_send_string(" rsv_mem: too many arguments\r\n"); + break; + } else if (arg_cnt < 2) { + uart_send_string(" rsv_mem: not enough arguments\r\n"); + break; + } + + uint64_t start = str2Int(arg[0], strlen(arg[0])); + uint64_t end = str2Int(arg[1], strlen(arg[1])); + // memory_reserve(start, end); + // Test multithreading. + case 18: + if (arg_cnt > 0) { + uart_send_string(" multithreading: too many arguments\r\n"); + break; + } + + thread_t* t1 = thread_create(foo); + thread_t* t2 = thread_create(foo); + thread_t* t3 = thread_create(foo); + + idle_thread(); + + break; + // Test getpid() system call. + case 19: + if (arg_cnt > 0) { + uart_send_string(" getpid: too many arguments\r\n"); + break; + } + + + break; + default: + break; + } + +} + +void shell() { + + // Buffer to store commands. + char cmd[SHELL_BUF_MAXSIZE]; + + // Acts as a pointer to the cmd buffer. + int pos = 0; + + // To avoid printing command prompt when pressing enter with empty input. + int press_backspace = 0; + int press_arrow_key = 0; + int press_space = 0; + + uart_send_string("Welcome to Josh's mini OS!\r\n"); + + // Wait for user input. + while (1) { + // Clear buffer for new input. + reset_buf(cmd); + pos = 0; + + // Ready to fetch input command from the user. + if (!press_backspace && !press_arrow_key && !press_space) + uart_send_string("josh@raspberrypi:~ $ "); + cmd_status = VALID_COMMAND; + char c = uart_recv(); + + // If a backspace character is entered. On M3 mac, 0x7F(Delete) is sent when backspace is pressed. + // On some other configurations, 0x08(backspace) might be sent. + if (c == 0x7F || c == 0x08) { + press_backspace = 1; + continue; + // User pressed enter with no input.(Enter: 0xA, Carriage return: 0xD) + } else if (c == 0xA || c == 0xD) { + uart_send_string("\r\n"); + continue; + // User pressed left arrow with no input. Arrow keys pass multi-bytes. "0xE0" and "0x4B". + } else if (c == 0xE0 || c == 0x8B) { + // uart_send_string("In\r\n"); + press_arrow_key = 1; + continue; + } + + // if (press_arrow_key) { + // if (c == 0x4B) { + // uart_send_string("In2\r\n"); + // continue; + // } + // } + + press_backspace = 0; + press_arrow_key = 0; + press_space = 0; + // Print the user input on the screen. + uart_send(c); + + // Discard all leading white spaces. + if (c == ' ') { + press_space = 1; + continue; + } + + // Fetch all characters the user inputs. + while ((c != '\n') && (c != '\r')) { + if (c == 0x7F || c == 0x08) { + if (pos) { + cmd[pos--] = '\0'; + uart_send('\b'); + uart_send(' '); + uart_send('\b'); + } + } else { + cmd[pos++] = c; + + // Print error message if user inputs too many characters. + if (pos >= SHELL_BUF_MAXSIZE) { + cmd_status = COMMAND_EXCEED_SIZELIMIT; + reset_buf(cmd); + pos = 0; + uart_send_string("Exceed input command size!\r\n"); + break; + } + } + c = uart_recv(); + uart_send(c); + } + + uart_send_string("\r\n"); + + if (cmd_status != VALID_COMMAND) + continue; + + // Add an ending string character to the input command. + cmd[pos] = '\0'; + + handle_command(cmd); + } +} \ No newline at end of file diff --git a/Lab5/kernel/shell.h b/Lab5/kernel/shell.h new file mode 100644 index 000000000..be10ef416 --- /dev/null +++ b/Lab5/kernel/shell.h @@ -0,0 +1,14 @@ +#ifndef _SHELL_H_ +#define _SHELL_H_ + +#define COMMAND_COUNT 19 +#define SHELL_BUF_MAXSIZE 256 +#define MAX_ARG_COUNT 5 +#define MAX_ARG_SIZE 16 + +#define VALID_COMMAND 0 +#define COMMAND_EXCEED_SIZELIMIT 1 + +void shell(); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/syscall.c b/Lab5/kernel/syscall.c new file mode 100644 index 000000000..35445ad5b --- /dev/null +++ b/Lab5/kernel/syscall.c @@ -0,0 +1,6 @@ +#include "syscall.h" +#include "thread.h" + +uint64_t getpid(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3) { + return cur_thread->pid; +} \ No newline at end of file diff --git a/Lab5/kernel/syscall.h b/Lab5/kernel/syscall.h new file mode 100644 index 000000000..fa2952fec --- /dev/null +++ b/Lab5/kernel/syscall.h @@ -0,0 +1,16 @@ +#ifndef _SYSCALL_H_ +#define _SYSCALL_H_ + +#include + +#define SYS_GETPID 0 +#define SYS_UART_READ 1 +#define SYS_UART_WRITE 2 +#define SYS_EXEC 3 +#define SYS_FORK 4 +#define SYS_EXIT 5 +#define SYS_MAILBOX 6 + +uint64_t getpid(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3) ; + +#endif \ No newline at end of file diff --git a/Lab5/kernel/task.c b/Lab5/kernel/task.c new file mode 100644 index 000000000..e602a8747 --- /dev/null +++ b/Lab5/kernel/task.c @@ -0,0 +1,136 @@ +#include "task.h" +#include "alloc.h" +#include "irq.h" +#include "entry.h" +#include "../peripherals/mini_uart.h" + +task_queue* t_priority_queue[MIN_PRIORITY + 1]; +int current_priority; + +void init_task_queue(void) { + for (int i = 0; i <= MIN_PRIORITY; i++) { + t_priority_queue[i] = (task_queue *)simple_malloc(sizeof(task_queue)); + t_priority_queue[i]->front = NULL; + t_priority_queue[i]->end = NULL; + } + current_priority = 10; +} + +// Insert the task into queue. +void enqueue_task(task* t) { + // The priority queue is empty initially. + if (t_priority_queue[t->priority]->front == NULL) { + t_priority_queue[t->priority]->front = t; + t_priority_queue[t->priority]->end = t; + } else { + t_priority_queue[t->priority]->end->next = t; + t_priority_queue[t->priority]->end = t_priority_queue[t->priority]->end->next; + } + t->next = NULL; +} + +// Dequeue the task with highest priority. +task* dequeue_task(void) { + for (int i = 0; i <= MIN_PRIORITY; i++) { + if (t_priority_queue[i]->front != NULL) { + task* tmp = t_priority_queue[i]->front; + t_priority_queue[i]->front = t_priority_queue[i]->front->next; + if (t_priority_queue[i]->front == NULL) { + t_priority_queue[i]->end = NULL; + } + + return tmp; + } + } + + return NULL; +} + +void execute_task() { + disable_el1_interrupt(); + // Iterate through the task queues and execute the tasks from high priority to + // low priority. + task* t = NULL; + for (int i = 0; i <= MIN_PRIORITY; i++) { + if (t_priority_queue[i]->front != NULL) { + if (t_priority_queue[i]->front->priority < current_priority) { + t = dequeue_task(); + break; + } + } + } + + if (t != NULL) { + // If the task has higher priority, execute it first. + int prev = current_priority; + current_priority = t->priority; + enable_el1_interrupt(); + t->intr_func(t->arg); + current_priority = prev; + // Check the queue recursively until no task is left. + execute_task(); + } + enable_el1_interrupt(); +} + +// Store the context of current task. +void save_context(task* t) { + asm volatile ( + // stp(Store Pair of registers) + "stp x0, x1, [%0, 16 * 0];" + "stp x2, x3, [%0, 16 * 1];" + "stp x4, x5, [%0, 16 * 2];" + "stp x6, x7, [%0, 16 * 3];" + "stp x8, x9, [%0, 16 * 4];" + "stp x10, x11, [%0, 16 * 5];" + "stp x12, x13, [%0, 16 * 6];" + "stp x14, x15, [%0, 16 * 7];" + "stp x16, x17, [%0, 16 * 8];" + "stp x18, x19, [%0, 16 * 9];" + "stp x20, x21, [%0, 16 * 10];" + "stp x22, x23, [%0, 16 * 11];" + "stp x24, x25, [%0, 16 * 12];" + "stp x26, x27, [%0, 16 * 13];" + "stp x28, x29, [%0, 16 * 14];" + "mrs x0, spsr_el1;" + "stp x30, x0, [%0, 16 * 15];" + "mrs x0, elr_el1;" + "str x0, [%0, 16 * 16];" + "ldp x0, x1, [%0, 16 * 0];" + + // Output + : + // Input + : "r" (t->sp) + : + ); +} + +void load_context(task* t) { + asm volatile ( + "ldp x0, x1, [%0, 16 * 0];" + "ldp x2, x3, [%0, 16 * 1];" + "ldp x4, x5, [%0, 16 * 2];" + "ldp x6, x7, [%0, 16 * 3];" + "ldp x8, x9, [%0, 16 * 4];" + "ldp x10, x11, [%0, 16 * 5];" + "ldp x12, x13, [%0, 16 * 6];" + "ldp x14, x15, [%0, 16 * 7];" + "ldp x16, x17, [%0, 16 * 8];" + "ldp x18, x19, [%0, 16 * 9];" + "ldp x20, x21, [%0, 16 * 10];" + "ldp x22, x23, [%0, 16 * 11];" + "ldp x24, x25, [%0, 16 * 12];" + "ldp x26, x27, [%0, 16 * 13];" + "ldp x28, x29, [%0, 16 * 14];" + "ldp x30, x0, [%0, 16 * 15];" + "msr spsr_el1, x0;" + "ldr x0, [%0, 16 * 16];" + "msr elr_el1, x0;" + "ldp x0, x1, [%0, 16 * 0];" + + : + : "r" (t->sp) + : + ); +} \ No newline at end of file diff --git a/Lab5/kernel/task.h b/Lab5/kernel/task.h new file mode 100644 index 000000000..f8b6e0425 --- /dev/null +++ b/Lab5/kernel/task.h @@ -0,0 +1,39 @@ +#ifndef _TASK_H_ +#define _TASK_H_ + +#include + +// There can be maximum of 10 priorities(0 ~ 9). +#define MIN_PRIORITY 9 + +// Function pointer for the handler function to be executed. +typedef void(*task_func_t)(void *); + +typedef struct _task { + task_func_t intr_func; + // Argument for interrupt function. + void* arg; + int priority; + struct _task* next; + // Stores the stack pointer of the current task to keep track in case of nested interrupt. + uint64_t sp; +} task; + +typedef struct _task_queue { + task* front; + task* end; +} task_queue; + +// Front -> highest priority +// End -> Lowest priority +extern task_queue* priority_q[MIN_PRIORITY + 1]; + +void save_context(task* t); +void load_context(task* t); + +void init_task_queue(void); +void enqueue_task(task* t); +task* dequeue_task(void); +void execute_task(); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/thread.c b/Lab5/kernel/thread.c new file mode 100644 index 000000000..cd1d27e94 --- /dev/null +++ b/Lab5/kernel/thread.c @@ -0,0 +1,129 @@ +#include "thread.h" +#include "dynamic_alloc.h" +#include "../peripherals/mini_uart.h" +#include "../peripherals/utils.h" + +thread_t thread_pool[MAX_THREADS]; +Queue run_queue; +Queue wait_queue; +Queue dead_queue; +thread_t* cur_thread; + +void init_thread_pool(void) { + for (int i = 0; i < MAX_THREADS; i++) { + thread_pool[i].free = TRUE; + thread_pool[i].pid = i; + thread_pool[i].state = CREATED; + } +} + +void init_scheduler(void) { + // Initialize run queue and wait queue. + init_queue(&run_queue); + init_queue(&wait_queue); + init_queue(&dead_queue); +} + +void schedule(void) { + if (cur_thread != NULL && cur_thread->state != DEAD) { + cur_thread->state = RUNNABLE; + enqueue(&run_queue, cur_thread); + } + + thread_t* next_thread = (thread_t*)dequeue(&run_queue); + + if (next_thread == NULL) { + uart_send_string("No runnable thread!\r\n"); + return; + } + + next_thread->state = RUNNING; + + // If no thread is running, branch to the thread and start running. + if (cur_thread == NULL) { + cur_thread = next_thread; + asm volatile("msr tpidr_el1, %0" :: "r"(&cur_thread->context)); + asm volatile( + "mov sp, %0\n" + "blr %1\n" + : + : "r"(cur_thread->context.sp), "r"(cur_thread->context.lr) + ); + } else { + thread_t* prev_thread = cur_thread; + cur_thread = next_thread; + switch_to(&prev_thread->context, &cur_thread->context); + } +} + + +thread_t* thread_create(void*(*start_routine)(void *)) { + thread_t* new_thread = NULL; + + for (int i = 0; i < MAX_THREADS; i++) { + if (thread_pool[i].free) { + new_thread = &thread_pool[i]; + break; + } + } + + // Initialize the components within the new thread. + new_thread->free = FALSE; + new_thread->user_stack_ptr = (char *)dynamic_alloc(USTACK); + new_thread->kernel_stack_ptr = (char *)dynamic_alloc(KSTACK); + // Link to the function the thread is supposed to execute. + new_thread->context.lr = (uint64_t)start_routine; + // Set sp to the top of user stack. + new_thread->context.sp = (uint64_t)(new_thread->user_stack_ptr + USTACK); + // This is where sp_el1 will point to during system call. + new_thread->context.kernel_sp = (uint64_t)(new_thread->kernel_stack_ptr + KSTACK); + new_thread->context.fp = new_thread->context.sp; + new_thread->state = RUNNABLE; + + if (new_thread != NULL) { + enqueue(&run_queue, (thread_t *)new_thread); + } + + uart_send_string("thread in run queue: "); + uart_send_int(queue_element_count(&run_queue)); + uart_send_string("\r\n"); + + return new_thread; +} + +void kill_zombies(void) { + thread_t* t; + + while ((t = dequeue(&dead_queue)) != NULL) { + t->state = CREATED; + t->free = TRUE; + + // Free allocated stacks. + dynamic_free((uint64_t)t->user_stack_ptr); + dynamic_free((uint64_t)t->kernel_stack_ptr); + } +} + +void* foo(void* arg) { + for(int i = 0; i < 10; ++i) { + uart_send_string("Thread id: "); + uart_send_int(cur_thread->pid); + uart_send_string(" "); + uart_send_int(i); + uart_send_string("\r\n"); + delay(1000000); + schedule(); + } +} + +void idle_thread(void) { + while (1) { + kill_zombies(); + schedule(); + } +} + +void exec_thread() { + +} + diff --git a/Lab5/kernel/thread.h b/Lab5/kernel/thread.h new file mode 100644 index 000000000..091dd1842 --- /dev/null +++ b/Lab5/kernel/thread.h @@ -0,0 +1,68 @@ +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#include +#include "../peripherals/utils.h" +#include "../DSLibrary/queue.h" + +#define MAX_THREADS 10 + +// Allocate 64 KB for user stack. +#define USTACK 0x10000 +// Allocate 64 KB for kernel stack. +#define KSTACK 0x10000 + +typedef struct context { + uint64_t x19; + uint64_t x20; + uint64_t x21; + uint64_t x22; + uint64_t x23; + uint64_t x24; + uint64_t x25; + uint64_t x26; + uint64_t x27; + uint64_t x28; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t kernel_sp; +} context_t; + +enum state_t { + CREATED, + RUNNABLE, + RUNNING, + WAITING, + DEAD +}; + +typedef struct thread { + // Process ID. + int pid; + // Current thread within the thread pool is used or free. + short free; + context_t context; + enum state_t state; + char* user_stack_ptr; + char* kernel_stack_ptr; +} thread_t; + +extern thread_t thread_pool[MAX_THREADS]; +extern thread_t* cur_thread; +extern Queue run_queue; +extern Queue wait_queue; +extern Queue dead_queue; + +extern void switch_to(context_t* cur_context, context_t* next_context); +extern context_t* get_current(void); +void init_thread_pool(void); +void init_scheduler(void); +void schedule(void); +thread_t* thread_create(void*(*start_routine)(void *)); +void* foo(void* arg); +void idle_thread(void); +void kill_zombies(void); + + +#endif \ No newline at end of file diff --git a/Lab5/kernel/timer.c b/Lab5/kernel/timer.c new file mode 100644 index 000000000..c82f824a4 --- /dev/null +++ b/Lab5/kernel/timer.c @@ -0,0 +1,221 @@ +#include "timer.h" +#include "alloc.h" +#include "mini_uart.h" +#include "task.h" +#include "irq.h" + +#define DEFAULT_TIMER 0xFFF + +// #define CORE0_TIMER_IRQ_CTRL 0x40000040 +TQueue* q_head; + +void core_timer_enable(void) { + // Initialize time queue. + q_head = NULL; + + // Set interrupt interval to a very large time initially. + int interval = 0xFFFFFFFF; + + asm volatile( + "mov x0, 1;" + // Control register for the physical timer on the current core. + "msr cntp_ctl_el0, x0;" // enable + + // cntfrq_el0 holds the frequency in Hz at which the system + // counter increments. + "mrs x0, cntfrq_el0;" + //"mul x0, x0, %0;" + //"msr cntp_tval_el0, x0;" // set expired time + "mov x0, 2;" + "ldr x1, =0x40000040;" + + // Stores the value from "x0"(which is the "w0" register, the 32-bit view of "x0"). + // Writing 2 into the register unmasks the timer interrupt. + "str x0, [x1];" // unmask timer interrupt + + : + : "r" (interval) + : + + ); +} + +unsigned long get_current_time(void) { + long long current_time; + long long freq; + + asm volatile( + "mrs x0, cntpct_el0;" + "mov %0, x0;" + "mrs x1, cntfrq_el0;" + "mov %1, x1;" + + // Output operands. + : "=r" (current_time), "=r" (freq) + // No input operands. + : + // Clobbered registers. + : "x0", "x1" + ); + + return (unsigned long)(current_time / freq); +} + +void set_timer_expire(long sec) { + + asm volatile( + "mrs x0, cntfrq_el0;" + "mul x0, x0, %0;" + "msr cntp_tval_el0, x0;" + + // Output operand. + : + // Input operand. + : "r" (sec) + // Clobbered registers. + : "x0" + ); +} + +int p = 1; + +void handle_timer_intr(void* data) { + disable_el1_interrupt(); + uart_send_string("Timer invoked!\r\n"); + + TQueue* cur = q_head; + if (cur == NULL) { + uart_send_string("Timer queue empty!\r\n"); + // Check which event should be invoked. + } else { + unsigned int cur_time = get_current_time(); + + // Check which event expired. + while (cur != NULL) { + if (cur->e.invoked_t <= cur_time) { + task* t = (task *)simple_malloc(sizeof(task)); + uart_send_string("Command executed time: "); + uart_send_uint(cur->e.enter_t); + uart_send_string("\r\n"); + uart_send_string("Event message: "); + + t->intr_func = cur->e.callback; + t->arg = cur->e.arg; + t->priority = p++; + enqueue_task(t); + uart_send_string("Current time: "); + uart_send_uint(cur_time); + uart_send_string("\r\n"); + + cur = cur->next; + } else { + break; + } + } + + + q_head = cur; + } + + update_timer(); + execute_task(); +} + +// Register the callback function into the timer queue. +int add_timer(one_shot_timer_callback_t callback, void* arg, unsigned int expire) { + // Allocate memory for the argument in case it was erased before executing the interrupt. + char* copy_arg = simple_malloc(sizeof(char) * strlen((char *)arg)); + strcpy(copy_arg, (char *)arg); + + TQueue* new = (TQueue *)simple_malloc(sizeof(TQueue)); + new->next = NULL; + new->e.enter_t = get_current_time(); + new->e.invoked_t = new->e.enter_t + expire; + new->e.callback = callback; + new->e.arg = copy_arg; + + // Queue is empty. + if (q_head == NULL) { + q_head = new; + set_timer_expire(expire); + } else { + TQueue* cur = q_head; + + // Sorted queue from smallest invoked time to largest + // to avoid inaccuracies when multiple timers are active. + if (cur->e.invoked_t > new->e.invoked_t) { + new->next = cur; + q_head = new; + } else { + while (cur->next != NULL) { + if (cur->next->e.invoked_t <= new->e.invoked_t) { + cur = cur->next; + } else { + break; + } + } + new->next = cur->next; + cur->next = new; + } + + update_timer(); + } +} + +// Check the invoked time of first element within the queue. +// If queue is empty, reset the timer to 20 seconds. +void update_timer(void) { + unsigned int cur_time = get_current_time(); + + if (q_head == NULL) { + set_timer_expire(DEFAULT_TIMER); + } else { + // Expire immediately if the event is overdue. + if (q_head->e.invoked_t <= cur_time) { + set_timer_expire(0); + } else { + set_timer_expire(q_head->e.invoked_t - cur_time); + } + } +} + +void clear_timer_intr(void) { + unsigned long sec = 0xFFF; + + asm volatile ( + "mrs x0, cntfrq_el0;" + "mul x0, x0, %0;" + "msr cntp_tval_el0, x0;" + + // Output operand. + : + // Input operand. + : "r" (sec) + // Clobbered registers. + : "x0" + ); +} + +void print_timeout_message(void* message) { + uart_send_string((char *)message); + uart_send_string("\r\n"); +} + + +static volatile int flag = 1; +void create_loop(void* data) { + uart_send_string("In create loop\r\n"); + while (flag); + uart_send_string("Exit create loop \r\n"); +} + +void exit_loop(void* data) { + uart_send_string("In exit loop\r\n"); + flag = 0; +} + +void delay_loop(void* data) { + // delay(10000); + uart_send_string("In delay loop\r\n"); + while (1); +} \ No newline at end of file diff --git a/Lab5/kernel/timer.h b/Lab5/kernel/timer.h new file mode 100644 index 000000000..0ba935a1d --- /dev/null +++ b/Lab5/kernel/timer.h @@ -0,0 +1,39 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +// Define a callback function for one-shot timer. +typedef void (*one_shot_timer_callback_t)(void* arg); + +typedef struct _event { + // Enter queue time of the event. + unsigned int enter_t; + + // The actual time it expires. + unsigned int invoked_t; + + // Arguments passed to the callback function. + void* arg; + + // Callback function to be invoked after timer expires. + one_shot_timer_callback_t callback; +} Event; + +// Singly linked list for the timer queue. +typedef struct _timer_queue { + Event e; + struct _timer_queue* next; +} TQueue; + +void core_timer_enable(void); +unsigned long get_current_time(void); +void set_timer_expire(long sec); +void handle_timer_intr(void* data); +void clear_timer_intr(void); +int add_timer(one_shot_timer_callback_t callback, void* arg, unsigned int expire); +void update_timer(void); +void print_timeout_message(void* message); +void create_loop(void* data); +void exit_loop(void* data); +void delay_loop(void* data); + +#endif \ No newline at end of file diff --git a/Lab5/kernel/timer_asm.S b/Lab5/kernel/timer_asm.S new file mode 100644 index 000000000..1a289d697 --- /dev/null +++ b/Lab5/kernel/timer_asm.S @@ -0,0 +1,23 @@ +#define CORE0_TIMER_IRQ_CTRL 0x40000040 + +core_timer_enable: + mov x0, 1 + // Control register for the physical timer on the current core. + msr cntp_ctl_el0, x0 // enable + + // cntfrq_el0 holds the frequency in Hz at which the system + // counter increments. + mrs x0, cntfrq_el0 + msr cntp_tval_el0, x0 // set expired time + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + + // Stores the value from "x0"(which is the "w0" register, the 32-bit view of "x0"). + // Writing 2 into the register unmasks the timer interrupt. + str w0, [x1] // unmask timer interrupt + + +// Set the timer to expire a second later. +core_timer_handler: + mrs x0, cntfrq_el0 + msr cntp_tval_el0, x0 \ No newline at end of file diff --git a/Lab5/kernel/user.c b/Lab5/kernel/user.c new file mode 100644 index 000000000..793cd8a8d --- /dev/null +++ b/Lab5/kernel/user.c @@ -0,0 +1,56 @@ +#include "initramdisk.h" +#include "user.h" +#include "../peripherals/mini_uart.h" +#include "../peripherals/mm.h" + +#define USER_STACK_SIZE 0x2000 + +int load_user_prog(uintptr_t file_addr) { + uintptr_t user_start = 0x100000; + uintptr_t user_sp = user_start + USER_STACK_SIZE; + + // Load the file content into memory. + struct File* file = (struct File *)file_addr; + unsigned int fileSize = file->fileSize; + + // Write the user program into the address specified by the linker. + char* userProg = (char *)0x100000; + + int i = 0; + while (fileSize--) { + uart_send(file->fData[i]); + *userProg++ = file->fData[i++]; + } + + uart_send_string("Print file size\r\n"); + uart_send_uint((uintptr_t)file); + uart_send_string("\r\n"); + uart_send_uint(file->fileSize); + uart_send_string("\r\n"); + + + // EL1 to EL0. + asm volatile ( + // Set SPSR_EL1 to 0x3C0 to disable interrupts and set appropriate state for EL0. + // [9:6]DAIF -> Enable interrupt in user program.(I -> 0) + "mov x0, 0x340;" + "msr spsr_el1, x0;" + + // Set ELR_EL1 to the start address of the user program. + "mov x1, %0;" + "msr elr_el1, x1;" + + // Set SP_EL0 to a valid stack pointer for the user program. + "mov x2, %1;" + "msr sp_el0, x2;" + "eret;" + + // No output operands. + : + // Input operands. + : "r" (user_start), "r" (user_sp) + // List of clobbered registers.(Tells the compiler which registers the assembly code modifies or "clobbers.") + : "x0", "x1", "x2" + ); + +} \ No newline at end of file diff --git a/Lab5/kernel/user.h b/Lab5/kernel/user.h new file mode 100644 index 000000000..a1228ab19 --- /dev/null +++ b/Lab5/kernel/user.h @@ -0,0 +1,8 @@ +#ifndef _USER_H_ +#define _USER_H_ + +#include + +int load_user_prog(uintptr_t file_addr); + +#endif \ No newline at end of file diff --git a/Lab5/output.dts b/Lab5/output.dts new file mode 100644 index 000000000..31c44b4ae --- /dev/null +++ b/Lab5/output.dts @@ -0,0 +1,1565 @@ +/dts-v1/; + +/memreserve/ 0x0000000000000000 0x0000000000001000; +/ { + compatible = "raspberrypi,3-model-b-plus\0brcm,bcm2837"; + model = "Raspberry Pi 3 Model B+"; + #address-cells = <0x01>; + #size-cells = <0x01>; + interrupt-parent = <0x01>; + + aliases { + serial0 = "/soc/serial@7e215040"; + serial1 = "/soc/serial@7e201000"; + aux = "/soc/aux@7e215000"; + sound = "/soc/sound"; + soc = "/soc"; + dma = "/soc/dma-controller@7e007000"; + intc = "/soc/interrupt-controller@7e00b200"; + watchdog = "/soc/watchdog@7e100000"; + random = "/soc/rng@7e104000"; + mailbox = "/soc/mailbox@7e00b880"; + gpio = "/soc/gpio@7e200000"; + uart0 = "/soc/serial@7e201000"; + uart1 = "/soc/serial@7e215040"; + sdhost = "/soc/mmc@7e202000"; + mmc = "/soc/mmc@7e300000"; + mmc1 = "/soc/mmcnr@7e300000"; + mmc0 = "/soc/mmc@7e202000"; + i2s = "/soc/i2s@7e203000"; + i2c0 = "/soc/i2c0mux/i2c@0"; + i2c1 = "/soc/i2c@7e804000"; + i2c10 = "/soc/i2c0mux/i2c@1"; + i2c = "/soc/i2c@7e804000"; + spi0 = "/soc/spi@7e204000"; + spi1 = "/soc/spi@7e215080"; + spi2 = "/soc/spi@7e2150c0"; + usb = "/soc/usb@7e980000"; + leds = "/leds"; + fb = "/soc/fb"; + thermal = "/soc/thermal@7e212000"; + axiperf = "/soc/axiperf"; + i2c2 = "/soc/i2c@7e805000"; + ethernet0 = "/soc/usb@7e980000/usb-port@1/usb-port@1/ethernet@1"; + bluetooth = "/soc/serial@7e201000/bluetooth"; + phandle = <0x32>; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0"; + phandle = <0x36>; + }; + + reserved-memory { + #address-cells = <0x01>; + #size-cells = <0x01>; + ranges; + phandle = <0x3a>; + + linux,cma { + compatible = "shared-dma-pool"; + size = <0x4000000>; + reusable; + linux,cma-default; + phandle = <0x3b>; + }; + }; + + thermal-zones { + + cpu-thermal { + polling-delay-passive = <0x00>; + polling-delay = <0x3e8>; + thermal-sensors = <0x02>; + coefficients = <0xfffffde6 0x64960>; + phandle = <0x3c>; + + trips { + phandle = <0x3d>; + + cpu-crit { + temperature = <0x1adb0>; + hysteresis = <0x00>; + type = "critical"; + }; + }; + + cooling-maps { + phandle = <0x3e>; + }; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <0x01>; + #size-cells = <0x01>; + ranges = <0x7e000000 0x3f000000 0x1000000 0x40000000 0x40000000 0x1000>; + dma-ranges = <0xc0000000 0x00 0x3f000000 0x7e000000 0x3f000000 0x1000000>; + phandle = <0x3f>; + + timer@7e003000 { + compatible = "brcm,bcm2835-system-timer"; + reg = <0x7e003000 0x1000>; + interrupts = <0x01 0x00 0x01 0x01 0x01 0x02 0x01 0x03>; + clock-frequency = <0xf4240>; + status = "disabled"; + phandle = <0x40>; + }; + + txp@7e004000 { + compatible = "brcm,bcm2835-txp"; + reg = <0x7e004000 0x20>; + interrupts = <0x01 0x0b>; + status = "disabled"; + phandle = <0x41>; + }; + + cprman@7e101000 { + compatible = "brcm,bcm2835-cprman"; + #clock-cells = <0x01>; + reg = <0x7e101000 0x2000>; + clocks = <0x03 0x04 0x00 0x04 0x01 0x04 0x02 0x05 0x00 0x05 0x01 0x05 0x02>; + firmware = <0x06>; + phandle = <0x08>; + }; + + mailbox@7e00b880 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0x00 0x01>; + #mbox-cells = <0x00>; + phandle = <0x1f>; + }; + + gpio@7e200000 { + compatible = "brcm,bcm2835-gpio"; + reg = <0x7e200000 0xb4>; + interrupts = <0x02 0x11 0x02 0x12>; + gpio-controller; + #gpio-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x02>; + gpio-ranges = <0x07 0x00 0x00 0x36>; + gpio-line-names = "ID_SDA\0ID_SCL\0GPIO2\0GPIO3\0GPIO4\0GPIO5\0GPIO6\0GPIO7\0GPIO8\0GPIO9\0GPIO10\0GPIO11\0GPIO12\0GPIO13\0GPIO14\0GPIO15\0GPIO16\0GPIO17\0GPIO18\0GPIO19\0GPIO20\0GPIO21\0GPIO22\0GPIO23\0GPIO24\0GPIO25\0GPIO26\0GPIO27\0HDMI_HPD_N\0STATUS_LED_G\0CTS0\0RTS0\0TXD0\0RXD0\0SD1_CLK\0SD1_CMD\0SD1_DATA0\0SD1_DATA1\0SD1_DATA2\0SD1_DATA3\0PWM0_OUT\0PWM1_OUT\0ETH_CLK\0WIFI_CLK\0SDA0\0SCL0\0SMPS_SCL\0SMPS_SDA\0SD_CLK_R\0SD_CMD_R\0SD_DATA0_R\0SD_DATA1_R\0SD_DATA2_R\0SD_DATA3_R"; + phandle = <0x07>; + + dpi-gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b>; + brcm,function = <0x06>; + phandle = <0x42>; + }; + + emmc-gpio22 { + brcm,pins = <0x16 0x17 0x18 0x19 0x1a 0x1b>; + brcm,function = <0x07>; + phandle = <0x43>; + }; + + emmc-gpio34 { + brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02 0x02 0x02 0x02 0x02>; + phandle = <0x44>; + }; + + emmc-gpio48 { + brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; + brcm,function = <0x07>; + phandle = <0x14>; + }; + + gpclk0-gpio4 { + brcm,pins = <0x04>; + brcm,function = <0x04>; + phandle = <0x45>; + }; + + gpclk1-gpio5 { + brcm,pins = <0x05>; + brcm,function = <0x04>; + phandle = <0x46>; + }; + + gpclk1-gpio42 { + brcm,pins = <0x2a>; + brcm,function = <0x04>; + phandle = <0x47>; + }; + + gpclk1-gpio44 { + brcm,pins = <0x2c>; + brcm,function = <0x04>; + phandle = <0x48>; + }; + + gpclk2-gpio6 { + brcm,pins = <0x06>; + brcm,function = <0x04>; + phandle = <0x49>; + }; + + gpclk2-gpio43 { + brcm,pins = <0x2b>; + brcm,function = <0x04>; + brcm,pull = <0x00>; + phandle = <0x4a>; + }; + + i2c0if-gpio0 { + brcm,pins = <0x00 0x01>; + brcm,function = <0x04>; + phandle = <0x1d>; + }; + + i2c0if-gpio28 { + brcm,pins = <0x1c 0x1d>; + brcm,function = <0x04>; + phandle = <0x4b>; + }; + + i2c0if-gpio44 { + brcm,pins = <0x2c 0x2d>; + brcm,function = <0x05>; + phandle = <0x1e>; + }; + + i2c1-gpio2 { + brcm,pins = <0x02 0x03>; + brcm,function = <0x04>; + phandle = <0x4c>; + }; + + i2c1-gpio44 { + brcm,pins = <0x2c 0x2d>; + brcm,function = <0x06>; + phandle = <0x4d>; + }; + + jtag-gpio22 { + brcm,pins = <0x16 0x17 0x18 0x19 0x1a 0x1b>; + brcm,function = <0x03>; + phandle = <0x4e>; + }; + + pcm-gpio18 { + brcm,pins = <0x12 0x13 0x14 0x15>; + brcm,function = <0x04>; + phandle = <0x4f>; + }; + + pcm-gpio28 { + brcm,pins = <0x1c 0x1d 0x1e 0x1f>; + brcm,function = <0x06>; + phandle = <0x50>; + }; + + sdhost-gpio48 { + brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; + brcm,function = <0x04>; + phandle = <0x0d>; + }; + + spi0-gpio7 { + brcm,pins = <0x07 0x08 0x09 0x0a 0x0b>; + brcm,function = <0x04>; + phandle = <0x51>; + }; + + spi0-gpio35 { + brcm,pins = <0x23 0x24 0x25 0x26 0x27>; + brcm,function = <0x04>; + phandle = <0x52>; + }; + + spi1-gpio16 { + brcm,pins = <0x10 0x11 0x12 0x13 0x14 0x15>; + brcm,function = <0x03>; + phandle = <0x53>; + }; + + spi2-gpio40 { + brcm,pins = <0x28 0x29 0x2a 0x2b 0x2c 0x2d>; + brcm,function = <0x03>; + phandle = <0x54>; + }; + + uart0-gpio14 { + brcm,pins = <0x0e 0x0f>; + brcm,function = <0x04>; + phandle = <0x55>; + }; + + uart0-ctsrts-gpio16 { + brcm,pins = <0x10 0x11>; + brcm,function = <0x07>; + phandle = <0x56>; + }; + + uart0-ctsrts-gpio30 { + brcm,pins = <0x1e 0x1f>; + brcm,function = <0x07>; + brcm,pull = <0x02 0x00>; + phandle = <0x57>; + }; + + uart0-gpio32 { + brcm,pins = <0x20 0x21>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02>; + phandle = <0x58>; + }; + + uart0-gpio36 { + brcm,pins = <0x24 0x25>; + brcm,function = <0x06>; + phandle = <0x59>; + }; + + uart0-ctsrts-gpio38 { + brcm,pins = <0x26 0x27>; + brcm,function = <0x06>; + phandle = <0x5a>; + }; + + uart1-gpio14 { + brcm,pins = <0x0e 0x0f>; + brcm,function = <0x02>; + phandle = <0x5b>; + }; + + uart1-ctsrts-gpio16 { + brcm,pins = <0x10 0x11>; + brcm,function = <0x02>; + phandle = <0x5c>; + }; + + uart1-gpio32 { + brcm,pins = <0x20 0x21>; + brcm,function = <0x02>; + phandle = <0x5d>; + }; + + uart1-ctsrts-gpio30 { + brcm,pins = <0x1e 0x1f>; + brcm,function = <0x02>; + phandle = <0x5e>; + }; + + uart1-gpio40 { + brcm,pins = <0x28 0x29>; + brcm,function = <0x02>; + phandle = <0x5f>; + }; + + uart1-ctsrts-gpio42 { + brcm,pins = <0x2a 0x2b>; + brcm,function = <0x02>; + phandle = <0x60>; + }; + + i2c-slave-gpio18 { + brcm,pins = <0x12 0x13 0x14 0x15>; + brcm,function = <0x07>; + phandle = <0x61>; + }; + + jtag-gpio4 { + brcm,pins = <0x04 0x05 0x06 0x0c 0x0d>; + brcm,function = <0x02>; + phandle = <0x62>; + }; + + pwm0-gpio12 { + brcm,pins = <0x0c>; + brcm,function = <0x04>; + phandle = <0x63>; + }; + + pwm0-gpio18 { + brcm,pins = <0x12>; + brcm,function = <0x02>; + phandle = <0x64>; + }; + + pwm0-gpio40 { + brcm,pins = <0x28>; + brcm,function = <0x04>; + phandle = <0x65>; + }; + + pwm1-gpio13 { + brcm,pins = <0x0d>; + brcm,function = <0x04>; + phandle = <0x66>; + }; + + pwm1-gpio19 { + brcm,pins = <0x13>; + brcm,function = <0x02>; + phandle = <0x67>; + }; + + pwm1-gpio41 { + brcm,pins = <0x29>; + brcm,function = <0x04>; + phandle = <0x68>; + }; + + pwm1-gpio45 { + brcm,pins = <0x2d>; + brcm,function = <0x04>; + phandle = <0x69>; + }; + + dpi_18bit_cpadhi_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>; + brcm,function = <0x06>; + brcm,pull = <0x00>; + phandle = <0x6a>; + }; + + dpi_18bit_cpadhi_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x19>; + brcm,function = <0x06>; + phandle = <0x6b>; + }; + + dpi_18bit_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>; + brcm,function = <0x06>; + phandle = <0x6c>; + }; + + dpi_18bit_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15>; + brcm,function = <0x06>; + phandle = <0x6d>; + }; + + dpi_16bit_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>; + brcm,function = <0x06>; + phandle = <0x6e>; + }; + + dpi_16bit_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13>; + brcm,function = <0x06>; + phandle = <0x6f>; + }; + + dpi_16bit_cpadhi_gpio0 { + brcm,pins = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>; + brcm,function = <0x06>; + phandle = <0x70>; + }; + + dpi_16bit_cpadhi_gpio2 { + brcm,pins = <0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x14 0x15 0x16 0x17 0x18>; + brcm,function = <0x06>; + phandle = <0x71>; + }; + + gpioout { + brcm,pins = <0x06>; + brcm,function = <0x01>; + phandle = <0x72>; + }; + + alt0 { + brcm,pins = <0x04 0x05 0x07 0x08 0x09 0x0a 0x0b>; + brcm,function = <0x04>; + phandle = <0x73>; + }; + + spi0_pins { + brcm,pins = <0x09 0x0a 0x0b>; + brcm,function = <0x04>; + phandle = <0x0f>; + }; + + spi0_cs_pins { + brcm,pins = <0x08 0x07>; + brcm,function = <0x01>; + phandle = <0x10>; + }; + + i2c0 { + brcm,pins = <0x00 0x01>; + brcm,function = <0x04>; + phandle = <0x74>; + }; + + i2c1 { + brcm,pins = <0x02 0x03>; + brcm,function = <0x04>; + phandle = <0x15>; + }; + + i2s { + brcm,pins = <0x12 0x13 0x14 0x15>; + brcm,function = <0x04>; + phandle = <0x0e>; + }; + + sdio_pins { + brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02 0x02 0x02 0x02 0x02>; + phandle = <0x1b>; + }; + + bt_pins { + brcm,pins = <0x2b>; + brcm,function = <0x04>; + brcm,pull = <0x00>; + phandle = <0x0a>; + }; + + uart0_pins { + brcm,pins = <0x20 0x21>; + brcm,function = <0x07>; + brcm,pull = <0x00 0x02>; + phandle = <0x09>; + }; + + uart1_pins { + brcm,pins; + brcm,function; + brcm,pull; + phandle = <0x13>; + }; + + uart1_bt_pins { + brcm,pins = <0x20 0x21 0x1e 0x1f>; + brcm,function = <0x02>; + brcm,pull = <0x00 0x02 0x02 0x00>; + phandle = <0x75>; + }; + + audio_pins { + brcm,pins = <0x28 0x29>; + brcm,function = <0x04>; + brcm,pull = <0x00>; + phandle = <0x20>; + }; + }; + + serial@7e201000 { + compatible = "arm,pl011\0arm,primecell"; + reg = <0x7e201000 0x200>; + interrupts = <0x02 0x19>; + clocks = <0x08 0x13 0x08 0x14>; + clock-names = "uartclk\0apb_pclk"; + arm,primecell-periphid = <0x241011>; + cts-event-workaround; + skip-init; + pinctrl-names = "default"; + pinctrl-0 = <0x09 0x0a>; + status = "okay"; + phandle = <0x26>; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + max-speed = <0x2dc6c0>; + shutdown-gpios = <0x0b 0x00 0x00>; + local-bd-address = [00 00 00 00 00 00]; + fallback-bd-address; + status = "okay"; + phandle = <0x34>; + }; + }; + + mmc@7e202000 { + compatible = "brcm,bcm2835-sdhost"; + reg = <0x7e202000 0x100>; + interrupts = <0x02 0x18>; + clocks = <0x08 0x14>; + status = "okay"; + dmas = <0x0c 0x2000000d>; + dma-names = "rx-tx"; + bus-width = <0x04>; + brcm,overclock-50 = <0x00>; + brcm,pio-limit = <0x01>; + firmware = <0x06>; + pinctrl-names = "default"; + pinctrl-0 = <0x0d>; + phandle = <0x2e>; + }; + + i2s@7e203000 { + compatible = "brcm,bcm2835-i2s"; + reg = <0x7e203000 0x24>; + clocks = <0x08 0x1f>; + status = "disabled"; + dmas = <0x0c 0x02 0x0c 0x03>; + dma-names = "tx\0rx"; + #sound-dai-cells = <0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x0e>; + phandle = <0x28>; + }; + + spi@7e204000 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7e204000 0x200>; + interrupts = <0x02 0x16>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + dmas = <0x0c 0x06 0x0c 0x07>; + dma-names = "tx\0rx"; + pinctrl-names = "default"; + pinctrl-0 = <0x0f 0x10>; + cs-gpios = <0x07 0x08 0x01 0x07 0x07 0x01>; + phandle = <0x29>; + + spidev@0 { + compatible = "spidev"; + reg = <0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + spi-max-frequency = <0x7735940>; + phandle = <0x76>; + }; + + spidev@1 { + compatible = "spidev"; + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + spi-max-frequency = <0x7735940>; + phandle = <0x77>; + }; + }; + + i2c@7e205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x200>; + interrupts = <0x02 0x15>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + clock-frequency = <0x186a0>; + phandle = <0x1c>; + }; + + dpi@7e208000 { + compatible = "brcm,bcm2835-dpi"; + reg = <0x7e208000 0x8c>; + clocks = <0x08 0x14 0x08 0x2c>; + clock-names = "core\0pixel"; + status = "disabled"; + phandle = <0x78>; + }; + + dsi@7e209000 { + compatible = "brcm,bcm2835-dsi0"; + reg = <0x7e209000 0x78>; + interrupts = <0x02 0x04>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + clocks = <0x08 0x20 0x08 0x2f 0x08 0x31>; + clock-names = "phy\0escape\0pixel"; + clock-output-names = "dsi0_byte\0dsi0_ddr2\0dsi0_ddr"; + status = "disabled"; + power-domains = <0x11 0x11>; + phandle = <0x04>; + }; + + aux@7e215000 { + compatible = "brcm,bcm2835-aux"; + #clock-cells = <0x01>; + reg = <0x7e215000 0x08>; + clocks = <0x08 0x14>; + phandle = <0x12>; + }; + + serial@7e215040 { + compatible = "brcm,bcm2835-aux-uart"; + reg = <0x7e215040 0x40>; + interrupts = <0x01 0x1d>; + clocks = <0x12 0x00>; + status = "okay"; + skip-init; + pinctrl-names = "default"; + pinctrl-0 = <0x13>; + phandle = <0x27>; + + bluetooth { + compatible = "brcm,bcm43438-bt"; + max-speed = <0x38400>; + shutdown-gpios = <0x0b 0x00 0x00>; + local-bd-address = [00 00 00 00 00 00]; + fallback-bd-address; + status = "disabled"; + phandle = <0x35>; + }; + }; + + spi@7e215080 { + compatible = "brcm,bcm2835-aux-spi"; + reg = <0x7e215080 0x40>; + interrupts = <0x01 0x1d>; + clocks = <0x12 0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + phandle = <0x79>; + }; + + spi@7e2150c0 { + compatible = "brcm,bcm2835-aux-spi"; + reg = <0x7e2150c0 0x40>; + interrupts = <0x01 0x1d>; + clocks = <0x12 0x02>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + phandle = <0x7a>; + }; + + pwm@7e20c000 { + compatible = "brcm,bcm2835-pwm"; + reg = <0x7e20c000 0x28>; + clocks = <0x08 0x1e>; + assigned-clocks = <0x08 0x1e>; + assigned-clock-rates = <0x2faf080>; + #pwm-cells = <0x03>; + status = "disabled"; + phandle = <0x7b>; + }; + + mmc@7e300000 { + compatible = "brcm,bcm2835-mmc\0brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <0x02 0x1e>; + clocks = <0x08 0x1c>; + status = "disabled"; + dmas = <0x0c 0x0b>; + dma-names = "rx-tx"; + brcm,overclock-50 = <0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x14>; + bus-width = <0x04>; + phandle = <0x2f>; + }; + + hvs@7e400000 { + compatible = "brcm,bcm2835-hvs"; + reg = <0x7e400000 0x6000>; + interrupts = <0x02 0x01>; + status = "disabled"; + phandle = <0x7c>; + }; + + dsi@7e700000 { + compatible = "brcm,bcm2835-dsi1"; + reg = <0x7e700000 0x8c>; + interrupts = <0x02 0x0c>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + clocks = <0x08 0x23 0x08 0x30 0x08 0x32>; + clock-names = "phy\0escape\0pixel"; + clock-output-names = "dsi1_byte\0dsi1_ddr2\0dsi1_ddr"; + status = "disabled"; + power-domains = <0x11 0x12>; + phandle = <0x05>; + }; + + i2c@7e804000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e804000 0x1000>; + interrupts = <0x02 0x15>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + pinctrl-names = "default"; + pinctrl-0 = <0x15>; + clock-frequency = <0x186a0>; + phandle = <0x2b>; + }; + + usb@7e980000 { + compatible = "brcm,bcm2708-usb"; + reg = <0x7e980000 0x10000 0x7e006000 0x1000>; + interrupts = <0x01 0x09 0x02 0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + clocks = <0x16>; + clock-names = "otg"; + phys = <0x17>; + phy-names = "usb2-phy"; + interrupt-names = "usb\0soft"; + power-domains = <0x11 0x06>; + phandle = <0x7d>; + + usb-port@1 { + compatible = "usb424,2514"; + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + usb-port@1 { + compatible = "usb424,2514"; + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + ethernet@1 { + compatible = "usb424,7800"; + reg = <0x01>; + phandle = <0x7e>; + + mdio { + #address-cells = <0x01>; + #size-cells = <0x00>; + + ethernet-phy@1 { + reg = <0x01>; + microchip,led-modes = <0x01 0x06>; + microchip,eee-enabled; + microchip,tx-lpi-timer = <0x258>; + microchip,downshift-after = <0x02>; + phandle = <0x39>; + }; + }; + }; + }; + }; + }; + + dma-controller@7e007000 { + compatible = "brcm,bcm2835-dma"; + reg = <0x7e007000 0xf00>; + interrupts = <0x01 0x10 0x01 0x11 0x01 0x12 0x01 0x13 0x01 0x14 0x01 0x15 0x01 0x16 0x01 0x17 0x01 0x18 0x01 0x19 0x01 0x1a 0x01 0x1b 0x01 0x1b 0x01 0x1b 0x01 0x1b 0x01 0x1c>; + interrupt-names = "dma0\0dma1\0dma2\0dma3\0dma4\0dma5\0dma6\0dma7\0dma8\0dma9\0dma10\0dma11\0dma12\0dma13\0dma14\0dma-shared-all"; + #dma-cells = <0x01>; + brcm,dma-channel-mask = <0x7f35>; + phandle = <0x0c>; + }; + + interrupt-controller@7e00b200 { + compatible = "brcm,bcm2836-armctrl-ic"; + reg = <0x7e00b200 0x200>; + interrupt-controller; + #interrupt-cells = <0x02>; + interrupt-parent = <0x18>; + interrupts = <0x08 0x04>; + phandle = <0x01>; + }; + + watchdog@7e100000 { + compatible = "brcm,bcm2835-pm\0brcm,bcm2835-pm-wdt"; + #power-domain-cells = <0x01>; + #reset-cells = <0x01>; + reg = <0x7e100000 0x114 0x7e00a000 0x24>; + reg-names = "pm\0asb"; + clocks = <0x08 0x15 0x08 0x1d 0x08 0x17 0x08 0x16>; + clock-names = "v3d\0peri_image\0h264\0isp"; + system-power-controller; + phandle = <0x2c>; + }; + + rng@7e104000 { + compatible = "brcm,bcm2835-rng"; + reg = <0x7e104000 0x10>; + interrupts = <0x02 0x1d>; + phandle = <0x2d>; + }; + + pixelvalve@7e206000 { + compatible = "brcm,bcm2835-pixelvalve0"; + reg = <0x7e206000 0x100>; + interrupts = <0x02 0x0d>; + status = "disabled"; + phandle = <0x7f>; + }; + + pixelvalve@7e207000 { + compatible = "brcm,bcm2835-pixelvalve1"; + reg = <0x7e207000 0x100>; + interrupts = <0x02 0x0e>; + status = "disabled"; + phandle = <0x80>; + }; + + thermal@7e212000 { + compatible = "brcm,bcm2837-thermal"; + reg = <0x7e212000 0x08>; + clocks = <0x08 0x1b>; + #thermal-sensor-cells = <0x00>; + status = "okay"; + phandle = <0x02>; + }; + + i2c@7e805000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e805000 0x1000>; + interrupts = <0x02 0x15>; + clocks = <0x08 0x14>; + #address-cells = <0x01>; + #size-cells = <0x00>; + status = "disabled"; + clock-frequency = <0x186a0>; + phandle = <0x1a>; + }; + + vec@7e806000 { + compatible = "brcm,bcm2835-vec"; + reg = <0x7e806000 0x1000>; + clocks = <0x19 0x0f>; + interrupts = <0x02 0x1b>; + status = "disabled"; + power-domains = <0x11 0x07>; + phandle = <0x81>; + }; + + pixelvalve@7e807000 { + compatible = "brcm,bcm2835-pixelvalve2"; + reg = <0x7e807000 0x100>; + interrupts = <0x02 0x0a>; + status = "disabled"; + phandle = <0x82>; + }; + + hdmi@7e902000 { + compatible = "brcm,bcm2835-hdmi"; + reg = <0x7e902000 0x600 0x7e808000 0x100>; + interrupts = <0x02 0x08 0x02 0x09>; + ddc = <0x1a>; + clocks = <0x19 0x09 0x19 0x0d>; + clock-names = "pixel\0hdmi"; + dmas = <0x0c 0x9000011>; + dma-names = "audio-rx"; + status = "disabled"; + reg-names = "hdmi\0hd"; + power-domains = <0x11 0x05>; + hpd-gpios = <0x07 0x1c 0x01>; + phandle = <0x33>; + }; + + v3d@7ec00000 { + compatible = "brcm,vc4-v3d"; + reg = <0x7ec00000 0x1000>; + interrupts = <0x01 0x0a>; + power-domains = <0x11 0x0a>; + status = "disabled"; + phandle = <0x83>; + }; + + gpu { + compatible = "brcm,bcm2835-vc4"; + status = "disabled"; + raspberrypi,firmware = <0x06>; + phandle = <0x84>; + }; + + local_intc@40000000 { + compatible = "brcm,bcm2836-l1-intc"; + reg = <0x40000000 0x100>; + interrupt-controller; + #interrupt-cells = <0x02>; + interrupt-parent = <0x18>; + phandle = <0x18>; + }; + + mmcnr@7e300000 { + compatible = "brcm,bcm2835-mmc\0brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <0x02 0x1e>; + clocks = <0x08 0x1c>; + dmas = <0x0c 0x0b>; + dma-names = "rx-tx"; + brcm,overclock-50 = <0x00>; + non-removable; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <0x1b>; + bus-width = <0x04>; + #address-cells = <0x01>; + #size-cells = <0x00>; + phandle = <0x30>; + + wifi@1 { + reg = <0x01>; + compatible = "brcm,bcm4329-fmac"; + phandle = <0x85>; + }; + }; + + firmwarekms@7e600000 { + compatible = "raspberrypi,rpi-firmware-kms"; + reg = <0x7e600000 0x100>; + interrupts = <0x02 0x10>; + brcm,firmware = <0x06>; + status = "disabled"; + phandle = <0x86>; + }; + + smi@7e600000 { + compatible = "brcm,bcm2835-smi"; + reg = <0x7e600000 0x100>; + interrupts = <0x02 0x10>; + clocks = <0x08 0x2a>; + assigned-clocks = <0x08 0x2a>; + assigned-clock-rates = <0x7735940>; + dmas = <0x0c 0x04>; + dma-names = "rx-tx"; + status = "disabled"; + phandle = <0x87>; + }; + + csi@7e800000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e800000 0x800 0x7e802000 0x04>; + interrupts = <0x02 0x06>; + clocks = <0x08 0x2d 0x19 0x04>; + clock-names = "lp\0vpu"; + power-domains = <0x11 0x0c>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + status = "disabled"; + phandle = <0x88>; + }; + + csi@7e801000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e801000 0x800 0x7e802004 0x04>; + interrupts = <0x02 0x07>; + clocks = <0x08 0x2e 0x19 0x04>; + clock-names = "lp\0vpu"; + power-domains = <0x11 0x0d>; + #address-cells = <0x01>; + #size-cells = <0x00>; + #clock-cells = <0x01>; + status = "disabled"; + brcm,num-data-lanes = <0x02>; + phandle = <0x89>; + }; + + axiperf { + compatible = "brcm,bcm2835-axiperf"; + reg = <0x7e009800 0x100 0x7ee08000 0x100>; + firmware = <0x06>; + status = "disabled"; + phandle = <0x31>; + }; + + i2c0mux { + compatible = "i2c-mux-pinctrl"; + #address-cells = <0x01>; + #size-cells = <0x00>; + i2c-parent = <0x1c>; + status = "disabled"; + pinctrl-names = "i2c0\0i2c_csi_dsi"; + pinctrl-0 = <0x1d>; + pinctrl-1 = <0x1e>; + phandle = <0x2a>; + + i2c@0 { + reg = <0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + phandle = <0x8a>; + }; + + i2c@1 { + reg = <0x01>; + #address-cells = <0x01>; + #size-cells = <0x00>; + phandle = <0x8b>; + }; + }; + + firmware { + compatible = "raspberrypi,bcm2835-firmware\0simple-mfd"; + #address-cells = <0x01>; + #size-cells = <0x01>; + mboxes = <0x1f>; + dma-ranges; + phandle = <0x06>; + + clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <0x01>; + phandle = <0x19>; + }; + + vcio { + compatible = "raspberrypi,vcio"; + phandle = <0x8c>; + }; + + expgpio { + compatible = "raspberrypi,firmware-gpio"; + gpio-controller; + #gpio-cells = <0x02>; + gpio-line-names = "BT_ON\0WL_ON\0PWR_LED_R\0LAN_RUN\0NC\0CAM_GPIO0\0CAM_GPIO1\0NC"; + status = "okay"; + phandle = <0x0b>; + }; + }; + + power { + compatible = "raspberrypi,bcm2835-power"; + firmware = <0x06>; + #power-domain-cells = <0x01>; + phandle = <0x11>; + }; + + mailbox@7e00b840 { + compatible = "brcm,bcm2836-vchiq\0brcm,bcm2835-vchiq"; + reg = <0x7e00b840 0x3c>; + interrupts = <0x00 0x02>; + pinctrl-names = "default"; + pinctrl-0 = <0x20>; + phandle = <0x8d>; + }; + + gpiomem { + compatible = "brcm,bcm2835-gpiomem"; + reg = <0x7e200000 0x1000>; + }; + + fb { + compatible = "brcm,bcm2708-fb"; + firmware = <0x06>; + status = "okay"; + phandle = <0x8e>; + }; + + sound { + status = "disabled"; + phandle = <0x8f>; + }; + }; + + clocks { + + clk-osc { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-output-names = "osc"; + clock-frequency = <0x124f800>; + phandle = <0x03>; + }; + + clk-usb { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-output-names = "otg"; + clock-frequency = <0x1c9c3800>; + phandle = <0x16>; + }; + }; + + phy { + compatible = "usb-nop-xceiv"; + #phy-cells = <0x00>; + phandle = <0x17>; + }; + + arm-pmu { + compatible = "arm,cortex-a53-pmu\0arm,cortex-a7-pmu"; + interrupt-parent = <0x18>; + interrupts = <0x09 0x04>; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupt-parent = <0x18>; + interrupts = <0x00 0x04 0x01 0x04 0x03 0x04 0x02 0x04>; + always-on; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + enable-method = "brcm,bcm2836-smp"; + phandle = <0x90>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x00>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xd8>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x22>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x01>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xe0>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x23>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x02>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xe8>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x24>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x03>; + enable-method = "spin-table"; + cpu-release-addr = <0x00 0xf0>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x40>; + d-cache-sets = <0x80>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x40>; + i-cache-sets = <0x100>; + next-level-cache = <0x21>; + phandle = <0x25>; + }; + + l2-cache0 { + compatible = "cache"; + cache-unified; + cache-size = <0x80000>; + cache-line-size = <0x40>; + cache-sets = <0x200>; + cache-level = <0x02>; + phandle = <0x21>; + }; + }; + + cam1_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam1-reg"; + enable-active-high; + status = "okay"; + gpio = <0x0b 0x05 0x00>; + phandle = <0x91>; + }; + + cam1_clk { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + status = "disabled"; + phandle = <0x92>; + }; + + cam0_regulator { + compatible = "regulator-fixed"; + regulator-name = "cam0-reg"; + enable-active-high; + status = "disabled"; + phandle = <0x93>; + }; + + cam0_clk { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + status = "disabled"; + phandle = <0x94>; + }; + + cam_dummy_reg { + compatible = "regulator-fixed"; + regulator-name = "cam-dummy-reg"; + status = "okay"; + phandle = <0x95>; + }; + + __overrides__ { + cam0-pwdn-ctrl; + cam0-pwdn; + cam0-led-ctrl; + cam0-led; + arm_freq = "\0\0\0\"clock-frequency:0\0\0\0\0#clock-frequency:0\0\0\0\0$clock-frequency:0\0\0\0\0%clock-frequency:0"; + cache_line_size; + uart0 = "\0\0\0&status"; + uart1 = "\0\0\0'status"; + i2s = "\0\0\0(status"; + spi = "\0\0\0)status"; + i2c0 = [00 00 00 1c 73 74 61 74 75 73 00 00 00 00 2a 73 74 61 74 75 73 00]; + i2c1 = "\0\0\0+status"; + i2c = "\0\0\0+status"; + i2c_arm = "\0\0\0+status"; + i2c_vc = [00 00 00 1c 73 74 61 74 75 73 00 00 00 00 2a 73 74 61 74 75 73 00]; + i2c0_baudrate = [00 00 00 1c 63 6c 6f 63 6b 2d 66 72 65 71 75 65 6e 63 79 3a 30 00]; + i2c1_baudrate = "\0\0\0+clock-frequency:0"; + i2c_baudrate = "\0\0\0+clock-frequency:0"; + i2c_arm_baudrate = "\0\0\0+clock-frequency:0"; + i2c_vc_baudrate = [00 00 00 1c 63 6c 6f 63 6b 2d 66 72 65 71 75 65 6e 63 79 3a 30 00]; + watchdog = "\0\0\0,status"; + random = "\0\0\0-status"; + sd_overclock = "\0\0\0.brcm,overclock-50:0"; + sd_force_pio = "\0\0\0.brcm,force-pio?"; + sd_pio_limit = "\0\0\0.brcm,pio-limit:0"; + sd_debug = "\0\0\0.brcm,debug"; + sdio_overclock = "\0\0\0/brcm,overclock-50:0\0\0\0\00brcm,overclock-50:0"; + axiperf = "\0\0\01status"; + drm_fb0_vc4 = "\0\0\02drm-fb0=\0/soc/gpu"; + drm_fb1_vc4 = "\0\0\02drm-fb1=\0/soc/gpu"; + drm_fb2_vc4 = "\0\0\02drm-fb2=\0/soc/gpu"; + hdmi = "\0\0\03status"; + i2c2_iknowwhatimdoing = [00 00 00 1a 73 74 61 74 75 73 00]; + i2c2_baudrate = [00 00 00 1a 63 6c 6f 63 6b 2d 66 72 65 71 75 65 6e 63 79 3a 30 00]; + sd = "\0\0\0.status"; + sd_poll_once = "\0\0\0.non-removable?"; + bdaddr = "\0\0\04local-bd-address[\0\0\0\04fallback-bd-address?=0\0\0\0\05local-bd-address[\0\0\0\05fallback-bd-address?=0"; + krnbt = "\0\0\04status"; + krnbt_baudrate = "\0\0\04max-speed:0\0\0\0\05max-speed:0"; + audio = "\0\0\06bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}"; + act_led_gpio = "\0\0\07gpios:4"; + act_led_activelow = "\0\0\07gpios:8"; + act_led_trigger = "\0\0\07linux,default-trigger"; + pwr_led_gpio = "\0\0\08gpios:4"; + pwr_led_activelow = "\0\0\08gpios:8"; + pwr_led_trigger = "\0\0\08linux,default-trigger"; + eee = "\0\0\09microchip,eee-enabled?"; + tx_lpi_timer = "\0\0\09microchip,tx-lpi-timer:0"; + eth_led0 = "\0\0\09microchip,led-modes:0"; + eth_led1 = "\0\0\09microchip,led-modes:4"; + eth_downshift_after = "\0\0\09microchip,downshift-after:0"; + eth_max_speed = "\0\0\09max-speed:0"; + }; + + fixedregulator_3v3 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <0x325aa0>; + regulator-min-microvolt = <0x325aa0>; + regulator-name = "3v3"; + phandle = <0x96>; + }; + + fixedregulator_5v0 { + compatible = "regulator-fixed"; + regulator-always-on; + regulator-max-microvolt = <0x4c4b40>; + regulator-min-microvolt = <0x4c4b40>; + regulator-name = "5v0"; + phandle = <0x97>; + }; + + memory@0 { + device_type = "memory"; + reg = <0x00 0x00>; + }; + + leds { + compatible = "gpio-leds"; + phandle = <0x98>; + + led-act { + label = "ACT"; + default-state = "off"; + linux,default-trigger = "mmc0"; + gpios = <0x07 0x1d 0x00>; + phandle = <0x37>; + }; + + led-pwr { + label = "PWR"; + gpios = <0x0b 0x02 0x01>; + default-state = "off"; + linux,default-trigger = "default-on"; + phandle = <0x38>; + }; + }; + + __symbols__ { + aliases = "/aliases"; + chosen = "/chosen"; + rmem = "/reserved-memory"; + cma = "/reserved-memory/linux,cma"; + cpu_thermal = "/thermal-zones/cpu-thermal"; + thermal_trips = "/thermal-zones/cpu-thermal/trips"; + cooling_maps = "/thermal-zones/cpu-thermal/cooling-maps"; + soc = "/soc"; + system_timer = "/soc/timer@7e003000"; + txp = "/soc/txp@7e004000"; + clocks = "/soc/cprman@7e101000"; + mailbox = "/soc/mailbox@7e00b880"; + gpio = "/soc/gpio@7e200000"; + dpi_gpio0 = "/soc/gpio@7e200000/dpi-gpio0"; + emmc_gpio22 = "/soc/gpio@7e200000/emmc-gpio22"; + emmc_gpio34 = "/soc/gpio@7e200000/emmc-gpio34"; + emmc_gpio48 = "/soc/gpio@7e200000/emmc-gpio48"; + gpclk0_gpio4 = "/soc/gpio@7e200000/gpclk0-gpio4"; + gpclk1_gpio5 = "/soc/gpio@7e200000/gpclk1-gpio5"; + gpclk1_gpio42 = "/soc/gpio@7e200000/gpclk1-gpio42"; + gpclk1_gpio44 = "/soc/gpio@7e200000/gpclk1-gpio44"; + gpclk2_gpio6 = "/soc/gpio@7e200000/gpclk2-gpio6"; + gpclk2_gpio43 = "/soc/gpio@7e200000/gpclk2-gpio43"; + i2c0_gpio0 = "/soc/gpio@7e200000/i2c0if-gpio0"; + i2c0_gpio28 = "/soc/gpio@7e200000/i2c0if-gpio28"; + i2c0_gpio44 = "/soc/gpio@7e200000/i2c0if-gpio44"; + i2c1_gpio2 = "/soc/gpio@7e200000/i2c1-gpio2"; + i2c1_gpio44 = "/soc/gpio@7e200000/i2c1-gpio44"; + jtag_gpio22 = "/soc/gpio@7e200000/jtag-gpio22"; + pcm_gpio18 = "/soc/gpio@7e200000/pcm-gpio18"; + pcm_gpio28 = "/soc/gpio@7e200000/pcm-gpio28"; + sdhost_gpio48 = "/soc/gpio@7e200000/sdhost-gpio48"; + spi0_gpio7 = "/soc/gpio@7e200000/spi0-gpio7"; + spi0_gpio35 = "/soc/gpio@7e200000/spi0-gpio35"; + spi1_gpio16 = "/soc/gpio@7e200000/spi1-gpio16"; + spi2_gpio40 = "/soc/gpio@7e200000/spi2-gpio40"; + uart0_gpio14 = "/soc/gpio@7e200000/uart0-gpio14"; + uart0_ctsrts_gpio16 = "/soc/gpio@7e200000/uart0-ctsrts-gpio16"; + uart0_ctsrts_gpio30 = "/soc/gpio@7e200000/uart0-ctsrts-gpio30"; + uart0_gpio32 = "/soc/gpio@7e200000/uart0-gpio32"; + uart0_gpio36 = "/soc/gpio@7e200000/uart0-gpio36"; + uart0_ctsrts_gpio38 = "/soc/gpio@7e200000/uart0-ctsrts-gpio38"; + uart1_gpio14 = "/soc/gpio@7e200000/uart1-gpio14"; + uart1_ctsrts_gpio16 = "/soc/gpio@7e200000/uart1-ctsrts-gpio16"; + uart1_gpio32 = "/soc/gpio@7e200000/uart1-gpio32"; + uart1_ctsrts_gpio30 = "/soc/gpio@7e200000/uart1-ctsrts-gpio30"; + uart1_gpio40 = "/soc/gpio@7e200000/uart1-gpio40"; + uart1_ctsrts_gpio42 = "/soc/gpio@7e200000/uart1-ctsrts-gpio42"; + i2c_slave_gpio18 = "/soc/gpio@7e200000/i2c-slave-gpio18"; + jtag_gpio4 = "/soc/gpio@7e200000/jtag-gpio4"; + pwm0_gpio12 = "/soc/gpio@7e200000/pwm0-gpio12"; + pwm0_gpio18 = "/soc/gpio@7e200000/pwm0-gpio18"; + pwm0_gpio40 = "/soc/gpio@7e200000/pwm0-gpio40"; + pwm1_gpio13 = "/soc/gpio@7e200000/pwm1-gpio13"; + pwm1_gpio19 = "/soc/gpio@7e200000/pwm1-gpio19"; + pwm1_gpio41 = "/soc/gpio@7e200000/pwm1-gpio41"; + pwm1_gpio45 = "/soc/gpio@7e200000/pwm1-gpio45"; + dpi_18bit_cpadhi_gpio0 = "/soc/gpio@7e200000/dpi_18bit_cpadhi_gpio0"; + dpi_18bit_cpadhi_gpio2 = "/soc/gpio@7e200000/dpi_18bit_cpadhi_gpio2"; + dpi_18bit_gpio0 = "/soc/gpio@7e200000/dpi_18bit_gpio0"; + dpi_18bit_gpio2 = "/soc/gpio@7e200000/dpi_18bit_gpio2"; + dpi_16bit_gpio0 = "/soc/gpio@7e200000/dpi_16bit_gpio0"; + dpi_16bit_gpio2 = "/soc/gpio@7e200000/dpi_16bit_gpio2"; + dpi_16bit_cpadhi_gpio0 = "/soc/gpio@7e200000/dpi_16bit_cpadhi_gpio0"; + dpi_16bit_cpadhi_gpio2 = "/soc/gpio@7e200000/dpi_16bit_cpadhi_gpio2"; + gpioout = "/soc/gpio@7e200000/gpioout"; + alt0 = "/soc/gpio@7e200000/alt0"; + spi0_pins = "/soc/gpio@7e200000/spi0_pins"; + spi0_cs_pins = "/soc/gpio@7e200000/spi0_cs_pins"; + i2c0_pins = "/soc/gpio@7e200000/i2c0"; + i2c1_pins = "/soc/gpio@7e200000/i2c1"; + i2s_pins = "/soc/gpio@7e200000/i2s"; + sdio_pins = "/soc/gpio@7e200000/sdio_pins"; + bt_pins = "/soc/gpio@7e200000/bt_pins"; + uart0_pins = "/soc/gpio@7e200000/uart0_pins"; + uart1_pins = "/soc/gpio@7e200000/uart1_pins"; + uart1_bt_pins = "/soc/gpio@7e200000/uart1_bt_pins"; + audio_pins = "/soc/gpio@7e200000/audio_pins"; + uart0 = "/soc/serial@7e201000"; + bt = "/soc/serial@7e201000/bluetooth"; + sdhost = "/soc/mmc@7e202000"; + i2s_clk_consumer = "/soc/i2s@7e203000"; + i2s_clk_producer = "/soc/i2s@7e203000"; + i2s = "/soc/i2s@7e203000"; + spi0 = "/soc/spi@7e204000"; + spi = "/soc/spi@7e204000"; + spidev0 = "/soc/spi@7e204000/spidev@0"; + spidev1 = "/soc/spi@7e204000/spidev@1"; + i2c0if = "/soc/i2c@7e205000"; + dpi = "/soc/dpi@7e208000"; + dsi0 = "/soc/dsi@7e209000"; + aux = "/soc/aux@7e215000"; + uart1 = "/soc/serial@7e215040"; + minibt = "/soc/serial@7e215040/bluetooth"; + spi1 = "/soc/spi@7e215080"; + spi2 = "/soc/spi@7e2150c0"; + pwm = "/soc/pwm@7e20c000"; + mmc = "/soc/mmc@7e300000"; + sdhci = "/soc/mmc@7e300000"; + hvs = "/soc/hvs@7e400000"; + dsi1 = "/soc/dsi@7e700000"; + i2c_arm = "/soc/i2c@7e804000"; + i2c1 = "/soc/i2c@7e804000"; + usb = "/soc/usb@7e980000"; + ethernet = "/soc/usb@7e980000/usb-port@1/usb-port@1/ethernet@1"; + eth_phy = "/soc/usb@7e980000/usb-port@1/usb-port@1/ethernet@1/mdio/ethernet-phy@1"; + dma = "/soc/dma-controller@7e007000"; + intc = "/soc/interrupt-controller@7e00b200"; + watchdog = "/soc/watchdog@7e100000"; + pm = "/soc/watchdog@7e100000"; + random = "/soc/rng@7e104000"; + pixelvalve0 = "/soc/pixelvalve@7e206000"; + pixelvalve1 = "/soc/pixelvalve@7e207000"; + thermal = "/soc/thermal@7e212000"; + i2c2 = "/soc/i2c@7e805000"; + vec = "/soc/vec@7e806000"; + pixelvalve2 = "/soc/pixelvalve@7e807000"; + hdmi = "/soc/hdmi@7e902000"; + v3d = "/soc/v3d@7ec00000"; + vc4 = "/soc/gpu"; + local_intc = "/soc/local_intc@40000000"; + mmcnr = "/soc/mmcnr@7e300000"; + brcmf = "/soc/mmcnr@7e300000/wifi@1"; + firmwarekms = "/soc/firmwarekms@7e600000"; + smi = "/soc/smi@7e600000"; + csi0 = "/soc/csi@7e800000"; + csi1 = "/soc/csi@7e801000"; + axiperf = "/soc/axiperf"; + i2c0mux = "/soc/i2c0mux"; + i2c_csi_dsi0 = "/soc/i2c0mux/i2c@0"; + i2c_vc = "/soc/i2c0mux/i2c@0"; + i2c0 = "/soc/i2c0mux/i2c@0"; + i2c_csi_dsi = "/soc/i2c0mux/i2c@1"; + firmware = "/soc/firmware"; + firmware_clocks = "/soc/firmware/clocks"; + vcio = "/soc/firmware/vcio"; + expgpio = "/soc/firmware/expgpio"; + power = "/soc/power"; + vchiq = "/soc/mailbox@7e00b840"; + fb = "/soc/fb"; + sound = "/soc/sound"; + clk_osc = "/clocks/clk-osc"; + clk_usb = "/clocks/clk-usb"; + usbphy = "/phy"; + cpus = "/cpus"; + cpu0 = "/cpus/cpu@0"; + cpu1 = "/cpus/cpu@1"; + cpu2 = "/cpus/cpu@2"; + cpu3 = "/cpus/cpu@3"; + l2 = "/cpus/l2-cache0"; + cam1_reg = "/cam1_regulator"; + cam1_clk = "/cam1_clk"; + cam0_regulator = "/cam0_regulator"; + cam0_clk = "/cam0_clk"; + cam0_reg = "/cam_dummy_reg"; + cam_dummy_reg = "/cam_dummy_reg"; + vdd_3v3_reg = "/fixedregulator_3v3"; + vdd_5v0_reg = "/fixedregulator_5v0"; + leds = "/leds"; + led_act = "/leds/led-act"; + led_pwr = "/leds/led-pwr"; + }; +}; diff --git a/Lab5/peripherals/base.h b/Lab5/peripherals/base.h new file mode 100644 index 000000000..50281ad8b --- /dev/null +++ b/Lab5/peripherals/base.h @@ -0,0 +1,7 @@ +#ifndef _P_BASE_H +#define _P_BASE_H + +// RPi3B+ reserves the memory above address 0x3F000000 for devices. +#define PBASE 0x3F000000 + +#endif /*_P_BASE_H */ diff --git a/Lab5/peripherals/gpio.h b/Lab5/peripherals/gpio.h new file mode 100644 index 000000000..6f26401f2 --- /dev/null +++ b/Lab5/peripherals/gpio.h @@ -0,0 +1,13 @@ +#ifndef _P_GPIO_H +#define _P_GPIO_H + +#include "base.h" + +// GPIO14 and GPIO15 are controlled by GPFSEL1(0x3F200004) +#define GPFSEL1 ((volatile unsigned int *)(PBASE+0x00200004)) +#define GPSET0 ((volatile unsigned int *)(PBASE+0x0020001C)) +#define GPCLR0 ((volatile unsigned int *)(PBASE+0x00200028)) +#define GPPUD ((volatile unsigned int *)(PBASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int *)(PBASE+0x00200098)) + +#endif /*_P_GPIO_H */ diff --git a/Lab5/peripherals/mailbox.c b/Lab5/peripherals/mailbox.c new file mode 100644 index 000000000..149e3d652 --- /dev/null +++ b/Lab5/peripherals/mailbox.c @@ -0,0 +1,113 @@ +#include "mailbox.h" +#include "mini_uart.h" + +// Check messageBuffer status. +volatile unsigned int* const mailboxStatus = MAILBOX_STATUS; +volatile unsigned int* const mailboxRead = MAILBOX_READ; +volatile unsigned int* const mailboxWrite = MAILBOX_WRITE; + +// Message buffer must be 16-byte aligned. +unsigned int messageBuffer[8] __attribute__((aligned(16))); + + +void mailbox_write(unsigned int channel, unsigned int data) { + // Wait for the mailbox to become not full. + while (*mailboxStatus & MAILBOX_FULL); + // Write the data (with channel) to the mailbox. + *mailboxWrite = data | (channel & 0xF); +} + +// Nothing is written in mailbox for channel 8. However, when using other channels this value might have its +// uses. +unsigned int mailbox_read(unsigned int channel) { + unsigned int data; + + do { + // If mailbox isn't empty, read it. + while (*mailboxStatus & MAILBOX_EMPTY); + data = *mailboxRead; + + } while (channel != (data & 0xF)); // Check whether the response is meant for the channel. + + return (data & 0xFFFFFFF0); +} + +void get_board_revision() { + messageBuffer[0] = sizeof(messageBuffer); // buffer size in bytes + messageBuffer[1] = MAILBOX_REQUEST; + // tags begin + // Tag identifier: Specifies the type of request or information being queried or configured. + messageBuffer[2] = MAILBOX_TAG_GETBOARD; + + // Value buffer size: Indicates the size of the value buffer that follows, specifying how much space is allocated + // for the data related to this tag. It includes space for both the request data (if any) and + // the response data. + // Allocates 4 bytes, since board revision number is a 32-bit value. + messageBuffer[3] = 4; + + // Request/Response Size: Specifies the size of the request data(which may be 0 if no request data is needed), + // Upon processing the message, the GPU updates this field to reflect the size of the response data it has placed + // in the value buffer. + messageBuffer[4] = TAG_REQUEST_CODE; + + // Value Buffer: Contains the actual data for the request or response. The content and format of this data vary + // depending on the tag identifier. For a request, this might be parameters or configuration data; for a response, + // it's the information being returned by the GPU. + messageBuffer[5] = 0; + // tags end + // A zero value marks the end of the tags in the message. This signals to the GPU that there are no more tags to + // process in this message. + messageBuffer[6] = MAILBOX_TAG_END; + messageBuffer[7] = 0; // Unused for requesting board revision number. + + // Here we're passing the ADDRESS of messageBuffer, not the actual data stored in the buffer! + // 0xC0000000 is for bypassing the CPU cache, ensuring that the mailbox message is READ DIRECTLY from memory by the GPU. + mailbox_write(MAILBOX_CH_PROP, (unsigned int)(unsigned long)messageBuffer | 0xC0000000); + + // Wait until the messageBuffer is available. + while (messageBuffer[1] != MAILBOX_RESPONSE_SUCCESS); + + uart_send_string("Board revision: "); + uart_send_uint(messageBuffer[5]); // it should be 0xa020d3 for rpi3 b+ +} + +void get_arm_memory() { + messageBuffer[0] = sizeof(messageBuffer); // buffer size in bytes + messageBuffer[1] = MAILBOX_REQUEST; + // tags begin + // Tag identifier: Specifies the type of request or information being queried or configured. + messageBuffer[2] = MAILBOX_TAG_GETARMMEM; + + // Value buffer size: Indicates the size of the value buffer that follows, specifying how much space is allocated + // for the data related to this tag. It includes space for both the request data (if any) and + // the response data. + // Allocates 8 bytes, containing both base address and size. + messageBuffer[3] = 8; + + // Request/Response Size: Specifies the size of the request data(which may be 0 if no request data is needed), + // Upon processing the message, the GPU updates this field to reflect the size of the response data it has placed + // in the value buffer. + messageBuffer[4] = TAG_REQUEST_CODE; + + // Value Buffer: Contains the actual data for the request or response. The content and format of this data vary + // depending on the tag identifier. For a request, this might be parameters or configuration data; for a response, + // it's the information being returned by the GPU. + messageBuffer[5] = 0; + messageBuffer[6] = 0; + // tags end + // A zero value marks the end of the tags in the message. This signals to the GPU that there are no more tags to + // process in this message. + messageBuffer[7] = MAILBOX_TAG_END; + + // Here we're passing the ADDRESS of messageBuffer, not the actual data stored in the buffer! + // 0xC0000000 is for bypassing the CPU cache, ensuring that the mailbox message is READ DIRECTLY from memory by the GPU. + mailbox_write(MAILBOX_CH_PROP, (unsigned int)(unsigned long)messageBuffer | 0xC0000000); + // Wait until the messageBuffer is available. + while (messageBuffer[1] != MAILBOX_RESPONSE_SUCCESS); + + uart_send_string("ARM base memory address: 0x"); + uart_send_uint(messageBuffer[5]); + uart_send_string("\r\n"); + uart_send_string("ARM memory size: 0x"); + uart_send_uint(messageBuffer[6]); +} \ No newline at end of file diff --git a/Lab5/peripherals/mailbox.h b/Lab5/peripherals/mailbox.h new file mode 100644 index 000000000..8510e943e --- /dev/null +++ b/Lab5/peripherals/mailbox.h @@ -0,0 +1,41 @@ +#ifndef _MAILBOX_H_ +#define _MAILBOX_H_ + +#include "base.h" + +#define MAILBOX_REQUEST 0 + +/* channels */ +#define MAILBOX_CH_POWER 0 +#define MAILBOX_CH_FB 1 +#define MAILBOX_CH_VUART 2 +#define MAILBOX_CH_VCHIQ 3 +#define MAILBOX_CH_LEDS 4 +#define MAILBOX_CH_BTNS 5 +#define MAILBOX_CH_TOUCH 6 +#define MAILBOX_CH_COUNT 7 +#define MAILBOX_CH_PROP 8 + +/* tags */ +#define TAG_REQUEST_CODE 0x00000000 +#define MAILBOX_TAG_GETBOARD 0x00010002 +#define MAILBOX_TAG_GETSERIAL 0x00010004 +#define MAILBOX_TAG_GETARMMEM 0x00010005 +#define MAILBOX_TAG_END 0x00000000 + +#define MAILBOX_BASE PBASE + 0xb880 +#define MAILBOX_READ ((volatile unsigned int*)MAILBOX_BASE) +#define MAILBOX_POLL ((volatile unsigned int*)(MAILBOX_BASE + 0x10)) +#define MAILBOX_SENDER ((volatile unsigned int*)(MAILBOX_BASE + 0x14)) +#define MAILBOX_STATUS ((volatile unsigned int*)(MAILBOX_BASE + 0x18)) +#define MAILBOX_CONFIG ((volatile unsigned int*)(MAILBOX_BASE + 0x1C)) +#define MAILBOX_WRITE ((volatile unsigned int*)(MAILBOX_BASE + 0x20)) +#define MAILBOX_EMPTY 0x40000000 // empty flag is represented by bit 30. Bit 30 is set(1), the mailbox is empty. +#define MAILBOX_FULL 0x80000000 // full flag is represented by bit 31. Bit 31 is set(1), the mailbox is full. +#define MAILBOX_RESPONSE_SUCCESS 0x80000000 // mailbox writes this to the message buffer if response success. +#define MAILBOX_RESPONSE_FAIL 0x80000001 // mailbox writes this to the message buffer if response fail. + +void get_board_revision(); +void get_arm_memory(); + +#endif \ No newline at end of file diff --git a/Lab5/peripherals/mini_uart.c b/Lab5/peripherals/mini_uart.c new file mode 100644 index 000000000..292cce7fb --- /dev/null +++ b/Lab5/peripherals/mini_uart.c @@ -0,0 +1,258 @@ +#include "mm.h" +#include "gpio.h" +#include "mini_uart.h" +#include "utils.h" + +void uart_init (void) +{ + unsigned int selector; + + // Set the bits in register GPFSEL1 for gpio14 and gpio15, configuring them + // for the use of mini UART. + selector = *GPFSEL1; + selector &= ~(7u << 12); // clean gpio14 + selector |= 2u << 12; // set alt5 for gpio14 + selector &= ~(7u << 15); // clean gpio15 + selector |= 2u << 15; // set alt5 for gpio 15 + *GPFSEL1 = selector; + + // Switching the states of the pin. + // Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither + // to remove the current Pull-up/down). + *GPPUD = 0u; + + // Wait 150 cycles – this provides the required set-up time for the control signal. + // delay(150); + delay(150u); + + // Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to + // modify – NOTE only the pads which receive a clock will be modified, all others + // will retain their previous state. + *GPPUDCLK0 = (1u << 14) | (1u << 15); + delay(150); + + // Write to GPPUDCLK0/1 to remove the clock + *GPPUDCLK0 = 0u; + + // Enabling mini uart + *AUX_ENABLES = 1u; // Enable mini uart (this also enables access to its registers) + *AUX_MU_CNTL_REG = 0u; // Disable auto flow control and disable receiver and transmitter (for now) + *AUX_MU_IER_REG = 0u; // Disable receive and transmit interrupts + *AUX_MU_LCR_REG = 3u; // Enable 8 bit mode + *AUX_MU_MCR_REG = 0u; // Set RTS line to be always high + *AUX_MU_BAUD_REG = 270u; // Set baud rate to 115200(meaning the serial port is capable of transferring + // a maximum of 115200 bits per second) + // baudrate = (system_clk_freq = 250MHz) / (8 * (baudrate_reg + 1)) + *AUX_MU_IIR_REG = 6u; + *AUX_MU_CNTL_REG = 3u; //Finally, enable transmitter and receiver +} + +void uart_send (char c) +{ + // bit_5 == 1 -> writable + // 0x20 = 0000 0000 0010 0000 + while (!((*AUX_MU_LSR_REG) & 0x20)); + *AUX_MU_IO_REG = c; +} + +char uart_recv (void) +{ + // bit_0 == 1 -> readable + // 0x01 = 0000 0000 0000 0001 + while (!((*AUX_MU_LSR_REG) & 0x01)); + + return (*AUX_MU_IO_REG & 0xFF); +} + +void uart_send_string(char* str) +{ + for (int i = 0; str[i] != '\0'; i++) { + uart_send((char)str[i]); + } +} + +// Convert unsigned int to string and send it to uart. +void uart_send_uint(unsigned long data) { + + // 64-bit + char str[17]; + int isZero = 1; + for (int i = 0; i < 16; i++) { + // Take the LS 4 bits. + int value = data & 0xF; + str[15 - i] = int2char(value); + data = data >> 4; + } + + for (int i = 0; i < 16; i++) { + // Don't print the preceding zeros. + if (str[i] != '0') { + isZero = 0; + } + + if (!isZero) { + uart_send(str[i]); + } + } + + // If the value is zero. + if (isZero) + uart_send('0'); + // uart_send_string("\r\n"); +} + +void uart_send_int(unsigned long data) { + char str[30]; + int isZero = 1; + + for (int i = 0; i < 30; i++) { + // Convert to ascii code. '0' <-> 48 + str[30 - 1 - i] = (data % 10) + 48; + data /= 10; + } + + for (int i = 0; i < 30; i++) { + if (str[i] != '0') { + isZero = 0; + } + + if (!isZero) { + uart_send(str[i]); + } + } + + if (isZero) { + uart_send('0'); + } + +} + +void enable_uart_interrupt(void) { + /* + * Manual p.12 + * If AUX_MU_IER_REG[0] is set, receive interrupts are enabled. + * If AUX_MU_IER_REG[1] is set, transmit interrupts are enabled. + */ + + *AUX_MU_IER_REG |= 3u; + + // Enable second level interrupt controller. + volatile unsigned int* IRQ_ENABLE_S1 = (volatile unsigned int *)(PBASE + 0x0000B210); + *IRQ_ENABLE_S1 |= (1 << 29); +} + +void disable_uart_interrupt(void) { + + // Disable both receive and transmit interrupts. + *AUX_MU_IER_REG = 0u; +} + +// #define ASYNC_BUF_SIZE 256 +// volatile char rx_buf_async[ASYNC_BUF_SIZE]; +// volatile int rx_buf_head = 0; +// volatile int rx_buf_tail = 0; + +// volatile char tx_buf_async[ASYNC_BUF_SIZE]; +// volatile int tx_buf_head = 0; +// volatile int tx_buf_tail = 0; + +// void handle_uart_interrupt() { + +// // disable_uart_interrupt(); + +// // On manual P.13 +// // Receive interrupt.(Receiver holds valid byte) +// if (*AUX_MU_IIR_REG & 0x4) { + +// // Read the least 8-bits(1 byte, the length of char) in IO register. +// // Indicating data is sent from laptop. +// char c = *AUX_MU_IO_REG & 0xFF; +// int next = (rx_buf_tail + 1) % ASYNC_BUF_SIZE; + +// // Check buffer overflow. +// if (next != rx_buf_head) { +// rx_buf_async[rx_buf_tail] = c; +// rx_buf_tail = next; +// } +// } +// /* +// // Transmit interrupt.(Transmit register empty) +// else if ((*AUX_MU_IIR_REG & 0x2) && (tx_buf_head != tx_buf_tail)) { +// *AUX_MU_IO_REG = tx_buf_async[tx_buf_head]; +// tx_buf_head = (tx_buf_head + 1) % ASYNC_BUF_SIZE; +// } +// */ + +// // enable_uart_interrupt(); + +// } + +// int uart_recv_async(char* buf, int buf_size) { +// int bytes_read = 0; + +// // Write the data within the RX buffer into buf. +// // Continue writing until the RX buffer is empty or exceed buf_size. +// while ((rx_buf_head != rx_buf_tail) && (bytes_read < buf_size)) { +// buf[bytes_read++] = rx_buf_async[rx_buf_head]; +// rx_buf_head = (rx_buf_head + 1) % ASYNC_BUF_SIZE; +// } + +// return bytes_read; +// } + +// void uart_send_async(const char* data, int len) { +// for (int i = 0; i < len; i++) { +// int next = (tx_buf_tail + 1) % ASYNC_BUF_SIZE; + +// // Wait until Transmit buffer isn't full. +// if (next == tx_buf_head) { +// continue; +// } + +// tx_buf_async[tx_buf_tail] = data[i]; +// tx_buf_tail = next; +// } + +// if (tx_buf_head == tx_buf_tail) { +// // Disable TX interrupt if no data +// *AUX_MU_IER_REG &= ~0x2; +// } else { +// *AUX_MU_IER_REG |= 0x2; +// } +// } + +#define BUFFER_SIZE 256 +volatile char tx_buffer[BUFFER_SIZE]; +volatile unsigned int tx_head = 0; +volatile unsigned int tx_tail = 0; + +// UART interrupt handler +void handle_uart_interrupt(void* data) { + // Check if the interrupt is for a received character + if (*AUX_MU_IIR_REG & 0x4) { // Receiver holds a valid byte + char received_char = *AUX_MU_IO_REG & 0xFF; // Read the received byte + uart_send_async(received_char); // Echo it back immediately or queue + } + + // Transmit buffer management + if ((*AUX_MU_IIR_REG & 0x2) && (tx_head != tx_tail)) { // Transmit register empty + *AUX_MU_IO_REG = tx_buffer[tx_tail]; // Send next character + tx_tail = (tx_tail + 1) % BUFFER_SIZE; // Move tail forward + } +} + +// Queue a character for transmission or send immediately if ready +void uart_send_async(char c) { + // Attempt immediate transmission if UART is ready and buffer is empty + if ((*AUX_MU_LSR_REG & 0x20) && (tx_head == tx_tail)) { + *AUX_MU_IO_REG = c; // Transmit immediately + } else { + // Otherwise, queue the character + unsigned int next_head = (tx_head + 1) % BUFFER_SIZE; + if (next_head != tx_tail) { // Ensure the buffer isn't full + tx_buffer[tx_head] = c; + tx_head = next_head; + *AUX_MU_IER_REG |= 0x2; // Ensure transmit interrupts are enabled + } + } +} \ No newline at end of file diff --git a/Lab5/peripherals/mini_uart.h b/Lab5/peripherals/mini_uart.h new file mode 100644 index 000000000..de5194ff5 --- /dev/null +++ b/Lab5/peripherals/mini_uart.h @@ -0,0 +1,38 @@ +#ifndef _MINI_UART_H +#define _MINI_UART_H + +#include "base.h" + +#define AUX_IRQ ((volatile unsigned int*)(PBASE+0x00215000)) +#define AUX_ENABLES ((volatile unsigned int*)(PBASE+0x00215004)) +#define AUX_MU_IO_REG ((volatile unsigned int*)(PBASE+0x00215040)) // Used to write data to and read data from the uart FIFOs. +#define AUX_MU_IER_REG ((volatile unsigned int*)(PBASE+0x00215044)) // Used to enable interrupts. +#define AUX_MU_IIR_REG ((volatile unsigned int*)(PBASE+0x00215048)) // Shows the interrupt status. +#define AUX_MU_LCR_REG ((volatile unsigned int*)(PBASE+0x0021504C)) // Controls the line data format and gives access to the baudrate register. +#define AUX_MU_MCR_REG ((volatile unsigned int*)(PBASE+0x00215050)) // Controls the "modem" signals. +#define AUX_MU_LSR_REG ((volatile unsigned int*)(PBASE+0x00215054)) // Shows data status. +#define AUX_MU_MSR_REG ((volatile unsigned int*)(PBASE+0x00215058)) // Shows modem status. +#define AUX_MU_SCRATCH ((volatile unsigned int*)(PBASE+0x0021505C)) // Single byte storage. +#define AUX_MU_CNTL_REG ((volatile unsigned int*)(PBASE+0x00215060)) // Provides access to some extra useful and nice features not found on a normal 16550 uart. +#define AUX_MU_STAT_REG ((volatile unsigned int*)(PBASE+0x00215064)) // Provides a lot of useful information about the internal status of the mini uart not found on + // a normal 16550 uart. +#define AUX_MU_BAUD_REG ((volatile unsigned int*)(PBASE+0x00215068)) // Allows direct access to the 16-bit wide baudrate counter. + + + +void uart_init (void); +char uart_recv (void); +void uart_send (char c); +void uart_send_string(char* str); +// For hexadecimal. +void uart_send_uint(unsigned long data); +// For decimal. +void uart_send_int(unsigned long data); +void enable_uart_interrupt(void); +void disable_uart_interrupt(void); +void handle_uart_interrupt(void* data); +int uart_recv_async(char* buf, int buf_size); +// void uart_send_async(const char* data, int len); +void uart_send_async(char c); + +#endif /*_MINI_UART_H */ \ No newline at end of file diff --git a/Lab5/peripherals/mm.h b/Lab5/peripherals/mm.h new file mode 100644 index 000000000..7e4517348 --- /dev/null +++ b/Lab5/peripherals/mm.h @@ -0,0 +1,11 @@ +#ifndef _MM_H +#define _MM_H + +// Heap end address: 0x1195B88 +// Allocate 1MB for stack: 0x1195B88 + 0x100000 = 0x1295B88 +#define LOW_MEMORY 0x400000 // Place the bootloader sp at 0x400000 +#define USER_STACK (1 << 23) // sp for a user program.(Ranging from 0x800000 to 0x400000) + + + +#endif /*_MM_H */ \ No newline at end of file diff --git a/Lab5/peripherals/utils.c b/Lab5/peripherals/utils.c new file mode 100644 index 000000000..aaee9aec6 --- /dev/null +++ b/Lab5/peripherals/utils.c @@ -0,0 +1,132 @@ +#include "utils.h" + +int strcmp(const char* str1, const char* str2) { + for (; *str1 == *str2; str1++, str2++) { + if (*str1 == '\0') + return 0; + } + return (unsigned char)*str1 - (unsigned char)*str2; +} + +char* strcpy(char* destination, const char* source) { + int ind = 0; + for (; source[ind] != '\0'; ind++) { + destination[ind] = source[ind]; + } + destination[ind] = '\0'; + + return destination; +} + +// Convert integers to characters. +char int2char(int data) { + switch (data) { + case 10: + return 'A'; + case 11: + return 'B'; + case 12: + return 'C'; + case 13: + return 'D'; + case 14: + return 'E'; + case 15: + return 'F'; + default: + // ascii code of '0' is 48. + return data + 48; + } +} + +// Convert hexadecimal strings to unsigned integers. Pass in the string array and the length of +// the string. +unsigned int strHex2Int(char* str, int len) { + unsigned int tot = 0; + + for (int i = 0; i < len; i++) { + switch (str[i]) { + case 'A': + tot += 10 * power(16, len - i - 1); + break; + case 'B': + tot += 11 * power(16, len - i - 1); + break; + case 'C': + tot += 12 * power(16, len - i - 1); + break; + case 'D': + tot += 13 * power(16, len - i - 1); + break; + case 'E': + tot += 14 * power(16, len - i - 1); + break; + case 'F': + tot += 15 * power(16, len - i - 1); + break; + default: + // Ascii code for '0' is 48. + tot += (str[i] - 48) * power(16, len - i - 1); + } + } + + return tot; +} + +unsigned int str2Int(char* str, int len) { + unsigned int tot = 0; + + for (int i = 0; i < len; i++) { + switch (str[i]) { + case 'A': + tot += 10 * power(10, len - i - 1); + break; + case 'B': + tot += 11 * power(10, len - i - 1); + break; + case 'C': + tot += 12 * power(10, len - i - 1); + break; + case 'D': + tot += 13 * power(10, len - i - 1); + break; + case 'E': + tot += 14 * power(10, len - i - 1); + break; + case 'F': + tot += 15 * power(10, len - i - 1); + break; + default: + // Ascii code for '0' is 48. + tot += (str[i] - 48) * power(10, len - i - 1); + } + } + + return tot; +} + +// Calculating base to the power of p. +unsigned int power(unsigned int base, int p) { + unsigned int tot = 1; + for (int i = 0; i < p; i++) { + tot *= base; + } + return tot; +} + +long unsigned int strlen(const char* str) { + int i = 0; + while (str[i] != '\0') { + i++; + } + + return i; +} + +void delay(unsigned int clock) { + while (clock--) { + asm volatile ( + "nop;" + ); + } +} \ No newline at end of file diff --git a/Lab5/peripherals/utils.h b/Lab5/peripherals/utils.h new file mode 100644 index 000000000..0f9f8350d --- /dev/null +++ b/Lab5/peripherals/utils.h @@ -0,0 +1,20 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include +#include + +#define FALSE 0 +#define TRUE 1 + +int strcmp(const char* str1, const char* str2); +char* uint_to_string(unsigned int data); +char int2char(int data); +void delay(unsigned int time); +unsigned int power(unsigned int base, int p); +unsigned int strHex2Int(char* str, int len); +unsigned int str2Int(char* str, int len); +char* strcpy(char* destination, const char* source); +long unsigned int strlen(const char* str); + +#endif \ No newline at end of file diff --git a/Lab5/rootfs/test1.txt b/Lab5/rootfs/test1.txt new file mode 100644 index 000000000..737c7c293 --- /dev/null +++ b/Lab5/rootfs/test1.txt @@ -0,0 +1 @@ +This is text1. \ No newline at end of file diff --git a/Lab5/rootfs/test2.txt b/Lab5/rootfs/test2.txt new file mode 100644 index 000000000..37626f1e1 --- /dev/null +++ b/Lab5/rootfs/test2.txt @@ -0,0 +1 @@ +This is text2. \ No newline at end of file diff --git a/Lab5/rootfs/test_user.img b/Lab5/rootfs/test_user.img new file mode 100755 index 000000000..1adf648a4 Binary files /dev/null and b/Lab5/rootfs/test_user.img differ diff --git a/Lab5/rootfs2/dir1/sub_dir1/test b/Lab5/rootfs2/dir1/sub_dir1/test new file mode 100644 index 000000000..793354cac --- /dev/null +++ b/Lab5/rootfs2/dir1/sub_dir1/test @@ -0,0 +1,2 @@ +This is test! +Newline. \ No newline at end of file diff --git a/Lab5/rootfs2/testRoot b/Lab5/rootfs2/testRoot new file mode 100644 index 000000000..d5fb86f42 --- /dev/null +++ b/Lab5/rootfs2/testRoot @@ -0,0 +1 @@ +This is testRoot. \ No newline at end of file diff --git a/Lab5/send_image.py b/Lab5/send_image.py new file mode 100644 index 000000000..6c7870c45 --- /dev/null +++ b/Lab5/send_image.py @@ -0,0 +1,40 @@ +import serial +import time +import os + +# Configuration +serial_port = '/dev/ttyUSB0' # Adjust this to your system's serial port +# serial_port = '/dev/tty.PL2303-USBtoUART1120' # Adjust this to your system's serial port +baud_rate = 115200 # Adjust baud rate as needed +kernel_image_path = 'build/kernel/kernel8.img' # Path to your kernel image + +# Function to send a file through serial +def send_file(file_path, port, baud): + try: + with serial.Serial(port, baud, timeout=1) as ser: + # Get file size + file_size = os.path.getsize(file_path) + print(f"File size: {file_size} bytes") + + # Send file size (4 bytes, little-endian format) + ser.write(file_size.to_bytes(4, byteorder='little')) + time.sleep(0.5) # Give the receiver a moment to process the size + + with open(file_path, 'rb') as file: + print("Sending file...") + while True: + chunk = file.read(1024) # Read in chunks of 1024 bytes + if not chunk: + break # End of file + ser.write(chunk) + time.sleep(0.02) # Short delay to ensure data is transmitted properly + print("File sent successfully.") + + except serial.SerialException as e: + print(f"Error: {e}") + except FileNotFoundError: + print(f"Error: File {file_path} not found.") + +# Main function +if __name__ == '__main__': + send_file(kernel_image_path, serial_port, baud_rate) \ No newline at end of file diff --git a/Lab5/test_user/Makefile b/Lab5/test_user/Makefile new file mode 100644 index 000000000..1d1087f80 --- /dev/null +++ b/Lab5/test_user/Makefile @@ -0,0 +1,36 @@ +CC = aarch64-linux-gnu-gcc +AS = aarch64-linux-gnu-as +LD = aarch64-linux-gnu-ld +OBJCOPY = aarch64-linux-gnu-objcopy +CFLAGS = -I../include -I../peripherals -fno-stack-protector -g -O0 +ASFLAGS = + +# Adjust these paths as necessary +SRC_C = $(wildcard *.c) +SRC_S = $(wildcard *.S) +OBJS = $(SRC_C:%.c=%.o) $(SRC_S:%.S=%.o) + +# Include peripherals objects +PERIPHERALS_OBJS = $(wildcard $(BUILD_DIR)/peripherals/*.o) +LINKER_SCRIPT = linker.ld +KERNEL8_ELF = test_user.elf +KERNEL8_IMG = test_user.img + +all: $(OBJS) kernel + +%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.S + @mkdir -p $(@D) + $(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@ + +kernel: $(OBJS) $(PERIPHERALS_OBJS) + $(LD) -T $(LINKER_SCRIPT) -o $(KERNEL8_ELF) $^ + $(OBJCOPY) -O binary $(KERNEL8_ELF) $(KERNEL8_IMG) + +clean: + rm test_user.o test_user.elf test_user.img + +.PHONY: all kernel diff --git a/Lab5/test_user/linker.ld b/Lab5/test_user/linker.ld new file mode 100644 index 000000000..bb6c709cd --- /dev/null +++ b/Lab5/test_user/linker.ld @@ -0,0 +1,15 @@ +SECTIONS +{ + . = 0x500000; + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + # Use NOLOAD in .bss ensures that this section is allocated space in the memory layout without + # attempting to load any pre-initialized data from the binary. + .bss (NOLOAD) : { + . = ALIGN(0x8); + bss_begin = .; + *(.bss*) + bss_end = .; + } +} \ No newline at end of file diff --git a/Lab5/test_user/test_user.S b/Lab5/test_user/test_user.S new file mode 100644 index 000000000..ce7e53908 --- /dev/null +++ b/Lab5/test_user/test_user.S @@ -0,0 +1,11 @@ +.section ".text" +.global _start +_start: + mov x0, 0 +1: + add x0, x0, 1 + svc 0 + cmp x0, 5 + blt 1b +1: + b 1b diff --git a/Lab5/test_user/test_user.elf b/Lab5/test_user/test_user.elf new file mode 100755 index 000000000..2ff4f2991 Binary files /dev/null and b/Lab5/test_user/test_user.elf differ diff --git a/Lab5/test_user/test_user.img b/Lab5/test_user/test_user.img new file mode 100755 index 000000000..1adf648a4 Binary files /dev/null and b/Lab5/test_user/test_user.img differ diff --git a/Lab5/test_user/test_user.o b/Lab5/test_user/test_user.o new file mode 100644 index 000000000..92e44ca8a Binary files /dev/null and b/Lab5/test_user/test_user.o differ