diff --git a/.gitignore b/.gitignore index 8bbd2c8..56b48db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.o *.swp *.swo +initrd.img +main.elf diff --git a/Makefile b/Makefile index c131a32..0d4d85c 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ CROSS = arm-none-eabi- CC = gcc AS = as +OBJCOPY = objcopy MCUFLAGS = -mthumb -mcpu=cortex-m4 #-mfloat-abi=hard -mfpu=fpv4-sp-d16 AFLAGS = $(MCUFLAGS) @@ -37,14 +38,19 @@ export all: @$(MAKE) -C src/kernel + @$(MAKE) -C src/fs @$(MAKE) -C src/user + @echo " INITRD" + @tools/rba initrd.img $$(find initrd/*) + @$(CROSS)$(OBJCOPY) -B arm -I binary -O elf32-littlearm initrd.img initrd.img.o @echo " LINK " $(OUT) - @$(CROSS)$(CC) $(CFLAGS) $(LFLAGS) -o $(OUT) $$(find src/ -name "*.o") + @$(CROSS)$(CC) $(CFLAGS) $(LFLAGS) -o $(OUT) $$(find src/ -name "*.o") initrd.img.o clean: @echo " CLEAN" @$(MAKE) -C src/kernel clean + @$(MAKE) -C src/fs clean @$(MAKE) -C src/user clean @rm -f $(OUT) diff --git a/initrd/hello b/initrd/hello new file mode 100644 index 0000000..5d437a5 --- /dev/null +++ b/initrd/hello @@ -0,0 +1 @@ +Hello, world!! diff --git a/src/fs/Makefile b/src/fs/Makefile new file mode 100644 index 0000000..d22fbf3 --- /dev/null +++ b/src/fs/Makefile @@ -0,0 +1,22 @@ +CFILES = $(wildcard *.c) +AFILES = $(wildcard *.s) +OFILES = $(patsubst %.c, %.o, $(CFILES)) \ + $(patsubst %.s, %.asm.o, $(AFILES)) + +CFLAGS += -I.. -I../arch/cmsis + +all: $(OFILES) + +%.o: %.c + @echo " CC " $< + @$(CROSS)$(CC) $(CFLAGS) -c $< -o $@ + +%.asm.o: %.s + @echo " AS " $< + @$(CROSS)$(AS) $(AFLAGS) -c $< -o $@ + +clean: + @echo " CLEAN" + @rm -f $(OFILES) + + diff --git a/src/fs/initrd.c b/src/fs/initrd.c new file mode 100644 index 0000000..efa430c --- /dev/null +++ b/src/fs/initrd.c @@ -0,0 +1,97 @@ +#include +#include + +#include +#include + +typedef struct { + char *address; + uint32_t pos; + uint32_t size; +} initrd_info; + +extern uint8_t _binary_initrd_img_start[]; +extern uint8_t _binary_initrd_img_size[]; + +static const uint8_t *initrd_start = (uint8_t *)_binary_initrd_img_start; +static const uint32_t initrd_size = (uint32_t)_binary_initrd_img_size; + +void *initrd_open(const char *file); +uint32_t initrd_read(void *info, uint32_t count, uint8_t *buffer); + +char *initrd_getfile(uint32_t offset); + +static const vfs_volume_funcs initrd_funcs = { + initrd_open, + 0, // close + initrd_read, + 0, // write + 0 // readdir +}; + +void initrd_init(void) +{ + vfs_mount(&initrd_funcs, VFS_READONLY); +} + +void *initrd_open(const char *file) +{ + char *ptr; + for (uint32_t i = 0; ptr = initrd_getfile(i), ptr != 0; i++) { + uint32_t len = *((uint32_t *)ptr); + if (!strncmp(file, ptr + 4, len)) { + initrd_info *file = (initrd_info *)malloc( + sizeof(initrd_info)); + file->address = ptr + len + 8; + file->pos = 0; + file->size = *(uint32_t *)(ptr + len + 4); + return file; + } + } + + return 0; +} + +uint32_t initrd_read(void *info, uint32_t count, uint8_t *buffer) +{ + initrd_info *iinfo = (initrd_info *)info; + if (iinfo == 0 || iinfo->address == 0) + return 0; + + uint32_t i; + for (i = 0; i < count; i++) { + if (iinfo->pos >= iinfo->size) + break; + + buffer[iinfo->pos] = iinfo->address[iinfo->pos]; + iinfo->pos++; + } + + return i; +} + +/*char *readfile(const char *name) +{ + char *ptr; + for (uint32_t i = 0; ptr = getfile(i), ptr != 0; i++) { + uint32_t len = *((uint32_t *)ptr); + if (!strncmp(name, ptr + 4, len)) + return ptr + len + 8; + } + return 0; +}*/ + +char *initrd_getfile(uint32_t offset) +{ + char *ptr = (char *)initrd_start; + for (uint32_t i = 0; i < offset; i++) { + uint32_t len = *((uint32_t *)ptr); + uint32_t datalen = *((uint32_t *)(ptr + 4 + len)); + ptr += len + datalen + 8; + if (ptr >= (char *)(initrd_start + initrd_size)) + return 0; + } + + return ptr; +} + diff --git a/src/fs/initrd.h b/src/fs/initrd.h new file mode 100644 index 0000000..dbc0079 --- /dev/null +++ b/src/fs/initrd.h @@ -0,0 +1,6 @@ +#ifndef INTIRD_H_ +#define INITRD_H_ + +void initrd_init(void); + +#endif // INITRD_H_ diff --git a/src/kernel/init.c b/src/kernel/init.c index 6bb7eb5..2ba607d 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -23,6 +23,7 @@ #include "heap.h" #include "task.h" #include "vfs.h" +#include #include extern uint8_t __bss_end__; @@ -45,6 +46,9 @@ int main(void) heap_init(&__bss_end__); gpio_init(); + vfs_init(); + initrd_init(); + // enable FPU //SCB->CPACR |= (0xF << 20); diff --git a/src/kernel/svc.c b/src/kernel/svc.c index 1e03262..f448ee0 100644 --- a/src/kernel/svc.c +++ b/src/kernel/svc.c @@ -26,6 +26,7 @@ extern void gpio_svc(uint32_t *); extern void clock_svc(uint32_t *); extern void task_svc(uint32_t *); +extern void vfs_svc(uint32_t *args); void SVC_Handler(void) { uint32_t *args; @@ -66,6 +67,12 @@ void SVC_Handler(void) { */ clock_svc(args); break; + case 3: /* Filesystem-related calls + * 0 - mount + * 1 - open + */ + vfs_svc(args); + break; default: break; } diff --git a/src/kernel/task.c b/src/kernel/task.c index e4531db..2550e4b 100644 --- a/src/kernel/task.c +++ b/src/kernel/task.c @@ -30,11 +30,6 @@ static task_t *task_queue; static uint8_t task_disable = 0; static pid_t task_next_pid = 0; -void task_exit(int code); -int task_fork(void); -pid_t task_getpid(void); -pid_t task_waitpid(pid_t pid, int *wstatus, int options); - void task_svc(uint32_t *args) { switch (args[0]) { diff --git a/src/kernel/task.h b/src/kernel/task.h index d78e1da..30e3e66 100644 --- a/src/kernel/task.h +++ b/src/kernel/task.h @@ -21,7 +21,6 @@ #ifndef TASK_H_ #define TASK_H_ -#include "vfs.h" #include #define WIFEXITED(w) (w & (1 << 8)) @@ -42,7 +41,6 @@ typedef struct task_t { uint32_t state : 8; uint32_t value : 24; } status; -// vfs_node *cwd; } task_t; enum TASK_STATUS_FLAGS { @@ -76,6 +74,13 @@ void task_hold(uint8_t hold); void task_sleep(uint32_t ms); -//vfs_node *task_getcwd(void); +void task_exit(int code); + +int task_fork(void); + +pid_t task_getpid(void); + +pid_t task_waitpid(pid_t pid, int *wstatus, int options); + #endif // TASK_H_ diff --git a/src/kernel/vfs.c b/src/kernel/vfs.c index a0d9bb4..1720a6b 100644 --- a/src/kernel/vfs.c +++ b/src/kernel/vfs.c @@ -1,15 +1,121 @@ #include "vfs.h" -#include "task.h" -//static vfs_node vfs_root; +#include -int vfs_open(const char *path, int mode) +#define VFS_MAX_VOLS 8 +#define VFS_MAX_FILES 10 + +static vfs_volume vfs_volumes[VFS_MAX_VOLS]; +static vfs_file vfs_files[VFS_MAX_FILES]; + +void vfs_svc(uint32_t *args) +{ + switch (args[0]) { + case 0: + *((int *)args[3]) = vfs_mount((vfs_volume_funcs *)args[1], + args[2]); + break; + case 1: + *((int *)args[3]) = vfs_open((const char *)args[1], args[2]); + break; + case 2: + *((int *)args[4]) = vfs_read(args[1], args[2], (uint8_t *)args[3]); + break; + default: + break; + } +} + +void vfs_init(void) +{ + for (int i = 0; i < VFS_MAX_VOLS; i++) + vfs_volumes[i].flags = 0; + for (int i = 0; i < VFS_MAX_FILES; i++) + vfs_files[i].flags = 0; +} + +int vfs_mount(vfs_volume_funcs *funcs, uint32_t flags) +{ + for (int i = 0; i < VFS_MAX_VOLS; i++) { + if (!(vfs_volumes[i].flags && VFS_MOUNTED)) { + vfs_volumes[i].flags = VFS_MOUNTED | flags; + vfs_volumes[i].funcs = funcs; + return i; + } + } + + return -1; +} + +int vfs_get_drive(const char *path) +{ + // Validate parameters + if (path[0] == '\0' || path[1] == '\0' || path[1] != ':' || + path[2] == '\0' || path[2] != '/') + return -1; + + // Find chosen drive + int drive = -1; + for (int i = 0; i < VFS_MAX_VOLS; i++) { + if (path[0] == ('a' + i) || path[0] == ('A' + i)) { + drive = i; + break; + } + } + + if (drive == -1 || !(vfs_volumes[drive].flags && VFS_MOUNTED)) + return -1; + + return drive; +} + +int vfs_open(const char *path, uint32_t flags) +{ + int drive = vfs_get_drive(path); + if (drive == -1) + return -1; + if (vfs_volumes[drive].funcs->open == 0) + return -1; + + // Find available file handle + int file = -1; + for (int i = 0; i < VFS_MAX_FILES; i++) { + if (!(vfs_files[i].flags & VFS_FILE_OPEN)) { + file = i; + break; + } + } + + if (file == -1) + return -1; + + vfs_files[file].flags = VFS_FILE_OPEN | flags; + vfs_files[file].vol = drive; + vfs_files[file].pid = task_getpid(); + vfs_files[file].fsinfo = + vfs_volumes[drive].funcs->open(path + 3); + + return file; +} + +uint32_t vfs_read(int fd, uint32_t count, uint8_t *buffer) { - (void)path; - (void)mode; -// vfs_node *cd = task_getcwd(); -// if (cd == 0) -// cd = &vfs_root; + if (fd < 0 || fd > VFS_MAX_FILES || count == 0 || buffer == 0) + return 0; + if (vfs_volumes[vfs_files[fd].vol].funcs->read == 0) + return -1; + if (vfs_files[fd].pid != task_getpid()) + return 0; + if ((!(vfs_files[fd].flags & VFS_FILE_READ)) || (vfs_files[fd].flags & + VFS_EOF)) + return 0; - return 0; + uint32_t ret = vfs_volumes[vfs_files[fd].vol].funcs->read(vfs_files[fd].fsinfo, + count, buffer); + + if (ret < count) + vfs_files[fd].flags |= VFS_EOF; + + return ret; } + diff --git a/src/kernel/vfs.h b/src/kernel/vfs.h index 6874905..a6de690 100644 --- a/src/kernel/vfs.h +++ b/src/kernel/vfs.h @@ -3,30 +3,44 @@ #include -struct vfs_node; -struct dirent; +typedef struct { + char name[32]; +} vfs_dirent; + +typedef struct { + void *(*open)(const char *file); + void (*close)(void *info); + uint32_t (*read)(void *info, uint32_t count, uint8_t *buffer); + uint32_t (*write)(void *info, uint32_t count, const uint8_t *buffer); + vfs_dirent *(*readdir)(const char *path); +} vfs_volume_funcs; + +#define VFS_MOUNTED (1 << 0) +#define VFS_READONLY (1 << 1) typedef struct { - uint32_t (*read)(struct vfs_node *, uint32_t, uint32_t, uint8_t *); - uint32_t (*write)(struct vfs_node *, uint32_t, uint32_t, const uint8_t *); - void (*open)(struct vfs_node *); - void (*close)(struct vfs_node *); -} vfs_driver; - -typedef struct vfs_node { - char *name; uint32_t flags; + vfs_volume_funcs *funcs; +} vfs_volume; + +#define VFS_FILE_OPEN (1 << 0) +#define VFS_FILE_READ (1 << 1) +#define VFS_FILE_WRITE (1 << 2) +#define VFS_FILE_MODF (1 << 3) +#define VFS_TEMPORARY (1 << 4) +#define VFS_EOF (1 << 5) + +typedef struct { + uint32_t flags; + uint32_t vol; + uint32_t pid; + void *fsinfo; +} vfs_file; + +void vfs_init(void); - struct vfs_node *parent; - union { - struct vfs_node* children; - vfs_driver *access; - } data; -} vfs_node; - -struct dirent { - char *name; - vfs_node *node; -}; +int vfs_mount(vfs_volume_funcs *funcs, uint32_t flags); +int vfs_open(const char *path, uint32_t flags); +uint32_t vfs_read(int fd, uint32_t count, uint8_t *buffer); #endif // VFS_H_ diff --git a/src/user/user.c b/src/user/user.c index 620ffd0..eb2d662 100644 --- a/src/user/user.c +++ b/src/user/user.c @@ -1,6 +1,8 @@ -#include #include "priv_gpio.h" -#include + +#include +#include +#include void user_delay(uint32_t ms) { @@ -28,6 +30,11 @@ void user_main(void) { gpio(GPIO_MODE, 5, OUTPUT); + int test = vfs_open("A:/hello", VFS_FILE_READ); + char *buf = malloc(20); + int count = vfs_read(test, 20, (uint8_t *)buf); + (void)count; + if (fork() == 0) { while (1) { gpio(GPIO_OUT, 5, 1); diff --git a/tools/rba b/tools/rba new file mode 100755 index 0000000..52f37cb Binary files /dev/null and b/tools/rba differ diff --git a/tools/rba.cpp b/tools/rba.cpp new file mode 100644 index 0000000..03ae1d9 --- /dev/null +++ b/tools/rba.cpp @@ -0,0 +1,72 @@ +/** + * @file rba.cpp + * A "really basic archiver" + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // check args + if (argc < 3) { + std::cout << "Usage: " << argv[0] << " archive files...\n"; + return 0; + } + + std::ofstream archive (argv[1], std::ios::out); + for (int i = 2; i < argc; i++) { + // attempt to read file contents + std::string fileName (argv[i]); + std::ifstream contents (fileName, std::ios::in | std::ios::ate); + std::string shortName; + if (auto pos = fileName.rfind('/'); pos != std::string::npos) + shortName = fileName.substr(pos + 1); + else + shortName = fileName; + + if (contents.good()) { + uint32_t size = contents.tellg(); + contents.seekg(0); + std::cout << "Adding file " << fileName <<" (" + << size << " bytes)...\n"; + + // read in file + char *buffer = new char[size]; + contents.read(buffer, size); + + // write name size/text + uint32_t nameSize = shortName.size(); + archive.write((char *)&nameSize, sizeof(uint32_t)); + archive.write(shortName.data(), nameSize); + // write data size/content + archive.write((char *)&size, sizeof(uint32_t)); + archive.write(buffer, size); + + delete[] buffer; + } else { + std::cout << "Failed to add file " << fileName + << ", continuing...\n"; + } + } + + std::cout << "Created archive " << argv[1] << '\n'; + return 0; +}