Compare commits
52 changed files with 202 additions and 2761 deletions
150
Makefile
150
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.
|
||||
#
|
||||
|
|
|
|||
40
NOTICE
40
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
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -25,11 +25,6 @@ struct file {
|
|||
int memfd_seg;
|
||||
struct evobj *ev;
|
||||
uint64 off;
|
||||
// What /proc/<pid>/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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <stdint.h>
|
||||
|
||||
#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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <uacpi/uacpi.h>
|
||||
#include <uacpi/utilities.h>
|
||||
#include <uacpi/namespace.h>
|
||||
#include <uacpi/resources.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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");
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/<pid>/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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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/<pid>/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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
// (pid << 8) | fileid a file inside it
|
||||
// (pid << 8) | 0x80 | fd /proc/<pid>/fd/<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/<pid>/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/<pid>/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/<pid>/.. is /proc
|
||||
return iget(dp->dev, INO_FILE(dp->inum) == PF_FD ? (dp->inum & ~0xFFULL)
|
||||
: PROCFS_ROOT);
|
||||
|
||||
// Inside /proc/<pid>/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/<pid>/.. is /proc
|
||||
|
||||
// Inside /proc/<pid>/
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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/<pid>/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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 <stddef.h>
|
||||
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
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// What zstd asks of <string.h>, 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 <stddef.h>
|
||||
void *memcpy(void *, const void *, unsigned int);
|
||||
void *memmove(void *, const void *, unsigned long);
|
||||
void *memset(void *, int, unsigned long);
|
||||
#endif
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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/<pid>/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)
|
||||
|
|
|
|||
|
|
@ -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/<pid>/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/<pid>/fd` and every program
|
||||
// asking "is my stdin a socket?" look at. ino matches /proc/<pid>/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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
190
tools/mkscff.c
190
tools/mkscff.c
|
|
@ -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 <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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=""
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue