diff options
author | Ian Moffett <ian@osmora.org> | 2025-05-27 01:41:17 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-05-27 01:41:17 -0400 |
commit | 055835295e4ea578d3f6346fde1c2ac44eb8f787 (patch) | |
tree | 998cca4b80f9cfd1f47c8d4363b860d2bfc35e1b | |
parent | 4d423137ba180d252088c3925587057abd02d084 (diff) |
kernel: Add initial installer impl
Signed-off-by: Ian Moffett <ian@osmora.org>
-rwxr-xr-x | hyra-build.sh | 52 | ||||
-rw-r--r-- | sys/include/sys/systm.h | 2 | ||||
-rw-r--r-- | sys/kern/init_main.c | 26 | ||||
-rw-r--r-- | sys/kern/kern_instl.c | 323 |
4 files changed, 387 insertions, 16 deletions
diff --git a/hyra-build.sh b/hyra-build.sh index 20a05d2..d8903c8 100755 --- a/hyra-build.sh +++ b/hyra-build.sh @@ -85,11 +85,6 @@ gen_isofs() { # Stage 1 - build production media #################################### stage1() { - if [[ $install_flag == "true" ]] - then - make clean - fi - iso_root_skel sysroot_skel @@ -119,6 +114,7 @@ stage2() { sysroot_skel echo "[*] stage2: Generate stage 2 RAMFS via OMAR" + mv Hyra.iso base/boot/ $RAMFS_TOOL -i base/ -o $RAMFS_NAME echo "[*] stage2: Build kernel" @@ -130,9 +126,43 @@ stage2() { # Clean up rm $RAMFS_NAME + rm base/boot/Hyra.iso rm -r iso_root } +################################## +# Clean up completly after build +################################## +hard_clean() { + make clean + rm -rf base/ +} + +################################## +# Build results +# +# ++ ARGS ++ +# $1: ISO output name +# -- -- +################################## +result() { + echo "-------------------------------------------" + echo "Build finish" + + if [[ $1 == "Hyra-install.iso" ]] + then + hard_clean # XXX: For safety + echo "Installer is at ./Hyra-install.iso" + echo "!!WARNING!!: Installer is _automatic_" + echo "!!NOTE!!: OSMORA is not responsible for incidental data loss" + else + echo "Boot image is at ./Hyra.iso" + fi + + echo "Finished in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds" + echo "-------------------------------------------" +} + while getopts "ih" flag do case "${flag}" in @@ -167,13 +197,15 @@ if [[ $install_flag != "true" ]] then echo "[?] Not building installer (-i unset)" echo "-- Skipping stage 2 --" + result "Hyra.iso" else echo "-- Begin stage 2 --" stage2 + result "Hyra-install.iso" fi -echo "-------------------------------------------" -echo "Build finish" -echo "Installer is at ./Hyra-install.iso" -echo "Finished in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds" -echo "-------------------------------------------" +if [[ $install_flag == "true" ]] +then + make clean + rm -rf base/ +fi diff --git a/sys/include/sys/systm.h b/sys/include/sys/systm.h index 42e1723..5d06257 100644 --- a/sys/include/sys/systm.h +++ b/sys/include/sys/systm.h @@ -55,5 +55,7 @@ __sigraise(int signo) dispatch_signals(td); } +int hyra_install(void); + #endif /* _KERNEL */ #endif /* !_SYS_SYSTM_H_ */ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index ff965bd..76fa6fd 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -35,6 +35,7 @@ #include <sys/exec.h> #include <sys/driver.h> #include <sys/panic.h> +#include <sys/systm.h> #include <dev/acpi/uacpi.h> #include <dev/cons/cons.h> #include <dev/acpi/acpi.h> @@ -52,6 +53,18 @@ copyright(void) "Copyright (c) 2023-2025 Ian Marco Moffett and the OSMORA team\n"); } +#if defined(_INSTALL_MEDIA) +static void +begin_install(void) +{ + struct cpu_info *ci; + + ci = this_cpu(); + ci->curtd = &proc0; + hyra_install(); +} +#endif + static void start_init(void) { @@ -84,11 +97,6 @@ main(void) kprintf("Starting Hyra/%s v%s: %s\n", HYRA_ARCH, HYRA_VERSION, HYRA_BUILDDATE); -#if _INSTALL_MEDIA - kprintf("Hyra install media detected\n"); - kprintf("Reform Industry!\n"); -#endif /* _INSTALL_MEDIA */ - /* Start the ACPI subsystem */ acpi_init(); @@ -111,11 +119,17 @@ main(void) /* Startup pid 1 */ spawn(&proc0, start_init, NULL, 0, NULL); + md_inton(); /* Load all drivers */ - md_inton(); DRIVERS_INIT(); +#if defined(_INSTALL_MEDIA) + kprintf("Hyra install media detected\n"); + kprintf("Reform Industry!\n"); + begin_install(); +#endif + /* Bootstrap APs and here we go! */ mp_bootstrap_aps(&g_bsp_ci); sched_enter(); diff --git a/sys/kern/kern_instl.c b/sys/kern/kern_instl.c new file mode 100644 index 0000000..4f4d0f1 --- /dev/null +++ b/sys/kern/kern_instl.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/filedesc.h> +#include <sys/fcntl.h> +#include <sys/syslog.h> +#include <sys/sio.h> +#include <sys/vnode.h> +#include <sys/disklabel.h> +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/sched.h> +#include <sys/reboot.h> +#include <machine/cdefs.h> +#include <vm/dynalloc.h> +#include <vm/vm.h> +#include <dev/timer.h> +#include <assert.h> +#include <string.h> + +#define DEFAULT_TIMEOUT 3 +#define YIELD_TIMEOUT 200000 /* In usec */ +#define BLOCK_SIZE 512 +#define BLOCK_THRESHOLD (BLOCK_SIZE * 1024) + +struct progress_bar { + bool dec; + uint8_t progress; +}; + +__used static struct timer tmr; +__used static struct timer sched_timer; +__used static struct sio_txn hdd_sio; + +#if defined(_INSTALL_MEDIA) +__dead static void +installer_quit(uint32_t seconds) +{ + kprintf("restarting in %d seconds...\n", seconds); + tmr.msleep(seconds * 1000); + cpu_reboot(REBOOT_RESET); + __builtin_unreachable(); +} + +static inline void +installer_yield(void) +{ + md_inton(); + sched_timer.oneshot_us(YIELD_TIMEOUT); + md_hlt(); + md_intoff(); +} + +/* + * Throttle CPU usage by giving it small + * breaks based on the amount of data already + * read. The installer needs to perform very + * large block I/O operations and we want to + * avoid significant temperature spikes that + * would be kind of scary :( + * + * @n: Number of bytes read + */ +static void +installer_throttle(size_t n) +{ + if ((n % BLOCK_THRESHOLD) == 0) { + installer_yield(); + } +} + +/* + * Create a progress bar animation for long + * operations. + * + * @bp: Pointer to a progress bar structure. + * @n: Number of blocks operated on. + * @max: Max blocks per bar update. + */ +static void +progress_update(struct progress_bar *bp, size_t n, size_t max) +{ + const char CHR = '.'; + + /* + * We only want to update the progress bar + * per `max' blocks written. + */ + if ((n > 0) && (n % max) != 0) { + return; + } + + /* Add more '.' chars */ + if (bp->progress < 8 && !bp->dec) { + kprintf(OMIT_TIMESTAMP "%c\f", CHR); + } else if (bp->progress >= 8) { + bp->dec = true; + } + + /* Remove '.' chars */ + if (bp->dec && bp->progress > 0) { + kprintf(OMIT_TIMESTAMP "\b\f"); + } else if (bp->progress == 0) { + bp->dec = false; + } + + if (!bp->dec) { + ++bp->progress; + } else { + --bp->progress; + } +} + +static void +installer_wipe(struct filedesc *hdd, uint32_t count) +{ + struct sio_txn sio; + struct progress_bar bar = {0, 0}; + size_t write_len, total_blocks; + size_t write_blocks; + char buf[BLOCK_SIZE * 2]; + + write_len = sizeof(buf); + memset(buf, 0, write_len); + write_blocks = write_len / BLOCK_SIZE; + + total_blocks = ALIGN_UP(count, BLOCK_SIZE); + total_blocks /= BLOCK_SIZE; + + if (__unlikely(total_blocks == 0)) { + kprintf("bad block size for /dev/sd1\n"); + installer_quit(DEFAULT_TIMEOUT); + } + + sio.buf = buf; + sio.offset = 0; + sio.len = write_len; + + /* Zero that shit */ + kprintf("zeroing %d blocks...\n", total_blocks); + for (int i = 0; i < total_blocks; i += write_blocks) { + vfs_vop_write(hdd->vp, &sio); + installer_throttle(sio.offset); + sio.offset += write_len; + progress_update(&bar, i, 1000); + } + + /* Cool off then continue */ + installer_yield(); + hdd_sio.offset = 0; + kprintf(OMIT_TIMESTAMP "OK\n"); + tmr.msleep(1000); +} + +/* + * Write data to the drive. + * + * @hdd: HDD file descriptor + * @file: Optional source file descriptor + * @p: Data pointer + * @len: Length of data. + */ +static void +installer_write(struct filedesc *hdd, struct filedesc *file, void *p, size_t len) +{ + size_t nblocks; + struct sio_txn sio; + struct progress_bar bar = {0, 0}; + char buf[BLOCK_SIZE]; + + len = ALIGN_UP(len, BLOCK_SIZE); + nblocks = len / BLOCK_SIZE; + + hdd_sio.len = BLOCK_SIZE; + hdd_sio.buf = (len < BLOCK_SIZE) ? buf : p; + + sio.len = BLOCK_SIZE; + sio.offset = 0; + sio.buf = (len < BLOCK_SIZE) ? buf : p; + + if (len < BLOCK_SIZE) { + memcpy(buf, p, len); + } + + kprintf("writing %d block(s)...\n", nblocks); + for (size_t i = 0; i < nblocks; ++i) { + if (file != NULL) { + vfs_vop_read(file->vp, &sio); + } + vfs_vop_write(hdd->vp, &hdd_sio); + installer_throttle(hdd_sio.offset); + + sio.offset += BLOCK_SIZE; + hdd_sio.offset += BLOCK_SIZE; + progress_update(&bar, i, 400); + } + + kprintf(OMIT_TIMESTAMP "OK\n"); +} + +#endif + +int +hyra_install(void) +{ +#if defined(_INSTALL_MEDIA) + int fd, hdd_fd; + char buf[BLOCK_SIZE]; + struct filedesc *fildes, *hdd_fildes; + struct disklabel label; + struct vop_getattr_args getattr_args; + struct vattr iso_attr; + size_t iso_size; + size_t nzeros = 0; /* Zero byte count */ + tmrr_status_t tmr_error; + + /* Needed for msleep() */ + tmr_error = req_timer(TIMER_GP, &tmr); + if (__unlikely(tmr_error != TMRR_SUCCESS)) { + kprintf("could not fetch TIMER_GP\n"); + installer_quit(DEFAULT_TIMEOUT); + } + + /* + * Grab the scheduler timer since we can + * reasonably assume it has oneshot capability. + */ + tmr_error = req_timer(TIMER_SCHED, &sched_timer); + if (__unlikely(tmr_error != TMRR_SUCCESS)) { + kprintf("could not fetch TIMER_SCHED\n"); + installer_quit(DEFAULT_TIMEOUT); + } + + kprintf("::::::::::::::::::::::::::::\n"); + kprintf("::::: Hyra Installer ::::::\n"); + kprintf("::::::::::::::::::::::::::::\n"); + kprintf("!! DRIVE WILL BE WIPED !!\n"); + tmr.msleep(5000); + + + /* + * See if the target drive exists + * + * XXX: As of now, we only support SATA drives + * as a target for the installer. + */ + hdd_fd = fd_open("/dev/sd1", O_RDWR); + if (hdd_fd < 0) { + kprintf("could not open /dev/sd1\n"); + installer_quit(DEFAULT_TIMEOUT); + } + + kprintf("installing to /dev/sd1...\n"); + + fd = fd_open("/boot/Hyra.iso", O_RDONLY); + if (fd < 0) { + kprintf("could not open /boot/Hyra.iso (status=%d)\n", fd); + installer_quit(DEFAULT_TIMEOUT); + } + + /* Get attributes */ + fildes = fd_get(fd); + getattr_args.vp = fildes->vp; + getattr_args.res = &iso_attr; + vfs_vop_getattr(fildes->vp, &getattr_args); + + /* Get the ISO size */ + iso_size = ALIGN_UP(iso_attr.size, BLOCK_SIZE); + + /* + * First, wipe part of the drive of any data. + * This is done by simply filling it with + * zeros. + */ + hdd_fildes = fd_get(hdd_fd); + nzeros = iso_size + sizeof(struct disklabel); + nzeros += BLOCK_SIZE; + installer_wipe(hdd_fildes, nzeros); + + /* + * Now since the drive is zerored, we can prep + * our data buffers to write the actual install. + */ + label.magic = DISK_MAG; + label.sect_size = BLOCK_SIZE; + installer_write(hdd_fildes, fildes, buf, iso_size); + installer_write(hdd_fildes, NULL, &label, sizeof(label)); + + kprintf("Installation complete!\n"); + kprintf("Please remove installation media\n"); + installer_quit(5); +#endif /* _INSTALL_MEDIA */ + return 0; +} |