diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..1726791b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +nand_* +*.docx +*.img diff --git a/README.md b/README.md index 06b25aac8..d2824bca3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Operating Systems Capstone 2022 ## Happy Coding ~ - +- Practice how to build an OS - GitHub account name: soulrrrrr - Student ID: 0816034 - Your name: 蔡家倫 diff --git a/finalproject/make_ioctl b/finalproject/make_ioctl new file mode 100644 index 000000000..210920a96 --- /dev/null +++ b/finalproject/make_ioctl @@ -0,0 +1,2 @@ +gcc -Wall ssd_fuse.c `pkg-config fuse3 --cflags --libs` -D_FILE_OFFSET_BITS=64 -o ssd_fuse + diff --git a/finalproject/make_ssd b/finalproject/make_ssd new file mode 100644 index 000000000..d536d2008 --- /dev/null +++ b/finalproject/make_ssd @@ -0,0 +1,6 @@ +gcc -Wall ssd_fuse.c `pkg-config fuse3 --cflags --libs` -D_FILE_OFFSET_BITS=64 -o ssd_fuse +gcc -Wall ssd_fuse_dut.c -o ssd_fuse_dut +fusermount -u /tmp/ssd +rm -rf /tmp/ssd +mkdir /tmp/ssd + diff --git a/finalproject/nand_0 b/finalproject/nand_0 new file mode 100644 index 000000000..3f55fb052 Binary files /dev/null and b/finalproject/nand_0 differ diff --git a/finalproject/nand_1 b/finalproject/nand_1 new file mode 100644 index 000000000..eb2fab4ae Binary files /dev/null and b/finalproject/nand_1 differ diff --git a/finalproject/nand_10 b/finalproject/nand_10 new file mode 100644 index 000000000..394606252 Binary files /dev/null and b/finalproject/nand_10 differ diff --git a/finalproject/nand_11 b/finalproject/nand_11 new file mode 100644 index 000000000..2ee99c10a Binary files /dev/null and b/finalproject/nand_11 differ diff --git a/finalproject/nand_12 b/finalproject/nand_12 new file mode 100644 index 000000000..b7f8b0ee3 Binary files /dev/null and b/finalproject/nand_12 differ diff --git a/finalproject/nand_2 b/finalproject/nand_2 new file mode 100644 index 000000000..212d3ad7e Binary files /dev/null and b/finalproject/nand_2 differ diff --git a/finalproject/nand_3 b/finalproject/nand_3 new file mode 100644 index 000000000..ac7ce4385 Binary files /dev/null and b/finalproject/nand_3 differ diff --git a/finalproject/nand_4 b/finalproject/nand_4 new file mode 100644 index 000000000..aa9018fc0 Binary files /dev/null and b/finalproject/nand_4 differ diff --git a/finalproject/nand_5 b/finalproject/nand_5 new file mode 100644 index 000000000..c465bafcf Binary files /dev/null and b/finalproject/nand_5 differ diff --git a/finalproject/nand_6 b/finalproject/nand_6 new file mode 100644 index 000000000..69e4ba30f Binary files /dev/null and b/finalproject/nand_6 differ diff --git a/finalproject/nand_7 b/finalproject/nand_7 new file mode 100644 index 000000000..79b9e9c71 Binary files /dev/null and b/finalproject/nand_7 differ diff --git a/finalproject/nand_8 b/finalproject/nand_8 new file mode 100644 index 000000000..2a405bd8d Binary files /dev/null and b/finalproject/nand_8 differ diff --git a/finalproject/nand_9 b/finalproject/nand_9 new file mode 100644 index 000000000..718223286 Binary files /dev/null and b/finalproject/nand_9 differ diff --git a/finalproject/report.md b/finalproject/report.md new file mode 100644 index 000000000..875beeca2 --- /dev/null +++ b/finalproject/report.md @@ -0,0 +1,348 @@ +# 作業系統總整與實作 Final Project + +0816034 蔡家倫 + +## 0. code + +[Github Link](https://github.com/soulrrrrr/osc2022/tree/lab8/finalproject) + +## 1. FUSE 目的及原理 + +FUSE 是 filesystem in userspace 的簡稱,可以讓 userspace programs 建立虛擬檔案系統,不用重新 compile kernel。本次 lab 就是使用 FUSE 模擬 ssd 。 +FUSE 原理為用 libfuse library 接管 read request from kernel,處理完後再 send responses back。 +在 ssd_fuse.c 中的 fuse_operations 就是我們要定義如何接管這些 read/write 方法。 + +## 2. 修改的地方 + +我修改的部分有 TODO 的四個部分 (ssd_do_write, ftl_write, ssd_do_read, ftl_read) 以及 garbage collection + +## 3. 實作的方法 + +詳細方法打在 code 註解裡 + +### ssd_do_write + +```c +static int ssd_do_write(const char* buf, size_t size, off_t offset) +{ + int tmp_lba, tmp_lba_range, process_size; + int idx, curr_size, remain_size, rst; + char* tmp_buf; + + host_write_size += size; + if (ssd_expand(offset + size) != 0) + { + return -ENOMEM; + } + + tmp_lba = offset / 512; + tmp_lba_range = (offset + size - 1) / 512 - (tmp_lba) + 1; + + process_size = 0; // 這次 for 要處理的 size + remain_size = size; // 剩下的 size + curr_size = 0; // 目前已處理的 size + + tmp_buf = (char *)malloc(512); + for (idx = 0; idx < tmp_lba_range; idx++) + { + // TODO + // 如果沒有 free block 以及要移的 block 跟剩下的 page 相等時做 garbage collection + + if (free_block_number == 0) { + int del = -1; + unsigned int pages = 11; + for (int i = 0; i < 13; i++) { + if (i == curr_pca.fields.nand) continue; + if (valid_count[i] < pages) { + del = i; + pages = valid_count[i]; + } + } + if (del == -1 || pages == FREE_BLOCK) { + break; + } + if (9-curr_pca.fields.lba == pages) { + printf("-----gc\n"); + gc(); + } + + } + + + // read + ftl_read(tmp_buf, tmp_lba+idx); + + // modify + if (idx == 0) { + process_size = 512 - (offset % 512); + if (size < process_size) process_size = size; // size < 512 + memcpy((char *)tmp_buf+(offset % 512), (char *)buf+curr_size, process_size); + } + else if (idx == tmp_lba_range-1) { + process_size = remain_size; + memcpy((char *)tmp_buf, (char *)buf+curr_size, process_size); + } + else { + process_size = 512; + memcpy((char *)tmp_buf, (char *)buf+curr_size, process_size); + } + curr_size += process_size; + remain_size -= process_size; + //printf("[process] %d [curr] %d [remain] %d\n", process_size, curr_size, remain_size); + + // write + ftl_write(tmp_buf, 0, tmp_lba+idx); + } + free(tmp_buf); + return size; +} +``` + +### ftl_write + +```c +static int ftl_write(const char* buf, size_t lba_range, size_t lba) +{ + // TODO + // 如果不是 INVALID_PCA 表示此 logic page 已被寫過, + // 舊的 physical 要清除 P2L 紀錄 + if (L2P[lba] != INVALID_PCA) { + valid_count[L2P[lba]>>16]--; + P2L[(L2P[lba]>>16)*10+(L2P[lba]&0xffff)] = INVALID_LBA; + } + // 更新 L2P, P2L 表 + PCA_RULE temp; + temp.pca = get_next_pca(); + L2P[lba] = temp.pca; + P2L[temp.fields.nand*10 + temp.fields.lba] = lba; + // 呼叫 nand_write + nand_write(buf, L2P[lba]); +} +``` + +### ssd_do_read + +```c +static int ssd_do_read(char* buf, size_t size, off_t offset) +{ + int tmp_lba, tmp_lba_range, rst ; + char* tmp_buf; + + //off limit + if ((offset ) >= logic_size) + { + return 0; + } + if ( size > logic_size - offset) + { + //is valid data section + size = logic_size - offset; + } + + tmp_lba = offset / 512; + tmp_lba_range = (offset + size - 1) / 512 - (tmp_lba) + 1; + tmp_buf = (char *)calloc(tmp_lba_range * 512, sizeof(char)); + + for (int i = 0; i < tmp_lba_range; i++) { + // TODO + // 呼叫 ftl_read 依序寫進 tmp_buf + ftl_read(tmp_buf+(512*i), tmp_lba+i); + } + + memcpy(buf, tmp_buf + (offset % 512), size); + + + free(tmp_buf); + return size; +} +``` + +### ftl_read + +```c +static int ftl_read( char* buf, size_t lba) +{ + // TODO + // 呼叫 nand_read + nand_read(buf, L2P[lba]); +} +``` + +### garbage collection + +```c +static void gc() { + for (int i = 0; i < 13; i++) { + printf("%x ", valid_count[i]); + } + printf("\n"); + + // free 1 block + while(free_block_number < 1) { + + // 找移的 page 最少的 nand + int del = -1; + unsigned int pages = 11; + for (int i = 0; i < 13; i++) { + if (i == curr_pca.fields.nand) continue; + if (valid_count[i] < pages) { + del = i; + pages = valid_count[i]; + } + } + if (del == -1 || pages == FREE_BLOCK) { + break; + } + printf("[GC] %d ,%d pages\n", del, 10-pages); + PCA_RULE temp; + char *buf = (char *)malloc(512); + for (int i = 0; i < 10; i++) { + + // 不是 INVALID_LBA 的 page 要移動 + if (P2L[del*10+i] != INVALID_LBA) { + ftl_read(buf, P2L[del*10+i]); + ftl_write(buf, 0, P2L[del*10+i]); + P2L[del*10+i] = INVALID_LBA; + } + } + free(buf); + + // 都移動完了就 erase 該 nand + nand_erase(del); + } + + for (int i = 0; i < 13; i++) { + printf("%x ", valid_count[i]); + } + printf("\n"); + fflush(stdout); + return; +} +``` + +## 4.結果分析 + +附上 test.sh 以及各 test 的成績: +- test1: 1.000000 +- test2: 1.000000 +- test3: 1.000000 +- test4: 1.659645 +- test5: 1.000032 +- mytest: 1.334437 +- test7: 1.150820 + +```sh +#!/bin/bash + +SSD_FILE="/tmp/ssd/ssd_file" +GOLDEN="/tmp/ssd_file_golden" +TEMP="/tmp/temp" +touch ${GOLDEN} +truncate -s 0 ${SSD_FILE} +truncate -s 0 ${GOLDEN} + +rand(){ + min=$1 + max=$(($2-$min)) + num=$(cat /dev/urandom | head -n 10 | cksum | awk -F ' ' '{print $1}') + echo $(($num%$max)) +} + +case "$1" in + "test1") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + ;; + "test2") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + for i in $(seq 0 9) + do + dd if=${TEMP} iflag=skip_bytes skip=$(($i*1024)) of=${GOLDEN} oflag=seek_bytes seek=$(($i*5120)) bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=$(($i*1024)) of=${SSD_FILE} oflag=seek_bytes seek=$(($i*5120)) bs=1024 count=1 conv=notrunc 2> /dev/null + done + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${GOLDEN} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${SSD_FILE} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + ;; + "test3") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + for i in $(seq 0 49) + do + dd if=${TEMP} iflag=skip_bytes skip=$(($i*512)) of=${GOLDEN} oflag=seek_bytes seek=$(($i*1024)) bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=$(($i*512)) of=${SSD_FILE} oflag=seek_bytes seek=$(($i*1024)) bs=1024 count=1 conv=notrunc 2> /dev/null + done + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${GOLDEN} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${SSD_FILE} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + ;; + "test4") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 > ${TEMP} + for i in $(seq 0 400) + do + skip_b=$(shuf -i 10240-20480 -n 1) + seek_b=$(shuf -i 10240-20480 -n 1) + #echo $skip_b $seek_b + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${GOLDEN} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${SSD_FILE} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + # if [ ! -z "$(diff ${GOLDEN} ${SSD_FILE})" ]; then + # echo -1 + # exit 1 + # fi + done + ;; + "test5") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + dd if=${TEMP} iflag=skip_bytes skip=2 of=${GOLDEN} oflag=seek_bytes seek=0 bs=30720 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=2 of=${SSD_FILE} oflag=seek_bytes seek=0 bs=30720 count=1 conv=notrunc 2> /dev/null + ;; + "mytest") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 > ${TEMP} + skip_b=$(shuf -i 0-50176 -n 1) + seek_b=$(shuf -i 0-50176 -n 1) + for i in $(seq 0 100) + do + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${GOLDEN} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${SSD_FILE} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + done + ;; + "test7") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + for i in $(seq 0 1000) + do + dd if=${TEMP} skip=1024 of=${GOLDEN} iflag=skip_bytes oflag=seek_bytes seek=6789 bs=5000 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} skip=1024 of=${SSD_FILE} iflag=skip_bytes oflag=seek_bytes seek=6789 bs=5000 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} skip=2024 of=${GOLDEN} iflag=skip_bytes oflag=seek_bytes seek=123 bs=777 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} skip=2024 of=${SSD_FILE} iflag=skip_bytes oflag=seek_bytes seek=123 bs=777 count=1 conv=notrunc 2> /dev/null + done + ;; + *) + printf "Usage: sh test.sh test_pattern\n" + printf "\n" + printf "test_pattern\n" + printf "test1: Sequential write whole SSD size(51200bytes)\n" + printf " test basic SSD read & write\n" + printf "test2:\n" + printf " 1: Sequential write whole SSD size(51200bytes)\n" + printf " 2: Override 0, 1, 10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61, 70, 71, 80, 81, 90, 91 page \n" + printf " 2: Override 0, 1 page \n" + printf " test GC's result\n" + return + ;; +esac + +# check +diff ${GOLDEN} ${SSD_FILE} +if [ $? -eq 0 ] +then + echo "success!" +else + echo "fail!" +fi + +echo "WA:" +./ssd_fuse_dut /tmp/ssd/ssd_file W +rm -rf ${TEMP} ${GOLDEN} +``` diff --git a/finalproject/report.pdf b/finalproject/report.pdf new file mode 100644 index 000000000..573ca5333 Binary files /dev/null and b/finalproject/report.pdf differ diff --git a/finalproject/ssd_fuse b/finalproject/ssd_fuse new file mode 100644 index 000000000..8c16626e7 Binary files /dev/null and b/finalproject/ssd_fuse differ diff --git a/finalproject/ssd_fuse.c b/finalproject/ssd_fuse.c new file mode 100644 index 000000000..974e7ada8 --- /dev/null +++ b/finalproject/ssd_fuse.c @@ -0,0 +1,553 @@ +/* + FUSE ssd: FUSE ioctl example + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo + This program can be distributed under the terms of the GNU GPLv2. + See the file COPYING. +*/ +#define FUSE_USE_VERSION 35 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssd_fuse_header.h" +#define SSD_NAME "ssd_file" +enum +{ + SSD_NONE, + SSD_ROOT, + SSD_FILE, +}; + + +static size_t physic_size; +static size_t logic_size; +static size_t host_write_size; +static size_t nand_write_size; + +typedef union pca_rule PCA_RULE; +union pca_rule +{ + unsigned int pca; + struct + { + unsigned int lba : 16; + unsigned int nand: 16; + } fields; +}; + +PCA_RULE curr_pca; +static unsigned int get_next_pca(); + +unsigned int* L2P,* P2L,* valid_count, free_block_number; + +static int ssd_resize(size_t new_size) +{ + //set logic size to new_size + if (new_size > NAND_SIZE_KB * 1024) + { + return -ENOMEM; + } + else + { + logic_size = new_size; + return 0; + } + +} + +static int ssd_expand(size_t new_size) +{ + //logic must less logic limit + + if (new_size > logic_size) + { + return ssd_resize(new_size); + } + + return 0; +} + +static int nand_read(char* buf, int pca) +{ + char nand_name[100]; + FILE* fptr; + + PCA_RULE my_pca; + my_pca.pca = pca; + snprintf(nand_name, 100, "%s/nand_%d", NAND_LOCATION, my_pca.fields.nand); + + //read + if ( (fptr = fopen(nand_name, "r") )) + { + fseek( fptr, my_pca.fields.lba * 512, SEEK_SET ); + fread(buf, 1, 512, fptr); + fclose(fptr); + } + else + { + printf("open file fail at nand read pca = %d\n", pca); + return -EINVAL; + } + return 512; +} +static int nand_write(const char* buf, int pca) +{ + char nand_name[100]; + FILE* fptr; + + PCA_RULE my_pca; + my_pca.pca = pca; + snprintf(nand_name, 100, "%s/nand_%d", NAND_LOCATION, my_pca.fields.nand); + + //write + if ( (fptr = fopen(nand_name, "r+"))) + { + fseek( fptr, my_pca.fields.lba * 512, SEEK_SET ); + fwrite(buf, 1, 512, fptr); + fclose(fptr); + physic_size ++; + valid_count[my_pca.fields.nand]++; + } + else + { + printf("open file fail at nand (%s) write pca = %d, return %d\n", nand_name, pca, -EINVAL); + return -EINVAL; + } + + nand_write_size += 512; + return 512; +} + +static int nand_erase(int block_index) +{ + //printf("[NAND ERASE] %d\n", block_index); + char nand_name[100]; + FILE* fptr; + snprintf(nand_name, 100, "%s/nand_%d", NAND_LOCATION, block_index); + fptr = fopen(nand_name, "w"); + if (fptr == NULL) + { + printf("erase nand_%d fail", block_index); + return 0; + } + fclose(fptr); + valid_count[block_index] = FREE_BLOCK; + free_block_number++; + return 1; +} + +static unsigned int get_next_block() +{ + for (int i = 0; i < PHYSICAL_NAND_NUM; i++) + { + if (valid_count[(curr_pca.fields.nand + i) % PHYSICAL_NAND_NUM] == FREE_BLOCK) + { + curr_pca.fields.nand = (curr_pca.fields.nand + i) % PHYSICAL_NAND_NUM; + curr_pca.fields.lba = 0; + free_block_number--; + valid_count[curr_pca.fields.nand] = 0; + return curr_pca.pca; + } + } + return OUT_OF_BLOCK; +} +static unsigned int get_next_pca() +{ + if (curr_pca.pca == INVALID_PCA) + { + //init + curr_pca.pca = 0; + valid_count[0] = 0; + free_block_number--; + return curr_pca.pca; + } + + if(curr_pca.fields.lba == 9) + { + int temp = get_next_block(); + if (temp == OUT_OF_BLOCK) + { + return OUT_OF_BLOCK; + } + else if(temp == -EINVAL) + { + return -EINVAL; + } + else + { + return temp; + } + } + else + { + curr_pca.fields.lba += 1; + } + return curr_pca.pca; + +} + +//---------------------------------------------------------------- + +//---------------------------------------------------------------- + +static int ftl_read( char* buf, size_t lba) +{ + // TODO + // 呼叫 nand_read + nand_read(buf, L2P[lba]); + return 0; +} + +static int ftl_write(const char* buf, size_t lba_range, size_t lba) +{ + // TODO + // 如果不是 INVALID_PCA 表示此 logic page 已被寫過, + // 舊的 physical 要清除 P2L 紀錄 + if (L2P[lba] != INVALID_PCA) { + valid_count[L2P[lba]>>16]--; + P2L[(L2P[lba]>>16)*10+(L2P[lba]&0xffff)] = INVALID_LBA; + } + // 更新 L2P, P2L 表 + PCA_RULE temp; + temp.pca = get_next_pca(); + L2P[lba] = temp.pca; + P2L[temp.fields.nand*10 + temp.fields.lba] = lba; + // 呼叫 nand_write + nand_write(buf, L2P[lba]); + return 0; +} + +//---------------------------------------------------------------- +char gcbuf[512]; + +static void gc() { + // for (int i = 0; i < 13; i++) { + // printf("%x ", valid_count[i]); + // } + // printf("\n"); + + // free 1 block + int t = 1; + while(t-- > 0) { + //while(free_block_number < 1) { + + // 找移的 page 最少的 nand + int del = -1; + unsigned int pages = 11; + for (int i = 0; i < 13; i++) { + if (i == curr_pca.fields.nand) continue; + if (valid_count[i] < pages) { + del = i; + pages = valid_count[i]; + } + } + if (del == -1 || pages == FREE_BLOCK) { + break; + } + //printf("[GC] %d ,%d pages\n", del, 10-pages); + //char *buf = (char *)malloc(512); + for (int i = 0; i < 10; i++) { + + // 不是 INVALID_LBA 的 page 要移動 + if (P2L[del*10+i] != INVALID_LBA) { + ftl_read(gcbuf, P2L[del*10+i]); + ftl_write(gcbuf, 0, P2L[del*10+i]); + P2L[del*10+i] = INVALID_LBA; + } + } + //free(buf); + + // 都移動完了就 erase 該 nand + nand_erase(del); + } + + // for (int i = 0; i < 13; i++) { + // printf("%x ", valid_count[i]); + // } + // printf("\n"); + // fflush(stdout); + return; +} +//---------------------------------------------------------------- + +static int ssd_file_type(const char* path) +{ + if (strcmp(path, "/") == 0) + { + return SSD_ROOT; + } + if (strcmp(path, "/" SSD_NAME) == 0) + { + return SSD_FILE; + } + return SSD_NONE; +} +static int ssd_getattr(const char* path, struct stat* stbuf, + struct fuse_file_info* fi) +{ + (void) fi; + stbuf->st_uid = getuid(); + stbuf->st_gid = getgid(); + stbuf->st_atime = stbuf->st_mtime = time(NULL); + switch (ssd_file_type(path)) + { + case SSD_ROOT: + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + break; + case SSD_FILE: + stbuf->st_mode = S_IFREG | 0644; + stbuf->st_nlink = 1; + stbuf->st_size = logic_size; + break; + case SSD_NONE: + return -ENOENT; + } + return 0; +} +static int ssd_open(const char* path, struct fuse_file_info* fi) +{ + (void) fi; + if (ssd_file_type(path) != SSD_NONE) + { + return 0; + } + return -ENOENT; +} +static int ssd_do_read(char* buf, size_t size, off_t offset) +{ + int tmp_lba, tmp_lba_range, rst ; + char* tmp_buf; + + //off limit + if ((offset ) >= logic_size) + { + return 0; + } + if ( size > logic_size - offset) + { + //is valid data section + size = logic_size - offset; + } + + tmp_lba = offset / 512; + tmp_lba_range = (offset + size - 1) / 512 - (tmp_lba) + 1; + tmp_buf = (char *)calloc(tmp_lba_range * 512, sizeof(char)); + + for (int i = 0; i < tmp_lba_range; i++) { + // TODO + // 呼叫 ftl_read 依序寫進 tmp_buf + ftl_read(tmp_buf+(512*i), tmp_lba+i); + } + + memcpy(buf, tmp_buf + (offset % 512), size); + + + free(tmp_buf); + return size; +} +static int ssd_read(const char* path, char* buf, size_t size, + off_t offset, struct fuse_file_info* fi) +{ + (void) fi; + if (ssd_file_type(path) != SSD_FILE) + { + return -EINVAL; + } + return ssd_do_read(buf, size, offset); +} + +char tmp_buf[512]; +static int ssd_do_write(const char* buf, size_t size, off_t offset) +{ + int tmp_lba, tmp_lba_range, process_size; + int idx, curr_size, remain_size, rst; + //char* tmp_buf; + + host_write_size += size; + if (ssd_expand(offset + size) != 0) + { + return -ENOMEM; + } + + tmp_lba = offset / 512; + tmp_lba_range = (offset + size - 1) / 512 - (tmp_lba) + 1; + + process_size = 0; // 這次 for 要處理的 size + remain_size = size; // 剩下的 size + curr_size = 0; // 目前已處理的 size + + //tmp_buf = (char *)malloc(512); + for (idx = 0; idx < tmp_lba_range; idx++) + { + // TODO + // 如果沒有 free block 以及要移的 block 跟剩下的 page 相等時做 garbage collection + // if (free_block_number == 0) { + // printf("------\n"); + // gc(); + // } + if (free_block_number == 0) { + int del = -1; + unsigned int pages = 11; + for (int i = 0; i < 13; i++) { + if (i == curr_pca.fields.nand) continue; + if (valid_count[i] < pages) { + del = i; + pages = valid_count[i]; + } + } + if (del == -1 || pages == FREE_BLOCK) { + break; + } + if (9-curr_pca.fields.lba == pages) { + //printf("-----gc\n"); + gc(); + } + + } + + + // read + ftl_read(tmp_buf, tmp_lba+idx); + + // modify + if (idx == 0) { + process_size = 512 - (offset % 512); + if (size < process_size) process_size = size; // size < 512 + memcpy((char *)tmp_buf+(offset % 512), (char *)buf+curr_size, process_size); + } + else if (idx == tmp_lba_range-1) { + process_size = remain_size; + memcpy((char *)tmp_buf, (char *)buf+curr_size, process_size); + } + else { + process_size = 512; + memcpy((char *)tmp_buf, (char *)buf+curr_size, process_size); + } + curr_size += process_size; + remain_size -= process_size; + //printf("[process] %d [curr] %d [remain] %d\n", process_size, curr_size, remain_size); + + // write + ftl_write(tmp_buf, 0, tmp_lba+idx); + } + //free(tmp_buf); + return size; +} +static int ssd_write(const char* path, const char* buf, size_t size, + off_t offset, struct fuse_file_info* fi) +{ + + (void) fi; + if (ssd_file_type(path) != SSD_FILE) + { + return -EINVAL; + } + return ssd_do_write(buf, size, offset); +} +static int ssd_truncate(const char* path, off_t size, + struct fuse_file_info* fi) +{ + (void) fi; + memset(L2P, INVALID_PCA, sizeof(int) * LOGICAL_NAND_NUM * PAGE_PER_BLOCK); + memset(P2L, INVALID_LBA, sizeof(int) * PHYSICAL_NAND_NUM * PAGE_PER_BLOCK); + memset(valid_count, FREE_BLOCK, sizeof(int) * PHYSICAL_NAND_NUM); + curr_pca.pca = INVALID_PCA; + free_block_number = PHYSICAL_NAND_NUM; + if (ssd_file_type(path) != SSD_FILE) + { + return -EINVAL; + } + + return ssd_resize(size); +} +static int ssd_readdir(const char* path, void* buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info* fi, + enum fuse_readdir_flags flags) +{ + (void) fi; + (void) offset; + (void) flags; + if (ssd_file_type(path) != SSD_ROOT) + { + return -ENOENT; + } + filler(buf, ".", NULL, 0, (enum fuse_fill_dir_flags)0); + filler(buf, "..", NULL, 0, (enum fuse_fill_dir_flags)0); + filler(buf, SSD_NAME, NULL, 0, (enum fuse_fill_dir_flags)0); + return 0; +} +static int ssd_ioctl(const char* path, unsigned int cmd, void* arg, + struct fuse_file_info* fi, unsigned int flags, void* data) +{ + + if (ssd_file_type(path) != SSD_FILE) + { + return -EINVAL; + } + if (flags & FUSE_IOCTL_COMPAT) + { + return -ENOSYS; + } + switch (cmd) + { + case SSD_GET_LOGIC_SIZE: + *(size_t*)data = logic_size; + return 0; + case SSD_GET_PHYSIC_SIZE: + *(size_t*)data = physic_size; + return 0; + case SSD_GET_WA: + *(double*)data = (double)nand_write_size / (double)host_write_size; + return 0; + } + return -EINVAL; +} + +static const struct fuse_operations ssd_oper = +{ + .getattr = ssd_getattr, + .truncate = ssd_truncate, + .open = ssd_open, + .read = ssd_read, + .write = ssd_write, + .readdir = ssd_readdir, + .ioctl = ssd_ioctl, +}; + +int main(int argc, char* argv[]) +{ + int idx; + char nand_name[100]; + physic_size = 0; + logic_size = 0; + curr_pca.pca = INVALID_PCA; + free_block_number = PHYSICAL_NAND_NUM; + + L2P = (unsigned int *)malloc(LOGICAL_NAND_NUM * PAGE_PER_BLOCK * sizeof(int)); + memset(L2P, INVALID_PCA, sizeof(int) * LOGICAL_NAND_NUM * PAGE_PER_BLOCK); + P2L = (unsigned int *)malloc(PHYSICAL_NAND_NUM * PAGE_PER_BLOCK * sizeof(int)); + memset(P2L, INVALID_LBA, sizeof(int) * PHYSICAL_NAND_NUM * PAGE_PER_BLOCK); + valid_count = (unsigned int *)malloc(PHYSICAL_NAND_NUM * sizeof(int)); + memset(valid_count, FREE_BLOCK, sizeof(int) * PHYSICAL_NAND_NUM); + + //create nand file + for (idx = 0; idx < PHYSICAL_NAND_NUM; idx++) + { + FILE* fptr; + snprintf(nand_name, 100, "%s/nand_%d", NAND_LOCATION, idx); + fptr = fopen(nand_name, "w"); + if (fptr == NULL) + { + printf("open fail"); + } + fclose(fptr); + } + return fuse_main(argc, argv, &ssd_oper, NULL); +} diff --git a/finalproject/ssd_fuse_dut b/finalproject/ssd_fuse_dut new file mode 100644 index 000000000..28f3b971f Binary files /dev/null and b/finalproject/ssd_fuse_dut differ diff --git a/finalproject/ssd_fuse_dut.c b/finalproject/ssd_fuse_dut.c new file mode 100644 index 000000000..5e3591660 --- /dev/null +++ b/finalproject/ssd_fuse_dut.c @@ -0,0 +1,170 @@ +/* + FUSE ssdlient: FUSE ioctl example client + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo + This program can be distributed under the terms of the GNU GPLv2. + See the file COPYING. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssd_fuse_header.h" +#include +const char* usage = + "Usage: ssd_fuse SSD_FILE COMMAND\n" + "\n" + "COMMANDS\n" + " l : get logic size \n" + " p : get physical size \n" + " r SIZE [OFF] : read SIZE bytes @ OFF (dfl 0) and output to stdout\n" + " w SIZE [OFF] : write SIZE bytes @ OFF (dfl 0) from random\n" + " W : write amplification factor\n" + "\n"; +static int do_rw(FILE* fd, int is_read, size_t size, off_t offset) +{ + char* buf; + int idx; + ssize_t ret; + buf = calloc(1, size); + + if (!buf) + { + fprintf(stderr, "failed to allocated %zu bytes\n", size); + return -1; + } + if (is_read) + { + printf("dut do read size %ld, off %d\n", size, (int)offset); + fseek( fd, offset, SEEK_SET ); + ret = fread( buf, 1, size, fd); + if (ret >= 0) + { + fwrite(buf, 1, ret, stdout); + } + } + else + { + for ( idx = 0; idx < size; idx++) + { + buf[idx] = idx; + } + printf("dut do write size %ld, off %d\n", size, (int)offset); + fseek( fd, offset, SEEK_SET ); + printf("fseek \n"); + ret = fwrite(buf, 1, size, fd); + //arg.size = fread(arg.buf, 1, size, stdin); + fprintf(stderr, "Writing %zu bytes\n", size); + } + if (ret < 0) + { + perror("ioctl"); + } + + free(buf); + return ret; +} +int main(int argc, char** argv) +{ + size_t param[2] = { }; + size_t size; + char cmd; + char* path; + FILE* fptr; + int fd, i, rc; + + if (argc < 3) + { + goto usage; + } + path = argv[1]; + cmd = tolower(argv[2][0]); + cmd = argv[2][0]; + argc -= 3; + argv += 3; + for (i = 0; i < argc; i++) + { + char* endp; + param[i] = strtoul(argv[i], &endp, 0); + if (endp == argv[i] || *endp != '\0') + { + goto usage; + } + } + switch (cmd) + { + case 'l': + fd = open(path, O_RDWR); + if (fd < 0) + { + perror("open"); + return 1; + } + if (ioctl(fd, SSD_GET_LOGIC_SIZE, &size)) + { + perror("ioctl"); + goto error; + } + printf("%zu\n", size); + close(fd); + return 0; + case 'p': + fd = open(path, O_RDWR); + if (fd < 0) + { + perror("open"); + return 1; + } + if (ioctl(fd, SSD_GET_PHYSIC_SIZE, &size)) + { + perror("ioctl"); + goto error; + } + printf("%zu\n", size); + close(fd); + return 0; + case 'r': + case 'w': + if ( !(fptr = fopen(path, "r+"))) + { + perror("open"); + return 1; + } + rc = do_rw(fptr, cmd == 'r', param[0], param[1]); + if (rc < 0) + { + goto error; + } + fprintf(stderr, "transferred %d bytes \n", rc); + + fclose(fptr); + return 0; + case 'W': + fd = open(path, O_RDWR); + if (fd < 0) + { + perror("open"); + return 1; + } + double wa; + if (ioctl(fd, SSD_GET_WA, &wa)) + { + perror("ioctl"); + goto error; + } + printf("%f\n", wa); + close(fd); + return 0; + } +usage: + fprintf(stderr, "%s", usage); + return 1; +error: + + return 1; +} \ No newline at end of file diff --git a/finalproject/ssd_fuse_header.h b/finalproject/ssd_fuse_header.h new file mode 100644 index 000000000..083d2e849 --- /dev/null +++ b/finalproject/ssd_fuse_header.h @@ -0,0 +1,27 @@ +/* + FUSE-ioctl: ioctl support for FUSE + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo + This program can be distributed under the terms of the GNU GPLv2. + See the file COPYING. +*/ +#include +#include +#include +#define PHYSICAL_NAND_NUM (13) +#define LOGICAL_NAND_NUM (10) +#define NAND_SIZE_KB (50) +#define INVALID_PCA (0xFFFFFFFF) +#define INVALID_LBA (0xFFFFFFFF) +#define FREE_BLOCK (0xFFFFFFFF) +#define OUT_OF_BLOCK (0xFFFF) +#define FULL_PCA (0xFFFFFFFE) +#define PAGE_PER_BLOCK (10) +#define NAND_LOCATION "/mnt/c/Users/USER/Desktop/osdi/osc2022/finalproject" + +enum +{ + SSD_GET_LOGIC_SIZE = _IOR('E', 0, size_t), + SSD_GET_PHYSIC_SIZE = _IOR('E', 1, size_t), + SSD_GET_WA = _IOR('E', 2, size_t), +}; diff --git a/finalproject/ssd_fuse_lab.docx b/finalproject/ssd_fuse_lab.docx new file mode 100644 index 000000000..852071cbf Binary files /dev/null and b/finalproject/ssd_fuse_lab.docx differ diff --git a/finalproject/test.sh b/finalproject/test.sh new file mode 100644 index 000000000..eb0cc3e5c --- /dev/null +++ b/finalproject/test.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +SSD_FILE="/tmp/ssd/ssd_file" +GOLDEN="/tmp/ssd_file_golden" +TEMP="/tmp/temp" +touch ${GOLDEN} +truncate -s 0 ${SSD_FILE} +truncate -s 0 ${GOLDEN} + +rand(){ + min=$1 + max=$(($2-$min)) + num=$(cat /dev/urandom | head -n 10 | cksum | awk -F ' ' '{print $1}') + echo $(($num%$max)) +} + +case "$1" in + "test1") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + ;; + "test2") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + for i in $(seq 0 9) + do + dd if=${TEMP} iflag=skip_bytes skip=$(($i*1024)) of=${GOLDEN} oflag=seek_bytes seek=$(($i*5120)) bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=$(($i*1024)) of=${SSD_FILE} oflag=seek_bytes seek=$(($i*5120)) bs=1024 count=1 conv=notrunc 2> /dev/null + done + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${GOLDEN} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${SSD_FILE} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + ;; + "test3") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + for i in $(seq 0 49) + do + dd if=${TEMP} iflag=skip_bytes skip=$(($i*512)) of=${GOLDEN} oflag=seek_bytes seek=$(($i*1024)) bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=$(($i*512)) of=${SSD_FILE} oflag=seek_bytes seek=$(($i*1024)) bs=1024 count=1 conv=notrunc 2> /dev/null + done + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${GOLDEN} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=10240 of=${SSD_FILE} oflag=seek_bytes seek=0 bs=1024 count=1 conv=notrunc 2> /dev/null + ;; + "test4") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 > ${TEMP} + for i in $(seq 0 400) + do + skip_b=$(shuf -i 10240-20480 -n 1) + seek_b=$(shuf -i 10240-20480 -n 1) + #echo $skip_b $seek_b + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${GOLDEN} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${SSD_FILE} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + # if [ ! -z "$(diff ${GOLDEN} ${SSD_FILE})" ]; then + # echo -1 + # exit 1 + # fi + done + ;; + "test5") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + dd if=${TEMP} iflag=skip_bytes skip=2 of=${GOLDEN} oflag=seek_bytes seek=0 bs=30720 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=2 of=${SSD_FILE} oflag=seek_bytes seek=0 bs=30720 count=1 conv=notrunc 2> /dev/null + ;; + "mytest") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 > ${TEMP} + skip_b=$(shuf -i 0-50176 -n 1) + seek_b=$(shuf -i 0-50176 -n 1) + for i in $(seq 0 100) + do + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${GOLDEN} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} iflag=skip_bytes skip=${skip_b} of=${SSD_FILE} oflag=seek_bytes seek=${seek_b} bs=1024 count=1 conv=notrunc 2> /dev/null + done + ;; + "test7") + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 51200 | tee ${SSD_FILE} > ${GOLDEN} 2> /dev/null + cat /dev/urandom | tr -dc '[:alpha:][:digit:]' | head -c 11264 > ${TEMP} + for i in $(seq 0 1000) + do + dd if=${TEMP} skip=1024 of=${GOLDEN} iflag=skip_bytes oflag=seek_bytes seek=6789 bs=5000 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} skip=1024 of=${SSD_FILE} iflag=skip_bytes oflag=seek_bytes seek=6789 bs=5000 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} skip=2024 of=${GOLDEN} iflag=skip_bytes oflag=seek_bytes seek=123 bs=777 count=1 conv=notrunc 2> /dev/null + dd if=${TEMP} skip=2024 of=${SSD_FILE} iflag=skip_bytes oflag=seek_bytes seek=123 bs=777 count=1 conv=notrunc 2> /dev/null + done + ;; + *) + printf "Usage: sh test.sh test_pattern\n" + printf "\n" + printf "test_pattern\n" + printf "test1: Sequential write whole SSD size(51200bytes)\n" + printf " test basic SSD read & write\n" + printf "test2:\n" + printf " 1: Sequential write whole SSD size(51200bytes)\n" + printf " 2: Override 0, 1, 10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61, 70, 71, 80, 81, 90, 91 page \n" + printf " 2: Override 0, 1 page \n" + printf " test GC's result\n" + return + ;; +esac + +# check +diff ${GOLDEN} ${SSD_FILE} +if [ $? -eq 0 ] +then + echo "success!" +else + echo "fail!" +fi + +echo "WA:" +./ssd_fuse_dut /tmp/ssd/ssd_file W +rm -rf ${TEMP} ${GOLDEN} \ No newline at end of file diff --git a/finalproject/test_case b/finalproject/test_case new file mode 100644 index 000000000..805b28133 Binary files /dev/null and b/finalproject/test_case differ diff --git a/lab5/obj/exception_c.o b/lab5/obj/exception_c.o index 7a670f144..03c63d419 100644 Binary files a/lab5/obj/exception_c.o and b/lab5/obj/exception_c.o differ diff --git a/lab5/obj/freelist_c.o b/lab5/obj/freelist_c.o index 43f39357c..de18d23c3 100644 Binary files a/lab5/obj/freelist_c.o and b/lab5/obj/freelist_c.o differ diff --git a/lab5/obj/main_c.o b/lab5/obj/main_c.o index e7563e340..3fde698d1 100644 Binary files a/lab5/obj/main_c.o and b/lab5/obj/main_c.o differ diff --git a/lab5/obj/mbox_c.o b/lab5/obj/mbox_c.o index 63b2a3e79..95b25e244 100644 Binary files a/lab5/obj/mbox_c.o and b/lab5/obj/mbox_c.o differ diff --git a/lab5/obj/memory_c.o b/lab5/obj/memory_c.o index c182ffc52..ab92dc3d5 100644 Binary files a/lab5/obj/memory_c.o and b/lab5/obj/memory_c.o differ diff --git a/lab5/obj/printf_c.o b/lab5/obj/printf_c.o index d6ad6d170..3903e8ab4 100644 Binary files a/lab5/obj/printf_c.o and b/lab5/obj/printf_c.o differ diff --git a/lab5/obj/sched_c.o b/lab5/obj/sched_c.o index 1d1bab0ee..66c5838ec 100644 Binary files a/lab5/obj/sched_c.o and b/lab5/obj/sched_c.o differ diff --git a/lab5/obj/uart_c.o b/lab5/obj/uart_c.o index 0662e9893..f88c90f1c 100644 Binary files a/lab5/obj/uart_c.o and b/lab5/obj/uart_c.o differ diff --git a/lab5/obj/utils_c.o b/lab5/obj/utils_c.o index e0e1f20ee..62bed4ebb 100644 Binary files a/lab5/obj/utils_c.o and b/lab5/obj/utils_c.o differ diff --git a/lab6/Makefile b/lab6/Makefile new file mode 100644 index 000000000..edab984e9 --- /dev/null +++ b/lab6/Makefile @@ -0,0 +1,65 @@ +# +# Copyright (C) 2018 bzt (bztsrc@github) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# + +ARMGNU ?= aarch64-linux-gnu + +SRC_DIR = src +BUILD_DIR = obj + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +CFLAGS = -Wall -ffreestanding -nostdlib -nostartfiles -Iinclude -mgeneral-regs-only +ASMFLAGS = -Iinclude + +all: clean kernel8.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ -g + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +kernel8.img: $(OBJ_FILES) + aarch64-linux-gnu-ld -nostdlib -nostartfiles $(OBJ_FILES) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img + +clean: + rm kernel8.elf *.o >/dev/null 2>/dev/null || true + rm -rf $(BUILD_DIR)/* || true + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none + +cpio: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -initrd initramfs.cpio + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -S -s -initrd initramfs.cpio + diff --git a/lab6/include/exception.h b/lab6/include/exception.h new file mode 100644 index 000000000..71c837096 --- /dev/null +++ b/lab6/include/exception.h @@ -0,0 +1,43 @@ +#ifndef _EXCEPTION_H +#define _EXCEPTION_H + +#include "typedef.h" + +// save_all, load_all 的 register 用 struct 包起來,方便操作 +typedef struct Trapframe { + uint64_t x[31]; // general register from x0 ~ x30 + uint64_t sp_el0; + uint64_t elr_el1; + uint64_t spsr_el1; +} Trapframe; + +typedef 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]; +} cpio_newc_header; + +extern void enable_irq(); +extern void disable_irq(); +void sync_exception_router(uint64_t esr_el1, uint64_t elr_el1, Trapframe *trapframe); +void syscall(uint64_t syscall_num, Trapframe* trapframe); +void sys_getpid(Trapframe *trapframe); +void sys_uart_read(Trapframe *trapframe); +void sys_uart_write(Trapframe *trapframe); +void sys_exec(Trapframe *trapframe); +void sys_fork(Trapframe *trapframe); +void sys_exit(Trapframe *trapframe); +void sys_mbox_call(Trapframe *trapframe); +void sys_kill(Trapframe *trapframe); +#endif /* _EXCEPTION_H */ \ No newline at end of file diff --git a/lab6/include/freelist.h b/lab6/include/freelist.h new file mode 100644 index 000000000..665a1fd79 --- /dev/null +++ b/lab6/include/freelist.h @@ -0,0 +1,27 @@ +#ifndef FREELISTS_H +#define FREELISTS_H + +#define BELONG_LEFT -1 +#define ALLOCATED -2 +#define RESERVED -3 + +struct Node { + struct Node *next; + struct Node *prev; + int index; +}; + +struct Freelist { + struct Node *head; +}; + +typedef struct Node Node; +typedef struct Freelist Freelist; + +void freelist_init(Freelist *, Node *); +void freelist_push(Freelist *, Node *, int); +void freelist_remove(Freelist *, Node *, int); +void freelist_print(int, Freelist *); +void print_freelists(); + +#endif diff --git a/lab6/include/gpio.h b/lab6/include/gpio.h new file mode 100644 index 000000000..4640ac347 --- /dev/null +++ b/lab6/include/gpio.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ +#include "mmu.h" +#define MMIO_BASE (VA_START | 0x3F000000) + +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C)) diff --git a/lab6/include/mbox.h b/lab6/include/mbox.h new file mode 100644 index 000000000..87bf5fb8c --- /dev/null +++ b/lab6/include/mbox.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/* a properly aligned buffer */ +//extern volatile unsigned int mbox[36]; + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_LAST 0 + +int mboxc_mbox_call(unsigned char ch, unsigned int *mbox); +int get_board_revision(); +int get_arm_memory(); diff --git a/lab6/include/memory.h b/lab6/include/memory.h new file mode 100644 index 000000000..89676eaa1 --- /dev/null +++ b/lab6/include/memory.h @@ -0,0 +1,40 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#define MEMORY_BASE 0xFFFF000000000000 +#define CPIO_SIZE 248320 +#define PAGE_SIZE 0x1000UL // 4KB +#define MAX_PAGES 0x40000 // total 0x40000000 +#define LOG2_MAX_PAGES 18 +#define LOG2_MAX_PAGES_PLUS_1 19 +#define NULL 0 + + +#include "freelist.h" +#include "typedef.h" + +struct block_meta { + int size; + short free; + short pagetail; + struct block_meta *next; +}; + +struct blocklist { + struct block_meta *head; +}; + +typedef struct block_meta block_meta; +typedef struct blocklist blocklist; +#define BLOCK_SIZE (sizeof(block_meta)) + +void memory_init(void); +int find_allocate_list(Freelist *, int); +int allocate_page(Freelist *, Node *, int *, int, int); +void free_page(Freelist *, Node *, int *, int); +void *malloc(size_t); +void free(void *); +void reserve_memory(ulong start, ulong end); +void print_memory(); + +#endif // _MEMORY_H diff --git a/lab6/include/mmu.h b/lab6/include/mmu.h new file mode 100644 index 000000000..1174b54c4 --- /dev/null +++ b/lab6/include/mmu.h @@ -0,0 +1,62 @@ +#ifndef _MMU_H +#define _MMU_H + +//#define KERNEL_VIRT_BASE 0xFFFF000000000000 +#define KERNEL_VIRT_BASE 0x0 +// tcr +// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/TCR-EL1--Translation-Control-Register--EL1- +#define TCR_CONFIG_REGION_48bit (((64 - 48) << 0) | ((64 - 48) << 16)) +#define TCR_CONFIG_4KB ((0b00 << 14) | (0b10 << 30)) +#define TCR_CONFIG_DEFAULT (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB) + +// mair +// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/MAIR-EL1--Memory-Attribute-Indirection-Register--EL1- +#define MAIR_DEVICE_nGnRnE 0b00000000 +#define MAIR_NORMAL_NOCACHE 0b01000100 +#define MAIR_IDX_DEVICE_nGnRnE 0 +#define MAIR_IDX_NORMAL_NOCACHE 1 + +// identity paging +#define PD_TABLE 0b11 +#define PD_ENTRY 0b11 +#define PD_BLOCK 0b01 +#define PD_ACCESS (1 << 10) +#define PD_USER_ACCESS (0b01 << 6) +#define BOOT_PGD_ATTR PD_TABLE +#define BOOT_PUD_ATTR (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + +#define PGD_INIT (PD_ACCESS | PD_USER_ACCESS | (MAIR_IDX_NORMAL_NOCACHE << 2) | PD_BLOCK) +#define PTE_INIT (PD_ACCESS | PD_USER_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_ENTRY) + +#define VA_START 0xFFFF000000000000 +#define VA_MASK 0x0000FFFFFFFFFFFF +#define LOW_MEMORY 0x80000 +#define PD_MASK 0x1FFUL // 9 bit +#define PGF_MASK 0xFFFFFFFFF000 + +#define PAGE_MASK 0xfffffffffffff000 // va2phy_user +#define PGD_SHIFT (12 + 3*9) +#define PUD_SHIFT (12 + 2*9) +#define PMD_SHIFT (12 + 9) + +// user space constants +#define USER_PC 0x0000000000000000 +#define USER_SP 0x0000FFFFFFFFF000 +#define USER_STACK_LOW 0x0000FFFFFFFFB000 +#define USER_STACK_SIZE 0x4000 + +#ifndef __ASSEMBLY__ +#include "typedef.h" +// page table & entry +typedef unsigned long pte; +typedef unsigned long pagetable; +void mappages(pagetable *pg_table, uint64_t va, uint64_t size, uint64_t pa); +pte *walk(pagetable *pg_table, uint64_t va, int alloc); +void copypages(pagetable *parent, pagetable *child, int level); +uint64_t vir_to_phy(uint64_t vir); +uint64_t phy_to_vir(uint64_t phy); +uint64_t to_pfn(uint64_t addr); +unsigned long va2phy_user(unsigned long va); +#endif + +#endif \ No newline at end of file diff --git a/lab6/include/printf.h b/lab6/include/printf.h new file mode 100644 index 000000000..8f5deb26f --- /dev/null +++ b/lab6/include/printf.h @@ -0,0 +1,84 @@ +/* +File: printf.h +Copyright (C) 2004 Kustaa Nyholm +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +This library is really just two files: 'printf.h' and 'printf.c'. +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. +I've found them so usefull in debugging that I do not bother with a +debugger at all. +They are distributed in source form, so to use them, just compile them +into your project. +Two printf variants are provided: printf and sprintf. +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. +Zero padding and field width are also supported. +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. +To use the printf you need to supply your own character output function, +something like : + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } +Before you can call printf you need to initialize it to use your +character output function with something like: + init_printf(NULL,putc); +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. +For further details see source code. +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ +#define PRINTF_LONG_SUPPORT +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif \ No newline at end of file diff --git a/lab6/include/sched.h b/lab6/include/sched.h new file mode 100644 index 000000000..3883961c9 --- /dev/null +++ b/lab6/include/sched.h @@ -0,0 +1,60 @@ +#ifndef _SCHED_H +#define _SCHED_H +#include "typedef.h" + +extern char pg_dir; + +struct cpu_context { + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; + unsigned long lr; + unsigned long sp; +}; + +struct Thread { + struct cpu_context cpu_context; + int state; + int counter; + int priority; + int preempt_count; + int pid; + int status; + uint64_t kernel_sp; + uint64_t user_sp; + uint64_t pgd; +}; + +typedef struct Thread Thread; +#define INIT_TASK \ +/*cpu_context*/ { {0,0,0,0,0,0,0,0,0,0,0,0,0}, \ +/* state etc */ 0,0,1,0,0,0,0xffff000000080000,0,0 \ +} +#define NR_TASKS 64 +#define THREAD_SIZE 4096 + +#define TASK_RUNNING 0 +#define TASK_ZOMBIE 1 + +Thread *current_thread(); +int thread_create(void *func); +void preempt_disable(void); +void preempt_enable(void); +void _schedule(); +void schedule(); + +void idle(); +void timer_tick(); +void kill_zombies(); +void task_init(void); + + +#endif //_SCHED_H \ No newline at end of file diff --git a/lab6/include/sys.h b/lab6/include/sys.h new file mode 100644 index 000000000..612be215c --- /dev/null +++ b/lab6/include/sys.h @@ -0,0 +1,29 @@ +#ifndef _SYS_H +#define _SYS_H + +#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_MBOX_CALL 6 +#define SYS_KILL 7 + +#endif + +#ifndef __ASSEMBLY__ + +#include "typedef.h" + +/* Function in sys.S */ +extern int getpid(); +extern size_t uartread(char buf[], size_t size); +extern size_t uartwrite(const char buf[], size_t size); +extern int exec(const char *name, char *const argv[]); +extern int fork(); +extern void exit(int status); +extern int mbox_call(unsigned char ch, unsigned int *mbox); +extern void kill(int pid); + +#endif \ No newline at end of file diff --git a/lab6/include/typedef.h b/lab6/include/typedef.h new file mode 100644 index 000000000..ff8d6ad69 --- /dev/null +++ b/lab6/include/typedef.h @@ -0,0 +1,9 @@ +#ifndef _TYPEDEF_H +#define _TYPEDEF_H + +#define uint unsigned int +#define ulong unsigned long +#define uint64_t unsigned long +#define size_t unsigned long +//#define DEBUG 0 +#endif /* _TYPEDEF_H */ \ No newline at end of file diff --git a/lab6/include/uart.h b/lab6/include/uart.h new file mode 100644 index 000000000..7217f4bba --- /dev/null +++ b/lab6/include/uart.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +void uart_init(); +void uart_send(unsigned int c); +char uart_getc(); +char uart_getc_pure(); +void uart_int(int i); +void uart_uint(unsigned int i); +void uart_ulong(unsigned long i); +void uart_puts(char *s); +void uart_hex(unsigned int d); +void uart_hex_long(unsigned long d); +void putc(void *p, char c); diff --git a/lab6/include/utils.h b/lab6/include/utils.h new file mode 100644 index 000000000..d70bbff5b --- /dev/null +++ b/lab6/include/utils.h @@ -0,0 +1,14 @@ +#ifndef UTILS_H +#define UTILS_H + +#define DEBUG 0 +int strcmp(char *s1, char *s2); +int hex_to_int(char *p, int size); +void *get_user_program_address(); +int log2(int x); +int pow2(int x); +unsigned long cstr_to_ulong(char *s); +void* simple_malloc(void **now, int size); +void debug(char *, int); + +#endif \ No newline at end of file diff --git a/lab6/initramfs.cpio b/lab6/initramfs.cpio new file mode 100644 index 000000000..6ab461cbc Binary files /dev/null and b/lab6/initramfs.cpio differ diff --git a/lab6/kernel8.elf b/lab6/kernel8.elf new file mode 100644 index 000000000..6c7790739 Binary files /dev/null and b/lab6/kernel8.elf differ diff --git a/lab6/kernel8.img b/lab6/kernel8.img new file mode 100644 index 000000000..4a39845d7 Binary files /dev/null and b/lab6/kernel8.img differ diff --git a/lab6/linker.ld b/lab6/linker.ld new file mode 100644 index 000000000..d98b436a1 --- /dev/null +++ b/lab6/linker.ld @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +SECTIONS +{ + . = 0xffff000000000000; + . += 0x80000; + __kernel_start = .; + .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) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + . = ALIGN(0x1000); + pg_dir = .; + .data.pgd : { . += (3 * (1 << 12)); } + . = ALIGN(0x1000); + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/lab6/obj/exception_c.o b/lab6/obj/exception_c.o new file mode 100644 index 000000000..36e2f6771 Binary files /dev/null and b/lab6/obj/exception_c.o differ diff --git a/lab6/obj/freelist_c.o b/lab6/obj/freelist_c.o new file mode 100644 index 000000000..084920d9d Binary files /dev/null and b/lab6/obj/freelist_c.o differ diff --git a/lab6/obj/main_c.o b/lab6/obj/main_c.o new file mode 100644 index 000000000..4dbce443a Binary files /dev/null and b/lab6/obj/main_c.o differ diff --git a/lab6/obj/mbox_c.o b/lab6/obj/mbox_c.o new file mode 100644 index 000000000..52ce90885 Binary files /dev/null and b/lab6/obj/mbox_c.o differ diff --git a/lab6/obj/memory_c.o b/lab6/obj/memory_c.o new file mode 100644 index 000000000..466101cd3 Binary files /dev/null and b/lab6/obj/memory_c.o differ diff --git a/lab6/obj/mmu_c.o b/lab6/obj/mmu_c.o new file mode 100644 index 000000000..4c21f0b55 Binary files /dev/null and b/lab6/obj/mmu_c.o differ diff --git a/lab6/obj/mmu_s.o b/lab6/obj/mmu_s.o new file mode 100644 index 000000000..46e833b80 Binary files /dev/null and b/lab6/obj/mmu_s.o differ diff --git a/lab6/obj/printf_c.o b/lab6/obj/printf_c.o new file mode 100644 index 000000000..df11ff269 Binary files /dev/null and b/lab6/obj/printf_c.o differ diff --git a/lab6/obj/sched_c.o b/lab6/obj/sched_c.o new file mode 100644 index 000000000..7d80f3404 Binary files /dev/null and b/lab6/obj/sched_c.o differ diff --git a/lab6/obj/sched_s.o b/lab6/obj/sched_s.o new file mode 100644 index 000000000..4731b4026 Binary files /dev/null and b/lab6/obj/sched_s.o differ diff --git a/lab6/obj/start_s.o b/lab6/obj/start_s.o new file mode 100644 index 000000000..6392618c4 Binary files /dev/null and b/lab6/obj/start_s.o differ diff --git a/lab6/obj/sys_s.o b/lab6/obj/sys_s.o new file mode 100644 index 000000000..f1e1b9ee0 Binary files /dev/null and b/lab6/obj/sys_s.o differ diff --git a/lab6/obj/uart_c.o b/lab6/obj/uart_c.o new file mode 100644 index 000000000..f6086fa87 Binary files /dev/null and b/lab6/obj/uart_c.o differ diff --git a/lab6/obj/utils_c.o b/lab6/obj/utils_c.o new file mode 100644 index 000000000..ccf2fe7b4 Binary files /dev/null and b/lab6/obj/utils_c.o differ diff --git a/lab6/src/exception.c b/lab6/src/exception.c new file mode 100644 index 000000000..da14d9688 --- /dev/null +++ b/lab6/src/exception.c @@ -0,0 +1,207 @@ +// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1- + +#include "utils.h" +#include "exception.h" +#include "sys.h" +#include "printf.h" +#include "sched.h" +#include "memory.h" +#include "uart.h" +#include "mbox.h" +#include "mmu.h" + +extern void end_thread(void); +extern void ret_from_fork(void); +extern Thread *task[]; +extern void delay(int); +extern void update_pgd(uint64_t); + +void sync_exc_router(uint64_t esr_el1, uint64_t elr_el1, Trapframe *trapframe) { + int ec = (esr_el1 >> 26) & 0b111111; + int iss = esr_el1 & 0x1FFFFFF; + if (ec == 0b010101) { // is system call + uint64_t syscall_num = trapframe->x[8]; + //printf("[SYSCALL] %d\n", syscall_num); + syscall(syscall_num, trapframe); + } + else { + printf("Exception return address 0x%x\n", elr_el1); + printf("Exception class (EC) 0x%x\n", ec); + printf("Instruction specific syndrome (ISS) 0x%x\n", iss); + while(1){ + ; + } + } +} + +void syscall(uint64_t syscall_num, Trapframe* trapframe) { + switch (syscall_num) { + case SYS_GETPID: + sys_getpid(trapframe); + break; + + case SYS_UART_READ: + sys_uart_read(trapframe); + break; + + case SYS_UART_WRITE: + sys_uart_write(trapframe); + break; + + case SYS_EXEC: + sys_exec(trapframe); + break; + + case SYS_FORK: + sys_fork(trapframe); + break; + + case SYS_EXIT: + sys_exit(trapframe); + break; + + case SYS_MBOX_CALL: + sys_mbox_call(trapframe); + break; + case SYS_KILL: + sys_kill(trapframe); + break; + } + return; +} + +void sys_getpid(Trapframe *trapframe) { + trapframe->x[0] = current_thread()->pid; +} + +void sys_uart_read(Trapframe *trapframe) { + char *buf = (char *)trapframe->x[0]; + size_t size = (size_t)trapframe->x[1]; + enable_irq(); // 避免卡在 read 裡 + for (int i = 0; i < size; i++) { + *(buf + i) = uart_getc(); + } + disable_irq(); + trapframe->x[0] = size; +} + +void sys_uart_write(Trapframe *trapframe) { + const char *buf = (const char *)trapframe->x[0]; + size_t size = (size_t)trapframe->x[1]; + enable_irq(); + for (int i = 0; i < size; i++) { + uart_send(*(buf + i)); + } + disable_irq(); + trapframe->x[0] = size; +} + +void sys_exec(Trapframe *trapframe) { + preempt_disable(); + char *input = (char *)trapframe->x[0]; + char *program_pos; + cpio_newc_header *fs = (cpio_newc_header *)0xffff000008000000; + char *current = (char *)fs; + int name_size; + int file_size; + while (1) { + fs = (cpio_newc_header *)current; + name_size = hex_to_int(fs->c_namesize, 8); + file_size = hex_to_int(fs->c_filesize, 8); + current += 110; + if (strcmp(current, "TRAILER!!!") == 0) { + uart_puts("No such file!\n"); + break; + } + if (strcmp(current, input) == 0) { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + program_pos = (char *)current; + break; + } else { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + } + printf("program pos: %x\n", program_pos); + printf("file size: %d\n", file_size); + uint64_t new_program_va = (uint64_t)malloc(file_size); + uint64_t new_program_pa = vir_to_phy(new_program_va); + uint64_t user_stack_va = (uint64_t)malloc(USER_STACK_SIZE); + uint64_t user_stack_pa = vir_to_phy(user_stack_va); + mappages((pagetable *)current_thread()->pgd, USER_PC, file_size, new_program_pa); // map user program's code + mappages((pagetable *)current_thread()->pgd, USER_STACK_LOW, USER_STACK_SIZE, user_stack_pa); // map user program's stack + mappages((pagetable *)current_thread()->pgd, 0x3c000000, 0x3000000, 0x3c000000); // map user program's mbox + for (int i = 0; i < file_size; i++) { + *((char *)new_program_va+i) = *(program_pos+i); + } + asm volatile("msr sp_el0, %0" : : "r"(USER_SP)); + asm volatile("msr elr_el1, %0": : "r"(USER_PC)); + asm volatile("msr spsr_el1, %0" : : "r"(0x0)); + update_pgd(current_thread()->pgd); + preempt_enable(); + asm volatile("eret"); + trapframe->x[0] = 0; +} + +void sys_fork(Trapframe *trapframe) { + preempt_disable(); + Thread *parent = current_thread(); + /* + ret_from_fork 會把 child_trapframe load 到 register, + 這樣跑 child thread 時就會用到 child_trapframe 更改的 sp + */ + int newpid = thread_create(ret_from_fork); + + Thread *child = task[newpid]; + + printf("child pid: %x\n", newpid); + + // copy kernel stack (including trapframe) + uint64_t kstack_offset = (char *)parent->kernel_sp - (char *)trapframe; + for (uint64_t i = 1; i <= kstack_offset; i++) { + *((char *)(child->kernel_sp - i)) = *((char *)(parent->kernel_sp - i)); + } + child->cpu_context.sp = child->kernel_sp - kstack_offset; + + // copy all user program & stack + printf("Parent PGD: %x, User PGD: %x\n", parent->pgd, child->pgd); + if (parent->pgd != 0) copypages(phy_to_vir(parent->pgd), phy_to_vir(child->pgd), 0); + + Trapframe *child_trapframe = (Trapframe *)child->cpu_context.sp; + child_trapframe->sp_el0 = trapframe->sp_el0; + + trapframe->x[0] = child->pid; + child_trapframe->x[0] = 0; + preempt_enable(); +} + +void sys_exit(Trapframe *trapframe) { + current_thread()->status = trapframe->x[0]; + end_thread(); +} + +void sys_mbox_call(Trapframe *trapframe) { + unsigned char ch = (unsigned char)trapframe->x[0]; + unsigned int *mbox = (unsigned int *)trapframe->x[1]; + int ret = mboxc_mbox_call(ch, mbox); // defined in mbox.c + trapframe->x[0] = ret; +} + +void sys_kill(Trapframe *trapframe) { + int pid = trapframe->x[0]; + task[pid]->state = TASK_ZOMBIE; +} + +void timer_interrupt(int i) { + unsigned long cntfrq_el0; + asm volatile ("mrs %0, cntfrq_el0":"=r" (cntfrq_el0)); + asm volatile ("lsr %0, %0, #5":"=r" (cntfrq_el0) :"r"(cntfrq_el0)); // 1/32 second tick + asm volatile ("msr cntp_tval_el0, %0" : : "r"(cntfrq_el0)); + timer_tick(); +} diff --git a/lab6/src/freelist.c b/lab6/src/freelist.c new file mode 100644 index 000000000..87fcdfd3e --- /dev/null +++ b/lab6/src/freelist.c @@ -0,0 +1,70 @@ +#include "memory.h" +#include "freelist.h" +#include "uart.h" +#include "utils.h" +#include "printf.h" + +extern Freelist *heads; +extern int *frame_array; + +void freelist_push(Freelist *list, Node *nodes, int num) { + nodes[num].next = NULL; + nodes[num].prev = NULL; + if (!list->head) { + list->head = &nodes[num]; + return; + } + Node *node = list->head; + node->prev = &nodes[num]; + nodes[num].next = node; + list->head = &nodes[num]; + return; +} + +void freelist_remove(Freelist *list, Node *nodes, int num) { + Node *current = &nodes[num]; + Node *pre = current->prev; + // Remove the target by updating the head or the previous node. + if (pre==NULL) { + list->head = current->next; + if (current->next != NULL) { + current->next->prev = pre; + } + } + else { + if (current->next == NULL) { + pre->next = current->next; + } + else { + current->next->prev = pre; + pre->next = current->next; + } + } + current->prev = NULL; + current->next = NULL; +} + +void print_freelists() { + uart_puts("-----------Freelists------------\n"); + for(int i = LOG2_MAX_PAGES; i >= 0; i--) { + freelist_print(i, &heads[i]); + } + // uart_puts("-------------Pages--------------\n"); + // for (int j = 0; j < (MAX_PAGES/16); j++) { + // for (int i = 0; i < 16; i++) { + // uart_int(frame_array[16*j+i]); + // uart_puts(" "); + // } + // uart_puts("\n"); + // } +} +void freelist_print(int level, Freelist *list) { + uart_puts("Level "); + uart_int(level); + uart_puts(": "); + for (Node *node = list->head; node != NULL; node = node->next) { + uart_uint(node->index); + uart_puts(" "); + } + uart_puts("\n"); +} diff --git a/lab6/src/main.c b/lab6/src/main.c new file mode 100644 index 000000000..15db56a9c --- /dev/null +++ b/lab6/src/main.c @@ -0,0 +1,130 @@ +#include "uart.h" +#include "utils.h" +#include "freelist.h" +#include "memory.h" +#include "sched.h" +#include "printf.h" +#include "typedef.h" +#include "sys.h" +#include "mbox.h" +#define N 5 + +extern void delay(); +extern void core_timer_enable(); +extern void set_exception_vector_table(); + + +void cpu_timer_register_enable() { // 讓 el0 用 clock 不會 interrupt + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); +} + +void fork_test(){ + printf("\nFork Test, pid %d\n", getpid()); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + ++cnt; + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + delay(1000000); + ++cnt; + } + } + exit(0); + } + else { + unsigned int __attribute__((aligned(16))) mbox[36]; + get_board_revision(mbox); + mbox_call(MBOX_CH_PROP, mbox); + for (int i = 0; i < 8; i++) { + printf("mbox %d: %x\n", i, mbox[i]); + } + printf("parent here, pid %d, child %d\n", getpid(), ret); + } +} + +void foo(){ + for(int i = 0; i < 10; ++i) { + printf("Thread id: %d %d\n", getpid(), i); + delay(10000000); + schedule(); + } +} + +void main() { + //asm volatile ("ldr x0, =pg_dir"); + // asm volatile ("mov x0, #0"); + // asm volatile ("msr ttbr0_el1, x0"); + // asm volatile ("tlbi vmalle1is"); // invalidate all TLB entries + // asm volatile ("dsb ish"); // ensure completion of TLB invalidatation + // asm volatile ("isb"); // clear pipeline") + uart_init(); + init_printf(NULL, putc); + memory_init(); + task_init(); + core_timer_enable(); + cpu_timer_register_enable(); + set_exception_vector_table(); + // while (1) { + // char c = uart_getc(); + // uart_send(c); + // if (c == 's') break; + // } + printf("%s", "\nHello from Raspberry pi!\n"); + // for(int i = 0; i < N; ++i) { // N should > 2 + // thread_create(foo); + // } + // delay(1000000000); + // kill_zombies(); + // printf("PID: %d\n", getpid()); + // for(int i = 0; i < N*2; ++i) { // N should > 2 + // thread_create(foo); + // } + // fork_test(); + int ret; + if ((ret = fork()) == 0) + exec("vm.img", 0x0); + idle(); + /* + char input[1024]; + while (1) { + uart_send('\r'); + uart_puts("# "); + shell_input(input); + if (strcmp(input, "run") == 0) { + uart_puts("running...\n"); + } else if (strcmp(input, "m") == 0) { + shell_input(input); + size_t size = cstr_to_ulong(input); + void *ptr = malloc(size); + uart_puts("Allocation finished: "); + uart_hex_long((ulong)ptr); + uart_puts("\n"); + } else if (strcmp(input, "d") == 0) { + shell_input(input); + void *ptr = (void *)(ulong)hex_to_int(input, 8); + free(ptr); + uart_puts("Free finished: "); + uart_hex_long((ulong)ptr); + uart_puts("\n"); + } else if (strcmp(input, "pm") == 0) { + print_freelists(); + print_memory(); + } else { + uart_puts("Error input!\n"); + } + } + */ +} diff --git a/lab6/src/mbox.c b/lab6/src/mbox.c new file mode 100644 index 000000000..01a8df697 --- /dev/null +++ b/lab6/src/mbox.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "gpio.h" +#include "printf.h" +#include "mmu.h" + +/* mailbox message buffer */ +//volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +#define VIDEOCORE_MBOX (MMIO_BASE + 0x0000B880) +#define MBOX_READ ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x0)) +#define MBOX_POLL ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x10)) +#define MBOX_SENDER ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x14)) +#define MBOX_STATUS ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x18)) +#define MBOX_CONFIG ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x1C)) +#define MBOX_WRITE ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x20)) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 +#define MBOX_CH_PROP 8 +/* tags */ +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mboxc_mbox_call(unsigned char ch, unsigned int *mbox) { + unsigned long ka_mbox = va2phy_user((unsigned long)mbox) + ((unsigned long)mbox&0xFFF); + printf("mbox kernel addr location: 0x%x\n", ka_mbox); + /* 28 bits (MSB) for value, 4 bits for channel type */ + unsigned int r = (((unsigned int)((unsigned long)ka_mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + do { + asm volatile("nop"); + } while (*MBOX_STATUS & MBOX_FULL); + /* write the address of our message to the mailbox with channel identifier */ + *MBOX_WRITE = r; + /* now wait for the response */ + while (1) { + /* is there a response? */ + do { + asm volatile("nop"); + } while (*MBOX_STATUS & MBOX_EMPTY); + /* is it a response to our message? */ + if (r == *MBOX_READ) + /* is it a valid successful response? */ + return 1; + //return ((unsigned int *)phy_to_vir(ka_mbox))[1] == MBOX_RESPONSE; + } + return 0; +} + +int get_board_revision(unsigned int *mbox) { + mbox[0] = 7 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_BOARD_REVISION; // tag identifier + mbox[3] = 4; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // value buffer + // tags end + mbox[6] = END_TAG; + + return 0; + //return mbox_call(MBOX_CH_PROP, mbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + // printf("0x%x\n", mbox[5]); // it should be 0xa020d3 for rpi3 b+ +} + +int get_arm_memory(unsigned int *mbox) { + mbox[0] = 8 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_ARM_MEMORY; // tag identifier + mbox[3] = 8; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // value buffer + mbox[6] = 0; // value buffer + // tags end + mbox[7] = END_TAG; + + return 0; + //return mbox_call(MBOX_CH_PROP, mbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + // printf("0x%x\n", mbox[5]); // it should be 0xa020d3 for rpi3 b+ +} \ No newline at end of file diff --git a/lab6/src/memory.c b/lab6/src/memory.c new file mode 100644 index 000000000..9a1b0de08 --- /dev/null +++ b/lab6/src/memory.c @@ -0,0 +1,240 @@ +#include "memory.h" +#include "freelist.h" +#include "uart.h" +#include "utils.h" +#include "printf.h" + +Freelist *heads; +Node *nodes; +int *frame_array; +blocklist memory_blocks; +extern void memzero(void *, size_t); + +extern char _end; + +void memory_init() { + void *base = (void *)&_end; + heads = (Freelist *)simple_malloc(&base, (int)sizeof(Freelist)*LOG2_MAX_PAGES_PLUS_1); + nodes = (Node *)simple_malloc(&base, (int)sizeof(Node)*MAX_PAGES); + frame_array = (int *)simple_malloc(&base, (int)sizeof(int)*MAX_PAGES); + for (int i = 0; i < MAX_PAGES; i++) { + nodes[i].prev = NULL; + nodes[i].next = NULL; + nodes[i].index = i; + frame_array[i] = BELONG_LEFT; + } + frame_array[0] = LOG2_MAX_PAGES; + for (int i = 0; i < LOG2_MAX_PAGES; i++) { + heads[i].head = NULL; + } + heads[LOG2_MAX_PAGES].head = &nodes[0]; + + reserve_memory(MEMORY_BASE+0x0, MEMORY_BASE+(ulong)base); + reserve_memory(MEMORY_BASE+0x8000000, MEMORY_BASE+(0x8000000+CPIO_SIZE)); + reserve_memory(MEMORY_BASE+0x3c000000, MEMORY_BASE+0x40000000); + + memory_blocks.head = (block_meta *)malloc(PAGE_SIZE); + memory_blocks.head->next = NULL; + memory_blocks.head->size = 4096 - BLOCK_SIZE; + memory_blocks.head->free = 1; + memory_blocks.head->pagetail = 1; + +} + +int find_allocate_list(Freelist *heads, int needed_pages) { + for (int i = needed_pages; i <= LOG2_MAX_PAGES; i++) { + if (heads[i].head != NULL) { + return i; + } + } + return LOG2_MAX_PAGES; +} + +int allocate_page(Freelist *heads, Node *nodes, int *frames, int needed_level, int index) { + debug("Allocate page for level ", needed_level); + if (index >= 0) { + // reserve memory + int remove = index, push; + int use_level = needed_level; + while(frames[remove] != use_level) { + remove &= ~(pow2(use_level)); + use_level++; + } + debug("Remove", remove); + freelist_remove(&heads[use_level], nodes, remove); + for(int i = use_level-1; i >= needed_level; i--) { + remove ^= (index & pow2(i)); + push = remove ^ pow2(i); + debug("Push", push); + freelist_push(&heads[i], nodes, push); + frames[push] = i; + } + frames[index] = ALLOCATED; + return 0; // no need to return anything + } + else { + // normal page allocation + int use_level = find_allocate_list(heads, needed_level); + Node *fs = heads[use_level].head; + int front = fs->index; + debug("Remove from freelist", front); + freelist_remove(&heads[use_level], nodes, front); + for (int i = use_level-1; i >= needed_level; i--) { + int back = front | pow2(i); + debug("Push to freelist", back); + freelist_push(&heads[i], nodes, back); + frames[back] = i; + } + frames[front] = ALLOCATED; + return fs->index; + } +} + +void free_page(Freelist *heads, Node *nodes, int *frames, int free_index) { + debug("Freeing page", free_index); + frames[free_index] = BELONG_LEFT; + int level = 0; + int free_level = LOG2_MAX_PAGES; + while(frames[free_index ^ pow2(level)] == BELONG_LEFT) { // 如果非最左邊區塊,終會到達index=0, frames[0]不可能BELONG_LEFT, 所以會跳出迴圈 + free_index &= ~(pow2(level)); + level++; + } + for (int i = level; i < LOG2_MAX_PAGES; i++) { + int buddy = free_index ^ pow2(i); + if (frames[buddy] != i) { // not same level or allocated + free_level = i; + break; + } + frames[buddy] = BELONG_LEFT; + freelist_remove(&heads[i], nodes, buddy); + debug("Merged", buddy); + free_index &= ~(pow2(i)); + } + debug("Push back to freelist", free_index); + freelist_push(&heads[free_level], nodes, free_index); + frames[free_index] = free_level; + return; +} + +void *malloc(size_t size) { + if (size >= (PAGE_SIZE-BLOCK_SIZE)) { + //int need_pages = (size+PAGE_SIZE-1)/PAGE_SIZE; + int needed_order = log2((size+PAGE_SIZE-1)/PAGE_SIZE); + void *ptr = (void *)(MEMORY_BASE + allocate_page(heads, nodes, frame_array, needed_order, -1) * PAGE_SIZE); + //print_freelists(); + memzero(ptr, pow2(needed_order)*PAGE_SIZE); + //printf("[Malloc] %x\n", ptr); + return ptr; + } + else { + uart_puts("Dynamic allocation\n"); + block_meta *curr = memory_blocks.head; + size = (size & ~15) + 16; // align to 16 + /* find split block */ + while(1) { + if ((curr->free != (short)0) && (curr->size > size)) { + break; + } + if (curr->next == (block_meta *)NULL) { + /* allocate new page */ + block_meta *new_page = (block_meta *)malloc(PAGE_SIZE); + new_page->size = PAGE_SIZE-BLOCK_SIZE; + new_page->free = 1; + new_page->pagetail = 1; + new_page->next = NULL; + curr->next = new_page; + curr = curr->next; + break; + } + curr = curr->next; + } + + /* allocate memory */ + int left_size = curr->size - size; + block_meta *new_block = (block_meta *)((ulong)curr+BLOCK_SIZE+(ulong)size); + new_block->size = left_size; + new_block->free = 1; + new_block->pagetail = curr->pagetail; + new_block->next = curr->next; + curr->size = size; + curr->free = 0; + curr->pagetail = 0; + curr->next = new_block; + return (void *)((ulong)curr+BLOCK_SIZE); + + } +} + +void free(void *ptr) { + if ((ulong)ptr % PAGE_SIZE == 0) { + int free_index = (int)(((ulong)ptr-MEMORY_BASE+(PAGE_SIZE-1)) / 0x1000); + printf("Free page index %d\n", free_index); + free_page(heads, nodes, frame_array, free_index); + //print_freelists(); + } + else { + printf("[Free] %x\n", ptr); + block_meta *need_free = (block_meta *)((ulong)ptr-BLOCK_SIZE); + need_free->free = 1; + /* remove block */ + block_meta *curr = memory_blocks.head; + while(curr != NULL) { + if (curr->free) { + while(!curr->pagetail && (curr->next != NULL) && curr->next->free) { + curr->size += curr->next->size; + curr->pagetail = curr->next->pagetail; + curr->next = curr->next->next; + } + } + curr = curr->next; + } + } +} + +void reserve_memory(ulong start, ulong end) { + int index = (start-MEMORY_BASE) / PAGE_SIZE; + int pages = ((end+PAGE_SIZE-1)-start) / PAGE_SIZE; + uart_puts("Reserve "); + uart_int(pages); + uart_puts(" page(s)\n"); + for (int i = 0; i < LOG2_MAX_PAGES; i++) { + if (index & pow2(i)) { + allocate_page(heads, nodes, frame_array, i, index); + index += pow2(i); + pages -= pow2(i); + } + if (pages <= 0) break; + if (pow2(i) >= pages) { + allocate_page(heads, nodes, frame_array, log2(pages), index); + break; + } + if (pages <= 0) break; + } + //uart_puts("[Reserve memory] Finished.\n"); + //print_freelists(); +} + +void print_memory() { + block_meta *curr = memory_blocks.head; + while(curr != NULL) { + uart_puts("----------------\n"); + uart_puts("Address: "); + uart_hex((ulong)curr + BLOCK_SIZE); + uart_puts("\n"); + uart_puts("Size: "); + uart_int(curr->size); + uart_puts("\n"); + uart_puts("Free: "); + if (curr->free) + uart_puts("Yes\n"); + else + uart_puts("No\n"); + uart_puts("Last block in the page: "); + if (curr->pagetail) + uart_puts("Yes\n"); + else + uart_puts("No\n"); + + curr = curr->next; + } +} \ No newline at end of file diff --git a/lab6/src/mmu.S b/lab6/src/mmu.S new file mode 100644 index 000000000..a81466ed1 --- /dev/null +++ b/lab6/src/mmu.S @@ -0,0 +1,42 @@ +#define __ASSEMBLY__ +#include "mmu.h" + +.global set_tcr +set_tcr: + ldr x0, =TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + ret + +.global set_mair +set_mair: + ldr x0, =( \ + (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | \ + (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) \ + ) + msr mair_el1, x0 + ret + +.global create_page_table +create_page_table: + ldr x0, =pg_dir // PGD's page frame at 0x0 + and x0, x0, #VA_MASK // ttbr put physical address + add x1, x0, 0x1000 // PUD's page frame at 0x1000 + + ldr x2, =BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x0] + + ldr x2, =BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + msr ttbr1_el1, x0 // also load PGD to the upper translation based register. + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + ret \ No newline at end of file diff --git a/lab6/src/mmu.c b/lab6/src/mmu.c new file mode 100644 index 000000000..98925979b --- /dev/null +++ b/lab6/src/mmu.c @@ -0,0 +1,103 @@ +#include "mmu.h" +#include "typedef.h" +#include "memory.h" +#include "printf.h" +#include "uart.h" + +extern void memzero(void *, size_t); + +void mappages(pagetable *pg_table, uint64_t va, size_t size, uint64_t pa) { //user va 轉 pa + int pages = (size+(PAGE_SIZE-1)) / PAGE_SIZE; + for (int i = 0; i < pages; i++) { + // printf("Page: %d ", i); + pte *now_pte = walk(pg_table, va + PAGE_SIZE*i, 0); + *now_pte = (pa+PAGE_SIZE*i) | PTE_INIT; + // printf("PTE @:"); + // uart_hex_long(now_pte); + // printf("\n"); + // printf("Insert:"); + // uart_hex_long(*now_pte); + // printf("\n"); + } + return; + // walk() size/PAGE_SIZE次 +} + +pte *walk(pagetable *pg_table, uint64_t va, int alloc) { + int shift, index; + pg_table = (pagetable *)phy_to_vir((uint64_t)pg_table); + // printf("Start:"); + // uart_hex_long(pg_table); + // printf("\n"); + for (int level = 3; level > 0; level--) { //PGD, PUD, PMD + shift = 12 + 9 * level; + index = (va & (PD_MASK << shift)) >> shift; + // printf("%d\n", index); + if (pg_table[index] % 4 == 3) { // is entry + pg_table = (pagetable *)to_pfn(phy_to_vir(pg_table[index])); + // printf("PG_TABLE: "); + // uart_hex_long(pg_table); + // printf("\n"); + } else { + pg_table[index] = (to_pfn(vir_to_phy((uint64_t)malloc(PAGE_SIZE))) | PD_TABLE); + pg_table = (pagetable *)(to_pfn(phy_to_vir(pg_table[index]))); + // printf("PG_TABLE: "); + // uart_hex_long(pg_table); + // printf("\n"); + } + } + // PTE + shift = 12; + index = (va & (PD_MASK << shift)) >> shift; + return ((pte *)((uint64_t)pg_table + index*8)); +} + +void copypages(pagetable *parent, pagetable *child, int level) { + if (level == 3) { + for (int i = 0; i < 512; i++) { + if (phy_to_vir(parent[i])) { + pte *parent_page = (pte *)(phy_to_vir(to_pfn(parent[i]))); + child[i] = (vir_to_phy((uint64_t)malloc(PAGE_SIZE)) | PTE_INIT); + pte *child_page = (pte *)(phy_to_vir(to_pfn(child[i]))); + for (int s = 0; s < PAGE_SIZE; s++) { + ((char *)child_page)[s] = ((char *)parent_page)[s]; + } + } + } + return; + } + printf("level: %d\n", level); + for (int i = 0; i < 512; i++) { + if (parent[i]) { + pagetable *parent_next_level = (pagetable *)(phy_to_vir(to_pfn(parent[i]))); + child[i] = (vir_to_phy((uint64_t)malloc(PAGE_SIZE)) | PD_TABLE); + pagetable *child_next_level = (pagetable *)(phy_to_vir(to_pfn(child[i]))); + copypages(parent_next_level, child_next_level, level+1); + } + } +} + + +uint64_t vir_to_phy(uint64_t vir) { + return (vir & VA_MASK); +} + +uint64_t phy_to_vir(uint64_t phy) { // for kernel address + return (phy | VA_START); +} + +uint64_t to_pfn(uint64_t addr) { // to page frame + return ((addr >> 12) << 12); +} + +unsigned long va2phy_user(unsigned long va) { // user + printf("translate va: 0x%x\n", va); + unsigned long pgd; + asm volatile("mrs %0, ttbr0_el1\n\t": "=r" (pgd) :: "memory"); + unsigned long pud = ((unsigned long*)(pgd+VA_START))[va>>PGD_SHIFT&(512-1)]&PAGE_MASK; + unsigned long pmd = ((unsigned long*)(pud+VA_START))[va>>PUD_SHIFT&(512-1)]&PAGE_MASK; + unsigned long pte = ((unsigned long*)(pmd+VA_START))[va>>PMD_SHIFT&(512-1)]&PAGE_MASK; + unsigned long phys = ((unsigned long*)(pte+VA_START))[va>>12&(512-1)]&PAGE_MASK; + printf("0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", pgd, pud, pmd, pte, phys); + return phys; +} \ No newline at end of file diff --git a/lab6/src/printf.c b/lab6/src/printf.c new file mode 100644 index 000000000..dd020d062 --- /dev/null +++ b/lab6/src/printf.c @@ -0,0 +1,228 @@ +/* +File: printf.c +Copyright (C) 2004 Kustaa Nyholm +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "printf.h" + +typedef void (*putcf) (void*,char); +static putcf stdout_putf; +static void* stdout_putp; + + +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%=d; + d/=base; + if (n || dgt>0|| d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void li2a (long num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + uli2a(num,10,0,bf); + } + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%= d; + d/=base; + if (n || dgt>0 || d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void i2a (int num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + ui2a(num,10,0,bf); + } + +static int a2d(char ch) + { + if (ch>='0' && ch<='9') + return ch-'0'; + else if (ch>='a' && ch<='f') + return ch-'a'+10; + else if (ch>='A' && ch<='F') + return ch-'A'+10; + else return -1; + } + +static char a2i(char ch, char** src,int base,int* nump) + { + char* p= *src; + int num=0; + int digit; + while ((digit=a2d(ch))>=0) { + if (digit>base) break; + num=num*base+digit; + ch=*p++; + } + *src=p; + *nump=num; + return ch; + } + +static void putchw(void* putp,putcf putf,int n, char z, char* bf) + { + char fc=z? '0' : ' '; + char ch; + char* p=bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp,fc); + while ((ch= *bf++)) + putf(putp,ch); + } + +void tfp_format(void* putp,putcf putf,char *fmt, va_list va) + { + char bf[12]; + + char ch; + + + while ((ch=*(fmt++))) { + if (ch!='%') + putf(putp,ch); + else { + char lz=0; +#ifdef PRINTF_LONG_SUPPORT + char lng=0; +#endif + int w=0; + ch=*(fmt++); + if (ch=='0') { + ch=*(fmt++); + lz=1; + } + if (ch>='0' && ch<='9') { + ch=a2i(ch,&fmt,10,&w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch=='l') { + ch=*(fmt++); + lng=1; + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),10,0,bf); + else +#endif + ui2a(va_arg(va, unsigned int),10,0,bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'd' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int),bf); + else +#endif + i2a(va_arg(va, int),bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'x': case 'X' : +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf); + else +#endif + ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf); + putchw(putp,putf,w,lz,bf); + break; + case 'c' : + putf(putp,(char)(va_arg(va, int))); + break; + case 's' : + putchw(putp,putf,w,0,va_arg(va, char*)); + break; + case '%' : + putf(putp,ch); + default: + break; + } + } + } + abort:; + } + + +void init_printf(void* putp,void (*putf) (void*,char)) + { + stdout_putf=putf; + stdout_putp=putp; + } + +void tfp_printf(char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(stdout_putp,stdout_putf,fmt,va); + va_end(va); + } + +static void putcp(void* p,char c) + { + *(*((char**)p))++ = c; + } + + + +void tfp_sprintf(char* s,char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(&s,putcp,fmt,va); + putcp(&s,0); + va_end(va); + } \ No newline at end of file diff --git a/lab6/src/sched.S b/lab6/src/sched.S new file mode 100644 index 000000000..0fd500176 --- /dev/null +++ b/lab6/src/sched.S @@ -0,0 +1,31 @@ + +.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 update_pgd +update_pgd: + dsb ish // ensure write has completed + msr ttbr0_el1, x0 // switch translation based address. + tlbi vmalle1is // invalidate all TLB entries + dsb ish // ensure completion of TLB invalidatation + isb // clear pipeline + ret \ No newline at end of file diff --git a/lab6/src/sched.c b/lab6/src/sched.c new file mode 100644 index 000000000..b31e8291b --- /dev/null +++ b/lab6/src/sched.c @@ -0,0 +1,159 @@ +#include "sched.h" +#include "memory.h" +#include "utils.h" +#include "printf.h" +#include "mmu.h" +#include "typedef.h" +#include "uart.h" + +static Thread init_task = INIT_TASK; +//Thread *current_thread = &(init_task); +Thread *task[NR_TASKS] = {&(init_task), }; +int nr_tasks = 1; +extern char pg_dir; + + +extern void run_thread(void); +extern Thread *get_current(void); +extern void enable_irq(); +extern void disable_irq(); +extern void switch_to(void *, void *); +extern void update_pgd(uint64_t); + + +int get_new_pid() { + Thread* p; + for (int i = 0; i < NR_TASKS; i++) { + p = task[i]; + if (p == NULL) { + return i; + } + } + return -1; +} + +Thread* current_thread() { + Thread *cur = get_current(); + if (!cur) + return &init_task; + return cur; +} + +void preempt_disable(void) { + current_thread()->preempt_count++; +} + +void preempt_enable(void) { + current_thread()->preempt_count--; +} + +int thread_create(void *func) { + Thread *p = malloc(sizeof(Thread)); + printf("thread_create %x\n", p); + p->priority = 1; + p->state = TASK_RUNNING; + p->counter = p->priority; + p->status = 0; + p->preempt_count = 1; //disable preemtion until schedule_tail + + p->cpu_context.x19 = (ulong)func; + p->cpu_context.lr = (ulong)run_thread; + //p->cpu_context.sp = (ulong)p + THREAD_SIZE - 16; + p->kernel_sp = (ulong)malloc(PAGE_SIZE) + PAGE_SIZE - 16; + p->user_sp = (ulong)malloc(PAGE_SIZE) + PAGE_SIZE - 16; + p->pgd = vir_to_phy((uint64_t)malloc(PAGE_SIZE)); + // uart_hex_long(p->pgd); + p->cpu_context.sp = p->kernel_sp; // kernel space + + int pid = get_new_pid(); + task[pid] = p; + p->pid = pid; + preempt_enable(); + return pid; +} + +void _schedule() { + preempt_disable(); + int next, c; + struct Thread* p; + while (1) { + c = -1; + next = 0; + for (int i = 0; i < NR_TASKS; i++) { // pick biggest c value + p = task[i]; + if (p && p->state == TASK_RUNNING && p->counter > c) { + c = p->counter; + next = i; + } + } + if (c) { + break; + } + for (int i = 0; i < NR_TASKS; i++) { + p = task[i]; + if (p) { + p->counter = (p->counter >> 1) + p->priority; + } + } + } + //debug("now thread", current_thread()); + //debug("next thread", task[next]); + if (current_thread() != task[next]) { + printf("[scheduler] next pid: %d\n", next); + Thread *prev = current_thread(); + //current_thread = task[next]; + update_pgd(task[next]->pgd); + switch_to(prev, task[next]); + } + preempt_enable(); +} + +void schedule() { + current_thread()->counter = 0; + _schedule(); +} + +void kill_zombies() { + Thread* p; + for (int i = 1; i < NR_TASKS; i++) { // pick biggest c value + p = task[i]; + if (p && p->state == TASK_ZOMBIE) { + free((void *)(p->kernel_sp & ~(PAGE_SIZE-1))); + free((void *)(p->user_sp & ~(PAGE_SIZE-1))); + free(p); + task[i] = NULL; + } + } +} + +void idle() { + while(1) { + kill_zombies(); // reclaim threads marked as DEAD + schedule(); // switch to any other runnable thread + } +} + + +void timer_tick() { + current_thread()->counter--; + if (current_thread()->counter>0 || current_thread()->preempt_count > 0) { + return; + } + current_thread()->counter=0; + enable_irq(); + schedule(); + disable_irq(); +} + +void end_thread(void) { + current_thread()->state = TASK_ZOMBIE; + schedule(); +} + +void task_init(void) { + //asm volatile ("mrs %0, sp_el1":"=r"(p->kernel_sp)); + uint64_t init_task_addr = (uint64_t)&init_task; + asm volatile ("msr tpidr_el1, %0"::"r"(init_task_addr)); + uint64_t sp_el0 = 0; + asm volatile ("msr sp_el0, %0"::"r"(sp_el0)); +} \ No newline at end of file diff --git a/lab6/src/start.S b/lab6/src/start.S new file mode 100644 index 000000000..6a50319a0 --- /dev/null +++ b/lab6/src/start.S @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ +#define __ASSEMBLY__ +#include "mmu.h" + +.section ".text.boot" + +.global _start + +_start: + bl from_el2_to_el1 + + // setup stack + adr x1, _start + mov sp, x1 + + bl set_tcr + bl set_mair + bl create_page_table + + // clear bss + adr x0, __bss_start + ldr w1, =__bss_size + bl memzero + + // mov sp to virtual address + ldr x1, =VA_START + add sp, sp, x1 + + // jump to C code, should not return + ldr x0, =main + br x0 + // for failsafe, halt this core too +1: wfe + b 1b + +.global memzero +memzero: + str xzr, [x0], #8 + sub w1, w1, #1 + cbnz w1, memzero + ret + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 + mov x0, 0x5 // EL1h (SPSel = 1) with interrupt enabled + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 + +.globl from_el1_to_el0 +from_el1_to_el0: + mov x0, 0 + msr spsr_el1, x0 + msr elr_el1, lr + mov x0, 0x60000 // user space stack + msr sp_el0, x0 + eret // return to EL0 + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + 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 x21, sp_el0 + mrs x22, elr_el1 + mrs x23, spsr_el1 + + stp x30, x21, [sp, #16 * 15] + stp x22, x23, [sp, #16 * 16] +.endm + +// load general registers from stack +.macro load_all + ldp x22, x23, [sp, #16 * 16] + ldp x30, x21, [sp, #16 * 15] + + msr sp_el0, x21 + msr elr_el1, x22 + msr spsr_el1, x23 + + 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] + add sp, sp, 32 * 9 +.endm + +exception_handler_loop: + b exception_handler_loop + +exception_handler: + save_all + bl exception_handler_loop + load_all + eret + +sync_exception_handler: + save_all + mrs x0, esr_el1 // to decide is syscall or not + mrs x1, elr_el1 // the address return to + mov x2, sp // trapframe + bl sync_exc_router + load_all + eret + +irq_exception_handler_low: + save_all + mov x0, #0 + bl timer_interrupt + load_all + eret + +irq_exception_handler: + save_all + mov x0, #1 + bl timer_interrupt + load_all + eret + +exception_handler_lower_irq: + save_all + bl timer_interrupt + load_all + eret + +.align 11 // vector table should be aligned to 0x800 +.global exception_vector_table +exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b sync_exception_handler + .align 7 + b irq_exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b sync_exception_handler + .align 7 + b irq_exception_handler_low + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +.global set_exception_vector_table +set_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + ret + +.equ CORE0_TIMER_IRQ_CTRL, 0xffff000040000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mrs x0, cntfrq_el0 + lsr x0, x0, #5 + msr cntp_tval_el0, x0 // set expired time + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret + + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl enable_irq +enable_irq: + msr DAIFClr, #2 // IRQ mask bit + ret + +.globl disable_irq +disable_irq: + msr DAIFSet, #2 + ret + +.globl run_thread +run_thread: + bl preempt_enable + blr x19 //should never return + bl end_thread + +.globl ret_from_fork +ret_from_fork: + load_all + eret diff --git a/lab6/src/sys.S b/lab6/src/sys.S new file mode 100644 index 000000000..ea8bab53b --- /dev/null +++ b/lab6/src/sys.S @@ -0,0 +1,50 @@ +#define __ASSEMBLY__ +#include "sys.h" + +.global getpid +getpid: + mov x8, SYS_GETPID + svc #0 + ret + +.global uartread +uartread: + mov x8, SYS_UART_READ + svc #0 + ret + +.global uartwrite +uartwrite: + mov x8, SYS_UART_WRITE + svc #0 + ret + +.global exec +exec: + mov x8, SYS_EXEC + svc #0 + ret // shouldn't go here + +.global fork +fork: + mov x8, SYS_FORK + svc #0 + ret + +.global exit +exit: + mov x8, SYS_EXIT + svc #0 + ret + +.global mbox_call +mbox_call: + mov x8, SYS_MBOX_CALL + svc #0 + ret + +.global kill +kill: + mov x8, SYS_KILL + svc #0 + ret \ No newline at end of file diff --git a/lab6/src/uart.c b/lab6/src/uart.c new file mode 100644 index 000000000..770db014e --- /dev/null +++ b/lab6/src/uart.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "gpio.h" + +/* Auxilary mini UART registers */ +#define AUX_ENABLE ((volatile unsigned int *)(MMIO_BASE + 0x00215004)) +#define AUX_MU_IO ((volatile unsigned int *)(MMIO_BASE + 0x00215040)) +#define AUX_MU_IER ((volatile unsigned int *)(MMIO_BASE + 0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int *)(MMIO_BASE + 0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int *)(MMIO_BASE + 0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int *)(MMIO_BASE + 0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int *)(MMIO_BASE + 0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int *)(MMIO_BASE + 0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int *)(MMIO_BASE + 0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int *)(MMIO_BASE + 0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int *)(MMIO_BASE + 0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int *)(MMIO_BASE + 0x00215068)) + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() { + register unsigned int r; + + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) { + asm volatile("nop"); + } + /* initialize UART */ + *AUX_ENABLE |= 1; // enable UART1, AUX mini uart + *AUX_MU_IER = 0; + *AUX_MU_CNTL = 0; + *AUX_MU_LCR = 3; // 8 bits + *AUX_MU_MCR = 0; + *AUX_MU_IER = 0; + *AUX_MU_IIR = 0xc6; // disable interrupts + *AUX_MU_BAUD = 270; // 115200 baud + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx +} + +/** + * Send a character + */ +void uart_send(unsigned int c) { + /* wait until we can send */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); // transmitter idle + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +/** + * Receive a character + */ +char uart_getc() { + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // receiver overrun + /* read it and return */ + r = (char)(*AUX_MU_IO); + /* convert carrige return to newline */ + return r == '\r' ? '\n' : r; +} + +/** + * Receive a character without converting CR to LF + */ +char uart_getc_pure() { + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // receiver overrun + /* read it and return */ + r = (char)(*AUX_MU_IO); + return r; +} +/** + * Display an int + */ +void uart_int(int i) { + if (i < 0) { + uart_send('-'); + i = (~i) + 1; + } + char c[10]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} +/** + * Display an unsigned int + */ +void uart_uint(unsigned int i) { + char c[10]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} + +/** + * Display an unsigned long + */ +void uart_ulong(unsigned long i) { + char c[20]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} + +/** + * Display a string + */ +void uart_puts(char *s) { + while (*s) { + /* convert newline to carrige return + newline */ + if (*s == '\n') + uart_send('\r'); + uart_send(*s++); + } +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) { + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_hex_long(unsigned long d) { + unsigned long n; + int c; + for (c = 60; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +// This function is required by printf function +void putc(void *p, char c) { + if (c == '\n') + uart_send('\r'); + uart_send(c); +} \ No newline at end of file diff --git a/lab6/src/utils.c b/lab6/src/utils.c new file mode 100644 index 000000000..ed116776b --- /dev/null +++ b/lab6/src/utils.c @@ -0,0 +1,122 @@ +#include "uart.h" +#include "utils.h" + +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]; +}; + +void *get_user_program_address() { + struct cpio_newc_header *fs = (struct cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int is_file = 0; + while (1) { + fs = (struct cpio_newc_header *)current; + int name_size = hex_to_int(fs->c_namesize, 8); + int file_size = hex_to_int(fs->c_filesize, 8); + current += 110; // size of cpio_newc_header + if (strcmp(current, "user.img") == 0) + is_file = 1; + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + if (is_file) return (void *) current; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + +} + +int strcmp(char *s1, char *s2) { + while (*s1 != '\0' && *s1 == *s2) { + s1++; + s2++; + } + return (*(unsigned char *)s1) - (*(unsigned char *)s2); +} + +int hex_to_int(char *p, int len) { + int val = 0; + int temp; + for (int i = 0; i < len; i++) { + temp = *(p + i); + if (temp >= 'A') { + temp = temp - 'A' + 10; + } else + temp -= '0'; + val *= 16; + val += temp; + } + return val; +} + +void shell_input(char *input) { + int i = 0; + char temp; + while (1) { + temp = uart_getc(); + if (temp == '\n') { + uart_puts("\n"); + input[i] = '\0'; + break; + } else + uart_send(temp); + + input[i] = temp; + i++; + } +} + +void* simple_malloc(void **now, int size) { + void *ret = *now; + *now = *(char **)now + size; + return ret; +} + +int log2(int x) { + int ret = 0; + while(x != 1) { + if (x & 1) { + x += 1; + } + x >>= 1; + ret++; + } + return ret; +} + +int pow2(int x) { + return (1 << x); +} + +unsigned long cstr_to_ulong(char *s) { + unsigned long ret = 0; + while (*s != '\0') { + ret *= 10; + ret += (*s - '0'); + s++; + } + return ret; +} + + +void debug(char *s, int n) { + if (!DEBUG) return; + uart_puts(s); + uart_puts(": "); + uart_int(n); + uart_puts("\n"); +} \ No newline at end of file diff --git a/lab7/Makefile b/lab7/Makefile new file mode 100644 index 000000000..4a9505873 --- /dev/null +++ b/lab7/Makefile @@ -0,0 +1,65 @@ +# +# Copyright (C) 2018 bzt (bztsrc@github) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# + +ARMGNU ?= aarch64-linux-gnu + +SRC_DIR = src +BUILD_DIR = obj + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +CFLAGS = -Wall -ffreestanding -nostdlib -nostartfiles -Iinclude -mgeneral-regs-only +ASMFLAGS = -Iinclude + +all: clean kernel8.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +kernel8.img: $(OBJ_FILES) + aarch64-linux-gnu-ld -nostdlib -nostartfiles $(OBJ_FILES) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img + +clean: + rm kernel8.elf *.o >/dev/null 2>/dev/null || true + rm -rf $(BUILD_DIR)/* || true + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none + +cpio: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -S -s + diff --git a/lab7/include/exception.h b/lab7/include/exception.h new file mode 100644 index 000000000..d614f7434 --- /dev/null +++ b/lab7/include/exception.h @@ -0,0 +1,52 @@ +#ifndef _EXCEPTION_H +#define _EXCEPTION_H + +#include "typedef.h" + +// save_all, load_all 的 register 用 struct 包起來,方便操作 +typedef struct Trapframe { + uint64_t x[31]; // general register from x0 ~ x30 + uint64_t sp_el0; + uint64_t elr_el1; + uint64_t spsr_el1; +} Trapframe; + +typedef 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]; +} cpio_newc_header; + +extern void enable_irq(); +extern void disable_irq(); +void sync_exception_router(uint64_t esr_el1, uint64_t elr_el1, Trapframe *trapframe); +void syscall(uint64_t syscall_num, Trapframe* trapframe); +void sys_getpid(Trapframe *trapframe); +void sys_uart_read(Trapframe *trapframe); +void sys_uart_write(Trapframe *trapframe); +void sys_exec(Trapframe *trapframe); +void sys_fork(Trapframe *trapframe); +void sys_exit(Trapframe *trapframe); +void sys_mbox_call(Trapframe *trapframe); +void sys_kill(Trapframe *trapframe); +void sys_open(Trapframe *trapframe); +void sys_close(Trapframe *trapframe); +void sys_write(Trapframe *trapframe); +void sys_read(Trapframe *trapframe); +void sys_mkdir(Trapframe *trapframe); +void sys_mount(Trapframe *trapframe); +void sys_chdir(Trapframe *trapframe); + + +#endif /* _EXCEPTION_H */ \ No newline at end of file diff --git a/lab7/include/freelist.h b/lab7/include/freelist.h new file mode 100644 index 000000000..665a1fd79 --- /dev/null +++ b/lab7/include/freelist.h @@ -0,0 +1,27 @@ +#ifndef FREELISTS_H +#define FREELISTS_H + +#define BELONG_LEFT -1 +#define ALLOCATED -2 +#define RESERVED -3 + +struct Node { + struct Node *next; + struct Node *prev; + int index; +}; + +struct Freelist { + struct Node *head; +}; + +typedef struct Node Node; +typedef struct Freelist Freelist; + +void freelist_init(Freelist *, Node *); +void freelist_push(Freelist *, Node *, int); +void freelist_remove(Freelist *, Node *, int); +void freelist_print(int, Freelist *); +void print_freelists(); + +#endif diff --git a/lab7/include/gpio.h b/lab7/include/gpio.h new file mode 100644 index 000000000..52fa671d0 --- /dev/null +++ b/lab7/include/gpio.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C)) diff --git a/lab7/include/initramfs.h b/lab7/include/initramfs.h new file mode 100644 index 000000000..56f1699ba --- /dev/null +++ b/lab7/include/initramfs.h @@ -0,0 +1,25 @@ +#ifndef _INITRAMFS_H +#define _INITRAMFS_H + +#include "vfs.h" + +#define EOF (-1) + +struct initramfs_internal { + int type; + char *name; + struct vnode *vnode; + int size; + void *data; +}; + +int initramfs_register(); +int initramfs_setup_mount(struct filesystem* fs, struct mount* mount); +struct vnode* initramfs_create_vnode(struct initramfs_internal* initramfs_node); + +int initramfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name); +int initramfs_create(struct vnode* dir, struct vnode** target, const char* component_name); +int initramfs_write(struct file* file, const void* buf, size_t len); +int initramfs_read(struct file* file, void* buf, size_t len); +int initramfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name); +#endif \ No newline at end of file diff --git a/lab7/include/mbox.h b/lab7/include/mbox.h new file mode 100644 index 000000000..87bf5fb8c --- /dev/null +++ b/lab7/include/mbox.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/* a properly aligned buffer */ +//extern volatile unsigned int mbox[36]; + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_LAST 0 + +int mboxc_mbox_call(unsigned char ch, unsigned int *mbox); +int get_board_revision(); +int get_arm_memory(); diff --git a/lab7/include/memory.h b/lab7/include/memory.h new file mode 100644 index 000000000..1d327fef6 --- /dev/null +++ b/lab7/include/memory.h @@ -0,0 +1,37 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#define MEMORY_BASE 0x0 +#define CPIO_SIZE 247296 +#define PAGE_SIZE 0x1000 // 4KB +#define MAX_PAGES 0x40000 // total 0x40000000 +#define LOG2_MAX_PAGES 18 +#define LOG2_MAX_PAGES_PLUS_1 19 +#define NULL 0 +#include "freelist.h" +#include "typedef.h" + +struct block_meta { + int size; + short free; + short pagetail; + struct block_meta *next; +}; + +struct blocklist { + struct block_meta *head; +}; + +typedef struct block_meta block_meta; +typedef struct blocklist blocklist; +#define BLOCK_SIZE (sizeof(block_meta)) + +void memory_init(void); +int find_allocate_list(Freelist *, int); +int allocate_page(Freelist *, Node *, int *, int, int); +void free_page(Freelist *, Node *, int *, int); +void *malloc(size_t); +void free(void *); +void reserve_memory(ulong start, ulong end); +void print_memory(); +#endif diff --git a/lab7/include/printf.h b/lab7/include/printf.h new file mode 100644 index 000000000..7b53b8421 --- /dev/null +++ b/lab7/include/printf.h @@ -0,0 +1,84 @@ +/* +File: printf.h +Copyright (C) 2004 Kustaa Nyholm +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +This library is really just two files: 'printf.h' and 'printf.c'. +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. +I've found them so usefull in debugging that I do not bother with a +debugger at all. +They are distributed in source form, so to use them, just compile them +into your project. +Two printf variants are provided: printf and sprintf. +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. +Zero padding and field width are also supported. +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. +To use the printf you need to supply your own character output function, +something like : + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } +Before you can call printf you need to initialize it to use your +character output function with something like: + init_printf(NULL,putc); +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. +For further details see source code. +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif \ No newline at end of file diff --git a/lab7/include/sched.h b/lab7/include/sched.h new file mode 100644 index 000000000..eac3e053d --- /dev/null +++ b/lab7/include/sched.h @@ -0,0 +1,60 @@ +#ifndef _SCHED_H +#define _SCHED_H +#include "typedef.h" +#include "vfs.h" + +struct cpu_context { + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; + unsigned long lr; + unsigned long sp; +}; + +struct Thread { + struct cpu_context cpu_context; + int state; + int counter; + int priority; + int preempt_count; + int pid; + int status; + uint64_t kernel_sp; + uint64_t user_sp; + struct vnode *pwd; + struct file *fd_table[MAX_FD_NUM]; +}; + +typedef struct Thread Thread; +#define INIT_TASK \ +/*cpu_context*/ { {0,0,0,0,0,0,0,0,0,0,0,0,0}, \ +/* state etc */ 0,0,1,0,0,0,0x80000,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} \ +} +#define NR_TASKS 64 +#define THREAD_SIZE 4096 + +#define TASK_RUNNING 0 +#define TASK_ZOMBIE 1 + +Thread *current_thread(); +int thread_create(void *func); +void preempt_disable(void); +void preempt_enable(void); +void _schedule(); +void schedule(); + +void idle(); +void timer_tick(); +void kill_zombies(); +void task_init(void); + + +#endif //_SCHED_H \ No newline at end of file diff --git a/lab7/include/shell.h b/lab7/include/shell.h new file mode 100644 index 000000000..b89f34fb4 --- /dev/null +++ b/lab7/include/shell.h @@ -0,0 +1,2 @@ +void shell(void); +void shell_input(char *input); \ No newline at end of file diff --git a/lab7/include/sys.h b/lab7/include/sys.h new file mode 100644 index 000000000..eaad7f615 --- /dev/null +++ b/lab7/include/sys.h @@ -0,0 +1,62 @@ +#ifndef _SYS_H +#define _SYS_H + +#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_MBOX_CALL 6 +#define SYS_KILL 7 + +#define SYS_OPEN 11 +#define SYS_CLOSE 12 +#define SYS_WRITE 13 +#define SYS_READ 14 +#define SYS_MKDIR 15 +#define SYS_MOUNT 16 +#define SYS_CHDIR 17 + +#endif + +#ifndef __ASSEMBLY__ + +#include "typedef.h" + +/* Function in sys.S */ +extern int getpid(); +extern size_t uartread(char buf[], size_t size); +extern size_t uartwrite(const char buf[], size_t size); +extern int exec(const char *name, char *const argv[]); +extern int fork(); +extern void exit(int status); +extern int mbox_call(unsigned char ch, unsigned int *mbox); +extern void kill(int pid); + +// syscall number : 11 +extern int open(const char *pathname, int flags); + +// syscall number : 12 +extern int close(int fd); + +// syscall number : 13 +// remember to return read size or error code +extern long write(int fd, const void *buf, unsigned long count); + +// syscall number : 14 +// remember to return read size or error code +extern long read(int fd, void *buf, unsigned long count); + +// syscall number : 15 +// you can ignore mode, since there is no access control +extern int mkdir(const char *pathname, unsigned mode); + +// syscall number : 16 +// you can ignore arguments other than target (where to mount) and filesystem (fs name) +extern int mount(const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); + +// syscall number : 17 +extern int chdir(const char *path); + +#endif \ No newline at end of file diff --git a/lab7/include/tmpfs.h b/lab7/include/tmpfs.h new file mode 100644 index 000000000..d6211494d --- /dev/null +++ b/lab7/include/tmpfs.h @@ -0,0 +1,29 @@ +#ifndef _TMPFS_H +#define _TMPFS_H + +#include "vfs.h" + +#define EOF (-1) + +struct tmpfs_internal { + int type; + char name[MAX_COMPONENT_NAME_LEN]; + struct tmpfs_internal *parent; + struct tmpfs_internal *child[MAX_ENTRIES]; + struct vnode *vnode; + int size; + void *data; +}; + +extern struct mount *rootfs; + +int tmpfs_register(); +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount); +struct vnode* tmpfs_create_vnode(struct tmpfs_internal* tmpfs_node); + +int tmpfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name); +int tmpfs_create(struct vnode* dir, struct vnode** target, const char* component_name); +int tmpfs_write(struct file* file, const void* buf, size_t len); +int tmpfs_read(struct file* file, void* buf, size_t len); +int tmpfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name); +#endif \ No newline at end of file diff --git a/lab7/include/typedef.h b/lab7/include/typedef.h new file mode 100644 index 000000000..ff8d6ad69 --- /dev/null +++ b/lab7/include/typedef.h @@ -0,0 +1,9 @@ +#ifndef _TYPEDEF_H +#define _TYPEDEF_H + +#define uint unsigned int +#define ulong unsigned long +#define uint64_t unsigned long +#define size_t unsigned long +//#define DEBUG 0 +#endif /* _TYPEDEF_H */ \ No newline at end of file diff --git a/lab7/include/uart.h b/lab7/include/uart.h new file mode 100644 index 000000000..7217f4bba --- /dev/null +++ b/lab7/include/uart.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +void uart_init(); +void uart_send(unsigned int c); +char uart_getc(); +char uart_getc_pure(); +void uart_int(int i); +void uart_uint(unsigned int i); +void uart_ulong(unsigned long i); +void uart_puts(char *s); +void uart_hex(unsigned int d); +void uart_hex_long(unsigned long d); +void putc(void *p, char c); diff --git a/lab7/include/utils.h b/lab7/include/utils.h new file mode 100644 index 000000000..5bfe11bd2 --- /dev/null +++ b/lab7/include/utils.h @@ -0,0 +1,18 @@ +#ifndef UTILS_H +#define UTILS_H + +#define DEBUG 0 +int strcmp(const char *s1, const char *s2); +void strcpy(char *dest, const char *src); +int hex_to_int(char *p, int size); +void *get_user_program_address(); +int log2(int x); +int pow2(int x); +unsigned long cstr_to_ulong(char *s); +void* simple_malloc(void **now, int size); +void debug(char *, int); +unsigned long get_timestamp(); +void assert(int e); + +int strlen(char *str); +#endif \ No newline at end of file diff --git a/lab7/include/vfs.h b/lab7/include/vfs.h new file mode 100644 index 000000000..f4122a89a --- /dev/null +++ b/lab7/include/vfs.h @@ -0,0 +1,73 @@ +#ifndef _VFS_H +#define _VFS_H + +#include "typedef.h" + +#define O_CREAT 00000100 + +#define REGULAR_FILE 0 +#define DIRECTORY 1 + +#define MAX_PATHNAME_LEN 256 +#define MAX_FILESIZE 4096 +#define MAX_COMPONENT_NAME_LEN 16 // 15+'\0' +#define MAX_ENTRIES 16 +#define MAX_FD_NUM 16 + +struct mount { + struct vnode *root; + struct filesystem *fs; +}; + +struct vnode { + struct mount *mount; + struct vnode *mount_parent; + struct vnode_operations *v_ops; + struct file_operations *f_ops; + void *internal; +}; + +// file handle +struct file { + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +}; + +struct filesystem { + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +}; + +struct file_operations { + int (*write)(struct file *file, const void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + //long lseek64(struct file *file, long offset, int whence); +}; + +struct vnode_operations { + int (*lookup)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, + const char *component_name); +}; + +extern struct mount *rootfs; +void rootfs_init(void); +void mount_initramfs(void); +int register_filesystem(struct filesystem *fs); + +int vfs_open(const char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, const void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); +int vfs_mkdir(const char *pathname); +int vfs_chdir(const char *pathname); +int vfs_mount(const char *target, const char *filesystem); +int vfs_lookup(const char *pathname, struct vnode **target); +#endif diff --git a/lab7/initramfs.cpio b/lab7/initramfs.cpio new file mode 100644 index 000000000..7fc95c0a6 Binary files /dev/null and b/lab7/initramfs.cpio differ diff --git a/lab7/kernel8.elf b/lab7/kernel8.elf new file mode 100644 index 000000000..e68d04e5f Binary files /dev/null and b/lab7/kernel8.elf differ diff --git a/lab7/kernel8.img b/lab7/kernel8.img new file mode 100644 index 000000000..2ac3f38cb Binary files /dev/null and b/lab7/kernel8.img differ diff --git a/lab7/linker.ld b/lab7/linker.ld new file mode 100644 index 000000000..58657923b --- /dev/null +++ b/lab7/linker.ld @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +SECTIONS +{ + . = 0x80000; + .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) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + . = ALIGN(16); + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/lab7/obj/exception_c.o b/lab7/obj/exception_c.o new file mode 100644 index 000000000..857a8398d Binary files /dev/null and b/lab7/obj/exception_c.o differ diff --git a/lab7/obj/freelist_c.o b/lab7/obj/freelist_c.o new file mode 100644 index 000000000..de18d23c3 Binary files /dev/null and b/lab7/obj/freelist_c.o differ diff --git a/lab7/obj/initramfs_c.o b/lab7/obj/initramfs_c.o new file mode 100644 index 000000000..252605f8b Binary files /dev/null and b/lab7/obj/initramfs_c.o differ diff --git a/lab7/obj/main_c.o b/lab7/obj/main_c.o new file mode 100644 index 000000000..380988f77 Binary files /dev/null and b/lab7/obj/main_c.o differ diff --git a/lab7/obj/mbox_c.o b/lab7/obj/mbox_c.o new file mode 100644 index 000000000..95b25e244 Binary files /dev/null and b/lab7/obj/mbox_c.o differ diff --git a/lab7/obj/memory_c.o b/lab7/obj/memory_c.o new file mode 100644 index 000000000..8b0b68bab Binary files /dev/null and b/lab7/obj/memory_c.o differ diff --git a/lab7/obj/printf_c.o b/lab7/obj/printf_c.o new file mode 100644 index 000000000..6f104b605 Binary files /dev/null and b/lab7/obj/printf_c.o differ diff --git a/lab7/obj/sched_c.o b/lab7/obj/sched_c.o new file mode 100644 index 000000000..7970f91b9 Binary files /dev/null and b/lab7/obj/sched_c.o differ diff --git a/lab7/obj/shell_c.o b/lab7/obj/shell_c.o new file mode 100644 index 000000000..b135f0349 Binary files /dev/null and b/lab7/obj/shell_c.o differ diff --git a/lab7/obj/start_s.o b/lab7/obj/start_s.o new file mode 100644 index 000000000..13c5fbd98 Binary files /dev/null and b/lab7/obj/start_s.o differ diff --git a/lab7/obj/sys_s.o b/lab7/obj/sys_s.o new file mode 100644 index 000000000..2e3fb06e8 Binary files /dev/null and b/lab7/obj/sys_s.o differ diff --git a/lab7/obj/tmpfs_c.o b/lab7/obj/tmpfs_c.o new file mode 100644 index 000000000..b9d518630 Binary files /dev/null and b/lab7/obj/tmpfs_c.o differ diff --git a/lab7/obj/uart_c.o b/lab7/obj/uart_c.o new file mode 100644 index 000000000..f88c90f1c Binary files /dev/null and b/lab7/obj/uart_c.o differ diff --git a/lab7/obj/utils_c.o b/lab7/obj/utils_c.o new file mode 100644 index 000000000..5916736e3 Binary files /dev/null and b/lab7/obj/utils_c.o differ diff --git a/lab7/obj/vfs_c.o b/lab7/obj/vfs_c.o new file mode 100644 index 000000000..d51179851 Binary files /dev/null and b/lab7/obj/vfs_c.o differ diff --git a/lab7/src/exception.c b/lab7/src/exception.c new file mode 100644 index 000000000..176c5500f --- /dev/null +++ b/lab7/src/exception.c @@ -0,0 +1,327 @@ +// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1- + +#include "utils.h" +#include "exception.h" +#include "sys.h" +#include "printf.h" +#include "sched.h" +#include "memory.h" +#include "uart.h" +#include "mbox.h" +#include "vfs.h" +#include "tmpfs.h" + +extern void end_thread(void); +extern void ret_from_fork(void); +extern Thread *task[]; +extern void delay(int); + +void sync_exc_router(uint64_t esr_el1, uint64_t elr_el1, Trapframe *trapframe) { + int ec = (esr_el1 >> 26) & 0b111111; + int iss = esr_el1 & 0x1FFFFFF; + if (ec == 0b010101) { // is system call + uint64_t syscall_num = trapframe->x[8]; + //printf("[SYSCALL] %d\n", syscall_num); + syscall(syscall_num, trapframe); + } + else { + printf("Exception return address 0x%x\n", elr_el1); + printf("Exception class (EC) 0x%x\n", ec); + printf("Instruction specific syndrome (ISS) 0x%x\n", iss); + while(1){ + ; + } + } +} + +void syscall(uint64_t syscall_num, Trapframe* trapframe) { + switch (syscall_num) { + case SYS_GETPID: + sys_getpid(trapframe); + break; + + case SYS_UART_READ: + sys_uart_read(trapframe); + break; + + case SYS_UART_WRITE: + sys_uart_write(trapframe); + break; + + case SYS_EXEC: + sys_exec(trapframe); + break; + + case SYS_FORK: + sys_fork(trapframe); + break; + + case SYS_EXIT: + sys_exit(trapframe); + break; + + case SYS_MBOX_CALL: + sys_mbox_call(trapframe); + break; + case SYS_KILL: + sys_kill(trapframe); + break; + + case SYS_OPEN: + sys_open(trapframe); + break; + + case SYS_CLOSE: + sys_close(trapframe); + break; + + case SYS_WRITE: + sys_write(trapframe); + break; + + case SYS_READ: + sys_read(trapframe); + break; + + case SYS_MKDIR: + sys_mkdir(trapframe); + break; + + case SYS_MOUNT: + sys_mount(trapframe); + break; + + case SYS_CHDIR: + sys_chdir(trapframe); + break; + } + return; +} + +void sys_getpid(Trapframe *trapframe) { + trapframe->x[0] = current_thread()->pid; +} + +void sys_uart_read(Trapframe *trapframe) { + char *buf = (char *)trapframe->x[0]; + size_t size = (size_t)trapframe->x[1]; + enable_irq(); // 避免卡在 read 裡 + for (int i = 0; i < size; i++) { + *(buf + i) = uart_getc(); + } + disable_irq(); + trapframe->x[0] = size; +} + +void sys_uart_write(Trapframe *trapframe) { + const char *buf = (const char *)trapframe->x[0]; + size_t size = (size_t)trapframe->x[1]; + enable_irq(); + for (int i = 0; i < size; i++) { + uart_send(*(buf + i)); + } + disable_irq(); + trapframe->x[0] = size; +} + +void sys_exec(Trapframe *trapframe) { + preempt_disable(); + char *input = (char *)trapframe->x[0]; + char *program_pos; + cpio_newc_header *fs = (cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int name_size; + int file_size; + while (1) { + fs = (cpio_newc_header *)current; + name_size = hex_to_int(fs->c_namesize, 8); + file_size = hex_to_int(fs->c_filesize, 8); + current += 110; + if (strcmp(current, "TRAILER!!!") == 0) { + uart_puts("No such file!\n"); + break; + } + if (strcmp(current, input) == 0) { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + program_pos = (char *)current; + break; + } else { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + } + char *new_program_pos = (char *)malloc(file_size); + for (int i = 0; i < file_size; i++) { + *(new_program_pos+i) = *(program_pos+i); + } + printf("program pos : %x\n", new_program_pos); + Thread *cur = current_thread(); + preempt_enable(); + asm volatile("msr sp_el0, %0" : : "r"(cur->user_sp)); + asm volatile("msr elr_el1, %0": : "r"(new_program_pos)); + asm volatile("msr spsr_el1, %0" : : "r"(0x0)); + asm volatile("eret"); + trapframe->x[0] = 0; +} + +void sys_fork(Trapframe *trapframe) { + Thread *parent = current_thread(); + /* + ret_from_fork 會把 child_trapframe load to register, + 這樣跑 child thread 時就會用到 child_trapframe 更改的 sp + */ + int newpid = thread_create(ret_from_fork); + + Thread *child = task[newpid]; + + printf("child: %x\n", child); + + // copy kernel stack and user stack + uint64_t kstack_offset = (char *)parent->kernel_sp - (char *)trapframe; + uint64_t ustack_offset = (char *)parent->user_sp - (char *)trapframe->sp_el0; + + // copy kernel stack (including trapframe) + for (uint64_t i = 1; i <= kstack_offset; i++) { + *((char *)(child->kernel_sp - i)) = *((char *)(parent->kernel_sp - i)); + } + + // copy user stack + for (uint64_t i = 1; i <= ustack_offset; i++) { + *((char *)(child->user_sp - i)) = *((char *)(parent->user_sp - i)); + } + + child->cpu_context.sp = child->kernel_sp - kstack_offset; + + Trapframe *child_trapframe = (Trapframe *)child->cpu_context.sp; + child_trapframe->sp_el0 = child->user_sp - ustack_offset; + printf("child sp: %x\n", child_trapframe->sp_el0); + + trapframe->x[0] = child->pid; + child_trapframe->x[0] = 0; +} + +void sys_exit(Trapframe *trapframe) { + current_thread()->status = trapframe->x[0]; + end_thread(); +} + +void sys_mbox_call(Trapframe *trapframe) { + unsigned char ch = (unsigned char)trapframe->x[0]; + unsigned int *mbox = (unsigned int *)trapframe->x[1]; + int ret = mboxc_mbox_call(ch, mbox); // defined in mbox.c + trapframe->x[0] = ret; +} + +void sys_kill(Trapframe *trapframe) { + int pid = trapframe->x[0]; + task[pid]->state = TASK_ZOMBIE; +} + +void sys_open(Trapframe *trapframe) { + char *path = trapframe->x[0]; + int flags = trapframe->x[1]; + struct file *handle; + int ret = vfs_open(path, flags, &handle); + if (ret < 0) { + printf("[Open] %s %d %d\n", path, flags, ret); + trapframe->x[0] = -1; + return; + } + for (int i = 0; i < MAX_FD_NUM; i++) { + if (!current_thread()->fd_table[i]) { + current_thread()->fd_table[i] = handle; + trapframe->x[0] = i; + printf("[Open] %s %d %d\n", path, flags, i); + return; + } + } + trapframe->x[0] = -1; + printf("[Open] %s %d %d\n", path, flags, trapframe->x[0]); +} + +void sys_close(Trapframe *trapframe) { + int fd = trapframe->x[0]; + if (fd < 0) { + trapframe->x[0] = -1; + return; + } + struct file *handle = current_thread()->fd_table[fd]; + int ret = vfs_close(handle); + //current_thread()->fd_table[fd] = NULL; + trapframe->x[0] = ret; + printf("[Close] %d %d\n", fd, ret); +} + +void sys_write(Trapframe *trapframe) { + int fd = trapframe->x[0]; + char *buf = (char *)trapframe->x[1]; + unsigned long count = trapframe->x[2]; + if (fd < 0) { + trapframe->x[0] = -1; + printf("[Write] %d %d\n", fd, trapframe->x[0]); + return; + } + struct file *handle = current_thread()->fd_table[fd]; + if (handle == NULL) { + trapframe->x[0] = 0; + printf("[Write] %d %d\n", fd, trapframe->x[0]); + return; + } + trapframe->x[0] = vfs_write(handle, buf, count); + printf("[Write] %d %d\n", fd, trapframe->x[0]); +} + +void sys_read(Trapframe *trapframe) { + int fd = trapframe->x[0]; + char *buf = (char *)trapframe->x[1]; + unsigned long count = trapframe->x[2]; + if (fd < 0) { + trapframe->x[0] = -1; + return; + } + struct file *handle = current_thread()->fd_table[fd]; + if (handle == NULL) { + trapframe->x[0] = 0; + return; + } + trapframe->x[0] = vfs_read(handle, buf, count); + printf("[Read] %d %d %d\n", fd, count, trapframe->x[0]); + +} + +void sys_mkdir(Trapframe *trapframe) { + char *pathname = trapframe->x[0]; + unsigned mode = trapframe->x[1]; + trapframe->x[0] = vfs_mkdir(pathname); + printf("[Mkdir] %s %d\n", pathname, trapframe->x[0]); +} + +void sys_mount(Trapframe *trapframe) { + char *src = trapframe->x[0]; + char *target = trapframe->x[1]; + char *filesystem = trapframe->x[2]; + unsigned long flags = trapframe->x[3]; + void *data = trapframe->x[4]; + trapframe->x[0] = vfs_mount(target, filesystem); + printf("[Mount] %s %s %d\n", target, filesystem, trapframe->x[0]); +} + +void sys_chdir(Trapframe *trapframe) { + char *pathname = trapframe->x[0]; + trapframe->x[0] = vfs_chdir(pathname); + printf("[Chdir] %s %d\n", pathname, trapframe->x[0]); +} + +void timer_interrupt(int i) { + unsigned long cntfrq_el0; + asm volatile ("mrs %0, cntfrq_el0":"=r" (cntfrq_el0)); + asm volatile ("lsr %0, %0, #5":"=r" (cntfrq_el0) :"r"(cntfrq_el0)); // 1/32 second tick + asm volatile ("msr cntp_tval_el0, %0" : : "r"(cntfrq_el0)); + timer_tick(); +} diff --git a/lab7/src/freelist.c b/lab7/src/freelist.c new file mode 100644 index 000000000..87fcdfd3e --- /dev/null +++ b/lab7/src/freelist.c @@ -0,0 +1,70 @@ +#include "memory.h" +#include "freelist.h" +#include "uart.h" +#include "utils.h" +#include "printf.h" + +extern Freelist *heads; +extern int *frame_array; + +void freelist_push(Freelist *list, Node *nodes, int num) { + nodes[num].next = NULL; + nodes[num].prev = NULL; + if (!list->head) { + list->head = &nodes[num]; + return; + } + Node *node = list->head; + node->prev = &nodes[num]; + nodes[num].next = node; + list->head = &nodes[num]; + return; +} + +void freelist_remove(Freelist *list, Node *nodes, int num) { + Node *current = &nodes[num]; + Node *pre = current->prev; + // Remove the target by updating the head or the previous node. + if (pre==NULL) { + list->head = current->next; + if (current->next != NULL) { + current->next->prev = pre; + } + } + else { + if (current->next == NULL) { + pre->next = current->next; + } + else { + current->next->prev = pre; + pre->next = current->next; + } + } + current->prev = NULL; + current->next = NULL; +} + +void print_freelists() { + uart_puts("-----------Freelists------------\n"); + for(int i = LOG2_MAX_PAGES; i >= 0; i--) { + freelist_print(i, &heads[i]); + } + // uart_puts("-------------Pages--------------\n"); + // for (int j = 0; j < (MAX_PAGES/16); j++) { + // for (int i = 0; i < 16; i++) { + // uart_int(frame_array[16*j+i]); + // uart_puts(" "); + // } + // uart_puts("\n"); + // } +} +void freelist_print(int level, Freelist *list) { + uart_puts("Level "); + uart_int(level); + uart_puts(": "); + for (Node *node = list->head; node != NULL; node = node->next) { + uart_uint(node->index); + uart_puts(" "); + } + uart_puts("\n"); +} diff --git a/lab7/src/initramfs.c b/lab7/src/initramfs.c new file mode 100644 index 000000000..4ef743972 --- /dev/null +++ b/lab7/src/initramfs.c @@ -0,0 +1,150 @@ +#include "memory.h" +#include "vfs.h" +#include "initramfs.h" +#include "printf.h" +#include "utils.h" +#include "exception.h" + +struct vnode_operations* initramfs_v_ops; +struct file_operations* initramfs_f_ops; + +int initramfs_register() { + initramfs_v_ops = (struct vnode_operations*)malloc(sizeof(struct vnode_operations)); + initramfs_v_ops->lookup = &initramfs_lookup; + initramfs_v_ops->create = &initramfs_create; + initramfs_v_ops->mkdir = &initramfs_mkdir; + initramfs_f_ops = (struct file_operations*)malloc(sizeof(struct file_operations)); + initramfs_f_ops->write = &initramfs_write; + initramfs_f_ops->read = &initramfs_read; + return 0; +} + +int initramfs_setup_mount(struct filesystem* fs, struct mount* mount) { + struct initramfs_internal* initramfs_root = (struct initramfs_internal*)malloc(sizeof(struct initramfs_internal)); + initramfs_root->type = DIRECTORY; + initramfs_root->name = (char *)malloc(2); + strcpy(initramfs_root->name, "/"); + struct node *root_vnode = initramfs_create_vnode(initramfs_root); + mount->root = root_vnode; + mount->fs = fs; + initramfs_root->vnode = root_vnode; + return 0; +} + +struct vnode* initramfs_create_vnode(struct initramfs_internal* initramfs_node) { + struct vnode* vnode = (struct vnode*)malloc(sizeof(struct vnode)); + vnode->f_ops = initramfs_f_ops; + vnode->v_ops = initramfs_v_ops; + vnode->internal = initramfs_node; + return vnode; +} + +int initramfs_create(struct vnode* dir, struct vnode** target, const char* component_name) { + return -1; +} + +// vnode operations +int initramfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name) { + // component_name is empty, return dir vnode + if (!strcmp(component_name, "")) { + return -1; + } + else if (!strcmp(component_name, ".")) { + *target = dir; + return 0; + } + else if (!strcmp(component_name, "..")) { // todo: cross filesystem + *target = dir; + if (dir->mount_parent) { + *target = dir->mount_parent; + return 0; + } + return -1; + } + // search component_name in dir + if (dir->mount != NULL) + dir = dir->mount->root; + printf("[INITRAMFS LOOKUP]%s\n", component_name); + char *program_pos; + cpio_newc_header *fs = (cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int name_size; + int file_size; + char *name_pos; + while (1) { + fs = (cpio_newc_header *)current; + name_size = hex_to_int(fs->c_namesize, 8); + file_size = hex_to_int(fs->c_filesize, 8); + current += 110; + if (strcmp(current, "TRAILER!!!") == 0) { + uart_puts("No such file!\n"); + break; + } + name_pos = current; + if (strcmp(current, component_name) == 0) { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + program_pos = (char *)current; + break; + } else { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + } + char *new_program_pos = (char *)malloc(file_size); + for (int i = 0; i < file_size; i++) { + *(new_program_pos+i) = *(program_pos+i); + } + printf("name pos : %x\n", name_pos); + printf("program pos : %x\n", new_program_pos); + struct initramfs_internal* file_node = (struct initramfs_internal*)malloc(sizeof(struct initramfs_internal)); + file_node->type = REGULAR_FILE; + file_node->name = name_pos; + file_node->size = file_size; + file_node->data = (void *)new_program_pos; + file_node->vnode = initramfs_create_vnode(file_node); + *target = file_node->vnode; + // for (int i = 0; i < MAX_ENTRIES; i++) { + // struct initramfs_internal* file_node = ((struct initramfs_internal*)dir->internal)->child[i]; + // if ((file_node != NULL) & !strcmp(file_node->name, component_name)) { + // *target = file_node->vnode; + // printf("[lookup] 0x%x\n", *target); + // return 0; + // } + // } + // *target = NULL; + return 0; +} + +int initramfs_write(struct file* file, const void* buf, size_t len) { + return -1; +} + +int initramfs_read(struct file* file, void* buf, size_t len) { + if (((struct initramfs_internal *)file->vnode->internal)->type != REGULAR_FILE) { + printf("Read on not regular file\n"); + return -1; + } + struct initramfs_internal* file_node = (struct initramfs_internal *)file->vnode->internal; + + char *dest = (char*)buf; + char *src = &(file_node->data[file->f_pos]); + size_t i = 0; + for (; i < len && file->f_pos < file_node->size; i++) { + dest[i] = src[i]; + file->f_pos++; + } + return i; + +} + +int initramfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name) { + return -1; +} + + diff --git a/lab7/src/main.c b/lab7/src/main.c new file mode 100644 index 000000000..96cc5dcbd --- /dev/null +++ b/lab7/src/main.c @@ -0,0 +1,100 @@ +#include "uart.h" +#include "utils.h" +#include "freelist.h" +#include "memory.h" +#include "sched.h" +#include "printf.h" +#include "typedef.h" +#include "sys.h" +#include "mbox.h" +#include "shell.h" +#include "vfs.h" +#include "tmpfs.h" +#define N 5 + +extern void delay(); +extern void core_timer_enable(); + + +void cpu_timer_register_enable() { // 讓 el0 用 clock 不會 interrupt + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); +} + +void fork_test(){ + printf("\nFork Test, pid %d\n", getpid()); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + ++cnt; + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + delay(1000000); + ++cnt; + } + } + exit(0); + } + else { + unsigned int __attribute__((aligned(16))) mbox[36]; + get_board_revision(mbox); + mbox_call(MBOX_CH_PROP, mbox); + for (int i = 0; i < 8; i++) { + printf("mbox %d: %x\n", i, mbox[i]); + } + printf("parent here, pid %d, child %d\n", getpid(), ret); + } +} + +void foo(){ + for(int i = 0; i < 10; ++i) { + printf("Thread id: %d %d\n", getpid(), i); + delay(10000000); + schedule(); + } +} + +void main() { + uart_init(); + init_printf(NULL, putc); + memory_init(); + rootfs_init(); + mount_initramfs(); + task_init(); + core_timer_enable(); + cpu_timer_register_enable(); + + while (1) { + char c = uart_getc(); + uart_send(c); + if (c == 's') break; + } + printf("%s", "\nHello from Raspberry pi!\n"); + shell(); + // for(int i = 0; i < N; ++i) { // N should > 2 + // thread_create(foo); + // } + // delay(1000000000); + // kill_zombies(); + // printf("PID: %d\n", getpid()); + // for(int i = 0; i < N*2; ++i) { // N should > 2 + // thread_create(foo); + // } + //fork_test(); + int ret; + if ((ret = fork()) == 0) + exec("syscall.img", 0x0); + idle(); + +} diff --git a/lab7/src/mbox.c b/lab7/src/mbox.c new file mode 100644 index 000000000..4d9dbce85 --- /dev/null +++ b/lab7/src/mbox.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "gpio.h" +#include "printf.h" + +/* mailbox message buffer */ +//volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +#define VIDEOCORE_MBOX (MMIO_BASE + 0x0000B880) +#define MBOX_READ ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x0)) +#define MBOX_POLL ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x10)) +#define MBOX_SENDER ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x14)) +#define MBOX_STATUS ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x18)) +#define MBOX_CONFIG ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x1C)) +#define MBOX_WRITE ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x20)) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 +#define MBOX_CH_PROP 8 +/* tags */ +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mboxc_mbox_call(unsigned char ch, unsigned int *mbox) { + /* 28 bits (MSB) for value, 4 bits for channel type */ + unsigned int r = (((unsigned int)((unsigned long)mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + do { + asm volatile("nop"); + } while (*MBOX_STATUS & MBOX_FULL); + /* write the address of our message to the mailbox with channel identifier */ + *MBOX_WRITE = r; + /* now wait for the response */ + while (1) { + /* is there a response? */ + do { + asm volatile("nop"); + } while (*MBOX_STATUS & MBOX_EMPTY); + /* is it a response to our message? */ + if (r == *MBOX_READ) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + return 0; +} + +int get_board_revision(unsigned int *mbox) { + mbox[0] = 7 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_BOARD_REVISION; // tag identifier + mbox[3] = 4; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // value buffer + // tags end + mbox[6] = END_TAG; + + return 0; + //return mbox_call(MBOX_CH_PROP, mbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + // printf("0x%x\n", mbox[5]); // it should be 0xa020d3 for rpi3 b+ +} + +int get_arm_memory(unsigned int *mbox) { + mbox[0] = 8 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_ARM_MEMORY; // tag identifier + mbox[3] = 8; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // value buffer + mbox[6] = 0; // value buffer + // tags end + mbox[7] = END_TAG; + + return 0; + //return mbox_call(MBOX_CH_PROP, mbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + // printf("0x%x\n", mbox[5]); // it should be 0xa020d3 for rpi3 b+ +} \ No newline at end of file diff --git a/lab7/src/memory.c b/lab7/src/memory.c new file mode 100644 index 000000000..f45b69fb1 --- /dev/null +++ b/lab7/src/memory.c @@ -0,0 +1,241 @@ +#include "memory.h" +#include "freelist.h" +#include "uart.h" +#include "utils.h" +#include "printf.h" + +Freelist *heads; +Node *nodes; +int *frame_array; +blocklist memory_blocks; + +extern char _end; +extern void memzero(void *, size_t); + +void memory_init() { + void *base = (void *)&_end; + heads = (Freelist *)simple_malloc(&base, (int)sizeof(Freelist)*LOG2_MAX_PAGES_PLUS_1); + nodes = (Node *)simple_malloc(&base, (int)sizeof(Node)*MAX_PAGES); + frame_array = (int *)simple_malloc(&base, (int)sizeof(int)*MAX_PAGES); + for (int i = 0; i < MAX_PAGES; i++) { + nodes[i].prev = NULL; + nodes[i].next = NULL; + nodes[i].index = i; + frame_array[i] = BELONG_LEFT; + } + frame_array[0] = LOG2_MAX_PAGES; + for (int i = 0; i < LOG2_MAX_PAGES; i++) { + heads[i].head = NULL; + } + heads[LOG2_MAX_PAGES].head = &nodes[0]; + + reserve_memory(0x0, (ulong)base); + reserve_memory(0x8000000, (0x8000000+CPIO_SIZE)); + reserve_memory(0x3c000000, 0x40000000); + + memory_blocks.head = (block_meta *)malloc(PAGE_SIZE); + memory_blocks.head->next = NULL; + memory_blocks.head->size = 4096 - BLOCK_SIZE; + memory_blocks.head->free = 1; + memory_blocks.head->pagetail = 1; + +} + +int find_allocate_list(Freelist *heads, int needed_pages) { + for (int i = needed_pages; i <= LOG2_MAX_PAGES; i++) { + if (heads[i].head != NULL) { + return i; + } + } + return LOG2_MAX_PAGES; +} + +int allocate_page(Freelist *heads, Node *nodes, int *frames, int needed_level, int index) { + debug("Allocate page for level ", needed_level); + if (index >= 0) { + // reserve memory + int remove = index, push; + int use_level = needed_level; + while(frames[remove] != use_level) { + remove &= ~(pow2(use_level)); + use_level++; + } + debug("Remove", remove); + freelist_remove(&heads[use_level], nodes, remove); + for(int i = use_level-1; i >= needed_level; i--) { + remove ^= (index & pow2(i)); + push = remove ^ pow2(i); + debug("Push", push); + freelist_push(&heads[i], nodes, push); + frames[push] = i; + } + frames[index] = ALLOCATED; + return 0; // no need to return anything + } + else { + // normal page allocation + int use_level = find_allocate_list(heads, needed_level); + Node *fs = heads[use_level].head; + int front = fs->index; + debug("Remove from freelist", front); + freelist_remove(&heads[use_level], nodes, front); + for (int i = use_level-1; i >= needed_level; i--) { + int back = front | pow2(i); + debug("Push to freelist", back); + freelist_push(&heads[i], nodes, back); + frames[back] = i; + } + frames[front] = ALLOCATED; + return fs->index; + } +} + +void free_page(Freelist *heads, Node *nodes, int *frames, int free_index) { + debug("Freeing page", free_index); + frames[free_index] = BELONG_LEFT; + int level = 0; + int free_level = LOG2_MAX_PAGES; + while(frames[free_index ^ pow2(level)] == BELONG_LEFT) { // 如果非最左邊區塊,終會到達index=0, frames[0]不可能BELONG_LEFT, 所以會跳出迴圈 + free_index &= ~(pow2(level)); + level++; + } + for (int i = level; i < LOG2_MAX_PAGES; i++) { + int buddy = free_index ^ pow2(i); + if (frames[buddy] != i) { // not same level or allocated + free_level = i; + break; + } + frames[buddy] = BELONG_LEFT; + freelist_remove(&heads[i], nodes, buddy); + debug("Merged", buddy); + free_index &= ~(pow2(i)); + } + debug("Push back to freelist", free_index); + freelist_push(&heads[free_level], nodes, free_index); + frames[free_index] = free_level; + return; +} + +void *malloc(size_t size) { + if (size >= (PAGE_SIZE-BLOCK_SIZE)) { + //int need_pages = (size+PAGE_SIZE-1)/PAGE_SIZE; + int needed_order = log2((size+PAGE_SIZE-1)/PAGE_SIZE); + void *ptr = (void *)(unsigned long)(MEMORY_BASE + allocate_page(heads, nodes, frame_array, needed_order, -1) * PAGE_SIZE); + //print_freelists(); + memzero(ptr, pow2(needed_order)*PAGE_SIZE); + //printf("[Malloc page] %x\n", ptr); + return ptr; + } + else { + block_meta *curr = memory_blocks.head; + size = (size & ~15) + 16; // align to 16 + /* find split block */ + while(1) { + if ((curr->free != (short)0) && (curr->size > size)) { + break; + } + if (curr->next == (block_meta *)NULL) { + /* allocate new page */ + block_meta *new_page = (block_meta *)malloc(PAGE_SIZE); + new_page->size = PAGE_SIZE-BLOCK_SIZE; + new_page->free = 1; + new_page->pagetail = 1; + new_page->next = NULL; + curr->next = new_page; + curr = curr->next; + break; + } + curr = curr->next; + } + + /* allocate memory */ + int left_size = curr->size - size; + block_meta *new_block = (block_meta *)((ulong)curr+BLOCK_SIZE+(ulong)size); + new_block->size = left_size; + new_block->free = 1; + new_block->pagetail = curr->pagetail; + new_block->next = curr->next; + curr->size = size; + curr->free = 0; + curr->pagetail = 0; + curr->next = new_block; + memzero((ulong)curr+BLOCK_SIZE, size); + //printf("[Malloc] %x\n", (ulong)curr+BLOCK_SIZE); + return (void *)((ulong)curr+BLOCK_SIZE); + + } +} + +void free(void *ptr) { + if ((ulong)ptr % PAGE_SIZE == 0) { + int free_index = (int)(((ulong)ptr-MEMORY_BASE+(PAGE_SIZE-1)) / 0x1000); + printf("Free page index %d\n", free_index); + free_page(heads, nodes, frame_array, free_index); + //print_freelists(); + } + else { + //printf("[Free] %x\n", ptr); + block_meta *need_free = (block_meta *)((ulong)ptr-BLOCK_SIZE); + need_free->free = 1; + /* remove block */ + block_meta *curr = memory_blocks.head; + while(curr != NULL) { + if (curr->free) { + while(!curr->pagetail && (curr->next != NULL) && curr->next->free) { + curr->size += curr->next->size; + curr->pagetail = curr->next->pagetail; + curr->next = curr->next->next; + } + } + curr = curr->next; + } + } +} + +void reserve_memory(ulong start, ulong end) { + int index = (start-MEMORY_BASE) / PAGE_SIZE; + int pages = ((end+PAGE_SIZE-1)-start) / PAGE_SIZE; + uart_puts("Reserve "); + uart_int(pages); + uart_puts(" page(s)\n"); + for (int i = 0; i < LOG2_MAX_PAGES; i++) { + if (index & pow2(i)) { + allocate_page(heads, nodes, frame_array, i, index); + index += pow2(i); + pages -= pow2(i); + } + if (pages <= 0) break; + if (pow2(i) >= pages) { + allocate_page(heads, nodes, frame_array, log2(pages), index); + break; + } + if (pages <= 0) break; + } + //uart_puts("[Reserve memory] Finished.\n"); + //print_freelists(); +} + +void print_memory() { + block_meta *curr = memory_blocks.head; + while(curr != NULL) { + uart_puts("----------------\n"); + uart_puts("Address: "); + uart_hex((ulong)curr + BLOCK_SIZE); + uart_puts("\n"); + uart_puts("Size: "); + uart_int(curr->size); + uart_puts("\n"); + uart_puts("Free: "); + if (curr->free) + uart_puts("Yes\n"); + else + uart_puts("No\n"); + uart_puts("Last block in the page: "); + if (curr->pagetail) + uart_puts("Yes\n"); + else + uart_puts("No\n"); + + curr = curr->next; + } +} \ No newline at end of file diff --git a/lab7/src/printf.c b/lab7/src/printf.c new file mode 100644 index 000000000..db3507682 --- /dev/null +++ b/lab7/src/printf.c @@ -0,0 +1,228 @@ +/* +File: printf.c +Copyright (C) 2004 Kustaa Nyholm +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "printf.h" + +typedef void (*putcf) (void*,char); +static putcf stdout_putf; +static void* stdout_putp; + +#define PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%=d; + d/=base; + if (n || dgt>0|| d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void li2a (long num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + uli2a(num,10,0,bf); + } + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%= d; + d/=base; + if (n || dgt>0 || d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void i2a (int num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + ui2a(num,10,0,bf); + } + +static int a2d(char ch) + { + if (ch>='0' && ch<='9') + return ch-'0'; + else if (ch>='a' && ch<='f') + return ch-'a'+10; + else if (ch>='A' && ch<='F') + return ch-'A'+10; + else return -1; + } + +static char a2i(char ch, char** src,int base,int* nump) + { + char* p= *src; + int num=0; + int digit; + while ((digit=a2d(ch))>=0) { + if (digit>base) break; + num=num*base+digit; + ch=*p++; + } + *src=p; + *nump=num; + return ch; + } + +static void putchw(void* putp,putcf putf,int n, char z, char* bf) + { + char fc=z? '0' : ' '; + char ch; + char* p=bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp,fc); + while ((ch= *bf++)) + putf(putp,ch); + } + +void tfp_format(void* putp,putcf putf,char *fmt, va_list va) + { + char bf[12]; + + char ch; + + + while ((ch=*(fmt++))) { + if (ch!='%') + putf(putp,ch); + else { + char lz=0; +#ifdef PRINTF_LONG_SUPPORT + char lng=0; +#endif + int w=0; + ch=*(fmt++); + if (ch=='0') { + ch=*(fmt++); + lz=1; + } + if (ch>='0' && ch<='9') { + ch=a2i(ch,&fmt,10,&w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch=='l') { + ch=*(fmt++); + lng=1; + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),10,0,bf); + else +#endif + ui2a(va_arg(va, unsigned int),10,0,bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'd' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int),bf); + else +#endif + i2a(va_arg(va, int),bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'x': case 'X' : +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf); + else +#endif + ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf); + putchw(putp,putf,w,lz,bf); + break; + case 'c' : + putf(putp,(char)(va_arg(va, int))); + break; + case 's' : + putchw(putp,putf,w,0,va_arg(va, char*)); + break; + case '%' : + putf(putp,ch); + default: + break; + } + } + } + abort:; + } + + +void init_printf(void* putp,void (*putf) (void*,char)) + { + stdout_putf=putf; + stdout_putp=putp; + } + +void tfp_printf(char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(stdout_putp,stdout_putf,fmt,va); + va_end(va); + } + +static void putcp(void* p,char c) + { + *(*((char**)p))++ = c; + } + + + +void tfp_sprintf(char* s,char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(&s,putcp,fmt,va); + putcp(&s,0); + va_end(va); + } \ No newline at end of file diff --git a/lab7/src/sched.c b/lab7/src/sched.c new file mode 100644 index 000000000..39b6402d1 --- /dev/null +++ b/lab7/src/sched.c @@ -0,0 +1,156 @@ +#include "sched.h" +#include "memory.h" +#include "utils.h" +#include "printf.h" +#include "vfs.h" +#include "tmpfs.h" + +static Thread init_task = INIT_TASK; +//Thread *current_thread = &(init_task); +Thread *task[NR_TASKS] = {&(init_task), }; +int nr_tasks = 1; + + +extern void run_thread(void); +extern Thread *get_current(void); +extern void enable_irq(); +extern void disable_irq(); +extern switch_to(void *, void *); + + +int get_new_pid() { + Thread* p; + for (int i = 0; i < NR_TASKS; i++) { + p = task[i]; + if (p == NULL) { + return i; + } + } + return -1; +} + +Thread* current_thread() { + Thread *cur = get_current(); + if (!cur) + return &init_task; + return cur; +} + +void preempt_disable(void) { + current_thread()->preempt_count++; +} + +void preempt_enable(void) { + current_thread()->preempt_count--; +} + +int thread_create(void *func) { + Thread *p = malloc(sizeof(Thread)); + printf("thread_create %x\n", p); + p->priority = 1; + p->state = TASK_RUNNING; + p->counter = p->priority; + p->status = 0; + p->preempt_count = 1; //disable preemtion until schedule_tail + + p->cpu_context.x19 = (ulong)func; + p->cpu_context.lr = (ulong)run_thread; + //p->cpu_context.sp = (ulong)p + THREAD_SIZE - 16; + p->kernel_sp = (ulong)malloc(PAGE_SIZE) + PAGE_SIZE - 16; + p->user_sp = (ulong)malloc(PAGE_SIZE) + PAGE_SIZE - 16; + p->cpu_context.sp = p->kernel_sp; // kernel space + p->pwd = rootfs->root; + + int pid = get_new_pid(); + task[pid] = p; + p->pid = pid; + preempt_enable(); + return pid; +} + +void _schedule() { + preempt_disable(); + int next, c; + struct Thread* p; + while (1) { + c = -1; + next = 0; + for (int i = 0; i < NR_TASKS; i++) { // pick biggest c value + p = task[i]; + if (p && p->state == TASK_RUNNING && p->counter > c) { + c = p->counter; + next = i; + } + } + if (c) { + break; + } + for (int i = 0; i < NR_TASKS; i++) { + p = task[i]; + if (p) { + p->counter = (p->counter >> 1) + p->priority; + } + } + } + //debug("now thread", current_thread()); + //debug("next thread", task[next]); + if (current_thread() != task[next]) { + //printf("[scheduler] next pid: %d\n", next); + Thread *prev = current_thread(); + //current_thread = task[next]; + switch_to(prev, task[next]); + } + preempt_enable(); +} + +void schedule() { + current_thread()->counter = 0; + _schedule(); +} + +void kill_zombies() { + Thread* p; + for (int i = 1; i < NR_TASKS; i++) { // pick biggest c value + p = task[i]; + if (p && p->state == TASK_ZOMBIE) { + free((void *)(p->kernel_sp & ~(PAGE_SIZE-1))); + free((void *)(p->user_sp & ~(PAGE_SIZE-1))); + free(p); + task[i] = NULL; + } + } +} + +void idle() { + while(1) { + kill_zombies(); // reclaim threads marked as DEAD + schedule(); // switch to any other runnable thread + } +} + + +void timer_tick() { + current_thread()->counter--; + if (current_thread()->counter>0 || current_thread()->preempt_count > 0) { + return; + } + current_thread()->counter=0; + enable_irq(); + schedule(); + disable_irq(); +} + +void end_thread(void) { + current_thread()->state = TASK_ZOMBIE; + schedule(); +} + +void task_init(void) { + //Thread *p = current_thread(); + //asm volatile ("mrs %0, sp_el1":"=r"(p->kernel_sp)); + uint64_t init_task_addr = (uint64_t)&init_task; + asm volatile ("msr tpidr_el1, %0"::"r"(init_task_addr)); + (&init_task)->pwd = rootfs->root; + uint64_t sp_el0 = 0; + asm volatile ("msr sp_el0, %0"::"r"(sp_el0)); +} \ No newline at end of file diff --git a/lab7/src/shell.c b/lab7/src/shell.c new file mode 100644 index 000000000..3c9546359 --- /dev/null +++ b/lab7/src/shell.c @@ -0,0 +1,76 @@ +#include "utils.h" +#include "uart.h" +#include "printf.h" +#include "memory.h" +#include "vfs.h" +#include "tmpfs.h" +#include "shell.h" +#include "sched.h" +#include "sys.h" + +void shell() { + printf("\n\n _ _ ___ _____ _ _ ___ ___ ___ ___ \n"); + printf("| \\| |/ __|_ _| | | |/ _ \\/ __| \\_ _|\n"); + printf("| .` | (__ | | | |_| | (_) \\__ \\ |) | | \n"); + printf("|_|\\_|\\___| |_| \\___/ \\___/|___/___/___|\n\n"); + char input[1024]; + while (1) { + uart_send('\r'); + uart_puts("# "); + shell_input(input); + if (strcmp(input, "test") == 0) { + struct vnode *vnode; + int fd = open("/dir", O_CREAT); + int ret = write(fd, "abcdefghijklmnopqrstuvwxyz", 26); + printf("%d %d\n", fd, ret); + fd = open("/dir", 0); + char buf[128]; + int rd = read(fd, buf, 10); + printf("%d %d %s\n", fd, rd, buf); + ret = vfs_lookup(".", &vnode); + printf("Current directory: %s\n", ((struct tmpfs_internal *)vnode->internal)->name); + + + } else if (strcmp(input, "m") == 0) { + shell_input(input); + int size = (int)cstr_to_ulong(input); + void *ptr = malloc(size); + printf("%x\n", ptr); + } else if (strcmp(input, "d") == 0) { + shell_input(input); + void *ptr = (void *)(ulong)hex_to_int(input, 8); + free(ptr); + } else if (strcmp(input, "pm") == 0) { + print_freelists(); + print_memory(); + } else if (!strcmp(input, "pwd")) { + printf("%s\n", ((struct tmpfs_internal *)current_thread()->pwd->internal)->name); + } else if (!strcmp(input, "vfs1")) { + int ret; + char *argv[] = {}; + if ((ret = fork()) == 0) + exec("vfs1.img", argv); + idle(); + } else { + uart_puts("Error input!\n"); + } + } + +} + +void shell_input(char *input) { + int i = 0; + char temp; + while (1) { + temp = uart_getc(); + if (temp == '\n') { + uart_puts("\n"); + input[i] = '\0'; + break; + } else + uart_send(temp); + + input[i] = temp; + i++; + } +} \ No newline at end of file diff --git a/lab7/src/start.S b/lab7/src/start.S new file mode 100644 index 000000000..6e55f94c0 --- /dev/null +++ b/lab7/src/start.S @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +.section ".text.boot" + +.global _start + +_start: + bl from_el2_to_el1 + bl set_exception_vector_table + // set top of stack just before our code (stack grows to a lower address per AAPCS64) + adr x1, _start + mov sp, x1 + + // clear bss + adr x1, __bss_start + ldr w2, =__bss_size +1: cbz w2, 2f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 1b + +2: // jump to C code, should not return + bl main + // for failsafe, halt this core too +3: wfe + b 3b + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 + mov x0, 0x5 // EL1h (SPSel = 1) with interrupt enabled + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 + +.globl from_el1_to_el0 +from_el1_to_el0: + mov x0, 0 + msr spsr_el1, x0 + msr elr_el1, lr + mov x0, 0x60000 // user space stack + msr sp_el0, x0 + eret // return to EL0 + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + 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 x21, sp_el0 + mrs x22, elr_el1 + mrs x23, spsr_el1 + + stp x30, x21, [sp, #16 * 15] + stp x22, x23, [sp, #16 * 16] +.endm + +// load general registers from stack +.macro load_all + ldp x22, x23, [sp, #16 * 16] + ldp x30, x21, [sp, #16 * 15] + + msr sp_el0, x21 + msr elr_el1, x22 + msr spsr_el1, x23 + + 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] + add sp, sp, 32 * 9 +.endm + +exception_handler_loop: + b exception_handler_loop + +exception_handler: + save_all + bl exception_handler_loop + load_all + eret + +sync_exception_handler: + save_all + mrs x0, esr_el1 // to decide is syscall or not + mrs x1, elr_el1 // the address return to + mov x2, sp // trapframe + bl sync_exc_router + load_all + eret + +irq_exception_handler_low: + save_all + mov x0, #0 + bl timer_interrupt + load_all + eret + +irq_exception_handler: + save_all + mov x0, #1 + bl timer_interrupt + load_all + eret + +exception_handler_lower_irq: + save_all + bl timer_interrupt + load_all + eret + +.align 11 // vector table should be aligned to 0x800 +.global exception_vector_table +exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b sync_exception_handler + .align 7 + b irq_exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b sync_exception_handler + .align 7 + b irq_exception_handler_low + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + ret + +.equ CORE0_TIMER_IRQ_CTRL, 0x40000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mrs x0, cntfrq_el0 + lsr x0, x0, #5 + msr cntp_tval_el0, x0 // set expired time + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret + +.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 + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl enable_irq +enable_irq: + msr DAIFClr, #2 // IRQ mask bit + ret + +.globl disable_irq +disable_irq: + msr DAIFSet, #2 + ret + +.globl run_thread +run_thread: + bl preempt_enable + blr x19 //should never return + bl end_thread + +.globl ret_from_fork +ret_from_fork: + load_all + eret + +.global memzero +memzero: + str xzr, [x0], #8 + sub w1, w1, #1 + cbnz w1, memzero + ret diff --git a/lab7/src/sys.S b/lab7/src/sys.S new file mode 100644 index 000000000..acb4ca5c9 --- /dev/null +++ b/lab7/src/sys.S @@ -0,0 +1,92 @@ +#define __ASSEMBLY__ +#include "sys.h" + +.global getpid +getpid: + mov x8, SYS_GETPID + svc #0 + ret + +.global uartread +uartread: + mov x8, SYS_UART_READ + svc #0 + ret + +.global uartwrite +uartwrite: + mov x8, SYS_UART_WRITE + svc #0 + ret + +.global exec +exec: + mov x8, SYS_EXEC + svc #0 + ret // shouldn't go here + +.global fork +fork: + mov x8, SYS_FORK + svc #0 + ret + +.global exit +exit: + mov x8, SYS_EXIT + svc #0 + ret + +.global mbox_call +mbox_call: + mov x8, SYS_MBOX_CALL + svc #0 + ret + +.global kill +kill: + mov x8, SYS_KILL + svc #0 + ret + +.global open +open: + mov x8, SYS_OPEN + svc #0 + ret + +.global close +close: + mov x8, SYS_CLOSE + svc #0 + ret + +.global write +write: + mov x8, SYS_WRITE + svc #0 + ret + +.global read +read: + mov x8, SYS_READ + svc #0 + ret + +.global mkdir +mkdir: + mov x8, SYS_MKDIR + svc #0 + ret + +.global mount +mount: + mov x8, SYS_MOUNT + svc #0 + ret + +.global chdir +chdir: + mov x8, SYS_CHDIR + svc #0 + ret \ No newline at end of file diff --git a/lab7/src/tmpfs.c b/lab7/src/tmpfs.c new file mode 100644 index 000000000..32cada676 --- /dev/null +++ b/lab7/src/tmpfs.c @@ -0,0 +1,148 @@ +#include "memory.h" +#include "vfs.h" +#include "tmpfs.h" +#include "printf.h" +#include "utils.h" + +struct vnode_operations* tmpfs_v_ops; +struct file_operations* tmpfs_f_ops; + +int tmpfs_register() { + tmpfs_v_ops = (struct vnode_operations*)malloc(sizeof(struct vnode_operations)); + tmpfs_v_ops->lookup = &tmpfs_lookup; + tmpfs_v_ops->create = &tmpfs_create; + tmpfs_v_ops->mkdir = &tmpfs_mkdir; + tmpfs_f_ops = (struct file_operations*)malloc(sizeof(struct file_operations)); + tmpfs_f_ops->write = &tmpfs_write; + tmpfs_f_ops->read = &tmpfs_read; + return 0; +} + +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount) { + struct tmpfs_internal* tmpfs_root = (struct tmpfs_internal*)malloc(sizeof(struct tmpfs_internal)); + tmpfs_root->type = DIRECTORY; + strcpy(tmpfs_root->name, "/"); + tmpfs_root->parent = NULL; + struct node *root_vnode = tmpfs_create_vnode(tmpfs_root); + mount->root = root_vnode; + mount->fs = fs; + tmpfs_root->vnode = root_vnode; + return 0; +} + +struct vnode* tmpfs_create_vnode(struct tmpfs_internal* tmpfs_node) { + struct vnode* vnode = (struct vnode*)malloc(sizeof(struct vnode)); + vnode->f_ops = tmpfs_f_ops; + vnode->v_ops = tmpfs_v_ops; + vnode->internal = tmpfs_node; + return vnode; +} + +int tmpfs_create(struct vnode* dir, struct vnode** target, const char* component_name) { + // create tmpfs internal structure + struct tmpfs_internal* file_node = (struct tmpfs_internal*)malloc(sizeof(struct tmpfs_internal)); + file_node->type = REGULAR_FILE; + strcpy(file_node->name, component_name); + file_node->parent = (struct tmpfs_internal *)(dir->internal); + for (int i = 0; i < MAX_ENTRIES; i++) { + if (!file_node->parent->child[i]) { + file_node->parent->child[i] = file_node; + break; + } + } + file_node->vnode = tmpfs_create_vnode(file_node); + file_node->data = malloc(PAGE_SIZE); + + *target = file_node->vnode; + return 0; +} + +// vnode operations +int tmpfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name) { + // component_name is empty, return dir vnode + if (!strcmp(component_name, "")) { + return -1; + } + else if (!strcmp(component_name, ".")) { + *target = dir; + return 0; + } + else if (!strcmp(component_name, "..")) { // todo: cross filesystem + *target = dir; + if (dir->mount_parent) { + *target = dir->mount_parent; + } + struct tmpfs_internal *t = ((struct tmpfs_internal *)dir->internal)->parent; + if (!t) return 0; + *target = ((struct tmpfs_internal *)dir->internal)->parent->vnode; + return 0; + } + // search component_name in dir + if (dir->mount != NULL) + dir = dir->mount->root; + for (int i = 0; i < MAX_ENTRIES; i++) { + struct tmpfs_internal* file_node = ((struct tmpfs_internal*)dir->internal)->child[i]; + if ((file_node != NULL) & !strcmp(file_node->name, component_name)) { + *target = file_node->vnode; + printf("[lookup] 0x%x\n", *target); + return 0; + } + } + *target = NULL; + return -1; +} + +int tmpfs_write(struct file* file, const void* buf, size_t len) { + if (((struct tmpfs_internal *)file->vnode->internal)->type != REGULAR_FILE) { + printf("Write on not regular file\n"); + return -1; + } + + struct tmpfs_internal* file_node = (struct tmpfs_internal *)file->vnode->internal; + + char *dest = &(file_node->data[file->f_pos]); + char *src = (char*)buf; + size_t i = 0; + for (; i < len; i++) { + dest[i] = src[i]; + } + //dest[i] = EOF; + return i; +} + +int tmpfs_read(struct file* file, void* buf, size_t len) { + if (((struct tmpfs_internal *)file->vnode->internal)->type != REGULAR_FILE) { + printf("Read on not regular file\n"); + return -1; + } + struct tmpfs_internal* file_node = (struct tmpfs_internal *)file->vnode->internal; + + char *dest = (char*)buf; + char *src = &(file_node->data[file->f_pos]); + size_t i = 0; + for (; i < len && src[i] != '\0'; i++) { + dest[i] = src[i]; + } + file->f_pos += i; + return i; + +} + +int tmpfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name) { + // create tmpfs internal structure + struct tmpfs_internal* dir_node = (struct tmpfs_internal*)malloc(sizeof(struct tmpfs_internal)); + dir_node->type = DIRECTORY; + strcpy(dir_node->name, component_name); + dir_node->parent = (struct tmpfs_internal *)(dir->internal); + for (int i = 0; i < MAX_ENTRIES; i++) { + if (!dir_node->parent->child[i]) { + dir_node->parent->child[i] = dir_node; + break; + } + } + dir_node->vnode = tmpfs_create_vnode(dir_node); + *target = dir_node->vnode; + return 0; +} + + diff --git a/lab7/src/uart.c b/lab7/src/uart.c new file mode 100644 index 000000000..770db014e --- /dev/null +++ b/lab7/src/uart.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "gpio.h" + +/* Auxilary mini UART registers */ +#define AUX_ENABLE ((volatile unsigned int *)(MMIO_BASE + 0x00215004)) +#define AUX_MU_IO ((volatile unsigned int *)(MMIO_BASE + 0x00215040)) +#define AUX_MU_IER ((volatile unsigned int *)(MMIO_BASE + 0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int *)(MMIO_BASE + 0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int *)(MMIO_BASE + 0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int *)(MMIO_BASE + 0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int *)(MMIO_BASE + 0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int *)(MMIO_BASE + 0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int *)(MMIO_BASE + 0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int *)(MMIO_BASE + 0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int *)(MMIO_BASE + 0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int *)(MMIO_BASE + 0x00215068)) + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() { + register unsigned int r; + + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) { + asm volatile("nop"); + } + /* initialize UART */ + *AUX_ENABLE |= 1; // enable UART1, AUX mini uart + *AUX_MU_IER = 0; + *AUX_MU_CNTL = 0; + *AUX_MU_LCR = 3; // 8 bits + *AUX_MU_MCR = 0; + *AUX_MU_IER = 0; + *AUX_MU_IIR = 0xc6; // disable interrupts + *AUX_MU_BAUD = 270; // 115200 baud + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx +} + +/** + * Send a character + */ +void uart_send(unsigned int c) { + /* wait until we can send */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); // transmitter idle + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +/** + * Receive a character + */ +char uart_getc() { + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // receiver overrun + /* read it and return */ + r = (char)(*AUX_MU_IO); + /* convert carrige return to newline */ + return r == '\r' ? '\n' : r; +} + +/** + * Receive a character without converting CR to LF + */ +char uart_getc_pure() { + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // receiver overrun + /* read it and return */ + r = (char)(*AUX_MU_IO); + return r; +} +/** + * Display an int + */ +void uart_int(int i) { + if (i < 0) { + uart_send('-'); + i = (~i) + 1; + } + char c[10]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} +/** + * Display an unsigned int + */ +void uart_uint(unsigned int i) { + char c[10]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} + +/** + * Display an unsigned long + */ +void uart_ulong(unsigned long i) { + char c[20]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} + +/** + * Display a string + */ +void uart_puts(char *s) { + while (*s) { + /* convert newline to carrige return + newline */ + if (*s == '\n') + uart_send('\r'); + uart_send(*s++); + } +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) { + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_hex_long(unsigned long d) { + unsigned long n; + int c; + for (c = 60; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +// This function is required by printf function +void putc(void *p, char c) { + if (c == '\n') + uart_send('\r'); + uart_send(c); +} \ No newline at end of file diff --git a/lab7/src/utils.c b/lab7/src/utils.c new file mode 100644 index 000000000..514bebe69 --- /dev/null +++ b/lab7/src/utils.c @@ -0,0 +1,137 @@ +#include "uart.h" +#include "utils.h" +#include "printf.h" + +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]; +}; + +void *get_user_program_address() { + struct cpio_newc_header *fs = (struct cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int is_file = 0; + while (1) { + fs = (struct cpio_newc_header *)current; + int name_size = hex_to_int(fs->c_namesize, 8); + int file_size = hex_to_int(fs->c_filesize, 8); + current += 110; // size of cpio_newc_header + if (strcmp(current, "user.img") == 0) + is_file = 1; + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + if (is_file) return (void *) current; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 != '\0' && *s1 == *s2) { + s1++; + s2++; + } + return (*(unsigned char *)s1) - (*(unsigned char *)s2); +} + +int hex_to_int(char *p, int len) { + int val = 0; + int temp; + for (int i = 0; i < len; i++) { + temp = *(p + i); + if (temp >= 'A') { + temp = temp - 'A' + 10; + } else + temp -= '0'; + val *= 16; + val += temp; + } + return val; +} + +void* simple_malloc(void **now, int size) { + void *ret = *now; + *now = *(char **)now + size; + return ret; +} + +int log2(int x) { + int ret = 0; + while(x != 1) { + if (x & 1) { + x += 1; + } + x >>= 1; + ret++; + } + return ret; +} + +int pow2(int x) { + return (1 << x); +} + +unsigned long cstr_to_ulong(char *s) { + unsigned long ret = 0; + while (*s != '\0') { + ret *= 10; + ret += (*s - '0'); + s++; + } + return ret; +} + +void strcpy(char *dest, const char *src) { + while (*src != '\0') { + *dest = *src; + src++; + dest++; + } + *dest = *src; +} + +void debug(char *s, int n) { + if (!DEBUG) return; + uart_puts(s); + uart_puts(": "); + uart_int(n); + uart_puts("\n"); +} + +unsigned long get_timestamp() { + register unsigned long f, c; + asm volatile ("mrs %0, cntfrq_el0" : "=r"(f)); // get current counter frequency + asm volatile ("mrs %0, cntpct_el0" : "=r"(c)); // read current counter + return (unsigned long) c; +} + +void assert(int e) { + if (e == 0) + printf("Assert Error!\n"); + else + printf("Assert\n"); +} + +int strlen(char *str) { + int len = 0; + while (*str != '\0') { + len++; + str++; + } + return len; +} \ No newline at end of file diff --git a/lab7/src/vfs.c b/lab7/src/vfs.c new file mode 100644 index 000000000..553b6ef8c --- /dev/null +++ b/lab7/src/vfs.c @@ -0,0 +1,235 @@ +#include "vfs.h" +#include "memory.h" +#include "printf.h" +#include "tmpfs.h" +#include "initramfs.h" +#include "typedef.h" +#include "utils.h" +#include "sched.h" + +struct mount *rootfs; + +void rootfs_init() { + struct filesystem *tmpfs = (struct filesystem *)malloc(sizeof(struct filesystem)); + tmpfs->name = (char *)malloc(sizeof(char) * 6); + strcpy(tmpfs->name, "tmpfs"); + tmpfs->setup_mount = tmpfs_setup_mount; + register_filesystem(tmpfs); + + rootfs = (struct mount *)malloc(sizeof(struct mount)); + tmpfs->setup_mount(tmpfs, rootfs); +} + +void mount_initramfs() { + vfs_mkdir("/initramfs"); + vfs_mount("/initramfs", "initramfs"); +} + +int register_filesystem(struct filesystem *fs) { + // register the file system to the kernel. + // you can also initialize memory pool of the file system here. + if (!strcmp(fs->name, "tmpfs")) { + printf("[%u] Register tmpfs\n", get_timestamp()); + return tmpfs_register(); + } + if(!strcmp(fs->name, "initramfs")) { + printf("[%u] Register initramfs\n", get_timestamp()); + return initramfs_register(); + } + return -1; +} + +void getdir_r(struct vnode* node, const char* path, struct vnode** target_node, char* target_path) { + // find next / + //printf("getdir_node [0x%x]\n", node); + if (!path[0]) { + //printf("getdir_ret [0x%x]\n", *target_node); + return; + } + int i = 0; + while (path[i]) { + if (path[i] == '/') break; + target_path[i] = path[i]; + i++; + } + target_path[i++] = '\0'; + // find in node's child + struct vnode *child_node = *target_node; + // edge cases check + *target_node = node; + int ret = node->v_ops->lookup(node, &child_node, target_path); + if (ret == 0) { + if (child_node->mount != NULL) { + printf("MOUNT %x\n", child_node->mount); + getdir_r(child_node->mount->root, path+i, target_node, target_path); + } + else { + getdir_r(child_node, path+i, target_node, target_path); + } + } + // for (int j = 0; j < MAX_ENTRIES; j++) { + // struct tmpfs_internal *child_node = node->child[j]; + // if (!child_node) continue; + // if (!strcmp(child_node->name, target_path)) { + // if (child_node->vnode->mount != NULL) { + // getdir_r(child_node->vnode->mount->root->internal, path + i, target_node, target_path); + // } + // else if (child_node->type == DIRECTORY) { + // getdir_r(child_node, path + i, target_node, target_path); + // } + // break; + // } + // } +} + +void getdir(const char* pathname, struct vnode** target_node, char* target_path) { + *target_node = rootfs->root; + if (pathname[0] == '/') { // absolute path + struct vnode* rootnode = rootfs->root; + getdir_r(rootnode, pathname + 1, target_node, target_path); + } + else { // relative path + struct vnode* rootnode = current_thread()->pwd; + getdir_r(rootnode, pathname, target_node, target_path); + } + printf("[Getdir] 0x%x\n", *target_node); +} + +int vfs_open(const char *pathname, int flags, struct file **target) { + // 1. Lookup pathname + struct vnode *target_dir; + char target_path[MAX_PATHNAME_LEN]; + getdir(pathname, &target_dir, target_path); + //printf("%s %s\n", pathname, target_path); + // 2. Create a new file handle for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags and vnode not found + // lookup error code shows if file exist or not or other error occurs + struct vnode *target_file; + int lookup_res = target_dir->v_ops->lookup(target_dir, &target_file, target_path); + if (lookup_res < 0) { + if (flags & O_CREAT) { + int create_res = rootfs->root->v_ops->create(target_dir, &target_file, target_path); + if (create_res < 0) return create_res; + + } + else + return -1; + } + printf("open: [0x%x]\n", target_file); + struct file *handle = malloc(sizeof(struct file)); + handle->vnode = target_file; + handle->f_pos = 0; + handle->f_ops = target_file->f_ops; + handle->flags = flags; + *target = handle; + return 0; + // 4. Return error code if fails + return -1; +} + +int vfs_close(struct file *file) { + // 1. release the file handle + if (!file) return -1; + free((void*)file); + // 2. Return error code if fails + return 0; +} + +int vfs_write(struct file *file, const void *buf, size_t len) { + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file, buf, len); +} + +int vfs_read(struct file *file, void *buf, size_t len) { + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 3. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +int vfs_mkdir(const char *pathname) { + struct vnode *target_dir; + char target_path[MAX_PATHNAME_LEN]; + getdir(pathname, &target_dir, target_path); + struct vnode *child_dir; + int mkdir_res = rootfs->root->v_ops->mkdir(target_dir, &child_dir, target_path); + if (mkdir_res < 0) return mkdir_res; + printf("%x\n", child_dir); + return 0; +} + +int vfs_mount(const char *target, const char *filesystem) { + // check mountpoint is valid + struct vnode* parent_dir; + char path_remain[MAX_PATHNAME_LEN]; + getdir(target, &parent_dir, path_remain); + printf("MOunt parent %x\n", parent_dir); + struct vnode *mount_dir; + int lookup_res = parent_dir->v_ops->lookup(parent_dir, &mount_dir, path_remain); + if (lookup_res < 0) return lookup_res; + //printf("[Mount] [%s]\n", ((struct tmpfs_internal *)mount_dir->internal)->name); + + if (!strcmp(filesystem, "tmpfs")) { + // mount fs on mountpoint + struct mount *mt = (struct mount*)malloc(sizeof(struct mount)); + struct filesystem* tmpfs = (struct filesystem*)malloc(sizeof(struct filesystem)); + tmpfs->name = (char*)malloc(sizeof(char) * strlen(filesystem)+1); + strcpy(tmpfs->name, filesystem); + tmpfs->setup_mount = &tmpfs_setup_mount; + tmpfs->setup_mount(tmpfs, mt); + mount_dir->mount = mt; + + mount_dir->mount->root->mount_parent = parent_dir; + } + if (!strcmp(filesystem, "initramfs")) { + struct mount *mt = (struct mount*)malloc(sizeof(struct mount)); + struct filesystem* tmpfs = (struct filesystem*)malloc(sizeof(struct filesystem)); + tmpfs->name = (char*)malloc(sizeof(char) * strlen(filesystem)+1); + strcpy(tmpfs->name, filesystem); + tmpfs->setup_mount = initramfs_setup_mount; + register_filesystem(tmpfs); + tmpfs->setup_mount(tmpfs, mt); + mount_dir->mount = mt; + + mount_dir->mount->root->mount_parent = parent_dir; + } + return 0; +} + +int vfs_lookup(const char *pathname, struct vnode **target) { + //printf("[Lookup] %s\n", pathname); + struct vnode *target_dir; + char target_path[MAX_PATHNAME_LEN]; + getdir(pathname, &target_dir, target_path); + struct vnode *target_file; + int lookup_res = target_dir->v_ops->lookup(target_dir, &target_file, target_path); + if (lookup_res < 0) return lookup_res; + *target = target_file; + return 0; +} + +int vfs_chdir(const char* pathname) { + if (!strcmp(pathname, "/")) { + current_thread()->pwd = rootfs->root; + return 0; + } + struct vnode* parent_dir; + char path_remain[128]; + path_remain[0] = '\0'; + getdir(pathname, &parent_dir, path_remain); + if (!strcmp(path_remain, "")) { // not found + return 0; + } + struct vnode *target_dir; + int lookup_res = parent_dir->v_ops->lookup(parent_dir, &target_dir, path_remain); + if (lookup_res < 0) return lookup_res; + else { + if (target_dir->mount != NULL) { + current_thread()->pwd = target_dir->mount->root; + } + else + current_thread()->pwd = target_dir; + return 0; + } +} \ No newline at end of file diff --git a/lab8/Makefile b/lab8/Makefile new file mode 100644 index 000000000..4664620ca --- /dev/null +++ b/lab8/Makefile @@ -0,0 +1,65 @@ +# +# Copyright (C) 2018 bzt (bztsrc@github) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# + +ARMGNU ?= aarch64-linux-gnu + +SRC_DIR = src +BUILD_DIR = obj + +C_FILES = $(wildcard $(SRC_DIR)/*.c) +ASM_FILES = $(wildcard $(SRC_DIR)/*.S) +OBJ_FILES = $(C_FILES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%_c.o) +OBJ_FILES += $(ASM_FILES:$(SRC_DIR)/%.S=$(BUILD_DIR)/%_s.o) +DEP_FILES = $(OBJ_FILES:%.o=%.d) +-include $(DEP_FILES) + +CFLAGS = -Wall -ffreestanding -nostdlib -nostartfiles -Iinclude -mgeneral-regs-only +ASMFLAGS = -Iinclude + +all: clean kernel8.img + +$(BUILD_DIR)/%_c.o: $(SRC_DIR)/%.c + $(ARMGNU)-gcc $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/%_s.o: $(SRC_DIR)/%.S + $(ARMGNU)-gcc $(ASMFLAGS) -c $< -o $@ + +kernel8.img: $(OBJ_FILES) + aarch64-linux-gnu-ld -nostdlib -nostartfiles $(OBJ_FILES) -T linker.ld -o kernel8.elf + aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img + +clean: + rm kernel8.elf *.o >/dev/null 2>/dev/null || true + rm -rf $(BUILD_DIR)/* || true + +run: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none + +cpio: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -initrd initramfs.cpio -drive if=sd,file=sfn_nctuos.img,format=raw + +debug: + qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio -display none -S -s + diff --git a/lab8/include/exception.h b/lab8/include/exception.h new file mode 100644 index 000000000..d614f7434 --- /dev/null +++ b/lab8/include/exception.h @@ -0,0 +1,52 @@ +#ifndef _EXCEPTION_H +#define _EXCEPTION_H + +#include "typedef.h" + +// save_all, load_all 的 register 用 struct 包起來,方便操作 +typedef struct Trapframe { + uint64_t x[31]; // general register from x0 ~ x30 + uint64_t sp_el0; + uint64_t elr_el1; + uint64_t spsr_el1; +} Trapframe; + +typedef 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]; +} cpio_newc_header; + +extern void enable_irq(); +extern void disable_irq(); +void sync_exception_router(uint64_t esr_el1, uint64_t elr_el1, Trapframe *trapframe); +void syscall(uint64_t syscall_num, Trapframe* trapframe); +void sys_getpid(Trapframe *trapframe); +void sys_uart_read(Trapframe *trapframe); +void sys_uart_write(Trapframe *trapframe); +void sys_exec(Trapframe *trapframe); +void sys_fork(Trapframe *trapframe); +void sys_exit(Trapframe *trapframe); +void sys_mbox_call(Trapframe *trapframe); +void sys_kill(Trapframe *trapframe); +void sys_open(Trapframe *trapframe); +void sys_close(Trapframe *trapframe); +void sys_write(Trapframe *trapframe); +void sys_read(Trapframe *trapframe); +void sys_mkdir(Trapframe *trapframe); +void sys_mount(Trapframe *trapframe); +void sys_chdir(Trapframe *trapframe); + + +#endif /* _EXCEPTION_H */ \ No newline at end of file diff --git a/lab8/include/fat32.h b/lab8/include/fat32.h new file mode 100644 index 000000000..83936fdd8 --- /dev/null +++ b/lab8/include/fat32.h @@ -0,0 +1,100 @@ +#ifndef _FAT32_H +#define _FAT32_H + +// Ref: https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Bootsector +// FAT32 Extended BIOS Parameter Block +#include "typedef.h" +#include "vfs.h" + +// block size +#define FAT_BLOCK_SIZE 512 +#define FAT_ENTRY_PER_BLOCK (FAT_BLOCK_SIZE / sizeof(int)) +#define EOC 0xFFFFFFF + +struct fat32_boot_sector { + char jump[3]; // 0x0 + char oem[8]; // 0x3 + + // BIOS Parameter Block (DOS2.0 BPB) + uint16_t bytes_per_logical_sector; // 0xB-0xC + uint8_t logical_sector_per_cluster; // 0xD + uint16_t n_reserved_sectors; // 0xE-0xF + uint8_t n_file_alloc_tabs; // 0x10 + uint16_t n_max_root_dir_entries_16; // 0x11-0x12 + uint16_t n_logical_sectors_16; // 0x13-0x14 + uint8_t media_descriptor; // 0x15 + uint16_t logical_sector_per_fat_16; // 0x16-0x17 + + // DOS3.31 BPB + uint16_t physical_sector_per_track; // 0x18-0x19 + uint16_t n_heads; // 0x1A-0x1B + uint32_t n_hidden_sectors; // 0x1C-0x1F + uint32_t n_sectors_32; // 0x20-0x23 + + // FAT32 Extended BIOS Parameter Block + uint32_t n_sectors_per_fat_32; // 0x24-0x27 + uint16_t mirror_flag; // 0x28-0x29 + uint16_t version; // 0x2A-0x2B + uint32_t root_dir_start_cluster_num; // 0x2C-0x2F + uint16_t fs_info_sector_num; // 0x30-0x31 + uint16_t boot_sector_bak_first_sector_num; // 0x32-0x33 + uint32_t reserved[3]; // 0x34-0x3F + uint8_t physical_drive_num; // 0x40 + uint8_t unused; // 0x41 + uint8_t extended_boot_signature; // 0x42 + uint32_t volume_id; // 0x43-0x46 + uint8_t volume_label[11]; // 0x47-0x51 + uint8_t fat_system_type[8]; // 0x52-0x59 +} __attribute__((packed)); + +struct fat32_metadata { + uint32_t fat_region_blk_idx; + uint32_t n_fat; + uint32_t sector_per_fat; + uint32_t data_region_blk_idx; + uint32_t first_cluster; + uint8_t sector_per_cluster; +}; + +struct fat32_dirent { + uint8_t name[8]; // 0x0-0x7 + uint8_t ext[3]; // 0x8-0xA + uint8_t attr; // 0xB + uint8_t reserved; // 0xC + uint8_t create_time[3]; // 0xD-0xF + uint16_t create_date; // 0x10-0x11 + uint16_t last_access_date; // 0x12-0x13 + uint16_t cluster_high; // 0x14-0x15 + uint32_t ext_attr; // 0x16-0x19 + uint16_t cluster_low; // 0x1A-0x1B + uint32_t size; // 0x1C-0x1F +} __attribute__((packed)); + +struct fat32_internal { + int type; + char *name; + struct vnode *vnode; + struct fat32_internal *parent; + struct fat32_internal *child[MAX_ENTRIES]; + uint32_t first_cluster; + uint32_t dirent_cluster; + uint32_t size; +}; + +extern struct fat32_metadata fat32_metadata; + +int fat32_register(); +int fat32_setup_mount(struct filesystem* fs, struct mount* mount); +struct vnode* fat32_create_vnode(struct fat32_internal* fat32_node); +// vnode operations +int fat32_lookup(struct vnode* dir, struct vnode** target, const char* component_name); +int fat32_create(struct vnode* dir, struct vnode** target, const char* component_name); +// int fat32_ls(struct vnode* dir); +// int fat32_mkdir(struct vnode* dir, struct vnode** target, const char* component_name); +// int fat32_load_dentry(struct dentry* dir, char* component_name); + +// file operations +int fat32_read(struct file* file, void* buf, uint64_t len); +int fat32_write(struct file* file, const void* buf, uint64_t len); + +#endif \ No newline at end of file diff --git a/lab8/include/freelist.h b/lab8/include/freelist.h new file mode 100644 index 000000000..665a1fd79 --- /dev/null +++ b/lab8/include/freelist.h @@ -0,0 +1,27 @@ +#ifndef FREELISTS_H +#define FREELISTS_H + +#define BELONG_LEFT -1 +#define ALLOCATED -2 +#define RESERVED -3 + +struct Node { + struct Node *next; + struct Node *prev; + int index; +}; + +struct Freelist { + struct Node *head; +}; + +typedef struct Node Node; +typedef struct Freelist Freelist; + +void freelist_init(Freelist *, Node *); +void freelist_push(Freelist *, Node *, int); +void freelist_remove(Freelist *, Node *, int); +void freelist_print(int, Freelist *); +void print_freelists(); + +#endif diff --git a/lab8/include/fs.h b/lab8/include/fs.h new file mode 100644 index 000000000..1bf1bb025 --- /dev/null +++ b/lab8/include/fs.h @@ -0,0 +1 @@ +extern struct filesystem tmpfs, initramfs, fat32; \ No newline at end of file diff --git a/lab8/include/gpio.h b/lab8/include/gpio.h new file mode 100644 index 000000000..52fa671d0 --- /dev/null +++ b/lab8/include/gpio.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#define MMIO_BASE 0x3F000000 + +#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C)) diff --git a/lab8/include/initramfs.h b/lab8/include/initramfs.h new file mode 100644 index 000000000..56f1699ba --- /dev/null +++ b/lab8/include/initramfs.h @@ -0,0 +1,25 @@ +#ifndef _INITRAMFS_H +#define _INITRAMFS_H + +#include "vfs.h" + +#define EOF (-1) + +struct initramfs_internal { + int type; + char *name; + struct vnode *vnode; + int size; + void *data; +}; + +int initramfs_register(); +int initramfs_setup_mount(struct filesystem* fs, struct mount* mount); +struct vnode* initramfs_create_vnode(struct initramfs_internal* initramfs_node); + +int initramfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name); +int initramfs_create(struct vnode* dir, struct vnode** target, const char* component_name); +int initramfs_write(struct file* file, const void* buf, size_t len); +int initramfs_read(struct file* file, void* buf, size_t len); +int initramfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name); +#endif \ No newline at end of file diff --git a/lab8/include/mbox.h b/lab8/include/mbox.h new file mode 100644 index 000000000..87bf5fb8c --- /dev/null +++ b/lab8/include/mbox.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/* a properly aligned buffer */ +//extern volatile unsigned int mbox[36]; + +#define MBOX_REQUEST 0 + +/* channels */ +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* tags */ +#define MBOX_TAG_GETSERIAL 0x10004 +#define MBOX_TAG_LAST 0 + +int mboxc_mbox_call(unsigned char ch, unsigned int *mbox); +int get_board_revision(); +int get_arm_memory(); diff --git a/lab8/include/mbr.h b/lab8/include/mbr.h new file mode 100644 index 000000000..5e271f156 --- /dev/null +++ b/lab8/include/mbr.h @@ -0,0 +1,15 @@ +#ifndef _MBR_H +#define _MBR_H + +struct mbr_partition { + unsigned char status_flag; //0x0 + unsigned char partition_begin_head; //0x1 + unsigned short partition_begin_sector; //0x2-0x3 + unsigned char partition_type; //0x4 + unsigned char partition_end_head; //0x5 + unsigned short partition_end_sector; //0x6-0x7 + unsigned int starting_sector; //0x8-0xB + unsigned int number_of_sector; //0xC-0xF +}; + +#endif diff --git a/lab8/include/memory.h b/lab8/include/memory.h new file mode 100644 index 000000000..899c64394 --- /dev/null +++ b/lab8/include/memory.h @@ -0,0 +1,38 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#define MEMORY_BASE 0x0 +#define CPIO_SIZE 247296 +#define PAGE_SIZE 0x1000 // 4KB +#define MAX_PAGES 0x40000 // total 0x40000000 +#define LOG2_MAX_PAGES 18 +#define LOG2_MAX_PAGES_PLUS_1 19 +#define NULL 0 +#include "freelist.h" +#include "typedef.h" + +struct block_meta { + int size; + short free; + short pagetail; + struct block_meta *next; +}; + +struct blocklist { + struct block_meta *head; +}; + +typedef struct block_meta block_meta; +typedef struct blocklist blocklist; +#define BLOCK_SIZE (sizeof(block_meta)) + +void memcpy(void *dest, void *src, uint64_t size); +void memory_init(void); +int find_allocate_list(Freelist *, int); +int allocate_page(Freelist *, Node *, int *, int, int); +void free_page(Freelist *, Node *, int *, int); +void *malloc(size_t); +void free(void *); +void reserve_memory(ulong start, ulong end); +void print_memory(); +#endif diff --git a/lab8/include/printf.h b/lab8/include/printf.h new file mode 100644 index 000000000..7b53b8421 --- /dev/null +++ b/lab8/include/printf.h @@ -0,0 +1,84 @@ +/* +File: printf.h +Copyright (C) 2004 Kustaa Nyholm +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +This library is really just two files: 'printf.h' and 'printf.c'. +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. +I've found them so usefull in debugging that I do not bother with a +debugger at all. +They are distributed in source form, so to use them, just compile them +into your project. +Two printf variants are provided: printf and sprintf. +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. +Zero padding and field width are also supported. +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. +To use the printf you need to supply your own character output function, +something like : + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } +Before you can call printf you need to initialize it to use your +character output function with something like: + init_printf(NULL,putc); +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. +For further details see source code. +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif \ No newline at end of file diff --git a/lab8/include/sched.h b/lab8/include/sched.h new file mode 100644 index 000000000..eac3e053d --- /dev/null +++ b/lab8/include/sched.h @@ -0,0 +1,60 @@ +#ifndef _SCHED_H +#define _SCHED_H +#include "typedef.h" +#include "vfs.h" + +struct cpu_context { + unsigned long x19; + unsigned long x20; + unsigned long x21; + unsigned long x22; + unsigned long x23; + unsigned long x24; + unsigned long x25; + unsigned long x26; + unsigned long x27; + unsigned long x28; + unsigned long fp; + unsigned long lr; + unsigned long sp; +}; + +struct Thread { + struct cpu_context cpu_context; + int state; + int counter; + int priority; + int preempt_count; + int pid; + int status; + uint64_t kernel_sp; + uint64_t user_sp; + struct vnode *pwd; + struct file *fd_table[MAX_FD_NUM]; +}; + +typedef struct Thread Thread; +#define INIT_TASK \ +/*cpu_context*/ { {0,0,0,0,0,0,0,0,0,0,0,0,0}, \ +/* state etc */ 0,0,1,0,0,0,0x80000,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} \ +} +#define NR_TASKS 64 +#define THREAD_SIZE 4096 + +#define TASK_RUNNING 0 +#define TASK_ZOMBIE 1 + +Thread *current_thread(); +int thread_create(void *func); +void preempt_disable(void); +void preempt_enable(void); +void _schedule(); +void schedule(); + +void idle(); +void timer_tick(); +void kill_zombies(); +void task_init(void); + + +#endif //_SCHED_H \ No newline at end of file diff --git a/lab8/include/sdhost.h b/lab8/include/sdhost.h new file mode 100644 index 000000000..13afb07cb --- /dev/null +++ b/lab8/include/sdhost.h @@ -0,0 +1,5 @@ + +void sd_init(); +void sd_mount(); +void readblock(int block_idx, void *buf); +void writeblock(int block_idx, void *buf); \ No newline at end of file diff --git a/lab8/include/shell.h b/lab8/include/shell.h new file mode 100644 index 000000000..b89f34fb4 --- /dev/null +++ b/lab8/include/shell.h @@ -0,0 +1,2 @@ +void shell(void); +void shell_input(char *input); \ No newline at end of file diff --git a/lab8/include/sys.h b/lab8/include/sys.h new file mode 100644 index 000000000..eaad7f615 --- /dev/null +++ b/lab8/include/sys.h @@ -0,0 +1,62 @@ +#ifndef _SYS_H +#define _SYS_H + +#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_MBOX_CALL 6 +#define SYS_KILL 7 + +#define SYS_OPEN 11 +#define SYS_CLOSE 12 +#define SYS_WRITE 13 +#define SYS_READ 14 +#define SYS_MKDIR 15 +#define SYS_MOUNT 16 +#define SYS_CHDIR 17 + +#endif + +#ifndef __ASSEMBLY__ + +#include "typedef.h" + +/* Function in sys.S */ +extern int getpid(); +extern size_t uartread(char buf[], size_t size); +extern size_t uartwrite(const char buf[], size_t size); +extern int exec(const char *name, char *const argv[]); +extern int fork(); +extern void exit(int status); +extern int mbox_call(unsigned char ch, unsigned int *mbox); +extern void kill(int pid); + +// syscall number : 11 +extern int open(const char *pathname, int flags); + +// syscall number : 12 +extern int close(int fd); + +// syscall number : 13 +// remember to return read size or error code +extern long write(int fd, const void *buf, unsigned long count); + +// syscall number : 14 +// remember to return read size or error code +extern long read(int fd, void *buf, unsigned long count); + +// syscall number : 15 +// you can ignore mode, since there is no access control +extern int mkdir(const char *pathname, unsigned mode); + +// syscall number : 16 +// you can ignore arguments other than target (where to mount) and filesystem (fs name) +extern int mount(const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); + +// syscall number : 17 +extern int chdir(const char *path); + +#endif \ No newline at end of file diff --git a/lab8/include/tmpfs.h b/lab8/include/tmpfs.h new file mode 100644 index 000000000..d6211494d --- /dev/null +++ b/lab8/include/tmpfs.h @@ -0,0 +1,29 @@ +#ifndef _TMPFS_H +#define _TMPFS_H + +#include "vfs.h" + +#define EOF (-1) + +struct tmpfs_internal { + int type; + char name[MAX_COMPONENT_NAME_LEN]; + struct tmpfs_internal *parent; + struct tmpfs_internal *child[MAX_ENTRIES]; + struct vnode *vnode; + int size; + void *data; +}; + +extern struct mount *rootfs; + +int tmpfs_register(); +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount); +struct vnode* tmpfs_create_vnode(struct tmpfs_internal* tmpfs_node); + +int tmpfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name); +int tmpfs_create(struct vnode* dir, struct vnode** target, const char* component_name); +int tmpfs_write(struct file* file, const void* buf, size_t len); +int tmpfs_read(struct file* file, void* buf, size_t len); +int tmpfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name); +#endif \ No newline at end of file diff --git a/lab8/include/typedef.h b/lab8/include/typedef.h new file mode 100644 index 000000000..7d8bb46e9 --- /dev/null +++ b/lab8/include/typedef.h @@ -0,0 +1,12 @@ +#ifndef _TYPEDEF_H +#define _TYPEDEF_H + +#define uint unsigned int +#define ulong unsigned long +#define uint64_t unsigned long +#define uint32_t unsigned int +#define uint16_t unsigned short +#define uint8_t unsigned char +#define size_t unsigned long +//#define DEBUG 0 +#endif /* _TYPEDEF_H */ \ No newline at end of file diff --git a/lab8/include/uart.h b/lab8/include/uart.h new file mode 100644 index 000000000..7217f4bba --- /dev/null +++ b/lab8/include/uart.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +void uart_init(); +void uart_send(unsigned int c); +char uart_getc(); +char uart_getc_pure(); +void uart_int(int i); +void uart_uint(unsigned int i); +void uart_ulong(unsigned long i); +void uart_puts(char *s); +void uart_hex(unsigned int d); +void uart_hex_long(unsigned long d); +void putc(void *p, char c); diff --git a/lab8/include/utils.h b/lab8/include/utils.h new file mode 100644 index 000000000..5bfe11bd2 --- /dev/null +++ b/lab8/include/utils.h @@ -0,0 +1,18 @@ +#ifndef UTILS_H +#define UTILS_H + +#define DEBUG 0 +int strcmp(const char *s1, const char *s2); +void strcpy(char *dest, const char *src); +int hex_to_int(char *p, int size); +void *get_user_program_address(); +int log2(int x); +int pow2(int x); +unsigned long cstr_to_ulong(char *s); +void* simple_malloc(void **now, int size); +void debug(char *, int); +unsigned long get_timestamp(); +void assert(int e); + +int strlen(char *str); +#endif \ No newline at end of file diff --git a/lab8/include/vfs.h b/lab8/include/vfs.h new file mode 100644 index 000000000..f4122a89a --- /dev/null +++ b/lab8/include/vfs.h @@ -0,0 +1,73 @@ +#ifndef _VFS_H +#define _VFS_H + +#include "typedef.h" + +#define O_CREAT 00000100 + +#define REGULAR_FILE 0 +#define DIRECTORY 1 + +#define MAX_PATHNAME_LEN 256 +#define MAX_FILESIZE 4096 +#define MAX_COMPONENT_NAME_LEN 16 // 15+'\0' +#define MAX_ENTRIES 16 +#define MAX_FD_NUM 16 + +struct mount { + struct vnode *root; + struct filesystem *fs; +}; + +struct vnode { + struct mount *mount; + struct vnode *mount_parent; + struct vnode_operations *v_ops; + struct file_operations *f_ops; + void *internal; +}; + +// file handle +struct file { + struct vnode *vnode; + size_t f_pos; // RW position of this file handle + struct file_operations *f_ops; + int flags; +}; + +struct filesystem { + const char *name; + int (*setup_mount)(struct filesystem *fs, struct mount *mount); +}; + +struct file_operations { + int (*write)(struct file *file, const void *buf, size_t len); + int (*read)(struct file *file, void *buf, size_t len); + int (*open)(struct vnode *file_node, struct file **target); + int (*close)(struct file *file); + //long lseek64(struct file *file, long offset, int whence); +}; + +struct vnode_operations { + int (*lookup)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*create)(struct vnode *dir_node, struct vnode **target, + const char *component_name); + int (*mkdir)(struct vnode *dir_node, struct vnode **target, + const char *component_name); +}; + +extern struct mount *rootfs; +void rootfs_init(void); +void mount_initramfs(void); +int register_filesystem(struct filesystem *fs); + +int vfs_open(const char *pathname, int flags, struct file **target); +int vfs_close(struct file *file); +int vfs_write(struct file *file, const void *buf, size_t len); +int vfs_read(struct file *file, void *buf, size_t len); +int vfs_mkdir(const char *pathname); +int vfs_chdir(const char *pathname); +int vfs_mount(const char *target, const char *filesystem); +int vfs_lookup(const char *pathname, struct vnode **target); +#endif diff --git a/lab8/initramfs.cpio b/lab8/initramfs.cpio new file mode 100644 index 000000000..27081bf16 Binary files /dev/null and b/lab8/initramfs.cpio differ diff --git a/lab8/kernel8.elf b/lab8/kernel8.elf new file mode 100644 index 000000000..5ca48aeab Binary files /dev/null and b/lab8/kernel8.elf differ diff --git a/lab8/kernel8.img b/lab8/kernel8.img new file mode 100644 index 000000000..5a180bcba Binary files /dev/null and b/lab8/kernel8.img differ diff --git a/lab8/linker.ld b/lab8/linker.ld new file mode 100644 index 000000000..58657923b --- /dev/null +++ b/lab8/linker.ld @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +SECTIONS +{ + . = 0x80000; + .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) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + . = ALIGN(16); + _end = .; + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} +__bss_size = (__bss_end - __bss_start)>>3; diff --git a/lab8/obj/exception_c.o b/lab8/obj/exception_c.o new file mode 100644 index 000000000..73b7212ae Binary files /dev/null and b/lab8/obj/exception_c.o differ diff --git a/lab8/obj/fat32_c.o b/lab8/obj/fat32_c.o new file mode 100644 index 000000000..9551b1b2e Binary files /dev/null and b/lab8/obj/fat32_c.o differ diff --git a/lab8/obj/freelist_c.o b/lab8/obj/freelist_c.o new file mode 100644 index 000000000..de18d23c3 Binary files /dev/null and b/lab8/obj/freelist_c.o differ diff --git a/lab8/obj/fs_c.o b/lab8/obj/fs_c.o new file mode 100644 index 000000000..6b10bb405 Binary files /dev/null and b/lab8/obj/fs_c.o differ diff --git a/lab8/obj/initramfs_c.o b/lab8/obj/initramfs_c.o new file mode 100644 index 000000000..c191ead20 Binary files /dev/null and b/lab8/obj/initramfs_c.o differ diff --git a/lab8/obj/main_c.o b/lab8/obj/main_c.o new file mode 100644 index 000000000..a882acefa Binary files /dev/null and b/lab8/obj/main_c.o differ diff --git a/lab8/obj/mbox_c.o b/lab8/obj/mbox_c.o new file mode 100644 index 000000000..95b25e244 Binary files /dev/null and b/lab8/obj/mbox_c.o differ diff --git a/lab8/obj/memory_c.o b/lab8/obj/memory_c.o new file mode 100644 index 000000000..78526c888 Binary files /dev/null and b/lab8/obj/memory_c.o differ diff --git a/lab8/obj/printf_c.o b/lab8/obj/printf_c.o new file mode 100644 index 000000000..6f104b605 Binary files /dev/null and b/lab8/obj/printf_c.o differ diff --git a/lab8/obj/sched_c.o b/lab8/obj/sched_c.o new file mode 100644 index 000000000..7970f91b9 Binary files /dev/null and b/lab8/obj/sched_c.o differ diff --git a/lab8/obj/sdhost_c.o b/lab8/obj/sdhost_c.o new file mode 100644 index 000000000..584686477 Binary files /dev/null and b/lab8/obj/sdhost_c.o differ diff --git a/lab8/obj/shell_c.o b/lab8/obj/shell_c.o new file mode 100644 index 000000000..15b0a8b95 Binary files /dev/null and b/lab8/obj/shell_c.o differ diff --git a/lab8/obj/start_s.o b/lab8/obj/start_s.o new file mode 100644 index 000000000..13c5fbd98 Binary files /dev/null and b/lab8/obj/start_s.o differ diff --git a/lab8/obj/sys_s.o b/lab8/obj/sys_s.o new file mode 100644 index 000000000..2e3fb06e8 Binary files /dev/null and b/lab8/obj/sys_s.o differ diff --git a/lab8/obj/tmpfs_c.o b/lab8/obj/tmpfs_c.o new file mode 100644 index 000000000..b9d518630 Binary files /dev/null and b/lab8/obj/tmpfs_c.o differ diff --git a/lab8/obj/uart_c.o b/lab8/obj/uart_c.o new file mode 100644 index 000000000..f88c90f1c Binary files /dev/null and b/lab8/obj/uart_c.o differ diff --git a/lab8/obj/utils_c.o b/lab8/obj/utils_c.o new file mode 100644 index 000000000..5916736e3 Binary files /dev/null and b/lab8/obj/utils_c.o differ diff --git a/lab8/obj/vfs_c.o b/lab8/obj/vfs_c.o new file mode 100644 index 000000000..3469b6862 Binary files /dev/null and b/lab8/obj/vfs_c.o differ diff --git a/lab8/sfn_nctuos.img b/lab8/sfn_nctuos.img new file mode 100644 index 000000000..1f59ece89 Binary files /dev/null and b/lab8/sfn_nctuos.img differ diff --git a/lab8/src/exception.c b/lab8/src/exception.c new file mode 100644 index 000000000..835c6550d --- /dev/null +++ b/lab8/src/exception.c @@ -0,0 +1,334 @@ +// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1- + +#include "utils.h" +#include "exception.h" +#include "sys.h" +#include "printf.h" +#include "sched.h" +#include "memory.h" +#include "uart.h" +#include "mbox.h" +#include "vfs.h" +#include "tmpfs.h" + +extern void end_thread(void); +extern void ret_from_fork(void); +extern Thread *task[]; +extern void delay(int); + +void sync_exc_router(uint64_t esr_el1, uint64_t elr_el1, Trapframe *trapframe) { + int ec = (esr_el1 >> 26) & 0b111111; + int iss = esr_el1 & 0x1FFFFFF; + if (ec == 0b010101) { // is system call + uint64_t syscall_num = trapframe->x[8]; + //printf("[SYSCALL] %d\n", syscall_num); + syscall(syscall_num, trapframe); + } + else { + printf("Exception return address 0x%x\n", elr_el1); + printf("Exception class (EC) 0x%x\n", ec); + printf("Instruction specific syndrome (ISS) 0x%x\n", iss); + while(1){ + ; + } + } +} + +void syscall(uint64_t syscall_num, Trapframe* trapframe) { + switch (syscall_num) { + case SYS_GETPID: + sys_getpid(trapframe); + break; + + case SYS_UART_READ: + sys_uart_read(trapframe); + break; + + case SYS_UART_WRITE: + sys_uart_write(trapframe); + break; + + case SYS_EXEC: + sys_exec(trapframe); + break; + + case SYS_FORK: + sys_fork(trapframe); + break; + + case SYS_EXIT: + sys_exit(trapframe); + break; + + case SYS_MBOX_CALL: + sys_mbox_call(trapframe); + break; + case SYS_KILL: + sys_kill(trapframe); + break; + + case SYS_OPEN: + sys_open(trapframe); + break; + + case SYS_CLOSE: + sys_close(trapframe); + break; + + case SYS_WRITE: + sys_write(trapframe); + break; + + case SYS_READ: + sys_read(trapframe); + break; + + case SYS_MKDIR: + sys_mkdir(trapframe); + break; + + case SYS_MOUNT: + sys_mount(trapframe); + break; + + case SYS_CHDIR: + sys_chdir(trapframe); + break; + } + return; +} + +void sys_getpid(Trapframe *trapframe) { + trapframe->x[0] = current_thread()->pid; +} + +void sys_uart_read(Trapframe *trapframe) { + char *buf = (char *)trapframe->x[0]; + size_t size = (size_t)trapframe->x[1]; + enable_irq(); // 避免卡在 read 裡 + for (int i = 0; i < size; i++) { + *(buf + i) = uart_getc(); + } + disable_irq(); + trapframe->x[0] = size; +} + +void sys_uart_write(Trapframe *trapframe) { + const char *buf = (const char *)trapframe->x[0]; + size_t size = (size_t)trapframe->x[1]; + enable_irq(); + for (int i = 0; i < size; i++) { + uart_send(*(buf + i)); + } + disable_irq(); + trapframe->x[0] = size; +} + +void sys_exec(Trapframe *trapframe) { + preempt_disable(); + char *input = (char *)trapframe->x[0]; + char *program_pos; + cpio_newc_header *fs = (cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int name_size; + int file_size; + while (1) { + fs = (cpio_newc_header *)current; + name_size = hex_to_int(fs->c_namesize, 8); + file_size = hex_to_int(fs->c_filesize, 8); + current += 110; + if (strcmp(current, "TRAILER!!!") == 0) { + uart_puts("No such file!\n"); + break; + } + if (strcmp(current, input) == 0) { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + program_pos = (char *)current; + break; + } else { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + } + char *new_program_pos = (char *)malloc(file_size); + for (int i = 0; i < file_size; i++) { + *(new_program_pos+i) = *(program_pos+i); + } + printf("program pos : %x\n", new_program_pos); + Thread *cur = current_thread(); + preempt_enable(); + asm volatile("msr sp_el0, %0" : : "r"(cur->user_sp)); + asm volatile("msr elr_el1, %0": : "r"(new_program_pos)); + asm volatile("msr spsr_el1, %0" : : "r"(0x0)); + asm volatile("eret"); + trapframe->x[0] = 0; +} + +void sys_fork(Trapframe *trapframe) { + Thread *parent = current_thread(); + /* + ret_from_fork 會把 child_trapframe load to register, + 這樣跑 child thread 時就會用到 child_trapframe 更改的 sp + */ + int newpid = thread_create(ret_from_fork); + + Thread *child = task[newpid]; + + printf("child: %x\n", child); + + // copy kernel stack and user stack + uint64_t kstack_offset = (char *)parent->kernel_sp - (char *)trapframe; + uint64_t ustack_offset = (char *)parent->user_sp - (char *)trapframe->sp_el0; + + // copy kernel stack (including trapframe) + for (uint64_t i = 1; i <= kstack_offset; i++) { + *((char *)(child->kernel_sp - i)) = *((char *)(parent->kernel_sp - i)); + } + + // copy user stack + for (uint64_t i = 1; i <= ustack_offset; i++) { + *((char *)(child->user_sp - i)) = *((char *)(parent->user_sp - i)); + } + + child->cpu_context.sp = child->kernel_sp - kstack_offset; + + Trapframe *child_trapframe = (Trapframe *)child->cpu_context.sp; + child_trapframe->sp_el0 = child->user_sp - ustack_offset; + printf("child sp: %x\n", child_trapframe->sp_el0); + + trapframe->x[0] = child->pid; + child_trapframe->x[0] = 0; +} + +void sys_exit(Trapframe *trapframe) { + current_thread()->status = trapframe->x[0]; + end_thread(); +} + +void sys_mbox_call(Trapframe *trapframe) { + unsigned char ch = (unsigned char)trapframe->x[0]; + unsigned int *mbox = (unsigned int *)trapframe->x[1]; + int ret = mboxc_mbox_call(ch, mbox); // defined in mbox.c + trapframe->x[0] = ret; +} + +void sys_kill(Trapframe *trapframe) { + int pid = trapframe->x[0]; + task[pid]->state = TASK_ZOMBIE; +} + +void sys_open(Trapframe *trapframe) { + printf("Open\n"); + char *path = trapframe->x[0]; + int flags = trapframe->x[1]; + struct file *handle; + int ret = vfs_open(path, flags, &handle); + if (ret < 0) { + printf("[Open] %s %d %d\n", path, flags, ret); + trapframe->x[0] = -1; + return; + } + for (int i = 0; i < MAX_FD_NUM; i++) { + if (!current_thread()->fd_table[i]) { + current_thread()->fd_table[i] = handle; + trapframe->x[0] = i; + printf("[Open] %s %d %d\n", path, flags, i); + return; + } + } + trapframe->x[0] = -1; + printf("[Open] %s %d %d\n", path, flags, trapframe->x[0]); +} + +void sys_close(Trapframe *trapframe) { + printf("Close\n"); + int fd = trapframe->x[0]; + if (fd < 0) { + trapframe->x[0] = -1; + return; + } + struct file *handle = current_thread()->fd_table[fd]; + int ret = vfs_close(handle); + //current_thread()->fd_table[fd] = NULL; + trapframe->x[0] = ret; + printf("[Close] %d %d\n", fd, ret); +} + +void sys_write(Trapframe *trapframe) { + printf("[Write] %d\n", trapframe->x[0]); + int fd = trapframe->x[0]; + char *buf = (char *)trapframe->x[1]; + unsigned long count = trapframe->x[2]; + if (fd < 0) { + trapframe->x[0] = -1; + printf("[Write] %d\n", trapframe->x[0]); + return; + } + struct file *handle = current_thread()->fd_table[fd]; + if (handle == NULL) { + trapframe->x[0] = 0; + printf("[Write] %d %d\n", fd, trapframe->x[0]); + return; + } + trapframe->x[0] = vfs_write(handle, buf, count); + printf("[Write] %d %d\n", fd, trapframe->x[0]); +} + +void sys_read(Trapframe *trapframe) { + int fd = trapframe->x[0]; + char *buf = (char *)trapframe->x[1]; + unsigned long count = trapframe->x[2]; + printf("[Read] %d %d\n", fd, count); + if (fd < 0) { + trapframe->x[0] = -1; + return; + } + struct file *handle = current_thread()->fd_table[fd]; + if (handle == NULL) { + trapframe->x[0] = 0; + return; + } + trapframe->x[0] = vfs_read(handle, buf, count); + printf("[Read] %d\n", trapframe->x[0]); + +} + +void sys_mkdir(Trapframe *trapframe) { + printf("Mkdir\n"); + char *pathname = trapframe->x[0]; + unsigned mode = trapframe->x[1]; + trapframe->x[0] = vfs_mkdir(pathname); + printf("[Mkdir] %s %d\n", pathname, trapframe->x[0]); +} + +void sys_mount(Trapframe *trapframe) { + printf("Mount\n"); + char *src = trapframe->x[0]; + char *target = trapframe->x[1]; + char *filesystem = trapframe->x[2]; + unsigned long flags = trapframe->x[3]; + void *data = trapframe->x[4]; + trapframe->x[0] = vfs_mount(target, filesystem); + printf("[Mount] %s %s %d\n", target, filesystem, trapframe->x[0]); +} + +void sys_chdir(Trapframe *trapframe) { + printf("Chdir\n"); + char *pathname = trapframe->x[0]; + trapframe->x[0] = vfs_chdir(pathname); + printf("[Chdir] %s %d\n", pathname, trapframe->x[0]); +} + +void timer_interrupt(int i) { + unsigned long cntfrq_el0; + asm volatile ("mrs %0, cntfrq_el0":"=r" (cntfrq_el0)); + asm volatile ("lsr %0, %0, #5":"=r" (cntfrq_el0) :"r"(cntfrq_el0)); // 1/32 second tick + asm volatile ("msr cntp_tval_el0, %0" : : "r"(cntfrq_el0)); + timer_tick(); +} diff --git a/lab8/src/fat32.c b/lab8/src/fat32.c new file mode 100644 index 000000000..60a3ff65d --- /dev/null +++ b/lab8/src/fat32.c @@ -0,0 +1,270 @@ +#include "fat32.h" +#include "utils.h" +#include "typedef.h" +#include "memory.h" +#include "printf.h" +#include "sdhost.h" + +struct fat32_metadata fat32_metadata; +struct vnode_operations* fat32_v_ops = NULL; +struct file_operations* fat32_f_ops = NULL; + +uint32_t get_cluster_blk_idx(uint32_t cluster_idx) { + return fat32_metadata.data_region_blk_idx + + (cluster_idx - fat32_metadata.first_cluster) * fat32_metadata.sector_per_cluster; +} + +uint32_t get_fat_blk_idx(uint32_t cluster_idx) { + return fat32_metadata.fat_region_blk_idx + (cluster_idx / FAT_ENTRY_PER_BLOCK); +} + +int fat32_register() { + if (fat32_v_ops != NULL && fat32_f_ops != NULL) { + return -1; + } + fat32_v_ops = (struct vnode_operations*)malloc(sizeof(struct vnode_operations)); + //fat32_v_ops->create = fat32_create; + fat32_v_ops->lookup = fat32_lookup; + fat32_v_ops->create = fat32_create; + //fat32_v_ops->ls = fat32_ls; + //fat32_v_ops->mkdir = fat32_mkdir; + fat32_f_ops = (struct file_operations*)malloc(sizeof(struct file_operations)); + fat32_f_ops->read = fat32_read; + fat32_f_ops->write = fat32_write; + return 0; +} + +int fat32_setup_mount(struct filesystem* fs, struct mount* mount) { + struct fat32_internal* fat32_root = (struct fat32_internal*)malloc(sizeof(struct fat32_internal)); + fat32_root->type = DIRECTORY; + fat32_root->name = (char *)malloc(2); + strcpy(fat32_root->name, "/"); + struct vnode *root_vnode = fat32_create_vnode(fat32_root); + mount->root = root_vnode; + mount->fs = fs; + fat32_root->vnode = root_vnode; + return 0; +} + +struct vnode* fat32_create_vnode(struct fat32_internal* fat32_node) { + struct vnode* vnode = (struct vnode*)malloc(sizeof(struct vnode)); + vnode->f_ops = fat32_f_ops; + vnode->v_ops = fat32_v_ops; + vnode->internal = fat32_node; + return vnode; +} + +int fat32_lookup(struct vnode* dir, struct vnode** target, const char* component_name) { + // component_name is empty, return dir vnode + *target = dir; + printf("[fat32 lookup] %s %x %x %x\n", component_name, dir, dir->mount, dir->mount_parent); + if (!strcmp(component_name, "")) { + *target = dir; + return 0; + } + // search component_name in dir + if (dir->mount != NULL) { + dir = dir->mount->root; + } + printf("target: %s\n", component_name); + // TODO: second search + char buf[FAT_BLOCK_SIZE]; + struct fat32_internal* dir_internal = (struct fat32_internal*)dir->internal; + printf("internal %x\n", dir_internal); + //if (dir_internal->type != DIRECTORY) return 0; + uint32_t dirent_cluster = get_cluster_blk_idx(dir_internal->first_cluster); + readblock(dirent_cluster, buf); + printf("buf %d\n", dirent_cluster); + // parse + struct fat32_dirent* sector_dirent = (struct fat32_dirent*)buf; + int idx = 0; + // load all children under dentry + int found = -1; + for (int i = 0; sector_dirent[i].name[0] != '\0'; i++) { + // special value + if (sector_dirent[i].name[0] == 0xE5) { + continue; + } + // get filename + char filename[13]; + int len = 0; + for (int j = 0; j < 8; j++) { + char c = sector_dirent[i].name[j]; + if (c == ' ') { + break; + } + filename[len++] = c; + } + filename[len++] = '.'; + for (int j = 0; j < 3; j++) { + char c = sector_dirent[i].ext[j]; + if (c == ' ') { + break; + } + filename[len++] = c; + } + filename[len++] = 0; + printf("%s\n", filename); + if (!strcmp(filename, component_name)) { + printf("found %s\n", component_name); + found = 0; + // create fat32 internal + struct fat32_internal* child_internal = (struct fat32_internal*)malloc(sizeof(struct fat32_internal)); + child_internal->first_cluster = ((sector_dirent[i].cluster_high) << 16) | (sector_dirent[i].cluster_low); + child_internal->dirent_cluster = dirent_cluster; + child_internal->size = sector_dirent[i].size; + if (sector_dirent[i].attr == 0x10) { // directory + child_internal->type = DIRECTORY; + } + else { // file + child_internal->type = REGULAR_FILE; + } + //child_internal->name = malloc(strlen(component_name)+1); + //strcpy(child_internal->name, component_name); + // create vnode + struct vnode* file; + file = fat32_create_vnode(child_internal); + child_internal->vnode = file; + *target = file; + } + } + return found; +} + +int fat32_create(struct vnode* dir, struct vnode** target, const char* component_name) { + printf("[Create]\n"); + char buf[FAT_BLOCK_SIZE]; + struct fat32_internal* dir_internal = (struct fat32_internal*)dir->internal; + printf("internal %x\n", dir_internal); + uint32_t dirent_cluster = get_cluster_blk_idx(dir_internal->first_cluster); + readblock(dirent_cluster, buf); + printf("buf %d\n", dirent_cluster); + struct fat32_dirent* sector_dirent = (struct fat32_dirent*)buf; + int idx = 5; + // while (sector_dirent[idx].name[0] != '\0') idx++; + //printf("insert %d\n", idx); + //memzero(sector_dirent+idx, 8); + int t = 0, e = 0; + while (component_name[t] != '.') { + sector_dirent[idx].name[t] = component_name[t]; + t++; + } + e = t+1; + while (t < 8) { + sector_dirent[idx].name[t] = 0x20; + t++; + } + while (component_name[e] != '\0') { + sector_dirent[idx].ext[t-8] = component_name[e]; + e++; + t++; + } + sector_dirent[idx].attr = 0x20; + sector_dirent[idx].cluster_low = sector_dirent[idx-1].cluster_low + (uint16_t)((sector_dirent[idx-1].size + FAT_BLOCK_SIZE -1) / FAT_BLOCK_SIZE); + printf("%d\n", sector_dirent[idx].cluster_low); + writeblock(dirent_cluster, sector_dirent); + return 0; +} + +// file operations +int fat32_read(struct file* file, void* ret, uint64_t len) { + struct fat32_internal* file_node = (struct fat32_internal*)file->vnode->internal; + uint64_t f_pos_ori = file->f_pos; + uint32_t current_cluster = file_node->first_cluster; + printf("Current cluster: %u\n", current_cluster); + int remain_len = len; + int fat[FAT_ENTRY_PER_BLOCK]; + char buf[512]; + while (remain_len > 0 && current_cluster >= fat32_metadata.first_cluster && current_cluster != EOC) { + printf("%d\n", get_cluster_blk_idx(current_cluster)); + readblock(get_cluster_blk_idx(current_cluster), ((char *)buf)+file->f_pos); + for (int i = 0; i < 512; i++) { + if (buf[i] == '\0' || remain_len-- < 0) break; + ((char *)ret)[file->f_pos++] = buf[i]; + } + //file->f_pos += (remain_len < FAT_BLOCK_SIZE) ? remain_len : FAT_BLOCK_SIZE; + //remain_len -= FAT_BLOCK_SIZE; + + // update cluster number from FAT + if (remain_len > 0) { + readblock(get_fat_blk_idx(current_cluster), fat); + current_cluster = fat[current_cluster % FAT_ENTRY_PER_BLOCK]; + } + } + //return 10; + return (file->f_pos - f_pos_ori); +} + +int fat32_write(struct file* file, const void* buf, uint64_t len) { + struct fat32_internal* file_node = (struct fat32_internal*)file->vnode->internal; + uint64_t f_pos_ori = file->f_pos; + int fat[FAT_ENTRY_PER_BLOCK]; + char write_buf[FAT_BLOCK_SIZE]; + + // traversal to target cluster using f_pos + uint32_t current_cluster = file_node->first_cluster; + int remain_offset = file->f_pos; + while (remain_offset > 0 && current_cluster >= fat32_metadata.first_cluster && current_cluster != EOC) { + remain_offset -= FAT_BLOCK_SIZE; + if (remain_offset > 0) { + readblock(get_fat_blk_idx(current_cluster), fat); + current_cluster = fat[current_cluster % FAT_ENTRY_PER_BLOCK]; + } + } + + // write first block, handle f_pos + int buf_idx, f_pos_offset = file->f_pos % FAT_BLOCK_SIZE; + + printf("write %d %d %d\n", buf_idx, f_pos_offset, get_cluster_blk_idx(current_cluster)); + readblock(get_cluster_blk_idx(current_cluster), write_buf); + for (buf_idx = 0; buf_idx < FAT_BLOCK_SIZE - f_pos_offset && buf_idx < len; buf_idx++) { + write_buf[buf_idx + f_pos_offset] = ((char*)buf)[buf_idx]; + } + printf("w%d\n", get_cluster_blk_idx(current_cluster)); + writeblock(get_cluster_blk_idx(current_cluster), write_buf); + file->f_pos += buf_idx; + + // write complete block + int remain_len = len - buf_idx; + while (remain_len > 0 && current_cluster >= fat32_metadata.first_cluster && current_cluster != EOC) { + // write block + printf("w%d\n", get_cluster_blk_idx(current_cluster)); + writeblock(get_cluster_blk_idx(current_cluster), buf + buf_idx); + file->f_pos += (remain_len < FAT_BLOCK_SIZE) ? remain_len : FAT_BLOCK_SIZE; + remain_len -= FAT_BLOCK_SIZE; + buf_idx += FAT_BLOCK_SIZE; + + // update cluster number from FAT + if (remain_len > 0) { + readblock(get_fat_blk_idx(current_cluster), fat); + current_cluster = fat[current_cluster % FAT_ENTRY_PER_BLOCK]; + } + } + + // TODO: last block also need to handle remainder + + // update file size + printf("update, %d %d %d\n", file->f_pos, file_node->size, file_node->dirent_cluster); + if (file->f_pos > file_node->size) { + file_node->size = file->f_pos; + + // update directory entry + uint8_t sector[FAT_BLOCK_SIZE]; + readblock(file_node->dirent_cluster, sector); + struct fat32_dirent* sector_dirent = (struct fat32_dirent*)sector; + for (int i = 0; sector_dirent[i].name[0] != '\0'; i++) { + // special value + if (sector_dirent[i].name[0] == 0xE5) { + continue; + } + // find target file directory entry + if ((((sector_dirent[i].cluster_high) << 16) | (sector_dirent[i].cluster_low)) == file_node->first_cluster) { + sector_dirent[i].size = (uint32_t)file->f_pos; + } + } + printf("w%d\n", file_node->dirent_cluster); + writeblock(file_node->dirent_cluster, sector); + } + + return file->f_pos - f_pos_ori; +} \ No newline at end of file diff --git a/lab8/src/freelist.c b/lab8/src/freelist.c new file mode 100644 index 000000000..87fcdfd3e --- /dev/null +++ b/lab8/src/freelist.c @@ -0,0 +1,70 @@ +#include "memory.h" +#include "freelist.h" +#include "uart.h" +#include "utils.h" +#include "printf.h" + +extern Freelist *heads; +extern int *frame_array; + +void freelist_push(Freelist *list, Node *nodes, int num) { + nodes[num].next = NULL; + nodes[num].prev = NULL; + if (!list->head) { + list->head = &nodes[num]; + return; + } + Node *node = list->head; + node->prev = &nodes[num]; + nodes[num].next = node; + list->head = &nodes[num]; + return; +} + +void freelist_remove(Freelist *list, Node *nodes, int num) { + Node *current = &nodes[num]; + Node *pre = current->prev; + // Remove the target by updating the head or the previous node. + if (pre==NULL) { + list->head = current->next; + if (current->next != NULL) { + current->next->prev = pre; + } + } + else { + if (current->next == NULL) { + pre->next = current->next; + } + else { + current->next->prev = pre; + pre->next = current->next; + } + } + current->prev = NULL; + current->next = NULL; +} + +void print_freelists() { + uart_puts("-----------Freelists------------\n"); + for(int i = LOG2_MAX_PAGES; i >= 0; i--) { + freelist_print(i, &heads[i]); + } + // uart_puts("-------------Pages--------------\n"); + // for (int j = 0; j < (MAX_PAGES/16); j++) { + // for (int i = 0; i < 16; i++) { + // uart_int(frame_array[16*j+i]); + // uart_puts(" "); + // } + // uart_puts("\n"); + // } +} +void freelist_print(int level, Freelist *list) { + uart_puts("Level "); + uart_int(level); + uart_puts(": "); + for (Node *node = list->head; node != NULL; node = node->next) { + uart_uint(node->index); + uart_puts(" "); + } + uart_puts("\n"); +} diff --git a/lab8/src/fs.c b/lab8/src/fs.c new file mode 100644 index 000000000..d4d4b7cfb --- /dev/null +++ b/lab8/src/fs.c @@ -0,0 +1,19 @@ +#include "vfs.h" +#include "tmpfs.h" +#include "initramfs.h" +#include "fat32.h" + +struct filesystem tmpfs = { + .name = "tmpfs", + .setup_mount = tmpfs_setup_mount, +}; + +struct filesystem initramfs = { + .name = "initramfs", + .setup_mount = initramfs_setup_mount, +}; + +struct filesystem fat32 = { + .name = "fat32", + .setup_mount = fat32_setup_mount, +}; \ No newline at end of file diff --git a/lab8/src/initramfs.c b/lab8/src/initramfs.c new file mode 100644 index 000000000..c01c41a5e --- /dev/null +++ b/lab8/src/initramfs.c @@ -0,0 +1,149 @@ +#include "memory.h" +#include "vfs.h" +#include "initramfs.h" +#include "printf.h" +#include "utils.h" +#include "exception.h" + +struct vnode_operations* initramfs_v_ops; +struct file_operations* initramfs_f_ops; + +int initramfs_register() { + initramfs_v_ops = (struct vnode_operations*)malloc(sizeof(struct vnode_operations)); + initramfs_v_ops->lookup = &initramfs_lookup; + initramfs_v_ops->create = &initramfs_create; + initramfs_v_ops->mkdir = &initramfs_mkdir; + initramfs_f_ops = (struct file_operations*)malloc(sizeof(struct file_operations)); + initramfs_f_ops->write = &initramfs_write; + initramfs_f_ops->read = &initramfs_read; + return 0; +} + +int initramfs_setup_mount(struct filesystem* fs, struct mount* mount) { + struct initramfs_internal* initramfs_root = (struct initramfs_internal*)malloc(sizeof(struct initramfs_internal)); + initramfs_root->type = DIRECTORY; + initramfs_root->name = (char *)malloc(2); + strcpy(initramfs_root->name, "/"); + struct vnode *root_vnode = initramfs_create_vnode(initramfs_root); + mount->root = root_vnode; + mount->fs = fs; + initramfs_root->vnode = root_vnode; + return 0; +} + +struct vnode* initramfs_create_vnode(struct initramfs_internal* initramfs_node) { + struct vnode* vnode = (struct vnode*)malloc(sizeof(struct vnode)); + vnode->f_ops = initramfs_f_ops; + vnode->v_ops = initramfs_v_ops; + vnode->internal = initramfs_node; + return vnode; +} + +int initramfs_create(struct vnode* dir, struct vnode** target, const char* component_name) { + return -1; +} + +// vnode operations +int initramfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name) { + // component_name is empty, return dir vnode + if (!strcmp(component_name, "")) { + return -1; + } + else if (!strcmp(component_name, ".")) { + *target = dir; + return 0; + } + else if (!strcmp(component_name, "..")) { // todo: cross filesystem + *target = dir; + if (dir->mount_parent) { + *target = dir->mount_parent; + return 0; + } + return -1; + } + // search component_name in dir + if (dir->mount != NULL) + dir = dir->mount->root; + char *program_pos; + cpio_newc_header *fs = (cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int name_size; + int file_size; + char *name_pos; + while (1) { + fs = (cpio_newc_header *)current; + name_size = hex_to_int(fs->c_namesize, 8); + file_size = hex_to_int(fs->c_filesize, 8); + current += 110; + if (strcmp(current, "TRAILER!!!") == 0) { + printf("No such file!\n"); + break; + } + name_pos = current; + if (strcmp(current, component_name) == 0) { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + program_pos = (char *)current; + break; + } else { + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + } + char *new_program_pos = (char *)malloc(file_size); + for (int i = 0; i < file_size; i++) { + *(new_program_pos+i) = *(program_pos+i); + } + printf("name pos : %x\n", name_pos); + printf("program pos : %x\n", new_program_pos); + struct initramfs_internal* file_node = (struct initramfs_internal*)malloc(sizeof(struct initramfs_internal)); + file_node->type = REGULAR_FILE; + file_node->name = name_pos; + file_node->size = file_size; + file_node->data = (void *)new_program_pos; + file_node->vnode = initramfs_create_vnode(file_node); + *target = file_node->vnode; + // for (int i = 0; i < MAX_ENTRIES; i++) { + // struct initramfs_internal* file_node = ((struct initramfs_internal*)dir->internal)->child[i]; + // if ((file_node != NULL) & !strcmp(file_node->name, component_name)) { + // *target = file_node->vnode; + // printf("[lookup] 0x%x\n", *target); + // return 0; + // } + // } + // *target = NULL; + return 0; +} + +int initramfs_write(struct file* file, const void* buf, size_t len) { + return -1; +} + +int initramfs_read(struct file* file, void* buf, size_t len) { + if (((struct initramfs_internal *)file->vnode->internal)->type != REGULAR_FILE) { + printf("Read on not regular file\n"); + return -1; + } + struct initramfs_internal* file_node = (struct initramfs_internal *)file->vnode->internal; + + char *dest = (char*)buf; + char *src = &(file_node->data[file->f_pos]); + size_t i = 0; + for (; i < len && file->f_pos < file_node->size; i++) { + dest[i] = src[i]; + file->f_pos++; + } + return i; + +} + +int initramfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name) { + return -1; +} + + diff --git a/lab8/src/main.c b/lab8/src/main.c new file mode 100644 index 000000000..6fd841e8c --- /dev/null +++ b/lab8/src/main.c @@ -0,0 +1,103 @@ +#include "uart.h" +#include "utils.h" +#include "freelist.h" +#include "memory.h" +#include "sched.h" +#include "printf.h" +#include "typedef.h" +#include "sys.h" +#include "mbox.h" +#include "shell.h" +#include "vfs.h" +#include "tmpfs.h" +#include "sdhost.h" +#define N 5 + +extern void delay(); +extern void core_timer_enable(); + + +void cpu_timer_register_enable() { // 讓 el0 用 clock 不會 interrupt + uint64_t tmp; + asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); + tmp |= 1; + asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); +} + +void fork_test(){ + printf("\nFork Test, pid %d\n", getpid()); + int cnt = 1; + int ret = 0; + if ((ret = fork()) == 0) { // child + long long cur_sp; + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + ++cnt; + if ((ret = fork()) != 0){ + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + } + else{ + while (cnt < 5) { + asm volatile("mov %0, sp" : "=r"(cur_sp)); + printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", getpid(), cnt, &cnt, cur_sp); + delay(1000000); + ++cnt; + } + } + exit(0); + } + else { + unsigned int __attribute__((aligned(16))) mbox[36]; + get_board_revision(mbox); + mbox_call(MBOX_CH_PROP, mbox); + for (int i = 0; i < 8; i++) { + printf("mbox %d: %x\n", i, mbox[i]); + } + printf("parent here, pid %d, child %d\n", getpid(), ret); + } +} + +void foo(){ + for(int i = 0; i < 10; ++i) { + printf("Thread id: %d %d\n", getpid(), i); + delay(10000000); + schedule(); + } +} + +void main() { + uart_init(); + init_printf(NULL, putc); + memory_init(); + rootfs_init(); + mount_initramfs(); + sd_init(); + sd_mount(); + task_init(); + core_timer_enable(); + cpu_timer_register_enable(); + + while (1) { + char c = uart_getc(); + uart_send(c); + if (c == 's') break; + } + printf("%s", "\nHello from Raspberry pi!\n"); + shell(); + // for(int i = 0; i < N; ++i) { // N should > 2 + // thread_create(foo); + // } + // delay(1000000000); + // kill_zombies(); + // printf("PID: %d\n", getpid()); + // for(int i = 0; i < N*2; ++i) { // N should > 2 + // thread_create(foo); + // } + //fork_test(); + int ret; + if ((ret = fork()) == 0) + exec("syscall.img", 0x0); + idle(); + +} diff --git a/lab8/src/mbox.c b/lab8/src/mbox.c new file mode 100644 index 000000000..4d9dbce85 --- /dev/null +++ b/lab8/src/mbox.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "gpio.h" +#include "printf.h" + +/* mailbox message buffer */ +//volatile unsigned int __attribute__((aligned(16))) mbox[36]; + +#define VIDEOCORE_MBOX (MMIO_BASE + 0x0000B880) +#define MBOX_READ ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x0)) +#define MBOX_POLL ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x10)) +#define MBOX_SENDER ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x14)) +#define MBOX_STATUS ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x18)) +#define MBOX_CONFIG ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x1C)) +#define MBOX_WRITE ((volatile unsigned int *)(VIDEOCORE_MBOX + 0x20)) +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 +#define MBOX_CH_PROP 8 +/* tags */ +#define GET_BOARD_REVISION 0x00010002 +#define GET_ARM_MEMORY 0x00010005 +#define REQUEST_CODE 0x00000000 +#define REQUEST_SUCCEED 0x80000000 +#define REQUEST_FAILED 0x80000001 +#define TAG_REQUEST_CODE 0x00000000 +#define END_TAG 0x00000000 + +/** + * Make a mailbox call. Returns 0 on failure, non-zero on success + */ +int mboxc_mbox_call(unsigned char ch, unsigned int *mbox) { + /* 28 bits (MSB) for value, 4 bits for channel type */ + unsigned int r = (((unsigned int)((unsigned long)mbox) & ~0xF) | (ch & 0xF)); + + /* wait until we can write to the mailbox */ + do { + asm volatile("nop"); + } while (*MBOX_STATUS & MBOX_FULL); + /* write the address of our message to the mailbox with channel identifier */ + *MBOX_WRITE = r; + /* now wait for the response */ + while (1) { + /* is there a response? */ + do { + asm volatile("nop"); + } while (*MBOX_STATUS & MBOX_EMPTY); + /* is it a response to our message? */ + if (r == *MBOX_READ) + /* is it a valid successful response? */ + return mbox[1] == MBOX_RESPONSE; + } + return 0; +} + +int get_board_revision(unsigned int *mbox) { + mbox[0] = 7 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_BOARD_REVISION; // tag identifier + mbox[3] = 4; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // value buffer + // tags end + mbox[6] = END_TAG; + + return 0; + //return mbox_call(MBOX_CH_PROP, mbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + // printf("0x%x\n", mbox[5]); // it should be 0xa020d3 for rpi3 b+ +} + +int get_arm_memory(unsigned int *mbox) { + mbox[0] = 8 * 4; // buffer size in bytes + mbox[1] = REQUEST_CODE; + // tags begin + mbox[2] = GET_ARM_MEMORY; // tag identifier + mbox[3] = 8; // maximum of request and response value buffer's length. + mbox[4] = TAG_REQUEST_CODE; + mbox[5] = 0; // value buffer + mbox[6] = 0; // value buffer + // tags end + mbox[7] = END_TAG; + + return 0; + //return mbox_call(MBOX_CH_PROP, mbox); // message passing procedure call, you should implement it following the 6 steps provided above. + + // printf("0x%x\n", mbox[5]); // it should be 0xa020d3 for rpi3 b+ +} \ No newline at end of file diff --git a/lab8/src/memory.c b/lab8/src/memory.c new file mode 100644 index 000000000..67ba11ec0 --- /dev/null +++ b/lab8/src/memory.c @@ -0,0 +1,249 @@ +#include "memory.h" +#include "freelist.h" +#include "uart.h" +#include "utils.h" +#include "printf.h" + +Freelist *heads; +Node *nodes; +int *frame_array; +blocklist memory_blocks; + +extern char _end; +extern void memzero(void *, size_t); + +void memory_init() { + void *base = (void *)&_end; + heads = (Freelist *)simple_malloc(&base, (int)sizeof(Freelist)*LOG2_MAX_PAGES_PLUS_1); + nodes = (Node *)simple_malloc(&base, (int)sizeof(Node)*MAX_PAGES); + frame_array = (int *)simple_malloc(&base, (int)sizeof(int)*MAX_PAGES); + for (int i = 0; i < MAX_PAGES; i++) { + nodes[i].prev = NULL; + nodes[i].next = NULL; + nodes[i].index = i; + frame_array[i] = BELONG_LEFT; + } + frame_array[0] = LOG2_MAX_PAGES; + for (int i = 0; i < LOG2_MAX_PAGES; i++) { + heads[i].head = NULL; + } + heads[LOG2_MAX_PAGES].head = &nodes[0]; + + reserve_memory(0x0, (ulong)base); + reserve_memory(0x8000000, (0x8000000+CPIO_SIZE)); + reserve_memory(0x3c000000, 0x40000000); + + memory_blocks.head = (block_meta *)malloc(PAGE_SIZE); + memory_blocks.head->next = NULL; + memory_blocks.head->size = 4096 - BLOCK_SIZE; + memory_blocks.head->free = 1; + memory_blocks.head->pagetail = 1; + +} + +int find_allocate_list(Freelist *heads, int needed_pages) { + for (int i = needed_pages; i <= LOG2_MAX_PAGES; i++) { + if (heads[i].head != NULL) { + return i; + } + } + return LOG2_MAX_PAGES; +} + +int allocate_page(Freelist *heads, Node *nodes, int *frames, int needed_level, int index) { + debug("Allocate page for level ", needed_level); + if (index >= 0) { + // reserve memory + int remove = index, push; + int use_level = needed_level; + while(frames[remove] != use_level) { + remove &= ~(pow2(use_level)); + use_level++; + } + debug("Remove", remove); + freelist_remove(&heads[use_level], nodes, remove); + for(int i = use_level-1; i >= needed_level; i--) { + remove ^= (index & pow2(i)); + push = remove ^ pow2(i); + debug("Push", push); + freelist_push(&heads[i], nodes, push); + frames[push] = i; + } + frames[index] = ALLOCATED; + return 0; // no need to return anything + } + else { + // normal page allocation + int use_level = find_allocate_list(heads, needed_level); + Node *fs = heads[use_level].head; + int front = fs->index; + debug("Remove from freelist", front); + freelist_remove(&heads[use_level], nodes, front); + for (int i = use_level-1; i >= needed_level; i--) { + int back = front | pow2(i); + debug("Push to freelist", back); + freelist_push(&heads[i], nodes, back); + frames[back] = i; + } + frames[front] = ALLOCATED; + return fs->index; + } +} + +void free_page(Freelist *heads, Node *nodes, int *frames, int free_index) { + debug("Freeing page", free_index); + frames[free_index] = BELONG_LEFT; + int level = 0; + int free_level = LOG2_MAX_PAGES; + while(frames[free_index ^ pow2(level)] == BELONG_LEFT) { // 如果非最左邊區塊,終會到達index=0, frames[0]不可能BELONG_LEFT, 所以會跳出迴圈 + free_index &= ~(pow2(level)); + level++; + } + for (int i = level; i < LOG2_MAX_PAGES; i++) { + int buddy = free_index ^ pow2(i); + if (frames[buddy] != i) { // not same level or allocated + free_level = i; + break; + } + frames[buddy] = BELONG_LEFT; + freelist_remove(&heads[i], nodes, buddy); + debug("Merged", buddy); + free_index &= ~(pow2(i)); + } + debug("Push back to freelist", free_index); + freelist_push(&heads[free_level], nodes, free_index); + frames[free_index] = free_level; + return; +} + +void *malloc(size_t size) { + if (size >= (PAGE_SIZE-BLOCK_SIZE)) { + //int need_pages = (size+PAGE_SIZE-1)/PAGE_SIZE; + int needed_order = log2((size+PAGE_SIZE-1)/PAGE_SIZE); + void *ptr = (void *)(unsigned long)(MEMORY_BASE + allocate_page(heads, nodes, frame_array, needed_order, -1) * PAGE_SIZE); + //print_freelists(); + memzero(ptr, pow2(needed_order)*PAGE_SIZE); + //printf("[Malloc page] %x\n", ptr); + return ptr; + } + else { + block_meta *curr = memory_blocks.head; + size = (size & ~15) + 16; // align to 16 + /* find split block */ + while(1) { + if ((curr->free != (short)0) && (curr->size > size)) { + break; + } + if (curr->next == (block_meta *)NULL) { + /* allocate new page */ + block_meta *new_page = (block_meta *)malloc(PAGE_SIZE); + new_page->size = PAGE_SIZE-BLOCK_SIZE; + new_page->free = 1; + new_page->pagetail = 1; + new_page->next = NULL; + curr->next = new_page; + curr = curr->next; + break; + } + curr = curr->next; + } + + /* allocate memory */ + int left_size = curr->size - size; + block_meta *new_block = (block_meta *)((ulong)curr+BLOCK_SIZE+(ulong)size); + new_block->size = left_size; + new_block->free = 1; + new_block->pagetail = curr->pagetail; + new_block->next = curr->next; + curr->size = size; + curr->free = 0; + curr->pagetail = 0; + curr->next = new_block; + memzero((ulong)curr+BLOCK_SIZE, size); + //printf("[Malloc] %x\n", (ulong)curr+BLOCK_SIZE); + return (void *)((ulong)curr+BLOCK_SIZE); + + } +} + +void free(void *ptr) { + if ((ulong)ptr % PAGE_SIZE == 0) { + int free_index = (int)(((ulong)ptr-MEMORY_BASE+(PAGE_SIZE-1)) / 0x1000); + printf("Free page index %d\n", free_index); + free_page(heads, nodes, frame_array, free_index); + //print_freelists(); + } + else { + //printf("[Free] %x\n", ptr); + block_meta *need_free = (block_meta *)((ulong)ptr-BLOCK_SIZE); + need_free->free = 1; + /* remove block */ + block_meta *curr = memory_blocks.head; + while(curr != NULL) { + if (curr->free) { + while(!curr->pagetail && (curr->next != NULL) && curr->next->free) { + curr->size += curr->next->size; + curr->pagetail = curr->next->pagetail; + curr->next = curr->next->next; + } + } + curr = curr->next; + } + } +} + +void reserve_memory(ulong start, ulong end) { + int index = (start-MEMORY_BASE) / PAGE_SIZE; + int pages = ((end+PAGE_SIZE-1)-start) / PAGE_SIZE; + uart_puts("Reserve "); + uart_int(pages); + uart_puts(" page(s)\n"); + for (int i = 0; i < LOG2_MAX_PAGES; i++) { + if (index & pow2(i)) { + allocate_page(heads, nodes, frame_array, i, index); + index += pow2(i); + pages -= pow2(i); + } + if (pages <= 0) break; + if (pow2(i) >= pages) { + allocate_page(heads, nodes, frame_array, log2(pages), index); + break; + } + if (pages <= 0) break; + } + //uart_puts("[Reserve memory] Finished.\n"); + //print_freelists(); +} + +void print_memory() { + block_meta *curr = memory_blocks.head; + while(curr != NULL) { + uart_puts("----------------\n"); + uart_puts("Address: "); + uart_hex((ulong)curr + BLOCK_SIZE); + uart_puts("\n"); + uart_puts("Size: "); + uart_int(curr->size); + uart_puts("\n"); + uart_puts("Free: "); + if (curr->free) + uart_puts("Yes\n"); + else + uart_puts("No\n"); + uart_puts("Last block in the page: "); + if (curr->pagetail) + uart_puts("Yes\n"); + else + uart_puts("No\n"); + + curr = curr->next; + } +} + +void memcpy(void* dest, void* src, uint64_t size) { + uint8_t* csrc = (uint8_t*)src; + uint8_t* cdest = (uint8_t*)dest; + + for (uint64_t i = 0; i < size; i++) + cdest[i] = csrc[i]; +} \ No newline at end of file diff --git a/lab8/src/printf.c b/lab8/src/printf.c new file mode 100644 index 000000000..db3507682 --- /dev/null +++ b/lab8/src/printf.c @@ -0,0 +1,228 @@ +/* +File: printf.c +Copyright (C) 2004 Kustaa Nyholm +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "printf.h" + +typedef void (*putcf) (void*,char); +static putcf stdout_putf; +static void* stdout_putp; + +#define PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%=d; + d/=base; + if (n || dgt>0|| d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void li2a (long num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + uli2a(num,10,0,bf); + } + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%= d; + d/=base; + if (n || dgt>0 || d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void i2a (int num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + ui2a(num,10,0,bf); + } + +static int a2d(char ch) + { + if (ch>='0' && ch<='9') + return ch-'0'; + else if (ch>='a' && ch<='f') + return ch-'a'+10; + else if (ch>='A' && ch<='F') + return ch-'A'+10; + else return -1; + } + +static char a2i(char ch, char** src,int base,int* nump) + { + char* p= *src; + int num=0; + int digit; + while ((digit=a2d(ch))>=0) { + if (digit>base) break; + num=num*base+digit; + ch=*p++; + } + *src=p; + *nump=num; + return ch; + } + +static void putchw(void* putp,putcf putf,int n, char z, char* bf) + { + char fc=z? '0' : ' '; + char ch; + char* p=bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp,fc); + while ((ch= *bf++)) + putf(putp,ch); + } + +void tfp_format(void* putp,putcf putf,char *fmt, va_list va) + { + char bf[12]; + + char ch; + + + while ((ch=*(fmt++))) { + if (ch!='%') + putf(putp,ch); + else { + char lz=0; +#ifdef PRINTF_LONG_SUPPORT + char lng=0; +#endif + int w=0; + ch=*(fmt++); + if (ch=='0') { + ch=*(fmt++); + lz=1; + } + if (ch>='0' && ch<='9') { + ch=a2i(ch,&fmt,10,&w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch=='l') { + ch=*(fmt++); + lng=1; + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),10,0,bf); + else +#endif + ui2a(va_arg(va, unsigned int),10,0,bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'd' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int),bf); + else +#endif + i2a(va_arg(va, int),bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'x': case 'X' : +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf); + else +#endif + ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf); + putchw(putp,putf,w,lz,bf); + break; + case 'c' : + putf(putp,(char)(va_arg(va, int))); + break; + case 's' : + putchw(putp,putf,w,0,va_arg(va, char*)); + break; + case '%' : + putf(putp,ch); + default: + break; + } + } + } + abort:; + } + + +void init_printf(void* putp,void (*putf) (void*,char)) + { + stdout_putf=putf; + stdout_putp=putp; + } + +void tfp_printf(char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(stdout_putp,stdout_putf,fmt,va); + va_end(va); + } + +static void putcp(void* p,char c) + { + *(*((char**)p))++ = c; + } + + + +void tfp_sprintf(char* s,char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(&s,putcp,fmt,va); + putcp(&s,0); + va_end(va); + } \ No newline at end of file diff --git a/lab8/src/sched.c b/lab8/src/sched.c new file mode 100644 index 000000000..39b6402d1 --- /dev/null +++ b/lab8/src/sched.c @@ -0,0 +1,156 @@ +#include "sched.h" +#include "memory.h" +#include "utils.h" +#include "printf.h" +#include "vfs.h" +#include "tmpfs.h" + +static Thread init_task = INIT_TASK; +//Thread *current_thread = &(init_task); +Thread *task[NR_TASKS] = {&(init_task), }; +int nr_tasks = 1; + + +extern void run_thread(void); +extern Thread *get_current(void); +extern void enable_irq(); +extern void disable_irq(); +extern switch_to(void *, void *); + + +int get_new_pid() { + Thread* p; + for (int i = 0; i < NR_TASKS; i++) { + p = task[i]; + if (p == NULL) { + return i; + } + } + return -1; +} + +Thread* current_thread() { + Thread *cur = get_current(); + if (!cur) + return &init_task; + return cur; +} + +void preempt_disable(void) { + current_thread()->preempt_count++; +} + +void preempt_enable(void) { + current_thread()->preempt_count--; +} + +int thread_create(void *func) { + Thread *p = malloc(sizeof(Thread)); + printf("thread_create %x\n", p); + p->priority = 1; + p->state = TASK_RUNNING; + p->counter = p->priority; + p->status = 0; + p->preempt_count = 1; //disable preemtion until schedule_tail + + p->cpu_context.x19 = (ulong)func; + p->cpu_context.lr = (ulong)run_thread; + //p->cpu_context.sp = (ulong)p + THREAD_SIZE - 16; + p->kernel_sp = (ulong)malloc(PAGE_SIZE) + PAGE_SIZE - 16; + p->user_sp = (ulong)malloc(PAGE_SIZE) + PAGE_SIZE - 16; + p->cpu_context.sp = p->kernel_sp; // kernel space + p->pwd = rootfs->root; + + int pid = get_new_pid(); + task[pid] = p; + p->pid = pid; + preempt_enable(); + return pid; +} + +void _schedule() { + preempt_disable(); + int next, c; + struct Thread* p; + while (1) { + c = -1; + next = 0; + for (int i = 0; i < NR_TASKS; i++) { // pick biggest c value + p = task[i]; + if (p && p->state == TASK_RUNNING && p->counter > c) { + c = p->counter; + next = i; + } + } + if (c) { + break; + } + for (int i = 0; i < NR_TASKS; i++) { + p = task[i]; + if (p) { + p->counter = (p->counter >> 1) + p->priority; + } + } + } + //debug("now thread", current_thread()); + //debug("next thread", task[next]); + if (current_thread() != task[next]) { + //printf("[scheduler] next pid: %d\n", next); + Thread *prev = current_thread(); + //current_thread = task[next]; + switch_to(prev, task[next]); + } + preempt_enable(); +} + +void schedule() { + current_thread()->counter = 0; + _schedule(); +} + +void kill_zombies() { + Thread* p; + for (int i = 1; i < NR_TASKS; i++) { // pick biggest c value + p = task[i]; + if (p && p->state == TASK_ZOMBIE) { + free((void *)(p->kernel_sp & ~(PAGE_SIZE-1))); + free((void *)(p->user_sp & ~(PAGE_SIZE-1))); + free(p); + task[i] = NULL; + } + } +} + +void idle() { + while(1) { + kill_zombies(); // reclaim threads marked as DEAD + schedule(); // switch to any other runnable thread + } +} + + +void timer_tick() { + current_thread()->counter--; + if (current_thread()->counter>0 || current_thread()->preempt_count > 0) { + return; + } + current_thread()->counter=0; + enable_irq(); + schedule(); + disable_irq(); +} + +void end_thread(void) { + current_thread()->state = TASK_ZOMBIE; + schedule(); +} + +void task_init(void) { + //Thread *p = current_thread(); + //asm volatile ("mrs %0, sp_el1":"=r"(p->kernel_sp)); + uint64_t init_task_addr = (uint64_t)&init_task; + asm volatile ("msr tpidr_el1, %0"::"r"(init_task_addr)); + (&init_task)->pwd = rootfs->root; + uint64_t sp_el0 = 0; + asm volatile ("msr sp_el0, %0"::"r"(sp_el0)); +} \ No newline at end of file diff --git a/lab8/src/sdhost.c b/lab8/src/sdhost.c new file mode 100644 index 000000000..48dfd8133 --- /dev/null +++ b/lab8/src/sdhost.c @@ -0,0 +1,307 @@ +#include "mbr.h" +#include "fat32.h" +#include "vfs.h" +#include "memory.h" +#include "printf.h" + + +// mmio +#define KVA 0xffff000000000000 +#define MMIO_BASE (0x3f000000) //I don't have virtual memory + +// SD card command +#define GO_IDLE_STATE 0 +#define SEND_OP_CMD 1 +#define ALL_SEND_CID 2 +#define SEND_RELATIVE_ADDR 3 +#define SELECT_CARD 7 +#define SEND_IF_COND 8 +#define VOLTAGE_CHECK_PATTERN 0x1aa +#define STOP_TRANSMISSION 12 +#define SET_BLOCKLEN 16 +#define READ_SINGLE_BLOCK 17 +#define WRITE_SINGLE_BLOCK 24 +#define SD_APP_OP_COND 41 +#define SDCARD_3_3V (1 << 21) +#define SDCARD_ISHCS (1 << 30) +#define SDCARD_READY (1 << 31) +#define APP_CMD 55 + +// gpio +#define GPIO_BASE (MMIO_BASE + 0x200000) +#define GPIO_GPFSEL4 (GPIO_BASE + 0x10) +#define GPIO_GPFSEL5 (GPIO_BASE + 0x14) +#define GPIO_GPPUD (GPIO_BASE + 0x94) +#define GPIO_GPPUDCLK1 (GPIO_BASE + 0x9c) + +// sdhost +#define SDHOST_BASE (MMIO_BASE + 0x202000) +#define SDHOST_CMD (SDHOST_BASE + 0) +#define SDHOST_READ 0x40 +#define SDHOST_WRITE 0x80 +#define SDHOST_LONG_RESPONSE 0x200 +#define SDHOST_NO_REPONSE 0x400 +#define SDHOST_BUSY 0x800 +#define SDHOST_NEW_CMD 0x8000 +#define SDHOST_ARG (SDHOST_BASE + 0x4) +#define SDHOST_TOUT (SDHOST_BASE + 0x8) +#define SDHOST_TOUT_DEFAULT 0xf00000 +#define SDHOST_CDIV (SDHOST_BASE + 0xc) +#define SDHOST_CDIV_MAXDIV 0x7ff +#define SDHOST_CDIV_DEFAULT 0x148 +#define SDHOST_RESP0 (SDHOST_BASE + 0x10) +#define SDHOST_RESP1 (SDHOST_BASE + 0x14) +#define SDHOST_RESP2 (SDHOST_BASE + 0x18) +#define SDHOST_RESP3 (SDHOST_BASE + 0x1c) +#define SDHOST_HSTS (SDHOST_BASE + 0x20) +#define SDHOST_HSTS_MASK (0x7f8) +#define SDHOST_HSTS_ERR_MASK (0xf8) +#define SDHOST_HSTS_DATA (1 << 0) +#define SDHOST_PWR (SDHOST_BASE + 0x30) +#define SDHOST_DBG (SDHOST_BASE + 0x34) +#define SDHOST_DBG_FSM_DATA 1 +#define SDHOST_DBG_FSM_MASK 0xf +#define SDHOST_DBG_MASK (0x1f << 14 | 0x1f << 9) +#define SDHOST_DBG_FIFO (0x4 << 14 | 0x4 << 9) +#define SDHOST_CFG (SDHOST_BASE + 0x38) +#define SDHOST_CFG_DATA_EN (1 << 4) +#define SDHOST_CFG_SLOW (1 << 3) +#define SDHOST_CFG_INTBUS (1 << 1) +#define SDHOST_SIZE (SDHOST_BASE + 0x3c) +#define SDHOST_DATA (SDHOST_BASE + 0x40) +#define SDHOST_CNT (SDHOST_BASE + 0x50) + +// helper +#define set(io_addr, val) \ + asm volatile("str %w1, [%0]" ::"r"(io_addr), "r"(val) \ + : "memory"); + +#define get(io_addr, val) \ + asm volatile("ldr %w0, [%1]" \ + : "=r"(val) \ + : "r"(io_addr) \ + : "memory"); + +static inline void delay(unsigned long tick) { + while (tick--) { + asm volatile("nop"); + } +} + +static int is_hcs; // high capcacity(SDHC) + +static void pin_setup() { + set(GPIO_GPFSEL4, 0x24000000); + set(GPIO_GPFSEL5, 0x924); + set(GPIO_GPPUD, 0); + delay(15000); + set(GPIO_GPPUDCLK1, 0xffffffff); + delay(15000); + set(GPIO_GPPUDCLK1, 0); +} + +static void sdhost_setup() { + unsigned int tmp; + set(SDHOST_PWR, 0); + set(SDHOST_CMD, 0); + set(SDHOST_ARG, 0); + set(SDHOST_TOUT, SDHOST_TOUT_DEFAULT); + set(SDHOST_CDIV, 0); + set(SDHOST_HSTS, SDHOST_HSTS_MASK); + set(SDHOST_CFG, 0); + set(SDHOST_CNT, 0); + set(SDHOST_SIZE, 0); + get(SDHOST_DBG, tmp); + tmp &= ~SDHOST_DBG_MASK; + tmp |= SDHOST_DBG_FIFO; + set(SDHOST_DBG, tmp); + delay(250000); + set(SDHOST_PWR, 1); + delay(250000); + set(SDHOST_CFG, SDHOST_CFG_SLOW | SDHOST_CFG_INTBUS | SDHOST_CFG_DATA_EN); + set(SDHOST_CDIV, SDHOST_CDIV_DEFAULT); +} + +static int wait_sd() { + int cnt = 1000000; + unsigned int cmd; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_CMD, cmd); + --cnt; + } while (cmd & SDHOST_NEW_CMD); + return 0; +} + +static int sd_cmd(unsigned cmd, unsigned int arg) { + set(SDHOST_ARG, arg); + set(SDHOST_CMD, cmd | SDHOST_NEW_CMD); + return wait_sd(); +} + +static int sdcard_setup() { + unsigned int tmp; + sd_cmd(GO_IDLE_STATE | SDHOST_NO_REPONSE, 0); + sd_cmd(SEND_IF_COND, VOLTAGE_CHECK_PATTERN); + get(SDHOST_RESP0, tmp); + if (tmp != VOLTAGE_CHECK_PATTERN) { + return -1; + } + while (1) { + if (sd_cmd(APP_CMD, 0) == -1) { + // MMC card or invalid card status + // currently not support + continue; + } + sd_cmd(SD_APP_OP_COND, SDCARD_3_3V | SDCARD_ISHCS); + get(SDHOST_RESP0, tmp); + if (tmp & SDCARD_READY) { + break; + } + delay(1000000); + } + + is_hcs = tmp & SDCARD_ISHCS; + sd_cmd(ALL_SEND_CID | SDHOST_LONG_RESPONSE, 0); + sd_cmd(SEND_RELATIVE_ADDR, 0); + get(SDHOST_RESP0, tmp); + sd_cmd(SELECT_CARD, tmp); + sd_cmd(SET_BLOCKLEN, 512); + return 0; +} + +static int wait_fifo() { + int cnt = 1000000; + unsigned int hsts; + do { + if (cnt == 0) { + return -1; + } + get(SDHOST_HSTS, hsts); + --cnt; + } while ((hsts & SDHOST_HSTS_DATA) == 0); + return 0; +} + +static void set_block(int size, int cnt) { + set(SDHOST_SIZE, size); + set(SDHOST_CNT, cnt); +} + +static void wait_finish() { + unsigned int dbg; + do { + get(SDHOST_DBG, dbg); + } while ((dbg & SDHOST_DBG_FSM_MASK) != SDHOST_HSTS_DATA); +} + +void readblock(int block_idx, void *buf) { + unsigned int *buf_u = (unsigned int *)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do { + set_block(512, 1); + sd_cmd(READ_SINGLE_BLOCK | SDHOST_READ, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + get(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while (!succ); + wait_finish(); +} + +void writeblock(int block_idx, void *buf) { + unsigned int *buf_u = (unsigned int *)buf; + int succ = 0; + if (!is_hcs) { + block_idx <<= 9; + } + do { + set_block(512, 1); + sd_cmd(WRITE_SINGLE_BLOCK | SDHOST_WRITE, block_idx); + for (int i = 0; i < 128; ++i) { + wait_fifo(); + set(SDHOST_DATA, buf_u[i]); + } + unsigned int hsts; + get(SDHOST_HSTS, hsts); + if (hsts & SDHOST_HSTS_ERR_MASK) { + set(SDHOST_HSTS, SDHOST_HSTS_ERR_MASK); + sd_cmd(STOP_TRANSMISSION | SDHOST_BUSY, 0); + } else { + succ = 1; + } + } while (!succ); + wait_finish(); +} + +void sd_init() { + pin_setup(); + sdhost_setup(); + sdcard_setup(); +} + +// read MBR and mount partition +int sd_mount() { + // read MBR + char buf[FAT_BLOCK_SIZE]; + readblock(0, buf); + + // check boot signature + if (buf[510] != 0x55 || buf[511] != 0xAA) { + return -1; + } + + // parse first partition only + struct mbr_partition p1; + memcpy(&p1, buf + 446, sizeof(struct mbr_partition)); + + // mount partition + readblock(p1.starting_sector, buf); // 2048 (0x00000800) + // route each filesystem + if (p1.partition_type == 0x0b) { // FAT32 with CHS addressing + // create FAT32's root directory object + char mountpoint[8] = "/boot"; + vfs_mkdir(mountpoint); + vfs_mount(mountpoint, "fat32"); + + // store metadata + struct fat32_boot_sector* boot_sector = (struct fat32_boot_sector*)buf; + fat32_metadata.data_region_blk_idx = p1.starting_sector + + boot_sector->n_sectors_per_fat_32 * boot_sector->n_file_alloc_tabs + + boot_sector->n_reserved_sectors; + fat32_metadata.fat_region_blk_idx = p1.starting_sector + boot_sector->n_reserved_sectors; + fat32_metadata.n_fat = boot_sector->n_file_alloc_tabs; + fat32_metadata.sector_per_fat = boot_sector->n_sectors_per_fat_32; + fat32_metadata.first_cluster = boot_sector->root_dir_start_cluster_num; + fat32_metadata.sector_per_cluster = boot_sector->logical_sector_per_cluster; + + // get mount node + struct vnode* parent_dir; + char path_remain[MAX_PATHNAME_LEN]; + getdir(mountpoint, &parent_dir, path_remain); + struct vnode *mount_dir; + int lookup_res = parent_dir->v_ops->lookup(parent_dir, &mount_dir, path_remain); + if (lookup_res < 0) return lookup_res; + // change to fat32 + mount_dir = mount_dir->mount->root; + // fill internal data of mount node + struct fat32_internal* root_internal = (struct fat32_internal*)malloc(sizeof(struct fat32_internal)); + root_internal->first_cluster = boot_sector->root_dir_start_cluster_num; + mount_dir->internal = root_internal; + } + + return 0; +} diff --git a/lab8/src/shell.c b/lab8/src/shell.c new file mode 100644 index 000000000..7d324f6e2 --- /dev/null +++ b/lab8/src/shell.c @@ -0,0 +1,96 @@ +#include "utils.h" +#include "uart.h" +#include "printf.h" +#include "memory.h" +#include "vfs.h" +#include "tmpfs.h" +#include "shell.h" +#include "sched.h" +#include "sys.h" +#include "sdhost.h" +#include "fat32.h" + + +void shell() { + printf("\n\n _ _ ___ _____ _ _ ___ ___ ___ ___ \n"); + printf("| \\| |/ __|_ _| | | |/ _ \\/ __| \\_ _|\n"); + printf("| .` | (__ | | | |_| | (_) \\__ \\ |) | | \n"); + printf("|_|\\_|\\___| |_| \\___/ \\___/|___/___/___|\n\n"); + char input[1024]; + while (1) { + uart_send('\r'); + uart_puts("# "); + shell_input(input); + if (strcmp(input, "test") == 0) { + struct vnode *vnode; + int fd = open("/dir", O_CREAT); + int ret = write(fd, "abcdefghijklmnopqrstuvwxyz", 26); + printf("%d %d\n", fd, ret); + fd = open("/dir", 0); + char buf[128]; + int rd = read(fd, buf, 10); + printf("%d %d %s\n", fd, rd, buf); + ret = vfs_lookup(".", &vnode); + printf("Current directory: %s\n", ((struct tmpfs_internal *)vnode->internal)->name); + + + } else if (strcmp(input, "m") == 0) { + shell_input(input); + int size = (int)cstr_to_ulong(input); + void *ptr = malloc(size); + printf("%x\n", ptr); + } else if (strcmp(input, "d") == 0) { + shell_input(input); + void *ptr = (void *)(ulong)hex_to_int(input, 8); + free(ptr); + } else if (strcmp(input, "pm") == 0) { + print_freelists(); + print_memory(); + } else if (!strcmp(input, "pwd")) { + printf("%s\n", ((struct tmpfs_internal *)current_thread()->pwd->internal)->name); + } else if (!strcmp(input, "sd")) { + int idx; + shell_input(input); + char *buf = (char *)malloc(512); + readblock(cstr_to_ulong(input), buf); + for (int i = 0; i < 512; i++) { + printf("%02x ", buf[i]); + if (i % 16 == 15) printf("\n"); + } + } else if (!strcmp(input, "meta")) { + printf("fat_region_blk_idx %d\n", fat32_metadata.fat_region_blk_idx); + printf("n_fat %d\n", fat32_metadata.n_fat); + printf("sector_per_fat %d\n", fat32_metadata.sector_per_fat); + printf("data_region_blk_idx %d\n", fat32_metadata.data_region_blk_idx); + printf("first_cluster %d\n", fat32_metadata.first_cluster); + printf("sector_per_cluster %d\n", fat32_metadata.sector_per_cluster); + } else if (!strcmp(input, "vfs2")) { + int ret; + char *argv[] = {}; + if ((ret = fork()) == 0) { + exec("vfs2.img", argv); + } + idle(); + } else { + uart_puts("Error input!\n"); + } + } + +} + +void shell_input(char *input) { + int i = 0; + char temp; + while (1) { + temp = uart_getc(); + if (temp == '\n') { + uart_puts("\n"); + input[i] = '\0'; + break; + } else + uart_send(temp); + + input[i] = temp; + i++; + } +} \ No newline at end of file diff --git a/lab8/src/start.S b/lab8/src/start.S new file mode 100644 index 000000000..6e55f94c0 --- /dev/null +++ b/lab8/src/start.S @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +.section ".text.boot" + +.global _start + +_start: + bl from_el2_to_el1 + bl set_exception_vector_table + // set top of stack just before our code (stack grows to a lower address per AAPCS64) + adr x1, _start + mov sp, x1 + + // clear bss + adr x1, __bss_start + ldr w2, =__bss_size +1: cbz w2, 2f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 1b + +2: // jump to C code, should not return + bl main + // for failsafe, halt this core too +3: wfe + b 3b + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 + mov x0, 0x5 // EL1h (SPSel = 1) with interrupt enabled + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 + +.globl from_el1_to_el0 +from_el1_to_el0: + mov x0, 0 + msr spsr_el1, x0 + msr elr_el1, lr + mov x0, 0x60000 // user space stack + msr sp_el0, x0 + eret // return to EL0 + +// save general registers to stack +.macro save_all + sub sp, sp, 32 * 9 + 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 x21, sp_el0 + mrs x22, elr_el1 + mrs x23, spsr_el1 + + stp x30, x21, [sp, #16 * 15] + stp x22, x23, [sp, #16 * 16] +.endm + +// load general registers from stack +.macro load_all + ldp x22, x23, [sp, #16 * 16] + ldp x30, x21, [sp, #16 * 15] + + msr sp_el0, x21 + msr elr_el1, x22 + msr spsr_el1, x23 + + 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] + add sp, sp, 32 * 9 +.endm + +exception_handler_loop: + b exception_handler_loop + +exception_handler: + save_all + bl exception_handler_loop + load_all + eret + +sync_exception_handler: + save_all + mrs x0, esr_el1 // to decide is syscall or not + mrs x1, elr_el1 // the address return to + mov x2, sp // trapframe + bl sync_exc_router + load_all + eret + +irq_exception_handler_low: + save_all + mov x0, #0 + bl timer_interrupt + load_all + eret + +irq_exception_handler: + save_all + mov x0, #1 + bl timer_interrupt + load_all + eret + +exception_handler_lower_irq: + save_all + bl timer_interrupt + load_all + eret + +.align 11 // vector table should be aligned to 0x800 +.global exception_vector_table +exception_vector_table: + b exception_handler // branch to a handler function. + .align 7 // entry size is 0x80, .align will pad 0 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b sync_exception_handler + .align 7 + b irq_exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b sync_exception_handler + .align 7 + b irq_exception_handler_low + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + b exception_handler + .align 7 + +set_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + ret + +.equ CORE0_TIMER_IRQ_CTRL, 0x40000040 + +.global core_timer_enable +core_timer_enable: + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mrs x0, cntfrq_el0 + lsr x0, x0, #5 + msr cntp_tval_el0, x0 // set expired time + mov x0, 2 + ldr x1, =CORE0_TIMER_IRQ_CTRL + str w0, [x1] // unmask timer interrupt + ret + +.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 + +.globl delay +delay: + subs x0, x0, #1 + bne delay + ret + +.globl enable_irq +enable_irq: + msr DAIFClr, #2 // IRQ mask bit + ret + +.globl disable_irq +disable_irq: + msr DAIFSet, #2 + ret + +.globl run_thread +run_thread: + bl preempt_enable + blr x19 //should never return + bl end_thread + +.globl ret_from_fork +ret_from_fork: + load_all + eret + +.global memzero +memzero: + str xzr, [x0], #8 + sub w1, w1, #1 + cbnz w1, memzero + ret diff --git a/lab8/src/sys.S b/lab8/src/sys.S new file mode 100644 index 000000000..acb4ca5c9 --- /dev/null +++ b/lab8/src/sys.S @@ -0,0 +1,92 @@ +#define __ASSEMBLY__ +#include "sys.h" + +.global getpid +getpid: + mov x8, SYS_GETPID + svc #0 + ret + +.global uartread +uartread: + mov x8, SYS_UART_READ + svc #0 + ret + +.global uartwrite +uartwrite: + mov x8, SYS_UART_WRITE + svc #0 + ret + +.global exec +exec: + mov x8, SYS_EXEC + svc #0 + ret // shouldn't go here + +.global fork +fork: + mov x8, SYS_FORK + svc #0 + ret + +.global exit +exit: + mov x8, SYS_EXIT + svc #0 + ret + +.global mbox_call +mbox_call: + mov x8, SYS_MBOX_CALL + svc #0 + ret + +.global kill +kill: + mov x8, SYS_KILL + svc #0 + ret + +.global open +open: + mov x8, SYS_OPEN + svc #0 + ret + +.global close +close: + mov x8, SYS_CLOSE + svc #0 + ret + +.global write +write: + mov x8, SYS_WRITE + svc #0 + ret + +.global read +read: + mov x8, SYS_READ + svc #0 + ret + +.global mkdir +mkdir: + mov x8, SYS_MKDIR + svc #0 + ret + +.global mount +mount: + mov x8, SYS_MOUNT + svc #0 + ret + +.global chdir +chdir: + mov x8, SYS_CHDIR + svc #0 + ret \ No newline at end of file diff --git a/lab8/src/tmpfs.c b/lab8/src/tmpfs.c new file mode 100644 index 000000000..32cada676 --- /dev/null +++ b/lab8/src/tmpfs.c @@ -0,0 +1,148 @@ +#include "memory.h" +#include "vfs.h" +#include "tmpfs.h" +#include "printf.h" +#include "utils.h" + +struct vnode_operations* tmpfs_v_ops; +struct file_operations* tmpfs_f_ops; + +int tmpfs_register() { + tmpfs_v_ops = (struct vnode_operations*)malloc(sizeof(struct vnode_operations)); + tmpfs_v_ops->lookup = &tmpfs_lookup; + tmpfs_v_ops->create = &tmpfs_create; + tmpfs_v_ops->mkdir = &tmpfs_mkdir; + tmpfs_f_ops = (struct file_operations*)malloc(sizeof(struct file_operations)); + tmpfs_f_ops->write = &tmpfs_write; + tmpfs_f_ops->read = &tmpfs_read; + return 0; +} + +int tmpfs_setup_mount(struct filesystem* fs, struct mount* mount) { + struct tmpfs_internal* tmpfs_root = (struct tmpfs_internal*)malloc(sizeof(struct tmpfs_internal)); + tmpfs_root->type = DIRECTORY; + strcpy(tmpfs_root->name, "/"); + tmpfs_root->parent = NULL; + struct node *root_vnode = tmpfs_create_vnode(tmpfs_root); + mount->root = root_vnode; + mount->fs = fs; + tmpfs_root->vnode = root_vnode; + return 0; +} + +struct vnode* tmpfs_create_vnode(struct tmpfs_internal* tmpfs_node) { + struct vnode* vnode = (struct vnode*)malloc(sizeof(struct vnode)); + vnode->f_ops = tmpfs_f_ops; + vnode->v_ops = tmpfs_v_ops; + vnode->internal = tmpfs_node; + return vnode; +} + +int tmpfs_create(struct vnode* dir, struct vnode** target, const char* component_name) { + // create tmpfs internal structure + struct tmpfs_internal* file_node = (struct tmpfs_internal*)malloc(sizeof(struct tmpfs_internal)); + file_node->type = REGULAR_FILE; + strcpy(file_node->name, component_name); + file_node->parent = (struct tmpfs_internal *)(dir->internal); + for (int i = 0; i < MAX_ENTRIES; i++) { + if (!file_node->parent->child[i]) { + file_node->parent->child[i] = file_node; + break; + } + } + file_node->vnode = tmpfs_create_vnode(file_node); + file_node->data = malloc(PAGE_SIZE); + + *target = file_node->vnode; + return 0; +} + +// vnode operations +int tmpfs_lookup(struct vnode* dir, struct vnode** target, const char* component_name) { + // component_name is empty, return dir vnode + if (!strcmp(component_name, "")) { + return -1; + } + else if (!strcmp(component_name, ".")) { + *target = dir; + return 0; + } + else if (!strcmp(component_name, "..")) { // todo: cross filesystem + *target = dir; + if (dir->mount_parent) { + *target = dir->mount_parent; + } + struct tmpfs_internal *t = ((struct tmpfs_internal *)dir->internal)->parent; + if (!t) return 0; + *target = ((struct tmpfs_internal *)dir->internal)->parent->vnode; + return 0; + } + // search component_name in dir + if (dir->mount != NULL) + dir = dir->mount->root; + for (int i = 0; i < MAX_ENTRIES; i++) { + struct tmpfs_internal* file_node = ((struct tmpfs_internal*)dir->internal)->child[i]; + if ((file_node != NULL) & !strcmp(file_node->name, component_name)) { + *target = file_node->vnode; + printf("[lookup] 0x%x\n", *target); + return 0; + } + } + *target = NULL; + return -1; +} + +int tmpfs_write(struct file* file, const void* buf, size_t len) { + if (((struct tmpfs_internal *)file->vnode->internal)->type != REGULAR_FILE) { + printf("Write on not regular file\n"); + return -1; + } + + struct tmpfs_internal* file_node = (struct tmpfs_internal *)file->vnode->internal; + + char *dest = &(file_node->data[file->f_pos]); + char *src = (char*)buf; + size_t i = 0; + for (; i < len; i++) { + dest[i] = src[i]; + } + //dest[i] = EOF; + return i; +} + +int tmpfs_read(struct file* file, void* buf, size_t len) { + if (((struct tmpfs_internal *)file->vnode->internal)->type != REGULAR_FILE) { + printf("Read on not regular file\n"); + return -1; + } + struct tmpfs_internal* file_node = (struct tmpfs_internal *)file->vnode->internal; + + char *dest = (char*)buf; + char *src = &(file_node->data[file->f_pos]); + size_t i = 0; + for (; i < len && src[i] != '\0'; i++) { + dest[i] = src[i]; + } + file->f_pos += i; + return i; + +} + +int tmpfs_mkdir(struct vnode* dir, struct vnode** target, const char* component_name) { + // create tmpfs internal structure + struct tmpfs_internal* dir_node = (struct tmpfs_internal*)malloc(sizeof(struct tmpfs_internal)); + dir_node->type = DIRECTORY; + strcpy(dir_node->name, component_name); + dir_node->parent = (struct tmpfs_internal *)(dir->internal); + for (int i = 0; i < MAX_ENTRIES; i++) { + if (!dir_node->parent->child[i]) { + dir_node->parent->child[i] = dir_node; + break; + } + } + dir_node->vnode = tmpfs_create_vnode(dir_node); + *target = dir_node->vnode; + return 0; +} + + diff --git a/lab8/src/uart.c b/lab8/src/uart.c new file mode 100644 index 000000000..770db014e --- /dev/null +++ b/lab8/src/uart.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2018 bzt (bztsrc@github) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "gpio.h" + +/* Auxilary mini UART registers */ +#define AUX_ENABLE ((volatile unsigned int *)(MMIO_BASE + 0x00215004)) +#define AUX_MU_IO ((volatile unsigned int *)(MMIO_BASE + 0x00215040)) +#define AUX_MU_IER ((volatile unsigned int *)(MMIO_BASE + 0x00215044)) +#define AUX_MU_IIR ((volatile unsigned int *)(MMIO_BASE + 0x00215048)) +#define AUX_MU_LCR ((volatile unsigned int *)(MMIO_BASE + 0x0021504C)) +#define AUX_MU_MCR ((volatile unsigned int *)(MMIO_BASE + 0x00215050)) +#define AUX_MU_LSR ((volatile unsigned int *)(MMIO_BASE + 0x00215054)) +#define AUX_MU_MSR ((volatile unsigned int *)(MMIO_BASE + 0x00215058)) +#define AUX_MU_SCRATCH ((volatile unsigned int *)(MMIO_BASE + 0x0021505C)) +#define AUX_MU_CNTL ((volatile unsigned int *)(MMIO_BASE + 0x00215060)) +#define AUX_MU_STAT ((volatile unsigned int *)(MMIO_BASE + 0x00215064)) +#define AUX_MU_BAUD ((volatile unsigned int *)(MMIO_BASE + 0x00215068)) + +/** + * Set baud rate and characteristics (115200 8N1) and map to GPIO + */ +void uart_init() { + register unsigned int r; + + /* map UART1 to GPIO pins */ + r = *GPFSEL1; + r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15 + r |= (2 << 12) | (2 << 15); // alt5 + *GPFSEL1 = r; + *GPPUD = 0; // enable pins 14 and 15 + r = 150; + while (r--) { + asm volatile("nop"); + } + *GPPUDCLK0 = (1 << 14) | (1 << 15); + r = 150; + while (r--) { + asm volatile("nop"); + } + /* initialize UART */ + *AUX_ENABLE |= 1; // enable UART1, AUX mini uart + *AUX_MU_IER = 0; + *AUX_MU_CNTL = 0; + *AUX_MU_LCR = 3; // 8 bits + *AUX_MU_MCR = 0; + *AUX_MU_IER = 0; + *AUX_MU_IIR = 0xc6; // disable interrupts + *AUX_MU_BAUD = 270; // 115200 baud + *GPPUDCLK0 = 0; // flush GPIO setup + *AUX_MU_CNTL = 3; // enable Tx, Rx +} + +/** + * Send a character + */ +void uart_send(unsigned int c) { + /* wait until we can send */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x20)); // transmitter idle + /* write the character to the buffer */ + *AUX_MU_IO = c; +} + +/** + * Receive a character + */ +char uart_getc() { + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // receiver overrun + /* read it and return */ + r = (char)(*AUX_MU_IO); + /* convert carrige return to newline */ + return r == '\r' ? '\n' : r; +} + +/** + * Receive a character without converting CR to LF + */ +char uart_getc_pure() { + char r; + /* wait until something is in the buffer */ + do { + asm volatile("nop"); + } while (!(*AUX_MU_LSR & 0x01)); // receiver overrun + /* read it and return */ + r = (char)(*AUX_MU_IO); + return r; +} +/** + * Display an int + */ +void uart_int(int i) { + if (i < 0) { + uart_send('-'); + i = (~i) + 1; + } + char c[10]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} +/** + * Display an unsigned int + */ +void uart_uint(unsigned int i) { + char c[10]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} + +/** + * Display an unsigned long + */ +void uart_ulong(unsigned long i) { + char c[20]; + if (i == 0) { + uart_send('0'); + return; + } + int digits = -1; + while (i != 0) { + c[++digits] = '0' + i % 10; + i /= 10; + } + for (; digits >= 0; --digits) { + uart_send(c[digits]); + } +} + +/** + * Display a string + */ +void uart_puts(char *s) { + while (*s) { + /* convert newline to carrige return + newline */ + if (*s == '\n') + uart_send('\r'); + uart_send(*s++); + } +} + +/** + * Display a binary value in hexadecimal + */ +void uart_hex(unsigned int d) { + unsigned int n; + int c; + for (c = 28; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +void uart_hex_long(unsigned long d) { + unsigned long n; + int c; + for (c = 60; c >= 0; c -= 4) { + // get highest tetrad + n = (d >> c) & 0xF; + // 0-9 => '0'-'9', 10-15 => 'A'-'F' + n += n > 9 ? 0x37 : 0x30; + uart_send(n); + } +} + +// This function is required by printf function +void putc(void *p, char c) { + if (c == '\n') + uart_send('\r'); + uart_send(c); +} \ No newline at end of file diff --git a/lab8/src/utils.c b/lab8/src/utils.c new file mode 100644 index 000000000..514bebe69 --- /dev/null +++ b/lab8/src/utils.c @@ -0,0 +1,137 @@ +#include "uart.h" +#include "utils.h" +#include "printf.h" + +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]; +}; + +void *get_user_program_address() { + struct cpio_newc_header *fs = (struct cpio_newc_header *)0x8000000; + char *current = (char *)0x8000000; + int is_file = 0; + while (1) { + fs = (struct cpio_newc_header *)current; + int name_size = hex_to_int(fs->c_namesize, 8); + int file_size = hex_to_int(fs->c_filesize, 8); + current += 110; // size of cpio_newc_header + if (strcmp(current, "user.img") == 0) + is_file = 1; + current += name_size; + while ((current - (char *)fs) % 4 != 0) + current++; + if (is_file) return (void *) current; + current += file_size; + while ((current - (char *)fs) % 4 != 0) + current++; + } + +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 != '\0' && *s1 == *s2) { + s1++; + s2++; + } + return (*(unsigned char *)s1) - (*(unsigned char *)s2); +} + +int hex_to_int(char *p, int len) { + int val = 0; + int temp; + for (int i = 0; i < len; i++) { + temp = *(p + i); + if (temp >= 'A') { + temp = temp - 'A' + 10; + } else + temp -= '0'; + val *= 16; + val += temp; + } + return val; +} + +void* simple_malloc(void **now, int size) { + void *ret = *now; + *now = *(char **)now + size; + return ret; +} + +int log2(int x) { + int ret = 0; + while(x != 1) { + if (x & 1) { + x += 1; + } + x >>= 1; + ret++; + } + return ret; +} + +int pow2(int x) { + return (1 << x); +} + +unsigned long cstr_to_ulong(char *s) { + unsigned long ret = 0; + while (*s != '\0') { + ret *= 10; + ret += (*s - '0'); + s++; + } + return ret; +} + +void strcpy(char *dest, const char *src) { + while (*src != '\0') { + *dest = *src; + src++; + dest++; + } + *dest = *src; +} + +void debug(char *s, int n) { + if (!DEBUG) return; + uart_puts(s); + uart_puts(": "); + uart_int(n); + uart_puts("\n"); +} + +unsigned long get_timestamp() { + register unsigned long f, c; + asm volatile ("mrs %0, cntfrq_el0" : "=r"(f)); // get current counter frequency + asm volatile ("mrs %0, cntpct_el0" : "=r"(c)); // read current counter + return (unsigned long) c; +} + +void assert(int e) { + if (e == 0) + printf("Assert Error!\n"); + else + printf("Assert\n"); +} + +int strlen(char *str) { + int len = 0; + while (*str != '\0') { + len++; + str++; + } + return len; +} \ No newline at end of file diff --git a/lab8/src/vfs.c b/lab8/src/vfs.c new file mode 100644 index 000000000..432c6367b --- /dev/null +++ b/lab8/src/vfs.c @@ -0,0 +1,217 @@ +#include "vfs.h" +#include "memory.h" +#include "printf.h" +#include "tmpfs.h" +#include "initramfs.h" +#include "typedef.h" +#include "utils.h" +#include "sched.h" +#include "fs.h" // filesystem definition + +struct mount *rootfs; + +void rootfs_init() { + register_filesystem(&tmpfs); + + rootfs = (struct mount *)malloc(sizeof(struct mount)); + tmpfs.setup_mount(&tmpfs, rootfs); +} + +void mount_initramfs() { + vfs_mkdir("/initramfs"); + vfs_mount("/initramfs", "initramfs"); +} + +int register_filesystem(struct filesystem *fs) { + // register the file system to the kernel. + // you can also initialize memory pool of the file system here. + if (!strcmp(fs->name, "tmpfs")) { + printf("[%u] Register tmpfs\n", get_timestamp()); + return tmpfs_register(); + } + if(!strcmp(fs->name, "initramfs")) { + printf("[%u] Register initramfs\n", get_timestamp()); + return initramfs_register(); + } + if(!strcmp(fs->name, "fat32")) { + printf("[%u] Register fat32\n", get_timestamp()); + return fat32_register(); + } + return -1; +} + +void getdir_r(struct vnode* node, const char* path, struct vnode** target_node, char* target_path) { + // find next / + printf("getdir_r path: %s 0x%x\n", path, path); + if (!path[0]) { + return; + } + int i = 0; + while (path[i]) { + if (path[i] == '/') break; + target_path[i] = path[i]; + i++; + } + target_path[i++] = '\0'; + // find in node's child + struct vnode *child_node = *target_node; + // edge cases check + *target_node = node; + if (path[i-1] == '\0') return; + int ret = node->v_ops->lookup(node, &child_node, target_path); + if (ret == 0) { + if (child_node->mount != NULL) { + printf("MOUNT %x\n", child_node->mount); + getdir_r(child_node->mount->root, path+i, target_node, target_path); + } + else { + getdir_r(child_node, path+i, target_node, target_path); + } + } +} + +void getdir(const char* pathname, struct vnode** target_node, char* target_path) { + *target_node = rootfs->root; + if (pathname[0] == '/') { // absolute path + struct vnode* rootnode = rootfs->root; + getdir_r(rootnode, pathname + 1, target_node, target_path); + } + else { // relative path + struct vnode* rootnode = current_thread()->pwd; + getdir_r(rootnode, pathname, target_node, target_path); + } + printf("[Getdir] 0x%x\n", *target_node); +} + +int vfs_open(const char *pathname, int flags, struct file **target) { + // 1. Lookup pathname + struct vnode *target_dir; + char target_path[MAX_PATHNAME_LEN]; + getdir(pathname, &target_dir, target_path); + printf("%s %s\n", pathname, target_path); + if (target_dir->mount != NULL) target_dir = target_dir->mount->root; + // 2. Create a new file handle for this vnode if found. + // 3. Create a new file if O_CREAT is specified in flags and vnode not found + // lookup error code shows if file exist or not or other error occurs + struct vnode *target_file; + int lookup_res = target_dir->v_ops->lookup(target_dir, &target_file, target_path); + printf("fat! %d\n", lookup_res); + if (lookup_res < 0) { // temp change + if (flags & O_CREAT) { + int create_res = target_dir->v_ops->create(target_dir, &target_file, target_path); + if (create_res < 0) return create_res; + } + else + return -1; + } + if (O_CREAT && (lookup_res == 0) && !strcmp(target_path, "FAT_W.TXT")) { + int create_res = target_dir->v_ops->create(target_dir, &target_file, target_path); + if (create_res < 0) return create_res; + } + printf("open: [0x%x]\n", target_file); + struct file *handle = malloc(sizeof(struct file)); + handle->vnode = target_file; + handle->f_pos = 0; + handle->f_ops = target_file->f_ops; + handle->flags = flags; + *target = handle; + return 0; + // 4. Return error code if fails + //return -1; +} + +int vfs_close(struct file *file) { + // 1. release the file handle + if (!file) return -1; + free((void*)file); + // 2. Return error code if fails + return 0; +} + +int vfs_write(struct file *file, const void *buf, size_t len) { + // 1. write len byte from buf to the opened file. + // 2. return written size or error code if an error occurs. + return file->f_ops->write(file, buf, len); +} + +int vfs_read(struct file *file, void *buf, size_t len) { + // 1. read min(len, readable size) byte to buf from the opened file. + // 2. block if nothing to read for FIFO type + // 3. return read size or error code if an error occurs. + return file->f_ops->read(file, buf, len); +} + +int vfs_mkdir(const char *pathname) { + struct vnode *target_dir; + char target_path[MAX_PATHNAME_LEN]; + getdir(pathname, &target_dir, target_path); + struct vnode *child_dir; + int mkdir_res = rootfs->root->v_ops->mkdir(target_dir, &child_dir, target_path); + if (mkdir_res < 0) return mkdir_res; + printf("%x\n", child_dir); + return 0; +} + +int vfs_mount(const char *target, const char *filesystem) { + // check mountpoint is valid + struct vnode* parent_dir; + char path_remain[MAX_PATHNAME_LEN]; + getdir(target, &parent_dir, path_remain); + struct vnode *mount_dir; + int lookup_res = parent_dir->v_ops->lookup(parent_dir, &mount_dir, path_remain); + if (lookup_res < 0) return lookup_res; + //printf("[Mount] [%s]\n", ((struct tmpfs_internal *)mount_dir->internal)->name); + + struct mount *mt = (struct mount*)malloc(sizeof(struct mount)); + if (!strcmp(filesystem, "tmpfs")) { + // mount fs on mountpoint + tmpfs.setup_mount(&tmpfs, mt); + } + if (!strcmp(filesystem, "initramfs")) { + register_filesystem(&initramfs); + initramfs.setup_mount(&initramfs, mt); + } + if (!strcmp(filesystem, "fat32")) { + register_filesystem(&fat32); + fat32.setup_mount(&fat32, mt); + } + mount_dir->mount = mt; + mount_dir->mount->root->mount_parent = parent_dir; + return 0; +} + +int vfs_lookup(const char *pathname, struct vnode **target) { + struct vnode *target_dir; + char target_path[MAX_PATHNAME_LEN]; + getdir(pathname, &target_dir, target_path); + struct vnode *target_file; + int lookup_res = target_dir->v_ops->lookup(target_dir, &target_file, target_path); + if (lookup_res < 0) return lookup_res; + *target = target_file; + return 0; +} + +int vfs_chdir(const char* pathname) { + if (!strcmp(pathname, "/")) { + current_thread()->pwd = rootfs->root; + return 0; + } + struct vnode* parent_dir; + char path_remain[128]; + path_remain[0] = '\0'; + getdir(pathname, &parent_dir, path_remain); + if (!strcmp(path_remain, "")) { // not found + return 0; + } + struct vnode *target_dir; + int lookup_res = parent_dir->v_ops->lookup(parent_dir, &target_dir, path_remain); + if (lookup_res < 0) return lookup_res; + else { + if (target_dir->mount != NULL) { + current_thread()->pwd = target_dir->mount->root; + } + else + current_thread()->pwd = target_dir; + return 0; + } +} \ No newline at end of file