diff --git a/build.sh b/build.sh index a77cf37..67e4e03 100755 --- a/build.sh +++ b/build.sh @@ -64,6 +64,10 @@ make_build() { build() { mkdir -p artifacts/ + make_build \ + service/host \ + "Building host utils..." + make_build \ service/ptos/ \ "Building kernel..." diff --git a/service/boot/host/iso.sh b/service/boot/host/iso.sh index 671475d..b828066 100755 --- a/service/boot/host/iso.sh +++ b/service/boot/host/iso.sh @@ -9,9 +9,15 @@ set -e ISO=$PT_PROJECT_ROOT/artifacts/ptos.iso CONFIG=$PT_PROJECT_ROOT/service/base/boot/limine.conf KERNEL=$PT_PROJECT_ROOT/artifacts/ptsv.sys +HOLE=$PT_PROJECT_ROOT/service/host/bin/hole mkdir -p iso_root/boot/ +# Create the pre-boot image (PBI) +pushd ../base +$HOLE -f . -o ../boot/iso_root/boot/pbi.hole +popd + # Copy the kernel to the ISO root cp $KERNEL iso_root/boot diff --git a/service/host/.gitignore b/service/host/.gitignore new file mode 100644 index 0000000..5e56e04 --- /dev/null +++ b/service/host/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/service/host/Makefile b/service/host/Makefile new file mode 100644 index 0000000..e552693 --- /dev/null +++ b/service/host/Makefile @@ -0,0 +1,21 @@ +# +# Copyright (c) 2026, Chloe M. +# Provided under the BSD-3 clause +# +# Description: Host tools build +# Author: Chloe M. +# + +.SILENT: + +.PHONY: all +all: bin hole + +.PHONY: bin +bin: + mkdir -p $@ + +.PHONY: hole +hole: + echo "[*] Building hole..." + cd hole/; $(MAKE) diff --git a/service/host/hole/Makefile b/service/host/hole/Makefile new file mode 100644 index 0000000..dfb01f3 --- /dev/null +++ b/service/host/hole/Makefile @@ -0,0 +1,29 @@ +# +# Copyright (c) 2026, Chloe M. +# Provided under the BSD-3 clause +# +# Description: PBI boot packer build script +# Author: Chloe M. +# + +.SILENT: +PROMPT := printf "%s\t\t%s\n" + +CC = gcc +CFILES = $(shell find . -name "*.c") +OFILES = $(CFILES:.c=.o) + +CFLAGS = \ + -Wall \ + -pedantic + +.PHONY: all +all: ../bin/hole + +.PHONY: hole +../bin/hole: $(OFILES) + $(CC) $^ -o $@ + +%.o: %.c + $(PROMPT) "CC" $< + $(CC) -c $(CFLAGS) $< -o $@ diff --git a/service/host/hole/core/hole.c b/service/host/hole/core/hole.c new file mode 100644 index 0000000..de008da --- /dev/null +++ b/service/host/hole/core/hole.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2026, Chloe M. + * Provided under the BSD-3 clause. + * + * Description: pre-boot image format packer + * Author: Chloe M. + */ + +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REF_MAGIC "PTOS.PBI" +#define REF_MAGIC_LEN 8 +#define REF_PATH_LEN 256 + +/* Set to 1 for verbose */ +#define DEBUG 0 + +#if DEBUG +#define DTRACE(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define DTRACE(...) (void)0 +#endif + +/* + * A reference is an entry within the table that refers + * to a block of data after the reference table. + * + * @magic: Reference magic + * @path: File path + * @size: File size + * @data_off: Data offset + * @real_path: Real path + * @link: Queue link + * + * XXX: Do not reorder anything ABOVE 'link' + */ +struct hole_ref { + char magic[REF_MAGIC_LEN]; + char path[REF_PATH_LEN]; + size_t size; + off_t data_off; + char *real_path; + TAILQ_ENTRY(hole_ref) link; +}; + +/* Globals */ +static bool output_override = false; +static char *output_path = "out.hole"; +static char *fill_dirpath = NULL; +static size_t file_count = 0; +static TAILQ_HEAD(, hole_ref) hole_files; + +/* + * Display the help options + */ +static void +help(void) +{ + printf("usage: ./hole [flags]\n"); + printf("[-h] Display this help menu\n"); + printf("[-f] Fill hole with directory\n"); + printf("[-o] Output blob path\n"); +} + +/* + * Destroy the hole files table + */ +static void +hole_files_destroy(void) +{ + struct hole_ref *ref, *tmp; + + while ((ref = TAILQ_FIRST(&hole_files)) != NULL) { + DTRACE("delete %s\n", ref->real_path); + free(ref->real_path); + + tmp = ref; + TAILQ_REMOVE(&hole_files, ref, link); + free(tmp); + } +} + +/* + * Used to fix up offsets after + */ +static void +hole_table_fixup(void) +{ + struct stat sb; + struct hole_ref *ref; + size_t table_end, offset; + size_t hdr_size; + + hdr_size = sizeof(struct hole_ref); + hdr_size -= sizeof(ref->real_path); + hdr_size -= sizeof(ref->link); + table_end = file_count * hdr_size; + offset = table_end; + + printf("* fixing up offsets...\n"); + printf("* table end @ %zx\n", table_end); + + TAILQ_FOREACH(ref, &hole_files, link) { + if (stat(ref->real_path, &sb) < 0) { + perror("stat"); + continue; + } + + ref->size = sb.st_size; + ref->data_off = offset; + + DTRACE("data_off=%zx\n", ref->data_off); + offset += ref->size; + } +} + +static void +blob_write(void) +{ + struct hole_ref *ref; + int fd, ffd; + void *data; + size_t fsize, hdr_len; + + fd = open(output_path, O_RDWR | O_CREAT | O_TRUNC, 0777); + if (fd < 0) { + perror("open"); + return; + } + + /* + * PASS 1): + * We are to write the header of each file into the + * top of the blob. + */ + TAILQ_FOREACH(ref, &hole_files, link) { + hdr_len = sizeof(*ref); + hdr_len -= sizeof(ref->real_path); + hdr_len -= sizeof(ref->link); + write(fd, ref, hdr_len); + } + + /* + * PASS 2): + * Now we can write the contents of each file after + */ + TAILQ_FOREACH(ref, &hole_files, link) { + ffd = open(ref->real_path, O_RDONLY); + if (ffd < 0) { + perror("open[ffd]"); + continue; + } + + /* Obtain the file size */ + fsize = lseek(ffd, 0, SEEK_END); + lseek(ffd, 0, SEEK_SET); + + data = mmap( + NULL, + fsize, + PROT_READ, + MAP_SHARED, + ffd, + 0 + ); + + if (data == NULL) { + close(ffd); + perror("mmap"); + continue; + } + + lseek(fd, SEEK_SET, ref->data_off); + write(fd, data, fsize); + munmap(data, fsize); + close(ffd); + } + + close(fd); + return; +} + +/* + * Strip a path of leading '.' and '/' characters + * + * @path: Path to strip + */ +static const char * +path_strip(const char *path) +{ + const char *p; + + if (path == NULL) { + return NULL; + } + + p = path; + while (*p == '.' || *p == '/') { + ++p; + } + + return p; +} + +/* + * Push a file to the hole files table + */ +static int +push_file(const char *path) +{ + struct hole_ref *ref; + const char *stripped; + + if (path == NULL) { + return -1; + } + + ref = malloc(sizeof(*ref)); + if (ref == NULL) { + printf("fatal: could not allocate ref\n"); + hole_files_destroy(); + return -1; + } + + ref->real_path = strdup(path); + stripped = path_strip(path); + + memcpy(ref->magic, REF_MAGIC, sizeof(REF_MAGIC)); + memcpy(ref->path, stripped, strlen(stripped) + 1); + ref->data_off = 0; + + TAILQ_INSERT_TAIL(&hole_files, ref, link); + ++file_count; + return 0; +} + +/* + * Scan a directory to fill the blob + */ +static void +scan_dir(const char *path) +{ + DIR *dir; + struct dirent *dirent; + char pathbuf[300]; + + if (path == NULL) { + return; + } + + dir = opendir(path); + if (dir == NULL) { + perror("opendir"); + return; + } + + while ((dirent = readdir(dir)) != NULL) { + if (dirent->d_name[0] == '.') { + continue; + } + + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, dirent->d_name); + switch (dirent->d_type) { + case DT_REG: + printf("[f] %s\n", pathbuf); + if (push_file(pathbuf) < 0) { + free(fill_dirpath); + exit(1); + } + break; + case DT_DIR: + printf("[d] %s\n", pathbuf); + scan_dir(pathbuf); + break; + } + } + + closedir(dir); +} + +int +main(int argc, char **argv) +{ + int opt; + + if (argc < 2) { + printf("fatal: too few arguments\n"); + help(); + return -1; + } + + while ((opt = getopt(argc, argv, "hf:o:")) != -1) { + switch (opt) { + case 'h': + help(); + return -1; + case 'f': + fill_dirpath = strdup(optarg); + if (fill_dirpath == NULL) { + printf("fatal: out of memory\n"); + return -1; + } + + break; + case 'o': + output_override = true; + output_path = strdup(optarg); + if (output_path == NULL) { + printf("fatal: out of memory\n"); + return -1; + } + break; + } + } + + if (fill_dirpath == NULL) { + printf("fatal: expected fill directory for blob...\n"); + help(); + return -1; + } + + TAILQ_INIT(&hole_files); + scan_dir(fill_dirpath); + + hole_table_fixup(); + blob_write(); + + hole_files_destroy(); + free(fill_dirpath); + + if (output_override) { + free(output_path); + } + + return 0; +} diff --git a/service/ptos/arch/amd64/cpu/init.c b/service/ptos/arch/amd64/cpu/init.c index 95ba421..d6987c6 100644 --- a/service/ptos/arch/amd64/cpu/init.c +++ b/service/ptos/arch/amd64/cpu/init.c @@ -88,4 +88,7 @@ HalKpcrP2Init(KPCR *Kpcr) : "i" (GDT_TSS) : "memory" ); + + /* Initialize the scheduler queue */ + SCHED_QUEUE_INIT(&Kpcr->SchedQueue); } diff --git a/service/ptos/head/hal/kpcr.h b/service/ptos/head/hal/kpcr.h index a5700e9..7ae2e1f 100644 --- a/service/ptos/head/hal/kpcr.h +++ b/service/ptos/head/hal/kpcr.h @@ -11,6 +11,7 @@ #include #include +#include #include /* @@ -19,11 +20,13 @@ * * @Id: Logical processor ID assigned by us * @PoolRegion: Allocator pool region + * @SchedQueue: Per-core scheduler queue * @Mcb: Machine core block */ typedef struct { USHORT Id; POOL_REGION PoolRegion; + SCHED_QUEUE SchedQueue; MCB Mcb; } KPCR; diff --git a/service/ptos/head/ke/sched.h b/service/ptos/head/ke/sched.h new file mode 100644 index 0000000..8c45664 --- /dev/null +++ b/service/ptos/head/ke/sched.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026, Chloe M. + * Provided under the BSD-3 clause. + * + * Description: Scheduler management + * Author: Chloe M. + */ + +#ifndef _KE_SCHED_H_ +#define _KE_SCHED_H_ 1 + +#include +#include +#include + +/* + * Represents a scheduler queue that threads can be pulled + * from and added to. + * + * @ThreadQueue: Represents the actual queue that holds threads + * @QueueEntries: Represents the number of entries in the queue + */ +typedef struct { + TAILQ_HEAD(, _THREAD) ThreadQueue; + USIZE QueueEntries; +} SCHED_QUEUE; + +/* Macro used to initialize the scheduler queue */ +#define SCHED_QUEUE_INIT(SQ_P) \ + TAILQ_INIT(&(SQ_P)->ThreadQueue); \ + (SQ_P)->QueueEntries = 0; + +#endif /* !_KE_SCHED_H_ */