diff --git a/Makefile b/Makefile index ef58aff..c465fef 100644 --- a/Makefile +++ b/Makefile @@ -101,19 +101,7 @@ KERN_ACPI = $(addprefix $(BUILD)/uacpi/, \ namespace.o notify.o opcodes.o opregion.o osi.o registers.o \ resources.o shareable.o sleep.o stdlib.o tables.o types.o \ uacpi.o utilities.o) -KERN_ACPI += $(addprefix $(BUILD)/kernel/acpi/, host.o aml.o i2c_hid.o) - -# zstd's decoder, for SCFF -- the compressed live root (include/fs/scff.h). -# Unpacked by ports/zstd into build/ports/zstd/src and compiled into the kernel -# like uACPI; kernel/scff/ is the host side of it. -ZSTDDIR = $(BUILD)/ports/zstd/src/lib -KERN_ZSTD = $(addprefix $(BUILD)/zstd/, \ - common/entropy_common.o common/error_private.o \ - common/fse_decompress.o common/zstd_common.o common/xxhash.o \ - common/debug.o decompress/huf_decompress.o \ - decompress/zstd_ddict.o decompress/zstd_decompress.o \ - decompress/zstd_decompress_block.o) -KERN_ZSTD += $(BUILD)/kernel/scff/scff.o +KERN_ACPI += $(addprefix $(BUILD)/kernel/acpi/, host.o aml.o) KERN_PROC = $(addprefix $(BUILD)/kernel/proc/, \ proc.o) @@ -126,7 +114,7 @@ KERN_SYS = $(addprefix $(BUILD)/kernel/sys/, \ KERN_ARCH += $(BUILD)/kernel/arch/x86_64/vectors.o KERNOBJS = $(KERN_ARCH) $(KERN_CORE) $(KERN_DEV) $(KERN_DRIVERS) \ - $(KERN_FS) $(KERN_NET) $(KERN_ACPI) $(KERN_ZSTD) $(KERN_PROC) $(KERN_SYS) + $(KERN_FS) $(KERN_NET) $(KERN_ACPI) $(KERN_PROC) $(KERN_SYS) # entry.o must be first in link order; remove it from KERNOBJS for the rule KERN_NOENTRY = $(filter-out $(BUILD)/kernel/arch/x86_64/entry.o, $(KERNOBJS)) @@ -404,7 +392,6 @@ endif # looks satisfied. MKFS = $(BUILD)/tools/mkfs$(EXE) MKSCHFS = $(BUILD)/tools/mkschfs$(EXE) -MKSCFF = $(BUILD)/tools/mkscff$(EXE) SCHFS_TEST = $(BUILD)/tools/schfs_test$(EXE) SCHFS_OBJT = $(BUILD)/tools/schfs_objtest$(EXE) SCHFS_FSCK = $(BUILD)/tools/schfs_fsck$(EXE) @@ -444,7 +431,7 @@ CFLAGS += $(NO_SSP) $(CFLAGS_EXTRA) # copies and memset into %xmm, and an interrupt then scribbles over the # registers of the program it interrupted. Kernel objects only -- cmd/ is # built with these CFLAGS too and is welcome to use SSE. -$(BUILD)/kernel/%.o $(BUILD)/lwip/%.o $(BUILD)/uacpi/%.o $(BUILD)/zstd/%.o: CFLAGS += -mgeneral-regs-only +$(BUILD)/kernel/%.o $(BUILD)/lwip/%.o $(BUILD)/uacpi/%.o: CFLAGS += -mgeneral-regs-only # lwIP's own headers, plus ours (cc.h and lwipopts.h live in kernel/net/lwip). # Only the network files are compiled with these: putting them in the global @@ -474,14 +461,14 @@ CFLAGS_SYS = $(CFLAGS) -I./kernel/sys # loads that by PIO one sector at a time -- a kernel with a userland inside it # takes visibly longer to boot than a 350 KB one. # -# Sized to fit, not fixed. The image is what the root uses plus RAMDISK_SLACK -# blocks and an eighth, measured by building it once large; the kernel then -# grows the volume into half of the free memory at boot (kernel/dev/ramdisk.c), -# which is where `apk add` on the live system writes. It was a fixed 96 MB, -# 60 of it zeros carried in the kernel and twice over on the ISO, and room for -# 60 MB of packages whatever the machine had. +# 8000 blocks of 4 KB is 32 MB. It was 6000, which the userland had quietly +# grown to fill: 5157 of 6000 used, 86%, after toybox gained its pending +# applets and an editor arrived. That still fit, and the *next* thing added +# would not have -- and what mkschfs says when an image is full is "try a +# larger --size", which is a poor way to find out that a build variant you were +# not using has been broken for a while. # ----------------------------------------------------------------------- -RAMDISK_SLACK ?= 1024 +RAMDISK_BLOCKS ?= 8000 # RAMDISK_NINODES used to live here, sizing mkfs's preallocated inode table. # The ramdisk is a SchFS image now (see $(BUILD)/fs-ram.img) and SchFS allocates @@ -499,10 +486,10 @@ RAMDISK_SLACK ?= 1024 # This is not hypothetical: a 33 MB test payload once landed here and overflowed # the 6 MB image, and mkfs's "increase FSSIZE" message then pointed at a constant # that had nothing to do with it. -# usb.ids and pci.ids used to be on it, as 2 MB of a 6 MB image. They are -# Alpine's hwdata packages now: apk owns them, and a root missing files its -# database lists would be one apk had to repair. -RAMDISK_EXCLUDE = +# usb.ids and pci.ids are 2 MB of a 6 MB image and nothing needs them to boot. +# They are lookup tables for userspace tools, so they stay on the disk image +# and out of the kernel binary. +RAMDISK_EXCLUDE = usr/share/hwdata RAMDISK_EXCLUDE_ARGS = $(foreach p,$(RAMDISK_EXCLUDE),--exclude $(p)) ifdef RAMROOT @@ -532,17 +519,6 @@ $(shell mkdir -p $(BUILD); \ printf '%s' '$(RAMROOT_STATE)' > $(RAMROOT_STAMP); \ fi) -# ----------------------------------------------------------------------- -# The root filesystem is an Alpine root that apk manages, with SchOS installed -# into it as the package schos-base -- see docs/plan/alpine.md and "The -# apk-managed root" below. Both images are made from it: schfs.img, which -# `make qemu` boots, and the ramdisk inside the kernel, which `make iso` -# boots. sysroot/ is what schos-base is packaged from, not a root of its own. -# ----------------------------------------------------------------------- -AROOT_DIR = $(BUILD)/aroot -ROOT_TREE = $(AROOT_DIR)/root -ROOT_STAMP = $(AROOT_DIR)/stamp - # ----------------------------------------------------------------------- # Top-level targets # ----------------------------------------------------------------------- @@ -781,9 +757,9 @@ iso: $(MAKE) RAMROOT=1 schos.iso # One El Torito entry, pointing at a FAT image holding the loader and kernel. -# The root is in the kernel, as its ramdisk; the disc carries no other copy. -schos.iso: $(BUILD)/kernel/kernel $(BUILD)/uefi/BOOTX64.EFI tools/posix/mkiso.sh $(ROOT_STAMP) - sh tools/posix/mkiso.sh $@ +# sysroot-install because the disc also carries the tree as a real hierarchy. +schos.iso: $(BUILD)/kernel/kernel $(BUILD)/uefi/BOOTX64.EFI tools/posix/mkiso.sh sysroot-install + SYSROOT=$(SYSROOT) sh tools/posix/mkiso.sh $@ # A partitioned disk -- GPT, an ESP and a SchFS partition -- which is what a # real machine boots from: dd it to a USB stick or a disk. See @@ -840,7 +816,7 @@ $(BUILD)/kernel/kernel: $(BUILD)/kernel/kernel.pass1 $(BUILD)/kernel/ksym.o $(OBJDUMP) -S $@ > $(BUILD)/kernel/kernel.asm # The ramdisk root image, and the object wrapping it for the linker. Built from -# the same staged sysroot as the main image, sized to fit (see RAMDISK_SLACK) -- +# the same staged sysroot as the main image, just capped at RAMDISK_BLOCKS -- # mkfs's content dedup means the whole userland still fits in a few MB. # Built with mkschfs, not mkfs. # @@ -849,33 +825,19 @@ $(BUILD)/kernel/kernel: $(BUILD)/kernel/kernel.pass1 $(BUILD)/kernel/ksym.o # volume on the root device" and made `make iso` and `make ramroot` unbootable. # RAMDISK_NINODES is gone with it: SchFS has no preallocated inode table, which # is what that knob existed to size. -$(BUILD)/fs-ram.img: $(MKSCHFS) $(ROOT_STAMP) +$(BUILD)/fs-ram.img: $(MKSCHFS) $(BUILD)/sysroot.stamp @mkdir -p $(BUILD) @rm -f $@ - @used=$$($(MKSCHFS) $@ --size 262144 --label schos \ - $(RAMDISK_EXCLUDE_ARGS) --sysroot $(ROOT_TREE) | \ - sed -n 's/^ *\([0-9][0-9]*\) of [0-9][0-9]* blocks used.*/\1/p'); \ - test -n "$$used" || { echo "fs-ram.img: mkschfs did not say how much it used" >&2; exit 1; }; \ - rm -f $@; \ - $(MKSCHFS) $@ --size $$((used + used / 8 + $(RAMDISK_SLACK))) --label schos \ - $(RAMDISK_EXCLUDE_ARGS) --sysroot $(ROOT_TREE) + $(MKSCHFS) $@ --size $(RAMDISK_BLOCKS) --label schos \ + $(RAMDISK_EXCLUDE_ARGS) --sysroot $(SYSROOT) -# The image goes into the kernel as SCFF (include/fs/scff.h): 128 KB chunks, -# each its own zstd frame, unpacked by the kernel the first time a block in it -# is read. -19 is slow to write and costs nothing to read -- zstd decodes at -# the same speed whatever level made the frame -- and mkscff uses every core. -SCFF_LEVEL ?= 19 -SCFF_CHUNK ?= 128 -$(BUILD)/fs-ram.scff: $(BUILD)/fs-ram.img $(MKSCFF) - $(MKSCFF) $< $@ --level $(SCFF_LEVEL) --chunk $(SCFF_CHUNK) - -$(BUILD)/kernel/ramdisk_obj: $(BUILD)/fs-ram.scff +$(BUILD)/kernel/ramdisk_obj: $(BUILD)/fs-ram.img @mkdir -p $(BUILD)/kernel $(LD) -r -b binary -m elf_x86_64 -o $@ $< $(OBJCOPY) \ - --redefine-sym _binary_build_fs_ram_scff_start=_binary_ramdisk_start \ - --redefine-sym _binary_build_fs_ram_scff_end=_binary_ramdisk_end \ - --redefine-sym _binary_build_fs_ram_scff_size=_binary_ramdisk_size \ + --redefine-sym _binary_build_fs_ram_img_start=_binary_ramdisk_start \ + --redefine-sym _binary_build_fs_ram_img_end=_binary_ramdisk_end \ + --redefine-sym _binary_build_fs_ram_img_size=_binary_ramdisk_size \ $@ # The console font, linked into the kernel. @@ -1041,30 +1003,6 @@ $(UACPIDIR): $(patsubst $(BUILD)/uacpi/%.o,$(UACPIDIR)/source/%.c,$(filter $(BUILD)/uacpi/%,$(KERN_ACPI))): | $(UACPIDIR) @: -# zstd at -O2 whatever OPT says: the decoder runs on every first read of a -# root block, and at -O0 it is several times slower. No assembly, no BMI2 -# dispatch, no legacy formats, no tracing hooks -- the kernel has none of -# what those need. compat/ stands in for the libc headers it includes. -ZSTD_CFLAGS = -Ikernel/scff/compat -I$(ZSTDDIR) -I$(ZSTDDIR)/common -O2 \ - -DZSTD_DISABLE_ASM -DDYNAMIC_BMI2=0 -DZSTD_LEGACY_SUPPORT=0 \ - -DZSTD_TRACE=0 -DDEBUGLEVEL=0 -DZSTD_STRIP_ERROR_STRINGS \ - -DXXH_NAMESPACE=ZSTD_ -ZSTD_CC = $(CC) $(filter-out -I./include,$(CFLAGS)) $(ZSTD_CFLAGS) - -$(BUILD)/zstd/%.o: $(ZSTDDIR)/%.c | $(ZSTDDIR) - @mkdir -p $(dir $@) - $(ZSTD_CC) -Wno-unused-function -c $< -o $@ - -$(BUILD)/kernel/scff/%.o: kernel/scff/%.c | $(ZSTDDIR) - @mkdir -p $(dir $@) - $(ZSTD_CC) -c $< -o $@ - -$(ZSTDDIR): - $(MAKE) -C ports zstd - -$(patsubst $(BUILD)/zstd/%.o,$(ZSTDDIR)/%.c,$(filter $(BUILD)/zstd/%,$(KERN_ZSTD))): | $(ZSTDDIR) - @: - $(BUILD)/kernel/proc/%.o: kernel/proc/%.c @mkdir -p $(BUILD)/kernel/proc $(CC) $(CFLAGS) -c $< -o $@ @@ -1268,14 +1206,6 @@ $(SCHFS_FSCK): tools/schfs_fsck.c $(SCHFS_SRC) $(HOSTCC) $(HOSTCFLAGS) -o $@ tools/schfs_fsck.c mkschfs: $(MKSCHFS) - -# The SCFF writer, built from the same zstd release as the kernel's decoder. -MKSCFF_ZSTD = $(wildcard $(ZSTDDIR)/common/*.c $(ZSTDDIR)/compress/*.c $(ZSTDDIR)/decompress/*.c) -$(MKSCFF): tools/mkscff.c include/fs/scff.h | $(ZSTDDIR) - @mkdir -p $(BUILD)/tools - $(HOSTCC) -O2 -pthread -iquote ./include -I$(ZSTDDIR) -DZSTD_DISABLE_ASM \ - -DZSTD_LEGACY_SUPPORT=0 -o $@ tools/mkscff.c \ - $(ZSTDDIR)/common/*.c $(ZSTDDIR)/compress/*.c $(ZSTDDIR)/decompress/*.c schfs-fsck: $(SCHFS_FSCK) # End-to-end filesystem test: build an image from a directory tree, read it @@ -1618,15 +1548,12 @@ qemu-console: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS) # is "try a larger --size", which is a worse thing to read than a number here # is to change. A raw disk image is sparse on any filesystem worth the name, # so the unused part costs nothing until it is used. -# -# 262144 blocks, 1 GB, since the root became one apk installs into: Xorg alone -# is 270 MB. Sparse, so the empty part costs nothing on the host. -SCHFS_BLOCKS ?= 262144 +SCHFS_BLOCKS ?= 25600 -$(BUILD)/schfs.img: $(MKSCHFS) $(ROOT_STAMP) +$(BUILD)/schfs.img: $(MKSCHFS) $(BUILD)/sysroot.stamp @mkdir -p $(BUILD) @rm -f $@ - ./$(MKSCHFS) $@ --size $(SCHFS_BLOCKS) --label schos --sysroot $(ROOT_TREE) + ./$(MKSCHFS) $@ --size $(SCHFS_BLOCKS) --label schos --sysroot $(SYSROOT) # --- The apk-managed root ------------------------------------------------------ # @@ -1634,11 +1561,14 @@ $(BUILD)/schfs.img: $(MKSCHFS) $(ROOT_STAMP) # apk package schos-base -- a root that `apk add` and `apk upgrade` manage. # See tools/posix/mkaroot.sh for what goes where and why. # -# make aroot the tree and build/schfs.img; `make qemu` boots it +# make aroot build/aroot.img, a SchFS root volume of AROOT_BLOCKS +# make qemu-aroot boot it as the root disk (the disk-root kernel) # make alpine-lock re-resolve ports/alpine/world against Alpine's mirror; # the one step that looks for newer packages # # The build itself is offline once dist/ has the files world.lock names. +AROOT_BLOCKS ?= 262144 +AROOT_DIR = $(BUILD)/aroot AROOT_KEYS = $(BUILD)/apk-keys APK_STATIC = $(BUILD)/ports/alpine/apk.static ALPINE_MINI = dist/alpine-minirootfs-3.24.3-x86_64.tar.gz @@ -1655,9 +1585,21 @@ $(AROOT_DIR)/stamp: $(BUILD)/sysroot.stamp ports/alpine/world.lock tools/posix/m $(AROOT_KEYS) $(SCHOS_PKGVER) ports/alpine/world.lock dist/alpine touch $@ -aroot: $(BUILD)/schfs.img +$(BUILD)/aroot.img: $(MKSCHFS) $(AROOT_DIR)/stamp + @rm -f $@ + ./$(MKSCHFS) $@ --size $(AROOT_BLOCKS) --label schos --sysroot $(AROOT_DIR)/root -.PHONY: alpine-port alpine-lock aroot +aroot: $(BUILD)/aroot.img + +qemu-aroot: esp $(BUILD)/aroot.img $(OVMF_CODE) $(OVMF_VARS) + $(QEMU) -machine pc-i440fx-9.2 \ + -drive if=pflash,format=raw,unit=0,readonly=on,file=$(OVMF_CODE) \ + -drive if=pflash,format=raw,unit=1,file=$(OVMF_VARS) \ + -drive file=fat:rw:$(BUILD)/esp,index=0,media=disk,format=raw \ + -drive file=$(BUILD)/aroot.img,format=raw,index=1,media=disk \ + -m 2G -smp 2 $(QEMU_ACCEL) -serial mon:stdio $(QEMU_DISPLAY) $(QEMU_INPUT) $(QEMU_NET) $(QEMU_AUDIO) + +.PHONY: alpine-port alpine-lock aroot qemu-aroot # Boot with a SchFS volume attached as the second disk. # diff --git a/NOTICE b/NOTICE index 4578dce..333b4f6 100644 --- a/NOTICE +++ b/NOTICE @@ -151,46 +151,6 @@ interpreter, used for PCI interrupt routing, the power button and power-off. The host layer written for SchOS (kernel/acpi/) is GPL-2.0-or-later and is not part of uACPI; uACPI's own sources are unmodified. --------------------------------------------------------------------------------- -zstd 1.5.7 BSD 3-Clause (or GPL-2.0) --------------------------------------------------------------------------------- -BSD License - -For Zstandard software - -Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * 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. - - * Neither the name Facebook, nor Meta, 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 HOLDER 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. - -Fetched by ports/zstd. Its decoder is compiled into the kernel to read SCFF, -the compressed live root (docs/architecture/scff.md), and tools/mkscff builds -the whole library on the host to write it. zstd is dual-licensed; SchOS takes -it under the BSD licence above. The glue written for SchOS (kernel/scff/) is -GPL-2.0-or-later and is not part of zstd; zstd's own sources are unmodified. - -------------------------------------------------------------------------------- Intel 8254x device identifiers BSD 3-Clause -------------------------------------------------------------------------------- diff --git a/docs/architecture/scff.md b/docs/architecture/scff.md deleted file mode 100644 index 5c1439e..0000000 --- a/docs/architecture/scff.md +++ /dev/null @@ -1,79 +0,0 @@ -# SCFF: the SchOS Compressed File Format - -The live root is shipped compressed. `make iso` builds the root as a SchFS -image, `tools/mkscff` writes that image as SCFF, and the kernel carries the -`.scff` file as its ramdisk. Blocks are decompressed the first time something -reads them. - -| | raw image | SCFF | -|---|---|---| -| root with Xorg and Window Maker | 358 MB | 102 MB | -| kernel and ISO | carry all 358 MB | carry 102 MB | -| memory the root takes at boot | 358 MB | what has been read | - -## Layout - -The definition is `include/fs/scff.h`, which the kernel and `mkscff` both -use. - - offset 0 struct scff_header (64 bytes, "SCFF", version 1) - index_off uint64 off[nchunks + 1] - off[i]..off[i+1] chunk i - -The image is cut into chunks of `chunk_bytes`, 128 KB by default. Each chunk -is stored in one of three ways, and its stored length says which: - -- **Zero bytes:** every byte of the chunk is zero. The slack at the end of the - image costs nothing. -- **The chunk's full length:** stored as it is, because it did not compress. -- **Anything else:** one zstd frame, with a content checksum. - -## Why it is shaped like this - -**Chunks, not one stream.** Each chunk can be decompressed without the others, -so reading one file does not mean decompressing the root. This costs a little -ratio: the 358 MB root is 104 MB in 128 KB chunks and 97 MB in 1 MB chunks. -128 KB is squashfs's default too. - -**A block image, not a compressed filesystem.** squashfs compresses files and -is read-only, so a writable root needs an overlay filesystem above it. SCFF -compresses a SchFS image instead, and the ramdisk is still a block device that -takes writes. A block that is written gets a page of its own, exactly as it -did before SCFF. SchFS does not know the image was ever compressed. - -**zstd.** It compresses close to xz at the levels a build can afford (`-19`). -It decompresses at memory speed whatever level made the frame. Its decoder is -dual BSD and GPL-2.0, and it is a plain C library. `ports/zstd` pins the -release. The kernel compiles only the decoder half. `mkscff` compiles all of -it from the same release, so neither side depends on the host's libzstd. - -## In the kernel - -`kernel/dev/ramdisk.c` reads the header at boot, checks it and the index, and -panics if either is wrong. A damaged image is found before anything mounts. - -The ramdisk's page directory covers every block. A block with no page has not -been unpacked yet. Reading one decompresses its whole chunk into `rd_chunk`, -and every block in that chunk without a page gets one. A block of zeros points -at one shared zero page and gets a real page only when it is written. - -`kernel/scff/scff.c` holds the decoder: one static zstd context in a 256 KB -workspace, so nothing allocates. **Decompression runs on a 64 KB stack of its -own.** A kernel stack is 8 KB, and a chunk is decompressed at the bottom of a -read that has already used most of it. zstd's sequence decoder needs several -KB of locals, and Linux gives it a 16 KB stack. The chunk lock (`rd_zlock`) -makes that stack its holder's alone. `kernel/scff/compat/` stands in for the -few libc headers zstd includes. - -zstd is built at `-O2` whatever `OPT` says, because it runs on every first -read of a block. - -## Building - - make iso builds build/fs-ram.scff and links it in - make iso SCFF_LEVEL=3 much faster to build, about a third bigger - make iso SCFF_CHUNK=256 bigger chunks: a little smaller, more work per miss - -`mkscff` uses every core. At `-19` the root takes about 11 seconds on 16 -threads. It decompresses every chunk again before writing it, so an image -that would not boot fails the build instead. diff --git a/docs/plan/alpine.md b/docs/plan/alpine.md index 5e141ab..a63a801 100644 --- a/docs/plan/alpine.md +++ b/docs/plan/alpine.md @@ -38,12 +38,10 @@ only what is its own (see `tools/posix/mkaroot.sh`): ## Building - make qemu boots build/schfs.img, the root as a 1 GB SchFS disk - make iso the same root as the live system's ramdisk + make aroot build/aroot.img: the root, as a SchFS volume + make qemu-aroot boot it make alpine-lock re-resolve world against Alpine (needs the network) -It is the only root. sysroot/ is what schos-base is packaged from. - Only `alpine-lock` asks Alpine what is current. A normal build installs exactly what `world.lock` names: files in `dist/alpine/`, fetched once and checked against the lock's sha256, and against Alpine's signatures by apk. @@ -68,13 +66,8 @@ All of it found by running apk: ## Next -- The live ISO runs the apk root from RAM. The image in the kernel is only - what the root holds; at boot the volume grows into half of the free memory - (4 GB of root on an 8 GB machine), backed by pages as they are written. - Xorg installs into it in about a minute. All of it is gone at reboot. Keeping packages needs an installer that writes the root - to a SATA or NVMe disk, and a kernel that finds its root there by the - SchFS GPT type rather than as IDE disk 1. -- Xorg and Window Maker are in `world`, so the image starts with them: - `startx` runs `wmaker` from `/root/.xinitrc`. Mesa brings LLVM with it, - about 200 MB of the 358 MB root; the root ships compressed - ([SCFF](../architecture/scff.md)), so that costs about 60 MB of ISO. +- The live ISO and USB stick are still the ramdisk root. They need the apk + root on the stick itself, or an installer that writes it to a disk. +- Xorg from apk (`xorg-server xf86-video-fbdev xf86-input-evdev`) instead of + the hand port in [xorg.md](xorg.md). The packages install; running them is + the next test. diff --git a/etc/zsh/zshrc b/etc/zsh/zshrc index 1b79ebc..7e500ba 100644 --- a/etc/zsh/zshrc +++ b/etc/zsh/zshrc @@ -22,12 +22,6 @@ setopt EXTENDED_GLOB setopt INTERACTIVE_COMMENTS # a # in an interactive line is a comment setopt NO_BEEP # SchOS has no audio, but it would also scroll unsetopt FLOW_CONTROL # ^S/^Q are worth more as keys than as flow control -# No PROMPT_SP. It marks output that did not end in a newline by printing a -# `#` and then padding to $COLUMNS, trusting the terminal to wrap so the next -# CR can erase it. The console is mirrored to the serial port, and a host -# terminal narrower than the SchOS screen wraps the padding early: every -# prompt came with a `#` line above it. -unsetopt PROMPT_SP # --------------------------------------------------------------------------- # Keys diff --git a/include/core/defs.h b/include/core/defs.h index c7cbd0c..94ce346 100644 --- a/include/core/defs.h +++ b/include/core/defs.h @@ -368,7 +368,6 @@ void input_mouse(int dx, int dy, int buttons); void ramdiskinit(void); void ramdiskrw(struct buf*); int ramdisk_present(void); -uint64 ramdisk_blocks(void); // usb_storage.c void usb_storage_rw(struct buf*); @@ -492,10 +491,6 @@ int sockbind(struct socket*, const char *path, uint dev, uint64 ino) int socklisten(struct socket*, int backlog); struct socket* sockaccept(struct socket*); int sockconnect(struct socket*, uint dev, uint64 ino); -int sockbind_abstract(struct socket*, uint64 key); -int sockname_unix(struct socket*, int peer, char *path); -int sockname_inet(struct socket*, int peer, uint *addr, ushort *port); -#define SOCK_ABSTRACT_DEV 0xFFFFFFFFu // no filesystem has it; see unix.c int sockreadable(struct socket*); int sock_hup(struct socket*); int sock_shutdown(struct socket*, int how); @@ -527,18 +522,12 @@ void* inet_accept(void *h); int inet_connect(void *h, uint addr, ushort port); int inet_read(void *h, char *buf, int n); int inet_write(void *h, char *buf, int n); -int inet_name(void *h, int peer, uint *addr, ushort *port); int inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port); int inet_sendto(void *h, char *buf, int n, uint addr, ushort port); int inet_readable(void *h); int inet_hup(void *h); -int inet_shutdown(void *h, int how); -int file_describe(struct file *f, char *buf, int cap); -int proc_fd_mask(int pid, uint64 mask[2]); -int proc_fd_link(int pid, int fd, char *buf, int cap); -const char *ev_kind_name(struct evobj *o); void inet_close(void *h); // proc.c @@ -554,11 +543,6 @@ int proc_pgid(int pid); int proc_setpgid(int pid, int pgid); int proc_setsid(void); int sigsend(int pid, int sig); -void alarm_arm(struct proc *p, uint after, uint every); -uint alarm_left(struct proc *p); -void alarm_thread_start(void); -void poll_kick(void); -int poll_wait_tick(void); int sig_default_kills(int sig); int proc_exists(int); int proc_num(void); diff --git a/include/core/proc.h b/include/core/proc.h index fbb692e..369ddef 100644 --- a/include/core/proc.h +++ b/include/core/proc.h @@ -231,11 +231,7 @@ struct proc { uint64 signal_mask; uint64 pending_signals; - // ITIMER_REAL, which alarm() is the whole-second form of: SIGALRM is due at - // tick alarm_at (0 = not armed) and then every alarm_every ticks after. - // alarmd, in kernel/proc/proc.c, is what posts it. - uint alarm_at; - uint alarm_every; + int alarm_ticks; char ofile_flags[NOFILE]; // Real signal delivery (added for SIGCHLD — see kernel/sys/syscall.c's diff --git a/include/dev/hid.h b/include/dev/hid.h deleted file mode 100644 index a390ddb..0000000 --- a/include/dev/hid.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "core/types.h" - -// HID over transports other than USB. The report parser and the keyboard and -// pointer paths are kernel/drivers/usb/hid/usb_hid.c's; a transport hands -// over the report descriptor once and every input report after it. - -// The device described by `desc`, or 0 if it is neither a keyboard nor a -// pointer. `what` names it in the log. -void *hid_attach(const uchar *desc, int n, const char *what); -// One input report, report ID included when the device uses them. -void hid_attached_input(void *h, const uchar *rep, int len); - -// I2C-HID: the touchpads (and some keyboards) of laptops. Started once ACPI -// and kernel threads are up; see kernel/drivers/i2c/i2c_hid.c. -void i2c_hid_start(void); diff --git a/include/fs/file.h b/include/fs/file.h index 7759a7b..6da4970 100644 --- a/include/fs/file.h +++ b/include/fs/file.h @@ -25,11 +25,6 @@ struct file { int memfd_seg; struct evobj *ev; uint64 off; - // What /proc//fd shows. `ino` names the open thing (one per socket, - // one per pipe); `path` is the absolute path an inode was opened by, as - // written -- symlinks along it are not resolved. kmalloc'd, or 0. - uint64 ino; - char *path; struct file *fnext; // file.c's free list, while ref is 0 }; diff --git a/include/fs/scff.h b/include/fs/scff.h deleted file mode 100644 index 19e0c44..0000000 --- a/include/fs/scff.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// SCFF, the SchOS Compressed File Format: a block image cut into chunks, each -// compressed on its own, with an index in front. -// -// It is what the live root is shipped as. The kernel carries the compressed -// image and decompresses a chunk the first time any block in it is read, so -// the kernel and the ISO are the compressed size, and the memory the root -// takes is what has been touched, not the whole of it. See kernel/dev/ramdisk.c -// for the reader and tools/mkscff.c for the writer; this header is the one -// definition of the layout both of them use. -// -// offset 0 struct scff_header -// index_off uint64_t off[nchunks + 1], from the start of the file -// off[i]..off[i+1] chunk i -// -// A chunk is chunk_bytes of the image (the last one may be shorter). Its -// stored length says how it is kept: -// -// 0 every byte is zero, and nothing is stored -// its full length stored as it is: it did not compress -// anything else one zstd frame -// -// Little-endian throughout, which is every machine SchOS runs on. - -#ifndef SCHOS_FS_SCFF_H -#define SCHOS_FS_SCFF_H - -#include - -#define SCFF_MAGIC "SCFF" -#define SCFF_VERSION 1 -#define SCFF_CODEC_ZSTD 1 - -// The kernel decompresses one chunk at a time into a buffer this big, so it -// is the largest chunk a reader accepts. 128 KB is squashfs's default and -// the point past which a bigger chunk buys little ratio for a lot of -// decompression on every miss. -#define SCFF_CHUNK_MAX (1u << 20) - -struct scff_header { - char magic[4]; // "SCFF" - uint16_t version; // SCFF_VERSION - uint16_t codec; // SCFF_CODEC_ZSTD - uint32_t chunk_bytes; // a power of two, a multiple of 4096, <= SCFF_CHUNK_MAX - uint32_t nchunks; - uint64_t image_bytes; // the uncompressed image - uint64_t index_off; - uint64_t stored_bytes; // the whole file, header and index included - uint8_t reserved[24]; -}; - -_Static_assert(sizeof(struct scff_header) == 64, "scff_header is 64 bytes"); - -#endif diff --git a/include/gfx/fb.h b/include/gfx/fb.h index 92a4786..e32ee79 100644 --- a/include/gfx/fb.h +++ b/include/gfx/fb.h @@ -19,7 +19,6 @@ #define FBIOGET_VSCREENINFO 0x4600 #define FBIOPUT_VSCREENINFO 0x4601 #define FBIOGET_FSCREENINFO 0x4602 -#define FBIOPUTCMAP 0x4605 #define FBIOBLANK 0x4611 #define FBIOPAN_DISPLAY 0x4606 diff --git a/include/net/socket.h b/include/net/socket.h index ccfdcdf..15753d9 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -330,9 +330,6 @@ struct socket { // bidirectional connection. struct socket *peer; int peer_closed; - // shutdown(SHUT_WR): this end has said all it will. The peer reads EOF but - // can still answer, which is what a request/response helper depends on. - int wr_shut; char data[SOCKBUF]; uint nread, nwrite; diff --git a/kernel/acpi/i2c_hid.c b/kernel/acpi/i2c_hid.c deleted file mode 100644 index d62ffc2..0000000 --- a/kernel/acpi/i2c_hid.c +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// -// Laptop touchpads: HID over I2C, on the SoC's DesignWare I2C controller. -// -// A laptop's touchpad is not on PS/2 any more and not on USB either. It hangs -// off an I2C bus inside the chipset, and the only place that says so is ACPI: -// a device whose _HID or _CID is PNP0C50, with an I2cSerialBus resource -// naming the controller and the touchpad's address, a GpioInt naming the pin -// it pulls when it has something to say, and a _DSM giving the one register -// number the protocol needs to bootstrap itself. Without this the touchpad of -// every recent laptop -- the ASUS TUF A15's included -- does nothing at all. -// -// Three layers, all here because each is small: -// -// The controller. Every x86 chipset since Haswell, AMD's included, uses the -// same Synopsys DesignWare I2C block (AMDI0010 on Ryzen, INT33C3, INT3433 -// and the PCI LPSS functions on Intel). Driven by polling: a touchpad report -// is about 30 bytes, a millisecond on the bus. -// -// HID over I2C (Microsoft's specification, version 1.0). Read the HID -// descriptor from the register _DSM names; power the device on and reset -// it; read the report descriptor; then every input report is a read of the -// input register, two bytes of length and the report. -// -// The interrupt line. It is level-triggered and stays asserted for as long -// as a report is waiting, so instead of routing it through the GPIO -// controller's interrupt the pin's *level* is read, every tick: low means -// read a report. That is exact -- no report is read twice and none is -// missed -- and needs nothing from the GPIO controller but its registers. -// On AMD those are one 32-bit word per pin. Elsewhere there is no pin to -// watch, and the input register is simply read every tick; devices answer -// a read with nothing waiting with a zero length. -// -// The reports themselves go through the USB HID driver's parser and pointer -// path (include/dev/hid.h). A Windows Precision Touchpad starts in mouse mode -// after a reset -- relative motion, a button for a click and for a tap -- and -// that is the report taken: it is what makes it a mouse on /dev/input/event1, -// for the console and for X. Multi-finger reports (two-finger scroll) need the -// device switched to touchpad mode and a gesture layer; not yet. - -#include "core/types.h" -#include "core/defs.h" -#include "core/param.h" -#include "core/spinlock.h" -#include "arch/mmu.h" -#include "dev/hid.h" -#include "dev/acpi.h" - -#include -#include -#include -#include - -// --------------------------------------------------------------------------- -// DesignWare I2C -// --------------------------------------------------------------------------- - -#define IC_CON 0x00 -#define IC_TAR 0x04 -#define IC_DATA_CMD 0x10 -#define IC_SS_SCL_HCNT 0x14 -#define IC_SS_SCL_LCNT 0x18 -#define IC_FS_SCL_HCNT 0x1C -#define IC_FS_SCL_LCNT 0x20 -#define IC_INTR_MASK 0x30 -#define IC_RAW_INTR_STAT 0x34 -#define IC_RX_TL 0x38 -#define IC_TX_TL 0x3C -#define IC_CLR_INTR 0x40 -#define IC_CLR_TX_ABRT 0x54 -#define IC_CLR_STOP_DET 0x60 -#define IC_ENABLE 0x6C -#define IC_STATUS 0x70 -#define IC_TXFLR 0x74 -#define IC_RXFLR 0x78 -#define IC_SDA_HOLD 0x7C -#define IC_TX_ABRT_SOURCE 0x80 -#define IC_ENABLE_STATUS 0x9C -#define IC_COMP_PARAM_1 0xF4 -#define IC_COMP_TYPE 0xFC - -#define DW_COMP_TYPE 0x44570140 - -#define CON_MASTER (1 << 0) -#define CON_SPEED_STD (1 << 1) -#define CON_SPEED_FAST (2 << 1) -#define CON_RESTART_EN (1 << 5) -#define CON_SLAVE_DISABLE (1 << 6) - -#define CMD_READ (1 << 8) -#define CMD_STOP (1 << 9) -#define CMD_RESTART (1 << 10) - -#define INTR_TX_ABRT (1 << 6) -#define INTR_STOP_DET (1 << 9) - -#define NCTL 4 - -struct dwi2c { - uint64 phys; - volatile uint32 *regs; - int txdepth, rxdepth; - int fast; -}; - -static struct dwi2c ctls[NCTL]; -static int nctl; - -static uint32 -rd(struct dwi2c *c, int reg) -{ - return c->regs[reg / 4]; -} - -static void -wr(struct dwi2c *c, int reg, uint32 v) -{ - c->regs[reg / 4] = v; -} - -static int -dw_enable(struct dwi2c *c, int on) -{ - int i; - - for(i = 0; i < 1000; i++){ - wr(c, IC_ENABLE, on); - if((rd(c, IC_ENABLE_STATUS) & 1) == (uint32)on) - return 0; - microdelay(25); - } - return -1; -} - -// SCL high and low periods, in controller clocks, from the firmware's own -// numbers when it gives them: SSCN and FMCN are packages of (HCNT, LCNT, -// SDA hold) for exactly this controller's clock. -static int -acpi_counts(uacpi_namespace_node *node, const char *name, - uint32 *hcnt, uint32 *lcnt, uint32 *hold) -{ - uacpi_object *o = 0; - uacpi_object_array a; - uacpi_u64 v[3]; - int i; - - if(uacpi_eval_package(node, name, 0, &o) != UACPI_STATUS_OK) - return -1; - if(uacpi_object_get_package(o, &a) != UACPI_STATUS_OK || a.count < 2){ - uacpi_object_unref(o); - return -1; - } - v[2] = 0; - for(i = 0; i < 3 && i < (int)a.count; i++) - if(uacpi_object_get_integer(a.objects[i], &v[i]) != UACPI_STATUS_OK){ - uacpi_object_unref(o); - return -1; - } - uacpi_object_unref(o); - *hcnt = v[0]; - *lcnt = v[1]; - *hold = v[2]; - return 0; -} - -static int -dw_init(struct dwi2c *c, uacpi_namespace_node *node, uint32 speed) -{ - uint32 type, param, hcnt, lcnt, hold = 0; - - c->regs = ioremap(c->phys, 0x1000); - if(c->regs == 0) - return -1; - type = rd(c, IC_COMP_TYPE); - if(type != DW_COMP_TYPE){ - cprintf("i2c: controller at 0x%lx is not DesignWare (0x%x)\n", - c->phys, type); - return -1; - } - param = rd(c, IC_COMP_PARAM_1); - c->txdepth = param ? ((param >> 16) & 0xFF) + 1 : 8; - c->rxdepth = param ? ((param >> 8) & 0xFF) + 1 : 8; - c->fast = speed >= 400000; - - if(dw_enable(c, 0) < 0) - return -1; - wr(c, IC_CON, CON_MASTER | CON_SLAVE_DISABLE | CON_RESTART_EN | - (c->fast ? CON_SPEED_FAST : CON_SPEED_STD)); - - // Without the firmware's numbers, periods long enough for a 150 MHz clock, - // the fastest any of these runs at: on a slower clock the bus is merely - // slower than it could be, never out of specification. - if(c->fast){ - if(acpi_counts(node, "FMCN", &hcnt, &lcnt, &hold) < 0){ - hcnt = 135; // 0.6 us high, 1.3 us low, plus rise and fall - lcnt = 240; - } - wr(c, IC_FS_SCL_HCNT, hcnt); - wr(c, IC_FS_SCL_LCNT, lcnt); - } else { - if(acpi_counts(node, "SSCN", &hcnt, &lcnt, &hold) < 0){ - hcnt = 645; // 4.0 us high, 4.7 us low, plus the same - lcnt = 750; - } - wr(c, IC_SS_SCL_HCNT, hcnt); - wr(c, IC_SS_SCL_LCNT, lcnt); - } - if(hold) - wr(c, IC_SDA_HOLD, hold); - wr(c, IC_INTR_MASK, 0); - wr(c, IC_RX_TL, 0); - wr(c, IC_TX_TL, 0); - rd(c, IC_CLR_INTR); - return 0; -} - -// Write `wn` bytes to `addr`, then read `rn` bytes back after a repeated -// start, all as one transaction. 0, or -1 if the device did not answer. -// -// The command FIFO holds reads as well as writes, and each read becomes a -// byte in the receive FIFO, so reads are queued no faster than they are -// drained: a full receive FIFO stalls the bus. -static int -dw_xfer(struct dwi2c *c, int addr, const uchar *w, int wn, uchar *r, int rn) -{ - int total = wn + rn, sent = 0, got = 0, spin; - uint32 raw; - - if(dw_enable(c, 0) < 0) - return -1; - wr(c, IC_TAR, addr); - rd(c, IC_CLR_INTR); - if(dw_enable(c, 1) < 0) - return -1; - - for(spin = 0; got < rn || sent < total; spin++){ - raw = rd(c, IC_RAW_INTR_STAT); - if(raw & INTR_TX_ABRT) - goto abort; - while(got < rn && rd(c, IC_RXFLR) > 0) - r[got++] = rd(c, IC_DATA_CMD); - while(sent < total && (int)rd(c, IC_TXFLR) < c->txdepth){ - uint32 cmd; - if(sent >= wn && (sent - wn) - got >= c->rxdepth) - break; - if(sent < wn) - cmd = w[sent]; - else - cmd = CMD_READ | (sent == wn && wn > 0 ? CMD_RESTART : 0); - if(sent == total - 1) - cmd |= CMD_STOP; - wr(c, IC_DATA_CMD, cmd); - sent++; - } - if(spin > 20000) // 200 ms: no transfer of ours takes that long - goto abort; - microdelay(10); - } - for(spin = 0; !(rd(c, IC_RAW_INTR_STAT) & INTR_STOP_DET); spin++){ - if(rd(c, IC_RAW_INTR_STAT) & INTR_TX_ABRT) - goto abort; - if(spin > 2000) - goto abort; - microdelay(10); - } - rd(c, IC_CLR_STOP_DET); - dw_enable(c, 0); - return 0; - -abort: - rd(c, IC_TX_ABRT_SOURCE); - rd(c, IC_CLR_TX_ABRT); - rd(c, IC_CLR_INTR); - dw_enable(c, 0); - return -1; -} - -// --------------------------------------------------------------------------- -// ACPI -// --------------------------------------------------------------------------- - -struct found { - uint64 mem; // the first memory range - int i2c, addr; - uint32 speed; - char bus[64]; // the controller's namespace path - int gpio, pin, active_low; - char gpioctl[64]; -}; - -static void -copy_source(char *dst, const uacpi_resource_source *s) -{ - int n = s->string && s->length ? s->length : 0; - if(n > 63) - n = 63; - if(n) - memmove(dst, s->string, n); - dst[n] = 0; -} - -static uacpi_iteration_decision -take_resource(void *user, uacpi_resource *r) -{ - struct found *f = user; - - switch(r->type){ - case UACPI_RESOURCE_TYPE_FIXED_MEMORY32: - if(!f->mem) f->mem = r->fixed_memory32.address; - break; - case UACPI_RESOURCE_TYPE_MEMORY32: - if(!f->mem) f->mem = r->memory32.minimum; - break; - case UACPI_RESOURCE_TYPE_ADDRESS32: - if(!f->mem && r->address32.common.type == UACPI_RANGE_MEMORY) - f->mem = r->address32.minimum; - break; - case UACPI_RESOURCE_TYPE_ADDRESS64: - if(!f->mem && r->address64.common.type == UACPI_RANGE_MEMORY) - f->mem = r->address64.minimum; - break; - case UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION: - if(!f->i2c){ - f->i2c = 1; - f->addr = r->i2c_connection.slave_address; - f->speed = r->i2c_connection.connection_speed; - copy_source(f->bus, &r->i2c_connection.common.source); - } - break; - case UACPI_RESOURCE_TYPE_GPIO_CONNECTION: - if(!f->gpio && r->gpio_connection.type == UACPI_GPIO_CONNECTION_INTERRUPT && - r->gpio_connection.pin_table_length > 0){ - f->gpio = 1; - f->pin = r->gpio_connection.pin_table[0]; - f->active_low = r->gpio_connection.intr.polarity != UACPI_POLARITY_ACTIVE_HIGH; - copy_source(f->gpioctl, &r->gpio_connection.source); - } - break; - } - return UACPI_ITERATION_DECISION_CONTINUE; -} - -static int -resources(uacpi_namespace_node *node, struct found *f) -{ - memset(f, 0, sizeof(*f)); - return uacpi_for_each_device_resource(node, "_CRS", take_resource, f) == - UACPI_STATUS_OK ? 0 : -1; -} - -// The I2C-HID _DSM, function 1: the HID descriptor's register. -static int -hid_desc_register(uacpi_namespace_node *node, uint32 *reg) -{ - static const uchar uuid[16] = { // 3CDFF6F7-4267-4555-AD05-B30A3D8938DE - 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, - 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, - }; - uacpi_data_view view = { .const_bytes = uuid, .length = sizeof(uuid) }; - uacpi_object_array none = { 0, 0 }; - uacpi_object *args[4], *ret = 0; - uacpi_object_array a = { args, 4 }; - uacpi_u64 v = 0; - int rc = -1, i; - - args[0] = uacpi_object_create_buffer(view); - args[1] = uacpi_object_create_integer(1); // revision - args[2] = uacpi_object_create_integer(1); // function - args[3] = uacpi_object_create_package(none); - if(args[0] && args[1] && args[2] && args[3] && - uacpi_eval(node, "_DSM", &a, &ret) == UACPI_STATUS_OK && ret && - uacpi_object_get_integer(ret, &v) == UACPI_STATUS_OK){ - *reg = v; - rc = 0; - } - if(ret) - uacpi_object_unref(ret); - for(i = 0; i < 4; i++) - if(args[i]) - uacpi_object_unref(args[i]); - return rc; -} - -static uacpi_namespace_node * -node_at(const char *path) -{ - uacpi_namespace_node *n = 0; - if(path[0] == 0 || - uacpi_namespace_node_find(0, path, &n) != UACPI_STATUS_OK) - return 0; - return n; -} - -static struct dwi2c * -controller(const char *path, uint32 speed) -{ - uacpi_namespace_node *node = node_at(path); - struct found f; - struct dwi2c *c; - int i; - - if(node == 0 || resources(node, &f) < 0 || f.mem == 0){ - cprintf("i2c: no registers for controller %s\n", path); - return 0; - } - for(i = 0; i < nctl; i++) - if(ctls[i].phys == f.mem) - return &ctls[i]; - if(nctl == NCTL) - return 0; - uacpi_execute(node, "_PS0", 0); - c = &ctls[nctl]; - memset(c, 0, sizeof(*c)); - c->phys = f.mem; - if(dw_init(c, node, speed) < 0) - return 0; - nctl++; - cprintf("i2c: %s at 0x%lx, %s mode, FIFOs %d/%d\n", path, c->phys, - c->fast ? "fast" : "standard", c->txdepth, c->rxdepth); - return c; -} - -// --------------------------------------------------------------------------- -// HID over I2C -// --------------------------------------------------------------------------- - -#define NI2CHID 4 - -struct i2chid { - struct dwi2c *c; - int addr; - uint32 cmdreg, inreg, maxin; - volatile uint32 *pin; // the interrupt pin's register, or 0 - int active_low; - void *hid; - uchar *buf; // one page, for input reports - char name[8]; -}; - -static struct i2chid devs[NI2CHID]; -static int ndev; - -static int -reg_write(struct i2chid *d, const uchar *w, int n) -{ - return dw_xfer(d->c, d->addr, w, n, 0, 0); -} - -static int -reg_read(struct i2chid *d, uint32 reg, uchar *r, int n) -{ - uchar w[2] = { reg & 0xFF, reg >> 8 }; - return dw_xfer(d->c, d->addr, w, 2, r, n); -} - -// SET_POWER and RESET, the two commands needed: written to the command -// register as (register, opcode byte, power state / report byte). -static int -command(struct i2chid *d, int opcode, int arg) -{ - uchar w[4] = { d->cmdreg & 0xFF, d->cmdreg >> 8, arg, opcode }; - return reg_write(d, w, 4); -} - -#define OP_RESET 0x01 -#define OP_SET_POWER 0x08 - -static void -wait_ticks(uint n) -{ - acquire(&tickslock); - uint start = ticks; - while(ticks - start < n) - sleep(&ticks, &tickslock); - release(&tickslock); -} - -// AMD's GPIO controller: one 32-bit register per pin, its level in bit 16. -#define AMD_PIN_STS (1u << 16) - -static int -asserted(struct i2chid *d) -{ - int high = (*d->pin & AMD_PIN_STS) != 0; - return d->active_low ? !high : high; -} - -static volatile uint32 * -interrupt_pin(const struct found *f) -{ - static const char *const amd[] = { "AMDI0030", "AMD0030", 0 }; - uacpi_namespace_node *node = node_at(f->gpioctl); - struct found g; - - if(!f->gpio || node == 0 || !uacpi_device_matches_pnp_id(node, amd)) - return 0; - if(resources(node, &g) < 0 || g.mem == 0) - return 0; - return (volatile uint32 *)((char *)ioremap(g.mem, 0x1000) + f->pin * 4); -} - -// One input report: two bytes of length, length included, then the report. -// A length of 0 is the device saying it has nothing, or acknowledging a -// reset. -static int -read_input(struct i2chid *d) -{ - int len; - - if(dw_xfer(d->c, d->addr, 0, 0, d->buf, d->maxin) < 0) - return -1; - len = d->buf[0] | (d->buf[1] << 8); - if(len <= 2 || len > (int)d->maxin) - return 0; - hid_attached_input(d->hid, d->buf + 2, len - 2); - return 1; -} - -static uacpi_iteration_decision -probe(void *user, uacpi_namespace_node *node, uacpi_u32 depth) -{ - struct i2chid *d; - struct found f; - uchar desc[30], *rdesc; - uint32 descreg, rlen, rreg; - int i; - - (void)user; - (void)depth; - if(ndev == NI2CHID) - return UACPI_ITERATION_DECISION_BREAK; - d = &devs[ndev]; - memset(d, 0, sizeof(*d)); - memmove(d->name, uacpi_namespace_node_name(node).text, 4); - - if(resources(node, &f) < 0 || !f.i2c){ - cprintf("i2c-hid: %s has no I2C connection\n", d->name); - return UACPI_ITERATION_DECISION_CONTINUE; - } - if(hid_desc_register(node, &descreg) < 0){ - cprintf("i2c-hid: %s: _DSM gave no descriptor register\n", d->name); - return UACPI_ITERATION_DECISION_CONTINUE; - } - if((d->c = controller(f.bus, f.speed)) == 0) - return UACPI_ITERATION_DECISION_CONTINUE; - d->addr = f.addr; - uacpi_execute(node, "_PS0", 0); - - if(reg_read(d, descreg, desc, sizeof(desc)) < 0 || - (desc[0] | desc[1] << 8) != 30){ - cprintf("i2c-hid: %s at 0x%x does not answer\n", d->name, d->addr); - return UACPI_ITERATION_DECISION_CONTINUE; - } - rlen = desc[4] | desc[5] << 8; - rreg = desc[6] | desc[7] << 8; - d->inreg = desc[8] | desc[9] << 8; - d->maxin = desc[10] | desc[11] << 8; - d->cmdreg = desc[16] | desc[17] << 8; - if(d->maxin < 3 || d->maxin > PGSIZE || rlen == 0 || rlen > PGSIZE){ - cprintf("i2c-hid: %s: unusable descriptor\n", d->name); - return UACPI_ITERATION_DECISION_CONTINUE; - } - d->pin = interrupt_pin(&f); - d->active_low = f.active_low; - - command(d, OP_SET_POWER, 0); // 0 = on - wait_ticks(2); - command(d, OP_RESET, 0); - wait_ticks(10); - d->buf = (uchar *)kalloc(); - rdesc = (uchar *)kalloc(); - if(d->buf == 0 || rdesc == 0) - return UACPI_ITERATION_DECISION_BREAK; - // The reset is acknowledged with an empty report. Read it, and anything - // else already waiting, so the first real report is not behind it. - for(i = 0; i < 4 && (d->pin == 0 || asserted(d)); i++) - if(dw_xfer(d->c, d->addr, 0, 0, d->buf, d->maxin) < 0) - break; - - if(reg_read(d, rreg, rdesc, rlen) < 0){ - cprintf("i2c-hid: %s: report descriptor unreadable\n", d->name); - kfree((char *)rdesc); - return UACPI_ITERATION_DECISION_CONTINUE; - } - char what[24] = "i2c-hid "; - memmove(what + 8, d->name, 4); - d->hid = hid_attach(rdesc, rlen, what); - kfree((char *)rdesc); - if(d->hid == 0){ - cprintf("i2c-hid: %s is neither a keyboard nor a pointer\n", d->name); - return UACPI_ITERATION_DECISION_CONTINUE; - } - cprintf("i2c-hid: %s: %04x:%04x at 0x%x, %s\n", d->name, - desc[20] | desc[21] << 8, desc[22] | desc[23] << 8, d->addr, - d->pin ? "interrupt pin watched" : "polled"); - ndev++; - return UACPI_ITERATION_DECISION_CONTINUE; -} - -static void -i2c_hid_thread(void) -{ - static const char *const ids[] = { "PNP0C50", "ACPI0C50", 0 }; - int i, n; - - uacpi_find_devices_at(uacpi_namespace_root(), ids, probe, 0); - if(ndev == 0) - for(;;) - wait_ticks(1000000); - - for(;;){ - wait_ticks(1); - for(i = 0; i < ndev; i++){ - struct i2chid *d = &devs[i]; - if(d->pin == 0){ - read_input(d); - continue; - } - // Everything waiting, up to a bound so that a stuck line cannot keep - // the other devices from their turn. - for(n = 0; n < 16 && asserted(d); n++) - if(read_input(d) < 0) - break; - } - } -} - -void -i2c_hid_start(void) -{ - if(acpi_rsdp_phys() == 0) - return; - if(kthread_create(i2c_hid_thread, "i2chid") < 0) - cprintf("i2c-hid: no thread; touchpads will not work\n"); -} diff --git a/kernel/arch/x86_64/trapasm.S b/kernel/arch/x86_64/trapasm.S index 1e6ab24..e5dfed9 100644 --- a/kernel/arch/x86_64/trapasm.S +++ b/kernel/arch/x86_64/trapasm.S @@ -1,4 +1,3 @@ -#include "core/param.h" /* KSTACKSIZE */ # trapasm.S — kernel/user entry/exit with FS_BASE swapping for TLS # # The kernel uses __thread struct cpu *cpu and __thread struct proc *proc, @@ -210,13 +209,7 @@ syscall_entry: movq %rax, %fs:(8) # save rax in per-CPU scratch slot movq %gs:24, %rax # proc pointer (PERCPU_PROC) movq 0x10(%rax), %rax # proc->kstack (offset 16) - # The top of the whole stack, where allocproc() put the trapframe and - # where a trap from user mode lands (TSS rsp0). This was $(4096-16) from - # when the stack was one page, and stayed that when it became two: every - # syscall ran in the bottom 4 KB with the top 4 KB unused, and a deep - # enough read -- startx exec'ing through a SCFF chunk unpack -- ran - # through the canary at kstack[0]. - addq $(KSTACKSIZE-16), %rax # point to tf->rsp slot + addq $(4096-16), %rax # point to tf->rsp slot movq %rsp, (%rax) # save user rsp movq %rax, %rsp # switch to kernel stack movq %fs:(8), %rax # restore rax diff --git a/kernel/core/console.c b/kernel/core/console.c index 2e97a19..099d000 100644 --- a/kernel/core/console.c +++ b/kernel/core/console.c @@ -442,8 +442,7 @@ erase_one(struct vt *v) // Keyboard input // --------------------------------------------------------------------------- -// Alt+F1..F6, Ctrl+Alt+F1..F6 and the settable chord (AltGr+F1..F6 by -// default) switch terminals. kbd.c reports them as +// Ctrl+Alt+Shift+F1..F6 switch terminals. kbd.c reports them as // KBD_VT_SWITCH+n so that the // decision of what a chord *means* is made here, where the terminals are, // rather than in a driver that should not know they exist. diff --git a/kernel/core/main.c b/kernel/core/main.c index dd3c0a2..475e171 100644 --- a/kernel/core/main.c +++ b/kernel/core/main.c @@ -12,7 +12,6 @@ #include "core/klog.h" #include "dev/usb.h" #include "dev/acpi.h" -#include "dev/hid.h" #include "arch/vmap.h" #include "core/hwinfo.h" @@ -392,9 +391,6 @@ kmain(uint64 fb_addr) usb_hotplug_start(); // Where AML runs for the SCI: the power button, GPEs, notifications. acpi_thread_start(); - // Laptop touchpads, on the chipset's I2C bus; found through ACPI, so after - // the namespace is loaded, and polled from a thread of their own. - i2c_hid_start(); // The debugger's own thread, for the same reason netinit's comes after // sinit: kthread_create needs a usable process table. From here on Ctrl-G // on the console answers, with no filesystem mounted and nothing in @@ -404,7 +400,6 @@ kmain(uint64 fb_addr) random_init(); netuser_init(); dbg_thread_start(); - alarm_thread_start(); if (boot_verbose) cprintf("verbose boot: dbg thread started\n"); // The greeting is not printed here. diff --git a/kernel/dev/fb.c b/kernel/dev/fb.c index 0a62efd..0b9412a 100644 --- a/kernel/dev/fb.c +++ b/kernel/dev/fb.c @@ -230,13 +230,6 @@ fb_ioctl(int request, addr_t arg) // blanking does too, as Linux's own drivers without the hook do. case FBIOBLANK: return 0; - - // A palette, on a framebuffer that is always true-colour. Linux's drivers - // keep the values for the console's sixteen colours and succeed; refusing - // made Xorg's fbdev driver log an error for every entry it loads, 256 of - // them at each start. - case FBIOPUTCMAP: - return 0; } return -ENOTTY; diff --git a/kernel/dev/input.c b/kernel/dev/input.c index 228e6c2..78c8801 100644 --- a/kernel/dev/input.c +++ b/kernel/dev/input.c @@ -156,7 +156,6 @@ input_sync(int dev) return; input_report(dev, EV_SYN, SYN_REPORT, 0); wakeup(&idev[dev]); - poll_kick(); // Xorg polls its devices; a mouse moves at the next tick otherwise } int diff --git a/kernel/dev/kbd.c b/kernel/dev/kbd.c index f7cc3b5..6acae66 100644 --- a/kernel/dev/kbd.c +++ b/kernel/dev/kbd.c @@ -152,16 +152,6 @@ kbdgetc(void) if (data == SCAN_EQUALS) return KBD_VT_NEXT; } - // And Linux's own: Alt+F1..F6 and Ctrl+Alt+F1..F6, whatever the chord is set - // to. These are what anyone who has used Linux presses, on a real machine, - // and nothing reached them: SchOS answered only AltGr+F-keys, which no one - // thinks to try. Under X the terminal is in VT_PROCESS, console_vt_hotkey() - // lets the keys through, and X switches with them itself -- as on Linux. - if (data >= SCAN_F1 && data < SCAN_F1 + NVT && - ((shift & (CTL | SHIFT | ALT | RALT)) == ALT || - (shift & (CTL | SHIFT | ALT | RALT)) == (CTL | ALT))) - return KBD_VT_SWITCH + (data - SCAN_F1); - cp = keymap_lookup(data, shift); if (cp == 0) return 0; diff --git a/kernel/dev/ramdisk.c b/kernel/dev/ramdisk.c index e988716..055ad99 100644 --- a/kernel/dev/ramdisk.c +++ b/kernel/dev/ramdisk.c @@ -14,28 +14,6 @@ // // Enabled by building with RAMROOT=1. Without it this file still compiles, the // device reports zero size, and the kernel boots from the IDE disk as before. -// -// The device is larger than the image. The image is only what the root holds, -// with a little slack; past its end the device goes on to half of the memory -// free at mount time, and those blocks are pages taken from kalloc the first -// time each is written -- a block nobody has written reads as zeros and costs -// nothing. SchFS grows the volume to the device's size when it mounts (see -// grow_to in kernel/fs/schfs/vol.c), so `apk add` on a live system has as -// much room as the machine has memory to give, rather than whatever empty -// space was built into the ISO. The ISO used to carry 60 MB of zeros for it. -// -// Pages are not given back when SchFS frees a block: there is no discard. -// A freed block is reused by the next write, so the cost is the high-water -// mark, not a leak. -// -// **A compressed image.** The blob can also be SCFF (include/fs/scff.h): the -// image cut into chunks, each compressed with zstd. Then no block is served in -// place. The page directory covers the image too, and a block with no page -// has not been unpacked yet: reading it decompresses its whole chunk, and -// every block of that chunk nobody has written gets a page of its own. The -// kernel and the ISO are the compressed size -- the 360 MB root with Xorg in -// it is 104 MB -- and the root costs memory only for what has been read or -// written. A block of zeros points at one shared zero page and costs nothing. #include "core/types.h" #include "core/defs.h" @@ -46,8 +24,6 @@ #include "core/sleeplock.h" #include "fs/fs.h" #include "fs/buf.h" -#include "fs/scff.h" -#include "../scff/scff_zstd.h" #ifdef RAMDISK_ROOT // Supplied by `ld -r -b binary` over build/fs-ram.img, renamed by objcopy. @@ -57,63 +33,15 @@ extern uchar _binary_ramdisk_size[]; static uchar *rd_base; static uint64 rd_size; // bytes -static uint rd_blocks; // blocks in the image -static uint rd_first; // the first block with a page: rd_blocks, or 0 for SCFF - -// SCFF only. rd_zlock is held across a chunk's decompression, and makes -// rd_chunk -- the one chunk-sized buffer -- its holder's. -static struct scff_header *rd_scff; -static uint64 *rd_index; -static struct sleeplock rd_zlock; -static uchar rd_chunk[SCFF_CHUNK_MAX] __attribute__((aligned(PGSIZE))); -static uchar rd_zero[BSIZE] __attribute__((aligned(PGSIZE))); -static uint64 rd_unpacked; // chunks decompressed - -// rd_dir[i] is a page of pointers, entry j the page holding block -// rd_first + i*RD_PTRS + j, or 0 for one never written. 32768 pages of -// 512 pointers reach 64 GB, which is as far as half of any machine this runs -// on is likely to go, and costs 256 KB of pointers here. -#define RD_PTRS (PGSIZE / sizeof(uchar *)) -#define RD_DIRS 32768 -static uchar **rd_dir[RD_DIRS]; -static uint64 rd_total; // blocks, image and the rest; 0 until sized -static uint64 rd_pages; // pages taken for blocks past the image -static struct spinlock rd_lock; +static uint rd_blocks; // rd_size / BSIZE void ramdiskinit(void) { - initlock(&rd_lock, "ramdisk"); #ifdef RAMDISK_ROOT rd_base = _binary_ramdisk_start; rd_size = (uint64)_binary_ramdisk_size; rd_blocks = (uint)(rd_size / BSIZE); - rd_first = rd_blocks; - - if(rd_size >= sizeof(struct scff_header) && memcmp(rd_base, SCFF_MAGIC, 4) == 0){ - struct scff_header *h = (struct scff_header *)rd_base; - uint64 chunks = h->chunk_bytes ? (h->image_bytes + h->chunk_bytes - 1) / h->chunk_bytes : 0; - if(h->version != SCFF_VERSION || h->codec != SCFF_CODEC_ZSTD || - h->chunk_bytes < BSIZE || h->chunk_bytes > SCFF_CHUNK_MAX || - (h->chunk_bytes & (h->chunk_bytes - 1)) || h->image_bytes % BSIZE || - chunks != h->nchunks || h->stored_bytes > rd_size || - h->index_off + (h->nchunks + 1) * sizeof(uint64) > rd_size) - panic("ramdiskinit: the SCFF header does not describe this image"); - rd_index = (uint64 *)(rd_base + h->index_off); - for(uint i = 0; i < h->nchunks; i++) - if(rd_index[i] > rd_index[i + 1] || rd_index[i + 1] > h->stored_bytes || - rd_index[i + 1] - rd_index[i] > h->chunk_bytes) - panic("ramdiskinit: the SCFF index is damaged"); - if(scff_zstd_init() < 0) - panic("ramdiskinit: no room for the zstd decoder"); - initsleeplock(&rd_zlock, "scff"); - rd_scff = h; - rd_blocks = (uint)(h->image_bytes / BSIZE); - rd_first = 0; - cprintf("ramdisk: SCFF, %u MB in %u MB, %u chunks of %u KB\n", - (uint)(h->image_bytes >> 20), (uint)(rd_size >> 20), - h->nchunks, h->chunk_bytes >> 10); - } if(rd_blocks == 0) panic("ramdiskinit: embedded image is empty"); @@ -123,9 +51,8 @@ ramdiskinit(void) if(rd_blocks < 2) panic("ramdiskinit: embedded image smaller than a superblock"); - if(!rd_scff) - cprintf("ramdisk: %u blocks (%u KB) at %p\n", - rd_blocks, (uint)(rd_size / 1024), rd_base); + cprintf("ramdisk: %u blocks (%u KB) at %p\n", + rd_blocks, (uint)(rd_size / 1024), rd_base); #else rd_base = 0; rd_size = 0; @@ -139,115 +66,6 @@ ramdisk_present(void) return rd_blocks != 0; } -// The device's size in blocks, fixed the first time anyone asks -- which is -// SchFS mounting the root, after every page has reached the allocator. Asked -// earlier, during ramdiskinit, the free count would be the first few -// megabytes kinit1 handed over and nothing more. -uint64 -ramdisk_blocks(void) -{ - if (rd_blocks == 0) - return 0; - acquire(&rd_lock); - if (rd_total == 0) { - uint64 extra = kalloc_free_bytes() / 2 / BSIZE; - uint64 room = (uint64)RD_DIRS * RD_PTRS - (rd_blocks - rd_first); - if (extra > room) - extra = room; - rd_total = rd_blocks + extra; - cprintf("ramdisk: %u MB image, %u MB of memory to grow into\n", - (uint)(rd_size >> 20), (uint)(extra * BSIZE >> 20)); - } - release(&rd_lock); - return rd_total; -} - -// The page behind a block, made when `make` says so. One that is only the -// shared zero page gets a page of its own before anything writes to it. -static uchar * -rd_page(uint64 blk, int make) -{ - uint64 i = (blk - rd_first) / RD_PTRS, j = (blk - rd_first) % RD_PTRS; - uchar *pg = 0; - - acquire(&rd_lock); - if (rd_dir[i] == 0 && make) { - if ((rd_dir[i] = (uchar **)kalloc()) != 0) - memset(rd_dir[i], 0, PGSIZE); - } - if (rd_dir[i]) { - pg = rd_dir[i][j]; - if ((pg == 0 || pg == rd_zero) && make && (pg = (uchar *)kalloc()) != 0) { - if (rd_dir[i][j] == rd_zero) - memset(pg, 0, BSIZE); - rd_dir[i][j] = pg; - rd_pages++; - } - } - release(&rd_lock); - if (make && pg == 0) - panic("ramdisk: out of memory for the live root"); - return pg; -} - -// Give every block of SCFF chunk c that has no page one: its contents, or the -// zero page. Blocks already there -- written since boot, or unpacked by -// whoever held the lock before us -- are left as they are. -static void -rd_unpack(uint c) -{ - uint per = rd_scff->chunk_bytes / BSIZE; - uint64 first = (uint64)c * per; - uint n = rd_blocks - first < per ? (uint)(rd_blocks - first) : per; - uint64 len = rd_index[c + 1] - rd_index[c]; - uchar *src = rd_base + rd_index[c]; - uint64 want = (uint64)n * BSIZE; - uchar *data = rd_chunk; - - acquiresleep(&rd_zlock); - if (len == 0) { - data = 0; // all zeros - } else if (len == want) { - data = src; // stored as it is - } else { - long got = scff_zstd_decompress(rd_chunk, sizeof(rd_chunk), src, len); - if (got != (long)want) - panic("ramdisk: an SCFF chunk does not decompress"); - rd_unpacked++; - } - - for (uint k = 0; k < n; k++) { - uint64 blk = first + k, i = blk / RD_PTRS, j = blk % RD_PTRS; - uchar *b = data ? data + (uint64)k * BSIZE : 0; - int zero = 1; - if (b) - for (uint w = 0; w < BSIZE / 8 && zero; w++) - zero = ((uint64 *)b)[w] == 0; - - uchar *pg = zero ? rd_zero : (uchar *)kalloc(); - if (pg == 0) - panic("ramdisk: out of memory for the live root"); - if (!zero) - memmove(pg, b, BSIZE); - - acquire(&rd_lock); - if (rd_dir[i] == 0 && (rd_dir[i] = (uchar **)kalloc()) != 0) - memset(rd_dir[i], 0, PGSIZE); - if (rd_dir[i] == 0) - panic("ramdisk: out of memory for the live root"); - if (rd_dir[i][j] == 0) { - rd_dir[i][j] = pg; - if (!zero) - rd_pages++; - pg = 0; - } - release(&rd_lock); - if (pg && pg != rd_zero) - kfree((char *)pg); // someone was there first - } - releasesleep(&rd_zlock); -} - // Sync buf with the in-memory image. Mirrors iderw()'s contract: on a read, // fill b->data and set B_VALID; on a write, copy out and clear B_DIRTY. void @@ -259,25 +77,8 @@ ramdiskrw(struct buf *b) panic("ramdiskrw: nothing to do"); if(rd_base == 0) panic("ramdiskrw: no ramdisk"); - if(b->blockno >= rd_first){ - if(b->blockno >= ramdisk_blocks()) - panic("ramdiskrw: block out of range"); - uchar *pg = rd_page(b->blockno, (b->flags & B_DIRTY) != 0); - if(pg == 0 && rd_scff && b->blockno < rd_blocks && !(b->flags & B_DIRTY)){ - rd_unpack((uint)(b->blockno / (rd_scff->chunk_bytes / BSIZE))); - pg = rd_page(b->blockno, 0); - } - if(b->flags & B_DIRTY){ - memmove(pg, b->data, BSIZE); - b->flags &= ~B_DIRTY; - } else if(pg){ - memmove(b->data, pg, BSIZE); - } else { - memset(b->data, 0, BSIZE); // never written - } - b->flags |= B_VALID; - return; - } + if(b->blockno >= rd_blocks) + panic("ramdiskrw: block out of range"); uchar *p = rd_base + (uint64)b->blockno * BSIZE; diff --git a/kernel/drivers/usb/hid/usb_hid.c b/kernel/drivers/usb/hid/usb_hid.c index 94cea98..569697e 100644 --- a/kernel/drivers/usb/hid/usb_hid.c +++ b/kernel/drivers/usb/hid/usb_hid.c @@ -25,9 +25,6 @@ #include "core/dma.h" #include "dev/usb.h" #include "dev/input.h" -#include "dev/hid.h" -#include "dev/kbd.h" -#include "dev/vt.h" #define NHID 8 @@ -50,11 +47,6 @@ struct hid { // report, for a mouse whose descriptor could not be used. int boot; int report_id; // 0 when reports carry no ID byte - // 1 + the only report ID to take pointer fields from, 0 for any; and - // 1 + the first report ID with a relative X. A touchpad describes both a - // mouse report and a multi-finger one with absolute coordinates, and only - // the mouse report makes sense as a pointer; see hid_attach(). - int only_id1, rel_id1; struct hfield x, y, wheel, btn[HID_NBTN]; // A keyboard in report protocol: where its keys are, so that each report @@ -114,7 +106,7 @@ static const uchar hid_shifted[0x39] = { // which means the entire line discipline is reused rather than reimplemented. #define KQSIZE 64 static struct { - int buf[KQSIZE]; // characters, and KBD_* codes past KBD_SPECIAL + uchar buf[KQSIZE]; uint r, w; } kq; @@ -157,32 +149,6 @@ usb_kbd_getc(void) } // Was this usage in that report? -// The keys past the character table, as the console names them. The PS/2 -// path has always produced these (kernel/dev/kbd.c); this one skipped every -// usage from 0x39 up, so on a USB keyboard the console had no arrows, no -// Home/End/PgUp/PgDn/Delete, and no way to switch terminals. -static int -hid_special(uchar usage, uchar mods) -{ - // F1..F6 with Alt, AltGr or Ctrl+Alt: switch terminals, as on Linux. - if (usage >= 0x3A && usage < 0x3A + NVT && (mods & HID_MOD_ALT) && - !(mods & HID_MOD_SHIFT)) - return KBD_VT_SWITCH + (usage - 0x3A); - switch (usage) { - case 0x49: return KBD_INS; - case 0x4A: return KBD_HOME; - case 0x4B: return KBD_PGUP; - case 0x4C: return KBD_DEL; - case 0x4D: return KBD_END; - case 0x4E: return KBD_PGDN; - case 0x4F: return KBD_RT; - case 0x50: return KBD_LF; - case 0x51: return KBD_DN; - case 0x52: return KBD_UP; - } - return 0; -} - static int was_down(const uchar *prev, uchar usage) { @@ -256,23 +222,18 @@ keyboard_report(struct hid *h, const uchar *rep, int len) for(i = 2; i < 8; i++){ uchar usage = rep[i]; - if(usage == 0) + if(usage == 0 || usage >= 0x39) continue; if(was_down(h->prev, usage)) continue; // still held from last time, not a new press - if(usage >= 0x39){ - if((c = hid_special(usage, mods)) == 0) - continue; - } else { - c = (mods & HID_MOD_SHIFT) ? hid_shifted[usage] : hid_unshifted[usage]; - if(c == 0) - continue; - } + c = (mods & HID_MOD_SHIFT) ? hid_shifted[usage] : hid_unshifted[usage]; + if(c == 0) + continue; // Ctrl folds a letter down into its control character, which is how ^C // and ^D reach the line discipline at all. - if((mods & HID_MOD_CTRL) && c < KBD_SPECIAL){ + if(mods & HID_MOD_CTRL){ if(c >= 'a' && c <= 'z') c = c - 'a' + 1; else if(c >= 'A' && c <= 'Z') @@ -281,8 +242,6 @@ keyboard_report(struct hid *h, const uchar *rep, int len) continue; } kq_put(c); - if(c >= KBD_VT_SWITCH && c < KBD_VT_SWITCH + NVT) - continue; // a held chord switches once // The newest key is the one that repeats, as on any keyboard. acquire(&kqlock); @@ -349,10 +308,6 @@ take_field(struct hid *h, uint32 usage, int bit, int size, int lmin, int lmax, struct hfield *f = 0; int page = usage >> 16, u = usage & 0xFFFF; - if(page == PAGE_DESKTOP && u == U_X && rel && h->rel_id1 == 0) - h->rel_id1 = id + 1; - if(h->only_id1 && id + 1 != h->only_id1) - return; if(page == PAGE_DESKTOP && u == U_X) f = &h->x; else if(page == PAGE_DESKTOP && u == U_Y) f = &h->y; else if(page == PAGE_DESKTOP && u == U_WHEEL) f = &h->wheel; @@ -640,8 +595,12 @@ pointer_report(struct hid *h, const uchar *rep, int len) // --------------------------------------------------------------------------- static void -hid_input(struct hid *h, const uchar *buf, int len) +hid_complete(struct usb_dev *dev, void *buf, int len) { + struct hid *h = dev->drvdata; + + if(h == 0) + return; // One interface can be both: a wireless receiver describes its keyboard // and its mouse as two report IDs of one descriptor. if(h->rkbd) @@ -652,70 +611,6 @@ hid_input(struct hid *h, const uchar *buf, int len) pointer_report(h, buf, len); } -static void -hid_complete(struct usb_dev *dev, void *buf, int len) -{ - struct hid *h = dev->drvdata; - - if(h != 0) - hid_input(h, buf, len); -} - -static struct hid * -hid_slot(void) -{ - int i; - - if(!kqinit){ - initlock(&kqlock, "usbkbd"); - kqinit = 1; - } - for(i = 0; i < NHID; i++) - if(!hids[i].in_use){ - memset(&hids[i], 0, sizeof(hids[i])); - return &hids[i]; - } - return 0; -} - -// A HID device on some other transport -- I2C, for a laptop's touchpad -- -// handing over its report descriptor. Keyboards and pointers come out of it -// exactly as from USB. A descriptor with a relative pointer is driven -// through that report alone, even if an absolute one comes first: that is a -// touchpad's mouse report, which it sends until told to report fingers. -void * -hid_attach(const uchar *desc, int n, const char *what) -{ - struct hid *h = hid_slot(); - int rel; - - if(h == 0) - return 0; - if(parse_report(h, desc, n) < 0) - return 0; - rel = h->rel_id1; - if(h->x.ok && !h->x.rel && rel){ - memset(h, 0, sizeof(*h)); - h->only_id1 = rel; - if(parse_report(h, desc, n) < 0) - return 0; - } - h->proto = !h->x.ok ? USB_HID_PROTO_KEYBOARD : - h->x.rel ? USB_HID_PROTO_MOUSE : USB_HID_PROTO_TABLET; - h->in_use = 1; - cprintf("%s: %s%s\n", what, - h->proto == USB_HID_PROTO_KEYBOARD ? "keyboard" : - h->proto == USB_HID_PROTO_MOUSE ? "pointer" : "absolute pointer", - h->rkbd && h->x.ok ? " and keyboard" : ""); - return h; -} - -void -hid_attached_input(void *h, const uchar *rep, int len) -{ - hid_input(h, rep, len); -} - static int hid_request(struct usb_dev *dev, int request, int value) { @@ -791,10 +686,18 @@ hid_probe(struct usb_dev *dev) cprintf("usb-hid: no interrupt IN endpoint; nothing to read\n"); return -1; } - if((h = hid_slot()) == 0){ + if(!kqinit){ + initlock(&kqlock, "usbkbd"); + kqinit = 1; + } + + for(i = 0; i < NHID; i++) + if(!hids[i].in_use){ h = &hids[i]; break; } + if(h == 0){ cprintf("usb-hid: %d devices already; this one is not used\n", NHID); return -1; } + memset(h, 0, sizeof(*h)); if(boot && proto == USB_HID_PROTO_KEYBOARD){ // Ask for the fixed layout explicitly. A device that supports the boot diff --git a/kernel/fs/devfs.c b/kernel/fs/devfs.c index 43c27c9..befd9a3 100644 --- a/kernel/fs/devfs.c +++ b/kernel/fs/devfs.c @@ -83,11 +83,6 @@ static const struct devnode devnodes[] = { was one terminal. Minor 10 resolves through proc->ctty; see vt_for_minor() in kernel/core/console.c. */ { "tty", 1, 10, D_ROOT }, - /* The terminal on screen, as on Linux. Xorg opens it to find a free VT and - switch to it, and without one ran on no VT at all: the console went on - drawing into the framebuffer over the X screen. Any minor that is not a - numbered terminal or the controlling one resolves to vt_current(). */ - { "tty0", 1, 0, D_ROOT }, { "tty1", 1, 11, D_ROOT }, { "tty2", 1, 12, D_ROOT }, { "tty3", 1, 13, D_ROOT }, diff --git a/kernel/fs/exec.c b/kernel/fs/exec.c index 98edb15..2744fb8 100644 --- a/kernel/fs/exec.c +++ b/kernel/fs/exec.c @@ -574,15 +574,11 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev) // first delivered signal jump to a stale address in the new image. // mmap_next likewise belongs to the old address space; left alone it // could hand out mmap ranges overlapping the new image's layout. - // Ignored signals stay ignored, and the mask is inherited (execve(2)): - // neither points into the old image. `nohup` and every program that ignores - // SIGPIPE before running a helper depend on it. memset(myproc()->vma, 0, sizeof(myproc()->vma)); - for (int i = 0; i < 32; i++) - if (myproc()->sig_handler[i] != 1) - myproc()->sig_handler[i] = 0; + memset(myproc()->sig_handler, 0, sizeof(myproc()->sig_handler)); memset(myproc()->sig_restorer, 0, sizeof(myproc()->sig_restorer)); myproc()->fs_base = 0; + myproc()->signal_mask = 0; myproc()->pending_signals = 0; myproc()->in_signal = 0; myproc()->sigsusp_active = 0; @@ -619,11 +615,11 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev) myproc()->tf->rcx = entry; myproc()->tf->rsp = sp; - // syscall_entry saves user RSP at kstack+KSTACKSIZE-16 (not in the trapframe). + // syscall_entry saves user RSP at kstack+PGSIZE-16 (not in the trapframe). // sysretq reads RSP from that slot, so update it to the new stack pointer. // Without this, sysretq returns with the old parent's RSP, causing lksh to // start with an invalid/wrong stack pointer. - *(uint64*)((char*)myproc()->kstack + KSTACKSIZE - 16) = sp; + *(uint64*)((char*)myproc()->kstack + PGSIZE - 16) = sp; // Userspace is running now, so the splash has done its job and the display // becomes a terminal. Only the first exec does anything; every later one diff --git a/kernel/fs/file.c b/kernel/fs/file.c index 382da87..a696823 100644 --- a/kernel/fs/file.c +++ b/kernel/fs/file.c @@ -24,7 +24,6 @@ struct { struct file *free; struct kmem_cache *more; uint n; - uint64 serial; // the last ino handed out } ftable; void @@ -64,8 +63,6 @@ filealloc(void) ftable.n++; } f->ref = 1; - f->ino = ++ftable.serial; - f->path = 0; FSSTAT_TAKE(fsstat.file_used, fsstat.file_peak); // 0 -> 1 // Cleared here rather than left to the caller: every creator sets // readable/writable/off, but only open() sets nonblock, so a struct file @@ -104,16 +101,12 @@ fileclose(struct file *f) lock_close_file(f); ff = *f; f->ref = 0; - f->path = 0; f->type = FD_NONE; f->fnext = ftable.free; ftable.free = f; FSSTAT_DROP(fsstat.file_used); // 1 -> 0 release(&ftable.lock); - if(ff.path) - kmfree(ff.path); - if(ff.type == FD_MEMFD) shm_detach(ff.memfd_seg); // the last mapper frees the pages else if(ff.type == FD_EVENT) @@ -136,42 +129,6 @@ fileclose(struct file *f) } } -// What /proc//fd/N points at, in Linux's spelling: the path, or -// "socket:[ino]", "pipe:[ino]", "anon_inode:[eventpoll]". Returns the length. -// ftable.lock is what keeps `path` alive: fileclose() takes it from the file -// under the lock and frees it after. -int -file_describe(struct file *f, char *buf, int cap) -{ - const char *pre = 0, *s = 0; - int n = 0; - - acquire(&ftable.lock); - if(f->ref > 0){ - switch(f->type){ - case FD_INODE: s = f->path; if(!s) pre = "inode:["; break; - case FD_SOCKET: pre = "socket:["; break; - case FD_PIPE: pre = "pipe:["; break; - case FD_MEMFD: s = "/memfd: (deleted)"; break; - case FD_EVENT: s = ev_kind_name(f->ev); break; - default: break; - } - } - if(pre){ - char num[24]; - int k = 0; - uint64 v = f->type == FD_INODE && f->ip ? f->ip->inum : f->ino; - do { num[k++] = '0' + v % 10; v /= 10; } while(v); - while(*pre && n < cap) buf[n++] = *pre++; - while(k && n < cap) buf[n++] = num[--k]; - if(n < cap) buf[n++] = ']'; - } else if(s){ - while(*s && n < cap) buf[n++] = *s++; - } - release(&ftable.lock); - return n; -} - int filestat(struct file *f, struct stat *st) { diff --git a/kernel/fs/pipe.c b/kernel/fs/pipe.c index 62c5189..f5c67a8 100644 --- a/kernel/fs/pipe.c +++ b/kernel/fs/pipe.c @@ -11,40 +11,24 @@ #include "uapi/posix.h" #include "uapi/errno.h" -// 64 KB, as on Linux, in pages taken the first time each is written. -// -// It was 512 bytes in the struct. That is xv6's size, and it made every pipe a -// handoff per half kilobyte: busybox's wget does TLS in a helper process that -// talks to it over pipes, a TLS record is 16 KB, and an HTTPS download that -// should have taken nine seconds was still going after a minute and a half. A -// shell pipeline that only ever moves a line still costs one page. -#define PIPESIZE 65536 -#define PIPE_PAGES (PIPESIZE / PGSIZE) -#define PIPE_BUF 4096 // writes up to this size are not interleaved +#define PIPESIZE 512 struct pipe { struct spinlock lock; - char *page[PIPE_PAGES]; + char data[PIPESIZE]; uint nread; // number of bytes read uint nwrite; // number of bytes written int readopen; // read fd is still open int writeopen; // write fd is still open }; -// The struct is small and comes from a cache; the buffer is pages. The cache -// is created on first use so a kernel that never opens a pipe holds no page -// for one. +// A pipe is about 560 bytes and used to come from kalloc(), which is a whole +// 4096-byte page -- so every `|` in a shell command burned three and a half +// kilobytes it never touched. This is what kmem_cache exists for, and the +// cache is created on first use rather than at boot so a kernel that never +// opens a pipe holds no page for one. static struct kmem_cache *pipe_cache; -static void -pipe_free(struct pipe *p) -{ - for(int i = 0; i < PIPE_PAGES; i++) - if(p->page[i]) - kfree(p->page[i]); - kmem_cache_free(pipe_cache, p); -} - int pipealloc(struct file **f0, struct file **f1) { @@ -58,7 +42,6 @@ pipealloc(struct file **f0, struct file **f1) goto bad; if((p = kmem_cache_alloc(pipe_cache)) == 0) goto bad; - memset(p->page, 0, sizeof(p->page)); p->readopen = 1; p->writeopen = 1; p->nwrite = 0; @@ -72,7 +55,6 @@ pipealloc(struct file **f0, struct file **f1) (*f1)->readable = 0; (*f1)->writable = 1; (*f1)->pipe = p; - (*f1)->ino = (*f0)->ino; // one pipe, as /proc//fd names it return 0; //PAGEBREAK: 20 @@ -99,10 +81,9 @@ pipeclose(struct pipe *p, int writable) } if(p->readopen == 0 && p->writeopen == 0){ release(&p->lock); - pipe_free(p); + kmem_cache_free(pipe_cache, p); } else release(&p->lock); - poll_kick(); // a poller sees the hangup now, not at the tick } //PAGEBREAK: 40 @@ -127,12 +108,8 @@ pipewrite(struct pipe *p, char *addr, int n) return -EPIPE; } - // A write of PIPE_BUF or less goes in whole or waits for room, so two - // writers' records never interleave; a larger one goes in as room appears. - uint want_room = (n <= PIPE_BUF) ? (uint)n : 1; - i = 0; - while(i < n){ - while(PIPESIZE - (p->nwrite - p->nread) < want_room){ //DOC: pipewrite-full + for(i = 0; i < n; i++){ + while(p->nwrite == p->nread + PIPESIZE){ //DOC: pipewrite-full if(p->readopen == 0){ release(&p->lock); sigsend(myproc()->pid, SIGPIPE); @@ -143,32 +120,12 @@ pipewrite(struct pipe *p, char *addr, int n) return -1; } wakeup(&p->nread); - release(&p->lock); - poll_kick(); - acquire(&p->lock); - if(PIPESIZE - (p->nwrite - p->nread) >= want_room) - break; sleep(&p->nwrite, &p->lock); //DOC: pipewrite-sleep } - // One run: up to the end of this page, of what is left, of the room. - uint off = p->nwrite % PIPESIZE; - uint run = PGSIZE - off % PGSIZE; - uint room = PIPESIZE - (p->nwrite - p->nread); - if(run > (uint)(n - i)) run = (uint)(n - i); - if(run > room) run = room; - char **pg = &p->page[off / PGSIZE]; - if(*pg == 0 && (*pg = kalloc()) == 0){ - release(&p->lock); - return i > 0 ? i : -ENOMEM; - } - memmove(*pg + off % PGSIZE, addr + i, run); - p->nwrite += run; - i += run; - want_room = 1; + p->data[p->nwrite++ % PIPESIZE] = addr[i]; } wakeup(&p->nread); //DOC: pipewrite-wakeup1 release(&p->lock); - poll_kick(); return n; } @@ -185,19 +142,13 @@ piperead(struct pipe *p, char *addr, int n) } sleep(&p->nread, &p->lock); //DOC: piperead-sleep } - i = 0; - while(i < n && p->nread != p->nwrite){ //DOC: piperead-copy - uint off = p->nread % PIPESIZE; - uint run = PGSIZE - off % PGSIZE; - if(run > (uint)(n - i)) run = (uint)(n - i); - if(run > p->nwrite - p->nread) run = p->nwrite - p->nread; - memmove(addr + i, p->page[off / PGSIZE] + off % PGSIZE, run); - p->nread += run; - i += run; + for(i = 0; i < n; i++){ //DOC: piperead-copy + if(p->nread == p->nwrite) + break; + addr[i] = p->data[p->nread++ % PIPESIZE]; } wakeup(&p->nwrite); //DOC: piperead-wakeup release(&p->lock); - poll_kick(); // a writer polling for room return i; } @@ -217,8 +168,8 @@ pipe_poll(struct pipe *p, int readable, int writable) if(writable){ if(p->readopen == 0) rev |= POLLERR; - else if(PIPESIZE - (p->nwrite - p->nread) >= PIPE_BUF) - rev |= POLLOUT; // room for a whole PIPE_BUF write, as on Linux + else if(p->nwrite < p->nread + PIPESIZE) + rev |= POLLOUT; } release(&p->lock); return rev; diff --git a/kernel/fs/procfs.c b/kernel/fs/procfs.c index 069d9a4..584a226 100644 --- a/kernel/fs/procfs.c +++ b/kernel/fs/procfs.c @@ -43,22 +43,18 @@ // identity of the object. // // 1 the root, /proc -// 2 /proc/self // PROCFS_GLOBAL | id a global file, id being the PROC_* minor that // kernel/dev/procfs.c already uses // pid << 8 the directory /proc/ // (pid << 8) | fileid a file inside it -// (pid << 8) | 0x80 | fd /proc//fd/ // -// File ids stay below 0x80 and fds below 128 (NOFILE). pids are not bounded -// by NPROC -- they count up -- so the global bit sits far above any pid << 8; -// at 0x10000 it was /proc/256. Shifting by 8 rather than packing tightly keeps -// the numbers readable in a debugger, which matters more than density here. +// pid is bounded by NPROC and file ids by PF_MAX, so the ranges cannot +// collide. Shifting by 8 rather than packing tightly keeps the numbers +// readable in a debugger, which matters more than density here. // --------------------------------------------------------------------------- #define PROCFS_ROOT 1ULL -#define PROCFS_SELF 2ULL -#define PROCFS_GLOBAL (1ULL << 40) +#define PROCFS_GLOBAL 0x10000ULL #define PF_DIR 0 // the directory itself #define PF_STAT 1 @@ -67,14 +63,10 @@ #define PF_STATM 4 #define PF_EXE 5 #define PF_SCHEDSTAT 6 -#define PF_FD 7 // the directory /proc//fd -#define PF_MAX 7 -#define PF_FDENT 0x80 // | fd: one link in it +#define PF_MAX 6 #define INO_PID(ino) ((int)((ino) >> 8)) #define INO_FILE(ino) ((int)((ino) & 0xFF)) -#define INO_IS_FDENT(ino) (!INO_IS_GLOBAL(ino) && (INO_FILE(ino) & PF_FDENT)) -#define INO_FD(ino) (INO_FILE(ino) & 0x7F) #define INO_IS_GLOBAL(ino) (((ino) & PROCFS_GLOBAL) != 0) // The global files, by the name they appear under and the PROC_* id that @@ -129,9 +121,6 @@ static const struct global_file pidfiles[] = { // inventing a wait time that is not measured would be worse than saying // something true in its place. { "schedstat", PF_SCHEDSTAT }, - // One symlink per open descriptor, named by its number and pointing at the - // path it was opened by, or at "socket:[ino]" and the like. - { "fd", PF_FD }, }; #define NPIDFILES ((int)(sizeof(pidfiles) / sizeof(pidfiles[0]))) @@ -278,15 +267,6 @@ gen_file(uint64 ino, char *buf, int cap) if (INO_IS_GLOBAL(ino)) return proc_gen_global((int)(ino & 0xFF), buf, cap); - // Whoever is asking: the one file whose content depends on the reader. - if (ino == PROCFS_SELF) { - sbputi(&s, myproc()->pid); - return s.n; - } - if (INO_IS_FDENT(ino)) { - int n = proc_fd_link(INO_PID(ino), INO_FD(ino), buf, cap); - return n > 0 ? n : 0; - } if (proc_snapshot_pid(INO_PID(ino), &pi) < 0) return 0; // it exited between lookup and read @@ -330,8 +310,7 @@ procfs_iload(struct inode *ip) ip->showmode = 1; ip->atime = ip->mtime = ip->ctime = 0; - if (ino == PROCFS_ROOT || - (!INO_IS_GLOBAL(ino) && (INO_FILE(ino) == PF_DIR || INO_FILE(ino) == PF_FD))) { + if (ino == PROCFS_ROOT || (!INO_IS_GLOBAL(ino) && INO_FILE(ino) == PF_DIR)) { ip->type = T_DIR; ip->size = 0; ip->nlink = 2; @@ -350,12 +329,8 @@ procfs_iload(struct inode *ip) // symlink whose size is 0 resolves to the empty string -- which is worse // than the regular file was, because it looks like it worked. Generating it // here costs one small render per stat() of one file. - // /proc/self and /proc//fd/N are links too. Their targets can change - // under a cached inode, which is why readlink() does not trust this size - // for procfs. - if (ino == PROCFS_SELF || INO_IS_FDENT(ino) || - (!INO_IS_GLOBAL(ino) && INO_FILE(ino) == PF_EXE)) { - char path[256]; + if (!INO_IS_GLOBAL(ino) && INO_FILE(ino) == PF_EXE) { + char path[64]; int len = gen_file(ino, path, sizeof(path)); ip->type = T_SYMLINK; ip->size = len > 0 ? (uint64)len : 0; @@ -494,19 +469,8 @@ procfs_dirlookup(struct inode *dp, char *name, uint *poff) if (namecmp(name, ".") == 0) return iget(dp->dev, dp->inum); - if (namecmp(name, "..") == 0) // /proc//.. is /proc - return iget(dp->dev, INO_FILE(dp->inum) == PF_FD ? (dp->inum & ~0xFFULL) - : PROCFS_ROOT); - - // Inside /proc//fd/: a number that is an open descriptor. - if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_FD) { - char tmp[4]; - int fd = name_to_pid(name); - if (fd < 0 || fd >= NOFILE || fd >= 128 || - proc_fd_link(INO_PID(dp->inum), fd, tmp, sizeof(tmp)) < 0) - return 0; - return iget(dp->dev, (dp->inum & ~0xFFULL) | PF_FDENT | fd); - } + if (namecmp(name, "..") == 0) + return iget(dp->dev, PROCFS_ROOT); // /proc//.. is /proc // Inside /proc// if (dp->inum != PROCFS_ROOT) { @@ -516,9 +480,6 @@ procfs_dirlookup(struct inode *dp, char *name, uint *poff) return 0; } - if (namecmp(name, "self") == 0) - return iget(dp->dev, PROCFS_SELF); - // In /proc itself: a number is a process, a word is a global file. pid = name_to_pid(name); if (pid > 0) { @@ -555,7 +516,6 @@ struct linux_dirent64 { #define DT_DIR_ 4 #define DT_REG_ 8 -#define DT_LNK_ 10 static int emit(char *out, int cap, int used, uint64 ino, int type, const char *name) @@ -614,30 +574,17 @@ procfs_getdents(struct inode *dp, char *out, int cap, uint64 *off) if (dp->inum == PROCFS_ROOT) { EMIT(PROCFS_ROOT, DT_DIR_, "."); EMIT(PROCFS_ROOT, DT_DIR_, ".."); - EMIT(PROCFS_SELF, DT_LNK_, "self"); for (i = 0; i < NGLOBALS; i++) EMIT(PROCFS_GLOBAL | globals[i].id, DT_REG_, globals[i].name); for (i = 0; proc_snapshot(i, &pi) == 0; i++) { utoa((uint)pi.pid, name); EMIT(((uint64)pi.pid << 8) | PF_DIR, DT_DIR_, name); } - } else if (INO_FILE(dp->inum) == PF_FD) { - uint64 mask[2]; - EMIT(dp->inum, DT_DIR_, "."); - EMIT(dp->inum & ~0xFFULL, DT_DIR_, ".."); - if (proc_fd_mask(INO_PID(dp->inum), mask) == 0) - for (i = 0; i < 128; i++) - if (mask[i / 64] & (1ULL << (i % 64))) { - utoa((uint)i, name); - EMIT((dp->inum & ~0xFFULL) | PF_FDENT | i, DT_LNK_, name); - } } else { EMIT(dp->inum, DT_DIR_, "."); EMIT(PROCFS_ROOT, DT_DIR_, ".."); for (i = 0; i < NPIDFILES; i++) - EMIT((dp->inum & ~0xFFULL) | pidfiles[i].id, - pidfiles[i].id == PF_FD ? DT_DIR_ : pidfiles[i].id == PF_EXE ? DT_LNK_ : DT_REG_, - pidfiles[i].name); + EMIT((dp->inum & ~0xFFULL) | pidfiles[i].id, DT_REG_, pidfiles[i].name); } full: diff --git a/kernel/fs/schfs/kmount.c b/kernel/fs/schfs/kmount.c index cbee97d..a16528e 100644 --- a/kernel/fs/schfs/kmount.c +++ b/kernel/fs/schfs/kmount.c @@ -641,9 +641,6 @@ kmount_slot(int slot, uint bdev) // Opening with a page allocator also builds the allocation bitmap, by walking // the tree — the only mount-time cost SchFS has, and what buys it a mount // that cannot disagree with the filesystem it just adopted. - // The live root fills its ramdisk, however much memory that turned out to - // be. See kernel/dev/ramdisk.c. - m->vol.grow_to = (bdev == RAMDISK_DEV) ? ramdisk_blocks() : 0; int r = schfs_vol_open(&m->vol, &bd, &cx, kd_palloc, kd_pfree); if (r == -1) return -EINVAL; /* not a SchFS volume at all */ diff --git a/kernel/fs/schfs/schfs_int.h b/kernel/fs/schfs/schfs_int.h index 991bcfc..0a0d302 100644 --- a/kernel/fs/schfs/schfs_int.h +++ b/kernel/fs/schfs/schfs_int.h @@ -104,13 +104,6 @@ struct schfs_vol { uint64_t total_blocks; uint64_t free_blocks; - // Set by the opener before schfs_vol_open: the device's size, when the - // volume should grow to fill it. Nothing on disk records which blocks are - // free -- open rebuilds that from the tree -- so growing is only a larger - // total before the map is built, and the next commit writes it down. The - // live root uses it to fill a ramdisk sized from memory at boot. 0, and - // anything not larger than the volume, leaves it alone. - uint64_t grow_to; uint64_t next_objectid; uint64_t root_objectid; uint64_t generation; // last *committed* transaction group diff --git a/kernel/fs/schfs/vol.c b/kernel/fs/schfs/vol.c index 4d3e1e2..b7b2254 100644 --- a/kernel/fs/schfs/vol.c +++ b/kernel/fs/schfs/vol.c @@ -215,8 +215,6 @@ schfs_vol_open(struct schfs_vol *v, struct schfs_bdev *dev, } vol_from_uber(v, u, best_slot); - if (v->grow_to > v->total_blocks) - v->total_blocks = v->grow_to; v->readonly = 1; // until an allocation map exists, below if (v->dev.bread(v->dev.ctx, v->tree.root, x->a) != 0) diff --git a/kernel/net/inet.c b/kernel/net/inet.c index 0e43fd9..47426ab 100644 --- a/kernel/net/inet.c +++ b/kernel/net/inet.c @@ -94,7 +94,6 @@ struct inet_sock { int inuse; int type; // SOCK_STREAM or SOCK_DGRAM int proto; // 0, or IPPROTO_ICMP for a ping socket - int iphdr; // SOCK_RAW: reads start at the IP header struct tcp_pcb *tpcb; struct udp_pcb *upcb; @@ -115,7 +114,6 @@ struct inet_sock { int connected; int conn_failed; int peer_closed; // FIN seen, or the connection died - int wr_shut; // shutdown(SHUT_WR): our FIN is sent int listening; // Connections lwIP has completed but nobody has accept()ed yet. @@ -151,36 +149,13 @@ net_seq(void) return s; } -// A blocked socket call gives up for a kill and for any signal it can take. -// The second used to be missing, so a signal woke the sleeper and the loop -// below put it straight back: busybox's ping sends each request from a -// SIGALRM handler while it sits in recvfrom, and got one reply and then -// silence. There is no SA_RESTART, so the call returns EINTR and the caller -// asks again, which is what every one of them is written to do. -// -// Only signals with a handler count, as on Linux. A SIGCHLD left at its -// default is posted to a parent all the same, and ending a server's accept() -// for it would be an EINTR that program had never been handed before. -static int -net_interrupted(void) -{ - struct proc *p = myproc(); - if (p->killed) - return 1; - uint64 live = p->pending_signals & ~p->signal_mask; - for (int sig = 1; sig < 32; sig++) - if ((live & (1ULL << sig)) && p->sig_handler[sig] > 1) // not DFL/IGN - return 1; - return 0; -} - -// Sleep until the count moves past `seen`, or a signal arrives. kill() makes -// a sleeping process runnable, so the loop re-checks and leaves. +// Sleep until the count moves past `seen`, or this process is killed. kill() +// makes a sleeping process runnable, so the loop re-checks and leaves. static void net_wait(uint seen) { acquire(&netwait_lock); - while (netwait_seq == seen && !net_interrupted()) + while (netwait_seq == seen && !myproc()->killed) sleep(&netwait_seq, &netwait_lock); release(&netwait_lock); } @@ -195,7 +170,6 @@ net_wake(void) netwait_seq++; release(&netwait_lock); wakeup(&netwait_seq); - poll_kick(); // and poll/select/epoll waiters, not at the tick } /* ------------------------------------------------------------------ */ @@ -464,11 +438,6 @@ inet_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, // processes ping at once; with one ping at a time the difference is invisible, // and a socket that sees a reply to somebody else's request will read a // sequence number it never sent and ignore it. -// -// A SOCK_RAW ICMP socket is the same socket, except that a read starts at the -// IP header: that is the raw-socket contract, and busybox's ping reads the TTL -// out of it and finds the ICMP message by IHL. Writing is the same for both -- -// without IP_HDRINCL a raw socket's caller sends the ICMP message alone too. static u8_t inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) @@ -493,8 +462,7 @@ inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, if (pbuf_copy_partial(p, &type, 1, hlen) != 1 || type != 0) return 0; - u16_t skip = is->iphdr ? 0 : hlen; - u16_t icmplen = (u16_t)(p->tot_len - skip); + u16_t icmplen = (u16_t)(p->tot_len - hlen); if (icmplen <= rx_space(is) && dq_used(is) < NDGRAM) { struct dgram *d = &is->dq[is->dhead % NDGRAM]; @@ -503,9 +471,9 @@ inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, d->len = icmplen; is->dhead++; - // Copy the message out, skipping the IP header unless this is a raw - // socket, across whatever pbuf chain it arrived in. - u16_t off = skip; + // Copy the ICMP message out, skipping the IP header, across whatever pbuf + // chain it arrived in. + u16_t off = hlen; while (off < p->tot_len) { char tmp[64]; u16_t want = (u16_t)(p->tot_len - off); @@ -547,15 +515,8 @@ inet_alloc(int type) void * inet_new(int type, int proto) { - if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW) + if (type != SOCK_STREAM && type != SOCK_DGRAM) return 0; - if (type == SOCK_RAW && proto != IPPROTO_ICMP) - return 0; - // Past this point a raw ICMP socket is a ping socket with one flag: every - // datagram path below already does the right thing for it. - int iphdr = (type == SOCK_RAW); - if (iphdr) - type = SOCK_DGRAM; // Ready, not *up*: see net_is_ready() in kernel/net/net.c. Requiring an // address here made every socket() fail until DHCP answered. if (!net_is_ready()) @@ -568,7 +529,6 @@ inet_new(int type, int proto) return 0; } is->proto = proto; - is->iphdr = iphdr; if (proto == IPPROTO_ICMP) { // A ping socket. raw_new registers for one IP protocol number; the @@ -728,7 +688,7 @@ inet_accept(void *h) uint seen = net_seq(); lwip_sys_unlock(); - if (net_interrupted()) + if (myproc()->killed) return 0; net_wait(seen); } @@ -769,7 +729,7 @@ inet_connect(void *h, uint32 addr, uint16 port) return 0; if (failed) return -ECONNREFUSED; - if (net_interrupted()) + if (myproc()->killed) return -EINTR; net_wait(seen); } @@ -782,47 +742,6 @@ inet_connect(void *h, uint32 addr, uint16 port) // peer it is connected to and is the same for every byte; for a datagram // socket it belongs to the individual datagram, which is why UDP needs the // queue above and TCP does not. -// getsockname / getpeername: this end's address or the other's, the address -// in network order and the port in host order, as inet_recvfrom gives them. A -// socket not yet bound reads as 0.0.0.0:0 locally, which is what Linux says; -// one with no peer is ENOTCONN. A listening pcb has no remote fields at all -// -- lwIP's listen pcb is the smaller struct -- so it is never asked for them. -int -inet_name(void *h, int peer, uint *addr, ushort *port) -{ - struct inet_sock *is = h; - int r = 0; - - *addr = 0; - *port = 0; - lwip_sys_lock(); - if (is->tpcb) { - if (peer && (is->listening || is->tpcb->state < SYN_SENT)) { - r = -ENOTCONN; - } else if (peer) { - *addr = ip4_addr_get_u32(ip_2_ip4(&is->tpcb->remote_ip)); - *port = is->tpcb->remote_port; - } else { - *addr = ip4_addr_get_u32(ip_2_ip4(&is->tpcb->local_ip)); - *port = is->tpcb->local_port; - } - } else if (is->upcb) { - if (peer && !(is->upcb->flags & UDP_FLAGS_CONNECTED)) { - r = -ENOTCONN; - } else if (peer) { - *addr = ip4_addr_get_u32(ip_2_ip4(&is->upcb->remote_ip)); - *port = is->upcb->remote_port; - } else { - *addr = ip4_addr_get_u32(ip_2_ip4(&is->upcb->local_ip)); - *port = is->upcb->local_port; - } - } else if (peer) { - r = -ENOTCONN; // a ping socket has no peer - } - lwip_sys_unlock(); - return r; -} - int inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port) { @@ -868,7 +787,7 @@ inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port) return got; if (eof) return 0; // end of file, not an error - if (net_interrupted()) + if (myproc()->killed) return -EINTR; net_wait(seen); } @@ -960,7 +879,7 @@ inet_sendto(void *h, char *buf, int n, uint addr, ushort port) uint seen; lwip_sys_lock(); - if (is->peer_closed || is->wr_shut || is->tpcb == 0) { + if (is->peer_closed || is->tpcb == 0) { lwip_sys_unlock(); // Same contract as a pipe with no reader, which is what the AF_UNIX side // does: the signal first, then the error. @@ -990,7 +909,7 @@ inet_sendto(void *h, char *buf, int n, uint addr, ushort port) lwip_sys_unlock(); if (done < n) { - if (net_interrupted()) + if (myproc()->killed) return done > 0 ? done : -EINTR; // Woken by inet_sent_cb when an acknowledgement frees send buffer, or by // a failure. Without tcp_sent registered this would be a sleep with @@ -1007,32 +926,6 @@ inet_write(void *h, char *buf, int n) return inet_sendto(h, buf, n, 0, 0); } -// shutdown(2) on a TCP socket. SHUT_WR sends our FIN and the peer can keep -// sending; SHUT_RD is local, reads see EOF. The pcb is never freed here -- -// tcp_shutdown(pcb, 1, 1) would free it -- so close() still owns it. -int -inet_shutdown(void *h, int how) -{ - struct inet_sock *is = h; - - if (is->type != SOCK_STREAM) - return 0; - lwip_sys_lock(); - if (is->tpcb == 0 || is->listening || !is->connected) { - lwip_sys_unlock(); - return -ENOTCONN; - } - if ((how == SHUT_WR || how == SHUT_RDWR) && !is->wr_shut) { - is->wr_shut = 1; - tcp_shutdown(is->tpcb, 0, 1); - } - if (how == SHUT_RD || how == SHUT_RDWR) - is->peer_closed = 1; - lwip_sys_unlock(); - net_wake(); - return 0; -} - int inet_readable(void *h) { diff --git a/kernel/net/unix.c b/kernel/net/unix.c index fca12e9..335aa66 100644 --- a/kernel/net/unix.c +++ b/kernel/net/unix.c @@ -230,7 +230,6 @@ sockclose(struct socket *s) s->ref = 0; wakeup(&s->nqueue); // an accept() blocked on this listener release(&stab.lock); - poll_kick(); // the peer's poll() sees the hangup now } void @@ -460,7 +459,6 @@ sockread_fds(struct socket *s, char *addr, int n, struct file **fout, int *nfout // on a full buffer that this call has just made room in. wakeup(&s->nwrite); release(&stab.lock); - poll_kick(); // a writer polling for room // Outside the lock, because fileclose can sleep. if(fout){ @@ -522,7 +520,7 @@ sockwrite_fds(struct socket *s, char *addr, int n, struct file **f, int nf) // look for it. Same contract as a pipe with no reader: a signal, then an // error. A process that writes to a dead connection and is not told about // it spins forever believing it is making progress. - if(s->peer == 0 || s->wr_shut){ + if(s->peer == 0){ release(&stab.lock); sigsend(myproc()->pid, SIGPIPE); return -EPIPE; @@ -571,9 +569,6 @@ sockwrite_fds(struct socket *s, char *addr, int n, struct file **f, int nf) wakeup(&s->peer->nread); release(&stab.lock); - // poll() and epoll waiters, now rather than at the next tick: an X client - // and its server talk over these, a request and a reply at a time. - poll_kick(); return i; } @@ -693,61 +688,6 @@ sockbind(struct socket *s, const char *path, uint dev, uint64 ino) return 0; } -// The abstract namespace: a sun_path that starts with a NUL names no file. -// Linux keeps these names in the socket layer, and Xorg binds one before its -// /tmp/.X11-unix path -- which this used to read as the empty path, resolve -// to the directory it was called from and refuse with EADDRINUSE, so Xorg -// reported "server already running" and exited. -// -// The name becomes a key on a device number no filesystem has, and the rest -// is the inode machinery unchanged: connect() finds the listener bound to -// (SOCK_ABSTRACT_DEV, key). The name goes when the socket does, because -// nothing else holds it. -int -sockbind_abstract(struct socket *s, uint64 key) -{ - acquire(&stab.lock); - if(s->state != SS_UNBOUND){ - release(&stab.lock); - return -EINVAL; - } - for(struct socket *o = stab.sock; o < stab.sock + NSOCK; o++) - if(o != s && o->state != SS_FREE && o->state != SS_UNBOUND && - o->bind_dev == SOCK_ABSTRACT_DEV && o->bind_ino == key){ - release(&stab.lock); - return -EADDRINUSE; - } - s->name[0] = '\0'; // getsockname reports it unnamed, not as a path - s->bind_dev = SOCK_ABSTRACT_DEV; - s->bind_ino = key; - s->state = SS_BOUND; - release(&stab.lock); - return 0; -} - -// The path for getsockname (peer 0) or getpeername (peer 1) of an AF_UNIX -// socket. Empty for an unnamed one -- a client that never bound, or an -// abstract name, which the caller reports as the family alone. -int -sockname_inet(struct socket *s, int peer, uint *addr, ushort *port) -{ - return inet_name(s->inet, peer, addr, port); -} - -int -sockname_unix(struct socket *s, int peer, char *path) -{ - acquire(&stab.lock); - struct socket *o = peer ? s->peer : s; - if(o == 0){ - release(&stab.lock); - return -ENOTCONN; - } - safestrcpy(path, o->name, UNIX_PATH_MAX); - release(&stab.lock); - return 0; -} - int socklisten(struct socket *s, int backlog) { @@ -859,7 +799,6 @@ sockconnect(struct socket *s, uint dev, uint64 ino) wakeup(&srv->nqueue); // an accept() may be blocked on this release(&stab.lock); - poll_kick(); // or a listener in poll(), as Xorg's is // Deliberately does *not* wait for accept(). On Unix a queued connection is // already established: the client may write, and the bytes sit in the @@ -878,9 +817,6 @@ sockconnect(struct socket *s, uint dev, uint64 ino) int sock_shutdown(struct socket *s, int how) { - if(s->domain == AF_INET) - return inet_shutdown(s->inet, how); - acquire(&stab.lock); if(s->state != SS_CONNECTED){ release(&stab.lock); @@ -891,20 +827,9 @@ sock_shutdown(struct socket *s, int how) s->peer_closed = 1; wakeup(&s->nread); } - if(how == SHUT_RDWR) + if(how == SHUT_WR || how == SHUT_RDWR) orphan_peer(s); - else if(how == SHUT_WR){ - // Half-close: the peer sees EOF once it has read what is there, and the - // link stays, so its reply still reaches us. Cutting the link here made - // the reply a SIGPIPE -- busybox wget does exactly this to ssl_client. - s->wr_shut = 1; - if(s->peer){ - s->peer->peer_closed = 1; - wakeup(&s->peer->nread); - } - } release(&stab.lock); - poll_kick(); return 0; } diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c index bde72f0..f132828 100644 --- a/kernel/proc/proc.c +++ b/kernel/proc/proc.c @@ -311,7 +311,6 @@ found: memset(p->sig_handler, 0, sizeof(p->sig_handler)); memset(p->sig_restorer, 0, sizeof(p->sig_restorer)); p->pending_signals = 0; - p->alarm_at = p->alarm_every = 0; // a child's timers start disarmed p->utime = p->stime = p->cutime = p->cstime = 0; // A recycled slot must not inherit a dead process's place in the queue -- @@ -1544,97 +1543,6 @@ sig_kills_now(struct proc *p, int sig) return p->sig_handler[sig] == 0 && sig_default_kills(sig); } -// SIGALRM, on time. -// -// alarm() and setitimer() stored a countdown that nothing ever counted down, -// so no timer anyone set has ever fired. Most programs never noticed, because -// what they used it for was a timeout; busybox's ping sends every request but -// the first from its SIGALRM handler, and on the Alpine root it is /bin/ping, -// so ping printed one reply and then nothing. -// -// A kernel thread rather than the timer interrupt, which is in trap.c, and -// which would have to walk the process table with interrupts off: this walks -// it once a tick, and only while some process has a timer armed. -static int alarms_armed; // processes with alarm_at set; a hint, not a lock - -void -alarm_arm(struct proc *p, uint after, uint every) -{ - acquire(&ptable.lock); - if (p->alarm_at == 0 && after) - alarms_armed++; - else if (p->alarm_at && after == 0) - alarms_armed--; - p->alarm_at = after ? ticks + after : 0; - if (p->alarm_at == 0 && after) - p->alarm_at = 1; // 0 means disarmed; a tick late is fine - p->alarm_every = after ? every : 0; - release(&ptable.lock); -} - -// Ticks until p's timer is due, 0 if it is not armed. -uint -alarm_left(struct proc *p) -{ - uint at = p->alarm_at; - if (at == 0) - return 0; - int left = (int)(at - ticks); - return left > 0 ? (uint)left : 1; -} - -static void -alarm_thread(void) -{ - for (;;) { - acquire(&tickslock); - sleep(&ticks, &tickslock); - uint now = ticks; - release(&tickslock); - if (alarms_armed == 0) - continue; - - acquire(&ptable.lock); - for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) { - if (p->alarm_at == 0 || (int)(now - p->alarm_at) < 0) - continue; - if (p->state == UNUSED || p->state == ZOMBIE) { - p->alarm_at = 0; - alarms_armed--; - continue; - } - if (p->alarm_every) { - p->alarm_at = now + p->alarm_every; - } else { - p->alarm_at = 0; - alarms_armed--; - } - // sigsend takes ptable.lock itself, and with this many lines it is - // clearer to repeat its three effects than to split it. - if (!init_ignores(p, SIGALRM)) { - p->pending_signals |= 1ULL << SIGALRM; - if (sig_kills_now(p, SIGALRM)) { - p->killed = 1; - if (p->killsig == 0) - p->killsig = SIGALRM; - } - if (p->state == SLEEPING) { - p->state = RUNNABLE; - sched_enqueue(p); - } - } - } - release(&ptable.lock); - } -} - -void -alarm_thread_start(void) -{ - if (kthread_create(alarm_thread, "alarmd") < 0) - cprintf("alarm: no kernel thread; alarm() and setitimer() will not fire\n"); -} - int sigsend(int pid, int sig) { @@ -1907,50 +1815,6 @@ proc_snapshot_pid(int pid, struct procinfo *out) return -1; } -// /proc//fd: which descriptors are open, and what one points at. The -// ptable lock keeps the process from being reaped -- exit() closes its files -// first -- and file_describe() holds the file steady itself. -int -proc_fd_mask(int pid, uint64 mask[2]) -{ - struct proc *p; - int r = -1; - - mask[0] = mask[1] = 0; - acquire(&ptable.lock); - for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { - if (p->state == UNUSED || p->pid != pid) - continue; - for (int fd = 0; fd < NOFILE && fd < 128; fd++) - if (p->ofile[fd]) - mask[fd / 64] |= 1ULL << (fd % 64); - r = 0; - break; - } - release(&ptable.lock); - return r; -} - -int -proc_fd_link(int pid, int fd, char *buf, int cap) -{ - struct proc *p; - int r = -1; - - if (fd < 0 || fd >= NOFILE) - return -1; - acquire(&ptable.lock); - for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) { - if (p->state == UNUSED || p->pid != pid) - continue; - if (p->ofile[fd]) - r = file_describe(p->ofile[fd], buf, cap); - break; - } - release(&ptable.lock); - return r; -} - int proc_num(void) { struct proc *p; diff --git a/kernel/scff/compat/assert.h b/kernel/scff/compat/assert.h deleted file mode 100644 index bb6e6fd..0000000 --- a/kernel/scff/compat/assert.h +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// zstd builds with DEBUGLEVEL 0, where assert() is never expanded. -#ifndef SCFF_COMPAT_ASSERT_H -#define SCFF_COMPAT_ASSERT_H -#define assert(x) ((void)0) -#endif diff --git a/kernel/scff/compat/stdio.h b/kernel/scff/compat/stdio.h deleted file mode 100644 index 454095e..0000000 --- a/kernel/scff/compat/stdio.h +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Included by zstd's debug printing, which DEBUGLEVEL 0 compiles out. -#ifndef SCFF_COMPAT_STDIO_H -#define SCFF_COMPAT_STDIO_H -#endif diff --git a/kernel/scff/compat/stdlib.h b/kernel/scff/compat/stdlib.h deleted file mode 100644 index be9b1d1..0000000 --- a/kernel/scff/compat/stdlib.h +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// zstd's allocator hooks. The kernel decoder is a static context in a fixed -// workspace (kernel/scff/scff.c), so nothing allocates: these exist only for -// code paths that are compiled in and never taken. -#ifndef SCFF_COMPAT_STDLIB_H -#define SCFF_COMPAT_STDLIB_H -#include -static inline void *malloc(size_t n) { (void)n; return 0; } -static inline void *calloc(size_t n, size_t s) { (void)n; (void)s; return 0; } -static inline void free(void *p) { (void)p; } -#endif diff --git a/kernel/scff/compat/string.h b/kernel/scff/compat/string.h deleted file mode 100644 index e238d0d..0000000 --- a/kernel/scff/compat/string.h +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// What zstd asks of , from the kernel's own (kernel/core/string.c). -// zstd calls __builtin_memcpy and friends where it can, so these are the -// fallbacks for the sizes the compiler does not open-code. -#ifndef SCFF_COMPAT_STRING_H -#define SCFF_COMPAT_STRING_H -#include -void *memcpy(void *, const void *, unsigned int); -void *memmove(void *, const void *, unsigned long); -void *memset(void *, int, unsigned long); -#endif diff --git a/kernel/scff/scff.c b/kernel/scff/scff.c deleted file mode 100644 index 5c2acfc..0000000 --- a/kernel/scff/scff.c +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// The zstd decoder for SCFF chunks (include/fs/scff.h). -// -// One static decompression context in a fixed workspace, so nothing here -// allocates, and one caller at a time: kernel/dev/ramdisk.c holds its chunk -// lock across every call. -// -// **The decoder runs on a stack of its own.** A kernel stack is 8 KB, and a -// chunk is decompressed at the bottom of a read -- syscall, namei, SchFS, -// bread, ramdiskrw -- with most of it already used. zstd's sequence decoder -// keeps several KB of state in locals, which Linux gives it on a 16 KB stack. -// Here that would run off the end of the process's stack into whatever is -// below it. So the call is made on scff_stack, which the chunk lock makes -// this caller's alone. An interrupt taken meanwhile lands on it too, which -// is fine: it is a kernel stack like any other, and a big one. -// -// This file sees zstd's headers and none of the kernel's, which keeps the two -// sets of type definitions from meeting. - -#define ZSTD_STATIC_LINKING_ONLY -#include "zstd.h" - -#include "scff_zstd.h" - -static unsigned char scff_work[256 * 1024] __attribute__((aligned(64))); -static unsigned char scff_stack[64 * 1024] __attribute__((aligned(16))); -static ZSTD_DCtx *dctx; - -// call_on_stack(fn, arg, top): fn(arg) with %rsp at top, then back. -// %rbp keeps the old stack pointer across the call; it is callee-saved. -long call_on_stack(long (*fn)(void *), void *arg, void *top); -__asm__( - ".text\n" - ".globl call_on_stack\n" - "call_on_stack:\n" - " pushq %rbp\n" - " movq %rsp, %rbp\n" - " movq %rdx, %rsp\n" - " movq %rdi, %rax\n" - " movq %rsi, %rdi\n" - " callq *%rax\n" - " movq %rbp, %rsp\n" - " popq %rbp\n" - " retq\n"); - -struct job { - void *dst; - unsigned long cap; - const void *src; - unsigned long n; -}; - -static long -run(void *arg) -{ - struct job *j = arg; - size_t r = ZSTD_decompressDCtx(dctx, j->dst, j->cap, j->src, j->n); - return ZSTD_isError(r) ? -1 : (long)r; -} - -int -scff_zstd_init(void) -{ - if (dctx) - return 0; - if (ZSTD_estimateDCtxSize() > sizeof(scff_work)) - return -1; - dctx = ZSTD_initStaticDCtx(scff_work, sizeof(scff_work)); - return dctx ? 0 : -1; -} - -long -scff_zstd_decompress(void *dst, unsigned long cap, const void *src, unsigned long n) -{ - struct job j = { dst, cap, src, n }; - - if (!dctx) - return -1; - return call_on_stack(run, &j, scff_stack + sizeof(scff_stack)); -} diff --git a/kernel/scff/scff_zstd.h b/kernel/scff/scff_zstd.h deleted file mode 100644 index 8badcf6..0000000 --- a/kernel/scff/scff_zstd.h +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// The SCFF decoder's interface, in types both the kernel's headers and zstd's -// agree on. See kernel/scff/scff.c. -#ifndef SCHOS_SCFF_ZSTD_H -#define SCHOS_SCFF_ZSTD_H - -// 0, or -1 if the workspace is too small for this zstd. -int scff_zstd_init(void); -// Decompress one zstd frame. The length written, or -1 for a bad frame. -// One caller at a time. -long scff_zstd_decompress(void *dst, unsigned long cap, const void *src, unsigned long n); - -#endif diff --git a/kernel/sys/sys_event.c b/kernel/sys/sys_event.c index dd0b0d3..6846177 100644 --- a/kernel/sys/sys_event.c +++ b/kernel/sys/sys_event.c @@ -113,13 +113,19 @@ clock_ns(int clock) return sec * 1000000000ULL + usec * 1000ULL; } -// One tick, a poll_kick, or a signal or kill that makes waiting pointless: -// poll's wait, so data arriving wakes an epoll_wait as it wakes a poll. -// Returns 1 for a tick, 0 for a kick, -EINTR. +// One tick, or until a signal or kill makes waiting pointless. static int ev_block(void) { - return poll_wait_tick(); + struct proc *p = myproc(); + if (p->killed || (p->pending_signals & ~p->signal_mask)) + return -EINTR; + acquire(&tickslock); + uint t0 = ticks; + while (ticks == t0 && !p->killed && !(p->pending_signals & ~p->signal_mask)) + sleep(&ticks, &tickslock); + release(&tickslock); + return 0; } static int @@ -172,19 +178,6 @@ ev_arg(int n, int kind, struct file **pf) return f->ev; } -// The name Linux gives each kind in /proc//fd. -const char * -ev_kind_name(struct evobj *o) -{ - switch (o ? o->kind : 0) { - case EV_EVENTFD: return "anon_inode:[eventfd]"; - case EV_TIMERFD: return "anon_inode:[timerfd]"; - case EV_SIGNALFD: return "anon_inode:[signalfd]"; - case EV_EPOLL: return "anon_inode:[eventpoll]"; - } - return "anon_inode:[event]"; -} - // Called by fileclose() for the last reference. void ev_close(struct evobj *o) @@ -721,12 +714,11 @@ epoll_wait_common(int timeout_ms) got = ep_ready(o, buf, max); if (got > 0 || ticks_left == 0) break; - int w = ev_block(); - if (w < 0) { + if (ev_block() < 0) { got = -EINTR; break; } - if (w > 0 && ticks_left > 0) + if (ticks_left > 0) ticks_left--; } if (got > 0 && copy_to_user(uevents, buf, got * sizeof(*buf)) < 0) diff --git a/kernel/sys/sys_fs.c b/kernel/sys/sys_fs.c index 1f6eaff..8ba18f9 100644 --- a/kernel/sys/sys_fs.c +++ b/kernel/sys/sys_fs.c @@ -13,7 +13,6 @@ #include "uapi/posix.h" #include "core/syscall.h" #include "core/uaccess.h" -#include "core/kmem.h" #include "uapi/errno.h" #include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */ #include "gfx/gfx.h" /* gfx_cols/gfx_rows — TIOCGWINSZ reports them */ @@ -70,22 +69,6 @@ linux_makedev(uint32 major, uint32 minor) | ((uint64)(major & ~0xfffu) << 32) | ((uint64)(minor & ~0xffu) << 12); } -// Terminals as Linux numbers them: tty0..63 are (4, n), /dev/tty is (5, 0) and -// /dev/console (5, 1). SchOS keeps them all under major 1 (kernel/fs/devfs.c), -// and Linux programs look at the number rather than the name. Xorg is the one -// that matters: it uses VTs only if stat("/dev/tty0") says major 4 -// (xf86HasTTYs), and otherwise runs on none -- no VT switch, no KD_GRAPHICS, -// the console drawing over it, and Ctrl+Alt+F-keys doing nothing. -static uint64 -linux_tty_rdev(uint32 major, uint32 minor) -{ - if (major == 1 && minor == 0) return linux_makedev(4, 0); - if (major == 1 && minor >= 11 && minor <= 16) return linux_makedev(4, minor - 10); - if (major == 1 && minor == 10) return linux_makedev(5, 0); - if (major == 1 && minor == 1) return linux_makedev(5, 1); - return linux_makedev(major, minor); -} - static void stati_linux(struct inode *ip, struct linux_stat *st) { @@ -118,7 +101,7 @@ stati_linux(struct inode *ip, struct linux_stat *st) // entry in /dev printed as "0, 0" under ls -l -- harmless while nothing // read it, and wrong the moment something has to tell two disks apart. if (ip->type == T_DEV) - st->st_rdev = linux_tty_rdev(ip->major, ip->minor); + st->st_rdev = linux_makedev(ip->major, ip->minor); st->st_atime = ip->atime; st->st_mtime = ip->mtime; st->st_ctime = ip->ctime; } @@ -156,53 +139,6 @@ fdalloc(struct file *f) return -1; } -// Remember the absolute path `f` was opened by, for /proc//fd. `path` is -// joined to the directory it was resolved from -- the cwd, or dirfd's own -// remembered path -- and `.`, `..` and repeated slashes are folded out. This -// is lexical: a symlink along the way stays in the name. Nothing depends on -// it being there, so running out of memory just leaves it unset. -static void -file_setpath(struct file *f, int dirfd, const char *path) -{ - const char *base = ""; - if (path[0] != '/') { - struct file *d; - if (dirfd == AT_FDCWD) - base = myproc()->cwd_path; - else if (dirfd >= 0 && dirfd < NOFILE && (d = myproc()->ofile[dirfd]) && d->path) - base = d->path; - else - return; - } - char *buf = kalloc(); - if (!buf) return; - int n = 0; // buf is "/a/b", no trailing slash - for (int pass = 0; pass < 2; pass++) { - const char *s = pass ? path : base; - for (;;) { - while (*s == '/') s++; - const char *e = s; - while (*e && *e != '/') e++; - int len = (int)(e - s); - if (len == 0) break; - if (len == 2 && s[0] == '.' && s[1] == '.') { - while (n > 0 && buf[--n] != '/') ; - } else if (!(len == 1 && s[0] == '.')) { - if (n + 1 + len >= PGSIZE) { kfree(buf); return; } - buf[n++] = '/'; - memmove(buf + n, s, len); - n += len; - } - s = e; - } - } - if (n == 0) buf[n++] = '/'; - buf[n] = 0; - if ((f->path = kmalloc(n + 1)) != 0) - memmove(f->path, buf, n + 1); - kfree(buf); -} - // Resolves a dirfd (AT_FDCWD or a real fd) to the inode a relative path // should be resolved from. 0 on an invalid dirfd. static struct inode * @@ -666,7 +602,6 @@ addr_t sys_open(void) } iunlock(ip); end_op(); f->type = FD_INODE; f->ip = ip; - file_setpath(f, AT_FDCWD, path); f->off = (omode & O_APPEND) ? ip->size : 0; f->readable = !(omode & O_WRONLY); f->writable = (omode & O_WRONLY) || (omode & O_RDWR); @@ -724,7 +659,6 @@ addr_t sys_openat(void) uint64 start = (omode & O_APPEND) ? ip->size : 0; iunlock(ip); end_op(); f->type = FD_INODE; f->ip = ip; f->off = start; - file_setpath(f, dirfd, path); f->readable = !(omode & O_WRONLY); f->writable = (omode & O_WRONLY) || (omode & O_RDWR); f->nonblock = (omode & O_NONBLOCK) != 0; @@ -763,19 +697,6 @@ addr_t sys_fstat(void) } return 0; } - // A socket, a pipe or an anonymous inode: Linux answers with its type and - // an inode number, which is what `ls -l /proc//fd` and every program - // asking "is my stdin a socket?" look at. ino matches /proc//fd. - if (f->type != FD_INODE && f->type != FD_MEMFD && myproc()->syscall_map) { - struct linux_stat *ls = (struct linux_stat *)st; - memset(ls, 0, sizeof(*ls)); - ls->st_mode = f->type == FD_SOCKET ? LINUX_S_IFSOCK | 0777 - : f->type == FD_PIPE ? LINUX_S_IFIFO | 0600 : 0600; - ls->st_ino = f->ino; - ls->st_nlink = 1; - ls->st_blksize = PGSIZE; - return 0; - } if (f->type != FD_INODE) return -EINVAL; ilock(f->ip); if (myproc()->syscall_map) stati_linux(f->ip, (struct linux_stat*)st); @@ -1308,14 +1229,10 @@ addr_t sys_readlink(void) if (!(ip = namei(path))) return -ENOENT; ilock(ip); if (ip->type != T_SYMLINK) { iunlockput(ip); return -EINVAL; } - // A /proc link can change after its inode was loaded -- an fd dup2()ed - // over -- so its size is not trusted: the read says how long it is. - int proc = ip->dev == PROCFS_DEV; - int n = (proc || (int)ip->size > bufsiz) ? bufsiz : (int)ip->size; - int got = readi(ip, 0, (uint64)buf, 0, n); - if (got < 0 || (!proc && got != n)) { iunlockput(ip); return -EIO; } + int n = (int)ip->size < bufsiz ? (int)ip->size : bufsiz; + if (readi(ip, 0, (uint64)buf, 0, n) != n) { iunlockput(ip); return -EIO; } iunlockput(ip); - return got; + return n; } // ----------------------------------------------------------------------- @@ -2173,14 +2090,10 @@ addr_t sys_readlinkat(void) if (!(ip = at_namei(dirfd, path))) return -ENOENT; ilock(ip); if (ip->type != T_SYMLINK) { iunlockput(ip); return -EINVAL; } - // A /proc link can change after its inode was loaded -- an fd dup2()ed - // over -- so its size is not trusted: the read says how long it is. - int proc = ip->dev == PROCFS_DEV; - int n = (proc || (int)ip->size > bufsiz) ? bufsiz : (int)ip->size; - int got = readi(ip, 0, (uint64)buf, 0, n); - if (got < 0 || (!proc && got != n)) { iunlockput(ip); return -EIO; } + int n = (int)ip->size < bufsiz ? (int)ip->size : bufsiz; + if (readi(ip, 0, (uint64)buf, 0, n) != n) { iunlockput(ip); return -EIO; } iunlockput(ip); - return got; + return n; } addr_t sys_fchmodat(void) { return 0; } // stub diff --git a/kernel/sys/sys_net.c b/kernel/sys/sys_net.c index fcf2038..bf52d9e 100644 --- a/kernel/sys/sys_net.c +++ b/kernel/sys/sys_net.c @@ -66,28 +66,6 @@ fetch_sun_path(addr_t uaddr, int ulen, char *out) return 0; } -// For an abstract address -- sun_path[0] == 0 -- the name as a key, and 1. -// The name is every byte after the NUL up to the length given, NULs included, -// as Linux has it; FNV-1a, which is plenty for a table of NSOCK. -static int -sun_abstract(addr_t uaddr, int ulen, uint64 *key) -{ - struct sockaddr_un sa; - int plen = ulen - (int)sizeof(sa.sun_family); - - if (plen < 2 || ulen > (int)sizeof(sa)) - return 0; - if (copy_from_user(&sa, uaddr, (uint64)ulen) < 0 || sa.sun_path[0] != 0) - return 0; - uint64 h = 1469598103934665603ULL; - for (int i = 1; i < plen; i++) { - h ^= (uchar)sa.sun_path[i]; - h *= 1099511628211ULL; - } - *key = h ? h : 1; - return 1; -} - // Wrap an established socket in a file and a descriptor. Consumes the // socket's reference on success; on failure the caller still owns it. // @@ -166,8 +144,7 @@ SYSCALL_DEFINE(socket) type &= 0xFF; if (domain == AF_UNIX && type != SOCK_STREAM) return -EPROTOTYPE; - if (domain == AF_INET && type != SOCK_STREAM && type != SOCK_DGRAM && - type != SOCK_RAW) + if (domain == AF_INET && type != SOCK_STREAM && type != SOCK_DGRAM) return -EPROTOTYPE; // AF_NETLINK takes SOCK_RAW or SOCK_DGRAM and its own protocol numbering; // socknew checks both, because which protocols exist is the socket layer's @@ -191,17 +168,10 @@ SYSCALL_DEFINE(socket) (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP); if (redundant) protocol = 0; - else if (!(domain == AF_INET && - (type == SOCK_DGRAM || type == SOCK_RAW) && + else if (!(domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_ICMP)) return -EPROTONOSUPPORT; } - // A raw socket is ICMP or nothing: it exists because busybox's ping, which - // is /bin/ping on the Alpine root, opens SOCK_RAW and stops at - // EPROTOTYPE. Raw TCP or UDP would be the stack handing over packets it is - // also processing, and nothing has asked for that. - if (domain == AF_INET && type == SOCK_RAW && protocol != IPPROTO_ICMP) - return -EPROTONOSUPPORT; // ENETDOWN rather than ENFILE when the stack itself is not running: the // old blanket ENFILE said "file table overflow" for a machine with no @@ -270,10 +240,6 @@ SYSCALL_DEFINE(bind) return sockbind_in(s, ip, port_from_net(port)); } - uint64 key; - if (sun_abstract(uaddr, ulen, &key)) - return sockbind_abstract(s, key); - if ((r = fetch_sun_path(uaddr, ulen, path)) < 0) return r; @@ -349,10 +315,6 @@ SYSCALL_DEFINE(connect) return sockconnect_in(s, ip, port_from_net(port)); } - uint64 key; - if (sun_abstract(uaddr, ulen, &key)) - return sockconnect(s, SOCK_ABSTRACT_DEV, key); - if ((r = fetch_sun_path(uaddr, ulen, path)) < 0) return r; @@ -588,76 +550,9 @@ SYSCALL_DEFINE(recvfrom) return r; } -// getsockname(2) and getpeername(2). -// -// Neither existed, and the first program to mind was every X client: libxcb -// picks the cookie to send by the family of the connection's address, asked -// with getpeername and then getsockname, and with both failing it sent none. -// Xorg answered "Authorization required, but no authorization protocol -// specified" and startx gave up. -static int -sock_name(int peer) -{ - struct socket *s; - addr_t uaddr, ulenp; - int r, cap; - - if ((r = argsock(0, &s)) < 0) - return r; - argaddr(1, &uaddr); - argaddr(2, &ulenp); - if (get_user(cap, ulenp) < 0) - return -EFAULT; - if (cap < 0) - return -EINVAL; - - union { - struct sockaddr_in in; - struct sockaddr_un un; - } sa; - int alen; - memset(&sa, 0, sizeof(sa)); - - if (sock_domain(s) == AF_INET) { - uint ip; - ushort port; - if ((r = sockname_inet(s, peer, &ip, &port)) < 0) - return r; - sa.in.sin_family = AF_INET; - sa.in.sin_addr = ip; - sa.in.sin_port = port_to_net(port); - alen = (int)sizeof(sa.in); - } else if (sock_domain(s) == AF_NETLINK) { - // A sockaddr_nl of zeros: family, padding, port id 0 (the kernel), - // no groups. - sa.un.sun_family = AF_NETLINK; - alen = 12; - } else { - if ((r = sockname_unix(s, peer, sa.un.sun_path)) < 0) - return r; - sa.un.sun_family = (uint16)sock_domain(s); - // The family, the path and its NUL; an unnamed socket is the family - // alone, as on Linux. - alen = (int)sizeof(sa.un.sun_family); - if (sa.un.sun_path[0]) - alen += (int)strlen(sa.un.sun_path) + 1; - } - - // Truncated to the caller's buffer, with the full length written back. - if (cap > alen) - cap = alen; - if (cap > 0 && copy_to_user(uaddr, &sa, (uint64)cap) < 0) - return -EFAULT; - if (copy_to_user(ulenp, &alen, sizeof(alen)) < 0) - return -EFAULT; - return 0; -} - -SYSCALL_DEFINE(getsockname) { return sock_name(0); } -SYSCALL_DEFINE(getpeername) { return sock_name(1); } - -// shutdown(2). SHUT_WR is a half-close: the peer reads EOF and can still -// write back (see sock_shutdown). +// shutdown(2). Half-close is not modelled -- there is one buffer per +// direction and no per-direction flag -- so SHUT_WR and SHUT_RDWR both mean +// "the peer sees end-of-file", which is the behaviour programs depend on. SYSCALL_DEFINE(sockshutdown) { struct socket *s; diff --git a/kernel/sys/sys_poll.c b/kernel/sys/sys_poll.c index cdcfac3..a28da29 100644 --- a/kernel/sys/sys_poll.c +++ b/kernel/sys/sys_poll.c @@ -24,47 +24,22 @@ #define POLL_MAX_FDS 64 -// Bumped, under tickslock, by anything that can make a descriptor ready -// sooner than the next tick: see poll_kick(). -static uint poll_seq; - -// Something a poller may be waiting for has happened. The wait below sleeps -// on the tick channel, so this wakes it now rather than at the tick. -// -// Without it every poll() that found nothing slept out the rest of a 10 ms -// tick whatever arrived in the meantime. busybox's wget polls before each -// read, and downloaded at 85 KB/s -- a segment per tick -- where apk, which -// blocks in read() and is woken by the data, managed 5.8 MB/s over the same -// connection. Called from the network stack's wakeup; anything else that -// makes a file readable can call it too, and until it does it is no worse -// than it was. -void -poll_kick(void) +// Sleep one timer tick, or until killed/signaled. Returns 0 to keep +// waiting, -EINTR if the wait must be abandoned. +static int +poll_block_one_tick(void) { - acquire(&tickslock); - poll_seq++; - release(&tickslock); - wakeup(&ticks); -} - -// Sleep one timer tick, or until a poll_kick, or until killed/signaled. -// Returns 1 if a tick went by, 0 if it was a kick -- which must not count -// against a timeout, or a timed poll on a busy network would end early -- -// and -EINTR if the wait must be abandoned. epoll waits with it too. -int -poll_wait_tick(void) -{ - struct proc *p = myproc(); - if (p->killed || (p->pending_signals & ~p->signal_mask)) + if (myproc()->killed) + return -EINTR; + if (myproc()->pending_signals & ~myproc()->signal_mask) return -EINTR; acquire(&tickslock); - uint t0 = ticks, s0 = poll_seq; - while (ticks == t0 && poll_seq == s0 && !p->killed && - !(p->pending_signals & ~p->signal_mask)) + uint t0 = ticks; + while (ticks == t0 && !myproc()->killed && + !(myproc()->pending_signals & ~myproc()->signal_mask)) sleep(&ticks, &tickslock); - int ticked = ticks != t0; release(&tickslock); - return ticked; + return 0; } // Shared core: fds[] already in kernel memory. timeout_ticks < 0 means @@ -92,10 +67,9 @@ do_poll(struct pollfd *fds, int nfds, int timeout_ticks) } if (nready > 0 || timeout_ticks == 0) return nready; - int w = poll_wait_tick(); - if (w < 0) + if (poll_block_one_tick() < 0) return -EINTR; - if (w > 0 && timeout_ticks > 0) + if (timeout_ticks > 0) timeout_ticks--; } } @@ -241,10 +215,9 @@ sys_select(void) } return nready; } - int w = poll_wait_tick(); - if (w < 0) + if (poll_block_one_tick() < 0) return -EINTR; - if (w > 0 && timeout_ticks > 0) + if (timeout_ticks > 0) timeout_ticks--; } } @@ -314,10 +287,9 @@ sys_pselect6(void) } return nready; } - int w = poll_wait_tick(); - if (w < 0) + if (poll_block_one_tick() < 0) return -EINTR; - if (w > 0 && timeout_ticks > 0) + if (timeout_ticks > 0) timeout_ticks--; } } diff --git a/kernel/sys/sys_time.c b/kernel/sys/sys_time.c index 412a3fa..9175c19 100644 --- a/kernel/sys/sys_time.c +++ b/kernel/sys/sys_time.c @@ -10,36 +10,14 @@ #include "core/uaccess.h" #include "uapi/errno.h" -// Timer values in ticks, rounding a nonzero time up: POSIX says a timer set -// to 1 us goes off, not that it is disarmed. -static uint -tv_ticks(struct timeval *tv) -{ - if (tv->tv_sec < 0 || tv->tv_usec < 0) - return 0; - uint64 t = (uint64)tv->tv_sec * HZ + - ((uint64)tv->tv_usec + USEC_PER_TICK - 1) / USEC_PER_TICK; - return t > 0x7fffffff ? 0x7fffffff : (uint)t; -} - -static void -ticks_tv(uint t, struct timeval *tv) -{ - tv->tv_sec = t / HZ; - tv->tv_usec = (t % HZ) * USEC_PER_TICK; -} - -// The seconds left on the previous alarm, rounded up, as POSIX asks. addr_t sys_alarm(void) { int seconds; if (argint(0, &seconds) < 0) return -1; - struct proc *p = myproc(); - uint left = alarm_left(p); - alarm_arm(p, seconds > 0 ? (uint)seconds * HZ : 0, 0); - return (left + HZ - 1) / HZ; + myproc()->alarm_ticks = seconds * HZ; + return 0; } addr_t @@ -99,52 +77,21 @@ sys_clock_gettime(void) return 0; } -// ITIMER_REAL only. ITIMER_VIRTUAL and ITIMER_PROF count CPU time, which -// nothing here measures per tick, and are refused rather than accepted and -// never fired -- which is what all three used to be. -static int -itimer_get(struct proc *p, struct itimerval *iv) -{ - ticks_tv(p->alarm_every, &iv->it_interval); - ticks_tv(alarm_left(p), &iv->it_value); - return 0; -} - -addr_t -sys_getitimer(void) -{ - int which; - addr_t curp; - argint(0, &which); argaddr(1, &curp); - if (which != 0) - return -EINVAL; - struct itimerval iv; - itimer_get(myproc(), &iv); - if (copy_to_user(curp, &iv, sizeof(iv)) != 0) - return -EFAULT; - return 0; -} +addr_t sys_getitimer(void) { return 0; } // stub addr_t sys_setitimer(void) { int which; - addr_t newp, oldp; - argint(0, &which); argaddr(1, &newp); argaddr(2, &oldp); - if (which != 0) - return -EINVAL; - struct proc *p = myproc(); - struct itimerval iv; - if (oldp) { - itimer_get(p, &iv); - if (copy_to_user(oldp, &iv, sizeof(iv)) != 0) - return -EFAULT; + addr_t newp; + argint(0, &which); argaddr(1, &newp); + // We don't implement interval timers; just track the alarm if it's ITIMER_REAL. + if (which == 0 && newp) { + struct itimerval iv; + if (copy_from_user(&iv, newp, sizeof(iv)) == 0) + myproc()->alarm_ticks = (int)(iv.it_value.tv_sec * HZ + + iv.it_value.tv_usec / USEC_PER_TICK); } - if (newp == 0) - return 0; - if (copy_from_user(&iv, newp, sizeof(iv)) != 0) - return -EFAULT; - alarm_arm(p, tv_ticks(&iv.it_value), tv_ticks(&iv.it_interval)); return 0; } diff --git a/kernel/sys/table.inc b/kernel/sys/table.inc index c2ac019..0002680 100644 --- a/kernel/sys/table.inc +++ b/kernel/sys/table.inc @@ -184,8 +184,8 @@ SYSCALL(102, 157, prctl) SYSCALL(103, 234, tgkill) SYSCALL(104, 200, tkill) SYSCALL(105, 34, pause) -SYSCALL(106, 36, getitimer) -SYSCALL(107, 38, setitimer) +SYSCALL(106, NO_LINUX, getitimer) +SYSCALL(107, NO_LINUX, setitimer) SYSCALL(108, 98, getrusage) SYSCALL(109, 100, times) SYSCALL(110, 99, sysinfo) @@ -296,8 +296,6 @@ SYSCALL(203, 281, epoll_pwait) SYSCALL(204, 282, signalfd) SYSCALL(205, 284, eventfd) SYSCALL(206, 73, flock) -SYSCALL(207, 51, getsockname) -SYSCALL(208, 52, getpeername) // --- mounts --- SYSCALL(185, 165, mount) diff --git a/ports/alpine/world b/ports/alpine/world index bb9a788..fab5aa1 100644 --- a/ports/alpine/world +++ b/ports/alpine/world @@ -16,13 +16,3 @@ ncurses-terminfo-base # The PCI and USB ID databases: lspci, fastfetch and Xorg read them. hwdata-pci hwdata-usb -# X on the framebuffer, with startx. /root/.xserverrc and .xinitrc (from -# mkaroot.sh) start it with -dumbSched and run Window Maker in it. -xorg-server -xf86-video-fbdev -xf86-input-evdev -xinit -# The desktop: Window Maker, a terminal for it, and a font for both. -windowmaker -xterm -font-dejavu diff --git a/ports/alpine/world.lock b/ports/alpine/world.lock index fd28a61..90005b1 100644 --- a/ports/alpine/world.lock +++ b/ports/alpine/world.lock @@ -1,91 +1,12 @@ # Generated by tools/posix/alpine-lock.sh from world. Do not edit; # change world and run `make alpine-lock`. # file sha256 repository -brotli-libs-1.2.1-r0.apk 62a7e37e43bbfc1f8525a00ad2125fd5897135dc93b849733884cb78ffd8e26c main busybox-1.37.0-r31.apk 0e626fa97cf937fda7bba4ed4dde8e2f2dbb5083ac34e02180cec0c4eba9351b main busybox-binsh-1.37.0-r31.apk 93f803ae5db6b729c0f66f4afba6e907ccc17d7d5f8b8c3d25bdb4636de2c48a main -encodings-1.1.0-r0.apk f7c0eb762638d0249b4fbbe07a8e4ed6aab8e86b74c239c40e47a36219777d52 main -eudev-libs-3.2.14-r6.apk 45fdbcf37c5c0e82067e98a818d3c1fd16d0261c62128dcb6b935b86b4ec6c86 main -font-alias-1.0.6-r0.apk 754e5c1d19c2c69d5a2c3c02c7340cad7df3c26e63789516372ea164b564b927 main -fontconfig-2.17.1-r1.apk 7634cf2fb1066ec97c8f41ec90ec9f036540e056276b7bf9a10dc4ec9e44d238 main -font-cursor-misc-1.0.4-r1.apk a1a0c0659a9a48e24b4cc4b35583f4e57da9d2a0ae2cc8adb9be1143b66911b4 main -font-dejavu-2.37-r6.apk 6e7609e7a1aaed947afe4ddcc02bbdeec571dac5ff4ea1c04af9b8480b7b9dcf main -font-misc-misc-1.1.3-r1.apk 2c48f2a8529ac63243a0bdd5e7ab2329e0a8b8f996ffe95faed8b79fb05b4a52 main -freetype-2.14.3-r0.apk 09d76fde675a9ca3cfeb83d72fb09cb2e9ccf79b2937daa05a1af9a37a6b7815 main -gmp-6.3.0-r4.apk 85323ef5bce8ff234736dc2d6592ea37aebdc6348a531e27430bdfe38364d256 main hwdata-pci-0.408-r0.apk fe61404e6d2bd1e04c021d53641c0ebce97911084d77994e43a288a1278686c1 main hwdata-usb-0.408-r0.apk 272d365cb05dd085dd8022154cf71e3f3b0aaddd41c1269eeaf939c9d38675c8 main -libbsd-0.12.2-r0.apk 407597c04b41dbfe8a05b35d45a3453e32fae7df84aed3a06ff48dd4df1338b6 main -libbz2-1.0.8-r6.apk 2737e2b23ed08d4911ee62cd0163b5f4bf0379cacf4dc35b23a00209584c98fd main libcap2-2.78-r0.apk 42886a7b4bcde26347c712925c8dc7db202b6029ef9b8c7ee81577ab7bdd8fad main -libdrm-2.4.134-r0.apk 584fd84ad5030040e613fa1816097cd1706ddd542100070852b0eab6e09e12ca main -libelf-0.195-r0.apk 78c5a1993342ab1bdcdce30639fd0937e8000f6d0d85cea171f573d31decd036 main -libepoxy-1.5.10-r1.apk 0be13b4b524fe64315e0663ab615a244d75d7da73130ac1c630cd6096841444e main -libevdev-1.13.6-r0.apk b38a27a4ac55701fd2eb0032977af2664b6fc23b2f9c2cc6e893ad4452fd82d5 community -libexpat-2.9.0-r0.apk 32b127f31e0a919110a18a7f0bb3780d8fb66ba4d8d61b2d301d633f0e319d0e main -libffi-3.5.2-r1.apk 6da1a19313a40c56cac756fb11e2c4d5267831e1c22fc8323f24c943c94da6e0 main -libfontenc-1.1.9-r0.apk ea7f6d097653572991da749ac0764c2568d8902a01eea4236e56b10b90a7b0e3 main -libgcc-15.2.0-r5.apk 393dcd32629f06d7d85409c272d142d0c082772d10b87ef55ee82f47de3be637 main -libice-1.1.2-r0.apk 08daa589d57149b52b00c666b4009212aa3af95021ebdb2a359555c9bcaa76e1 main -libjpeg-turbo-3.1.3-r0.apk 6299627dd0a9156a7463511e4915a6294dfb592d8b4bdf85e55265adf1b8ea04 main -libmd-1.2.0-r0.apk f6d68d072fcefc8edd4ffe3afc341ca391f8a9a209130628fb065d98b18a2792 main libncursesw-6.6_p20260516-r0.apk cf8caa8a88bc4ce9d9e395567fefaf9ef7fbd55c50ae6155b35c1b58f3755023 main -libpciaccess-0.19-r0.apk 03fdccd1e2a0825e7f7fabbac67212e05d271d31d2eff44cc92f707e69908ad4 main -libpng-1.6.59-r0.apk d16aeb0597f2bc43e0583ac9ff779388ec160c3e22568b640122e94ba55be6d7 main -libsharpyuv-1.6.0-r0.apk 34621ff53f43030d24d1224fe9cb53b1590ab614cbce6080005cde703e6c874a main -libsm-1.2.6-r0.apk dd7eff58a46fd60e4afee9c7c1c3508920b0e1d6560129e59aee82329f68afc8 main -libstdc++-15.2.0-r5.apk 14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6 main -libuuid-2.42.3-r1.apk 8306e5bb577696c9069fe1dfd9e1dcc39d2d481c6a1b0e707fd03c3e21aa6aa2 main -libwebp-1.6.0-r0.apk dc50c0280348330bb113079331c4a35d85e28351c7baa9268ee346aad8398288 main -libx11-1.8.14-r0.apk 016efc189c6d566061ffe206ed0780e32686c1a8c832a3917d35d2c66d61a40f main -libxau-1.0.12-r0.apk 3506cdba7ed916a6f814a010fbb127dbfdae4147107f64a15f5fa2651c5d8510 main -libxaw-1.0.16-r1.apk 8ac469cddeac40ff2e91c84cb02ba526d7c62e30a79a25c2e3b6e5269236801d main -libxcb-1.17.0-r2.apk 7935d5ad9c7137b7b3f1292d62f55f7e0cc5c919155218c3060ea46503b3f211 main -libxcvt-0.1.3-r0.apk 809b454981887c1d1d835c909c46a80650ca72a73a92268a2cf8a666ad82b6b7 community -libxdmcp-1.1.5-r1.apk 2a93f44f3f85c1eda9ed53691c35d0742b5fcd8e0057cd7380934025936cbf6f main -libxext-1.3.7-r0.apk 8afe1ee6ab8cb656e12f20dc5b7fb2d4103faee4a38298b94f26ab52e597cbc8 main -libxfont2-2.0.8-r0.apk f8bf87b9ce835cb307eb8a3d270e226c4437757478c52ba3ad384cecc2ac6fef community -libxft-2.3.9-r0.apk eba6fd85599b4fedf3acdd3ad3e2eec84ea97a5ba8f1ac570fa1ee37a7d81420 main -libxinerama-1.1.6-r0.apk 9f780efb3cf675b5253b73b84f278b40757fa7869c6fe70a9790a73f4f7e810f main -libxkbfile-1.2.0-r0.apk e575c6c314ad87e74887526aca3b1429f5c1d6290e66f2945d4053f387beea57 main -libxml2-2.13.9-r2.apk 4b2f986159c659f014b942fe8a5d70c67af475153ca826c3dd53389eea46a300 main -libxmu-1.3.1-r0.apk 5d0b06d1a4b38ac0e070e0dad5e61583e5e147e378ba50418c83e1be4b19986c main -libxpm-3.5.20-r0.apk 1ed312a00abac5a5840c1f3721921ea8183e664566c19763ed00aa9f35bb2178 main -libxrender-0.9.13-r0.apk 724aa6c6353e70869e0eb5aa5461e8fc0753b99db765de47a3ae238573dbdcf8 main -libxshmfence-1.3.3-r0.apk 53790552de7eea17a47aa4c082b06b735c545a0947c41128f97c7be95a7d3d92 main -libxt-1.3.1-r0.apk 08b167b05ac7dfb892421876cc2467214208b83b2960a9f49e84f9dbcf36e812 main -libxxf86vm-1.1.7-r0.apk 887a3c5438e1db624d07ac23d16ec08d21fb16de438d75ad2dd5637c958b4108 main -llvm22-libs-22.1.3-r1.apk dec15bdd29b6ec7fc22aee43bac153a22e6b132c599e5e5d9ccd0d159eae764f main -mcookie-2.42.3-r1.apk 64f86340991e2f283ce95b1bae21157da6849118cd9f1a2931de5c73d0f9fc3d main -mesa-26.1.6-r0.apk 8fb0e3d386f09df4f3e3643108b38197558465b2558e6d66d6a9c055adc8d1a9 main -mesa-egl-26.1.6-r0.apk ccdd25c8f3a069b018a39a5641e4f13ab7ceb511d5991e9f8fb18bd551172ffa main -mesa-gbm-26.1.6-r0.apk db6b00543e7e686f377a5c250036067742f914112839255e2e5edbbc241bfb0d main -mesa-gl-26.1.6-r0.apk 4d2d502a3938bd5dc4affec13545271571bb8bec40e7078ef01f34222bfb5d5d main -mesa-gles-26.1.6-r0.apk 2544a75e74778ab9ddcecfc8d9dc35375cb3a4ac3661e94a1156e2c528d7b59b main -mkfontscale-1.2.4-r0.apk 2385a7aad3971fcad9d862cb90937e643e633b14bf4b91f8b8ce3040a1468d20 main -mtdev-1.1.7-r0.apk 004f42bc67bc0eb573d64d802c5f0325f49013cf4aa7576d4b571399df46f3f7 community musl-1.2.6-r3.apk e142255d0fa397f14318996bc10378093dcc069dc3dd1b6818c8f28a789c21da main ncurses-terminfo-base-6.6_p20260516-r0.apk ce83be6d0bd10584a53c3a5868cc1595ce5f0f8e0b621f8f7f70b6144e521fc9 main -nettle-3.10.2-r0.apk 693c1a99b781cd17149916df30113ccd6bdddac130298c17b1286fd2ae438386 main -pixman-0.46.4-r0.apk 378550cbe348dda9b0733014375dba87a89f0077c5f3dbeba7dce8dbcdda707e main -pkgconf-2.5.1-r0.apk 20cc47b76b82e9424a35ecc4fdd7bd904ef49ada1684214910fae31bc0cab737 main -spirv-tools-1.4.341.0-r0.apk 6034c22c4f61fb76e517c1d5b11ef5c26c7154d2baeacec8b0a9dcca94ff9a93 main -tiff-4.7.2-r0.apk 757ce87ebe4923a6be9869d026e250958ca09696529c464781e8a91d2b81670b main -util-macros-1.20.2-r0.apk 7c129d085b646b13ccef3738e93038221aec840c1a6764279c5adc9950ba1bdc main -wayland-libs-client-1.25.0-r0.apk a728f7103217cafdc83ff6ad0d679c197dbbe280bd5788c8336259f7a39cb303 main -windowmaker-0.96.0-r0.apk 15b500d9afccf94a385e042b01ed9b1d709b1627d97597cea59bf63d0bab4f6b community -xauth-1.1.5-r0.apk 94e80e9687a1829a6fe95fb28d4b08a5531dab182cde8a58ff1b370b2cd293fb community -xf86-input-evdev-2.11.0-r0.apk 1f3fcd3e837b43ad5e32005e03095b547ddbfc25973f0511e9ff74bf342d2fe0 community -xf86-video-fbdev-0.5.0-r6.apk 6a8a1b16f82d8810896f00c00b356c8b8d923e9aa0ce64737ae8274ca82f507a community -xinit-1.4.4-r0.apk efb5dc1e7929824f5add7d5d51d0ecb34ecfd0a773dfca4f061adc8a353b234f community -xkbcomp-1.5.0-r0.apk 8bfbd3c8ce7180e8cf599d5a0b791993b61fa98f3a19c47aaf5fcbe30c12d0ba main -xkeyboard-config-2.47-r0.apk 8c8f9af2b78f8e955480aa88103e495d2637edac659a417075e877c88a41fed0 main -xmodmap-1.0.11-r1.apk 42489c8865d401e894b8da210f5c8e27f58c1d72c83250a505a65bdb8c45bec0 community -xorg-server-21.1.25-r0.apk 8130c341a5f047069cb35ac77a44d8bab6a2e3cb5a7d0745e3c5096b1909ecfb community -xorg-server-common-21.1.25-r0.apk bf5994707b4030f249ad44a123ad2686501c82d2002d73d0a641d9001caa37c3 community -xrdb-1.2.2-r0.apk 29649088693b5974dec3f928a5303e354f7b91e381b778006fe3011d003b1369 community -xterm-410-r0.apk eff11da325ffb1dcfa4c9902ef27d34cc27b76457e1fc8dad90f10944ecb33b8 community -xz-libs-5.8.4-r0.apk ab362dd515ead04c31a6c302163248da7aa4813e283c971e6dd3f789b453a1d9 main -zlib-1.3.2-r1.apk 63aeea03c15a2f9018f81805cfc8aa926bdf5cd68921f22149c2fbb5d0ee9f47 main zsh-5.9-r7.apk 22ec3c4de69e38437e351ba5a881f5d88e9b518a248c8aa06d36b148cf16ce32 main -zstd-libs-1.5.7-r2.apk 23c6065b0049b2406441564bcf0032515a43f78e80d76fcb85535a3803ef5d4e main diff --git a/ports/zstd/Makefile b/ports/zstd/Makefile deleted file mode 100644 index 33fca6b..0000000 --- a/ports/zstd/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# zstd -- the decompressor behind SCFF, the compressed live root. Dual -# BSD-3-Clause / GPL-2.0; see LICENSE and COPYING in the tarball and -# docs/licensing.md. -# -# Kernel code, like uACPI: nothing is installed into the sysroot. This -# directory fetches and unpacks a pinned release, and the top-level Makefile -# compiles the decoder half of lib/ straight into the kernel against -# kernel/scff/compat, which stands in for the libc headers it includes. The -# host side, tools/mkscff, links the system's libzstd. -# -# Chosen for what SCFF asks of a codec: compress as hard as the build likes, -# once, and decompress at memory speed on every first read of a block. - -include ../mk/port.mk - -ZSTD_VERSION := 1.5.7 -ZSTD_URL := https://github.com/facebook/zstd/releases/download/v$(ZSTD_VERSION)/zstd-$(ZSTD_VERSION).tar.gz -ZSTD_SHA256 := eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 - -TARBALL := $(DISTDIR)/zstd-$(ZSTD_VERSION).tar.gz - -$(TARBALL): | $(DISTDIR) - curl -fsSL -o $@ $(ZSTD_URL) - sh $(SHA256CHECK) "$(ZSTD_SHA256)" "$@" - -download: $(TARBALL) - -$(SRCDIR): $(TARBALL) - mkdir -p $(PORTDIR) - cd $(PORTDIR) && tar xzf $(abspath $<) - mv $(PORTDIR)/zstd-$(ZSTD_VERSION) $@ - -# The kernel build depends on this directory existing, nothing more. -all: | $(SRCDIR) - -install: - -clean: - rm -rf $(PORTDIR) - -distclean: clean - rm -f $(TARBALL) diff --git a/shell.nix b/shell.nix index 2a63eb7..0da3540 100644 --- a/shell.nix +++ b/shell.nix @@ -16,14 +16,6 @@ pkgs.mkShell { name = "schos"; - # Off, all of it. Nix's compiler wrapper adds -fPIC, -fstack-protector, - # -D_FORTIFY_SOURCE and friends to every compile, which is right for Linux - # programs and wrong for everything here: the kernel is freestanding and - # linked at fixed addresses, and the UEFI loader targets PE/COFF, where clang - # refuses -fPIC outright ("unsupported option '-fPIC' for target - # 'x86_64-unknown-windows-msvc'") and `make` stops at BOOTX64.EFI. - hardeningDisable = [ "all" ]; - nativeBuildInputs = with pkgs; [ # The cross-compiler, and the reason this file exists. clang targets # bare-metal ELF, 16-bit code and PE/COFF from one install; lld-link is what diff --git a/tools/mkscff.c b/tools/mkscff.c deleted file mode 100644 index 64d34d1..0000000 --- a/tools/mkscff.c +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// mkscff: write a block image as SCFF, the SchOS Compressed File Format. -// -// mkscff IN.img OUT.scff [--chunk KB] [--level N] [--threads N] -// -// The layout is include/fs/scff.h. Chunks are compressed on every core, since -// zstd's high levels are slow and the image is hundreds of megabytes, and each -// one is decompressed again and compared before it is written: an image the -// kernel cannot read is found here rather than at boot. -// -// Built against the zstd release the kernel's decoder comes from (ports/zstd), -// not the host's libzstd, so both sides are one version and nothing needs to -// be installed for it. - -#include -#include -#include -#include -#include -#include - -#include "fs/scff.h" -#include "zstd.h" - -static unsigned char *img; -static uint64_t img_bytes; -static uint32_t chunk_bytes = 128 * 1024; -static int level = 19; -static uint32_t nchunks; - -static unsigned char **out; // chunk i as stored, or 0 for an all-zero one -static size_t *outlen; -static uint32_t next_chunk; -static pthread_mutex_t next_lock = PTHREAD_MUTEX_INITIALIZER; - -static void -die(const char *what) -{ - fprintf(stderr, "mkscff: %s\n", what); - exit(1); -} - -static int -all_zero(const unsigned char *p, size_t n) -{ - for (size_t i = 0; i < n; i++) - if (p[i]) - return 0; - return 1; -} - -static void * -worker(void *arg) -{ - (void)arg; - ZSTD_CCtx *cc = ZSTD_createCCtx(); - ZSTD_DCtx *dc = ZSTD_createDCtx(); - size_t cap = ZSTD_compressBound(chunk_bytes); - unsigned char *buf = malloc(cap), *back = malloc(chunk_bytes); - - if (!cc || !dc || !buf || !back) - die("out of memory"); - ZSTD_CCtx_setParameter(cc, ZSTD_c_compressionLevel, level); - ZSTD_CCtx_setParameter(cc, ZSTD_c_checksumFlag, 1); - - for (;;) { - pthread_mutex_lock(&next_lock); - uint32_t i = next_chunk++; - pthread_mutex_unlock(&next_lock); - if (i >= nchunks) - break; - - const unsigned char *src = img + (uint64_t)i * chunk_bytes; - size_t n = img_bytes - (uint64_t)i * chunk_bytes; - if (n > chunk_bytes) - n = chunk_bytes; - - if (all_zero(src, n)) { - outlen[i] = 0; - continue; - } - size_t c = ZSTD_compress2(cc, buf, cap, src, n); - if (ZSTD_isError(c)) - die(ZSTD_getErrorName(c)); - if (c >= n) { // did not compress: stored as it is - out[i] = malloc(n); - memcpy(out[i], src, n); - outlen[i] = n; - continue; - } - size_t d = ZSTD_decompressDCtx(dc, back, chunk_bytes, buf, c); - if (ZSTD_isError(d) || d != n || memcmp(back, src, n) != 0) - die("a chunk did not decompress to itself"); - out[i] = malloc(c); - memcpy(out[i], buf, c); - outlen[i] = c; - } - ZSTD_freeCCtx(cc); - ZSTD_freeDCtx(dc); - free(buf); - free(back); - return 0; -} - -int -main(int argc, char **argv) -{ - const char *in = 0, *outpath = 0; - long threads = sysconf(_SC_NPROCESSORS_ONLN); - - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--chunk") && i + 1 < argc) - chunk_bytes = (uint32_t)atoi(argv[++i]) * 1024; - else if (!strcmp(argv[i], "--level") && i + 1 < argc) - level = atoi(argv[++i]); - else if (!strcmp(argv[i], "--threads") && i + 1 < argc) - threads = atol(argv[++i]); - else if (!in) - in = argv[i]; - else if (!outpath) - outpath = argv[i]; - else - die("usage: mkscff IN.img OUT.scff [--chunk KB] [--level N] [--threads N]"); - } - if (!in || !outpath) - die("usage: mkscff IN.img OUT.scff [--chunk KB] [--level N] [--threads N]"); - if (chunk_bytes < 4096 || chunk_bytes > SCFF_CHUNK_MAX || - (chunk_bytes & (chunk_bytes - 1))) - die("--chunk must be a power of two from 4 to 1024 KB"); - if (threads < 1) - threads = 1; - - FILE *f = fopen(in, "rb"); - if (!f) - die(strerror(errno)); - fseek(f, 0, SEEK_END); - img_bytes = (uint64_t)ftell(f); - fseek(f, 0, SEEK_SET); - if (img_bytes == 0 || img_bytes % 4096) - die("the image is not a whole number of 4 KB blocks"); - if (!(img = malloc(img_bytes)) || fread(img, 1, img_bytes, f) != img_bytes) - die("cannot read the image"); - fclose(f); - - nchunks = (uint32_t)((img_bytes + chunk_bytes - 1) / chunk_bytes); - out = calloc(nchunks, sizeof(*out)); - outlen = calloc(nchunks, sizeof(*outlen)); - if (!out || !outlen) - die("out of memory"); - - pthread_t *t = calloc((size_t)threads, sizeof(*t)); - for (long i = 0; i < threads; i++) - pthread_create(&t[i], 0, worker, 0); - for (long i = 0; i < threads; i++) - pthread_join(t[i], 0); - - struct scff_header h; - memset(&h, 0, sizeof(h)); - memcpy(h.magic, SCFF_MAGIC, 4); - h.version = SCFF_VERSION; - h.codec = SCFF_CODEC_ZSTD; - h.chunk_bytes = chunk_bytes; - h.nchunks = nchunks; - h.image_bytes = img_bytes; - h.index_off = sizeof(h); - - uint64_t *index = malloc(((size_t)nchunks + 1) * sizeof(uint64_t)); - uint64_t pos = sizeof(h) + ((uint64_t)nchunks + 1) * sizeof(uint64_t); - for (uint32_t i = 0; i < nchunks; i++) { - index[i] = pos; - pos += outlen[i]; - } - index[nchunks] = pos; - h.stored_bytes = pos; - - if (!(f = fopen(outpath, "wb"))) - die(strerror(errno)); - fwrite(&h, sizeof(h), 1, f); - fwrite(index, sizeof(uint64_t), (size_t)nchunks + 1, f); - for (uint32_t i = 0; i < nchunks; i++) - if (outlen[i]) - fwrite(out[i], 1, outlen[i], f); - if (fclose(f) != 0) - die("cannot write the output"); - - printf("mkscff: %llu KB -> %llu KB (%.1f%%), %u chunks of %u KB, zstd -%d\n", - (unsigned long long)(img_bytes >> 10), (unsigned long long)(pos >> 10), - 100.0 * (double)pos / (double)img_bytes, nchunks, chunk_bytes >> 10, level); - return 0; -} diff --git a/tools/posix/mkaroot.sh b/tools/posix/mkaroot.sh index 4d2e71e..d3499cc 100644 --- a/tools/posix/mkaroot.sh +++ b/tools/posix/mkaroot.sh @@ -97,77 +97,6 @@ mkdir -p "$P/etc/profile.d" grep -v '^export \(PATH\|HOME\|TERM\)=' "$P/etc/profile" > "$P/etc/profile.d/schos.sh" rm -f "$P/etc/profile" -# How startx starts the X server for root: Alpine's xserverrc, plus -# -dumbSched. Xorg reads input on a thread of its own, SchOS has no threads -# yet, and the server crashed on its way out the first time a client -# disconnected, in pthread_join on a thread that was never made. -dumbSched is -# Xorg's own switch for input on the main loop. The file is root's own rather -# than /etc/X11/xinit/xserverrc, which belongs to the xinit package. -mkdir -p "$P/root" -cat > "$P/root/.xserverrc" <<'XEOF' -#!/bin/sh -# The VT X runs on is the one startx was started from. startx works that out -# only when `uname -s` says Linux, and SchOS's says SchIFT. -vt=$(tty | sed -n 's|^/dev/tty\([0-9][0-9]*\)$|vt\1|p') -exec /usr/bin/X -nolisten tcp -dumbSched $vt "$@" -XEOF - -# What startx runs inside it: Window Maker, which world installs. Without this -# xinit falls back to twm and an xterm, neither of which is in the image. -cat > "$P/root/.xinitrc" <<'XEOF' -#!/bin/sh -exec wmaker -XEOF - -# X's input devices, named rather than found. Alpine's Xorg asks udev for -# them, SchOS has no udev, and X came up with no keyboard and no mouse: the -# pointer did not move, Ctrl+Alt+F-keys did nothing and nothing could end the -# session. SchOS has exactly two input nodes whatever the machine -- every -# keyboard on event0, every mouse on event1 (kernel/dev/input.c) -- so a -# fixed list is the whole answer. The file is SchOS's, in xorg.conf.d, which -# no Alpine package writes into. -mkdir -p "$P/etc/X11/xorg.conf.d" -cat > "$P/etc/X11/xorg.conf.d/10-schos.conf" <<'XEOF' -# SchOS has no udev, so X is told its input devices instead of finding them. -# event0 is every keyboard and event1 every mouse (kernel/dev/input.c). -Section "ServerFlags" - Option "AutoAddDevices" "false" - # Ctrl+Alt+Backspace ends X. - Option "DontZap" "false" -EndSection - -Section "InputDevice" - Identifier "keyboard" - Driver "evdev" - Option "Device" "/dev/input/event0" - Option "XkbRules" "evdev" - Option "XkbOptions" "terminate:ctrl_alt_bksp" -EndSection - -Section "InputDevice" - Identifier "mouse" - Driver "evdev" - Option "Device" "/dev/input/event1" -EndSection - -Section "Device" - Identifier "fb" - Driver "fbdev" -EndSection - -Section "Screen" - Identifier "screen" - Device "fb" -EndSection - -Section "ServerLayout" - Identifier "schos" - Screen "screen" - InputDevice "keyboard" "CoreKeyboard" - InputDevice "mouse" "CorePointer" -EndSection -XEOF - # Mount points and runtime directories are alpine-baselayout's; an empty # directory here only duplicates it. for d in dev proc sys run tmp srv; do @@ -181,9 +110,6 @@ sed -i 's|^\(root:[^:]*:0:0:[^:]*:[^:]*:\)/bin/sh$|\1/bin/zsh|' /etc/passwd for s in /bin/zsh /bin/mksh; do grep -qx "$s" /etc/shells 2>/dev/null || echo "$s" >> /etc/shells done -# The machine's own name, which Alpine's hosts leaves out. Without it xauth -# cannot resolve "schos:0", and startx says "bad display name" four times. -grep -qw schos /etc/hosts || printf '127.0.1.1\tschos\n' >> /etc/hosts exit 0 EOF cp "$PKG/post-install" "$PKG/post-upgrade" @@ -231,8 +157,7 @@ ROOT_PI=$ROOT sh -c ' sed -i "s|^\(root:[^:]*:0:0:[^:]*:[^:]*:\)/bin/sh$|\1/bin/zsh|" "$ROOT_PI/etc/passwd" for s in /bin/zsh /bin/mksh; do grep -qx "$s" "$ROOT_PI/etc/shells" 2>/dev/null || echo "$s" >> "$ROOT_PI/etc/shells" - done - grep -qw schos "$ROOT_PI/etc/hosts" || printf "127.0.1.1\tschos\n" >> "$ROOT_PI/etc/hosts"' + done' # The repository goes in too, so the installed system can reinstall and # upgrade schos-base, and Alpine's are listed for everything else. diff --git a/tools/posix/mkiso.sh b/tools/posix/mkiso.sh index 09c120a..4cf408a 100644 --- a/tools/posix/mkiso.sh +++ b/tools/posix/mkiso.sh @@ -3,16 +3,11 @@ # # tools/posix/mkiso.sh [output.iso] # -# The disc carries one El Torito boot entry and the loader and kernel as files: +# The disc carries one El Torito boot entry and a real directory tree: # # /boot/efiboot.img a FAT image holding the loader and the kernel # /EFI/BOOT/BOOTX64.EFI the same loader, visible as a file on the disc -# /schos/kernel the same kernel, for chainloaders (below) -# -# The root filesystem is inside the kernel, as its ramdisk. The disc used to -# carry it a third time as an ISO 9660 tree, which nothing reads -- SchOS has -# no ISO 9660 driver -- and once Xorg was in the root that copy alone was -# 360 MB. +# /bin /etc /usr ... the staged sysroot, as an ordinary hierarchy # # --- Why only one entry ----------------------------------------------------- # @@ -74,6 +69,7 @@ cd "$repo" BUILD=${BUILD:-build} STAGING="$BUILD/iso" +SYSROOT=${SYSROOT:-sysroot} KERNEL="$BUILD/kernel/kernel" EFIAPP="$BUILD/uefi/BOOTX64.EFI" @@ -117,6 +113,9 @@ cp "$KERNEL" "$STAGING/schos/kernel" # this FAT image -- not the ISO 9660 tree around it. Without the kernel in here # the loader comes up and halts with EFI_NOT_FOUND, which is what it did. # +# The ISO 9660 tree still carries the sysroot for a running system to read; the +# loader's own two files live in here because that is the volume it is handed. +# # Sized from what actually goes in plus slack, rather than fixed: a hardcoded # size is a thing that breaks quietly the first time the kernel grows. EFI_IMG="$STAGING/boot/efiboot.img" @@ -146,6 +145,15 @@ if true; then mcopy -i "$EFI_IMG" "$KERNEL" ::/schos/kernel fi +# The sysroot, as an ordinary hierarchy. Copied rather than linked so that +# whatever ends up on the disc is a snapshot, not a view of a tree that may be +# rebuilt underneath it. +if [ -d "$SYSROOT" ]; then + for d in "$SYSROOT"/*; do + [ -e "$d" ] || continue + cp -a "$d" "$STAGING/" + done +fi # xorriso if it is here, mkisofs otherwise. Both accept the El Torito flags # below unchanged -- xorriso's -as mkisofs mode exists precisely to be @@ -165,11 +173,6 @@ if command -v xorriso >/dev/null 2>&1; then # of type 0xEF is what every UEFI firmware looks for on a removable # disk, MBR rather than GPT because the oldest ones read that best. HYBRID="-append_partition 2 0xef $EFI_IMG" - # El Torito keeps its own copy of the image in the tree, the layout real - # machines have booted. Pointing it into the partition instead - # (--interval:appended_partition_2:all::, as Arch does) saves a copy, and - # an ASUS laptop stopped listing the stick the day that was tried. With - # the root compressed (SCFF) the copy is only the kernel's size. elif command -v mkisofs >/dev/null 2>&1; then MKISOFS="mkisofs" HYBRID="" diff --git a/tools/windows/bootcheck.ps1 b/tools/windows/bootcheck.ps1 index f94c304..32645cb 100644 --- a/tools/windows/bootcheck.ps1 +++ b/tools/windows/bootcheck.ps1 @@ -495,11 +495,8 @@ try { Start-Sleep -Milliseconds 150 $t = Read-Log if ($null -eq $t -or $t.Length -le $Since) { continue } - # A prompt at the very end, with nothing typed after it: mksh's `#`, - # or zsh's `»`, which root's shell has been since the root became - # Alpine's. zsh follows it with erase-to-end-of-line and turns - # bracketed paste on, so both escapes may trail the prompt. - if ($t.Substring($Since) -match '(?s)(#|\u00bb)(\e\[[0-9;]*m)*[ ]?(\e\[K)?(\e\[\?2004h)?$') { return $true } + # A prompt at the very end, with nothing typed after it. + if ($t.Substring($Since) -match '(?s)#[ ]?$') { return $true } } return $false } @@ -632,12 +629,6 @@ if ($null -eq $text) { $text = '' } Write-Host "==> serial transcript ($($text.Length) bytes) -> $log" -ForegroundColor Cyan Write-Host $text -# Matched without terminal escapes. busybox's ls, which is the Alpine root's, -# colours what it lists -- `copy.txt` arrives as ESC[1;32mcopy.txt -- and zsh -# wraps every prompt in them, so a pattern anchored at a line start found -# nothing in output that was perfectly right. Shown raw above, matched clean. -$text = $text -replace '\e\[[0-9;?]*[A-Za-z]', '' -replace '\e[()][0-9A-Za-z]', '' -replace '[\x0e\x0f]', '' - $script:fail = $false function Expect($pattern, $what) { if ($script:text -match $pattern) { @@ -706,10 +697,7 @@ Expect 'schfs: mounted' 'the kernel mounted SchFS as the root filesystem' Expect 'init: ready' 'init came up on a SchFS root' Expect 'label:\s*schos' '/proc/schfs reports the volume label' Expect 'root_objectid:\s*16' '/proc/schfs reports the root object id' -# /proc/schfs lists the first 16 entries, in the tree's order, and on the -# Alpine root bin/ is the seventeenth: any of the top-level directories proves -# the same thing. -Expect '(?m)^\s*dir\s+(bin|etc|usr)\s' 'the root directory really contains bin/, etc/ or usr/' +Expect 'dir\s+bin' 'the root directory really contains bin/' # / is the mounted volume, so mounting that same device again must be # refused. Without this a kernel that had quietly fallen back to some other # root would still pass everything else here.