summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/ints.txt2
-rw-r--r--fs-skel/user/test.txt1
-rw-r--r--makefile23
-rw-r--r--qemu.gdb5
-rw-r--r--src/kernel/drive.c28
-rw-r--r--src/kernel/drive.h3
-rw-r--r--src/kernel/fat.c104
-rw-r--r--src/kernel/fat.h2
-rw-r--r--src/kernel/ide.c78
-rw-r--r--src/kernel/idt.c27
-rw-r--r--src/kernel/isrs.asm2
-rw-r--r--src/kernel/kbd.c4
-rw-r--r--src/kernel/shutdown.c20
-rw-r--r--src/kernel/shutdown.h6
-rw-r--r--src/kernel/util.c36
-rw-r--r--src/kernel/util.h1
-rw-r--r--src/kernel/window.c54
-rw-r--r--src/kernel/window.h2
-rw-r--r--src/user/filetest/filetest.c36
-rw-r--r--src/user/include/knob/file.h2
-rw-r--r--src/user/include/pland/syscall.h12
-rw-r--r--src/user/knob/file.c15
22 files changed, 408 insertions, 55 deletions
diff --git a/doc/ints.txt b/doc/ints.txt
index 631ed52..095298f 100644
--- a/doc/ints.txt
+++ b/doc/ints.txt
@@ -60,6 +60,8 @@ table 1:
wait ipc read | 0x17 | | reading task | | | |
is task running | 0x18 | boolean | task handle | | | |
get timestamp | 0x19 | secs since 2000 | | | | |
+ file write | 0x1a | written | handle | file offset | count | buffer |
+ set file size | 0x1b | | handle | new file size | | |
table 2:
diff --git a/fs-skel/user/test.txt b/fs-skel/user/test.txt
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/fs-skel/user/test.txt
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/makefile b/makefile
index 611593a..073f130 100644
--- a/makefile
+++ b/makefile
@@ -22,7 +22,7 @@ clean:
rm -r obj out || true
qemu: out/disk.img
- qemu-system-i386 -m 512 -s -S out/disk.img
+ gdb -x qemu.gdb
bochs: out/disk.img
bochs -q
@@ -37,7 +37,8 @@ out/fs/bin/%: obj/%.elf
out/fs: out/fs/bin/init out/fs/bin/highway out/fs/bin/meminfo \
out/fs/bin/terminal out/fs/bin/hello out/fs/bin/mkpopup \
- out/fs/bin/dirlist out/fs/bin/ttt out/fs/bin/time
+ out/fs/bin/dirlist out/fs/bin/ttt out/fs/bin/time \
+ out/fs/bin/filetest
touch out/fs
cp -r fs-skel/* out/fs/
@@ -49,13 +50,13 @@ obj/kernel/%.kao: src/kernel/%.asm
mkdir -p $(shell dirname $@)
nasm ${nasmargs} $< -o $@
-out/kernel.bin: obj/kernel/drive.ko obj/kernel/fat.ko obj/kernel/ide.ko \
- obj/kernel/idt.ko obj/kernel/log.ko obj/kernel/main.ko \
- obj/kernel/panic.ko obj/kernel/pci.ko obj/kernel/elf.ko \
- obj/kernel/serial.ko obj/kernel/task.ko obj/kernel/util.ko \
- obj/kernel/window.ko obj/kernel/isrs.kao obj/kernel/kbd.ko \
- obj/kernel/pmap.ko obj/kernel/paging.ko obj/kernel/dump.ko \
- obj/kernel/cmos.ko obj/kernel/settings.ko
+out/kernel.bin: obj/kernel/drive.ko obj/kernel/fat.ko obj/kernel/ide.ko \
+ obj/kernel/idt.ko obj/kernel/log.ko obj/kernel/main.ko \
+ obj/kernel/panic.ko obj/kernel/pci.ko obj/kernel/elf.ko \
+ obj/kernel/serial.ko obj/kernel/task.ko obj/kernel/util.ko \
+ obj/kernel/window.ko obj/kernel/isrs.kao obj/kernel/kbd.ko \
+ obj/kernel/pmap.ko obj/kernel/paging.ko obj/kernel/dump.ko \
+ obj/kernel/cmos.ko obj/kernel/settings.ko obj/kernel/shutdown.ko
mkdir -p out
ld -T src/kernel/elf-link.ld $^ -o obj/kernel.elf
objcopy -O binary obj/kernel.elf out/kernel.bin
@@ -131,4 +132,8 @@ obj/ttt.elf : obj/ttt/main.o obj/popups.so obj/libfont.so \
obj/time.elf: obj/time/time.o obj/libterm.so obj/knob.so \
obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
+
+obj/filetest.elf: obj/filetest/filetest.o obj/libterm.so obj/knob.so \
+ obj/c.rto
ld -T src/user/runtimes/c/elf.ld $^ -o $@ \ No newline at end of file
diff --git a/qemu.gdb b/qemu.gdb
new file mode 100644
index 0000000..e44d392
--- /dev/null
+++ b/qemu.gdb
@@ -0,0 +1,5 @@
+target remote | qemu-system-i386 -m 512 -gdb stdio out/disk.img
+symbol-file obj/kernel.elf
+set disassembly-flavor intel
+break main
+layout split \ No newline at end of file
diff --git a/src/kernel/drive.c b/src/kernel/drive.c
index 2ae050f..8dc0e68 100644
--- a/src/kernel/drive.c
+++ b/src/kernel/drive.c
@@ -1,6 +1,7 @@
#include "drive.h"
#include "panic.h"
#include "fat.h"
+#include "log.h"
uint8_t n_drives;
struct drive drives[256];
@@ -14,21 +15,9 @@ static file_id_t unknown_get_file(const struct drive *d, const char *path) {
return 0;
}
-static void unknown_free_file(const struct drive *d, file_id_t fid) {
- PANIC("Free file called on unknown file system.");
-}
-
-static void unknown_load_sector(const struct drive *d, file_id_t fid, uint32_t sector, void *at) {
- PANIC("Load sector called on unknown file system.");
-}
-
-static uint32_t unknown_get_file_length(const struct drive *d, file_id_t fid) {
- PANIC("Get file length called on unknown file system.");
-}
-
__attribute__ ((const))
static uint32_t unknown_get_free_sectors(const struct drive *d) {
- return -1;
+ return 0;
}
__attribute__ ((const))
@@ -41,15 +30,22 @@ static uint32_t unknown_n_dir_entries(const struct drive *d, const char *path) {
return 0;
}
+static void unknown_no_call(const struct drive *d) {
+ logf(LOG_ERROR, "file-level operation called on drive %u (%s), which is an unknown file system", d - drives + 1, d->fs_type);
+}
+
static inline void determine_fs(struct drive *d) {
if (try_fat_init_drive(d))
return;
d->fs_type = "Unknown";
d->get_file = &unknown_get_file;
- d->free_file = &unknown_free_file;
- d->load_sector = &unknown_load_sector;
- d->get_file_length = &unknown_get_file_length;
+ d->free_file = &unknown_no_call;
+ d->load_sector = &unknown_no_call;
+ d->save_sector = &unknown_no_call;
+ d->is_writable = &unknown_no_call;
+ d->get_file_length = &unknown_no_call;
+ d->set_file_length = &unknown_no_call;
d->enumerate_dir = &unknown_enumerate_dir;
d->n_dir_entries = &unknown_n_dir_entries;
d->get_free_sectors = &unknown_get_free_sectors;
diff --git a/src/kernel/drive.h b/src/kernel/drive.h
index 03fe4b6..93811e8 100644
--- a/src/kernel/drive.h
+++ b/src/kernel/drive.h
@@ -33,7 +33,10 @@ struct drive {
file_id_t (*get_file) (const struct drive *d, const char *path);
void (*free_file) (const struct drive *d, file_id_t fid);
void (*load_sector) (const struct drive *d, file_id_t fid, uint32_t sector, void *at);
+ void (*save_sector) (const struct drive *d, file_id_t fid, uint32_t sector, const void *from);
+ bool (*is_writable) (const struct drive *d, file_id_t fid);
uint32_t (*get_file_length) (const struct drive *d, file_id_t fid);
+ void (*set_file_length) (const struct drive *d, file_id_t fid, uint32_t new_len);
uint32_t (*enumerate_dir) (const struct drive *d, const char *path, struct directory_content_info *info, uint32_t max);
uint32_t (*n_dir_entries) (const struct drive *d, const char *path);
uint32_t (*get_free_sectors)(const struct drive *d);
diff --git a/src/kernel/fat.c b/src/kernel/fat.c
index 5ba62cf..b865859 100644
--- a/src/kernel/fat.c
+++ b/src/kernel/fat.c
@@ -3,6 +3,7 @@
#include "util.h"
#include "ata.h"
#include "fat.h"
+#include "log.h"
#include "pmap.h"
#define MAX_FAT_DRIVES 16
@@ -18,14 +19,17 @@ enum {
FA_LABEL = 0x08,
FA_DIRECTORY = 0x10,
FA_ARCHIVE = 0x20,
+};
- FA_LFN = 0x0f
+enum {
+ //applied to every file without FA_READ_ONLY
+ FEA_OPEN_EXCLUSIVE = 0x01
};
struct directory_entry {
uint8_t name[11];
uint8_t attrib;
- uint8_t name_case;
+ uint8_t extra_attrib;//non-standard attributes
uint8_t created_decimal;
uint16_t created_time;
uint16_t created_date;
@@ -70,9 +74,12 @@ struct open_file_info {
uint16_t start_cluster;
uint32_t length;
+
+ uint8_t attrib;
};
struct fat_drive_info {
+ const struct drive *from_drive;
const struct fat_info *fi;
uint16_t *fat;
uint16_t root_start;
@@ -109,6 +116,11 @@ static void load_cluster(uint16_t c, void *to) {
cur_drive->read_sectors(cur_drive, s, 1, to);
}
+static void save_cluster(uint16_t c, const void *from) {
+ uint32_t s = CTOS(c, cur_fdi);
+ cur_drive->write_sectors(cur_drive, s, 1, from);
+}
+
__attribute__ ((pure))
static uint16_t next_cluster(uint16_t c) {
uint16_t found = infos[cur_id].fat[c];
@@ -226,22 +238,47 @@ static file_id_t fat_get_file(const struct drive *d, const char *path) {
return 0;
}
+ if (cur_dir->extra_attrib & FEA_OPEN_EXCLUSIVE) {
+ d->done(d);
+ return 0;//maybe have a special return value for this?
+ }
+
+ if (!(cur_dir->attrib & FA_READ_ONLY)) {
+ cur_dir->extra_attrib |= FEA_OPEN_EXCLUSIVE;
+ d->write_sectors(d, cur_sect, 1, fat_driver_buffer);
+ }
+
d->done(d);
+
open_files[n].di_sector = cur_sect;
open_files[n].di_number = cur_dir - (struct directory_entry *)fat_driver_buffer;
open_files[n].start_cluster = cur_dir->first_cluster;
open_files[n].length = cur_dir->length;
+ open_files[n].attrib = cur_dir->attrib;
+
return n;
}
- PANIC("Maximum number of files open reached for FAT drive.");
+ d->done(d);
+ logf(LOG_ERROR, "Maximum number of open files (%d) reached for drive %u (%s, FAT).", MAX_OPEN_FILES_PER_DRIVE, d - drives + 1, d->drive_type);
+ return 0;
}
static void fat_free_file(const struct drive *d, file_id_t fid) {
- infos[d->drive_id].open_files[fid - 1].di_sector = 0;
+ struct open_file_info *const of = infos[d->drive_id].open_files + fid - 1;
+
+ if (!(of->attrib & FA_READ_ONLY)) {
+ d->ready(d);
+ d->read_sectors(d, of->di_sector, 1, fat_driver_buffer);
+ ((struct directory_entry *)fat_driver_buffer)[of->di_number].extra_attrib &= ~FEA_OPEN_EXCLUSIVE;
+ d->write_sectors(d, of->di_sector, 1, fat_driver_buffer);
+ d->done(d);
+ }
+
+ of->di_sector = 0;
}
-static void fat_load_sector(const struct drive *d, file_id_t fid, uint32_t sector, void *at) {
+static uint16_t get_nth_cluster(const struct drive *d, file_id_t fid, uint32_t sector) {
cur_drive = d;
cur_id = d->drive_id;
cur_fdi = &infos[cur_id];
@@ -250,16 +287,48 @@ static void fat_load_sector(const struct drive *d, file_id_t fid, uint32_t secto
for (uint32_t i = 0; i < sector; ++i)
c = next_cluster(c);
+ return c;
+}
+
+static void fat_load_sector(const struct drive *d, file_id_t fid, uint32_t sector, void *at) {
+ uint16_t c = get_nth_cluster(d, fid, sector);
+
d->ready(d);
load_cluster(c, at);
d->done(d);
}
+static void fat_save_sector(const struct drive *d, file_id_t fid, uint32_t sector, const void *from) {
+ if (infos[d->drive_id].open_files[fid - 1].attrib & FA_READ_ONLY)
+ return;//maybe return special value for this
+
+ uint16_t c = get_nth_cluster(d, fid, sector);
+
+ d->ready(d);
+ save_cluster(c, from);
+ d->done(d);
+}
+
__attribute__ ((pure))
static uint32_t fat_get_file_length(const struct drive *d, file_id_t fid) {
return infos[d->drive_id].open_files[fid - 1].length;
}
+static void fat_set_file_length(const struct drive *d, file_id_t fid, uint32_t new_length) {
+ struct open_file_info *const of = infos[d->drive_id].open_files + fid - 1;
+ if (of->attrib & FA_READ_ONLY)
+ return;//maybe return special vlue for this
+
+ if (((new_length - 1) >> 9) + 1 != ((of->length - 1) >> 9) + 1)
+ PANIC("TODO: resizing FAT file to different sector count");
+
+ of->length = new_length;
+
+ d->read_sectors(d, of->di_sector, 1, fat_driver_buffer);
+ ((struct directory_entry *)fat_driver_buffer)[of->di_number].length = new_length;
+ d->write_sectors(d, of->di_sector, 1, fat_driver_buffer);
+}
+
__attribute__ ((pure))
static uint32_t fat_get_free_sectors(const struct drive *d) {
uint16_t *start = infos[d->fs_id].fat + 2;
@@ -429,6 +498,11 @@ static uint32_t fat_n_dir_entries(const struct drive *d, const char *path) {
}
}
+__attribute__ ((__pure__))
+static bool fat_is_writable(const struct drive *d, file_id_t fid) {
+ return !(infos[d->drive_id].open_files[fid - 1].attrib & FA_READ_ONLY);
+}
+
void init_fat() {
next_fi = allocate_kernel_pages(1);
next_id = 0;
@@ -450,25 +524,41 @@ bool try_fat_init_drive(struct drive *d) {
d->get_file = &fat_get_file;
d->free_file = &fat_free_file;
d->load_sector = &fat_load_sector;
+ d->save_sector = &fat_save_sector;
d->get_file_length = &fat_get_file_length;
+ d->set_file_length = &fat_set_file_length;
d->enumerate_dir = &fat_enumerate_dir;
d->n_dir_entries = &fat_n_dir_entries;
d->get_free_sectors = &fat_get_free_sectors;
+ d->is_writable = &fat_is_writable;
d->fs_id = next_id;
infos[next_id].fi = next_fi;
infos[next_id].fat = allocate_kernel_pages(((next_fi->sectors_per_fat - 1) >> 3) + 1);
- infos[next_id].root_start = next_fi->reserved_sectors +
+ infos[next_id].root_start = next_fi->reserved_sectors +
next_fi->sectors_per_fat * next_fi->fats;
infos[next_id].data_start = infos[next_id].root_start +
((next_fi->root_entries - 1) >> 4) + 1;
+ logf(LOG_INFO, "0x%h <- 0x%h", &infos[next_id].from_drive, drives + n_drives);
+ infos[next_id].from_drive = drives + n_drives;
d->read_sectors(d, next_fi->reserved_sectors, next_fi->sectors_per_fat, infos[next_id].fat);
- struct open_file_info *open_files = infos[next_id].open_files - 1;
+ struct open_file_info *open_files = infos[next_id].open_files;
for (file_id_t i = 0; i < MAX_OPEN_FILES_PER_DRIVE; ++i)
open_files[i].di_sector = 0;
alloc_next_fi();
++next_id;
return true;
+}
+
+void fat_ready_shutdown() {
+ for (uint8_t i = 0; i < next_id; ++i)
+ for (uint8_t j = 0; j < MAX_OPEN_FILES_PER_DRIVE; ++j)
+ if (infos[i].open_files[j].di_sector) {
+ logf(LOG_INFO, "i = %d, j = %d", i, j);
+ logf(LOG_INFO, "freeing file %d from drive 0x%h (number %d)", j + 1, infos[i].from_drive, infos[i].from_drive - drives + 1);
+ logf(LOG_INFO, "0x%h -> 0x%h", &infos[i].from_drive, infos[i].from_drive);
+ fat_free_file(infos[i].from_drive, j + 1);
+ }
} \ No newline at end of file
diff --git a/src/kernel/fat.h b/src/kernel/fat.h
index 3ca9cc3..364b3db 100644
--- a/src/kernel/fat.h
+++ b/src/kernel/fat.h
@@ -8,4 +8,6 @@ void init_fat();
bool try_fat_init_drive(struct drive *d);
+void fat_ready_shutdown();
+
#endif \ No newline at end of file
diff --git a/src/kernel/ide.c b/src/kernel/ide.c
index 39525ad..1f7df10 100644
--- a/src/kernel/ide.c
+++ b/src/kernel/ide.c
@@ -4,6 +4,7 @@
#include "panic.h"
#include "util.h"
#include "ata.h"
+#include "log.h"
#include "pci.h"
#define MAX_IDE_DRIVES 8
@@ -17,7 +18,7 @@ struct ide_drive_info {
static struct ide_drive_info ide_drives[MAX_IDE_DRIVES];
static drive_id_t n_ide_drives;
-typedef uint16_t spinner_t;
+typedef uint32_t spinner_t;
//returns the status after waiting
static uint8_t wait_for_ready(uint16_t base_port) {
@@ -31,6 +32,17 @@ static uint8_t wait_for_ready(uint16_t base_port) {
PANIC("Spun out in IDE driver.");
}
+static void wait_for_not_busy(uint16_t base_port) {
+ for (spinner_t n = -1; n; --n) {
+ uint8_t s = inb(base_port | ATA_REG_STATUS);
+ if (s & ATA_STATUS_ERROR)
+ PANIC("Error status in IDE driver.");
+ if (!(s & ATA_STATUS_BUSY))
+ return;
+ }
+ PANIC("Spun out in IDE driver.");
+}
+
//returns the status after waiting
static uint8_t wait_for_error_or_ready(uint16_t base_port) {
for (spinner_t n = -1; n; --n) {
@@ -53,6 +65,21 @@ static uint8_t wait_for_data_ready_not_busy(uint16_t base_port) {
PANIC("Spun out in IDE driver.");
}
+static void prepare_ata_access(uint16_t port, bool slave, uint32_t lba, uint32_t count) {
+ if (lba & 0xf0000000)
+ PANIC("IDE ATA driver does not support reads starting past 128GiB currently.");
+ if (count & 0xffffff00)
+ PANIC("IDE ATA driver does not support reads over 128kiB in length currently.");
+
+ outb(port | ATA_REG_SELECT, (slave ? 0xf0 : 0xe0) | (lba >> 24));
+ //spin?
+
+ outb(port | ATA_REG_COUNT, count);
+ outb(port | ATA_REG_LBA0, lba);
+ outb(port | ATA_REG_LBA1, lba >> 8);
+ outb(port | ATA_REG_LBA2, lba >> 16);
+}
+
static uint32_t ide_ata_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) {
if (start >= d->n_sectors)
return 0;
@@ -61,38 +88,49 @@ static uint32_t ide_ata_rs(const struct drive *d, uint32_t start, uint32_t count
if (!count)
return 0;
- if (start & 0xf0000000)
- PANIC("IDE ATA driver does not support reads starting past 256MiB currently.");
- if (count & 0xffffff00)
- PANIC("IDE ATA driver does not support reads over 128kiB in length currently.");
-
- uint32_t lba = start & 0x00ffffff;
-
- struct ide_drive_info *info = ide_drives + d->drive_id;
-
- outb(info->base_port | ATA_REG_SELECT, (info->slave ? 0xf0 : 0xe0) | (start >> 24));
- //spin?
-
- outb(info->base_port | ATA_REG_COUNT, count);
- outb(info->base_port | ATA_REG_LBA0, lba);
- outb(info->base_port | ATA_REG_LBA1, lba >> 8);
- outb(info->base_port | ATA_REG_LBA2, lba >> 16);
+ const struct ide_drive_info *const info = ide_drives + d->drive_id;
+ prepare_ata_access(info->base_port, info->slave, start, count);
outb(info->base_port | ATA_REG_CMD, ATA_CMD_READ);
uint16_t *buf16 = (uint16_t *)buffer;
- for (uint16_t i = 0; i < count; ++i) {
+ for (uint32_t i = 0; i < count; ++i) {
wait_for_data_ready_not_busy(info->base_port);
for (uint16_t j = 0; j < 256; ++j)
- *buf16++ = inw(info->base_port | ATA_REG_DATA);
+ *(buf16++) = inw(info->base_port | ATA_REG_DATA);
}
return count;
}
static uint32_t ide_ata_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) {
- PANIC("IDE ATA writing not implemented yet.");
+ if (start >= d->n_sectors)
+ return 0;
+ if (start + count > d->n_sectors)
+ count = d->n_sectors - start;
+ if (!count)
+ return 0;
+
+//logf(LOG_INFO, "writing IDE sectors: 0x%h - 0x%h", start * 512, start * 512 + count * 512 - 1);
+
+ const struct ide_drive_info *const info = ide_drives + d->drive_id;
+
+ prepare_ata_access(info->base_port, info->slave, start, count);
+ outb(info->base_port | ATA_REG_CMD, ATA_CMD_WRITE);
+
+ const uint16_t *buf16 = (const uint16_t *)buffer;
+
+ for (uint32_t i = 0; i < count; ++i) {
+ wait_for_not_busy(info->base_port);
+ for (uint16_t j = 0; j < 256; ++j)
+ outw(info->base_port | ATA_REG_DATA, *(buf16++));
+ }
+
+ outb(info->base_port | ATA_REG_CMD, ATA_CMD_FLUSH);
+ wait_for_not_busy(info->base_port);
+
+ return count;
}
static uint32_t ide_atapi_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) {
diff --git a/src/kernel/idt.c b/src/kernel/idt.c
index c79ae7b..1a78da0 100644
--- a/src/kernel/idt.c
+++ b/src/kernel/idt.c
@@ -52,6 +52,12 @@ uint32_t sc_file_get_size(uint32_t handle) {
return drives[handle >> 8].get_file_length(drives + (handle >> 8), handle & 0xff);
}
+void sc_file_set_size(uint32_t handle, uint32_t new_size) {
+ if (!handle)
+ return;
+ drives[handle >> 8].set_file_length(drives + (handle >> 8), handle & 0xff, new_size);
+}
+
uint32_t sc_file_read(uint32_t handle, uint32_t file_offset, uint32_t count, void *buffer) {
if (!handle)
return 0;
@@ -62,6 +68,23 @@ uint32_t sc_file_read(uint32_t handle, uint32_t file_offset, uint32_t count, voi
return count;
}
+uint32_t sc_file_write(uint32_t handle, uint32_t file_offset, uint32_t count, void *buffer) {
+ if (!handle)
+ return 0;
+ const struct drive *const d = drives + (handle >> 8);
+ const fs_id_t fid = handle & 0xff;
+
+ if (!d->is_writable(d, fid))
+ return 0;
+
+ const uint32_t l = d->get_file_length(d, fid);
+ if (file_offset + count > l)
+ count = l - file_offset;
+
+ mfcpy(d, fid, file_offset, buffer, count);
+ return count;
+}
+
uint32_t sc_start_task(uint32_t drive_number, char *path, const char *pass, uint32_t io_task) {
switch_to_kernel_cr3();
uint32_t process_id = try_elf_run(drives + drive_number, vma_to_pma(active_task->page_directory, path), pass, io_task);
@@ -183,7 +206,9 @@ void const *syscall_table[] = {
&find_unread_ipc,
&sc_wait_ipc_read,
&sc_is_task_running,
- &sc_get_timestamp
+ &sc_get_timestamp,
+ &sc_file_write,
+ &sc_file_set_size
};
//these aren't really void ()'s, but gcc complains if we take an address of a void, so we give it a type
diff --git a/src/kernel/isrs.asm b/src/kernel/isrs.asm
index f547bfb..1aba884 100644
--- a/src/kernel/isrs.asm
+++ b/src/kernel/isrs.asm
@@ -25,7 +25,7 @@ extern exception_halt
extern pf_check_stack
extern dump
-n_syscalls equ 0x1a
+n_syscalls equ 0x1c
;section .bss
;_debug_is_start_task resb 1
diff --git a/src/kernel/kbd.c b/src/kernel/kbd.c
index 96d2069..47894cd 100644
--- a/src/kernel/kbd.c
+++ b/src/kernel/kbd.c
@@ -1,6 +1,7 @@
#include <stdint.h>
#include "settings.h"
+#include "shutdown.h"
#include "window.h"
#include "drive.h"
#include "panic.h"
@@ -221,6 +222,9 @@ enum kbd_isr_result on_kbd_isr() {
if (!is_up && (entry == KEY_PAUSE) && (keymods & ALTS))
return (keymods & SHIFTS) ? SHIFT_DUMP : DUMP;
+ if (!is_up && (entry == KEY_Q) && (keymods & WINS) && (keymods & SHIFTS))
+ shutdown();
+
on_action((struct window_action){
.action_type = is_up ? KEY_UP : KEY_DOWN,
.as_key = (struct key_packet){
diff --git a/src/kernel/shutdown.c b/src/kernel/shutdown.c
new file mode 100644
index 0000000..9f7ccc7
--- /dev/null
+++ b/src/kernel/shutdown.c
@@ -0,0 +1,20 @@
+#include "paging.h"
+#include "window.h"
+#include "fat.h"
+#include "log.h"
+
+__attribute__ ((noreturn))
+void shutdown() {
+ logf(LOG_INFO, "Shutting down...");
+
+ asm ("cli");
+ switch_to_kernel_cr3();
+
+ fat_ready_shutdown();
+
+ logf(LOG_INFO, "Finished getting ready for shutdown.");
+
+ show_shutdown();
+ while (1)
+ asm ("hlt");
+} \ No newline at end of file
diff --git a/src/kernel/shutdown.h b/src/kernel/shutdown.h
new file mode 100644
index 0000000..2a76e64
--- /dev/null
+++ b/src/kernel/shutdown.h
@@ -0,0 +1,6 @@
+#ifndef SHUTDOWN_H
+#define SHUTDOWN_H
+
+void shutdown() __attribute__ ((noreturn));
+
+#endif \ No newline at end of file
diff --git a/src/kernel/util.c b/src/kernel/util.c
index ea0496e..279616c 100644
--- a/src/kernel/util.c
+++ b/src/kernel/util.c
@@ -2,7 +2,15 @@
#include <stdint.h>
#include "drive.h"
+#include "log.h"
+#define ACROSS 0x04004e60
+
void memcpy(void *to, const void *from, uint32_t n) {
+ if (((uint32_t)to <= ACROSS) && ((uint32_t)(to + n) > ACROSS)) {
+ logf(LOG_WARN, "memcpy across 0x%h!", ACROSS);
+ logf(LOG_INFO, "was: memcpy(0x%h, 0x%h, %d)", to, from, n);
+ }
+
uint32_t *tp = to;
const uint32_t *fp = from;
while (n >= 4) {
@@ -39,4 +47,32 @@ void fmcpy(void *to, const struct drive *d, file_id_t f, uint32_t from, uint32_t
d->load_sector(d, f, fsi, buf);
memcpy(to + i, buf, n);
+}
+
+void mfcpy(const struct drive *d, file_id_t f, uint32_t to, const void *from, uint32_t n) {
+ uint8_t buf[512];
+
+ const uint16_t first_batch = to % 512 + n >= 512 ? 512 - to % 512 : n;
+ if (first_batch) {
+ d->load_sector(d, f, to / 512, buf);
+ memcpy(buf + to % 512, from, first_batch);
+ d->save_sector(d, f, to / 512, buf);
+ n -= first_batch;
+ from += first_batch;
+ }
+
+ uint32_t sector_on = to / 512 + 1;
+ while (n >= 512) {
+ memcpy(buf, from, 512);
+ d->save_sector(d, f, sector_on, buf);
+ n -= 512;
+ from += 512;
+ ++sector_on;
+ }
+
+ if (n) {
+ d->load_sector(d, f, sector_on, buf);
+ memcpy(buf, from, n);
+ d->save_sector(d, f, sector_on, buf);
+ }
} \ No newline at end of file
diff --git a/src/kernel/util.h b/src/kernel/util.h
index c5b784a..2500904 100644
--- a/src/kernel/util.h
+++ b/src/kernel/util.h
@@ -43,6 +43,7 @@ static inline uint32_t ind(uint16_t port) {
void memcpy(void *to, const void *from, uint32_t n);
void fmcpy(void *to, const struct drive *d, file_id_t f, uint32_t from, uint32_t n);
+void mfcpy(const struct drive *d, file_id_t f, uint32_t to, const void *from, uint32_t n);
uint8_t u32_dec(uint32_t n, char *b);
uint8_t u16_dec(uint16_t n, char *b);
diff --git a/src/kernel/window.c b/src/kernel/window.c
index 79d718e..1c4eb26 100644
--- a/src/kernel/window.c
+++ b/src/kernel/window.c
@@ -82,6 +82,60 @@ static void blit() {
}
}
+#define POWER_OFF_MESSAGE_PITCH 45
+#define POWER_OFF_MESSAGE_ROWS 28
+static const bool power_off_message[] = {
+ 1,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,0,1,
+ 1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,1,
+ 0,1,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,
+ 0,1,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,1,1,1,1,0,0,0,1,0,1,0,
+ 0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,
+ 0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,
+ 0,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,
+ 0,0,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,0,0,0,1,0,0,0,1,0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+
+#define POWER_OFF_SCALE 3
+
+void show_shutdown() {
+ for (uint32_t i = 0; i < VBE_MODE_INFO->height * VBE_MODE_INFO->width; ++i)
+ buffer[i] = (struct pixel){.r = 0, .g = 0, .b = 0};
+
+ const uint16_t x_pad = (VBE_MODE_INFO->width - POWER_OFF_MESSAGE_PITCH * POWER_OFF_SCALE) / 2;
+ const uint16_t y_pad = (VBE_MODE_INFO->height - POWER_OFF_MESSAGE_ROWS * POWER_OFF_SCALE) / 2;
+
+ for (uint8_t r = 0; r < POWER_OFF_MESSAGE_ROWS; ++r)
+ for (uint8_t i = 0; i < POWER_OFF_MESSAGE_PITCH; ++i)
+ if (power_off_message[r * POWER_OFF_MESSAGE_PITCH + i]) {
+ const uint32_t o = x_pad + i * POWER_OFF_SCALE + (y_pad + r * POWER_OFF_SCALE) * VBE_MODE_INFO->width;
+ for (uint8_t y = 0; y < POWER_OFF_SCALE; ++y)
+ for (uint8_t x = 0; x < POWER_OFF_SCALE; ++x)
+ buffer[o + x + y * VBE_MODE_INFO->width] = (struct pixel){.r = 255, .g = 255, .b = 255};
+ }
+
+ blit();
+}
+
static inline void draw_hz_line(uint16_t y, uint16_t xs, uint16_t xm, struct pixel color) {
if (y >= VBE_MODE_INFO->height)
return;
diff --git a/src/kernel/window.h b/src/kernel/window.h
index ebd0496..0a83845 100644
--- a/src/kernel/window.h
+++ b/src/kernel/window.h
@@ -31,4 +31,6 @@ void on_action(struct window_action packet);
void delete_any_windows_from(struct task_state *tstate);
+void show_shutdown();
+
#endif
diff --git a/src/user/filetest/filetest.c b/src/user/filetest/filetest.c
new file mode 100644
index 0000000..e7792ff
--- /dev/null
+++ b/src/user/filetest/filetest.c
@@ -0,0 +1,36 @@
+#include <libterm/terminal.h>
+#include <knob/file.h>
+
+#define TEST_FILE "user/test.txt"
+
+void main() {
+ struct file *f = open_file(TEST_FILE);
+ if (!f) {
+ term_add_sz("Failed to open " TEST_FILE ".\n");
+ return;
+ }
+
+ char ch;
+ if (!read_from_file(f, 1, &ch)) {
+ term_add_sz(TEST_FILE " is empty.\n");
+ close_file(f);
+ return;
+ }
+
+ term_addf(TEST_FILE " contained '%c'.\n", ch);
+
+ if (++ch >= 0x7f)
+ ch = 0x21;
+
+ seek_file_to(f, 0);
+
+ if (!write_to_file(f, 1, &ch)) {
+ term_add_sz("Failed to write to " TEST_FILE ".\n");
+ close_file(f);
+ return;
+ }
+
+ term_addf("Wrote '%c' to " TEST_FILE ".\n", ch);
+
+ close_file(f);
+} \ No newline at end of file
diff --git a/src/user/include/knob/file.h b/src/user/include/knob/file.h
index 8862098..d283288 100644
--- a/src/user/include/knob/file.h
+++ b/src/user/include/knob/file.h
@@ -12,12 +12,14 @@ struct file *open_file(const char *path);
void close_file(struct file *f);
uint32_t read_from_file(struct file *f, uint32_t max, void *buf);
+uint32_t write_to_file(struct file *f, uint32_t max, void *buf);
//return value and max_length don't include null terminator
uint32_t read_line_from_file(struct file *f, char *sz, uint32_t max_length);
uint32_t seek_file_to(struct file *f, uint32_t to);
int32_t seek_file_by(struct file *f, int32_t by);
uint32_t file_size(struct file *f) __attribute__ ((pure));
+void trunc_file(struct file *f);
//return value must be manually freed, unless it is a null pointer
_dir_info_entry_t *get_directory_info(const char *path, uint32_t *count_out);
diff --git a/src/user/include/pland/syscall.h b/src/user/include/pland/syscall.h
index 9518d09..3c4f421 100644
--- a/src/user/include/pland/syscall.h
+++ b/src/user/include/pland/syscall.h
@@ -51,7 +51,9 @@ enum _scn {
_SCN_FIND_UNREAD_IPC,
_SCN_WAIT_IPC_READ,
_SCN_IS_TASK_RUNNING,
- _SCN_GET_TIMESTAMP
+ _SCN_GET_TIMESTAMP,
+ _SCN_FILE_WRITE,
+ _SCN_SET_FILE_SIZE
};
static inline uint32_t _sc0(enum _scn eax) {
@@ -240,4 +242,12 @@ static inline uint32_t _get_timestamp() {
return _sc0(_SCN_GET_TIMESTAMP);
}
+static inline uint32_t _file_write(_file_handle_t handle, uint32_t offset, uint32_t count, const void *from) {
+ return _sc4(_SCN_FILE_WRITE, handle, offset, count, (uint32_t)from);
+}
+
+static inline void _set_file_size(_file_handle_t handle, uint32_t new_size) {
+ _sc2(_SCN_SET_FILE_SIZE, handle, new_size);
+}
+
#endif \ No newline at end of file
diff --git a/src/user/knob/file.c b/src/user/knob/file.c
index 3083503..283f984 100644
--- a/src/user/knob/file.c
+++ b/src/user/knob/file.c
@@ -1,4 +1,5 @@
#include <knob/format.h>
+#include <knob/block.h>
#include <knob/panic.h>
#include <knob/heap.h>
@@ -83,6 +84,16 @@ uint32_t read_from_file(struct file *f, uint32_t max, void *buf) {
return read;
}
+uint32_t write_to_file(struct file *f, uint32_t max, void *buf) {
+ if (f->position + max > f->length)
+ _set_file_size(f->handle, f->length = f->position + max);
+
+ uint32_t written = _file_write(f->handle, f->position, max, buf);
+
+ f->position += written;
+ return written;
+}
+
//return value and max_length don't include null terminator
uint32_t read_line_from_file(struct file *f, char *sz, uint32_t max_length) {
uint8_t i;
@@ -114,6 +125,10 @@ uint32_t file_size(struct file *f) {
return f->length;
}
+void trunc_file(struct file *f) {
+ _set_file_size(f->handle, f->length = f->position);
+}
+
//return value must be manually freed, unless it is a null pointer
_dir_info_entry_t *get_directory_info(const char *path, uint32_t *count_out) {
uint8_t dn;