Compare commits

...

No commits in common. "v5.x-branch-binary" and "v5.x-branch" have entirely different histories.

212 changed files with 28115 additions and 1895 deletions

31
.github/workflows/check.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Check for compilation failures
on: [push, pull_request]
jobs:
build:
name: Check for compilation failures
runs-on: ubuntu-latest
container: archlinux:latest
steps:
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S base-devel git autoconf automake nasm curl mtools llvm clang lld aarch64-linux-gnu-gcc riscv64-linux-gnu-gcc
- name: Checkout code
uses: actions/checkout@v3
- name: Git config
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Build the bootloader (LLVM)
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=llvm --enable-werror --enable-all && make all && make maintainer-clean
- name: Build the bootloader (GNU, x86)
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=gnu --enable-werror --enable-bios --enable-uefi-ia32 --enable-uefi-x86-64 && make all && make maintainer-clean
- name: Build the bootloader (GNU, aarch64)
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=aarch64-linux-gnu --enable-werror --enable-uefi-aarch64 && make all && make maintainer-clean
- name: Build the bootloader (GNU, riscv64)
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=riscv64-linux-gnu --enable-werror --enable-uefi-riscv64 && make all && make maintainer-clean

97
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,97 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
build:
name: Build and upload artifacts
runs-on: ubuntu-latest
container: archlinux:latest
steps:
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S base-devel git autoconf automake nasm curl mtools llvm clang lld mingw-w64-gcc
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: '0'
- name: Git config
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Get tag name
run: echo "TAG_NAME=$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" >> $GITHUB_ENV
- name: Get branch name
run: echo "BRANCH_NAME=$(echo "$TAG_NAME" | grep -o 'v[0-9]\+\.')x-branch" >> $GITHUB_ENV
- name: Regenerate
run: ./bootstrap
- name: Create build dir
run: mkdir -p build
- name: Configure
run: cd build && ../configure --enable-all TOOLCHAIN_FOR_TARGET=llvm
- name: Build the bootloader
run: make -C build
- name: Clean limine
run: rm build/bin/limine
- name: Build limine for Windows
run: make -C build/bin CC="i686-w64-mingw32-gcc" CFLAGS="-O2 -pipe" CPPFLAGS="-D__USE_MINGW_ANSI_STDIO" limine
- name: Strip limine for Windows
run: i686-w64-mingw32-strip build/bin/limine.exe
- name: Copy LICENSE to bin
run: cp COPYING build/bin/LICENSE
- name: Copy install-sh to bin
run: cp build-aux/install-sh build/bin/
- name: Copy limine.h to bin
run: cp limine.h build/bin/
- name: Remove limine-bios-hdd.bin
run: rm build/bin/limine-bios-hdd.bin
- name: Push binaries to binary branch
run: |
git config user.name 'mintsuki'
git config user.email 'mintsuki@users.noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/limine-bootloader/limine.git
git fetch --all
git checkout $BRANCH_NAME-binary || git checkout --orphan $BRANCH_NAME-binary
rm -rf $(ls -a | grep -v '^\.git$' | grep -v '^\.\.$' | grep -v '^\.$' | grep -v '^build$')
cp -r build/bin/. ./
rm -rf build
git add -f .
git commit -m "Binary release $TAG_NAME"
git push origin $BRANCH_NAME-binary
git tag $TAG_NAME-binary
git push origin $BRANCH_NAME-binary --tags
- name: Cleanup source tree
run: git checkout $TAG_NAME && rm -rf * && git checkout .
- name: Package release tarball
run: ./bootstrap && ./configure --enable-all && make dist
- name: Create release notes
run: echo "Binary release can be found at https://github.com/limine-bootloader/limine/tree/$TAG_NAME-binary" > rel_notes.txt
- name: Release
uses: softprops/action-gh-release@v1
with:
body_path: rel_notes.txt
files: |
limine-*.tar.*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

50
.gitignore vendored
View File

@ -1,2 +1,48 @@
limine
limine.exe
/bin
/build
/toolchain-files
/limine-efi
/freestanding-headers
/libgcc-binaries
/common/flanterm
/common/stb/stb_image.h
/decompressor/tinf
/ovmf*
*.o
*.d
*.a
*.exe
*.EFI
*.bin
*.bin.gz
*.tar.xz
*.tar.gz
*.elf
*.hdd
*.iso
*.sys
/bochsout.txt
/bx_enh_dbg.ini
/.vscode
/test_image
!/common/font.bin
/freestanding-toolchain
/configure
/configure.ac.save
/INSTALL
/build-aux
/aclocal.m4
/*~
/config.status
/config.log
/autom4te.cache
/man/man1/limine.1
/GNUmakefile
/config.h
/common-bios
/common-uefi-ia32
/common-uefi-x86-64
/common-uefi-aarch64
/common-uefi-riscv64
/decompressor-build
/stage1.stamp

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

202
CONFIG.md Normal file
View File

@ -0,0 +1,202 @@
# Limine configuration file
## Location of the config file
Limine scans for a config file on *the boot drive*. Every partition on the boot drive
is scanned sequentially - first partition first (or, on UEFI, the partition containing the
EFI executable of the booted Limine is scanned first), last partition last - for the presence
of either a `/limine.cfg`, `/limine/limine.cfg`, `/boot/limine.cfg`, `/boot/limine/limine.cfg`,
or a `/EFI/BOOT/limine.cfg` file, in that order.
Once the file is located, Limine will use it as its config file. Other possible
candidates in subsequent partitions or directories are ignored.
It is thus imperative that the intended config file is placed in a location that will
not be shadowed by another potentially candidate config file.
## Structure of the config file
The Limine configuration file is comprised of *assignments* and *entries*.
Comments begin in '#'.
### Entries and sub-entries
*Entries* describe boot *entries* which the user can select in the *boot menu*.
An *entry* is simply a line starting with `:` followed by a newline-terminated
string.
Any *locally assignable* key that comes after it, and before another *entry*, or
the end of the file, will be tied to the *entry*.
An *entry* can be a directory, meaning it can hold sub-entries. In order for an
entry to become a directory, it needs to have a sub-entry following right after it.
A *sub-entry* is an entry with a number of `:` greater than 1 prepended to it.
Each `:` represents 1 level deeper down the tree hierarchy of directories and
entries.
Directories can be expanded (meaning they will not show up as collapsed in the
menu) by default if a `+` is put between the `:`s and the beginning of the entry's name.
### Assignments
*Assignments* are simple `KEY=VALUE` style assignments.
`VALUE` can have spaces and `=` symbols, without requiring quotations. New lines
are delimiters.
Some *assignments* are part of an entry (*local*), some other assignments are *global*.
*Global assignments* can appear anywhere in the file and are not part of an entry,
although usually one would put them at the beginning of the config.
Some *local assignments* are shared between entries using any *protocol*, while other
*local assignments* are specific to a given *protocol*.
Some keys take *URIs* as values; these are described in the next section.
*Globally assignable* keys are:
* `TIMEOUT` - Specifies the timeout in seconds before the first *entry* is automatically booted. If set to `no`, disable automatic boot. If set to `0`, boots default entry instantly (see `DEFAULT_ENTRY` key).
* `QUIET` - If set to `yes`, enable quiet mode, where all screen output except panics and important warnings is suppressed. If `TIMEOUT` is not 0, the `TIMEOUT` still occurs, and pressing any key during the timeout will reveal the menu and disable quiet mode.
* `SERIAL` - If set to `yes`, enable serial I/O for the bootloader.
* `DEFAULT_ENTRY` - 1-based entry index of the entry which will be automatically selected at startup. If unspecified, it is `1`.
* `GRAPHICS` - If set to `no`, force CGA text mode for the boot menu, else use a video mode. Ignored with Limine UEFI.
* `VERBOSE` - If set to `yes`, print additional information during boot. Defaults to not verbose.
* `RANDOMISE_MEMORY` - If set to `yes`, randomise the contents of RAM at bootup in order to find bugs related to non zeroed memory or for security reasons. This option will slow down boot time significantly. For the BIOS port of Limine, this will only randomise memory below 4GiB.
* `RANDOMIZE_MEMORY` - Alias of `RANDOMISE_MEMORY`.
* `HASH_MISMATCH_PANIC` - If set to `no`, do not panic if there is a hash mismatch for a file, but print a warning instead.
Limine interface control options.
* `INTERFACE_RESOLUTION` - Specify screen resolution to be used by the Limine interface (menu, editor, console...) in the form `<width>x<height>`. This will *only* affect the Limine interface, not any booted OS. If not specified, Limine will pick a resolution automatically. If the resolution is not available, Limine will pick another one automatically. Ignored if using text mode.
* `INTERFACE_BRANDING` - A string that will be displayed on top of the Limine interface.
* `INTERFACE_BRANDING_COLOUR` - A value between 0 and 7 specifying the colour of the branding string. Default is cyan (6).
* `INTERFACE_BRANDING_COLOR` - Alias of `INTERFACE_BRANDING_COLOUR`.
* `INTERFACE_HELP_HIDDEN` - Hides the help text located at the top of the screen showing the key bindings.
Limine graphical terminal control options. They are ignored if using text mode.
* `TERM_FONT` - URI path to a font file to be used instead of the default one for the menu and terminal. The font file must be a code page 437 character set comprised of 256 consecutive glyph bitmaps. Each glyph's bitmap must be expressed left to right (1 byte per row), and top to bottom (16 bytes per whole glyph by default; see `TERM_FONT_SIZE`). See e.g. the [VGA text mode font collection](https://github.com/viler-int10h/vga-text-mode-fonts) for fonts.
* `TERM_FONT_SIZE` - The size of the font in dots, which must correspond to the font file or the display will be garbled. Note that glyphs are always one byte wide, and columns over 8 are empty. Many fonts may be used in both 8- and 9-dot wide variants. Defaults to `8x16`. Ignored if `TERM_FONT` not set or if the font fails to load.
* `TERM_FONT_SCALE` - Scaling for the font in the x and y directions. `2x2` would display the font in double size, which is useful on high-DPI displays at native resolution. `2x1` only makes the font twice as wide, similar to the VGA 40 column mode. `4x2` might be good for a narrow font on a high resolution display. Values over 8 are disallowed. Default is no scaling, i.e. `1x1`.
* `TERM_FONT_SPACING` - Horizontal spacing, in pixels, between glyphs on screen. It is equivalent to setting a font width of `<specified width>+<this value>`, except this value is preserved even in case font loading fails, and it also applies to the built-in Limine font. Defaults to 1. 0 is allowed.
* `TERM_PALETTE` - Specifies the colour palette used by the terminal (RRGGBB). It is a `;` separated array of 8 colours: black, red, green, brown, blue, magenta, cyan, and gray. Ignored if not using a graphical terminal.
* `TERM_PALETTE_BRIGHT` - Specifies the bright colour palette used by the terminal (RRGGBB). It is a `;` separated array of 8 bright colours: dark gray, bright red, bright green, yellow, bright blue, bright magenta, bright cyan, and white. Ignored if not using a graphical terminal.
* `TERM_BACKGROUND` - Terminal text background colour (TTRRGGBB). TT stands for transparency.
* `TERM_FOREGROUND` - Terminal text foreground colour (RRGGBB).
* `TERM_BACKGROUND_BRIGHT` - Terminal text background bright colour (RRGGBB).
* `TERM_FOREGROUND_BRIGHT` - Terminal text foreground bright colour (RRGGBB).
* `TERM_MARGIN` - Set the amount of margin around the terminal.
* `TERM_MARGIN_GRADIENT` - Set the thickness in pixel for the gradient around the terminal.
* `TERM_WALLPAPER` - URI where to find the file to use as wallpaper. BMP, PNG, and JPEG formats are supported.
* `TERM_WALLPAPER_STYLE` - The style which will be used to display the wallpaper image: `tiled`, `centered`, or `stretched`. Default is `stretched`.
* `TERM_BACKDROP` - When the background style is `centered`, this specifies the colour of the backdrop for parts of the screen not covered by the background image, in RRGGBB format.
Editor control options.
* `EDITOR_ENABLED` - If set to `no`, the editor will not be accessible. Defaults to `yes`.
* `EDITOR_HIGHLIGHTING` - If set to `no`, syntax highlighting in the editor will be disabled. Defaults to `yes`.
* `EDITOR_VALIDATION` - If set to `no`, the editor will not alert you about invalid keys / syntax errors. Defaults to `yes`.
*Locally assignable (non protocol specific)* keys are:
* `COMMENT` - An optional comment string that will be displayed by the bootloader on the menu when an entry is selected.
* `PROTOCOL` - The boot protocol that will be used to boot the kernel. Valid protocols are: `linux`, `limine`, `chainload`, `chainload_next`, `multiboot` (or `multiboot1`), and `multiboot2`.
* `CMDLINE` - The command line string to be passed to the kernel/executable. Can be omitted.
* `KERNEL_CMDLINE` - Alias of `CMDLINE`.
*Locally assignable (protocol specific)* keys are:
* Linux protocol:
* `KERNEL_PATH` - The URI path of the kernel.
* `MODULE_PATH` - The URI path to a module (such as initramfs).
Note that one can define this last variable multiple times to specify multiple
modules.
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
* `TEXTMODE` - If set to `yes`, prefer text mode. (BIOS only)
* Limine protocol:
* `KERNEL_PATH` - The URI path of the kernel.
* `MODULE_PATH` - The URI path to a module.
* `MODULE_CMDLINE` - A command line to be passed to a module.
**Note:** One can define these 2 last variable multiple times to specify multiple
modules.
The entries will be matched in order. E.g.: The 1st module path entry will be matched
to the 1st module string entry that appear, and so on.
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
* `KASLR` - For relocatable kernels, if set to `no`, disable kernel address space layout randomisation. KASLR is enabled by default.
* `TERM_CONFIG_OVERRIDE` - If set to `yes`, override the terminal configuration for this entry. Resets all the `TERM_*` assignments to default and allows setting them anew inside the entry. Note that without this, Limine will never look for terminal configuration settings inside entries.
* Chainload protocol on BIOS:
* `DRIVE` - The 1-based drive to chainload, if omitted, assume boot drive.
* `PARTITION` - The 1-based partition to chainload, if omitted, or set to 0, chainload drive (MBR).
* `MBR_ID` - Optional. If passed, use an MBR ID (32-bit hex value) to identify the drive containing the volume to chainload. Overrides `DRIVE`, if present, but does *not* override `PARTITION`.
* `GPT_UUID` or `GPT_GUID` - Optional. If passed, use the GPT GUID to identify the drive containing the volume to chainload. Overrides `DRIVE` and `MBR_ID`, if present, but does *not* override `PARTITION`.
* Chainload protocol on UEFI:
* `IMAGE_PATH` - URI of the EFI application to chainload.
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
* chainload_next protocol:
This protocol does not specify any locally assignable key on BIOS. Will boot the next bootable drive found in the system, if there is one.
* `RESOLUTION` - For UEFI, the resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
* multiboot1 and multiboot2 protocols:
* `KERNEL_PATH` - The URI path of the kernel.
* `MODULE_PATH` - The URI path to a module.
* `MODULE_STRING` - A string to be passed to a module.
**Note:** One can define these 2 last variable multiple times to specify multiple
modules.
The entries will be matched in order. E.g.: the 1st module path entry will be matched
to the 1st module string entry that appear, and so on.
* `RESOLUTION` - The resolution to be used should the kernel request a graphical framebuffer. This setting takes the form of `<width>x<height>x<bpp>` and *overrides* any resolution requested by the kernel. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
* `TEXTMODE` - If set to `yes`, prefer text mode. (BIOS only)
## URIs
A URI is a path that Limine uses to locate resources in the whole system. It is
comprised of a *resource*, a *root*, and a *path*. It takes the form of:
```
resource://root/path
```
The format for `root` changes depending on the resource used.
A resource can be one of the following:
* `boot` - If booted off PXE this is an alias of `tftp`. Else the `root` is the 1-based decimal value representing the partition on the boot drive (values of 5+ for MBR logical partitions). If omitted, the partition containing the configuration file on the boot drive is used. For example: `boot://2/...` will use partition 2 of the boot drive and `boot:///...` will use the partition containing the config file on the boot drive.
* `hdd` - Hard disk drives. The `root` takes the form of `drive:partition`; for example: `hdd://3:1/...` would use hard drive 3, partition 1. Partitions and drives are both 1-based (partition values of 5+ for MBR logical partitions). Omitting the partition is possible; for example: `hdd://2:/...`. Omitting the partition will access the entire volume instead of a specific partition (useful for unpartitioned media).
* `odd` - Optical disk drives (CDs/DVDs/...). The `root` takes the form of `drive:partition`; for example: `odd://3:1/...` would use optical drive 3, partition 1. Partitions and drives are both 1-based (partition values of 5+ for MBR logical partitions). Omitting the partition is possible; for example: `odd://2:/...`. Omitting the partition will access the entire volume instead of a specific partition (useful for unpartitioned media, which is often the case for optical media).
* `guid` - The `root` takes the form of a GUID/UUID, such as `guid://736b5698-5ae1-4dff-be2c-ef8f44a61c52/...`. The GUID is that of either a filesystem, when available, or a GPT partition GUID, when using GPT, in a unified namespace.
* `uuid` - Alias of `guid`.
* `fslabel` - The `root` is the name of the filesystem label of a partition.
* `tftp` - The `root` is the IP address of the tftp server to load the file from. If the root is left empty (`tftp:///...`) the file will be loaded from the server Limine booted from. This resource is only available when booting off PXE.
A URI can optionally be suffixed with a blake2b hash for the referenced file,
by appending a pound character (`#`) followed by the blake2b hash.
E.g.: `boot:///somemodule.gz#ca6914d2...446b470a`.
A URI can optionally be prefixed by a `$` character to indicate that the file
pointed to be the URI is a gzip-compressed payload to be uncompressed on the
fly. E.g.: `$boot:///somemodule.gz`.
## Macros
Macros are strings that can be arbitrarily assigned to represent other strings. For example:
```
${MY_MACRO}=Some text
```
Now, whenever `${MY_MACRO}` is used in the config file (except for an assignment as above), it will
be replaced by the text `Some text`. For example:
```
CMDLINE=something before ${MY_MACRO} something after
```
Macros must always be placed inside `${...}` where `...` is the arbitrary macro name.
### Built-in macros
Limine automatically defines these macros:
* `ARCH` - This built-in macro expands to the architecture of the machine. Possible values are: `x86-64`, `ia-32`, `aarch64`, `riscv64`. In the case of IA-32, BIOS or UEFI, the macro will always expand to `x86-64` if the 64-bit extensions are available, else `ia-32`.

View File

396
GNUmakefile.in Normal file
View File

@ -0,0 +1,396 @@
override MAKEFLAGS += -rR
override prefix := @prefix@
override exec_prefix := @exec_prefix@
override PACKAGE_TARNAME := @PACKAGE_TARNAME@
override bindir := @bindir@
override datarootdir := @datarootdir@
override includedir := @includedir@
override datarootdir := @datarootdir@
override mandir := @mandir@
override docdir := @docdir@
override STRIP := @STRIP@
DESTDIR ?=
override BUILDDIR := @BUILDDIR@
override BINDIR := $(BUILDDIR)/bin
override SRCDIR := @SRCDIR@
override SPACE := $(subst ,, )
override COMMA := ,
MKESCAPE = $(subst $(SPACE),\ ,$(1))
SHESCAPE = $(subst ','\'',$(1))
NASMESCAPE = $(subst ','"'$(COMMA) \"'\"$(COMMA) '"',$(1))
define DEFAULT_VAR =
ifeq ($(origin $1),default)
override $(1) := $(2)
endif
ifeq ($(origin $1),undefined)
override $(1) := $(2)
endif
endef
override BUILD_BIOS := @BUILD_BIOS@
override BUILD_UEFI_X86_64 := @BUILD_UEFI_X86_64@
override BUILD_UEFI_IA32 := @BUILD_UEFI_IA32@
override BUILD_UEFI_AARCH64 := @BUILD_UEFI_AARCH64@
override BUILD_UEFI_RISCV64 := @BUILD_UEFI_RISCV64@
override BUILD_UEFI_CD := @BUILD_UEFI_CD@
override BUILD_BIOS_PXE := @BUILD_BIOS_PXE@
override BUILD_BIOS_CD := @BUILD_BIOS_CD@
override DEFAULT_CC := @CC@
$(eval $(call DEFAULT_VAR,CC,$(DEFAULT_CC)))
export CC
override MKDIR_P := @MKDIR_P@
export MKDIR_P
override INSTALL := @INSTALL@
export INSTALL
override INSTALL_PROGRAM := @INSTALL_PROGRAM@
export INSTALL_PROGRAM
override INSTALL_DATA := @INSTALL_DATA@
export INSTALL_DATA
override GREP := @GREP@
export GREP
override SED := @SED@
export SED
override AWK := @AWK@
export AWK
override DEFAULT_CPPFLAGS := @CPPFLAGS@
$(eval $(call DEFAULT_VAR,CPPFLAGS,$(DEFAULT_CPPFLAGS)))
override CPPFLAGS := @PKGCONF_CPPFLAGS@ $(CPPFLAGS)
export CPPFLAGS
override DEFAULT_CFLAGS := @CFLAGS@
$(eval $(call DEFAULT_VAR,CFLAGS,$(DEFAULT_CFLAGS)))
override CFLAGS += @PKGCONF_CFLAGS@
export CFLAGS
override DEFAULT_LDFLAGS := @LDFLAGS@
$(eval $(call DEFAULT_VAR,LDFLAGS,$(DEFAULT_LDFLAGS)))
export LDFLAGS
override DEFAULT_LIBS := @LIBS@
$(eval $(call DEFAULT_VAR,LIBS,$(DEFAULT_LIBS)))
override LIBS += @PKGCONF_LIBS@
export LIBS
override WERROR_FLAG := @WERROR_FLAG@
export WERROR_FLAG
override DEFAULT_CFLAGS_FOR_TARGET := @CFLAGS_FOR_TARGET@
$(eval $(call DEFAULT_VAR,CFLAGS_FOR_TARGET,$(DEFAULT_CFLAGS_FOR_TARGET)))
export CFLAGS_FOR_TARGET
override DEFAULT_CPPFLAGS_FOR_TARGET := @CPPFLAGS_FOR_TARGET@
$(eval $(call DEFAULT_VAR,CPPFLAGS_FOR_TARGET,$(DEFAULT_CPPFLAGS_FOR_TARGET)))
export CPPFLAGS_FOR_TARGET
override DEFAULT_LDFLAGS_FOR_TARGET := @LDFLAGS_FOR_TARGET@
$(eval $(call DEFAULT_VAR,LDFLAGS_FOR_TARGET,$(DEFAULT_LDFLAGS_FOR_TARGET)))
export LDFLAGS_FOR_TARGET
override LIMINE_VERSION := @PACKAGE_VERSION@
override STAGE1_FILES := $(shell find '$(call SHESCAPE,$(SRCDIR))/stage1' -type f -name '*.asm')
.PHONY: all
all: $(call MKESCAPE,$(BINDIR))/Makefile
$(MAKE) all1
.PHONY: all1
all1: $(BUILD_UEFI_X86_64) $(BUILD_UEFI_IA32) $(BUILD_UEFI_AARCH64) $(BUILD_UEFI_RISCV64) $(BUILD_BIOS)
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine'
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
$(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h: $(call MKESCAPE,$(BINDIR))/limine-bios-hdd.bin
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cd '$(call SHESCAPE,$(BINDIR))' && '$(call SHESCAPE,$(SRCDIR))/host/hgen.sh' >limine-bios-hdd.h
ifeq ($(BUILD_BIOS),limine-bios)
$(call MKESCAPE,$(BINDIR))/limine: $(call MKESCAPE,$(BINDIR))/Makefile $(call MKESCAPE,$(SRCDIR))/host/limine.c $(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h
else
override LIMINE_NO_BIOS := -DLIMINE_NO_BIOS
$(call MKESCAPE,$(BINDIR))/limine: $(call MKESCAPE,$(BINDIR))/Makefile $(call MKESCAPE,$(SRCDIR))/host/limine.c
endif
$(SED) 's/%VERSION%/@PACKAGE_VERSION@/g;s/%COPYRIGHT%/@LIMINE_COPYRIGHT@/g' <'$(call SHESCAPE,$(SRCDIR))/host/limine.c' >'$(call SHESCAPE,$(BINDIR))/limine.c'
CPPFLAGS="$(CPPFLAGS) $(LIMINE_NO_BIOS)" $(MAKE) -C '$(call SHESCAPE,$(BINDIR))' limine
$(call MKESCAPE,$(BINDIR))/Makefile: $(call MKESCAPE,$(SRCDIR))/host/Makefile $(call MKESCAPE,$(SRCDIR))/host/.gitignore
mkdir -p '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(SRCDIR))/host/Makefile' '$(call SHESCAPE,$(SRCDIR))/host/.gitignore' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine
limine:
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine'
.PHONY: clean
clean: limine-bios-clean limine-uefi-ia32-clean limine-uefi-x86-64-clean limine-uefi-aarch64-clean limine-uefi-riscv64-clean
rm -rf '$(call SHESCAPE,$(BINDIR))' '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
.PHONY: install
install: all
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(docdir))'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/COPYING' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/CONFIG.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/PROTOCOL.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/PHILOSOPHY.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/README.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1'
$(INSTALL_DATA) '$(call SHESCAPE,$(BUILDDIR))/man/man1/limine.1' '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/'
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(datarootdir))'
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine'
ifeq ($(BUILD_BIOS),limine-bios)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios.sys' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifneq ($(BUILD_BIOS_CD),no)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios-cd.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifneq ($(BUILD_UEFI_CD),no)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifneq ($(BUILD_BIOS_PXE),no)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios-pxe.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifeq ($(BUILD_UEFI_AARCH64),limine-uefi-aarch64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifeq ($(BUILD_UEFI_RISCV64),limine-uefi-riscv64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifeq ($(BUILD_UEFI_X86_64),limine-uefi-x86-64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
ifeq ($(BUILD_UEFI_IA32),limine-uefi-ia32)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTIA32.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
endif
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(includedir))'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/limine.h' '$(call SHESCAPE,$(DESTDIR)$(includedir))/'
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(bindir))'
$(INSTALL_PROGRAM) '$(call SHESCAPE,$(BINDIR))/limine' '$(call SHESCAPE,$(DESTDIR)$(bindir))/'
.PHONY: install-strip
install-strip: install
$(STRIP) '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine'
.PHONY: uninstall
uninstall:
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/COPYING'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/CONFIG.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/PROTOCOL.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/PHILOSOPHY.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/README.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/limine.1'
rm -f '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine'
rm -rf '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine'
rm -f '$(call SHESCAPE,$(DESTDIR)$(includedir))/limine.h'
$(call MKESCAPE,$(BUILDDIR))/stage1.stamp: $(STAGE1_FILES) $(call MKESCAPE,$(BUILDDIR))/decompressor-build/decompressor.bin $(call MKESCAPE,$(BUILDDIR))/common-bios/stage2.bin.gz
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cd '$(call SHESCAPE,$(SRCDIR))/stage1/hdd' && nasm bootsect.asm -Wall $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-hdd.bin'
ifneq ($(BUILD_BIOS_CD),no)
cd '$(call SHESCAPE,$(SRCDIR))/stage1/cd' && nasm bootsect.asm -Wall $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-cd.bin'
endif
ifneq ($(BUILD_BIOS_PXE),no)
cd '$(call SHESCAPE,$(SRCDIR))/stage1/pxe' && nasm bootsect.asm -Wall $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-pxe.bin'
endif
cp '$(call SHESCAPE,$(BUILDDIR))/common-bios/limine-bios.sys' '$(call SHESCAPE,$(BINDIR))/'
touch '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
.PHONY: limine-bios
limine-bios: common-bios decompressor
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
$(call MKESCAPE,$(BINDIR))/limine-uefi-cd.bin: $(if $(BUILD_UEFI_IA32),$(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI) $(if $(BUILD_UEFI_X86_64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI) $(if $(BUILD_UEFI_AARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI) $(if $(BUILD_UEFI_RISCV64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI)
ifneq ($(BUILD_UEFI_CD),no)
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
rm -f '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
dd if=/dev/zero of='$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' bs=512 count=2880 2>/dev/null
( mformat -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' -f 1440 :: && \
mmd -D s -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' ::/EFI && \
mmd -D s -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' ::/EFI/BOOT && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' ::/EFI/BOOT ) || true ) && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' ::/EFI/BOOT ) || true ) && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' ::/EFI/BOOT ) || true ) && \
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' ] && \
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' ::/EFI/BOOT ) || true ) \
) || rm -f '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
endif
.PHONY: limine-uefi-cd
limine-uefi-cd:
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
$(call MKESCAPE,$(BINDIR))/BOOTX64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine-uefi-x86-64
limine-uefi-x86-64:
$(MAKE) common-uefi-x86-64
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI'
$(call MKESCAPE,$(BINDIR))/BOOTIA32.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine-uefi-ia32
limine-uefi-ia32:
$(MAKE) common-uefi-ia32
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTIA32.EFI'
$(call MKESCAPE,$(BINDIR))/BOOTAA64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine-uefi-aarch64
limine-uefi-aarch64:
$(MAKE) common-uefi-aarch64
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI'
$(call MKESCAPE,$(BINDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine-uefi-riscv64
limine-uefi-riscv64:
$(MAKE) common-uefi-riscv64
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI'
.PHONY: limine-bios-clean
limine-bios-clean: common-bios-clean decompressor-clean
.PHONY: limine-uefi-x86-64-clean
limine-uefi-x86-64-clean: common-uefi-x86-64-clean
.PHONY: limine-uefi-ia32-clean
limine-uefi-ia32-clean: common-uefi-ia32-clean
.PHONY: limine-uefi-aarch64-clean
limine-uefi-aarch64-clean: common-uefi-aarch64-clean
.PHONY: limine-uefi-riscv64-clean
limine-uefi-riscv64-clean: common-uefi-riscv64-clean
.PHONY: dist
dist:
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
cp -r '$(call SHESCAPE,$(SRCDIR))'/.git '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"/
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && git checkout .
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && git log --oneline --decorate > ChangeLog
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && ./bootstrap
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/common/flanterm/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/freestanding-headers/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.gitignore"
libgcc_needed="i686 x86_64-no-red-zone aarch64 riscv64-softfloat"; \
for f in $$libgcc_needed; do \
mv '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a" '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a.save"; \
done; \
rm '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries"/*.a; \
for f in $$libgcc_needed; do \
mv '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a.save" '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a"; \
done
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/limine-efi/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/limine-efi/.gitignore"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/.gitignore"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/.github"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/autom4te.cache"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/test"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/test.mk"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/screenshot.png"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/bochsrc"
echo "$(LIMINE_VERSION)" > '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/version"
cd '$(call SHESCAPE,$(BUILDDIR))' && tar -Jcf "limine-$(LIMINE_VERSION).tar.xz" "limine-$(LIMINE_VERSION)"
cd '$(call SHESCAPE,$(BUILDDIR))' && tar -zcf "limine-$(LIMINE_VERSION).tar.gz" "limine-$(LIMINE_VERSION)"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
.PHONY: distclean
distclean: clean
rm -rf ovmf* config.log config.status GNUmakefile config.h toolchain-files man/man1/limine.1
.PHONY: maintainer-clean
maintainer-clean: distclean
cd '$(call SHESCAPE,$(SRCDIR))' && rm -rf common/flanterm common/stb/stb_image.h decompressor/tinf freestanding-headers libgcc-binaries limine-efi freestanding-toolchain configure INSTALL build-aux *'~' autom4te.cache aclocal.m4 *.tar.xz *.tar.gz
.PHONY: common-uefi-x86-64
common-uefi-x86-64:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-x86_64-toolchain.mk' \
TARGET=uefi-x86-64 \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64'
.PHONY: common-uefi-x86-64-clean
common-uefi-x86-64-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64'
.PHONY: common-uefi-aarch64
common-uefi-aarch64:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-aarch64-toolchain.mk' \
TARGET=uefi-aarch64 \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64'
.PHONY: common-uefi-aarch64-clean
common-uefi-aarch64-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64'
.PHONY: common-uefi-riscv64
common-uefi-riscv64:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-riscv64-toolchain.mk' \
TARGET=uefi-riscv64 \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64'
.PHONY: common-uefi-riscv64-clean
common-uefi-riscv64-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64'
.PHONY: common-uefi-ia32
common-uefi-ia32:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-i686-toolchain.mk' \
TARGET=uefi-ia32 \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32'
.PHONY: common-uefi-ia32-clean
common-uefi-ia32-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32'
.PHONY: common-bios
common-bios:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/bios-i686-toolchain.mk' \
TARGET=bios \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-bios'
.PHONY: common-bios-clean
common-bios-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-bios'
.PHONY: decompressor
decompressor:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/decompressor' all \
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/bios-i686-toolchain.mk' \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/decompressor-build'
.PHONY: decompressor-clean
decompressor-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/decompressor-build'
-include test.mk

31
PHILOSOPHY.md Normal file
View File

@ -0,0 +1,31 @@
# Limine's Design Philosophy
### Why not support filesystem X or feature Y? (eg: LUKS, LVM)
The idea with Limine is to remove the responsibility of parsing filesystems and formats, aside from the bare minimum necessities (eg: FAT*, ISO9660),
from the bootloader itself.
It is a needless duplication of efforts to have bootloaders support all possible filesystems and formats, and it leads to massive, bloated
bootloaders as a result (eg: GRUB2).
What is needed is to simply make sure the bootloader is capable of reading its own files, configuration, and be able to load kernel/module files
from disk. The kernel should be responsible for parsing everything else as it sees fit.
### What about LUKS? What about security? Encrypt the kernel!
Simply put, this is unnecessary. Putting the kernel/modules in a readable FAT32 partition and letting Limine know about their BLAKE2B checksums
in the config file provides as much security as encrypting the kernel does.
### What? But what if someone modifies the config file! Ha! You clearly have not thought about that!
We have. While this is a pointless effort on legacy x86 BIOS, it is a reasonable expectation on UEFI systems with Secure Boot. Limine provides a
way to modify its own EFI executable to bake in the BLAKE2B checksum of the config file itself. The EFI executable can then get signed with
a key added to the firmware's keychain. This prevents modifications to the config file (and in turn the checksums contained there)
from going unnoticed.
### What about ext2/3/4? Why is that supported then?
This is explicitly against the philosophy, but it is a pragmatic compromise since a lot of Linux distros and setups expect it to "work that way".
### But I don't want to have a separate FAT boot partition! I don't want it!!!
Well tough luck. It is `$year_following_2012` now and most PCs are equipped with UEFI and simply won't boot without a FAT EFI system partition
anyways. It is not unreasonable to share the EFI system partition with the OS's /boot and store kernels and initramfses there.

1375
PROTOCOL.md Normal file

File diff suppressed because it is too large Load Diff

251
README.md Normal file
View File

@ -0,0 +1,251 @@
# Limine
### What is Limine?
Limine (pronounced as shown [here](https://www.merriam-webster.com/dictionary/in%20limine))
is a modern, advanced, portable, multiprotocol bootloader, also used
as the reference implementation for the [Limine boot protocol](/PROTOCOL.md).
### Donate
If you want to support the work I (@mintsuki) do on Limine, feel free to donate to me on Liberapay:
<a href="https://liberapay.com/mintsuki/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
Donations welcome, but absolutely not mandatory!
### Limine's boot menu
![Reference screenshot](/screenshot.png?raw=true "Reference screenshot")
[Photo by Pixabay](https://www.pexels.com/photo/painting-of-black-cloud-during-sunset-164175/)
### Supported architectures
* IA-32 (32-bit x86)
* x86-64
* aarch64 (arm64)
* riscv64
### Supported boot protocols
* Linux
* [Limine](/PROTOCOL.md)
* Multiboot 1
* Multiboot 2
* Chainloading
### Supported partitioning schemes
* MBR
* GPT
* Unpartitioned media
### Supported filesystems
* ext2/3/4
* FAT12/16/32
* ISO9660 (CDs/DVDs)
If your filesystem isn't listed here, please read [the philosophy](/PHILOSOPHY.md) first, especially before
opening issues or pull requests related to this.
### Minimum system requirements
For 32-bit x86 systems, support is only ensured starting with those with
Pentium Pro (i686) class CPUs.
All x86-64, aarch64, and riscv64 (UEFI) systems are supported.
## Packaging status
[![Packaging status](https://repology.org/badge/vertical-allrepos/limine.svg)](https://repology.org/project/limine/versions)
## Binary releases
For convenience, for point releases, binaries are distributed. These binaries
are shipped in the `-binary` branches and tags of this repository
(see [branches](https://github.com/limine-bootloader/limine/branches/all) and
[tags](https://github.com/limine-bootloader/limine/tags)).
For example, to clone the latest binary release of the `v5.x` branch one can do
```bash
git clone https://github.com/limine-bootloader/limine.git --branch=v5.x-branch-binary --depth=1
```
or, to clone a specific binary point release (for example `v5.20231024.0`)
```bash
git clone https://github.com/limine-bootloader/limine.git --branch=v5.20231024.0-binary --depth=1
```
In order to rebuild host utilities like `limine`, simply run `make` in the binary
release directory.
Host utility binaries are provided for Windows.
## Building the bootloader
*The following steps are not necessary if cloning a binary release. If so, skip to*
*"Installing Limine binaries".*
### Prerequisites
In order to build Limine, the following programs have to be installed:
common UNIX tools (also known as `coreutils`),
`GNU make`, `grep`, `sed`, `find`, `awk`, `gzip`, `nasm`, `mtools`
(optional, necessary to build `limine-uefi-cd.bin`).
Furthermore, `gcc` or `llvm/clang` must also be installed, alongside
the respective binutils.
### Configure
If using a release tarball (recommended, see https://github.com/limine-bootloader/limine/releases),
run `./configure` directly.
If checking out from the repository, run `./bootstrap` first in order to download the
necessary dependencies and generate the configure script (`GNU autoconf` and `GNU automake` required).
`./configure` takes arguments and environment variables; for more information on
these, run `./configure --help`.
**`./configure` by default does not build any Limine port. Make sure to read the**
**output of `./configure --help` and enable any or all ports!**
Limine supports both in-tree and out-of-tree builds. Simply run the `configure`
script from the directory you wish to execute the build in. The following `make`
commands are supposed to be run inside the build directory.
### Building Limine
To build Limine, run:
```bash
make # (or gmake where applicable)
```
The generated bootloader files are going to be in `./bin`.
## Installing Limine binaries
This step is optional as the bootloader binaries can be used from the `./bin` or
release directory just fine. This step will only install them to `share`, `include`, and
`bin` directories in the specified prefix (default is `/usr/local`, see
`./configure --help`, or the `PREFIX` variable if installing from a binary release).
To install Limine, run:
```bash
make install # (or gmake where applicable)
```
## How to use
### UEFI
The `BOOT*.EFI` files are valid EFI applications that can be simply copied to
the `/EFI/BOOT` directory of a FAT formatted EFI system partition. These files can
be installed there and coexist with a BIOS installation of Limine (see below) so
that the disk will be bootable on both BIOS and UEFI systems.
The boot device must contain the `limine.cfg` files in
either the root, `limine`, `boot`, or `boot/limine` directory of one of the
partitions, formatted with a supported file system (the ESP partition is recommended).
### Secure Boot
Limine can be booted with secure boot if the executable is signed and the key used to
sign it is added to the firmware's keychain. This should be done in combination with enrolling
the BLAKE2B hash of the Limine config file into the Limine EFI executable image itself for
verification purposes.
For more information see the `limine enroll-config` program and [the philosophy](/PHILOSOPHY.md).
### BIOS/MBR
In order to install Limine on a MBR device (which can just be a raw image file),
run `limine bios-install` as such:
```bash
limine bios-install <path to device/image>
```
The boot device must contain the `limine-bios.sys` and `limine.cfg` files in
either the root, `limine`, `boot`, or `boot/limine` directory of one of the
partitions, formatted with a supported file system.
### BIOS/GPT
If using a GPT formatted device, there are 2 options one can follow for
installation:
* Specifying a dedicated stage 2 partition.
* Letting `limine bios-install` attempt to embed stage 2 within GPT structures.
In case one wants to specify a stage 2 partition, create a partition on the GPT
device of at least 32KiB in size, and pass the 1-based number of the partition
to `limine bios-install` as a second argument; such as:
```bash
limine bios-install <path to device/image> <1-based stage 2 partition number>
```
In case one wants to let `limine bios-install` embed stage 2 within GPT's structures,
simply omit the partition number, and invoke `limine bios-install` the same as one
would do for an MBR partitioned device.
The boot device must contain the `limine-bios.sys` and `limine.cfg` files in
either the root, `limine`, `boot`, or `boot/limine` directory of one of the
partitions, formatted with a supported file system.
### BIOS/UEFI hybrid ISO creation
In order to create a hybrid ISO with Limine, place the
`limine-uefi-cd.bin`, `limine-bios-cd.bin`, `limine-bios.sys`, and `limine.cfg` files
into a directory which will serve as the root of the created ISO.
(`limine-bios.sys` and `limine.cfg` must either be in the root, `limine`, `boot`, or
`boot/limine` directory; `limine-uefi-cd.bin` and `limine-bios-cd.bin` can reside
anywhere).
After that, create a `<ISO root directory>/EFI/BOOT` directory and copy the
relevant Limine EFI executables over (such as `BOOTX64.EFI`).
Place any other file you want to be on the final ISO in said directory, then
run:
```
xorriso -as mkisofs -b <relative path of limine-bios-cd.bin> \
-no-emul-boot -boot-load-size 4 -boot-info-table \
--efi-boot <relative path of limine-uefi-cd.bin> \
-efi-boot-part --efi-boot-image --protective-msdos-label \
<root directory> -o image.iso
```
*Note: `xorriso` is required.*
And do not forget to also run `limine bios-install` on the generated image:
```
limine bios-install image.iso
```
`<relative path of limine-bios-cd.bin>` is the relative path of
`limine-bios-cd.bin` inside the root directory.
For example, if it was copied in `<root directory>/boot/limine-bios-cd.bin`,
it would be `boot/limine-bios-cd.bin`.
`<relative path of limine-uefi-cd.bin>` is the relative path of
`limine-uefi-cd.bin` inside the root directory.
For example, if it was copied in
`<root directory>/boot/limine-uefi-cd.bin`, it would be
`boot/limine-uefi-cd.bin`.
### BIOS/PXE boot
The `limine-bios-pxe.bin` binary is a valid PXE boot image.
In order to boot Limine from PXE it is necessary to setup a DHCP server with
support for PXE booting. This can either be accomplished using a single DHCP
server or your existing DHCP server and a proxy DHCP server such as dnsmasq.
`limine.cfg` and `limine-bios.sys` are expected to be on the server used for boot.
### UEFI/PXE boot
The `BOOT*.EFI` files are compatible with UEFI PXE.
The steps needed to boot Limine are the same as with BIOS PXE,
except that the `limine-bios.sys` file is not needed on the server.
### Configuration
The `limine.cfg` file contains Limine's configuration.
An example `limine.cfg` file can be found in [`test/limine.cfg`](https://github.com/limine-bootloader/limine/blob/trunk/test/limine.cfg).
More info on the format of `limine.cfg` can be found in [`CONFIG.md`](https://github.com/limine-bootloader/limine/blob/trunk/CONFIG.md).
## Acknowledgments
Limine uses a stripped-down version of [tinf](https://github.com/jibsen/tinf) for early GZIP decompression.
Limine relies on [stb_image](https://github.com/nothings/stb/blob/dev/stb_image.h) for runtime GZIP decompression and image loading.
## Discord server
We have a [Discord server](https://discord.gg/QEeZMz4) if you need support,
info, or you just want to hang out with us.

17
bochsrc Normal file
View File

@ -0,0 +1,17 @@
cpu: count=2, reset_on_triple_fault=0
display_library: x, options="gui_debug"
megs: 512
clock: sync=realtime, time0=local
ata0-master: type=disk, path="test.hdd", mode=flat
boot: c
log: ./bochsout.txt
mouse: enabled=0
magic_break: enabled=1

34
bootstrap Executable file
View File

@ -0,0 +1,34 @@
#! /bin/sh
set -ex
srcdir="$(dirname "$0")"
test -z "$srcdir" && srcdir=.
cd "$srcdir"
if [ -z "$BOOTSTRAP_NO_SHALLOW_CLONES" ]; then
SHALLOW_CLONE_FLAG="--depth=1"
fi
[ -d common/flanterm ] || git clone https://github.com/mintsuki/flanterm.git common/flanterm $SHALLOW_CLONE_FLAG
[ -f common/stb/stb_image.h ] || ( curl -Lo common/stb/stb_image.h https://github.com/nothings/stb/raw/dev/stb_image.h && patch -p0 < common/stb_image.patch )
[ -d decompressor/tinf ] || (
set -e
mkdir -p decompressor/tinf
curl -Lo decompressor/tinf/tinf.h https://github.com/jibsen/tinf/raw/master/src/tinf.h
curl -Lo decompressor/tinf/tinflate.c https://github.com/jibsen/tinf/raw/master/src/tinflate.c
curl -Lo decompressor/tinf/tinfgzip.c https://github.com/jibsen/tinf/raw/master/src/tinfgzip.c
patch -p0 < decompressor/tinf.patch
)
[ -f freestanding-toolchain ] || ( curl -Lo freestanding-toolchain https://github.com/mintsuki/freestanding-toolchain/raw/trunk/freestanding-toolchain && chmod +x freestanding-toolchain )
[ -d freestanding-headers ] || git clone https://github.com/mintsuki/freestanding-headers.git $SHALLOW_CLONE_FLAG
[ -d limine-efi ] || git clone https://github.com/limine-bootloader/limine-efi.git $SHALLOW_CLONE_FLAG
[ -d libgcc-binaries ] || git clone https://github.com/mintsuki/libgcc-binaries.git $SHALLOW_CLONE_FLAG
AUTOMAKE_LIBDIR="$(automake --print-libdir)"
cp "${AUTOMAKE_LIBDIR}/INSTALL" ./
mkdir -p build-aux
cp "${AUTOMAKE_LIBDIR}/install-sh" build-aux/
autoreconf -fvi -Wall

626
common/GNUmakefile Normal file
View File

@ -0,0 +1,626 @@
override MAKEFLAGS += -rR
include $(TOOLCHAIN_FILE)
export CC_FOR_TARGET
export LD_FOR_TARGET
export OBJDUMP_FOR_TARGET
export OBJCOPY_FOR_TARGET
export READELF_FOR_TARGET
TARGET ?=
BUILDDIR ?=
override SRCDIR := $(shell pwd -P)
override SPACE := $(subst ,, )
MKESCAPE = $(subst $(SPACE),\ ,$(1))
SHESCAPE = $(subst ','\'',$(1))
OBJESCAPE = $(subst .a ,.a' ',$(subst .o ,.o' ',$(call SHESCAPE,$(1))))
ifeq ($(call MKESCAPE,$(BUILDDIR)),)
$(error BUILDDIR not specified)
endif
COM_OUTPUT ?= false
E9_OUTPUT ?= false
override S2CFLAGS := -Os
override BASE_CFLAGS := $(CFLAGS_FOR_TARGET)
override CFLAGS_FOR_TARGET += \
-g \
-Wall \
-Wextra \
-Wshadow \
-Wvla \
$(WERROR_FLAG) \
-std=gnu11 \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-omit-frame-pointer \
-fno-strict-aliasing \
-fno-lto
override CPPFLAGS_FOR_TARGET := \
-I../freestanding-headers \
-I'$(call SHESCAPE,$(BUILDDIR))/..' \
-I. \
$(CPPFLAGS_FOR_TARGET) \
-DCOM_OUTPUT=$(COM_OUTPUT) \
-DE9_OUTPUT=$(E9_OUTPUT) \
-MMD \
-MP
$(call MKESCAPE,$(BUILDDIR))/./flanterm/backends/fb.o: override CPPFLAGS_FOR_TARGET += \
-DFLANTERM_FB_DISABLE_BUMP_ALLOC \
-DFLANTERM_FB_SUPPORT_BPP
ifeq ($(TARGET),bios)
override CFLAGS_FOR_TARGET += \
-fno-PIE \
-fno-PIC \
-m32 \
-march=i686 \
-mno-80387
override CPPFLAGS_FOR_TARGET := \
$(CPPFLAGS_FOR_TARGET) \
-DBIOS
endif
ifeq ($(TARGET),uefi-x86-64)
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-m64 \
-march=x86-64 \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone
override CPPFLAGS_FOR_TARGET := \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/x86_64' \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
endif
ifeq ($(TARGET),uefi-ia32)
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-m32 \
-march=i686 \
-mno-80387
override CPPFLAGS_FOR_TARGET := \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/ia32' \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
endif
ifeq ($(TARGET),uefi-aarch64)
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-mgeneral-regs-only
override CPPFLAGS_FOR_TARGET := \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/aarch64' \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
endif
ifeq ($(TARGET),uefi-riscv64)
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar
ifeq ($(CC_FOR_TARGET_IS_CLANG),yes)
override CFLAGS_FOR_TARGET += -march=rv64imac
else
override CFLAGS_FOR_TARGET += -march=rv64imac_zicsr_zifencei
endif
override CFLAGS_FOR_TARGET += \
-mabi=lp64 \
-mno-relax
override CPPFLAGS_FOR_TARGET := \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/riscv64' \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI \
-D__riscv64
endif
override LDFLAGS_FOR_TARGET += \
-nostdlib \
-z max-page-size=0x1000
ifeq ($(TARGET),bios)
override LDFLAGS_FOR_TARGET += \
-m elf_i386 \
-static \
--build-id=sha1
ifeq ($(LD_FOR_TARGET_HAS_NO_PIE),yes)
override LDFLAGS_FOR_TARGET += -no-pie
endif
endif
ifeq ($(TARGET),uefi-x86-64)
override LDFLAGS_FOR_TARGET += \
-m elf_x86_64 \
-static \
-pie \
--no-dynamic-linker \
-z text
endif
ifeq ($(TARGET),uefi-ia32)
override LDFLAGS_FOR_TARGET += \
-m elf_i386 \
-static \
-pie \
--no-dynamic-linker \
-z text
endif
ifeq ($(TARGET),uefi-aarch64)
override LDFLAGS_FOR_TARGET += \
-m aarch64elf \
-static \
-pie \
--no-dynamic-linker \
-z text
endif
ifeq ($(TARGET),uefi-riscv64)
override LDFLAGS_FOR_TARGET += \
-m elf64lriscv \
--no-relax \
-static \
-pie \
--no-dynamic-linker \
-z text
endif
override C_FILES := $(shell find . -type f -name '*.c')
ifeq ($(TARGET),bios)
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
override ASM32_FILES := $(shell find . -type f -name '*.asm_ia32')
override ASMB_FILES := $(shell find . -type f -name '*.asm_bios_ia32')
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASMB_FILES:.asm_bios_ia32=.o) $(ASMX86_FILES:.asm_x86=.o))
override OBJ_S2 := $(filter %.s2.o,$(OBJ))
endif
ifeq ($(TARGET),uefi-x86-64)
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
override ASM64_FILES := $(shell find . -type f -name '*.asm_x86_64')
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_x86_64')
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_x86_64=.o) $(ASM64U_FILES:.asm_uefi_x86_64=.o) $(ASMX86_FILES:.asm_x86=.o))
endif
ifeq ($(TARGET),uefi-ia32)
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
override ASM32_FILES := $(shell find . -type f -name '*.asm_ia32')
override ASM32U_FILES := $(shell find . -type f -name '*.asm_uefi_ia32')
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASM32U_FILES:.asm_uefi_ia32=.o) $(ASMX86_FILES:.asm_x86=.o))
endif
ifeq ($(TARGET),uefi-aarch64)
override ASM64_FILES := $(shell find . -type f -name '*.asm_aarch64')
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_aarch64')
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_aarch64=.o) $(ASM64U_FILES:.asm_uefi_aarch64=.o))
endif
ifeq ($(TARGET),uefi-riscv64)
override ASM64_FILES := $(shell find . -type f -name '*.asm_riscv64')
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_riscv64')
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_riscv64=.o) $(ASM64U_FILES:.asm_uefi_riscv64=.o))
endif
override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d))
.PHONY: all
ifeq ($(TARGET),bios)
all: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys $(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz
else ifeq ($(TARGET),uefi-x86-64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI
else ifeq ($(TARGET),uefi-ia32)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI
else ifeq ($(TARGET),uefi-aarch64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI
else ifeq ($(TARGET),uefi-riscv64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI
endif
ifeq ($(TARGET),bios)
$(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz: $(call MKESCAPE,$(BUILDDIR))/stage2.bin
gzip -n -9 < '$(call SHESCAPE,$<)' > '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/stage2.bin: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys
dd if='$(call SHESCAPE,$<)' bs=$$(( 0x$$("$(READELF_FOR_TARGET)" -S '$(call SHESCAPE,$(BUILDDIR))/limine.elf' | $(GREP) '\.text\.stage3' | $(SED) 's/^.*] //' | $(AWK) '{print $$3}' | $(SED) 's/^0*//') - 0xf000 )) count=1 of='$(call SHESCAPE,$@)' 2>/dev/null
$(call MKESCAPE,$(BUILDDIR))/stage2.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' stage2 32 '\.text\.stage2'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/stage2.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/stage2.map.S' '$(call SHESCAPE,$(BUILDDIR))/stage2.map.d'
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nos3map.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 32 '\.text'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
$(call MKESCAPE,$(BUILDDIR))/limine-bios.sys: $(call MKESCAPE,$(BUILDDIR))/limine_stage2only.elf $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$(BUILDDIR))/limine.elf' '$(call SHESCAPE,$@)'
chmod -x '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker_stage2only.ld: linker_bios.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_STAGE2ONLY linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_stage2only.elf: $(OBJ_S2) ../libgcc-binaries/libgcc-i686.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld'
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld' -o '$(call SHESCAPE,$@)' || \
( echo "This error may mean that stage 2 was trying to use stage 3 symbols before loading stage 3" && \
false )
$(call MKESCAPE,$(BUILDDIR))/linker_nos2map.ld: linker_bios.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP -DLINKER_NOS2MAP linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld'
$(call MKESCAPE,$(BUILDDIR))/empty:
touch '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) ../libgcc-binaries/libgcc-i686.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/empty'
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld'
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' -o '$(call SHESCAPE,$@)'
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin'
cd '$(call SHESCAPE,$(BUILDDIR))' && \
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin'
cd '$(call SHESCAPE,$(BUILDDIR))' && \
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_bios.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nos3map.elf: $(OBJ) ../libgcc-binaries/libgcc-i686.a $(call MKESCAPE,$(BUILDDIR))/stage2.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/empty'
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' -o '$(call SHESCAPE,$@)'
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin'
cd '$(call SHESCAPE,$(BUILDDIR))' && \
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin'
cd '$(call SHESCAPE,$(BUILDDIR))' && \
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_bios.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) ../libgcc-binaries/libgcc-i686.a $(call MKESCAPE,$(BUILDDIR))/stage2.map.o $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/empty'
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)'
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin'
cd '$(call SHESCAPE,$(BUILDDIR))' && \
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin'
cd '$(call SHESCAPE,$(BUILDDIR))' && \
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)'
endif
$(call MKESCAPE,$(BUILDDIR))/limine-efi: ../limine-efi/*
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
cp -r ../limine-efi '$(call SHESCAPE,$(BUILDDIR))/'
ifeq ($(TARGET),uefi-x86-64)
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
$(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
chmod -x '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-x86_64.S.o: limine-efi
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_x86_64.c.o: limine-efi
.PHONY: limine-efi
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
CC="$(CC_FOR_TARGET)" \
CFLAGS="$(BASE_CFLAGS)" \
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
ARCH=x86_64
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_x86_64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_x86_64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-x86_64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_x86_64.c.o $(OBJ) ../libgcc-binaries/libgcc-x86_64-no-red-zone.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_x86_64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_x86_64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-x86_64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_x86_64.c.o $(OBJ) ../libgcc-binaries/libgcc-x86_64-no-red-zone.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-aarch64)
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
$(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
chmod -x '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-aarch64.S.o: limine-efi
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_aarch64.c.o: limine-efi
.PHONY: limine-efi
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
CC="$(CC_FOR_TARGET)" \
CFLAGS="$(BASE_CFLAGS)" \
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
ARCH=aarch64
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_aarch64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_aarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-aarch64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_aarch64.c.o $(OBJ) ../libgcc-binaries/libgcc-aarch64.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_aarch64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_aarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-aarch64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_aarch64.c.o $(OBJ) ../libgcc-binaries/libgcc-aarch64.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-riscv64)
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
$(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
chmod -x '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.S.o: limine-efi
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.c.o: limine-efi
.PHONY: limine-efi
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
CC="$(CC_FOR_TARGET)" \
CFLAGS="$(BASE_CFLAGS)" \
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
ARCH=riscv64
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_riscv64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.c.o $(OBJ) ../libgcc-binaries/libgcc-riscv64-softfloat.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_riscv64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.c.o $(OBJ) ../libgcc-binaries/libgcc-riscv64-softfloat.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-ia32)
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
cd '$(call SHESCAPE,$(BUILDDIR))' && \
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 32 '\.text'
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
$(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
chmod -x '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-ia32.S.o: limine-efi
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_ia32.c.o: limine-efi
.PHONY: limine-efi
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
CC="$(CC_FOR_TARGET)" \
CFLAGS="$(BASE_CFLAGS)" \
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
ARCH=ia32
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_ia32.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_ia32.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-ia32.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_ia32.c.o $(OBJ) ../libgcc-binaries/libgcc-i686.a
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_ia32.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_ia32.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-ia32.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_ia32.c.o $(OBJ) ../libgcc-binaries/libgcc-i686.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
-include $(HEADER_DEPS)
ifeq ($(TARGET),uefi-x86-64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-aarch64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-riscv64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-ia32)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),bios)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
-include $(HEADER_DEPS)
ifeq ($(TARGET),bios)
$(call MKESCAPE,$(BUILDDIR))/%.s2.o: %.s2.c
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(S2CFLAGS) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
-include $(HEADER_DEPS)
ifeq ($(TARGET),bios)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_bios_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-x86-64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86_64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf64 -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_x86_64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf64 -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf64 -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-aarch64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_aarch64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_aarch64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-riscv64)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_riscv64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_riscv64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-ia32)
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
endif

128
common/compress/gzip.c Normal file
View File

@ -0,0 +1,128 @@
/*
* originally from tinfgzip - tiny gzip decompressor
*
* Copyright (c) 2003-2019 Joergen Ibsen
* Copyright (c) 2023 mintsuki and contributors to the Limine project
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must
* not claim that you wrote the original software. If you use this
* software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must
* not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#include <stdint.h>
#include <stb/stb_image.h>
typedef enum {
FTEXT = 1,
FHCRC = 2,
FEXTRA = 4,
FNAME = 8,
FCOMMENT = 16
} gzip_flag;
void *gzip_uncompress(const void *source, uint64_t sourceLen, uint64_t *outsize) {
const uint8_t *src = (const uint8_t *) source;
const uint8_t *start;
int res;
uint8_t flg;
/* -- Check header -- */
/* Check room for at least 10 byte header and 8 byte trailer */
if (sourceLen < 18) {
return NULL;
}
/* Check id bytes */
if (src[0] != 0x1F || src[1] != 0x8B) {
return NULL;
}
/* Check method is deflate */
if (src[2] != 8) {
return NULL;
}
/* Get flag byte */
flg = src[3];
/* Check that reserved bits are zero */
if (flg & 0xE0) {
return NULL;
}
/* -- Find start of compressed data -- */
/* Skip base header of 10 bytes */
start = src + 10;
/* Skip extra data if present */
if (flg & FEXTRA) {
uint64_t xlen = *((uint16_t *)start);
if (xlen > sourceLen - 12) {
return NULL;
}
start += xlen + 2;
}
/* Skip file name if present */
if (flg & FNAME) {
do {
if (((uint64_t)(start - src)) >= sourceLen) {
return NULL;
}
} while (*start++);
}
/* Skip file comment if present */
if (flg & FCOMMENT) {
do {
if (((uint64_t)(start - src)) >= sourceLen) {
return NULL;
}
} while (*start++);
}
if (flg & FHCRC) {
start += 2;
}
/* -- Get decompressed length -- */
uint32_t dlen = *((uint32_t *)&src[sourceLen - 4]);
/* -- Decompress data -- */
if ((src + sourceLen) - start < 8) {
return NULL;
}
void *buf = ext_mem_alloc(dlen);
res = stbi_zlib_decode_noheader_buffer(buf, dlen, (const char *)start, (src + sourceLen) - start - 8);
if (res == -1) {
pmm_free(buf, dlen);
return NULL;
}
*outsize = (uint64_t)dlen;
return buf;
}

8
common/compress/gzip.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __COMPRESS__GZIP_H__
#define __COMPRESS__GZIP_H__
#include <stdint.h>
void *gzip_uncompress(const void *source, uint64_t sourceLen, uint64_t *outsize);
#endif

85
common/console.c Normal file
View File

@ -0,0 +1,85 @@
#include <stddef.h>
#include <stdint.h>
#include <config.h>
#include <console.h>
#include <menu.h>
#include <mm/pmm.h>
#include <lib/print.h>
#include <lib/readline.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/term.h>
#include <lib/part.h>
#include <lib/config.h>
static void console_help(void) {
print(
"Available commands:\n"
"exit -- Exit Limine console.\n"
"clear -- Clear the console.\n"
"%s"
"lsvol -- List volumes.\n"
"firmware -- Show firmware type.\n"
#if defined (UEFI)
"slide -- Print load slide offset.\n"
#endif
"version -- Print version.\n"
"copyright -- Print copyright.\n"
"help -- Print this help message.\n",
editor_enabled ? "editor -- Open an empty boot entry editor.\n" : ""
);
}
#if defined (UEFI)
extern symbol __slide;
#endif
void console(void) {
print("Welcome to the Limine console.\nType 'help' for more information.\n\n");
char *prompt = ext_mem_alloc(256);
for (;;) {
print(">>> ");
readline("", prompt, 256);
if (strcmp(prompt, "help") == 0) {
console_help();
} else if (strcmp(prompt, "exit") == 0) {
break;
} else if (strcmp(prompt, "clear") == 0) {
print("\e[2J\e[H");
} else if (strcmp(prompt, "lsvol") == 0) {
list_volumes();
} else if (editor_enabled && strcmp(prompt, "editor") == 0) {
char *new_entry = config_entry_editor("New Entry", "");
if (new_entry != NULL) {
config_ready = true;
boot(new_entry);
}
} else if (strcmp(prompt, "firmware") == 0) {
#if defined (BIOS)
print("BIOS\n");
#elif defined (UEFI)
print("UEFI\n");
#else
print("unknown\n");
#endif
#if defined (UEFI)
} else if (strcmp(prompt, "slide") == 0) {
print("%p\n", __slide);
#endif
} else if (strcmp(prompt, "version") == 0) {
print(LIMINE_VERSION "\n");
} else if (strcmp(prompt, "copyright") == 0) {
print(LIMINE_COPYRIGHT "\n");
print("Limine is distributed under the terms of the BSD-2-Clause license.\n");
print("There is ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n");
} else if (*prompt != 0) {
print("Invalid command: `%s`.\n", prompt);
}
}
reset_term();
pmm_free(prompt, 256);
}

6
common/console.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __CONSOLE_H__
#define __CONSOLE_H__
void console(void);
#endif

209
common/crypt/blake2b.c Normal file
View File

@ -0,0 +1,209 @@
// This blake2b implementation comes from the GNU coreutils project.
// https://github.com/coreutils/coreutils/blob/master/src/blake2/blake2b-ref.c
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <crypt/blake2b.h>
#include <lib/libc.h>
#define BLAKE2B_BLOCK_BYTES 128
#define BLAKE2B_KEY_BYTES 64
#define BLAKE2B_SALT_BYTES 16
#define BLAKE2B_PERSONAL_BYTES 16
static const uint64_t blake2b_iv[8] = {
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
};
static const uint8_t blake2b_sigma[12][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
};
struct blake2b_state {
uint64_t h[8];
uint64_t t[2];
uint64_t f[2];
uint8_t buf[BLAKE2B_BLOCK_BYTES];
size_t buf_len;
uint8_t last_node;
};
struct blake2b_param {
uint8_t digest_length;
uint8_t key_length;
uint8_t fan_out;
uint8_t depth;
uint32_t leaf_length;
uint32_t node_offset;
uint32_t xof_length;
uint8_t node_depth;
uint8_t inner_length;
uint8_t reserved[14];
uint8_t salt[BLAKE2B_SALT_BYTES];
uint8_t personal[BLAKE2B_PERSONAL_BYTES];
} __attribute__((packed));
static void blake2b_increment_counter(struct blake2b_state *state, uint64_t inc) {
state->t[0] += inc;
state->t[1] += state->t[0] < inc;
}
static inline uint64_t rotr64(uint64_t w, unsigned c) {
return (w >> c) | (w << (64 - c));
}
#define G(r, i, a, b, c, d) do { \
a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
d = rotr64(d ^ a, 32); \
c = c + d; \
b = rotr64(b ^ c, 24); \
a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
d = rotr64(d ^ a, 16); \
c = c + d; \
b = rotr64(b ^ c, 63); \
} while (0)
#define ROUND(r) do { \
G(r, 0, v[0], v[4], v[8], v[12]); \
G(r, 1, v[1], v[5], v[9], v[13]); \
G(r, 2, v[2], v[6], v[10], v[14]); \
G(r, 3, v[3], v[7], v[11], v[15]); \
G(r, 4, v[0], v[5], v[10], v[15]); \
G(r, 5, v[1], v[6], v[11], v[12]); \
G(r, 6, v[2], v[7], v[8], v[13]); \
G(r, 7, v[3], v[4], v[9], v[14]); \
} while (0)
static void blake2b_compress(struct blake2b_state *state, const uint8_t block[static BLAKE2B_BLOCK_BYTES]) {
uint64_t m[16];
uint64_t v[16];
for (int i = 0; i < 16; i++) {
m[i] = *(uint64_t *)(block + i * sizeof(m[i]));
}
for (int i = 0; i < 8; i++) {
v[i] = state->h[i];
}
v[8] = blake2b_iv[0];
v[9] = blake2b_iv[1];
v[10] = blake2b_iv[2];
v[11] = blake2b_iv[3];
v[12] = blake2b_iv[4] ^ state->t[0];
v[13] = blake2b_iv[5] ^ state->t[1];
v[14] = blake2b_iv[6] ^ state->f[0];
v[15] = blake2b_iv[7] ^ state->f[1];
ROUND(0);
ROUND(1);
ROUND(2);
ROUND(3);
ROUND(4);
ROUND(5);
ROUND(6);
ROUND(7);
ROUND(8);
ROUND(9);
ROUND(10);
ROUND(11);
for (int i = 0; i < 8; i++) {
state->h[i] = state->h[i] ^ v[i] ^ v[i + 8];
}
}
#undef G
#undef ROUND
static void blake2b_init(struct blake2b_state *state) {
struct blake2b_param param = {0};
param.digest_length = BLAKE2B_OUT_BYTES;
param.fan_out = 1;
param.depth = 1;
memset(state, 0, sizeof(struct blake2b_state));
for (int i = 0; i < 8; i++) {
state->h[i] = blake2b_iv[i];
}
for (int i = 0; i < 8; i++) {
state->h[i] ^= *(uint64_t *)((void *)&param + sizeof(state->h[i]) * i);
}
}
static void blake2b_update(struct blake2b_state *state, const void *in, size_t in_len) {
if (in_len == 0) {
return;
}
size_t left = state->buf_len;
size_t fill = BLAKE2B_BLOCK_BYTES - left;
if (in_len > fill) {
state->buf_len = 0;
memcpy(state->buf + left, in, fill);
blake2b_increment_counter(state, BLAKE2B_BLOCK_BYTES);
blake2b_compress(state, state->buf);
in += fill;
in_len -= fill;
while (in_len > BLAKE2B_BLOCK_BYTES) {
blake2b_increment_counter(state, BLAKE2B_BLOCK_BYTES);
blake2b_compress(state, in);
in += fill;
in_len -= fill;
}
}
memcpy(state->buf + state->buf_len, in, in_len);
state->buf_len += in_len;
}
static void blake2b_final(struct blake2b_state *state, void *out) {
uint8_t buffer[BLAKE2B_OUT_BYTES] = {0};
blake2b_increment_counter(state, state->buf_len);
state->f[0] = (uint64_t)-1;
memset(state->buf + state->buf_len, 0, BLAKE2B_BLOCK_BYTES - state->buf_len);
blake2b_compress(state, state->buf);
for (int i = 0; i < 8; i++) {
*(uint64_t *)(buffer + sizeof(state->h[i]) * i) = state->h[i];
}
memcpy(out, buffer, BLAKE2B_OUT_BYTES);
memset(buffer, 0, sizeof(buffer));
}
void blake2b(void *out, const void *in, size_t in_len) {
struct blake2b_state state = {0};
blake2b_init(&state);
blake2b_update(&state, in, in_len);
blake2b_final(&state, out);
}

10
common/crypt/blake2b.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __CRYPT__BLAKE2B_H__
#define __CRYPT__BLAKE2B_H__
#include <stddef.h>
#define BLAKE2B_OUT_BYTES 64
void blake2b(void *out, const void *in, size_t in_len);
#endif

26
common/drivers/disk.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef __DRIVERS__DISK_H__
#define __DRIVERS__DISK_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/part.h>
#if defined (UEFI)
#include <efi.h>
struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle);
#endif
enum {
DISK_SUCCESS,
DISK_NO_MEDIA,
DISK_FAILURE
};
void disk_create_index(void);
int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count);
#endif

672
common/drivers/disk.s2.c Normal file
View File

@ -0,0 +1,672 @@
#include <stdint.h>
#include <stddef.h>
#include <stdalign.h>
#include <drivers/disk.h>
#include <lib/libc.h>
#if defined (BIOS)
# include <lib/real.h>
#elif defined (UEFI)
# include <efi.h>
# include <crypt/blake2b.h>
#endif
#include <lib/misc.h>
#include <lib/print.h>
#include <lib/rand.h>
#include <mm/pmm.h>
#include <sys/cpu.h>
#include <pxe/pxe.h>
#define DEFAULT_FASTEST_XFER_SIZE 64
#define MAX_FASTEST_XFER_SIZE 512
#define MAX_VOLUMES 64
#if defined (BIOS)
struct bios_drive_params {
uint16_t buf_size;
uint16_t info_flags;
uint32_t cyl;
uint32_t heads;
uint32_t sects;
uint64_t lba_count;
uint16_t bytes_per_sect;
uint32_t edd;
} __attribute__((packed));
struct dap {
uint16_t size;
uint16_t count;
uint16_t offset;
uint16_t segment;
uint64_t lba;
};
#define XFER_BUF_SIZE (xfer_sizes[SIZEOF_ARRAY(xfer_sizes) - 1] * 512)
static const size_t xfer_sizes[] = { 1, 2, 4, 8, 16, 24, 32, 48, 64 };
static uint8_t *xfer_buf = NULL;
static size_t fastest_xfer_size(struct volume *volume) {
struct dap dap = {0};
if (xfer_buf == NULL)
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
size_t fastest_size = 1;
uint64_t last_speed = (uint64_t)-1;
for (size_t i = 0; i < SIZEOF_ARRAY(xfer_sizes); i++) {
if (xfer_sizes[i] * volume->sector_size > XFER_BUF_SIZE) {
break;
}
dap.size = 16;
dap.count = xfer_sizes[i];
dap.segment = rm_seg(xfer_buf);
dap.offset = rm_off(xfer_buf);
dap.lba = 0;
uint64_t start_timestamp = rdtsc();
for (size_t j = 0; j < XFER_BUF_SIZE / 512; j += xfer_sizes[i]) {
struct rm_regs r = {0};
r.eax = 0x4200;
r.edx = volume->drive;
r.esi = (uint32_t)rm_off(&dap);
r.ds = rm_seg(&dap);
rm_int(0x13, &r, &r);
if (r.eflags & EFLAGS_CF) {
int ah = (r.eax >> 8) & 0xff;
print("Disk error %x. Drive %x", ah, volume->drive);
return 8;
}
dap.lba += xfer_sizes[i];
}
uint64_t end_timestamp = rdtsc();
uint64_t speed = end_timestamp - start_timestamp;
if (speed < last_speed) {
last_speed = speed;
fastest_size = xfer_sizes[i];
}
}
return fastest_size;
}
int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
struct dap dap = {0};
if (count * volume->sector_size > XFER_BUF_SIZE)
panic(false, "XFER");
if (xfer_buf == NULL)
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
dap.size = 16;
dap.count = count;
dap.segment = rm_seg(xfer_buf);
dap.offset = rm_off(xfer_buf);
dap.lba = block;
struct rm_regs r = {0};
r.eax = 0x4200;
r.edx = volume->drive;
r.esi = (uint32_t)rm_off(&dap);
r.ds = rm_seg(&dap);
rm_int(0x13, &r, &r);
if (r.eflags & EFLAGS_CF) {
return DISK_FAILURE;
}
if (buf != NULL)
memcpy(buf, xfer_buf, count * volume->sector_size);
return DISK_SUCCESS;
}
static int disk_write_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
struct dap dap = {0};
if (count * volume->sector_size > XFER_BUF_SIZE)
panic(false, "XFER");
if (xfer_buf == NULL)
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
dap.size = 16;
dap.count = count;
dap.segment = rm_seg(xfer_buf);
dap.offset = rm_off(xfer_buf);
dap.lba = block;
struct rm_regs r = {0};
r.eax = 0x4301;
r.edx = volume->drive;
r.esi = (uint32_t)rm_off(&dap);
r.ds = rm_seg(&dap);
if (buf != NULL)
memcpy(xfer_buf, buf, count * volume->sector_size);
rm_int(0x13, &r, &r);
if (r.eflags & EFLAGS_CF) {
return DISK_FAILURE;
}
return DISK_SUCCESS;
}
static bool detect_sector_size(struct volume *volume) {
struct dap dap = {0};
if (xfer_buf == NULL)
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
dap.size = 16;
dap.count = 1;
dap.segment = rm_seg(xfer_buf);
dap.offset = rm_off(xfer_buf);
dap.lba = 0;
struct rm_regs r = {0};
r.eax = 0x4200;
r.edx = volume->drive;
r.esi = (uint32_t)rm_off(&dap);
r.ds = rm_seg(&dap);
struct rm_regs r_copy = r;
struct dap dap_copy = dap;
memset(xfer_buf, 0, XFER_BUF_SIZE);
rm_int(0x13, &r, &r);
if (r.eflags & EFLAGS_CF) {
return false;
}
size_t sector_size_a = 0;
for (long i = XFER_BUF_SIZE - 1; i >= 0; i--) {
if (xfer_buf[i] != 0) {
sector_size_a = i + 1;
break;
}
}
r = r_copy;
dap = dap_copy;
memset(xfer_buf, 0xff, XFER_BUF_SIZE);
rm_int(0x13, &r, &r);
if (r.eflags & EFLAGS_CF) {
return false;
}
size_t sector_size_b = 0;
for (long i = XFER_BUF_SIZE - 1; i >= 0; i--) {
if (xfer_buf[i] != 0xff) {
sector_size_b = i + 1;
break;
}
}
volume->sector_size = sector_size_a > sector_size_b ? sector_size_a : sector_size_b;
if (volume->sector_size == 0) {
return false;
}
return true;
}
void disk_create_index(void) {
volume_index = ext_mem_alloc(sizeof(struct volume) * MAX_VOLUMES);
int optical_indices = 1, hdd_indices = 1;
for (uint8_t drive = 0x80; drive < 0xf0; drive++) {
struct rm_regs r = {0};
struct bios_drive_params drive_params;
r.eax = 0x4800;
r.edx = drive;
r.ds = rm_seg(&drive_params);
r.esi = rm_off(&drive_params);
drive_params.buf_size = sizeof(struct bios_drive_params);
rm_int(0x13, &r, &r);
if (r.eflags & EFLAGS_CF) {
continue;
}
struct volume *block = ext_mem_alloc(sizeof(struct volume));
block->drive = drive;
block->partition = 0;
block->first_sect = 0;
block->sect_count = drive_params.lba_count;
block->max_partition = -1;
if (!detect_sector_size(block)) {
continue;
}
if (disk_read_sectors(block, xfer_buf, 0, 1) != DISK_SUCCESS) {
continue;
}
block->is_optical = disk_write_sectors(block, xfer_buf, 0, 1) != DISK_SUCCESS;
if (block->is_optical) {
block->index = optical_indices++;
} else {
block->index = hdd_indices++;
}
block->fastest_xfer_size = fastest_xfer_size(block);
if (gpt_get_guid(&block->guid, block)) {
block->guid_valid = true;
}
if (volume_index_i == MAX_VOLUMES) {
print("WARNING: TOO MANY VOLUMES!");
return;
}
volume_index[volume_index_i++] = block;
for (int part = 0; ; part++) {
struct volume *p = ext_mem_alloc(sizeof(struct volume));
int ret = part_get(p, block, part);
if (ret == END_OF_TABLE || ret == INVALID_TABLE)
break;
if (ret == NO_PARTITION)
continue;
if (volume_index_i == MAX_VOLUMES) {
print("WARNING: TOO MANY VOLUMES!");
return;
}
volume_index[volume_index_i++] = p;
block->max_partition++;
}
}
}
#endif
#if defined (UEFI)
int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
EFI_STATUS status;
status = volume->block_io->ReadBlocks(volume->block_io,
volume->block_io->Media->MediaId,
block, count * volume->sector_size, buf);
switch (status) {
case EFI_SUCCESS: return DISK_SUCCESS;
case EFI_NO_MEDIA: return DISK_NO_MEDIA;
default: return DISK_FAILURE;
}
}
static struct volume *pxe_from_efi_handle(EFI_HANDLE efi_handle) {
static struct volume *vol = NULL;
// There's only one PXE volume
if (vol) {
return vol;
}
EFI_STATUS status;
EFI_GUID pxe_base_code_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID;
EFI_PXE_BASE_CODE *pxe_base_code = NULL;
status = gBS->HandleProtocol(efi_handle, &pxe_base_code_guid, (void **)&pxe_base_code);
if (status) {
return NULL;
}
if (!pxe_base_code->Mode->DhcpDiscoverValid) {
print("PXE somehow didn't use DHCP?\n");
return NULL;
}
if (pxe_base_code->Mode->UsingIpv6) {
print("Sorry, unsupported: PXE IPv6\n");
return NULL;
}
vol = pxe_bind_volume(efi_handle, pxe_base_code);
return vol;
}
static alignas(4096) uint8_t unique_sector_pool[4096];
struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle) {
EFI_STATUS status;
EFI_GUID block_io_guid = BLOCK_IO_PROTOCOL;
EFI_BLOCK_IO *block_io = NULL;
status = gBS->HandleProtocol(efi_handle, &block_io_guid, (void **)&block_io);
if (status) {
return pxe_from_efi_handle(efi_handle);
}
block_io->Media->WriteCaching = false;
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->unique_sector_valid == false) {
continue;
}
if (volume_index[i]->unique_sector % block_io->Media->BlockSize) {
continue;
}
size_t unique_sector = volume_index[i]->unique_sector / block_io->Media->BlockSize;
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId,
unique_sector,
4096,
unique_sector_pool);
if (status != 0) {
continue;
}
uint8_t b2b[BLAKE2B_OUT_BYTES];
blake2b(b2b, unique_sector_pool, 4096);
if (memcmp(b2b, volume_index[i]->unique_sector_b2b, BLAKE2B_OUT_BYTES) == 0) {
return volume_index[i];
}
}
// Fallback to read-back method
uint64_t signature = rand64();
uint64_t new_signature;
do { new_signature = rand64(); } while (new_signature == signature);
uint64_t orig;
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
orig = *(uint64_t *)unique_sector_pool;
if (status) {
return NULL;
}
*(uint64_t *)unique_sector_pool = signature;
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
return NULL;
}
struct volume *ret = NULL;
for (size_t i = 0; i < volume_index_i; i++) {
uint64_t compare;
status = volume_index[i]->block_io->ReadBlocks(volume_index[i]->block_io,
volume_index[i]->block_io->Media->MediaId,
(volume_index[i]->first_sect * 512) / volume_index[i]->sector_size,
4096, unique_sector_pool);
compare = *(uint64_t *)unique_sector_pool;
if (status) {
continue;
}
if (compare == signature) {
// Double check
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
break;
}
*(uint64_t *)unique_sector_pool = new_signature;
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
break;
}
status = volume_index[i]->block_io->ReadBlocks(volume_index[i]->block_io,
volume_index[i]->block_io->Media->MediaId,
(volume_index[i]->first_sect * 512) / volume_index[i]->sector_size,
4096, unique_sector_pool);
compare = *(uint64_t *)unique_sector_pool;
if (status) {
continue;
}
if (compare == new_signature) {
ret = volume_index[i];
break;
}
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
break;
}
*(uint64_t *)unique_sector_pool = signature;
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
break;
}
}
}
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
return NULL;
}
*(uint64_t *)unique_sector_pool = orig;
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
return NULL;
}
if (ret != NULL) {
return ret;
}
return NULL;
}
static struct volume *volume_by_unique_sector(uint64_t sect, void *b2b) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->unique_sector_valid == false) {
continue;
}
if (volume_index[i]->unique_sector == sect
&& memcmp(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES) == 0) {
return volume_index[i];
}
}
return NULL;
}
#define UNIQUE_SECT_MAX_SEARCH_RANGE 0x1000
static void find_unique_sectors(void) {
EFI_STATUS status;
for (size_t i = 0; i < volume_index_i; i++) {
for (size_t j = 0; j < UNIQUE_SECT_MAX_SEARCH_RANGE; j++) {
if ((volume_index[i]->first_sect * 512) % volume_index[i]->block_io->Media->BlockSize) {
break;
}
size_t first_sect = (volume_index[i]->first_sect * 512) / volume_index[i]->block_io->Media->BlockSize;
status = volume_index[i]->block_io->ReadBlocks(
volume_index[i]->block_io,
volume_index[i]->block_io->Media->MediaId,
first_sect + j,
4096,
unique_sector_pool);
if (status != 0) {
break;
}
uint8_t b2b[BLAKE2B_OUT_BYTES];
blake2b(b2b, unique_sector_pool, 4096);
uint64_t uniq = (uint64_t)j * volume_index[i]->block_io->Media->BlockSize;
if (volume_by_unique_sector(uniq, b2b) == NULL) {
volume_index[i]->unique_sector_valid = true;
volume_index[i]->unique_sector = uniq;
memcpy(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES);
break;
}
}
}
}
static void find_part_handles(EFI_HANDLE *handles, size_t handle_count) {
for (size_t i = 0; i < handle_count; i++) {
struct volume *vol = disk_volume_from_efi_handle(handles[i]);
if (vol == NULL) {
continue;
}
vol->efi_part_handle = handles[i];
}
}
void disk_create_index(void) {
EFI_STATUS status;
EFI_HANDLE tmp_handles[1];
EFI_GUID block_io_guid = BLOCK_IO_PROTOCOL;
EFI_HANDLE *handles = tmp_handles;
UINTN handles_size = sizeof(tmp_handles);
status = gBS->LocateHandle(ByProtocol, &block_io_guid, NULL, &handles_size, handles);
// we only care about the first handle, so ignore if we get EFI_BUFFER_TOO_SMALL
if (status != EFI_BUFFER_TOO_SMALL && status != EFI_SUCCESS) {
EFI_GUID pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID;
status = gBS->LocateHandle(ByProtocol, &pxe_guid, NULL, &handles_size, handles);
// likewise, all that matters is that the protocol is present
if (status == EFI_BUFFER_TOO_SMALL || status == EFI_SUCCESS) {
return;
}
goto fail;
}
handles = ext_mem_alloc(handles_size);
status = gBS->LocateHandle(ByProtocol, &block_io_guid, NULL, &handles_size, handles);
if (status != EFI_SUCCESS) {
fail:
panic(false, "LocateHandle for BLOCK_IO_PROTOCOL failed. Machine not supported by Limine UEFI.");
}
volume_index = ext_mem_alloc(sizeof(struct volume) * MAX_VOLUMES);
int optical_indices = 1, hdd_indices = 1;
size_t handle_count = handles_size / sizeof(EFI_HANDLE);
for (size_t i = 0; i < handle_count; i++) {
EFI_BLOCK_IO *drive = NULL;
status = gBS->HandleProtocol(handles[i], &block_io_guid, (void **)&drive);
if (status != 0 || drive == NULL || drive->Media->LastBlock == 0)
continue;
if (drive->Media->LogicalPartition)
continue;
drive->Media->WriteCaching = false;
status = drive->ReadBlocks(drive, drive->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
continue;
}
status = drive->WriteBlocks(drive, drive->Media->MediaId, 0, 4096, unique_sector_pool);
struct volume *block = ext_mem_alloc(sizeof(struct volume));
if (status || drive->Media->ReadOnly) {
block->index = optical_indices++;
block->is_optical = true;
} else {
block->index = hdd_indices++;
}
block->efi_handle = handles[i];
block->block_io = drive;
block->partition = 0;
block->sector_size = drive->Media->BlockSize;
block->first_sect = 0;
block->sect_count = drive->Media->LastBlock + 1;
block->max_partition = -1;
if (drive->Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION3) {
block->fastest_xfer_size = drive->Media->OptimalTransferLengthGranularity;
}
if (block->fastest_xfer_size == 0) {
block->fastest_xfer_size = DEFAULT_FASTEST_XFER_SIZE;
} else if (block->fastest_xfer_size >= MAX_FASTEST_XFER_SIZE) {
block->fastest_xfer_size = MAX_FASTEST_XFER_SIZE;
}
if (gpt_get_guid(&block->guid, block)) {
block->guid_valid = true;
}
if (volume_index_i == MAX_VOLUMES) {
print("WARNING: TOO MANY VOLUMES!");
return;
}
volume_index[volume_index_i++] = block;
for (int part = 0; ; part++) {
struct volume _p = {0};
int ret = part_get(&_p, block, part);
if (ret == END_OF_TABLE || ret == INVALID_TABLE)
break;
if (ret == NO_PARTITION)
continue;
struct volume *p = ext_mem_alloc(sizeof(struct volume));
memcpy(p, &_p, sizeof(struct volume));
if (volume_index_i == MAX_VOLUMES) {
print("WARNING: TOO MANY VOLUMES!");
return;
}
volume_index[volume_index_i++] = p;
block->max_partition++;
}
}
find_unique_sectors();
find_part_handles(handles, handle_count);
pmm_free(handles, handles_size);
}
#endif

84
common/drivers/edid.c Normal file
View File

@ -0,0 +1,84 @@
#include <stdint.h>
#include <stddef.h>
#include <drivers/gop.h>
#include <drivers/edid.h>
#include <mm/pmm.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/print.h>
#if defined (BIOS)
#include <lib/real.h>
struct edid_info_struct *get_edid_info(void) {
static struct edid_info_struct *buf = NULL;
if (buf == NULL)
buf = conv_mem_alloc(sizeof(struct edid_info_struct));
struct rm_regs r = {0};
r.eax = 0x4f15;
r.ebx = 0x0001;
r.edi = (uint32_t)rm_off(buf);
r.ds = (uint32_t)rm_seg(buf);
r.es = r.ds;
rm_int(0x10, &r, &r);
if ((r.eax & 0x00ff) != 0x4f)
goto fail;
if ((r.eax & 0xff00) != 0)
goto fail;
for (size_t i = 0; i < sizeof(struct edid_info_struct); i++)
if (((uint8_t *)buf)[i] != 0)
goto success;
fail:
printv("edid: Could not fetch EDID data.\n");
return NULL;
success:
printv("edid: Success.\n");
return buf;
}
#endif
#if defined (UEFI)
#include <efi.h>
struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle) {
struct edid_info_struct *buf = ext_mem_alloc(sizeof(struct edid_info_struct));
EFI_STATUS status;
EFI_EDID_ACTIVE_PROTOCOL *edid = NULL;
EFI_GUID edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID;
status = gBS->HandleProtocol(gop_handle, &edid_guid, (void **)&edid);
if (status)
goto fail;
if (edid->SizeOfEdid < sizeof(struct edid_info_struct))
goto fail;
memcpy(buf, edid->Edid, sizeof(struct edid_info_struct));
for (size_t i = 0; i < sizeof(struct edid_info_struct); i++)
if (((uint8_t *)buf)[i] != 0)
goto success;
fail:
printv("edid: Could not fetch EDID data.\n");
return NULL;
success:
printv("edid: Success.\n");
return buf;
}
#endif

43
common/drivers/edid.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef __DRIVERS__EDID_H__
#define __DRIVERS__EDID_H__
#include <stdint.h>
struct edid_info_struct {
uint8_t padding[8];
uint16_t manufacturer_id_be;
uint16_t edid_id_code;
uint32_t serial_num;
uint8_t man_week;
uint8_t man_year;
uint8_t edid_version;
uint8_t edid_revision;
uint8_t video_input_type;
uint8_t max_hor_size;
uint8_t max_ver_size;
uint8_t gamma_factor;
uint8_t dpms_flags;
uint8_t chroma_info[10];
uint8_t est_timings1;
uint8_t est_timings2;
uint8_t man_res_timing;
uint16_t std_timing_id[8];
uint8_t det_timing_desc1[18];
uint8_t det_timing_desc2[18];
uint8_t det_timing_desc3[18];
uint8_t det_timing_desc4[18];
uint8_t unused;
uint8_t checksum;
} __attribute__((packed));
#if defined (UEFI)
#include <efi.h>
struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle);
#endif
#if defined (BIOS)
struct edid_info_struct *get_edid_info(void);
#endif
#endif

340
common/drivers/gop.c Normal file
View File

@ -0,0 +1,340 @@
#if defined (UEFI)
#include <stdint.h>
#include <stddef.h>
#include <efi.h>
#include <lib/misc.h>
#include <lib/term.h>
#include <drivers/gop.h>
#include <drivers/edid.h>
#include <lib/print.h>
#include <mm/pmm.h>
static uint16_t linear_masks_to_bpp(uint32_t red_mask, uint32_t green_mask,
uint32_t blue_mask, uint32_t alpha_mask) {
uint32_t compound_mask = red_mask | green_mask | blue_mask | alpha_mask;
uint16_t ret = 32;
while ((compound_mask & (1 << 31)) == 0) {
ret--;
compound_mask <<= 1;
}
return ret;
}
static void linear_mask_to_mask_shift(
uint8_t *mask, uint8_t *shift, uint32_t linear_mask) {
*shift = 0;
while ((linear_mask & 1) == 0) {
(*shift)++;
linear_mask >>= 1;
}
*mask = 0;
while ((linear_mask & 1) == 1) {
(*mask)++;
linear_mask >>= 1;
}
}
// Most of this code taken from https://wiki.osdev.org/GOP
static bool mode_to_fb_info(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, size_t mode) {
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
UINTN mode_info_size;
status = gop->QueryMode(gop, mode, &mode_info_size, &mode_info);
if (status) {
return false;
}
switch (mode_info->PixelFormat) {
case PixelBlueGreenRedReserved8BitPerColor:
ret->framebuffer_bpp = 32;
ret->red_mask_size = 8;
ret->red_mask_shift = 16;
ret->green_mask_size = 8;
ret->green_mask_shift = 8;
ret->blue_mask_size = 8;
ret->blue_mask_shift = 0;
break;
case PixelRedGreenBlueReserved8BitPerColor:
ret->framebuffer_bpp = 32;
ret->red_mask_size = 8;
ret->red_mask_shift = 0;
ret->green_mask_size = 8;
ret->green_mask_shift = 8;
ret->blue_mask_size = 8;
ret->blue_mask_shift = 16;
break;
case PixelBitMask:
ret->framebuffer_bpp = linear_masks_to_bpp(
mode_info->PixelInformation.RedMask,
mode_info->PixelInformation.GreenMask,
mode_info->PixelInformation.BlueMask,
mode_info->PixelInformation.ReservedMask);
linear_mask_to_mask_shift(&ret->red_mask_size,
&ret->red_mask_shift,
mode_info->PixelInformation.RedMask);
linear_mask_to_mask_shift(&ret->green_mask_size,
&ret->green_mask_shift,
mode_info->PixelInformation.GreenMask);
linear_mask_to_mask_shift(&ret->blue_mask_size,
&ret->blue_mask_shift,
mode_info->PixelInformation.BlueMask);
break;
default:
return false;
}
ret->memory_model = 0x06;
ret->framebuffer_pitch = mode_info->PixelsPerScanLine * (ret->framebuffer_bpp / 8);
ret->framebuffer_width = mode_info->HorizontalResolution;
ret->framebuffer_height = mode_info->VerticalResolution;
return true;
}
bool gop_force_16 = false;
static bool try_mode(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
size_t mode, uint64_t width, uint64_t height, int bpp,
struct fb_info *fbs, size_t fbs_count) {
EFI_STATUS status;
if (!mode_to_fb_info(ret, gop, mode)) {
return false;
}
if (width != 0 && height != 0 && bpp != 0) {
if (ret->framebuffer_width != width
|| ret->framebuffer_height != height
|| ret->framebuffer_bpp != bpp) {
return false;
}
}
if (gop_force_16) {
if (ret->framebuffer_width >= 65536
|| ret->framebuffer_height >= 65536
|| ret->framebuffer_pitch >= 65536) {
return false;
}
}
for (size_t i = 0; i < fbs_count; i++) {
if (gop->Mode->FrameBufferBase == fbs[i].framebuffer_addr) {
return false;
}
}
printv("gop: Found matching mode %x, attempting to set...\n", mode);
if (mode == gop->Mode->Mode) {
printv("gop: Mode was already set, perfect!\n");
} else {
status = gop->SetMode(gop, mode);
if (status) {
printv("gop: Failed to set video mode %x, moving on...\n", mode);
return false;
}
}
ret->framebuffer_addr = gop->Mode->FrameBufferBase;
fb_clear(ret);
return true;
}
static struct fb_info *get_mode_list(size_t *count, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop) {
UINTN modes_count = gop->Mode->MaxMode;
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
size_t actual_count = 0;
for (size_t i = 0; i < modes_count; i++) {
if (mode_to_fb_info(&ret[actual_count], gop, i)) {
actual_count++;
}
}
struct fb_info *tmp = ext_mem_alloc(actual_count * sizeof(struct fb_info));
memcpy(tmp, ret, actual_count * sizeof(struct fb_info));
pmm_free(ret, modes_count * sizeof(struct fb_info));
ret = tmp;
*count = modes_count;
return ret;
}
#define MAX_PRESET_MODES 128
no_unwind static int preset_modes[MAX_PRESET_MODES];
no_unwind static bool preset_modes_initialised = false;
void init_gop(struct fb_info **ret, size_t *_fbs_count,
uint64_t target_width, uint64_t target_height, uint16_t target_bpp) {
if (preset_modes_initialised == false) {
for (size_t i = 0; i < MAX_PRESET_MODES; i++) {
preset_modes[i] = -1;
}
preset_modes_initialised = true;
}
EFI_STATUS status;
EFI_HANDLE tmp_handles[1];
EFI_HANDLE *handles = tmp_handles;
UINTN handles_size = sizeof(EFI_HANDLE);
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles);
if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
*_fbs_count = 0;
return;
}
handles = ext_mem_alloc(handles_size);
status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles);
if (status != EFI_SUCCESS) {
pmm_free(handles, handles_size);
*_fbs_count = 0;
return;
}
size_t handles_count = handles_size / sizeof(EFI_HANDLE);
*ret = ext_mem_alloc(handles_count * sizeof(struct fb_info));
const struct resolution fallback_resolutions[] = {
{ 0, 0, 0 }, // Overridden by EDID
{ 0, 0, 0 }, // Overridden by preset
{ 1024, 768, 32 },
{ 800, 600, 32 },
{ 640, 480, 32 },
{ 1024, 768, 24 },
{ 800, 600, 24 },
{ 640, 480, 24 },
{ 1024, 768, 16 },
{ 800, 600, 16 },
{ 640, 480, 16 }
};
size_t fbs_count = 0;
for (size_t i = 0; i < handles_count && i < MAX_PRESET_MODES; i++) {
struct fb_info *fb = &(*ret)[fbs_count];
uint64_t _target_width = target_width;
uint64_t _target_height = target_height;
uint64_t _target_bpp = target_bpp;
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
status = gBS->HandleProtocol(handles[i], &gop_guid, (void **)&gop);
if (status != EFI_SUCCESS) {
continue;
}
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
UINTN mode_info_size;
status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode,
&mode_info_size, &mode_info);
if (status == EFI_NOT_STARTED) {
status = gop->SetMode(gop, 0);
if (status) {
continue;
}
status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode,
&mode_info_size, &mode_info);
}
if (status) {
continue;
}
if (preset_modes[i] == -1) {
preset_modes[i] = gop->Mode->Mode;
}
fb->edid = get_edid_info(handles[i]);
UINTN modes_count = gop->Mode->MaxMode;
size_t current_fallback = 0;
if (!_target_width || !_target_height || !_target_bpp) {
goto fallback;
} else {
printv("gop: Requested resolution of %ux%ux%u\n",
_target_width, _target_height, _target_bpp);
}
retry:
for (size_t j = 0; j < modes_count; j++) {
if (try_mode(fb, gop, j, _target_width, _target_height, _target_bpp, *ret, fbs_count)) {
goto success;
}
}
fallback:
if (current_fallback == 0) {
current_fallback++;
if (fb->edid != NULL) {
uint64_t edid_width = (uint64_t)fb->edid->det_timing_desc1[2];
edid_width += ((uint64_t)fb->edid->det_timing_desc1[4] & 0xf0) << 4;
uint64_t edid_height = (uint64_t)fb->edid->det_timing_desc1[5];
edid_height += ((uint64_t)fb->edid->det_timing_desc1[7] & 0xf0) << 4;
if (edid_width >= mode_info->HorizontalResolution
&& edid_height >= mode_info->VerticalResolution) {
_target_width = edid_width;
_target_height = edid_height;
_target_bpp = 32;
goto retry;
}
}
}
if (current_fallback == 1) {
current_fallback++;
if (try_mode(fb, gop, preset_modes[i], 0, 0, 0, *ret, fbs_count)) {
goto success;
}
}
if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) {
current_fallback++;
_target_width = fallback_resolutions[current_fallback].width;
_target_height = fallback_resolutions[current_fallback].height;
_target_bpp = fallback_resolutions[current_fallback].bpp;
goto retry;
}
continue;
success:;
size_t mode_count;
fb->mode_list = get_mode_list(&mode_count, gop);
fb->mode_count = mode_count;
fbs_count++;
}
pmm_free(handles, handles_size);
gop_force_16 = false;
*_fbs_count = fbs_count;
}
#endif

18
common/drivers/gop.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __DRIVERS__GOP_H__
#define __DRIVERS__GOP_H__
#if defined (UEFI)
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/fb.h>
void init_gop(struct fb_info **ret, size_t *_fbs_count,
uint64_t target_width, uint64_t target_height, uint16_t target_bpp);
extern bool gop_force_16;
#endif
#endif

13
common/drivers/serial.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __DRIVERS__SERIAL_H__
#define __DRIVERS__SERIAL_H__
#if defined (BIOS)
#include <stdint.h>
void serial_out(uint8_t b);
int serial_in(void);
#endif
#endif

View File

@ -0,0 +1,46 @@
#if defined (BIOS)
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <drivers/serial.h>
#include <sys/cpu.h>
#include <lib/misc.h>
static bool serial_initialised = false;
static void serial_initialise(void) {
if (serial_initialised) {
return;
}
// Init com1
outb(0x3f8 + 3, 0x00);
outb(0x3f8 + 1, 0x00);
outb(0x3f8 + 3, 0x80);
outb(0x3f8 + 0, 0x0c); // 9600 baud
outb(0x3f8 + 1, 0x00);
outb(0x3f8 + 3, 0x03);
outb(0x3f8 + 2, 0xc7);
outb(0x3f8 + 4, 0x0b);
serial_initialised = true;
}
void serial_out(uint8_t b) {
serial_initialise();
while ((inb(0x3f8 + 5) & 0x20) == 0);
outb(0x3f8, b);
}
int serial_in(void) {
serial_initialise();
if ((inb(0x3f8 + 5) & 0x01) == 0) {
return -1;
}
return inb(0x3f8);
}
#endif

303
common/drivers/vbe.c Normal file
View File

@ -0,0 +1,303 @@
#if defined (BIOS)
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <drivers/vbe.h>
#include <drivers/edid.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/real.h>
#include <lib/print.h>
#include <lib/image.h>
#include <lib/config.h>
#include <lib/uri.h>
#include <lib/term.h>
#include <mm/pmm.h>
struct vbe_info_struct {
char signature[4];
uint8_t version_min;
uint8_t version_maj;
uint16_t oem_off;
uint16_t oem_seg;
uint32_t capabilities;
uint16_t vid_modes_off;
uint16_t vid_modes_seg;
uint16_t vid_mem_blocks;
uint16_t software_rev;
uint16_t vendor_off;
uint16_t vendor_seg;
uint16_t prod_name_off;
uint16_t prod_name_seg;
uint16_t prod_rev_off;
uint16_t prod_rev_seg;
uint8_t reserved[222];
uint8_t oem_data[256];
} __attribute__((packed));
struct vbe_mode_info_struct {
uint16_t mode_attributes;
uint8_t wina_attributes;
uint8_t winb_attributes;
uint16_t win_granularity;
uint16_t win_size;
uint16_t wina_segment;
uint16_t winb_segment;
uint32_t win_farptr;
uint16_t bytes_per_scanline;
uint16_t res_x;
uint16_t res_y;
uint8_t charsize_x;
uint8_t charsize_y;
uint8_t plane_count;
uint8_t bpp;
uint8_t bank_count;
uint8_t memory_model;
uint8_t bank_size;
uint8_t image_count;
uint8_t reserved0;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
uint8_t rsvd_mask_size;
uint8_t rsvd_mask_shift;
uint8_t direct_color_info;
uint32_t framebuffer_addr;
uint8_t reserved1[6];
uint16_t lin_bytes_per_scanline;
uint8_t banked_image_count;
uint8_t lin_image_count;
uint8_t lin_red_mask_size;
uint8_t lin_red_mask_shift;
uint8_t lin_green_mask_size;
uint8_t lin_green_mask_shift;
uint8_t lin_blue_mask_size;
uint8_t lin_blue_mask_shift;
uint8_t lin_rsvd_mask_size;
uint8_t lin_rsvd_mask_shift;
uint32_t max_pixel_clock;
uint8_t reserved2[189];
} __attribute__((packed));
static void get_vbe_info(struct vbe_info_struct *buf) {
struct rm_regs r = {0};
r.eax = 0x4f00;
r.edi = (uint32_t)buf;
rm_int(0x10, &r, &r);
}
static void get_vbe_mode_info(struct vbe_mode_info_struct *buf,
uint16_t mode) {
struct rm_regs r = {0};
r.eax = 0x4f01;
r.ecx = (uint32_t)mode;
r.edi = (uint32_t)buf;
rm_int(0x10, &r, &r);
}
static int set_vbe_mode(uint16_t mode) {
struct rm_regs r = {0};
r.eax = 0x4f02;
r.ebx = (uint32_t)mode | (1 << 14);
rm_int(0x10, &r, &r);
return r.eax & 0xff;
}
struct fb_info *vbe_get_mode_list(size_t *count) {
struct vbe_info_struct vbe_info;
get_vbe_info(&vbe_info);
uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
vbe_info.vid_modes_off);
size_t modes_count = 0;
for (size_t i = 0; vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
// We only support RGB for now
if (vbe_mode_info.memory_model != 0x06)
continue;
// We only support linear modes
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
continue;
modes_count++;
}
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
for (size_t i = 0, j = 0; vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
// We only support RGB for now
if (vbe_mode_info.memory_model != 0x06)
continue;
// We only support linear modes
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
continue;
ret[j].memory_model = vbe_mode_info.memory_model;
ret[j].framebuffer_width = vbe_mode_info.res_x;
ret[j].framebuffer_height = vbe_mode_info.res_y;
ret[j].framebuffer_bpp = vbe_mode_info.bpp;
if (vbe_info.version_maj < 3) {
ret[j].framebuffer_pitch = vbe_mode_info.bytes_per_scanline;
ret[j].red_mask_size = vbe_mode_info.red_mask_size;
ret[j].red_mask_shift = vbe_mode_info.red_mask_shift;
ret[j].green_mask_size = vbe_mode_info.green_mask_size;
ret[j].green_mask_shift = vbe_mode_info.green_mask_shift;
ret[j].blue_mask_size = vbe_mode_info.blue_mask_size;
ret[j].blue_mask_shift = vbe_mode_info.blue_mask_shift;
} else {
ret[j].framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline;
ret[j].red_mask_size = vbe_mode_info.lin_red_mask_size;
ret[j].red_mask_shift = vbe_mode_info.lin_red_mask_shift;
ret[j].green_mask_size = vbe_mode_info.lin_green_mask_size;
ret[j].green_mask_shift = vbe_mode_info.lin_green_mask_shift;
ret[j].blue_mask_size = vbe_mode_info.lin_blue_mask_size;
ret[j].blue_mask_shift = vbe_mode_info.lin_blue_mask_shift;
}
j++;
}
*count = modes_count;
return ret;
}
bool init_vbe(struct fb_info *ret,
uint16_t target_width, uint16_t target_height, uint16_t target_bpp) {
printv("vbe: Initialising...\n");
size_t current_fallback = 0;
struct vbe_info_struct vbe_info;
get_vbe_info(&vbe_info);
printv("vbe: Version: %u.%u\n", vbe_info.version_maj, vbe_info.version_min);
printv("vbe: OEM: %s\n", (char *)rm_desegment(vbe_info.oem_seg, vbe_info.oem_off));
printv("vbe: Graphics vendor: %s\n", (char *)rm_desegment(vbe_info.vendor_seg, vbe_info.vendor_off));
printv("vbe: Product name: %s\n", (char *)rm_desegment(vbe_info.prod_name_seg, vbe_info.prod_name_off));
printv("vbe: Product revision: %s\n", (char *)rm_desegment(vbe_info.prod_rev_seg, vbe_info.prod_rev_off));
uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
vbe_info.vid_modes_off);
struct resolution fallback_resolutions[] = {
{ 1024, 768, 32 },
{ 800, 600, 32 },
{ 640, 480, 32 },
{ 1024, 768, 24 },
{ 800, 600, 24 },
{ 640, 480, 24 },
{ 1024, 768, 16 },
{ 800, 600, 16 },
{ 640, 480, 16 }
};
if (!target_width || !target_height || !target_bpp) {
struct edid_info_struct *edid_info = get_edid_info();
if (edid_info != NULL) {
int edid_width = (int)edid_info->det_timing_desc1[2];
edid_width += ((int)edid_info->det_timing_desc1[4] & 0xf0) << 4;
int edid_height = (int)edid_info->det_timing_desc1[5];
edid_height += ((int)edid_info->det_timing_desc1[7] & 0xf0) << 4;
if (edid_width && edid_height) {
target_width = edid_width;
target_height = edid_height;
target_bpp = 32;
printv("vbe: EDID detected screen resolution of %ux%u\n",
target_width, target_height);
goto retry;
}
}
goto fallback;
} else {
printv("vbe: Requested resolution of %ux%ux%u\n",
target_width, target_height, target_bpp);
}
retry:
for (size_t i = 0; vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
if (vbe_mode_info.res_x == target_width
&& vbe_mode_info.res_y == target_height
&& vbe_mode_info.bpp == target_bpp) {
// We only support RGB for now
if (vbe_mode_info.memory_model != 0x06)
continue;
// We only support linear modes
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
continue;
printv("vbe: Found matching mode %x, attempting to set...\n", vid_modes[i]);
if (vid_modes[i] == current_video_mode) {
printv("vbe: Mode was already set, perfect!\n");
} else if (set_vbe_mode(vid_modes[i]) == 0x01) {
current_video_mode = -1;
printv("vbe: Failed to set video mode %x, moving on...\n", vid_modes[i]);
continue;
}
current_video_mode = vid_modes[i];
printv("vbe: Framebuffer address: %x\n", vbe_mode_info.framebuffer_addr);
ret->memory_model = vbe_mode_info.memory_model;
ret->framebuffer_addr = vbe_mode_info.framebuffer_addr;
ret->framebuffer_width = vbe_mode_info.res_x;
ret->framebuffer_height = vbe_mode_info.res_y;
ret->framebuffer_bpp = vbe_mode_info.bpp;
if (vbe_info.version_maj < 3) {
ret->framebuffer_pitch = vbe_mode_info.bytes_per_scanline;
ret->red_mask_size = vbe_mode_info.red_mask_size;
ret->red_mask_shift = vbe_mode_info.red_mask_shift;
ret->green_mask_size = vbe_mode_info.green_mask_size;
ret->green_mask_shift = vbe_mode_info.green_mask_shift;
ret->blue_mask_size = vbe_mode_info.blue_mask_size;
ret->blue_mask_shift = vbe_mode_info.blue_mask_shift;
} else {
ret->framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline;
ret->red_mask_size = vbe_mode_info.lin_red_mask_size;
ret->red_mask_shift = vbe_mode_info.lin_red_mask_shift;
ret->green_mask_size = vbe_mode_info.lin_green_mask_size;
ret->green_mask_shift = vbe_mode_info.lin_green_mask_shift;
ret->blue_mask_size = vbe_mode_info.lin_blue_mask_size;
ret->blue_mask_shift = vbe_mode_info.lin_blue_mask_shift;
}
fb_clear(ret);
return true;
}
}
fallback:
if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) {
target_width = fallback_resolutions[current_fallback].width;
target_height = fallback_resolutions[current_fallback].height;
target_bpp = fallback_resolutions[current_fallback].bpp;
current_fallback++;
goto retry;
}
return false;
}
#endif

14
common/drivers/vbe.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __DRIVERS__VBE_H__
#define __DRIVERS__VBE_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/fb.h>
bool init_vbe(struct fb_info *ret,
uint16_t target_width, uint16_t target_height, uint16_t target_bpp);
struct fb_info *vbe_get_mode_list(size_t *count);
#endif

View File

@ -0,0 +1,368 @@
#if defined (BIOS)
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <drivers/vga_textmode.h>
#include <sys/cpu.h>
#include <lib/real.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/term.h>
#include <mm/pmm.h>
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
static void draw_cursor(struct textmode_context *ctx) {
uint8_t pal = ctx->back_buffer[ctx->cursor_offset + 1];
ctx->video_mem[ctx->cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
}
static void text_save_state(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->saved_state_text_palette = ctx->text_palette;
ctx->saved_state_cursor_offset = ctx->cursor_offset;
}
static void text_restore_state(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = ctx->saved_state_text_palette;
ctx->cursor_offset = ctx->saved_state_cursor_offset;
}
static void text_swap_palette(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette << 4) | (ctx->text_palette >> 4);
}
static void text_scroll(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
// move the text up by one row
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
i < (_ctx->scroll_bottom_margin - 1) * VD_COLS; i++) {
ctx->back_buffer[i] = ctx->back_buffer[i + VD_COLS];
}
// clear the last line of the screen
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS;
i < _ctx->scroll_bottom_margin * VD_COLS; i += 2) {
ctx->back_buffer[i] = ' ';
ctx->back_buffer[i + 1] = ctx->text_palette;
}
}
static void text_revscroll(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
// move the text up by one row
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
ctx->back_buffer[i + VD_COLS] = ctx->back_buffer[i];
if (i == _ctx->scroll_top_margin * VD_COLS) {
break;
}
}
// clear the first line of the screen
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
i < (_ctx->scroll_top_margin + 1) * VD_COLS; i += 2) {
ctx->back_buffer[i] = ' ';
ctx->back_buffer[i + 1] = ctx->text_palette;
}
}
static void text_clear(struct flanterm_context *_ctx, bool move) {
struct textmode_context *ctx = (void *)_ctx;
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
ctx->back_buffer[i] = ' ';
ctx->back_buffer[i + 1] = ctx->text_palette;
}
if (move) {
ctx->cursor_offset = 0;
}
}
static void text_full_refresh(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
ctx->video_mem[i] = ctx->front_buffer[i];
ctx->back_buffer[i] = ctx->front_buffer[i];
}
if (_ctx->cursor_enabled) {
draw_cursor(ctx);
ctx->old_cursor_offset = ctx->cursor_offset;
}
}
static void text_double_buffer_flush(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
if (_ctx->cursor_enabled) {
draw_cursor(ctx);
}
if (ctx->cursor_offset != ctx->old_cursor_offset || _ctx->cursor_enabled == false) {
ctx->video_mem[ctx->old_cursor_offset + 1] = ctx->back_buffer[ctx->old_cursor_offset + 1];
}
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
if (ctx->back_buffer[i] == ctx->front_buffer[i]) {
continue;
}
ctx->front_buffer[i] = ctx->back_buffer[i];
if (_ctx->cursor_enabled && i == ctx->cursor_offset + 1) {
continue;
}
ctx->video_mem[i] = ctx->back_buffer[i];
}
if (_ctx->cursor_enabled) {
ctx->old_cursor_offset = ctx->cursor_offset;
}
}
static void text_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) {
struct textmode_context *ctx = (void *)_ctx;
*x = (ctx->cursor_offset % VD_COLS) / 2;
*y = ctx->cursor_offset / VD_COLS;
}
static void text_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
struct textmode_context *ctx = (void *)_ctx;
if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS
|| new_x >= VD_COLS / 2 || new_y >= VD_ROWS) {
return;
}
ctx->back_buffer[new_y * VD_COLS + new_x * 2] = ctx->back_buffer[old_y * VD_COLS + old_x * 2];
}
static void text_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) {
struct textmode_context *ctx = (void *)_ctx;
if (x >= VD_COLS / 2) {
if ((int)x < 0) {
x = 0;
} else {
x = VD_COLS / 2 - 1;
}
}
if (y >= VD_ROWS) {
if ((int)y < 0) {
y = 0;
} else {
y = VD_ROWS - 1;
}
}
ctx->cursor_offset = y * VD_COLS + x * 2;
}
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
static void text_set_text_fg(struct flanterm_context *_ctx, size_t fg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | ansi_colours[fg];
}
static void text_set_text_bg(struct flanterm_context *_ctx, size_t bg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0x0f) | (ansi_colours[bg] << 4);
}
static void text_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
}
static void text_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
}
static void text_set_text_fg_rgb(struct flanterm_context *ctx, uint32_t n) {
(void)ctx;
(void)n;
}
static void text_set_text_bg_rgb(struct flanterm_context *ctx, uint32_t n) {
(void)ctx;
(void)n;
}
static void text_set_text_fg_default(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | 7;
}
static void text_set_text_bg_default(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette &= 0x0f;
}
static void text_set_text_fg_default_bright(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0xf0) | (7 | (1 << 3));
}
static void text_set_text_bg_default_bright(struct flanterm_context *_ctx) {
struct textmode_context *ctx = (void *)_ctx;
ctx->text_palette = (ctx->text_palette & 0x0f) | ((1 << 3) << 4);
}
static void text_putchar(struct flanterm_context *_ctx, uint8_t c) {
struct textmode_context *ctx = (void *)_ctx;
ctx->back_buffer[ctx->cursor_offset] = c;
ctx->back_buffer[ctx->cursor_offset + 1] = ctx->text_palette;
if (ctx->cursor_offset / VD_COLS == _ctx->scroll_bottom_margin - 1
&& ctx->cursor_offset % VD_COLS == VD_COLS - 2) {
if (_ctx->scroll_enabled) {
text_scroll(_ctx);
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
}
} else if (ctx->cursor_offset >= (VIDEO_BOTTOM - 1)) {
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
} else {
ctx->cursor_offset += 2;
}
}
static void text_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) {
struct textmode_context *ctx = (void *)_ctx;
if (ctx->back_buffer != NULL) {
_free(ctx->back_buffer, VD_ROWS * VD_COLS);
ctx->back_buffer = NULL;
}
if (ctx->front_buffer != NULL) {
_free(ctx->front_buffer, VD_ROWS * VD_COLS);
ctx->front_buffer = NULL;
}
pmm_free(ctx, sizeof(struct textmode_context));
}
void vga_textmode_init(bool managed) {
term_notready();
if (quiet) {
return;
}
if (current_video_mode != 0x3) {
struct rm_regs r = {0};
r.eax = 0x0003;
rm_int(0x10, &r, &r);
current_video_mode = 0x3;
}
terms = ext_mem_alloc(sizeof(void *));
terms_i = 1;
terms[0] = ext_mem_alloc(sizeof(struct textmode_context));
struct flanterm_context *term = terms[0];
struct textmode_context *ctx = (void *)term;
if (ctx->back_buffer == NULL) {
ctx->back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
} else {
memset(ctx->back_buffer, 0, VD_ROWS * VD_COLS);
}
if (ctx->front_buffer == NULL) {
ctx->front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
} else {
memset(ctx->front_buffer, 0, VD_ROWS * VD_COLS);
}
ctx->cursor_offset = 0;
ctx->text_palette = 0x07;
ctx->video_mem = (volatile uint8_t *)0xb8000;
text_clear(term, false);
// VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
if (!managed) {
term->cursor_enabled = false;
outb(0x3d4, 0x0a);
outb(0x3d5, (inb(0x3d5) & 0xc0) | 14);
outb(0x3d4, 0x0b);
outb(0x3d5, (inb(0x3d5) & 0xe0) | 15);
outb(0x3d4, 0x0f);
outb(0x3d5, 0);
outb(0x3d4, 0x0e);
outb(0x3d5, 0);
struct rm_regs r = {0};
r.eax = 0x0200;
rm_int(0x10, &r, &r);
} else {
outb(0x3d4, 0x0a);
outb(0x3d5, 0x20);
}
text_double_buffer_flush(term);
if (managed && serial) {
term->cols = 80;
term->rows = 24;
} else {
term->cols = 80;
term->rows = 25;
}
term->raw_putchar = text_putchar;
term->clear = text_clear;
term->set_cursor_pos = text_set_cursor_pos;
term->get_cursor_pos = text_get_cursor_pos;
term->set_text_fg = text_set_text_fg;
term->set_text_bg = text_set_text_bg;
term->set_text_fg_bright = text_set_text_fg_bright;
term->set_text_bg_bright = text_set_text_bg_bright;
term->set_text_fg_rgb = text_set_text_fg_rgb;
term->set_text_bg_rgb = text_set_text_bg_rgb;
term->set_text_fg_default = text_set_text_fg_default;
term->set_text_bg_default = text_set_text_bg_default;
term->set_text_fg_default_bright = text_set_text_fg_default_bright;
term->set_text_bg_default_bright = text_set_text_bg_default_bright;
term->move_character = text_move_character;
term->scroll = text_scroll;
term->revscroll = text_revscroll;
term->swap_palette = text_swap_palette;
term->save_state = text_save_state;
term->restore_state = text_restore_state;
term->double_buffer_flush = text_double_buffer_flush;
term->full_refresh = text_full_refresh;
term->deinit = text_deinit;
flanterm_context_reinit(term);
if (!managed) {
term->cursor_enabled = false;
}
term->full_refresh(term);
if (!managed) {
term->deinit(term, pmm_free);
pmm_free(terms, sizeof(void *));
terms_i = 0;
terms = NULL;
term_backend = _NOT_READY;
} else {
term_backend = TEXTMODE;
}
}
#endif

View File

@ -0,0 +1,35 @@
#ifndef __DRIVERS__VGA_TEXTMODE_H__
#define __DRIVERS__VGA_TEXTMODE_H__
#if defined (BIOS)
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <flanterm/flanterm.h>
#define VD_COLS (80 * 2)
#define VD_ROWS 25
struct textmode_context {
struct flanterm_context term;
volatile uint8_t *video_mem;
uint8_t *back_buffer;
uint8_t *front_buffer;
size_t cursor_offset;
size_t old_cursor_offset;
bool cursor_status;
uint8_t text_palette;
uint8_t saved_state_text_palette;
size_t saved_state_cursor_offset;
};
void vga_textmode_init(bool managed);
#endif
#endif

View File

@ -0,0 +1,12 @@
.section .text
.global efi_main
.extern uefi_entry
efi_main:
mov x30, xzr
mov x29, xzr
b uefi_entry
.section .note.GNU-stack,"",%progbits

View File

@ -0,0 +1,10 @@
section .text
global efi_main
extern uefi_entry
efi_main:
xor eax, eax
mov [esp], eax
jmp uefi_entry
section .note.GNU-stack noalloc noexec nowrite progbits

View File

@ -0,0 +1,11 @@
.section .text
.global efi_main
.extern uefi_entry
efi_main:
.option norelax
mv fp, zero
mv ra, zero
j uefi_entry
.section .note.GNU-stack,"",%progbits

View File

@ -0,0 +1,10 @@
section .text
global efi_main
extern uefi_entry
efi_main:
xor eax, eax
mov [rsp], rax
jmp uefi_entry
section .note.GNU-stack noalloc noexec nowrite progbits

128
common/entry.s2.c Normal file
View File

@ -0,0 +1,128 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdnoreturn.h>
#include <lib/term.h>
#include <lib/real.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/part.h>
#include <lib/config.h>
#include <lib/trace.h>
#include <sys/e820.h>
#include <sys/a20.h>
#include <lib/print.h>
#include <fs/file.h>
#include <lib/elf.h>
#include <mm/pmm.h>
#include <protos/linux.h>
#include <protos/chainload.h>
#include <menu.h>
#include <pxe/pxe.h>
#include <pxe/tftp.h>
#include <drivers/disk.h>
#include <sys/idt.h>
#include <sys/cpu.h>
struct volume *boot_volume;
#if defined (BIOS)
bool stage3_loaded = false;
static bool stage3_found = false;
extern symbol stage3_addr;
extern symbol limine_bios_sys_size;
extern symbol build_id_s2;
extern symbol build_id_s3;
static bool stage3_init(struct volume *part) {
struct file_handle *stage3;
bool old_cif = case_insensitive_fopen;
case_insensitive_fopen = true;
if ((stage3 = fopen(part, "/limine-bios.sys")) == NULL
&& (stage3 = fopen(part, "/limine/limine-bios.sys")) == NULL
&& (stage3 = fopen(part, "/boot/limine-bios.sys")) == NULL
&& (stage3 = fopen(part, "/boot/limine/limine-bios.sys")) == NULL) {
case_insensitive_fopen = old_cif;
return false;
}
case_insensitive_fopen = old_cif;
stage3_found = true;
if (stage3->size != (size_t)limine_bios_sys_size) {
print("limine-bios.sys size incorrect.\n");
return false;
}
fread(stage3, stage3_addr,
(uintptr_t)stage3_addr - 0xf000,
stage3->size - ((uintptr_t)stage3_addr - 0xf000));
fclose(stage3);
if (memcmp(build_id_s2 + 16, build_id_s3 + 16, 20) != 0) {
print("limine-bios.sys build ID mismatch.\n");
return false;
}
stage3_loaded = true;
return true;
}
enum {
BOOTED_FROM_HDD = 0,
BOOTED_FROM_PXE = 1,
BOOTED_FROM_CD = 2
};
noreturn void entry(uint8_t boot_drive, int boot_from) {
// XXX DO NOT MOVE A20 ENABLE CALL
if (!a20_enable()) {
panic(false, "Could not enable A20 line");
}
init_e820();
init_memmap();
init_idt();
disk_create_index();
if (boot_from == BOOTED_FROM_HDD || boot_from == BOOTED_FROM_CD) {
boot_volume = volume_get_by_bios_drive(boot_drive);
} else if (boot_from == BOOTED_FROM_PXE) {
pxe_init();
boot_volume = pxe_bind_volume();
}
if (boot_volume == NULL) {
panic(false, "Could not determine boot drive");
}
volume_iterate_parts(boot_volume,
if (stage3_init(_PART)) {
break;
}
);
if (!stage3_found) {
print("\n"
"!! Stage 3 file not found!\n"
"!! Have you copied limine-bios.sys to the root, /boot, /limine, or /boot/limine\n"
"!! directories of one of the partitions on the boot device?\n\n");
}
if (!stage3_loaded) {
panic(false, "Failed to load stage 3.");
}
term_fallback();
stage3_common();
}
#endif

149
common/entry.s3.c Normal file
View File

@ -0,0 +1,149 @@
#include <stddef.h>
#include <stdint.h>
#include <stdnoreturn.h>
#include <lib/term.h>
#include <lib/real.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/part.h>
#include <lib/config.h>
#include <lib/trace.h>
#include <sys/e820.h>
#include <sys/a20.h>
#include <sys/idt.h>
#include <sys/gdt.h>
#include <lib/print.h>
#include <fs/file.h>
#include <lib/elf.h>
#include <mm/pmm.h>
#include <menu.h>
#include <pxe/pxe.h>
#include <pxe/tftp.h>
#include <drivers/disk.h>
#include <sys/lapic.h>
#include <lib/readline.h>
#include <sys/cpu.h>
void stage3_common(void);
#if defined (UEFI)
extern symbol __slide;
noreturn void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
efi_image_handle = ImageHandle;
EFI_STATUS status;
gST->ConOut->EnableCursor(gST->ConOut, false);
init_memmap();
term_fallback();
status = gBS->SetWatchdogTimer(0, 0x10000, 0, NULL);
if (status) {
print("WARNING: Failed to disable watchdog timer!\n");
}
#if defined (__x86_64__) || defined (__i386__)
init_gdt();
#endif
#if defined (__x86_64__)
if ((uintptr_t)__slide >= 0x100000000) {
panic(false, "Limine does not support being loaded above 4GiB");
}
#endif
disk_create_index();
boot_volume = NULL;
EFI_HANDLE current_handle = ImageHandle;
for (size_t j = 0; j < 25; j++) {
if (current_handle == NULL) {
could_not_match:
print("WARNING: Could not meaningfully match the boot device handle with a volume.\n");
print(" Using the first volume containing a Limine configuration!\n");
for (size_t i = 0; i < volume_index_i; i++) {
struct file_handle *f;
bool old_cif = case_insensitive_fopen;
case_insensitive_fopen = true;
if ((f = fopen(volume_index[i], "/limine.cfg")) == NULL
&& (f = fopen(volume_index[i], "/limine/limine.cfg")) == NULL
&& (f = fopen(volume_index[i], "/boot/limine.cfg")) == NULL
&& (f = fopen(volume_index[i], "/boot/limine/limine.cfg")) == NULL
&& (f = fopen(volume_index[i], "/EFI/BOOT/limine.cfg")) == NULL) {
case_insensitive_fopen = old_cif;
continue;
}
case_insensitive_fopen = old_cif;
fclose(f);
if (volume_index[i]->backing_dev != NULL) {
boot_volume = volume_index[i]->backing_dev;
} else {
boot_volume = volume_index[i];
}
break;
}
if (boot_volume != NULL) {
stage3_common();
}
panic(false, "No volume contained a Limine configuration file");
}
EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
status = gBS->HandleProtocol(current_handle, &loaded_img_prot_guid,
(void **)&loaded_image);
if (status) {
goto could_not_match;
}
boot_volume = disk_volume_from_efi_handle(loaded_image->DeviceHandle);
if (boot_volume != NULL) {
stage3_common();
}
current_handle = loaded_image->ParentHandle;
}
goto could_not_match;
}
#endif
noreturn void stage3_common(void) {
#if defined (__x86_64__) || defined (__i386__)
init_flush_irqs();
init_io_apics();
#endif
#if defined (__riscv)
#if defined (UEFI)
RISCV_EFI_BOOT_PROTOCOL *rv_proto = get_riscv_boot_protocol();
if (rv_proto == NULL || rv_proto->GetBootHartId(rv_proto, &bsp_hartid) != EFI_SUCCESS) {
panic(false, "failed to get BSP's hartid");
}
#else
#error riscv: only UEFI is supported
#endif
init_riscv();
#endif
term_notready();
menu(true);
}

View File

@ -0,0 +1,31 @@
extern bss_begin
extern bss_end
extern entry
extern gdt
section .entry progbits alloc exec nowrite align=16
global _start
_start:
cld
; Zero out .bss
xor al, al
mov edi, bss_begin
mov ecx, bss_end
sub ecx, bss_begin
rep stosb
lgdt [gdt]
jmp 0x18:.reload_cs
.reload_cs:
mov eax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp entry
section .note.GNU-stack noalloc noexec nowrite progbits

13
common/fs/ext2.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __FS__EXT2_H__
#define __FS__EXT2_H__
#include <stdbool.h>
#include <lib/part.h>
#include <fs/file.h>
bool ext2_get_guid(struct guid *guid, struct volume *part);
char *ext2_get_label(struct volume *part);
struct file_handle *ext2_open(struct volume *part, const char *path);
#endif

685
common/fs/ext2.s2.c Normal file
View File

@ -0,0 +1,685 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <fs/ext2.h>
#include <drivers/disk.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/print.h>
#include <mm/pmm.h>
/* Superblock Fields */
struct ext2_superblock {
uint32_t s_inodes_count;
uint32_t s_blocks_count;
uint32_t s_r_blocks_count;
uint32_t s_free_blocks_count;
uint32_t s_free_inodes_count;
uint32_t s_first_data_block;
uint32_t s_log_block_size;
uint32_t s_log_frag_size;
uint32_t s_blocks_per_group;
uint32_t s_frags_per_group;
uint32_t s_inodes_per_group;
uint32_t s_mtime;
uint32_t s_wtime;
uint16_t s_mnt_count;
uint16_t s_max_mnt_count;
uint16_t s_magic;
uint16_t s_state;
uint16_t s_errors;
uint16_t s_minor_rev_level;
uint32_t s_lastcheck;
uint32_t s_checkinterval;
uint32_t s_creator_os;
uint32_t s_rev_level;
uint16_t s_def_resuid;
uint16_t s_def_gid;
// if version number >= 1, we have to use the ext2 extended superblock as well
/* Extended Superblock */
uint32_t s_first_ino;
uint16_t s_inode_size;
uint16_t s_block_group_nr;
uint32_t s_feature_compat;
uint32_t s_feature_incompat;
uint32_t s_feature_ro_compat;
uint64_t s_uuid[2];
uint8_t s_volume_name[16];
uint64_t s_last_mounted[8];
uint32_t compression_info;
uint8_t prealloc_blocks;
uint8_t prealloc_dir_blocks;
uint16_t reserved_gdt_blocks;
uint8_t journal_uuid[16];
uint32_t journal_inum;
uint32_t journal_dev;
uint32_t last_orphan;
uint32_t hash_seed[4];
uint8_t def_hash_version;
uint8_t jnl_backup_type;
uint16_t group_desc_size;
uint32_t default_mount_opts;
uint32_t first_meta_bg;
uint32_t mkfs_time;
uint32_t jnl_blocks[17];
} __attribute__((packed));
struct ext2_linux {
uint8_t frag_num;
uint8_t frag_size;
uint16_t reserved_16;
uint16_t user_id_high;
uint16_t group_id_high;
uint32_t reserved_32;
} __attribute__((packed));
struct ext2_inode {
uint16_t i_mode;
uint16_t i_uid;
uint32_t i_size;
uint32_t i_atime;
uint32_t i_ctime;
uint32_t i_mtime;
uint32_t i_dtime;
uint16_t i_gid;
uint16_t i_links_count;
uint32_t i_blocks_count;
uint32_t i_flags;
uint32_t i_osd1;
uint32_t i_blocks[15];
uint32_t i_generation;
/* EXT2 v >= 1.0 */
uint32_t i_eab;
uint32_t i_maj;
/* EXT2 vAll */
uint32_t i_frag_block;
struct ext2_linux i_osd2;
} __attribute__((packed));
struct ext2_file_handle {
struct volume *part;
struct ext2_superblock sb;
int size;
struct ext2_inode root_inode;
struct ext2_inode inode;
uint64_t block_size;
uint32_t *alloc_map;
};
/* Inode types */
#define S_IFIFO 0x1000
#define S_IFCHR 0x2000
#define S_IFDIR 0x4000
#define S_IFBLK 0x6000
#define S_IFREG 0x8000
#define S_IFLNK 0xa000
#define S_IFSOCK 0xc000
#define FMT_MASK 0xf000
/* EXT2 Filesystem States */
#define EXT2_FS_UNRECOVERABLE_ERRORS 3
/* Ext2 incompatible features */
#define EXT2_IF_COMPRESSION 0x01
#define EXT2_IF_EXTENTS 0x40
#define EXT2_IF_64BIT 0x80
#define EXT2_IF_INLINE_DATA 0x8000
#define EXT2_IF_ENCRYPT 0x10000
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
/* Ext4 flags */
#define EXT4_EXTENTS_FLAG 0x80000
#define EXT2_S_MAGIC 0xEF53
/* EXT2 Block Group Descriptor */
struct ext2_bgd {
uint32_t bg_block_bitmap;
uint32_t bg_inode_bitmap;
uint32_t bg_inode_table;
uint16_t bg_free_blocks_count;
uint16_t bg_free_inodes_count;
uint16_t bg_dirs_count;
uint16_t reserved[7];
} __attribute__((packed));
struct ext4_bgd {
uint32_t bg_block_bitmap;
uint32_t bg_inode_bitmap;
uint32_t bg_inode_table;
uint16_t bg_free_blocks_count;
uint16_t bg_free_inodes_count;
uint16_t bg_dirs_count;
uint16_t pad;
uint32_t reserved[3];
uint32_t block_id_hi;
uint32_t inode_id_hi;
uint32_t inode_table_id_hi;
uint16_t free_blocks_hi;
uint16_t free_inodes_hi;
uint16_t used_dirs_hi;
uint16_t pad2;
uint32_t reserved2[3];
} __attribute__((packed));
/* EXT2 Inode Types */
#define EXT2_INO_DIRECTORY 0x4000
/* EXT2 Directory Entry */
struct ext2_dir_entry {
uint32_t inode;
uint16_t rec_len;
uint8_t name_len;
uint8_t type;
} __attribute__((packed));
struct ext4_extent_header {
uint16_t magic;
uint16_t entries;
uint16_t max;
uint16_t depth;
uint16_t generation;
} __attribute__((packed));
struct ext4_extent {
uint32_t block;
uint16_t len;
uint16_t start_hi;
uint32_t start;
} __attribute__((packed));
struct ext4_extent_idx {
uint32_t block;
uint32_t leaf;
uint16_t leaf_hi;
uint16_t empty;
} __attribute__((packed));
static int inode_read(void *buf, uint64_t loc, uint64_t count,
struct ext2_inode *inode, struct ext2_file_handle *fd,
uint32_t *alloc_map);
static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handle *fd, const char *path);
// parse an inode given the partition base and inode number
static bool ext2_get_inode(struct ext2_inode *ret,
struct ext2_file_handle *fd, uint64_t inode) {
if (inode == 0)
return false;
struct ext2_superblock *sb = &fd->sb;
//determine if we need to use 64 bit inode ids
bool bit64 = false;
if (sb->s_rev_level != 0
&& (sb->s_feature_incompat & (EXT2_IF_64BIT))
&& sb->group_desc_size != 0
&& ((sb->group_desc_size & (sb->group_desc_size - 1)) == 0)) {
if(sb->group_desc_size > 32) {
bit64 = true;
}
}
const uint64_t ino_blk_grp = (inode - 1) / sb->s_inodes_per_group;
const uint64_t ino_tbl_idx = (inode - 1) % sb->s_inodes_per_group;
const uint64_t block_size = ((uint64_t)1024 << sb->s_log_block_size);
uint64_t ino_offset;
const uint64_t bgd_start_offset = block_size >= 2048 ? block_size : block_size * 2;
const uint64_t ino_size = sb->s_rev_level == 0 ? sizeof(struct ext2_inode) : sb->s_inode_size;
if (!bit64) {
struct ext2_bgd target_descriptor;
const uint64_t bgd_offset = bgd_start_offset + (sizeof(struct ext2_bgd) * ino_blk_grp);
volume_read(fd->part, &target_descriptor, bgd_offset, sizeof(struct ext2_bgd));
ino_offset = ((target_descriptor.bg_inode_table) * block_size) +
(ino_size * ino_tbl_idx);
} else {
struct ext4_bgd target_descriptor;
const uint64_t bgd_offset = bgd_start_offset + (sizeof(struct ext4_bgd) * ino_blk_grp);
volume_read(fd->part, &target_descriptor, bgd_offset, sizeof(struct ext4_bgd));
ino_offset = ((target_descriptor.bg_inode_table | (bit64 ? ((uint64_t)target_descriptor.inode_id_hi << 32) : 0)) * block_size) +
(ino_size * ino_tbl_idx);
}
volume_read(fd->part, ret, ino_offset, sizeof(struct ext2_inode));
return true;
}
static uint32_t *create_alloc_map(struct ext2_file_handle *fd,
struct ext2_inode *inode) {
if (inode->i_flags & EXT4_EXTENTS_FLAG)
return NULL;
size_t entries_per_block = fd->block_size / sizeof(uint32_t);
// Cache the map of blocks
uint32_t *alloc_map = ext_mem_alloc(inode->i_blocks_count * sizeof(uint32_t));
for (uint32_t i = 0; i < inode->i_blocks_count; i++) {
uint32_t block = i;
if (block < 12) {
// Direct block
alloc_map[i] = inode->i_blocks[block];
} else {
// Indirect block
block -= 12;
if (block >= entries_per_block) {
// Double indirect block
block -= entries_per_block;
uint32_t index = block / entries_per_block;
uint32_t indirect_block;
if (index >= entries_per_block) {
uint32_t first_index = index / entries_per_block;
uint32_t first_indirect_block;
volume_read(
fd->part, &first_indirect_block,
inode->i_blocks[14] * fd->block_size + first_index * sizeof(uint32_t),
sizeof(uint32_t)
);
uint32_t second_index = index % entries_per_block;
volume_read(
fd->part, &indirect_block,
first_indirect_block * fd->block_size + second_index * sizeof(uint32_t),
sizeof(uint32_t)
);
} else {
volume_read(
fd->part, &indirect_block,
inode->i_blocks[13] * fd->block_size + index * sizeof(uint32_t),
sizeof(uint32_t)
);
}
for (uint32_t j = 0; j < entries_per_block; j++) {
if (i + j >= inode->i_blocks_count)
return alloc_map;
volume_read(
fd->part, &alloc_map[i + j],
indirect_block * fd->block_size + j * sizeof(uint32_t),
sizeof(uint32_t)
);
}
i += entries_per_block - 1;
} else {
// Single indirect block
volume_read(
fd->part, &alloc_map[i],
inode->i_blocks[12] * fd->block_size + block * sizeof(uint32_t),
sizeof(uint32_t)
);
}
}
}
return alloc_map;
}
static bool symlink_to_inode(struct ext2_inode *inode, struct ext2_file_handle *fd,
const char *cwd, size_t cwd_len) {
// I cannot find whether this is 0-terminated or not, so I'm gonna take the
// safe route here and assume it is not.
if (inode->i_size < 59) {
struct ext2_dir_entry dir;
char *symlink = (char *)inode->i_blocks;
symlink[59] = 0;
char *abs = ext_mem_alloc(4096);
char *cwd_copy = ext_mem_alloc(cwd_len + 1);
memcpy(cwd_copy, cwd, cwd_len);
get_absolute_path(abs, symlink, cwd_copy);
pmm_free(cwd_copy, cwd_len + 1);
if (!ext2_parse_dirent(&dir, fd, abs)) {
pmm_free(abs, 4096);
return false;
}
pmm_free(abs, 4096);
ext2_get_inode(inode, fd, dir.inode);
return true;
} else {
print("ext2: Symlinks with destination paths longer than 60 chars unsupported\n");
return false;
}
}
static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handle *fd, const char *path) {
if (*path != '/') {
panic(true, "ext2: Path does not start in /");
}
path++;
struct ext2_inode current_inode = fd->root_inode;
bool escape = false;
static char token[256];
bool ret;
const char *cwd = path - 1; // because /
size_t cwd_len = 1;
size_t next_cwd_len = cwd_len;
next:
memset(token, 0, 256);
for (size_t i = 0; i < 255 && *path != '/' && *path != '\0'; i++, path++, next_cwd_len++)
token[i] = *path;
if (*path == '\0')
escape = true;
else
path++, next_cwd_len++;
uint32_t *alloc_map = create_alloc_map(fd, &current_inode);
for (uint32_t i = 0; i < current_inode.i_size; ) {
// preliminary read
inode_read(dir, i, sizeof(struct ext2_dir_entry),
&current_inode, fd, alloc_map);
// name read
char *name = ext_mem_alloc(dir->name_len + 1);
memset(name, 0, dir->name_len + 1);
inode_read(name, i + sizeof(struct ext2_dir_entry), dir->name_len,
&current_inode, fd, alloc_map);
int (*strcmpfn)(const char *, const char *) = case_insensitive_fopen ? strcasecmp : strcmp;
int test = strcmpfn(token, name);
pmm_free(name, dir->name_len + 1);
if (test == 0) {
if (escape) {
ret = true;
goto out;
} else {
// update the current inode
ext2_get_inode(&current_inode, fd, dir->inode);
while ((current_inode.i_mode & FMT_MASK) != S_IFDIR) {
if ((current_inode.i_mode & FMT_MASK) == S_IFLNK) {
if (!symlink_to_inode(&current_inode, fd, cwd, cwd_len)) {
ret = false;
goto out;
}
} else {
print("ext2: Part of path is not directory nor symlink\n");
ret = false;
goto out;
}
}
pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t));
cwd_len = next_cwd_len;
goto next;
}
}
i += dir->rec_len;
}
ret = false;
out:
pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t));
return ret;
}
static void ext2_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
static void ext2_close(struct file_handle *file);
struct file_handle *ext2_open(struct volume *part, const char *path) {
struct ext2_file_handle *ret = ext_mem_alloc(sizeof(struct ext2_file_handle));
ret->part = part;
volume_read(ret->part, &ret->sb, 1024, sizeof(struct ext2_superblock));
struct ext2_superblock *sb = &ret->sb;
if (sb->s_magic != EXT2_S_MAGIC) {
pmm_free(ret, sizeof(struct ext2_file_handle));
return NULL;
}
if (sb->s_rev_level != 0 &&
(sb->s_feature_incompat & EXT2_IF_COMPRESSION ||
sb->s_feature_incompat & EXT2_IF_INLINE_DATA ||
sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)) {
print("ext2: filesystem has unsupported features %x\n", sb->s_feature_incompat);
pmm_free(ret, sizeof(struct ext2_file_handle));
return NULL;
}
if (sb->s_rev_level != 0 && sb->s_feature_incompat & EXT2_IF_ENCRYPT) {
print("ext2: WARNING: File system has encryption feature on, stuff may misbehave\n");
}
if (sb->s_state == EXT2_FS_UNRECOVERABLE_ERRORS) {
print("ext2: unrecoverable errors found\n");
pmm_free(ret, sizeof(struct ext2_file_handle));
return NULL;
}
ret->block_size = ((uint64_t)1024 << ret->sb.s_log_block_size);
ext2_get_inode(&ret->root_inode, ret, 2);
struct ext2_dir_entry entry;
size_t cwd_len = 0;
char *cwd = ext_mem_alloc(4096);
for (int i = strlen(path) - 1; i > 0; i--) {
if (path[i] == '/' || path[i] == 0) {
cwd_len = i;
break;
}
}
memcpy(cwd, path, cwd_len);
if (!ext2_parse_dirent(&entry, ret, path)) {
pmm_free(cwd, 4096);
pmm_free(ret, sizeof(struct ext2_file_handle));
return NULL;
}
ext2_get_inode(&ret->inode, ret, entry.inode);
while ((ret->inode.i_mode & FMT_MASK) != S_IFREG) {
if ((ret->inode.i_mode & FMT_MASK) == S_IFLNK) {
if (!symlink_to_inode(&ret->inode, ret, cwd, cwd_len)) {
pmm_free(cwd, 4096);
pmm_free(ret, sizeof(struct ext2_file_handle));
return NULL;
}
} else {
print("ext2: Entity is not regular file nor symlink\n");
pmm_free(cwd, 4096);
pmm_free(ret, sizeof(struct ext2_file_handle));
return NULL;
}
}
pmm_free(cwd, 4096);
ret->size = ret->inode.i_size;
ret->alloc_map = create_alloc_map(ret, &ret->inode);
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
handle->fd = ret;
handle->read = (void *)ext2_read;
handle->close = (void *)ext2_close;
handle->size = ret->size;
handle->vol = part;
#if defined (UEFI)
handle->efi_part_handle = part->efi_part_handle;
#endif
return handle;
}
static void ext2_close(struct file_handle *file) {
struct ext2_file_handle *f = file->fd;
if (f->alloc_map != NULL) {
pmm_free(f->alloc_map, f->inode.i_blocks_count * sizeof(uint32_t));
}
pmm_free(f, sizeof(struct ext2_file_handle));
}
static void ext2_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
struct ext2_file_handle *f = file->fd;
inode_read(buf, loc, count, &f->inode, f, f->alloc_map);
}
static struct ext4_extent_header *ext4_find_leaf(struct ext4_extent_header *ext_block, uint32_t read_block, uint64_t block_size, struct volume *part) {
struct ext4_extent_idx *index;
void *buf = ext_mem_alloc(block_size);
memcpy(buf, ext_block, block_size);
ext_block = buf;
for (;;) {
index = (struct ext4_extent_idx *)((size_t)ext_block + 12);
#define EXT4_EXT_MAGIC 0xf30a
if (ext_block->magic != EXT4_EXT_MAGIC)
panic(false, "invalid extent magic");
if (ext_block->depth == 0) {
return ext_block;
}
int i;
for (i = 0; i < ext_block->entries; i++) {
if (read_block < index[i].block)
break;
}
if (--i < 0)
panic(false, "extent not found");
uint64_t block = ((uint64_t)index[i].leaf_hi << 32) | index[i].leaf;
volume_read(part, buf, (block * block_size), block_size);
ext_block = buf;
}
}
static int inode_read(void *buf, uint64_t loc, uint64_t count,
struct ext2_inode *inode, struct ext2_file_handle *fd,
uint32_t *alloc_map) {
for (uint64_t progress = 0; progress < count;) {
uint64_t block = (loc + progress) / fd->block_size;
uint64_t chunk = count - progress;
uint64_t offset = (loc + progress) % fd->block_size;
if (chunk > fd->block_size - offset)
chunk = fd->block_size - offset;
uint32_t block_index;
if (inode->i_flags & EXT4_EXTENTS_FLAG) {
struct ext4_extent_header *leaf;
struct ext4_extent *ext;
int i;
leaf = ext4_find_leaf((struct ext4_extent_header *)inode->i_blocks, block, fd->block_size, fd->part);
if (!leaf)
panic(false, "invalid extent");
ext = (struct ext4_extent*)((size_t)leaf + 12);
for (i = 0; i < leaf->entries; i++) {
if (block < ext[i].block) {
break;
}
}
if (--i >= 0) {
block -= ext[i].block;
if (block >= ext[i].len) {
panic(false, "block longer than extent");
} else {
uint64_t start = ((uint64_t)ext[i].start_hi << 32) + ext[i].start;
block_index = start + block;
}
} else {
panic(false, "extent for block not found");
}
pmm_free(leaf, fd->block_size);
} else {
block_index = alloc_map[block];
}
volume_read(fd->part, buf + progress, (block_index * fd->block_size) + offset, chunk);
progress += chunk;
}
return 0;
}
bool ext2_get_guid(struct guid *guid, struct volume *part) {
struct ext2_superblock sb;
volume_read(part, &sb, 1024, sizeof(struct ext2_superblock));
if (sb.s_magic != EXT2_S_MAGIC)
return false;
((uint64_t *)guid)[0] = sb.s_uuid[0];
((uint64_t *)guid)[1] = sb.s_uuid[1];
return true;
}
char *ext2_get_label(struct volume *part) {
struct ext2_superblock sb;
volume_read(part, &sb, 1024, sizeof(struct ext2_superblock));
if (sb.s_magic != EXT2_S_MAGIC) {
return NULL;
}
if (sb.s_rev_level < 1) {
return NULL;
}
size_t label_len = strlen((char *)sb.s_volume_name);
if (label_len == 0) {
return NULL;
}
char *ret = ext_mem_alloc(label_len + 1);
strcpy(ret, (char *)sb.s_volume_name);
return ret;
}

11
common/fs/fat32.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef __FS__FAT32_H__
#define __FS__FAT32_H__
#include <lib/part.h>
#include <fs/file.h>
char *fat32_get_label(struct volume *part);
struct file_handle *fat32_open(struct volume *part, const char *path);
#endif

528
common/fs/fat32.s2.c Normal file
View File

@ -0,0 +1,528 @@
#include <fs/fat32.h>
#include <lib/misc.h>
#include <drivers/disk.h>
#include <lib/libc.h>
#include <lib/print.h>
#include <mm/pmm.h>
#include <stdbool.h>
#define FAT32_LFN_MAX_ENTRIES 20
#define FAT32_LFN_MAX_FILENAME_LENGTH (FAT32_LFN_MAX_ENTRIES * 13 + 1)
#define FAT32_ATTRIBUTE_SUBDIRECTORY 0x10
#define FAT32_LFN_ATTRIBUTE 0x0F
#define FAT32_ATTRIBUTE_VOLLABEL 0x08
struct fat32_context {
struct volume *part;
int type;
char *label;
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t number_of_fats;
uint32_t hidden_sectors;
uint32_t sectors_per_fat;
uint32_t fat_start_lba;
uint32_t data_start_lba;
uint32_t root_directory_cluster;
uint16_t root_entries;
uint32_t root_start;
uint32_t root_size;
};
struct fat32_file_handle {
struct fat32_context context;
uint32_t first_cluster;
uint32_t size_bytes;
uint32_t size_clusters;
uint32_t *cluster_chain;
size_t chain_len;
};
struct fat32_bpb {
union {
struct {
uint8_t jump[3];
char oem[8];
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fats_count;
uint16_t root_entries_count;
uint16_t sectors_count_16;
uint8_t media_descriptor_type;
uint16_t sectors_per_fat_16;
uint16_t sectors_per_track;
uint16_t heads_count;
uint32_t hidden_sectors_count;
uint32_t sectors_count_32;
uint32_t sectors_per_fat_32;
uint16_t flags;
uint16_t fat_version_number;
uint32_t root_directory_cluster;
uint16_t fs_info_sector;
uint16_t backup_boot_sector;
uint8_t reserved[12];
uint8_t drive_number;
uint8_t nt_flags;
uint8_t signature;
uint32_t volume_serial_number;
char label[11];
char system_identifier[8];
} __attribute__((packed));
uint8_t padding[512];
};
} __attribute__((packed));
struct fat32_directory_entry {
char file_name_and_ext[8 + 3];
uint8_t attribute;
uint8_t file_data_1[8];
uint16_t cluster_num_high;
uint8_t file_data_2[4];
uint16_t cluster_num_low;
uint32_t file_size_bytes;
} __attribute__((packed));
struct fat32_lfn_entry {
uint8_t sequence_number;
char name1[10];
uint8_t attribute;
uint8_t type;
uint8_t dos_checksum;
char name2[12];
uint16_t first_cluster;
char name3[4];
} __attribute__((packed));
static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name);
static int fat32_init_context(struct fat32_context* context, struct volume *part) {
context->part = part;
struct fat32_bpb bpb;
volume_read(context->part, &bpb, 0, sizeof(struct fat32_bpb));
// Checks for FAT12/16
if (strncmp((((void *)&bpb) + 0x36), "FAT", 3) == 0) {
goto valid;
}
// Checks for FAT32
if (strncmp((((void *)&bpb) + 0x52), "FAT", 3) == 0) {
goto valid;
}
// Checks for FAT32 (with 64-bit sector count)
if (strncmp((((void *)&bpb) + 0x03), "FAT32", 5) == 0) {
goto valid;
}
return 1;
valid:;
// The following mess to identify the FAT type is from the FAT spec
// at paragraph 3.5
size_t root_dir_sects = ((bpb.root_entries_count * 32) + (bpb.bytes_per_sector - 1)) / bpb.bytes_per_sector;
size_t data_sects = (bpb.sectors_count_16 ?: bpb.sectors_count_32) - (bpb.reserved_sectors + (bpb.fats_count * (bpb.sectors_per_fat_16 ?: bpb.sectors_per_fat_32)) + root_dir_sects);
size_t clusters_count = data_sects / bpb.sectors_per_cluster;
if (clusters_count < 4085) {
context->type = 12;
} else if (clusters_count < 65525) {
context->type = 16;
} else {
context->type = 32;
}
context->bytes_per_sector = bpb.bytes_per_sector;
context->sectors_per_cluster = bpb.sectors_per_cluster;
context->reserved_sectors = bpb.reserved_sectors;
context->number_of_fats = bpb.fats_count;
context->hidden_sectors = bpb.hidden_sectors_count;
context->sectors_per_fat = context->type == 32 ? bpb.sectors_per_fat_32 : bpb.sectors_per_fat_16;
context->root_directory_cluster = bpb.root_directory_cluster;
context->fat_start_lba = bpb.reserved_sectors;
context->root_entries = bpb.root_entries_count;
context->root_start = context->reserved_sectors + context->number_of_fats * context->sectors_per_fat;
context->root_size = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), context->bytes_per_sector);
switch (context->type) {
case 12:
case 16:
context->data_start_lba = context->root_start + context->root_size;
break;
case 32:
context->data_start_lba = context->root_start;
break;
default:
__builtin_unreachable();
}
// get the volume label
struct fat32_directory_entry _current_directory;
struct fat32_directory_entry *current_directory;
switch (context->type) {
case 12:
case 16:
current_directory = NULL;
break;
case 32:
_current_directory.cluster_num_low = context->root_directory_cluster & 0xFFFF;
_current_directory.cluster_num_high = context->root_directory_cluster >> 16;
current_directory = &_current_directory;
break;
default:
__builtin_unreachable();
}
char *vol_label;
if (fat32_open_in(context, current_directory, (struct fat32_directory_entry *)&vol_label, NULL) == 0) {
context->label = vol_label;
} else {
context->label = NULL;
}
return 0;
}
static int read_cluster_from_map(struct fat32_context *context, uint32_t cluster, uint32_t *out) {
switch (context->type) {
case 12: {
*out = 0;
uint16_t tmp = 0;
volume_read(context->part, &tmp, context->fat_start_lba * context->bytes_per_sector + (cluster + cluster / 2), sizeof(uint16_t));
if (cluster % 2 == 0) {
*out = tmp & 0xfff;
} else {
*out = tmp >> 4;
}
break;
}
case 16:
*out = 0;
volume_read(context->part, out, context->fat_start_lba * context->bytes_per_sector + cluster * sizeof(uint16_t), sizeof(uint16_t));
break;
case 32:
volume_read(context->part, out, context->fat_start_lba * context->bytes_per_sector + cluster * sizeof(uint32_t), sizeof(uint32_t));
*out &= 0x0fffffff;
break;
default:
__builtin_unreachable();
}
return 0;
}
static uint32_t *cache_cluster_chain(struct fat32_context *context,
uint32_t initial_cluster,
size_t *_chain_length) {
uint32_t cluster_limit = (context->type == 12 ? 0xfef : 0)
| (context->type == 16 ? 0xffef : 0)
| (context->type == 32 ? 0xfffffef : 0);
if (initial_cluster < 0x2 || initial_cluster > cluster_limit)
return NULL;
uint32_t cluster = initial_cluster;
size_t chain_length;
for (chain_length = 1; ; chain_length++) {
read_cluster_from_map(context, cluster, &cluster);
if (cluster < 0x2 || cluster > cluster_limit)
break;
}
uint32_t *cluster_chain = ext_mem_alloc(chain_length * sizeof(uint32_t));
cluster = initial_cluster;
for (size_t i = 0; i < chain_length; i++) {
cluster_chain[i] = cluster;
read_cluster_from_map(context, cluster, &cluster);
}
*_chain_length = chain_length;
return cluster_chain;
}
static bool read_cluster_chain(struct fat32_context *context,
uint32_t *cluster_chain,
void *buf, uint64_t loc, uint64_t count) {
size_t block_size = context->sectors_per_cluster * context->bytes_per_sector;
for (uint64_t progress = 0; progress < count;) {
uint64_t block = (loc + progress) / block_size;
uint64_t chunk = count - progress;
uint64_t offset = (loc + progress) % block_size;
if (chunk > block_size - offset)
chunk = block_size - offset;
uint64_t base = ((uint64_t)context->data_start_lba + (cluster_chain[block] - 2) * context->sectors_per_cluster) * context->bytes_per_sector;
volume_read(context->part, buf + progress, base + offset, chunk);
progress += chunk;
}
return true;
}
// Copy ucs-2 characters to char*
static void fat32_lfncpy(char* destination, const void* source, unsigned int size) {
for (unsigned int i = 0; i < size; i++) {
// ignore high bytes
*(((uint8_t*) destination) + i) = *(((uint8_t*) source) + (i * 2));
}
}
static bool fat32_filename_to_8_3(char *dest, const char *src) {
int i = 0, j = 0;
bool ext = false;
for (size_t k = 0; k < 8+3; k++)
dest[k] = ' ';
while (src[i]) {
if (src[i] == '.') {
if (ext) {
// This is a double extension here, just give up.
return false;
}
ext = true;
j = 8;
i++;
continue;
}
if (j >= 8+3 || (j >= 8 && !ext)) {
// Filename too long, give up.
return false;
}
dest[j++] = toupper(src[i++]);
}
return true;
}
static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name) {
size_t block_size = context->sectors_per_cluster * context->bytes_per_sector;
char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0};
size_t dir_chain_len;
struct fat32_directory_entry *directory_entries;
if (directory != NULL) {
uint32_t current_cluster_number = directory->cluster_num_low;
if (context->type == 32)
current_cluster_number |= (uint32_t)directory->cluster_num_high << 16;
uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len);
if (directory_cluster_chain == NULL)
return -1;
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
read_cluster_chain(context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size);
pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
} else {
dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size);
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
volume_read(context->part, directory_entries, context->root_start * context->bytes_per_sector, context->root_entries * sizeof(struct fat32_directory_entry));
}
int ret;
for (size_t i = 0; i < (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry); i++) {
if (directory_entries[i].file_name_and_ext[0] == 0x00) {
// no more entries here
break;
}
if (name == NULL) {
if (directory_entries[i].attribute != FAT32_ATTRIBUTE_VOLLABEL) {
continue;
}
char *r = ext_mem_alloc(12);
memcpy(r, directory_entries[i].file_name_and_ext, 11);
// remove trailing spaces
for (int j = 10; j >= 0; j--) {
if (r[j] == ' ') {
r[j] = 0;
continue;
}
break;
}
*((char **)file) = r;
ret = 0;
goto out;
}
if (directory_entries[i].attribute == FAT32_LFN_ATTRIBUTE) {
struct fat32_lfn_entry* lfn = (struct fat32_lfn_entry*) &directory_entries[i];
if (lfn->sequence_number & 0b01000000) {
// this lfn is the first entry in the table, clear the lfn buffer
memset(current_lfn, ' ', sizeof(current_lfn));
}
const unsigned int lfn_index = ((lfn->sequence_number & 0b00011111) - 1U) * 13U;
if (lfn_index >= FAT32_LFN_MAX_ENTRIES * 13) {
continue;
}
fat32_lfncpy(current_lfn + lfn_index + 00, lfn->name1, 5);
fat32_lfncpy(current_lfn + lfn_index + 05, lfn->name2, 6);
fat32_lfncpy(current_lfn + lfn_index + 11, lfn->name3, 2);
if (lfn_index != 0)
continue;
// remove trailing spaces
for (int j = SIZEOF_ARRAY(current_lfn) - 2; j >= -1; j--) {
if (j == -1 || current_lfn[j] != ' ') {
current_lfn[j + 1] = 0;
break;
}
}
int (*strcmpfn)(const char *, const char *) = case_insensitive_fopen ? strcasecmp : strcmp;
if (strcmpfn(current_lfn, name) == 0) {
*file = directory_entries[i+1];
ret = 0;
goto out;
}
}
if (directory_entries[i].attribute & (1 << 3)) {
// It is a volume label, skip
continue;
}
// SFN
char fn[8+3];
if (!fat32_filename_to_8_3(fn, name)) {
continue;
}
if (!strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) {
*file = directory_entries[i];
ret = 0;
goto out;
}
}
// file not found
ret = -1;
out:
pmm_free(directory_entries, dir_chain_len * block_size);
return ret;
}
char *fat32_get_label(struct volume *part) {
struct fat32_context context;
if (fat32_init_context(&context, part) != 0) {
return NULL;
}
return context.label;
}
static void fat32_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
static void fat32_close(struct file_handle *file);
struct file_handle *fat32_open(struct volume *part, const char *path) {
struct fat32_context context;
int r = fat32_init_context(&context, part);
if (r) {
return NULL;
}
struct fat32_directory_entry _current_directory;
struct fat32_directory_entry *current_directory;
struct fat32_directory_entry current_file;
unsigned int current_index = 0;
char current_part[FAT32_LFN_MAX_FILENAME_LENGTH];
// skip trailing slashes
while (path[current_index] == '/') {
current_index++;
}
// walk down the directory tree
switch (context.type) {
case 12:
case 16:
current_directory = NULL;
break;
case 32:
_current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF;
_current_directory.cluster_num_high = context.root_directory_cluster >> 16;
current_directory = &_current_directory;
break;
default:
__builtin_unreachable();
}
for (;;) {
bool expect_directory = false;
for (unsigned int i = 0; i < SIZEOF_ARRAY(current_part); i++) {
if (path[i + current_index] == 0) {
memcpy(current_part, path + current_index, i);
current_part[i] = 0;
expect_directory = false;
break;
}
if (path[i + current_index] == '/') {
memcpy(current_part, path + current_index, i);
current_part[i] = 0;
current_index += i + 1;
expect_directory = true;
break;
}
}
if ((r = fat32_open_in(&context, current_directory, &current_file, current_part)) != 0) {
return NULL;
}
if (expect_directory) {
_current_directory = current_file;
current_directory = &_current_directory;
} else {
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
struct fat32_file_handle *ret = ext_mem_alloc(sizeof(struct fat32_file_handle));
ret->context = context;
ret->first_cluster = current_file.cluster_num_low;
if (context.type == 32)
ret->first_cluster |= (uint64_t)current_file.cluster_num_high << 16;
ret->size_clusters = DIV_ROUNDUP(current_file.file_size_bytes, context.bytes_per_sector);
ret->size_bytes = current_file.file_size_bytes;
ret->cluster_chain = cache_cluster_chain(&context, ret->first_cluster, &ret->chain_len);
handle->fd = (void *)ret;
handle->read = (void *)fat32_read;
handle->close = (void *)fat32_close;
handle->size = ret->size_bytes;
handle->vol = part;
#if defined (UEFI)
handle->efi_part_handle = part->efi_part_handle;
#endif
return handle;
}
}
}
static void fat32_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
struct fat32_file_handle *f = file->fd;
read_cluster_chain(&f->context, f->cluster_chain, buf, loc, count);
}
static void fat32_close(struct file_handle *file) {
struct fat32_file_handle *f = file->fd;
pmm_free(f->cluster_chain, f->chain_len * sizeof(uint32_t));
pmm_free(f, sizeof(struct fat32_file_handle));
}

41
common/fs/file.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __FS__FILE_H__
#define __FS__FILE_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/part.h>
#if defined (UEFI)
# include <efi.h>
#endif
extern bool case_insensitive_fopen;
bool fs_get_guid(struct guid *guid, struct volume *part);
char *fs_get_label(struct volume *part);
struct file_handle {
bool is_memfile;
bool readall;
struct volume *vol;
char *path;
size_t path_len;
void *fd;
void (*read)(void *fd, void *buf, uint64_t loc, uint64_t count);
void (*close)(void *fd);
uint64_t size;
#if defined (UEFI)
EFI_HANDLE efi_part_handle;
#endif
bool pxe;
uint32_t pxe_ip;
uint16_t pxe_port;
};
struct file_handle *fopen(struct volume *part, const char *filename);
void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count);
void fclose(struct file_handle *fd);
void *freadall(struct file_handle *fd, uint32_t type);
void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs);
#endif

119
common/fs/file.s2.c Normal file
View File

@ -0,0 +1,119 @@
#include <stddef.h>
#include <stdint.h>
#include <fs/file.h>
#include <fs/ext2.h>
#include <fs/fat32.h>
#include <fs/iso9660.h>
#include <lib/print.h>
#include <lib/misc.h>
#include <mm/pmm.h>
#include <lib/part.h>
#include <lib/libc.h>
#include <pxe/tftp.h>
char *fs_get_label(struct volume *part) {
char *ret;
if ((ret = fat32_get_label(part)) != NULL) {
return ret;
}
if ((ret = ext2_get_label(part)) != NULL) {
return ret;
}
return NULL;
}
bool fs_get_guid(struct guid *guid, struct volume *part) {
if (ext2_get_guid(guid, part) == true) {
return true;
}
return false;
}
bool case_insensitive_fopen = false;
struct file_handle *fopen(struct volume *part, const char *filename) {
size_t filename_new_len = strlen(filename) + 2;
char *filename_new = ext_mem_alloc(filename_new_len);
if (filename[0] != '/') {
filename_new[0] = '/';
strcpy(&filename_new[1], filename);
} else {
strcpy(filename_new, filename);
}
filename = filename_new;
struct file_handle *ret;
if (part->pxe) {
if ((ret = tftp_open(part, "", filename)) == NULL) {
return NULL;
}
return ret;
}
if ((ret = ext2_open(part, filename)) != NULL) {
goto success;
}
if ((ret = iso9660_open(part, filename)) != NULL) {
goto success;
}
if ((ret = fat32_open(part, filename)) != NULL) {
goto success;
}
return NULL;
success:
ret->path = (char *)filename;
ret->path_len = filename_new_len;
return ret;
}
void fclose(struct file_handle *fd) {
if (fd->is_memfile) {
if (fd->readall == false) {
pmm_free(fd->fd, fd->size);
}
} else {
fd->close(fd);
}
pmm_free(fd->path, fd->path_len);
pmm_free(fd, sizeof(struct file_handle));
}
void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count) {
if (fd->is_memfile) {
memcpy(buf, fd->fd + loc, count);
} else {
fd->read(fd, buf, loc, count);
}
}
void *freadall(struct file_handle *fd, uint32_t type) {
return freadall_mode(fd, type, false);
}
void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs) {
if (fd->is_memfile) {
if (fd->readall) {
return fd->fd;
}
memmap_alloc_range((uint64_t)(size_t)fd->fd, ALIGN_UP(fd->size, 4096), type, 0, true, false, false);
fd->readall = true;
return fd->fd;
} else {
void *ret = ext_mem_alloc_type_aligned_mode(fd->size, type, 4096, allow_high_allocs);
fd->read(fd, ret, 0, fd->size);
fd->close(fd);
fd->fd = ret;
fd->readall = true;
fd->is_memfile = true;
return ret;
}
}

10
common/fs/iso9660.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __FS__ISO9660_H__
#define __FS__ISO9660_H__
#include <stdint.h>
#include <lib/part.h>
#include <fs/file.h>
struct file_handle *iso9660_open(struct volume *vol, const char *path);
#endif

297
common/fs/iso9660.s2.c Normal file
View File

@ -0,0 +1,297 @@
#include <fs/iso9660.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <mm/pmm.h>
#define ISO9660_SECTOR_SIZE (2 << 10)
struct iso9660_context {
struct volume *vol;
void *root;
uint32_t root_size;
};
struct iso9660_file_handle {
struct iso9660_context *context;
uint32_t LBA;
uint32_t size;
};
#define ISO9660_FIRST_VOLUME_DESCRIPTOR 0x10
#define ISO9660_VOLUME_DESCRIPTOR_SIZE ISO9660_SECTOR_SIZE
#define ROCK_RIDGE_MAX_FILENAME 255
// --- Both endian structures ---
struct BE16_t { uint16_t little, big; } __attribute__((packed));
struct BE32_t { uint32_t little, big; } __attribute__((packed));
// --- Directory entries ---
struct iso9660_directory_entry {
uint8_t length;
uint8_t extended_attribute_length;
struct BE32_t extent;
struct BE32_t extent_size;
uint8_t datetime[7];
uint8_t flags;
uint8_t interleaved_unit_size;
uint8_t interleaved_gap_size;
struct BE16_t volume_seq;
uint8_t filename_size;
char name[];
} __attribute__((packed));
// --- Volume descriptors ---
// VDT = Volume Descriptor Type
enum {
ISO9660_VDT_BOOT_RECORD,
ISO9660_VDT_PRIMARY,
ISO9660_VDT_SUPPLEMENTARY,
ISO9660_VDT_PARTITION_DESCRIPTOR,
ISO9660_VDT_TERMINATOR = 255
};
struct iso9660_volume_descriptor {
uint8_t type;
char identifier[5];
uint8_t version;
} __attribute__((packed));
struct iso9660_primary_volume {
struct iso9660_volume_descriptor volume_descriptor;
union {
struct {
uint8_t unused0[1];
char system_identifier[32];
char volume_identifier[32];
uint8_t unused1[8];
struct BE32_t space_size;
uint8_t unused2[32];
struct BE16_t set_size;
struct BE16_t volume_seq;
struct BE16_t LBA_size;
struct BE32_t path_table_size;
uint32_t LBA_path_table_little;
uint32_t LBA_optional_path_table_little;
uint32_t LBA_path_table_big;
uint32_t LBA_optional_path_table_big;
struct iso9660_directory_entry root;
} __attribute__((packed));
uint8_t padding[2041];
};
} __attribute__((packed));
// --- Implementation ---
struct iso9660_contexts_node {
struct iso9660_context context;
struct iso9660_contexts_node *next;
};
static struct iso9660_contexts_node *contexts = NULL;
static void iso9660_find_PVD(struct iso9660_volume_descriptor *desc, struct volume *vol) {
uint32_t lba = ISO9660_FIRST_VOLUME_DESCRIPTOR;
while (true) {
volume_read(vol, desc, lba * ISO9660_SECTOR_SIZE, ISO9660_SECTOR_SIZE);
switch (desc->type) {
case ISO9660_VDT_PRIMARY:
return;
case ISO9660_VDT_TERMINATOR:
panic(false, "ISO9660: no primary volume descriptor");
break;
}
++lba;
}
}
static void iso9660_cache_root(struct volume *vol,
void **root,
uint32_t *root_size) {
struct iso9660_primary_volume pv;
iso9660_find_PVD((struct iso9660_volume_descriptor *)&pv, vol);
*root_size = pv.root.extent_size.little;
*root = ext_mem_alloc(*root_size);
volume_read(vol, *root, pv.root.extent.little * ISO9660_SECTOR_SIZE, *root_size);
}
static struct iso9660_context *iso9660_get_context(struct volume *vol) {
struct iso9660_contexts_node *current = contexts;
while (current) {
if (current->context.vol == vol)
return &current->context;
current = current->next;
}
// The context is not cached at this point
struct iso9660_contexts_node *node = ext_mem_alloc(sizeof(struct iso9660_contexts_node));
node->context.vol = vol;
iso9660_cache_root(vol, &node->context.root, &node->context.root_size);
node->next = contexts;
contexts = node;
return &node->context;
}
static bool load_name(char *buf, struct iso9660_directory_entry *entry) {
unsigned char* sysarea = ((unsigned char*)entry) + sizeof(struct iso9660_directory_entry) + entry->filename_size;
int sysarea_len = entry->length - sizeof(struct iso9660_directory_entry) - entry->filename_size;
if ((entry->filename_size & 0x1) == 0) {
sysarea++;
sysarea_len--;
}
int rrnamelen = 0;
while ((sysarea_len >= 4) && ((sysarea[3] == 1) || (sysarea[2] == 2))) {
if (sysarea[0] == 'N' && sysarea[1] == 'M') {
rrnamelen = sysarea[2] - 5;
break;
}
sysarea_len -= sysarea[2];
sysarea += sysarea[2];
}
size_t name_len = 0;
if (rrnamelen) {
/* rock ridge naming scheme */
name_len = rrnamelen;
memcpy(buf, sysarea + 5, name_len);
buf[name_len] = 0;
return true;
} else {
name_len = entry->filename_size;
size_t j;
for (j = 0; j < name_len; j++) {
if (entry->name[j] == ';')
break;
if (entry->name[j] == '.' && entry->name[j+1] == ';')
break;
buf[j] = entry->name[j];
}
buf[j] = 0;
return false;
}
}
static struct iso9660_directory_entry *iso9660_find(void *buffer, uint32_t size, const char *filename) {
while (size) {
struct iso9660_directory_entry *entry = buffer;
if (entry->length == 0) {
if (size <= ISO9660_SECTOR_SIZE)
return NULL;
size_t prev_size = size;
size = ALIGN_DOWN(size, ISO9660_SECTOR_SIZE);
buffer += prev_size - size;
continue;
}
char entry_filename[128];
bool rr = load_name(entry_filename, entry);
if (rr && !case_insensitive_fopen) {
if (strcmp(filename, entry_filename) == 0) {
return buffer;
}
} else {
if (strcasecmp(filename, entry_filename) == 0) {
return buffer;
}
}
size -= entry->length;
buffer += entry->length;
}
return NULL;
}
static void iso9660_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
static void iso9660_close(struct file_handle *file);
struct file_handle *iso9660_open(struct volume *vol, const char *path) {
char buf[6];
const uint64_t signature = ISO9660_FIRST_VOLUME_DESCRIPTOR * ISO9660_SECTOR_SIZE + 1;
volume_read(vol, buf, signature, 5);
buf[5] = '\0';
if (strcmp(buf, "CD001") != 0) {
return NULL;
}
struct iso9660_file_handle *ret = ext_mem_alloc(sizeof(struct iso9660_file_handle));
ret->context = iso9660_get_context(vol);
while (*path == '/')
++path;
struct iso9660_directory_entry *current = ret->context->root;
uint32_t current_size = ret->context->root_size;
bool first = true;
uint32_t next_sector = 0;
uint32_t next_size = 0;
char filename[ROCK_RIDGE_MAX_FILENAME];
while (true) {
char *aux = filename;
while (!(*path == '/' || *path == '\0'))
*aux++ = *path++;
*aux = '\0';
struct iso9660_directory_entry *entry = iso9660_find(current, current_size, filename);
if (!entry) {
pmm_free(ret, sizeof(struct iso9660_file_handle));
return NULL; // Not found :(
}
next_sector = entry->extent.little;
next_size = entry->extent_size.little;
if (*path++ == '\0')
break; // Found :)
if (!first) {
pmm_free(current, current_size);
}
current_size = next_size;
current = ext_mem_alloc(current_size);
first = false;
volume_read(vol, current, next_sector * ISO9660_SECTOR_SIZE, current_size);
}
ret->LBA = next_sector;
ret->size = next_size;
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
handle->fd = ret;
handle->read = (void *)iso9660_read;
handle->close = (void *)iso9660_close;
handle->size = ret->size;
handle->vol = vol;
#if defined (UEFI)
handle->efi_part_handle = vol->efi_part_handle;
#endif
return handle;
}
static void iso9660_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
struct iso9660_file_handle *f = file->fd;
volume_read(f->context->vol, buf, f->LBA * ISO9660_SECTOR_SIZE + loc, count);
}
static void iso9660_close(struct file_handle *file) {
pmm_free(file->fd, sizeof(struct iso9660_file_handle));
}

51
common/gensyms.sh Executable file
View File

@ -0,0 +1,51 @@
#! /bin/sh
set -e
LC_ALL=C
export LC_ALL
TMP0="$(mktemp)"
cat >"$TMP0" <<EOF
#! /bin/sh
set -e
set -o pipefail 2>/dev/null
EOF
chmod +x "$TMP0"
"$TMP0" && set -o pipefail
rm "$TMP0"
TMP1="$(mktemp)"
TMP2="$(mktemp)"
TMP3="$(mktemp)"
TMP4="$(mktemp)"
trap "rm -f '$TMP1' '$TMP2' '$TMP3' '$TMP4'; trap - EXIT; exit" EXIT INT TERM QUIT HUP
"$OBJDUMP_FOR_TARGET" -t "$1" | ( "$SED" '/[[:<:]]d[[:>:]]/d' 2>/dev/null || "$SED" '/\bd\b/d' ) | sort > "$TMP1"
"$GREP" "F $4" < "$TMP1" | cut -d' ' -f1 > "$TMP2"
"$GREP" "F $4" < "$TMP1" | "$AWK" 'NF{ print $NF }' > "$TMP3"
echo ".section .$2_map" > "$TMP4"
echo ".globl $2_map" >> "$TMP4"
echo "$2_map:" >> "$TMP4"
if [ "$3" = "32" ]; then
paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.long 0x/g;s/$/"/g;s/#/\
.asciz "/g' >> "$TMP4"
echo ".long 0xffffffff" >> "$TMP4"
elif [ "$3" = "64" ]; then
paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.quad 0x/g;s/$/"/g;s/#/\
.asciz "/g' >> "$TMP4"
echo ".quad 0xffffffffffffffff" >> "$TMP4"
fi
echo '.section .note.GNU-stack,"",%progbits' >> "$TMP4"
mv "$TMP4" "$2.map.S"

240
common/lib/acpi.c Normal file
View File

@ -0,0 +1,240 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <lib/acpi.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/print.h>
// Following function based on https://github.com/managarm/lai/blob/master/helpers/pc-bios.c's function lai_bios_calc_checksum()
uint8_t acpi_checksum(void *ptr, size_t size) {
uint8_t sum = 0, *_ptr = ptr;
for (size_t i = 0; i < size; i++)
sum += _ptr[i];
return sum;
}
#if defined (BIOS)
void *acpi_get_rsdp(void) {
size_t ebda = EBDA;
for (size_t i = ebda; i < 0x100000; i += 16) {
if (i == ebda + 1024) {
// We probed the 1st KiB of the EBDA as per spec, move onto 0xe0000
i = 0xe0000;
}
if (!memcmp((char *)i, "RSD PTR ", 8)
&& !acpi_checksum((void *)i, 20)) {
printv("acpi: Found RSDP at %p\n", i);
return (void *)i;
}
}
return NULL;
}
/// Returns the RSDP v1 pointer if avaliable or else NULL.
void *acpi_get_rsdp_v1(void) {
// In BIOS according to the ACPI spec (see ACPI 6.2 section
// 5.2.5.1 'Finding the RSDP on IA-PC Systems') it either contains
// the RSDP or the XSDP and it cannot contain both. So, we directly
// use acpi_get_rsdp function to find the RSDP and if it has the correct
// revision, return it.
struct rsdp *rsdp = acpi_get_rsdp();
if (rsdp != NULL && rsdp->rev < 2)
return rsdp;
return NULL;
}
void acpi_get_smbios(void **smbios32, void **smbios64) {
*smbios32 = NULL;
*smbios64 = NULL;
for (size_t i = 0xf0000; i < 0x100000; i += 16) {
struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)i;
if (!memcmp(ptr->anchor_str, "_SM_", 4) &&
!acpi_checksum((void *)ptr, ptr->length)) {
printv("acpi: Found SMBIOS 32-bit entry point at %p\n", i);
*smbios32 = (void *)ptr;
break;
}
}
for (size_t i = 0xf0000; i < 0x100000; i += 16) {
struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)i;
if (!memcmp(ptr->anchor_str, "_SM3_", 5) &&
!acpi_checksum((void *)ptr, ptr->length)) {
printv("acpi: Found SMBIOS 64-bit entry point at %p\n", i);
*smbios64 = (void *)ptr;
break;
}
}
}
#endif
#if defined (UEFI)
#include <efi.h>
void *acpi_get_rsdp(void) {
EFI_GUID acpi_2_guid = ACPI_20_TABLE_GUID;
EFI_GUID acpi_1_guid = ACPI_TABLE_GUID;
void *rsdp = NULL;
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
bool is_xsdp = memcmp(&cur_table->VendorGuid, &acpi_2_guid, sizeof(EFI_GUID)) == 0;
bool is_rsdp = memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) == 0;
if (!is_xsdp && !is_rsdp)
continue;
if ((is_xsdp && acpi_checksum(cur_table->VendorTable, sizeof(struct rsdp)) != 0) || // XSDP is 36 bytes wide
(is_rsdp && acpi_checksum(cur_table->VendorTable, 20) != 0)) // RSDP is 20 bytes wide
continue;
printv("acpi: Found %s at %p\n", is_xsdp ? "XSDP" : "RSDP", cur_table->VendorTable);
// We want to return the XSDP if it exists rather then returning
// the RSDP. We need to add a check for that since the table entries
// are not in the same order for all EFI systems since it might be the
// case where the RSDP ocurs before the XSDP.
if (is_xsdp) {
rsdp = (void *)cur_table->VendorTable;
break; // Found it!.
} else {
// Found the RSDP but we continue to loop since we might
// find the XSDP.
rsdp = (void *)cur_table->VendorTable;
}
}
return rsdp;
}
/// Returns the RSDP v1 pointer if avaliable or else NULL.
void *acpi_get_rsdp_v1(void) {
// To maintain GRUB compatibility we will need to probe for the RSDP
// again since UEFI can contain both XSDP and RSDP (see ACPI 6.2 section
// 5.2.5.2 'Finding the RSDP on UEFI Enabled Systems') and in the acpi_get_rsdp
// function we look for the RSDP with the latest revision.
EFI_GUID acpi_1_guid = ACPI_TABLE_GUID;
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
if (memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) != 0)
continue;
if (acpi_checksum(cur_table->VendorTable, 20) != 0)
continue;
return (void *)cur_table->VendorTable;
}
return NULL;
}
void acpi_get_smbios(void **smbios32, void **smbios64) {
*smbios32 = NULL;
*smbios64 = NULL;
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
if (memcmp(&cur_table->VendorGuid, &smbios_guid, sizeof(EFI_GUID)) != 0)
continue;
struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)cur_table->VendorTable;
if (acpi_checksum((void *)ptr, ptr->length) != 0)
continue;
printv("acpi: Found SMBIOS 32-bit entry point at %X\n", ptr);
*smbios32 = (void *)ptr;
break;
}
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
if (memcmp(&cur_table->VendorGuid, &smbios3_guid, sizeof(EFI_GUID)) != 0)
continue;
struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)cur_table->VendorTable;
if (acpi_checksum((void *)ptr, ptr->length) != 0)
continue;
printv("acpi: Found SMBIOS 64-bit entry point at %X\n", ptr);
*smbios64 = (void *)ptr;
break;
}
}
#endif
/// Returns the RSDP v2 pointer if avaliable or else NULL.
void *acpi_get_rsdp_v2(void) {
// Since the acpi_get_rsdp function already looks for the XSDP we can
// just check if it has the correct revision and return the pointer :^)
struct rsdp *rsdp = acpi_get_rsdp();
if (rsdp != NULL && rsdp->rev >= 2)
return rsdp;
return NULL;
}
void *acpi_get_table(const char *signature, int index) {
int cnt = 0;
struct rsdp *rsdp = acpi_get_rsdp();
if (rsdp == NULL)
return NULL;
bool use_xsdt = false;
if (rsdp->rev >= 2 && rsdp->xsdt_addr)
use_xsdt = true;
struct rsdt *rsdt;
if (use_xsdt)
rsdt = (struct rsdt *)(uintptr_t)rsdp->xsdt_addr;
else
rsdt = (struct rsdt *)(uintptr_t)rsdp->rsdt_addr;
size_t entry_count =
(rsdt->header.length - sizeof(struct sdt)) / (use_xsdt ? 8 : 4);
for (size_t i = 0; i < entry_count; i++) {
struct sdt *ptr;
if (use_xsdt)
ptr = (struct sdt *)(uintptr_t)((uint64_t *)rsdt->ptrs_start)[i];
else
ptr = (struct sdt *)(uintptr_t)((uint32_t *)rsdt->ptrs_start)[i];
if (!memcmp(ptr->signature, signature, 4)
&& !acpi_checksum(ptr, ptr->length)
&& cnt++ == index) {
printv("acpi: Found \"%s\" at %x\n", signature, ptr);
return ptr;
}
}
printv("acpi: \"%s\" not found\n", signature);
return NULL;
}

184
common/lib/acpi.h Normal file
View File

@ -0,0 +1,184 @@
#ifndef __LIB__ACPI_H__
#define __LIB__ACPI_H__
#include <stdint.h>
#include <stddef.h>
#include <sys/cpu.h>
#define EBDA (ebda_get())
#if defined (BIOS)
static inline uintptr_t ebda_get(void) {
uintptr_t ebda = (uintptr_t)mminw(0x40e) << 4;
// Sanity checks
if (ebda < 0x80000 || ebda >= 0xa0000) {
ebda = 0x80000;
}
return ebda;
}
#endif
struct sdt {
char signature[4];
uint32_t length;
uint8_t rev;
uint8_t checksum;
char oem_id[6];
char oem_table_id[8];
uint32_t oem_rev;
char creator_id[4];
uint32_t creator_rev;
} __attribute__((packed));
struct rsdp {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t rev;
uint32_t rsdt_addr;
// Revision 2 only after this comment
uint32_t length;
uint64_t xsdt_addr;
uint8_t ext_checksum;
uint8_t reserved[3];
} __attribute__((packed));
struct rsdt {
struct sdt header;
char ptrs_start[];
} __attribute__((packed));
struct smbios_entry_point_32 {
char anchor_str[4];
/// This value summed with all the values of the table.
uint8_t checksum;
/// Length of the entry point table.
uint8_t length;
/// Major version of SMBIOS.
uint8_t major_version;
/// Minor version of SMBIOS.
uint8_t minor_version;
/// Size of the largest SMBIOS structure, in bytes, and encompasses the
/// structures formatted area and text strings
uint16_t max_structure_size;
uint8_t entry_point_revision;
char formatted_area[5];
char intermediate_anchor_str[5];
/// Checksum for values from intermediate anchor str to the
/// end of table.
uint8_t intermediate_checksum;
/// Total length of SMBIOS Structure Table, pointed to by the structure
/// table address, in bytes.
uint16_t table_length;
/// 32-bit physical starting address of the read-only SMBIOS Structure
/// Table.
uint32_t table_address;
/// Total number of structures present in the SMBIOS Structure Table.
uint16_t number_of_structures;
/// Indicates compliance with a revision of this specification.
uint8_t bcd_revision;
} __attribute__((packed));
struct smbios_entry_point_64 {
char anchor_str[5];
/// This value summed with all the values of the table.
uint8_t checksum;
/// Length of the entry point table.
uint8_t length;
/// Major version of SMBIOS.
uint8_t major_version;
/// Minor version of SMBIOS.
uint8_t minor_version;
uint8_t docrev;
uint8_t entry_point_revision;
uint8_t reserved;
/// Size of the largest SMBIOS structure, in bytes, and encompasses the
/// structures formatted area and text strings
uint16_t max_structure_size;
/// 64-bit physical starting address of the read-only SMBIOS Structure
/// Table.
uint64_t table_address;
} __attribute__((packed));
struct madt {
struct sdt header;
uint32_t local_controller_addr;
uint32_t flags;
char madt_entries_begin[];
} __attribute__((packed));
struct madt_header {
uint8_t type;
uint8_t length;
} __attribute__((packed));
struct madt_lapic {
struct madt_header header;
uint8_t acpi_processor_uid;
uint8_t lapic_id;
uint32_t flags;
} __attribute__((packed));
struct madt_x2apic {
struct madt_header header;
uint8_t reserved[2];
uint32_t x2apic_id;
uint32_t flags;
uint32_t acpi_processor_uid;
} __attribute__((packed));
struct madt_io_apic {
uint8_t type;
uint8_t length;
uint8_t apic_id;
uint8_t reserved;
uint32_t address;
uint32_t gsib;
} __attribute__((packed));
struct madt_gicc {
struct madt_header header;
uint8_t reserved1[2];
uint32_t iface_no;
uint32_t acpi_uid;
uint32_t flags;
uint32_t parking_ver;
uint32_t perf_gsiv;
uint64_t parking_addr;
uint64_t gicc_base_addr;
uint64_t gicv_base_addr;
uint64_t gich_base_addr;
uint32_t vgic_maint_gsiv;
uint64_t gicr_base_addr;
uint64_t mpidr;
uint8_t power_eff_class;
uint8_t reserved2;
uint16_t spe_overflow_gsiv;
} __attribute__((packed));
// Reference: https://github.com/riscv-non-isa/riscv-acpi/issues/15
struct madt_riscv_intc {
struct madt_header header;
uint8_t version;
uint8_t reserved;
uint32_t flags;
uint64_t hartid;
uint32_t acpi_processor_uid;
} __attribute__((packed));
#define MADT_RISCV_INTC_ENABLED ((uint32_t)1 << 0)
#define MADT_RISCV_INTC_ONLINE_CAPABLE ((uint32_t)1 << 1)
uint8_t acpi_checksum(void *ptr, size_t size);
void *acpi_get_rsdp(void);
void *acpi_get_rsdp_v1(void);
void *acpi_get_rsdp_v2(void);
void *acpi_get_table(const char *signature, int index);
void acpi_get_smbios(void **smbios32, void **smbios64);
#endif

471
common/lib/config.c Normal file
View File

@ -0,0 +1,471 @@
#include <stddef.h>
#include <stdbool.h>
#include <lib/config.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/readline.h>
#include <mm/pmm.h>
#include <fs/file.h>
#include <lib/print.h>
#include <pxe/tftp.h>
#include <crypt/blake2b.h>
#include <sys/cpu.h>
#define CONFIG_B2SUM_SIGNATURE "++CONFIG_B2SUM_SIGNATURE++"
#define CONFIG_B2SUM_EMPTY "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
const char *config_b2sum = CONFIG_B2SUM_SIGNATURE CONFIG_B2SUM_EMPTY;
static bool config_get_entry_name(char *ret, size_t index, size_t limit);
static char *config_get_entry(size_t *size, size_t index);
#define SEPARATOR '\n'
bool config_ready = false;
no_unwind bool bad_config = false;
static char *config_addr;
int init_config_disk(struct volume *part) {
struct file_handle *f;
bool old_cif = case_insensitive_fopen;
case_insensitive_fopen = true;
if ((f = fopen(part, "/limine.cfg")) == NULL
&& (f = fopen(part, "/limine/limine.cfg")) == NULL
&& (f = fopen(part, "/boot/limine.cfg")) == NULL
&& (f = fopen(part, "/boot/limine/limine.cfg")) == NULL
&& (f = fopen(part, "/EFI/BOOT/limine.cfg")) == NULL) {
case_insensitive_fopen = old_cif;
return -1;
}
case_insensitive_fopen = old_cif;
size_t config_size = f->size + 2;
config_addr = ext_mem_alloc(config_size);
fread(f, config_addr, 0, f->size);
fclose(f);
return init_config(config_size);
}
#define NOT_CHILD (-1)
#define DIRECT_CHILD 0
#define INDIRECT_CHILD 1
static int is_child(char *buf, size_t limit,
size_t current_depth, size_t index) {
if (!config_get_entry_name(buf, index, limit))
return NOT_CHILD;
if (strlen(buf) < current_depth + 1)
return NOT_CHILD;
for (size_t j = 0; j < current_depth; j++)
if (buf[j] != ':')
return NOT_CHILD;
if (buf[current_depth] == ':')
return INDIRECT_CHILD;
return DIRECT_CHILD;
}
static bool is_directory(char *buf, size_t limit,
size_t current_depth, size_t index) {
switch (is_child(buf, limit, current_depth + 1, index + 1)) {
default:
case NOT_CHILD:
return false;
case INDIRECT_CHILD:
bad_config = true;
panic(true, "config: Malformed config file. Parentless child.");
case DIRECT_CHILD:
return true;
}
}
static struct menu_entry *create_menu_tree(struct menu_entry *parent,
size_t current_depth, size_t index) {
struct menu_entry *root = NULL, *prev = NULL;
for (size_t i = index; ; i++) {
static char name[64];
switch (is_child(name, 64, current_depth, i)) {
case NOT_CHILD:
return root;
case INDIRECT_CHILD:
continue;
case DIRECT_CHILD:
break;
}
struct menu_entry *entry = ext_mem_alloc(sizeof(struct menu_entry));
if (root == NULL)
root = entry;
config_get_entry_name(name, i, 64);
bool default_expanded = name[current_depth] == '+';
strcpy(entry->name, name + current_depth + default_expanded);
entry->parent = parent;
size_t entry_size;
char *config_entry = config_get_entry(&entry_size, i);
entry->body = ext_mem_alloc(entry_size + 1);
memcpy(entry->body, config_entry, entry_size);
entry->body[entry_size] = 0;
if (is_directory(name, 64, current_depth, i)) {
entry->sub = create_menu_tree(entry, current_depth + 1, i + 1);
entry->expanded = default_expanded;
}
char *comment = config_get_value(entry->body, 0, "COMMENT");
if (comment != NULL) {
entry->comment = comment;
}
if (prev != NULL)
prev->next = entry;
prev = entry;
}
}
struct menu_entry *menu_tree = NULL;
struct macro {
char name[1024];
char value[2048];
struct macro *next;
};
static struct macro *macros = NULL;
int init_config(size_t config_size) {
config_b2sum += sizeof(CONFIG_B2SUM_SIGNATURE) - 1;
if (memcmp((void *)config_b2sum, CONFIG_B2SUM_EMPTY, 128) != 0) {
editor_enabled = false;
uint8_t out_buf[BLAKE2B_OUT_BYTES];
blake2b(out_buf, config_addr, config_size - 2);
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
for (size_t i = 0; i < BLAKE2B_OUT_BYTES; i++) {
hash_buf[i] = digit_to_int(config_b2sum[i * 2]) << 4 | digit_to_int(config_b2sum[i * 2 + 1]);
}
if (memcmp(hash_buf, out_buf, BLAKE2B_OUT_BYTES) != 0) {
panic(false, "!!! CHECKSUM MISMATCH FOR CONFIG FILE !!!");
}
}
// add trailing newline if not present
config_addr[config_size - 2] = '\n';
// remove windows carriage returns and spaces at the start of lines, if any
for (size_t i = 0; i < config_size; i++) {
size_t skip = 0;
while ((config_addr[i + skip] == '\r')
|| ((!i || config_addr[i - 1] == '\n')
&& (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t'))) {
skip++;
}
if (skip) {
for (size_t j = i; j < config_size - skip; j++)
config_addr[j] = config_addr[j + skip];
config_size -= skip;
}
}
// Load macros
struct macro *arch_macro = ext_mem_alloc(sizeof(struct macro));
strcpy(arch_macro->name, "ARCH");
#if defined (__x86_64__)
strcpy(arch_macro->value, "x86-64");
#elif defined (__i386__)
{
uint32_t eax, ebx, ecx, edx;
if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
strcpy(arch_macro->value, "ia-32");
} else {
strcpy(arch_macro->value, "x86-64");
}
}
#elif defined (__aarch64__)
strcpy(arch_macro->value, "aarch64");
#elif defined (__riscv64)
strcpy(arch_macro->value, "riscv64");
#else
#error "Unspecified architecture"
#endif
arch_macro->next = macros;
macros = arch_macro;
for (size_t i = 0; i < config_size;) {
if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0)
|| (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) {
struct macro *macro = ext_mem_alloc(sizeof(struct macro));
i += i ? 3 : 2;
size_t j;
for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
macro->name[j] = config_addr[i];
}
if (config_addr[i] == '\n' || config_addr[i] == 0 || config_addr[i+1] != '=') {
continue;
}
i += 2;
macro->name[j] = 0;
for (j = 0; config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
macro->value[j] = config_addr[i];
}
macro->value[j] = 0;
macro->next = macros;
macros = macro;
continue;
}
i++;
}
// Expand macros
if (macros != NULL) {
size_t new_config_size = config_size * 4;
char *new_config = ext_mem_alloc(new_config_size);
size_t i, in;
for (i = 0, in = 0; i < config_size;) {
if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0)
|| (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) {
size_t orig_i = i;
i += i ? 3 : 2;
while (config_addr[i++] != '}') {
if (i >= config_size) {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
}
if (config_addr[i++] != '=') {
i = orig_i;
goto next;
}
while (config_addr[i] != '\n' && config_addr[i] != 0) {
i++;
if (i >= config_size) {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
}
continue;
}
next:
if (config_size - i >= 2 && memcmp(config_addr + i, "${", 2) == 0) {
char *macro_name = ext_mem_alloc(1024);
i += 2;
size_t j;
for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
macro_name[j] = config_addr[i];
}
if (config_addr[i] != '}') {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
i++;
macro_name[j] = 0;
char *macro_value = "";
struct macro *macro = macros;
for (;;) {
if (macro == NULL) {
break;
}
if (strcmp(macro->name, macro_name) == 0) {
macro_value = macro->value;
break;
}
macro = macro->next;
}
pmm_free(macro_name, 1024);
for (j = 0; macro_value[j] != 0; j++, in++) {
if (in >= new_config_size) {
goto overflow;
}
new_config[in] = macro_value[j];
}
continue;
}
if (in >= new_config_size) {
overflow:
bad_config = true;
panic(true, "config: Macro-induced buffer overflow");
}
new_config[in++] = config_addr[i++];
}
pmm_free(config_addr, config_size);
config_addr = new_config;
config_size = in;
// Free macros
struct macro *macro = macros;
for (;;) {
if (macro == NULL) {
break;
}
struct macro *next = macro->next;
pmm_free(macro, sizeof(struct macro));
macro = next;
}
}
config_ready = true;
menu_tree = create_menu_tree(NULL, 1, 0);
size_t s;
char *c = config_get_entry(&s, 0);
while (*c != ':') {
c--;
}
if (c > config_addr) {
c[-1] = 0;
}
return 0;
}
static bool config_get_entry_name(char *ret, size_t index, size_t limit) {
if (!config_ready)
return false;
char *p = config_addr;
for (size_t i = 0; i <= index; i++) {
while (*p != ':') {
if (!*p)
return false;
p++;
}
p++;
if ((p - 1) != config_addr && *(p - 2) != '\n')
i--;
}
p--;
size_t i;
for (i = 0; i < (limit - 1); i++) {
if (p[i] == SEPARATOR)
break;
ret[i] = p[i];
}
ret[i] = 0;
return true;
}
static char *config_get_entry(size_t *size, size_t index) {
if (!config_ready)
return NULL;
char *ret;
char *p = config_addr;
for (size_t i = 0; i <= index; i++) {
while (*p != ':') {
if (!*p)
return NULL;
p++;
}
p++;
if ((p - 1) != config_addr && *(p - 2) != '\n')
i--;
}
do {
p++;
} while (*p != '\n');
ret = p;
cont:
while (*p != ':' && *p)
p++;
if (*p && *(p - 1) != '\n') {
p++;
goto cont;
}
*size = p - ret;
return ret;
}
static const char *lastkey;
struct conf_tuple config_get_tuple(const char *config, size_t index,
const char *key1, const char *key2) {
struct conf_tuple conf_tuple;
conf_tuple.value1 = config_get_value(config, index, key1);
if (conf_tuple.value1 == NULL) {
return (struct conf_tuple){0};
}
conf_tuple.value2 = config_get_value(lastkey, 0, key2);
const char *lk1 = lastkey;
const char *next_value1 = config_get_value(config, index + 1, key1);
const char *lk2 = lastkey;
if (conf_tuple.value2 != NULL && next_value1 != NULL) {
if ((uintptr_t)lk1 > (uintptr_t)lk2) {
conf_tuple.value2 = NULL;
}
}
return conf_tuple;
}
char *config_get_value(const char *config, size_t index, const char *key) {
if (!key || !config_ready)
return NULL;
if (config == NULL)
config = config_addr;
size_t key_len = strlen(key);
for (size_t i = 0; config[i]; i++) {
if (!strncmp(&config[i], key, key_len) && config[i + key_len] == '=') {
if (i && config[i - 1] != SEPARATOR)
continue;
if (index--)
continue;
i += key_len + 1;
size_t value_len;
for (value_len = 0;
config[i + value_len] != SEPARATOR && config[i + value_len];
value_len++);
char *buf = ext_mem_alloc(value_len + 1);
memcpy(buf, config + i, value_len);
lastkey = config + i;
return buf;
}
}
return NULL;
}

35
common/lib/config.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __LIB__CONFIG_H__
#define __LIB__CONFIG_H__
#include <stddef.h>
#include <stdbool.h>
#include <lib/part.h>
extern bool config_ready;
extern bool bad_config;
struct menu_entry {
char name[64];
char *comment;
struct menu_entry *parent;
struct menu_entry *sub;
bool expanded;
char *body;
struct menu_entry *next;
};
struct conf_tuple {
char *value1;
char *value2;
};
extern struct menu_entry *menu_tree;
int init_config_disk(struct volume *part);
int init_config(size_t config_size);
char *config_get_value(const char *config, size_t index, const char *key);
struct conf_tuple config_get_tuple(const char *config, size_t index,
const char *key1, const char *key2);
#endif

765
common/lib/elf.c Normal file
View File

@ -0,0 +1,765 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/misc.h>
#include <sys/cpu.h>
#include <lib/libc.h>
#include <lib/elf.h>
#include <lib/print.h>
#include <lib/rand.h>
#include <lib/elsewhere.h>
#include <mm/pmm.h>
#include <fs/file.h>
#define PT_LOAD 0x00000001
#define PT_DYNAMIC 0x00000002
#define PT_INTERP 0x00000003
#define PT_PHDR 0x00000006
#define DT_NULL 0x00000000
#define DT_NEEDED 0x00000001
#define DT_RELA 0x00000007
#define DT_RELASZ 0x00000008
#define DT_RELAENT 0x00000009
#define DT_FLAGS_1 0x6ffffffb
#define DF_1_PIE 0x08000000
#define ABI_SYSV 0x00
#define ARCH_X86_64 0x3e
#define ARCH_X86_32 0x03
#define ARCH_AARCH64 0xb7
#define ARCH_RISCV 0xf3
#define BITS_LE 0x01
#define ELFCLASS64 0x02
#define ET_DYN 0x0003
#define SHT_RELA 0x00000004
#define R_X86_64_RELATIVE 0x00000008
#define R_AARCH64_RELATIVE 0x00000403
#define R_RISCV_RELATIVE 0x00000003
/* Indices into identification array */
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_OSABI 7
struct elf32_hdr {
uint8_t ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint32_t entry;
uint32_t phoff;
uint32_t shoff;
uint32_t flags;
uint16_t hdr_size;
uint16_t phdr_size;
uint16_t ph_num;
uint16_t shdr_size;
uint16_t sh_num;
uint16_t shstrndx;
};
struct elf64_phdr {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
};
struct elf32_phdr {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct elf64_rela {
uint64_t r_addr;
uint32_t r_info;
uint32_t r_symbol;
uint64_t r_addend;
};
struct elf64_dyn {
uint64_t d_tag;
uint64_t d_un;
};
int elf_bits(uint8_t *elf) {
struct elf64_hdr *hdr = (void *)elf;
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
printv("elf: Not a valid ELF file.\n");
return -1;
}
switch (hdr->machine) {
case ARCH_X86_64:
case ARCH_AARCH64:
return 64;
case ARCH_RISCV:
return (hdr->ident[EI_CLASS] == ELFCLASS64) ? 64 : 32;
case ARCH_X86_32:
return 32;
default:
return -1;
}
}
struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf) {
struct elf_section_hdr_info info = {0};
struct elf64_hdr *hdr = (void *)elf;
info.num = hdr->sh_num;
info.section_entry_size = hdr->shdr_size;
info.str_section_idx = hdr->shstrndx;
info.section_offset = hdr->shoff;
return info;
}
struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf) {
struct elf_section_hdr_info info = {0};
struct elf32_hdr *hdr = (void *)elf;
info.num = hdr->sh_num;
info.section_entry_size = hdr->shdr_size;
info.str_section_idx = hdr->shstrndx;
info.section_offset = hdr->shoff;
return info;
}
static bool elf64_is_relocatable(uint8_t *elf, struct elf64_hdr *hdr) {
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
}
// Find DYN segment
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_DYNAMIC) {
continue;
}
for (uint16_t j = 0; j < phdr->p_filesz / sizeof(struct elf64_dyn); j++) {
struct elf64_dyn *dyn = (void *)elf + (phdr->p_offset + j * sizeof(struct elf64_dyn));
switch (dyn->d_tag) {
case DT_FLAGS_1:
if (dyn->d_un & DF_1_PIE) {
return true;
}
break;
case DT_RELA:
return true;
}
}
}
return false;
}
static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
}
// Find DYN segment
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_DYNAMIC)
continue;
uint64_t rela_offset = 0;
uint64_t rela_size = 0;
uint64_t rela_ent = 0;
for (uint16_t j = 0; j < phdr->p_filesz / sizeof(struct elf64_dyn); j++) {
struct elf64_dyn *dyn = (void *)elf + (phdr->p_offset + j * sizeof(struct elf64_dyn));
switch (dyn->d_tag) {
case DT_RELA:
rela_offset = dyn->d_un;
break;
case DT_RELAENT:
rela_ent = dyn->d_un;
break;
case DT_RELASZ:
rela_size = dyn->d_un;
break;
}
}
if (rela_offset == 0) {
break;
}
if (rela_ent != sizeof(struct elf64_rela)) {
print("elf: Unknown sh_entsize for RELA section!\n");
return false;
}
for (uint16_t j = 0; j < hdr->ph_num; j++) {
struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + j * hdr->phdr_size);
if (_phdr->p_vaddr <= rela_offset && _phdr->p_vaddr + _phdr->p_filesz > rela_offset) {
rela_offset -= _phdr->p_vaddr;
rela_offset += _phdr->p_offset;
break;
}
}
// This is a RELA header, get and apply all relocations
for (uint64_t offset = 0; offset < rela_size; offset += rela_ent) {
struct elf64_rela *relocation = (void *)elf + (rela_offset + offset);
switch (relocation->r_info) {
#if defined (__x86_64__) || defined (__i386__)
case R_X86_64_RELATIVE:
#elif defined (__aarch64__)
case R_AARCH64_RELATIVE:
#elif defined (__riscv64)
case R_RISCV_RELATIVE:
#else
#error Unknown architecture
#endif
{
// Relocation is before buffer
if (relocation->r_addr < vaddr)
continue;
// Relocation is after buffer
if (vaddr + size < relocation->r_addr + 8)
continue;
// It's inside it, calculate where it is
uint64_t *ptr = (uint64_t *)((uint8_t *)buffer - vaddr + relocation->r_addr);
// Write the relocated value
*ptr = slide + relocation->r_addend;
break;
}
default: {
print("elf: Unknown RELA type: %x\n", relocation->r_info);
return false;
}
}
}
break;
}
return true;
}
bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide) {
struct elf64_hdr *hdr = (void *)elf;
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
printv("elf: Not a valid ELF file.\n");
return false;
}
if (hdr->ident[EI_DATA] != BITS_LE) {
printv("elf: Not a Little-endian ELF file.\n");
return false;
}
#if defined (__x86_64__) || defined (__i386__)
if (hdr->machine != ARCH_X86_64) {
printv("elf: Not an x86-64 ELF file.\n");
return false;
}
#elif defined (__aarch64__)
if (hdr->machine != ARCH_AARCH64) {
printv("elf: Not an aarch64 ELF file.\n");
return false;
}
#elif defined (__riscv64)
if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) {
printv("elf: Not a riscv64 ELF file.\n");
return false;
}
#else
#error Unknown architecture
#endif
if (hdr->sh_num == 0) {
return false;
}
if (hdr->shdr_size < sizeof(struct elf64_shdr)) {
panic(true, "elf: shdr_size < sizeof(struct elf64_shdr)");
}
struct elf64_shdr *shstrtab = (void *)elf + (hdr->shoff + hdr->shstrndx * hdr->shdr_size);
char *names = (void *)elf + shstrtab->sh_offset;
for (uint16_t i = 0; i < hdr->sh_num; i++) {
struct elf64_shdr *section = (void *)elf + (hdr->shoff + i * hdr->shdr_size);
if (strcmp(&names[section->sh_name], name) == 0) {
if (limit == 0) {
*(void **)buffer = ext_mem_alloc(section->sh_size);
buffer = *(void **)buffer;
limit = section->sh_size;
}
if (section->sh_size > limit) {
return false;
}
memcpy(buffer, elf + section->sh_offset, section->sh_size);
return elf64_apply_relocations(elf, hdr, buffer, section->sh_addr, section->sh_size, slide);
}
}
return false;
}
static uint64_t elf64_max_align(uint8_t *elf) {
uint64_t ret = 0;
struct elf64_hdr *hdr = (void *)elf;
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
}
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD) {
continue;
}
if (phdr->p_align > ret) {
ret = phdr->p_align;
}
}
if (ret == 0) {
panic(true, "elf: Executable has no loadable segments");
}
return ret;
}
static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct elf_range **_ranges, uint64_t *_ranges_count) {
struct elf64_hdr *hdr = (void *)elf;
uint64_t ranges_count = 0;
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
}
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD) {
continue;
}
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
continue;
}
ranges_count++;
}
if (ranges_count == 0) {
panic(true, "elf: No higher half PHDRs exist");
}
struct elf_range *ranges = ext_mem_alloc(ranges_count * sizeof(struct elf_range));
size_t r = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD) {
continue;
}
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
continue;
}
uint64_t load_addr = phdr->p_vaddr + slide;
uint64_t this_top = load_addr + phdr->p_memsz;
ranges[r].base = load_addr & ~(phdr->p_align - 1);
ranges[r].length = ALIGN_UP(this_top - ranges[r].base, phdr->p_align);
ranges[r].permissions = phdr->p_flags & 0b111;
r++;
}
*_ranges_count = ranges_count;
*_ranges = ranges;
}
bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct elf_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) {
struct elf64_hdr *hdr = (void *)elf;
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
printv("elf: Not a valid ELF file.\n");
return false;
}
if (hdr->ident[EI_DATA] != BITS_LE) {
panic(true, "elf: Not a Little-endian ELF file.\n");
}
#if defined (__x86_64__) || defined (__i386__)
if (hdr->machine != ARCH_X86_64) {
panic(true, "elf: Not an x86-64 ELF file.\n");
}
#elif defined (__aarch64__)
if (hdr->machine != ARCH_AARCH64) {
panic(true, "elf: Not an aarch64 ELF file.\n");
}
#elif defined (__riscv64)
if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) {
panic(true, "elf: Not a riscv64 ELF file.\n");
}
#else
#error Unknown architecture
#endif
if (is_reloc) {
*is_reloc = false;
}
uint64_t slide = 0;
size_t try_count = 0;
size_t max_simulated_tries = 0x10000;
uint64_t entry = hdr->entry;
uint64_t max_align = elf64_max_align(elf);
uint64_t image_size = 0;
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
}
uint64_t min_vaddr = (uint64_t)-1;
uint64_t max_vaddr = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD) {
continue;
}
// Drop entries not in the higher half
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
continue;
}
// check for overlapping phdrs
for (uint16_t j = 0; j < hdr->ph_num; j++) {
struct elf64_phdr *phdr_in = (void *)elf + (hdr->phoff + j * hdr->phdr_size);
if (phdr_in->p_type != PT_LOAD) {
continue;
}
// Drop entries not in the higher half
if (phdr_in->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
continue;
}
if (phdr_in == phdr) {
continue;
}
if ((phdr_in->p_vaddr >= phdr->p_vaddr
&& phdr_in->p_vaddr < phdr->p_vaddr + phdr->p_memsz)
||
(phdr_in->p_vaddr + phdr_in->p_memsz > phdr->p_vaddr
&& phdr_in->p_vaddr + phdr_in->p_memsz <= phdr->p_vaddr + phdr->p_memsz)) {
panic(true, "elf: Attempted to load ELF file with overlapping PHDRs (%u and %u overlap)", i, j);
}
if (ranges != NULL) {
uint64_t page_rounded_base = ALIGN_DOWN(phdr->p_vaddr, 4096);
uint64_t page_rounded_top = ALIGN_UP(phdr->p_vaddr + phdr->p_memsz, 4096);
uint64_t page_rounded_base_in = ALIGN_DOWN(phdr_in->p_vaddr, 4096);
uint64_t page_rounded_top_in = ALIGN_UP(phdr_in->p_vaddr + phdr_in->p_memsz, 4096);
if ((page_rounded_base >= page_rounded_base_in
&& page_rounded_base < page_rounded_top_in)
||
(page_rounded_top > page_rounded_base_in
&& page_rounded_top <= page_rounded_top_in)) {
if ((phdr->p_flags & 0b111) != (phdr_in->p_flags & 0b111)) {
panic(true, "elf: Attempted to load ELF file with PHDRs with different permissions sharing the same memory page.");
}
}
}
}
if (phdr->p_vaddr < min_vaddr) {
min_vaddr = phdr->p_vaddr;
}
if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
max_vaddr = phdr->p_vaddr + phdr->p_memsz;
}
}
if (max_vaddr == 0 || min_vaddr == (uint64_t)-1) {
panic(true, "elf: No higher half PHDRs exist");
}
image_size = max_vaddr - min_vaddr;
*physical_base = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, max_align);
*virtual_base = min_vaddr;
if (_image_size) {
*_image_size = image_size;
}
if (elf64_is_relocatable(elf, hdr)) {
if (is_reloc) {
*is_reloc = true;
}
}
again:
if (*is_reloc && kaslr) {
slide = rand32() & ~(max_align - 1);
if ((*virtual_base - FIXED_HIGHER_HALF_OFFSET_64) + slide + image_size >= 0x80000000) {
if (++try_count == max_simulated_tries) {
panic(true, "elf: Image wants to load too high");
}
goto again;
}
}
uint64_t bss_size = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD) {
continue;
}
// Drop entries not in the higher half
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
continue;
}
// Sanity checks
if (phdr->p_filesz > phdr->p_memsz) {
panic(true, "elf: p_filesz > p_memsz");
}
uint64_t load_addr = *physical_base + (phdr->p_vaddr - *virtual_base);
#if defined (__aarch64__)
uint64_t this_top = load_addr + phdr->p_memsz;
uint64_t mem_base, mem_size;
mem_base = load_addr & ~(phdr->p_align - 1);
mem_size = this_top - mem_base;
#endif
memcpy((void *)(uintptr_t)load_addr, elf + (phdr->p_offset), phdr->p_filesz);
bss_size = phdr->p_memsz - phdr->p_filesz;
if (!elf64_apply_relocations(elf, hdr, (void *)(uintptr_t)load_addr, phdr->p_vaddr, phdr->p_memsz, slide)) {
panic(true, "elf: Failed to apply relocations");
}
#if defined (__aarch64__)
clean_dcache_poc(mem_base, mem_base + mem_size);
inval_icache_pou(mem_base, mem_base + mem_size);
#endif
}
if (_image_size_before_bss != NULL) {
*_image_size_before_bss = image_size - bss_size;
}
*virtual_base += slide;
*entry_point = entry + slide;
if (_slide) {
*_slide = slide;
}
if (ranges_count != NULL && ranges != NULL) {
elf64_get_ranges(elf, slide, ranges, ranges_count);
}
return true;
}
bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
struct elsewhere_range **ranges,
uint64_t *ranges_count) {
struct elf32_hdr *hdr = (void *)elf;
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
printv("elf: Not a valid ELF file.\n");
return false;
}
if (hdr->ident[EI_DATA] != BITS_LE) {
printv("elf: Not a Little-endian ELF file.\n");
return false;
}
if (hdr->machine != ARCH_X86_32) {
printv("elf: Not an x86_32 ELF file.\n");
return false;
}
*entry_point = hdr->entry;
bool entry_adjusted = false;
if (hdr->phdr_size < sizeof(struct elf32_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf32_phdr)");
}
*ranges_count = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf32_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD)
continue;
*ranges_count += 1;
}
*ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * *ranges_count);
size_t cur_entry = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf32_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD)
continue;
// Sanity checks
if (phdr->p_filesz > phdr->p_memsz) {
panic(true, "elf: p_filesz > p_memsz");
}
void *elsewhere = ext_mem_alloc(phdr->p_memsz);
memcpy(elsewhere, elf + phdr->p_offset, phdr->p_filesz);
if (!entry_adjusted
&& *entry_point >= phdr->p_vaddr
&& *entry_point < (phdr->p_vaddr + phdr->p_memsz)) {
*entry_point -= phdr->p_vaddr;
*entry_point += phdr->p_paddr;
entry_adjusted = true;
}
(*ranges)[cur_entry].elsewhere = (uintptr_t)elsewhere;
(*ranges)[cur_entry].target = phdr->p_paddr;
(*ranges)[cur_entry].length = phdr->p_memsz;
cur_entry++;
}
return true;
}
bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
struct elsewhere_range **ranges,
uint64_t *ranges_count) {
struct elf64_hdr *hdr = (void *)elf;
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
printv("elf: Not a valid ELF file.\n");
return false;
}
if (hdr->ident[EI_DATA] != BITS_LE) {
printv("elf: Not a Little-endian ELF file.\n");
return false;
}
if (hdr->machine != ARCH_X86_64) {
printv("elf: Not an x86-64 ELF file.\n");
return false;
}
*entry_point = hdr->entry;
bool entry_adjusted = false;
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
}
*ranges_count = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD)
continue;
*ranges_count += 1;
}
*ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * *ranges_count);
size_t cur_entry = 0;
for (uint16_t i = 0; i < hdr->ph_num; i++) {
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
if (phdr->p_type != PT_LOAD)
continue;
// Sanity checks
if (phdr->p_filesz > phdr->p_memsz) {
panic(true, "elf: p_filesz > p_memsz");
}
void *elsewhere = ext_mem_alloc(phdr->p_memsz);
memcpy(elsewhere, elf + phdr->p_offset, phdr->p_filesz);
if (!entry_adjusted
&& *entry_point >= phdr->p_vaddr
&& *entry_point < (phdr->p_vaddr + phdr->p_memsz)) {
*entry_point -= phdr->p_vaddr;
*entry_point += phdr->p_paddr;
entry_adjusted = true;
}
(*ranges)[cur_entry].elsewhere = (uintptr_t)elsewhere;
(*ranges)[cur_entry].target = phdr->p_paddr;
(*ranges)[cur_entry].length = phdr->p_memsz;
cur_entry++;
}
return true;
}

94
common/lib/elf.h Normal file
View File

@ -0,0 +1,94 @@
#ifndef __LIB__ELF_H__
#define __LIB__ELF_H__
#include <stdint.h>
#include <stdbool.h>
#include <lib/elsewhere.h>
#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
#define ELF_PF_X 1
#define ELF_PF_W 2
#define ELF_PF_R 4
struct elf_range {
uint64_t base;
uint64_t length;
uint64_t permissions;
};
struct elf_section_hdr_info {
uint32_t section_entry_size;
uint32_t str_section_idx;
uint32_t num;
uint32_t section_offset;
};
int elf_bits(uint8_t *elf);
struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf);
struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf);
bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide);
bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct elf_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
struct elsewhere_range **ranges,
uint64_t *ranges_count);
bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
struct elsewhere_range **ranges,
uint64_t *ranges_count);
struct elf64_hdr {
uint8_t ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entry;
uint64_t phoff;
uint64_t shoff;
uint32_t flags;
uint16_t hdr_size;
uint16_t phdr_size;
uint16_t ph_num;
uint16_t shdr_size;
uint16_t sh_num;
uint16_t shstrndx;
};
struct elf64_shdr {
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
uint64_t sh_addr;
uint64_t sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
};
struct elf32_shdr {
uint32_t sh_name;
uint32_t sh_type;
uint32_t sh_flags;
uint32_t sh_addr;
uint32_t sh_offset;
uint32_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint32_t sh_addralign;
uint32_t sh_entsize;
};
struct elf64_sym {
uint32_t st_name;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
uint64_t st_value;
uint64_t st_size;
};
#endif

96
common/lib/elsewhere.c Normal file
View File

@ -0,0 +1,96 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/elsewhere.h>
#include <lib/misc.h>
#include <mm/pmm.h>
static bool elsewhere_overlap_check(uint64_t base1, uint64_t top1,
uint64_t base2, uint64_t top2) {
return ((base1 >= base2 && base1 < top2)
|| (top1 > base2 && top1 <= top2));
}
bool elsewhere_append(
bool flexible_target,
struct elsewhere_range *ranges, uint64_t *ranges_count,
void *elsewhere, uint64_t *target, size_t t_length) {
// original target of -1 means "allocate after top of all ranges"
// flexible target is ignored
flexible_target = true;
if (*target == (uint64_t)-1) {
uint64_t top = 0;
for (size_t i = 0; i < *ranges_count; i++) {
uint64_t r_top = ranges[i].target + ranges[i].length;
if (top < r_top) {
top = r_top;
}
}
*target = ALIGN_UP(top, 4096);
}
uint64_t max_retries = 0x10000;
retry:
if (max_retries-- == 0) {
return false;
}
for (size_t i = 0; i < *ranges_count; i++) {
uint64_t t_top = *target + t_length;
// Does it overlap with other elsewhere ranges targets?
{
uint64_t base = ranges[i].target;
uint64_t length = ranges[i].length;
uint64_t top = base + length;
if (elsewhere_overlap_check(base, top, *target, t_top)) {
if (!flexible_target) {
return false;
}
*target = ALIGN_UP(top, 4096);
goto retry;
}
}
// Does it overlap with other elsewhere ranges sources?
{
uint64_t base = ranges[i].elsewhere;
uint64_t length = ranges[i].length;
uint64_t top = base + length;
if (elsewhere_overlap_check(base, top, *target, t_top)) {
if (!flexible_target) {
return false;
}
*target += 0x1000;
goto retry;
}
}
// Make sure it is memory that actually exists.
if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE,
MEMMAP_USABLE, false, true, false)) {
if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE,
MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, false)) {
if (!flexible_target) {
return false;
}
*target += 0x1000;
goto retry;
}
}
}
// Add the elsewhere range
ranges[*ranges_count].elsewhere = (uintptr_t)elsewhere;
ranges[*ranges_count].target = *target;
ranges[*ranges_count].length = t_length;
*ranges_count += 1;
return true;
}

19
common/lib/elsewhere.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __LIB__ELSEWHERE_H__
#define __LIB__ELSEWHERE_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
struct elsewhere_range {
uint64_t elsewhere;
uint64_t target;
uint64_t length;
};
bool elsewhere_append(
bool flexible_target,
struct elsewhere_range *ranges, uint64_t *ranges_count,
void *elsewhere, uint64_t *target, size_t t_length);
#endif

58
common/lib/fb.c Normal file
View File

@ -0,0 +1,58 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <lib/fb.h>
#include <drivers/vbe.h>
#include <drivers/gop.h>
#include <mm/pmm.h>
void fb_init(struct fb_info **ret, size_t *_fbs_count,
uint64_t target_width, uint64_t target_height, uint16_t target_bpp) {
#if defined (BIOS)
*ret = ext_mem_alloc(sizeof(struct fb_info));
if (init_vbe(*ret, target_width, target_height, target_bpp)) {
*_fbs_count = 1;
(*ret)->edid = get_edid_info();
size_t mode_count;
(*ret)->mode_list = vbe_get_mode_list(&mode_count);
(*ret)->mode_count = mode_count;
} else {
*_fbs_count = 0;
pmm_free(*ret, sizeof(struct fb_info));
}
#elif defined (UEFI)
init_gop(ret, _fbs_count, target_width, target_height, target_bpp);
#endif
}
void fb_clear(struct fb_info *fb) {
for (size_t y = 0; y < fb->framebuffer_height; y++) {
switch (fb->framebuffer_bpp) {
case 32: {
uint32_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
size_t row = (y * fb->framebuffer_pitch) / 4;
for (size_t x = 0; x < fb->framebuffer_width; x++) {
fbp[row + x] = 0;
}
break;
}
case 16: {
uint16_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
size_t row = (y * fb->framebuffer_pitch) / 2;
for (size_t x = 0; x < fb->framebuffer_width; x++) {
fbp[row + x] = 0;
}
break;
}
default: {
uint8_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
size_t row = y * fb->framebuffer_pitch;
for (size_t x = 0; x < fb->framebuffer_width * fb->framebuffer_bpp; x++) {
fbp[row + x] = 0;
}
break;
}
}
}
}

40
common/lib/fb.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef __LIB__FB_H__
#define __LIB__FB_H__
#include <stdint.h>
#include <stddef.h>
#include <drivers/edid.h>
struct resolution {
uint64_t width;
uint64_t height;
uint16_t bpp;
};
struct fb_info {
uint64_t framebuffer_pitch;
uint64_t framebuffer_width;
uint64_t framebuffer_height;
uint16_t framebuffer_bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
uint64_t framebuffer_addr;
struct edid_info_struct *edid;
uint64_t mode_count;
struct fb_info *mode_list;
};
void fb_init(struct fb_info **ret, size_t *_fbs_count,
uint64_t target_width, uint64_t target_height, uint16_t target_bpp);
void fb_clear(struct fb_info *fb);
#endif

785
common/lib/gterm.c Normal file
View File

@ -0,0 +1,785 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/gterm.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/config.h>
#include <lib/print.h>
#include <lib/uri.h>
#include <lib/fb.h>
#include <lib/image.h>
#include <mm/pmm.h>
#include <flanterm/flanterm.h>
#define FLANTERM_FB_SUPPORT_BPP
#include <flanterm/backends/fb.h>
#include <lib/term.h>
// Builtin font originally taken from:
// https://github.com/viler-int10h/vga-text-mode-fonts/raw/master/FONTS/PC-OTHER/TOSH-SAT.F16
static const uint8_t builtin_font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81, 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00,
0x00, 0x3c, 0x7e, 0xff, 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00,
0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x1e, 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12, 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00,
0x00, 0x30, 0x38, 0x2c, 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00,
0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00,
0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e, 0xfe, 0x7e, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00,
0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00,
0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda, 0x6c, 0x06, 0x00, 0x00,
0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xc0, 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00,
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x30, 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x38, 0x6c, 0x38, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x0c, 0x38,
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x10, 0x38, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1e, 0x3c, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00,
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00,
0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x76, 0xdc, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00,
0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18,
0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xd8, 0xc0, 0xc0, 0x00,
0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78, 0xc0, 0x80, 0x00, 0x00,
0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00,
0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static struct image *background;
static size_t margin = 64;
static size_t margin_gradient = 4;
static uint32_t default_bg, default_fg;
static uint32_t default_bg_bright, default_fg_bright;
static size_t bg_canvas_size;
static uint32_t *bg_canvas;
#define A(rgb) (uint8_t)(rgb >> 24)
#define R(rgb) (uint8_t)(rgb >> 16)
#define G(rgb) (uint8_t)(rgb >> 8)
#define B(rgb) (uint8_t)(rgb)
#define ARGB(a, r, g, b) (a << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF)
static inline uint32_t colour_blend(uint32_t fg, uint32_t bg) {
unsigned alpha = 255 - A(fg);
unsigned inv_alpha = A(fg) + 1;
uint8_t r = (uint8_t)((alpha * R(fg) + inv_alpha * R(bg)) / 256);
uint8_t g = (uint8_t)((alpha * G(fg) + inv_alpha * G(bg)) / 256);
uint8_t b = (uint8_t)((alpha * B(fg) + inv_alpha * B(bg)) / 256);
return ARGB(0, r, g, b);
}
static uint32_t blend_gradient_from_box(struct fb_info *fb, size_t x, size_t y, uint32_t bg_px, uint32_t hex) {
size_t distance, x_distance, y_distance;
size_t gradient_stop_x = fb->framebuffer_width - margin;
size_t gradient_stop_y = fb->framebuffer_height - margin;
if (x < margin)
x_distance = margin - x;
else
x_distance = x - gradient_stop_x;
if (y < margin)
y_distance = margin - y;
else
y_distance = y - gradient_stop_y;
if (x >= margin && x < gradient_stop_x) {
distance = y_distance;
} else if (y >= margin && y < gradient_stop_y) {
distance = x_distance;
} else {
distance = sqrt((uint64_t)x_distance * (uint64_t)x_distance
+ (uint64_t)y_distance * (uint64_t)y_distance);
}
if (distance > margin_gradient)
return bg_px;
uint8_t gradient_step = (0xff - A(hex)) / margin_gradient;
uint8_t new_alpha = A(hex) + gradient_step * distance;
return colour_blend((hex & 0xffffff) | (new_alpha << 24), bg_px);
}
typedef size_t fixedp6; // the last 6 bits are the fixed point part
static size_t fixedp6_to_int(fixedp6 value) { return value / 64; }
static fixedp6 int_to_fixedp6(size_t value) { return value * 64; }
// Draw rect at coordinates, copying from the image to the fb and canvas, applying fn on every pixel
__attribute__((always_inline)) static inline void genloop(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend, uint32_t (*blend)(struct fb_info *fb, size_t x, size_t y, uint32_t orig)) {
uint8_t *img = background->img;
const size_t img_width = background->img_width, img_height = background->img_height, img_pitch = background->pitch, colsize = background->bpp / 8;
switch (background->type) {
case IMAGE_TILED:
for (size_t y = ystart; y < yend; y++) {
size_t image_y = y % img_height, image_x = xstart % img_width;
const size_t off = img_pitch * image_y;
size_t canvas_off = fb->framebuffer_width * y;
for (size_t x = xstart; x < xend; x++) {
uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off);
uint32_t i = blend(fb, x, y, img_pixel);
bg_canvas[canvas_off + x] = i;
if (image_x++ == img_width) image_x = 0; // image_x = x % img_width, but modulo is too expensive
}
}
break;
case IMAGE_CENTERED:
for (size_t y = ystart; y < yend; y++) {
size_t image_y = y - background->y_displacement;
const size_t off = img_pitch * image_y;
size_t canvas_off = fb->framebuffer_width * y;
if (image_y >= background->y_size) { /* external part */
for (size_t x = xstart; x < xend; x++) {
uint32_t i = blend(fb, x, y, background->back_colour);
bg_canvas[canvas_off + x] = i;
}
}
else { /* internal part */
for (size_t x = xstart; x < xend; x++) {
size_t image_x = (x - background->x_displacement);
bool x_external = image_x >= background->x_size;
uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off);
uint32_t i = blend(fb, x, y, x_external ? background->back_colour : img_pixel);
bg_canvas[canvas_off + x] = i;
}
}
}
break;
// For every pixel, ratio = img_width / gterm_width, img_x = x * ratio, x = (xstart + i)
// hence x = xstart * ratio + i * ratio
// so you can set x = xstart * ratio, and increment by ratio at each iteration
case IMAGE_STRETCHED:
for (size_t y = ystart; y < yend; y++) {
size_t img_y = (y * img_height) / fb->framebuffer_height; // calculate Y with full precision
size_t off = img_pitch * img_y;
size_t canvas_off = fb->framebuffer_width * y;
size_t ratio = int_to_fixedp6(img_width) / fb->framebuffer_width;
fixedp6 img_x = ratio * xstart;
for (size_t x = xstart; x < xend; x++) {
uint32_t img_pixel = *(uint32_t*)(img + fixedp6_to_int(img_x) * colsize + off);
uint32_t i = blend(fb, x, y, img_pixel);
bg_canvas[canvas_off + x] = i;
img_x += ratio;
}
}
break;
}
}
static uint32_t blend_external(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { (void)fb; (void)x; (void)y; return orig; }
static uint32_t blend_internal(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { (void)fb; (void)x; (void)y; return colour_blend(default_bg, orig); }
static uint32_t blend_margin(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { return blend_gradient_from_box(fb, x, y, orig, default_bg); }
static void loop_external(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_external); }
static void loop_margin(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_margin); }
static void loop_internal(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_internal); }
static void generate_canvas(struct fb_info *fb) {
if (background) {
bg_canvas_size = fb->framebuffer_width * fb->framebuffer_height * sizeof(uint32_t);
bg_canvas = ext_mem_alloc(bg_canvas_size);
int64_t margin_no_gradient = (int64_t)margin - margin_gradient;
if (margin_no_gradient < 0) {
margin_no_gradient = 0;
}
size_t scan_stop_x = fb->framebuffer_width - margin_no_gradient;
size_t scan_stop_y = fb->framebuffer_height - margin_no_gradient;
loop_external(fb, 0, fb->framebuffer_width, 0, margin_no_gradient);
loop_external(fb, 0, fb->framebuffer_width, scan_stop_y, fb->framebuffer_height);
loop_external(fb, 0, margin_no_gradient, margin_no_gradient, scan_stop_y);
loop_external(fb, scan_stop_x, fb->framebuffer_width, margin_no_gradient, scan_stop_y);
size_t gradient_stop_x = fb->framebuffer_width - margin;
size_t gradient_stop_y = fb->framebuffer_height - margin;
if (margin_gradient) {
loop_margin(fb, margin_no_gradient, scan_stop_x, margin_no_gradient, margin);
loop_margin(fb, margin_no_gradient, scan_stop_x, gradient_stop_y, scan_stop_y);
loop_margin(fb, margin_no_gradient, margin, margin, gradient_stop_y);
loop_margin(fb, gradient_stop_x, scan_stop_x, margin, gradient_stop_y);
}
loop_internal(fb, margin, gradient_stop_x, margin, gradient_stop_y);
} else {
bg_canvas = NULL;
}
}
bool gterm_init(struct fb_info **_fbs, size_t *_fbs_count,
char *config, size_t width, size_t height) {
static struct fb_info *fbs;
static size_t fbs_count;
static bool prev_valid = false;
static char *prev_config;
static size_t prev_width, prev_height;
if (prev_valid && config == prev_config && width == prev_width && height == prev_height) {
*_fbs = fbs;
*_fbs_count = fbs_count;
reset_term();
return true;
}
prev_valid = false;
if (quiet) {
term_notready();
return false;
}
#if defined (UEFI)
if (serial || COM_OUTPUT) {
term_fallback();
return true;
}
#endif
term_notready();
// We force bpp to 32
fb_init(&fbs, &fbs_count, width, height, 32);
if (_fbs != NULL) {
*_fbs = fbs;
}
if (_fbs_count != NULL) {
*_fbs_count = fbs_count;
}
if (fbs_count == 0) {
return false;
}
// default scheme
margin = 64;
margin_gradient = 4;
uint32_t ansi_colours[8];
ansi_colours[0] = 0x00000000; // black
ansi_colours[1] = 0x00aa0000; // red
ansi_colours[2] = 0x0000aa00; // green
ansi_colours[3] = 0x00aa5500; // brown
ansi_colours[4] = 0x000000aa; // blue
ansi_colours[5] = 0x00aa00aa; // magenta
ansi_colours[6] = 0x0000aaaa; // cyan
ansi_colours[7] = 0x00aaaaaa; // grey
char *colours = config_get_value(config, 0, "TERM_PALETTE");
if (colours != NULL) {
const char *first = colours;
size_t i;
for (i = 0; i < 8; i++) {
const char *last;
uint32_t col = strtoui(first, &last, 16);
if (first == last)
break;
ansi_colours[i] = col & 0xffffff;
if (*last == 0)
break;
first = last + 1;
}
}
uint32_t ansi_bright_colours[8];
ansi_bright_colours[0] = 0x00555555; // black
ansi_bright_colours[1] = 0x00ff5555; // red
ansi_bright_colours[2] = 0x0055ff55; // green
ansi_bright_colours[3] = 0x00ffff55; // brown
ansi_bright_colours[4] = 0x005555ff; // blue
ansi_bright_colours[5] = 0x00ff55ff; // magenta
ansi_bright_colours[6] = 0x0055ffff; // cyan
ansi_bright_colours[7] = 0x00ffffff; // grey
char *bright_colours = config_get_value(config, 0, "TERM_PALETTE_BRIGHT");
if (bright_colours != NULL) {
const char *first = bright_colours;
size_t i;
for (i = 0; i < 8; i++) {
const char *last;
uint32_t col = strtoui(first, &last, 16);
if (first == last)
break;
ansi_bright_colours[i] = col & 0xffffff;
if (*last == 0)
break;
first = last + 1;
}
}
default_bg = 0x00000000; // background (black)
default_fg = 0x00aaaaaa; // foreground (grey)
default_bg_bright = 0x00555555; // background (black)
default_fg_bright = 0x00ffffff; // foreground (grey)
char *theme_background = config_get_value(config, 0, "TERM_BACKGROUND");
if (theme_background != NULL) {
default_bg = strtoui(theme_background, NULL, 16);
}
char *theme_foreground = config_get_value(config, 0, "TERM_FOREGROUND");
if (theme_foreground != NULL) {
default_fg = strtoui(theme_foreground, NULL, 16) & 0xffffff;
}
char *theme_background_bright = config_get_value(config, 0, "TERM_BACKGROUND_BRIGHT");
if (theme_background_bright != NULL) {
default_bg_bright = strtoui(theme_background_bright, NULL, 16);
}
char *theme_foreground_bright = config_get_value(config, 0, "TERM_FOREGROUND_BRIGHT");
if (theme_foreground_bright != NULL) {
default_fg_bright = strtoui(theme_foreground_bright, NULL, 16);
}
background = NULL;
char *background_path = config_get_value(config, 0, "TERM_WALLPAPER");
if (background_path != NULL) {
struct file_handle *bg_file;
if ((bg_file = uri_open(background_path)) != NULL) {
background = image_open(bg_file);
fclose(bg_file);
}
}
if (background == NULL) {
margin = 0;
margin_gradient = 0;
} else {
if (theme_background == NULL) {
default_bg = 0x80000000;
}
}
char *theme_margin = config_get_value(config, 0, "TERM_MARGIN");
if (theme_margin != NULL) {
margin = strtoui(theme_margin, NULL, 10);
}
char *theme_margin_gradient = config_get_value(config, 0, "TERM_MARGIN_GRADIENT");
if (theme_margin_gradient != NULL) {
margin_gradient = strtoui(theme_margin_gradient, NULL, 10);
}
size_t font_width = 8;
size_t font_height = 16;
size_t font_size = (font_width * font_height * FLANTERM_FB_FONT_GLYPHS) / 8;
#define FONT_MAX 16384
uint8_t *font = ext_mem_alloc(FONT_MAX);
memcpy(font, builtin_font, 4096);
size_t tmp_font_width, tmp_font_height;
char *menu_font_size = config_get_value(config, 0, "TERM_FONT_SIZE");
if (menu_font_size != NULL) {
parse_resolution(&tmp_font_width, &tmp_font_height, NULL, menu_font_size);
size_t tmp_font_size = (tmp_font_width * tmp_font_height * FLANTERM_FB_FONT_GLYPHS) / 8;
if (tmp_font_size > FONT_MAX) {
print("Font would be too large (%u bytes, %u bytes allowed). Not loading.\n", tmp_font_size, FONT_MAX);
goto no_load_font;
}
font_size = tmp_font_size;
}
char *menu_font = config_get_value(config, 0, "TERM_FONT");
if (menu_font != NULL) {
struct file_handle *f;
if ((f = uri_open(menu_font)) == NULL) {
print("menu: Could not open font file.\n");
} else {
fread(f, font, 0, font_size);
if (menu_font_size != NULL) {
font_width = tmp_font_width;
font_height = tmp_font_height;
}
fclose(f);
}
}
no_load_font:;
size_t font_spacing = 1;
char *font_spacing_str = config_get_value(config, 0, "TERM_FONT_SPACING");
if (font_spacing_str != NULL) {
font_spacing = strtoui(font_spacing_str, NULL, 10);
}
size_t font_scale_x = 1;
size_t font_scale_y = 1;
bool font_scale_is_default = true;
char *menu_font_scale = config_get_value(config, 0, "TERM_FONT_SCALE");
if (menu_font_scale != NULL) {
parse_resolution(&font_scale_x, &font_scale_y, NULL, menu_font_scale);
if (font_scale_x > 8 || font_scale_y > 8) {
font_scale_x = 1;
font_scale_y = 1;
} else {
font_scale_is_default = false;
}
}
terms_i = 0;
terms = ext_mem_alloc(fbs_count * sizeof(void *));
for (size_t i = 0; i < fbs_count; i++) {
struct fb_info *fb = &fbs[i];
// Ensure that this framebuffer uses 32-bits per pixel.
if (fb->framebuffer_bpp != 32) {
continue;
}
if (background != NULL) {
char *background_layout = config_get_value(config, 0, "TERM_WALLPAPER_STYLE");
if (background_layout != NULL && strcmp(background_layout, "centered") == 0) {
char *background_colour = config_get_value(config, 0, "TERM_BACKDROP");
if (background_colour == NULL)
background_colour = "0";
uint32_t bg_col = strtoui(background_colour, NULL, 16);
image_make_centered(background, fb->framebuffer_width, fb->framebuffer_height, bg_col);
} else if (background_layout != NULL && strcmp(background_layout, "tiled") == 0) {
} else {
image_make_stretched(background, fb->framebuffer_width, fb->framebuffer_height);
}
}
generate_canvas(fb);
if (font_scale_is_default) {
if (fb->framebuffer_width >= (1920 + 1920 / 3) && fb->framebuffer_height >= (1080 + 1080 / 3)) {
font_scale_x = 2;
font_scale_y = 2;
}
if (fb->framebuffer_width >= (3840 + 3840 / 3) && fb->framebuffer_height >= (2160 + 2160 / 3)) {
font_scale_x = 4;
font_scale_y = 4;
}
}
terms[terms_i] = flanterm_fb_init(ext_mem_alloc,
pmm_free,
(void *)(uintptr_t)fb->framebuffer_addr,
fb->framebuffer_width, fb->framebuffer_height, fb->framebuffer_pitch,
fb->red_mask_size, fb->red_mask_shift,
fb->green_mask_size, fb->green_mask_shift,
fb->blue_mask_size, fb->blue_mask_shift,
bg_canvas,
ansi_colours, ansi_bright_colours,
&default_bg, &default_fg,
&default_bg_bright, &default_fg_bright,
font, font_width, font_height, font_spacing,
font_scale_x, font_scale_y,
margin);
if (terms[terms_i] != NULL) {
terms_i++;
}
if (bg_canvas != NULL) {
pmm_free(bg_canvas, bg_canvas_size);
}
}
pmm_free(font, FONT_MAX);
if (background != NULL) {
image_close(background);
background = NULL;
}
if (terms_i == 0) {
return false;
}
for (size_t i = 0; i < terms_i; i++) {
struct flanterm_context *term = terms[i];
if (serial) {
term->cols = term->cols > 80 ? 80 : term->cols;
term->rows = term->rows > 24 ? 24 : term->rows;
}
}
size_t min_cols = (size_t)-1;
size_t min_rows = (size_t)-1;
for (size_t i = 0; i < terms_i; i++) {
struct flanterm_context *term = terms[i];
if (term->cols < min_cols) {
min_cols = term->cols;
}
if (term->rows < min_rows) {
min_rows = term->rows;
}
}
for (size_t i = 0; i < terms_i; i++) {
struct flanterm_context *term = terms[i];
term->cols = min_cols;
term->rows = min_rows;
flanterm_context_reinit(term);
}
term_backend = GTERM;
prev_config = config;
prev_height = height;
prev_width = width;
prev_valid = true;
return true;
}

11
common/lib/gterm.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef __LIB__GTERM_H__
#define __LIB__GTERM_H__
#include <stddef.h>
#include <stdbool.h>
#include <lib/fb.h>
bool gterm_init(struct fb_info **ret, size_t *_fbs_count,
char *config, size_t width, size_t height);
#endif

69
common/lib/guid.c Normal file
View File

@ -0,0 +1,69 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <lib/guid.h>
#include <lib/misc.h>
bool is_valid_guid(const char *s) {
for (size_t i = 0; ; i++) {
switch (i) {
case 8:
case 13:
case 18:
case 23:
if (s[i] != '-')
return false;
break;
case 36:
return s[i] == 0;
default:
if (digit_to_int(s[i]) == -1)
return false;
break;
}
}
}
static void guid_convert_le_cluster(uint8_t *dest, const char *s, int len) {
size_t p = 0;
for (int i = len - 1; i >= 0; i--) {
int val = digit_to_int(s[i]);
i % 2 ? (dest[p] = val) : (dest[p++] |= val << 4);
}
}
static void guid_convert_be_cluster(uint8_t *dest, const char *s, int len) {
size_t p = 0;
for (int i = 0; i < len; i++) {
int val = digit_to_int(s[i]);
i % 2 ? (dest[p++] |= val) : (dest[p] = val << 4);
}
}
bool string_to_guid_be(struct guid *guid, const char *s) {
if (!is_valid_guid(s))
return false;
guid_convert_be_cluster((uint8_t *)guid + 0, s + 0, 8);
guid_convert_be_cluster((uint8_t *)guid + 4, s + 9, 4);
guid_convert_be_cluster((uint8_t *)guid + 6, s + 14, 4);
guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4);
guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12);
return true;
}
bool string_to_guid_mixed(struct guid *guid, const char *s) {
if (!is_valid_guid(s))
return false;
guid_convert_le_cluster((uint8_t *)guid + 0, s + 0, 8);
guid_convert_le_cluster((uint8_t *)guid + 4, s + 9, 4);
guid_convert_le_cluster((uint8_t *)guid + 6, s + 14, 4);
guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4);
guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12);
return true;
}

18
common/lib/guid.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __LIB__GUID_H__
#define __LIB__GUID_H__
#include <stdint.h>
#include <stdbool.h>
struct guid {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
};
bool is_valid_guid(const char *s);
bool string_to_guid_be(struct guid *guid, const char *s);
bool string_to_guid_mixed(struct guid *guid, const char *s);
#endif

64
common/lib/image.c Normal file
View File

@ -0,0 +1,64 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/image.h>
#include <lib/config.h>
#include <lib/misc.h>
#include <mm/pmm.h>
#include <stb/stb_image.h>
void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour) {
image->type = IMAGE_CENTERED;
image->x_displacement = frame_x_size / 2 - image->x_size / 2;
image->y_displacement = frame_y_size / 2 - image->y_size / 2;
image->back_colour = back_colour;
}
void image_make_stretched(struct image *image, int new_x_size, int new_y_size) {
image->type = IMAGE_STRETCHED;
image->x_size = new_x_size;
image->y_size = new_y_size;
}
struct image *image_open(struct file_handle *file) {
struct image *image = ext_mem_alloc(sizeof(struct image));
image->type = IMAGE_TILED;
void *src = ext_mem_alloc(file->size);
fread(file, src, 0, file->size);
int x, y, bpp;
image->img = stbi_load_from_memory(src, file->size, &x, &y, &bpp, 4);
pmm_free(src, file->size);
if (image->img == NULL) {
pmm_free(image, sizeof(struct image));
return NULL;
}
// Convert ABGR to XRGB
uint32_t *pptr = (void *)image->img;
for (int i = 0; i < x * y; i++) {
pptr[i] = (pptr[i] & 0x0000ff00) | ((pptr[i] & 0x00ff0000) >> 16) | ((pptr[i] & 0x000000ff) << 16);
}
image->x_size = x;
image->y_size = y;
image->pitch = x * 4;
image->bpp = 32;
image->img_width = x;
image->img_height = y;
return image;
}
void image_close(struct image *image) {
stbi_image_free(image->img);
pmm_free(image, sizeof(struct image));
}

32
common/lib/image.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __LIB__IMAGE_H__
#define __LIB__IMAGE_H__
#include <stdint.h>
#include <fs/file.h>
struct image {
size_t x_size;
size_t y_size;
int type;
uint8_t *img;
int bpp;
int pitch;
size_t img_width; // x_size = scaled size, img_width = bitmap size
size_t img_height;
size_t x_displacement;
size_t y_displacement;
uint32_t back_colour;
};
enum {
IMAGE_TILED,
IMAGE_CENTERED,
IMAGE_STRETCHED
};
void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour);
void image_make_stretched(struct image *image, int new_x_size, int new_y_size);
struct image *image_open(struct file_handle *file);
void image_close(struct image *image);
#endif

31
common/lib/libc.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef __LIB__LIBC_H__
#define __LIB__LIBC_H__
#include <stddef.h>
#include <stdbool.h>
bool isprint(int c);
bool isspace(int c);
bool isalpha(int c);
bool isdigit(int c);
int toupper(int c);
int tolower(int c);
int abs(int i);
void *memset(void *, int, size_t);
void *memcpy(void *, const void *, size_t);
int memcmp(const void *, const void *, size_t);
void *memmove(void *, const void *, size_t);
char *strcpy(char *, const char *);
char *strncpy(char *, const char *, size_t);
size_t strlen(const char *);
int strcmp(const char *, const char *);
int strcasecmp(const char *, const char *);
int strncmp(const char *, const char *, size_t);
int strncasecmp(const char *, const char *, size_t);
int inet_pton(const char *src, void *dst);
#endif

134
common/lib/libc.s2.c Normal file
View File

@ -0,0 +1,134 @@
#include <stddef.h>
#include <stdint.h>
#include <lib/libc.h>
#include <stdbool.h>
#include <lib/misc.h>
bool isprint(int c) {
return c >= ' ' && c <= '~';
}
bool isspace(int c) {
return (c >= '\t' && c <= 0xD) || c == ' ';
}
bool isalpha(int c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
bool isdigit(int c) {
return c >= '0' && c <= '9';
}
int toupper(int c) {
if (c >= 'a' && c <= 'z') {
return c - 0x20;
}
return c;
}
int tolower(int c) {
if (c >= 'A' && c <= 'Z') {
return c + 0x20;
}
return c;
}
int abs(int i) {
return i < 0 ? -i : i;
}
char *strcpy(char *dest, const char *src) {
size_t i;
for (i = 0; src[i]; i++)
dest[i] = src[i];
dest[i] = 0;
return dest;
}
char *strncpy(char *dest, const char *src, size_t n) {
size_t i;
for (i = 0; i < n && src[i]; i++)
dest[i] = src[i];
for ( ; i < n; i++)
dest[i] = 0;
return dest;
}
int strcmp(const char *s1, const char *s2) {
for (size_t i = 0; ; i++) {
char c1 = s1[i], c2 = s2[i];
if (c1 != c2)
return c1 < c2 ? -1 : 1;
if (!c1)
return 0;
}
}
int strcasecmp(const char *s1, const char *s2) {
for (size_t i = 0; ; i++) {
char c1 = s1[i], c2 = s2[i];
if (tolower(c1) != tolower(c2))
return c1 < c2 ? -1 : 1;
if (!c1)
return 0;
}
}
int strncmp(const char *s1, const char *s2, size_t n) {
for (size_t i = 0; i < n; i++) {
char c1 = s1[i], c2 = s2[i];
if (c1 != c2)
return c1 < c2 ? -1 : 1;
if (!c1)
return 0;
}
return 0;
}
int strncasecmp(const char *s1, const char *s2, size_t n) {
for (size_t i = 0; i < n; i++) {
char c1 = s1[i], c2 = s2[i];
if (tolower(c1) != tolower(c2))
return c1 < c2 ? -1 : 1;
if (!c1)
return 0;
}
return 0;
}
size_t strlen(const char *str) {
size_t len;
for (len = 0; str[len]; len++);
return len;
}
int inet_pton(const char *src, void *dst) {
uint8_t array[4];
const char *current = src;
for (int i = 0; i < 4; i++) {
const char *newcur;
uint64_t value = strtoui(current, &newcur, 10);
if (current == newcur)
return -1;
current = newcur;
if (*current == 0 && i < 3)
return -1;
if (value > 255)
return -1;
current++;
array[i] = value;
}
memcpy(dst, array, 4);
return 0;
}

View File

@ -0,0 +1,52 @@
// Branch to \el1 if in EL1, or to \el2 if in EL2
// Uses \reg, halts if not in EL1 or EL2
.macro PICK_EL reg, el1, el2
mrs \reg, currentel
and \reg, \reg, #0b1100
cmp \reg, #0b0100 // EL1?
b.eq \el1
cmp \reg, #0b1000 // EL2?
b.eq \el2
// Halt otherwise
msr daifset, #0b1111
99:
wfi
b 99b
.endm
// Zero out all general purpose registers apart from X0
.macro ZERO_REGS_EXCEPT_X0
mov x1, xzr
mov x2, xzr
mov x3, xzr
mov x4, xzr
mov x5, xzr
mov x6, xzr
mov x7, xzr
mov x8, xzr
mov x9, xzr
mov x10, xzr
mov x11, xzr
mov x12, xzr
mov x13, xzr
mov x14, xzr
mov x15, xzr
mov x16, xzr
mov x17, xzr
mov x18, xzr
mov x19, xzr
mov x20, xzr
mov x21, xzr
mov x22, xzr
mov x23, xzr
mov x24, xzr
mov x25, xzr
mov x26, xzr
mov x27, xzr
mov x28, xzr
mov x29, xzr
mov x30, xzr
.endm

57
common/lib/mem.asm_x86_64 Normal file
View File

@ -0,0 +1,57 @@
section .text
global memcpy
memcpy:
mov rcx, rdx
mov rax, rdi
rep movsb
ret
global memset
memset:
push rdi
mov rax, rsi
mov rcx, rdx
rep stosb
pop rax
ret
global memmove
memmove:
mov rcx, rdx
mov rax, rdi
cmp rdi, rsi
ja .copy_backwards
rep movsb
jmp .done
.copy_backwards:
lea rdi, [rdi+rcx-1]
lea rsi, [rsi+rcx-1]
std
rep movsb
cld
.done:
ret
global memcmp
memcmp:
mov rcx, rdx
repe cmpsb
je .equal
mov al, byte [rdi-1]
sub al, byte [rsi-1]
movsx rax, al
jmp .done
.equal:
xor eax, eax
.done:
ret
section .note.GNU-stack noalloc noexec nowrite progbits

111
common/lib/mem.s2.asm_ia32 Normal file
View File

@ -0,0 +1,111 @@
section .text
global memcpy
memcpy:
push esi
push edi
mov eax, dword [esp+12]
mov edi, eax
mov esi, dword [esp+16]
mov ecx, dword [esp+20]
rep movsb
pop edi
pop esi
ret
global memset
memset:
push edi
mov edx, dword [esp+8]
mov edi, edx
mov eax, dword [esp+12]
mov ecx, dword [esp+16]
rep stosb
mov eax, edx
pop edi
ret
global memmove
memmove:
push esi
push edi
mov eax, dword [esp+12]
mov edi, eax
mov esi, dword [esp+16]
mov ecx, dword [esp+20]
cmp edi, esi
ja .copy_backwards
rep movsb
jmp .done
.copy_backwards:
lea edi, [edi+ecx-1]
lea esi, [esi+ecx-1]
std
rep movsb
cld
.done:
pop edi
pop esi
ret
global memcmp
memcmp:
push esi
push edi
mov edi, dword [esp+12]
mov esi, dword [esp+16]
mov ecx, dword [esp+20]
repe cmpsb
je .equal
mov al, byte [edi-1]
sub al, byte [esi-1]
movsx eax, al
jmp .done
.equal:
xor eax, eax
.done:
pop edi
pop esi
ret
global memcpy32to64
memcpy32to64:
push ebp
mov ebp, esp
push esi
push edi
push 0x28
call .p1
.p1:
add dword [esp], .mode64 - .p1
retfd
bits 64
.mode64:
mov rdi, [rbp + 8]
mov rsi, [rbp + 16]
mov rcx, [rbp + 24]
rep movsb
push 0x18
call .p2
.p2:
add qword [rsp], .mode32 - .p2
retfq
bits 32
.mode32:
pop edi
pop esi
pop ebp
ret
section .note.GNU-stack noalloc noexec nowrite progbits

57
common/lib/memory.s2.c Normal file
View File

@ -0,0 +1,57 @@
#if !defined (__x86_64__) && !defined (__i386__)
#include <stdint.h>
#include <stddef.h>
void *memcpy(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
return dest;
}
void *memset(void *s, int c, size_t n) {
uint8_t *p = (uint8_t *)s;
for (size_t i = 0; i < n; i++) {
p[i] = (uint8_t)c;
}
return s;
}
void *memmove(void *dest, const void *src, size_t n) {
uint8_t *pdest = (uint8_t *)dest;
const uint8_t *psrc = (const uint8_t *)src;
if (src > dest) {
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if (src < dest) {
for (size_t i = n; i > 0; i--) {
pdest[i-1] = psrc[i-1];
}
}
return dest;
}
int memcmp(const void *s1, const void *s2, size_t n) {
const uint8_t *p1 = (const uint8_t *)s1;
const uint8_t *p2 = (const uint8_t *)s2;
for (size_t i = 0; i < n; i++) {
if (p1[i] != p2[i]) {
return p1[i] < p2[i] ? -1 : 1;
}
}
return 0;
}
#endif

317
common/lib/misc.c Normal file
View File

@ -0,0 +1,317 @@
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/print.h>
#include <lib/trace.h>
#include <lib/real.h>
#include <fs/file.h>
#include <mm/pmm.h>
#if defined (UEFI)
EFI_SYSTEM_TABLE *gST;
EFI_BOOT_SERVICES *gBS;
EFI_RUNTIME_SERVICES *gRT;
EFI_HANDLE efi_image_handle;
EFI_MEMORY_DESCRIPTOR *efi_mmap = NULL;
UINTN efi_mmap_size = 0, efi_desc_size = 0;
UINT32 efi_desc_ver = 0;
#endif
bool editor_enabled = true;
bool help_hidden = false;
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf) {
size_t res[3] = {0};
const char *first = buf;
for (size_t i = 0; i < 3; i++) {
const char *last;
size_t x = strtoui(first, &last, 10);
if (first == last)
break;
res[i] = x;
if (*last == 0)
break;
first = last + 1;
}
if (res[0] == 0 || res[1] == 0)
return false;
if (res[2] == 0)
res[2] = 32;
*width = res[0], *height = res[1];
if (bpp != NULL)
*bpp = res[2];
return true;
}
// This integer sqrt implementation has been adapted from:
// https://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
uint64_t sqrt(uint64_t a_nInput) {
uint64_t op = a_nInput;
uint64_t res = 0;
uint64_t one = (uint64_t)1 << 62;
// "one" starts at the highest power of four <= than the argument.
while (one > op) {
one >>= 2;
}
while (one != 0) {
if (op >= res + one) {
op = op - (res + one);
res = res + 2 * one;
}
res >>= 1;
one >>= 2;
}
return res;
}
size_t get_trailing_zeros(uint64_t val) {
for (size_t i = 0; i < 64; i++) {
if ((val & 1) != 0) {
return i;
}
val >>= 1;
}
return 64;
}
uint32_t oct2bin(uint8_t *str, uint32_t max) {
uint32_t value = 0;
while (max-- > 0) {
value <<= 3;
value += *str++ - '0';
}
return value;
}
uint32_t hex2bin(uint8_t *str, uint32_t size) {
uint32_t value = 0;
while (size-- > 0) {
value <<= 4;
if (*str >= '0' && *str <= '9')
value += (uint32_t)((*str) - '0');
else if (*str >= 'A' && *str <= 'F')
value += (uint32_t)((*str) - 'A' + 10);
else if (*str >= 'a' && *str <= 'f')
value += (uint32_t)((*str) - 'a' + 10);
str++;
}
return value;
}
#if defined (UEFI)
#if defined (__riscv)
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void) {
EFI_GUID boot_proto_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
RISCV_EFI_BOOT_PROTOCOL *proto;
// LocateProtocol() is available from EFI version 1.1
if (gBS->Hdr.Revision >= ((1 << 16) | 10)) {
if (gBS->LocateProtocol(&boot_proto_guid, NULL, (void **)&proto) == EFI_SUCCESS) {
return proto;
}
}
UINTN bufsz = 0;
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, NULL) != EFI_BUFFER_TOO_SMALL)
return NULL;
EFI_HANDLE *handles_buf = ext_mem_alloc(bufsz);
if (handles_buf == NULL)
return NULL;
if (bufsz < sizeof(EFI_HANDLE))
goto error;
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, handles_buf) != EFI_SUCCESS)
goto error;
if (gBS->HandleProtocol(handles_buf[0], &boot_proto_guid, (void **)&proto) != EFI_SUCCESS)
goto error;
pmm_free(handles_buf, bufsz);
return proto;
error:
pmm_free(handles_buf, bufsz);
return NULL;
}
#endif
no_unwind bool efi_boot_services_exited = false;
bool efi_exit_boot_services(void) {
EFI_STATUS status;
EFI_MEMORY_DESCRIPTOR tmp_mmap[1];
efi_mmap_size = sizeof(tmp_mmap);
UINTN mmap_key = 0;
gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver);
efi_mmap_size += 4096;
status = gBS->FreePool(efi_mmap);
if (status) {
goto fail;
}
status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&efi_mmap);
if (status) {
goto fail;
}
EFI_MEMORY_DESCRIPTOR *efi_copy;
status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size * 2, (void **)&efi_copy);
if (status) {
goto fail;
}
const size_t EFI_COPY_MAX_ENTRIES = (efi_mmap_size * 2) / efi_desc_size;
size_t retries = 0;
retry:
status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver);
if (retries == 0 && status) {
goto fail;
}
// Be gone, UEFI!
status = gBS->ExitBootServices(efi_image_handle, mmap_key);
if (status) {
if (retries == 128) {
goto fail;
}
retries++;
goto retry;
}
#if defined(__x86_64__) || defined(__i386__)
asm volatile ("cli" ::: "memory");
#elif defined (__aarch64__)
asm volatile ("msr daifset, #15" ::: "memory");
#elif defined (__riscv64)
asm volatile ("csrci sstatus, 0x2" ::: "memory");
#else
#error Unknown architecture
#endif
// Go through new EFI memmap and free up bootloader entries
size_t entry_count = efi_mmap_size / efi_desc_size;
size_t efi_copy_i = 0;
for (size_t i = 0; i < entry_count; i++) {
EFI_MEMORY_DESCRIPTOR *orig_entry = (void *)efi_mmap + i * efi_desc_size;
EFI_MEMORY_DESCRIPTOR *new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
memcpy(new_entry, orig_entry, efi_desc_size);
uint64_t base = orig_entry->PhysicalStart;
uint64_t length = orig_entry->NumberOfPages * 4096;
uint64_t top = base + length;
// Find for a match in the untouched memory map
for (size_t j = 0; j < untouched_memmap_entries; j++) {
if (untouched_memmap[j].type != MEMMAP_USABLE)
continue;
if (top > untouched_memmap[j].base && top <= untouched_memmap[j].base + untouched_memmap[j].length) {
if (untouched_memmap[j].base < base) {
new_entry->NumberOfPages = (base - untouched_memmap[j].base) / 4096;
efi_copy_i++;
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
panic(false, "efi: New memory map exhausted");
}
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
memcpy(new_entry, orig_entry, efi_desc_size);
new_entry->NumberOfPages -= (base - untouched_memmap[j].base) / 4096;
new_entry->PhysicalStart = base;
new_entry->VirtualStart = new_entry->PhysicalStart;
length = new_entry->NumberOfPages * 4096;
top = base + length;
}
if (untouched_memmap[j].base > base) {
new_entry->NumberOfPages = (untouched_memmap[j].base - base) / 4096;
efi_copy_i++;
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
panic(false, "efi: New memory map exhausted");
}
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
memcpy(new_entry, orig_entry, efi_desc_size);
new_entry->NumberOfPages -= (untouched_memmap[j].base - base) / 4096;
new_entry->PhysicalStart = untouched_memmap[j].base;
new_entry->VirtualStart = new_entry->PhysicalStart;
base = new_entry->PhysicalStart;
length = new_entry->NumberOfPages * 4096;
top = base + length;
}
if (length < untouched_memmap[j].length) {
panic(false, "efi: Memory map corruption");
}
new_entry->Type = EfiConventionalMemory;
if (length == untouched_memmap[j].length) {
// It's a perfect match!
break;
}
new_entry->NumberOfPages = untouched_memmap[j].length / 4096;
efi_copy_i++;
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
panic(false, "efi: New memory map exhausted");
}
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
memcpy(new_entry, orig_entry, efi_desc_size);
new_entry->NumberOfPages = (length - untouched_memmap[j].length) / 4096;
new_entry->PhysicalStart = base + untouched_memmap[j].length;
new_entry->VirtualStart = new_entry->PhysicalStart;
break;
}
}
efi_copy_i++;
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
panic(false, "efi: New memory map exhausted");
}
}
efi_mmap = efi_copy;
efi_mmap_size = efi_copy_i * efi_desc_size;
efi_boot_services_exited = true;
printv("efi: Exited boot services.\n");
return true;
fail:
panic(false, "efi: Failed to exit boot services");
}
#endif

112
common/lib/misc.h Normal file
View File

@ -0,0 +1,112 @@
#ifndef __LIB__MISC_H__
#define __LIB__MISC_H__
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdnoreturn.h>
#include <fs/file.h>
#include <lib/part.h>
#include <lib/libc.h>
#if defined (UEFI)
# include <efi.h>
# if defined (__riscv64)
# include <protocol/riscv/efiboot.h>
# endif
#endif
#if defined (UEFI)
extern EFI_SYSTEM_TABLE *gST;
extern EFI_BOOT_SERVICES *gBS;
extern EFI_RUNTIME_SERVICES *gRT;
extern EFI_HANDLE efi_image_handle;
extern EFI_MEMORY_DESCRIPTOR *efi_mmap;
extern UINTN efi_mmap_size, efi_desc_size;
extern UINT32 efi_desc_ver;
extern bool efi_boot_services_exited;
bool efi_exit_boot_services(void);
#endif
extern const char bsd_2_clause[];
extern struct volume *boot_volume;
#if defined (BIOS)
extern bool stage3_loaded;
#endif
extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic;
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf);
void get_absolute_path(char *path_ptr, const char *path, const char *pwd);
uint32_t oct2bin(uint8_t *str, uint32_t max);
uint32_t hex2bin(uint8_t *str, uint32_t size);
uint64_t sqrt(uint64_t a_nInput);
size_t get_trailing_zeros(uint64_t val);
int digit_to_int(char c);
uint8_t bcd_to_int(uint8_t val);
uint8_t int_to_bcd(uint8_t val);
noreturn void panic(bool allow_menu, const char *fmt, ...);
int pit_sleep_and_quit_on_keypress(int seconds);
uint64_t strtoui(const char *s, const char **end, int base);
#if defined (__i386__)
void memcpy32to64(uint64_t, uint64_t, uint64_t);
#elif defined (__x86_64__) || defined (__aarch64__) || defined(__riscv64)
# define memcpy32to64(X, Y, Z) memcpy((void *)(uintptr_t)(X), (void *)(uintptr_t)(Y), Z)
#else
#error Unknown architecture
#endif
#define DIV_ROUNDUP(a, b) ({ \
__auto_type DIV_ROUNDUP_a = (a); \
__auto_type DIV_ROUNDUP_b = (b); \
(DIV_ROUNDUP_a + (DIV_ROUNDUP_b - 1)) / DIV_ROUNDUP_b; \
})
#define ALIGN_UP(x, a) ({ \
__auto_type ALIGN_UP_value = (x); \
__auto_type ALIGN_UP_align = (a); \
ALIGN_UP_value = DIV_ROUNDUP(ALIGN_UP_value, ALIGN_UP_align) * ALIGN_UP_align; \
ALIGN_UP_value; \
})
#define ALIGN_DOWN(x, a) ({ \
__auto_type ALIGN_DOWN_value = (x); \
__auto_type ALIGN_DOWN_align = (a); \
ALIGN_DOWN_value = (ALIGN_DOWN_value / ALIGN_DOWN_align) * ALIGN_DOWN_align; \
ALIGN_DOWN_value; \
})
#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
typedef char symbol[];
noreturn void stage3_common(void);
#if defined (__x86_64__) || defined (__i386__)
noreturn void common_spinup(void *fnptr, int args, ...);
#elif defined (__aarch64__)
noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr,
uint64_t mair, uint64_t tcr, uint64_t ttbr0,
uint64_t ttbr1, uint64_t target_x0);
#elif defined (__riscv64)
noreturn void riscv_spinup(uint64_t entry, uint64_t sp, uint64_t satp);
#if defined (UEFI)
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void);
#endif
#else
#error Unknown architecture
#endif
#define no_unwind __attribute__((section(".no_unwind")))
#endif

110
common/lib/misc.s2.c Normal file
View File

@ -0,0 +1,110 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/misc.h>
#include <lib/print.h>
bool verbose = false;
bool quiet = false;
bool serial = false;
bool hash_mismatch_panic = false;
uint8_t bcd_to_int(uint8_t val) {
return (val & 0x0f) + ((val & 0xf0) >> 4) * 10;
}
uint8_t int_to_bcd(uint8_t val) {
return (val % 10) | (val / 10) << 4;
}
int digit_to_int(char c) {
if (c >= 'a' && c <= 'f') {
return (c - 'a') + 10;
}
if (c >= 'A' && c <= 'F') {
return (c - 'A') + 10;
}
if (c >= '0' && c <= '9'){
return c - '0';
}
return -1;
}
uint64_t strtoui(const char *s, const char **end, int base) {
uint64_t n = 0;
for (size_t i = 0; ; i++) {
int d = digit_to_int(s[i]);
if (d == -1) {
if (end != NULL)
*end = &s[i];
break;
}
n = n * base + d;
}
return n;
}
void get_absolute_path(char *path_ptr, const char *path, const char *pwd) {
char *orig_ptr = path_ptr;
if (!*path) {
strcpy(path_ptr, pwd);
return;
}
if (*path != '/') {
strcpy(path_ptr, pwd);
path_ptr += strlen(path_ptr);
} else {
*path_ptr = '/';
path_ptr++;
path++;
}
goto first_run;
for (;;) {
switch (*path) {
case '/':
path++;
first_run:
if (*path == '/') continue;
if ((!strncmp(path, ".\0", 2))
|| (!strncmp(path, "./\0", 3))) {
goto term;
}
if ((!strncmp(path, "..\0", 3))
|| (!strncmp(path, "../\0", 4))) {
while (*path_ptr != '/') path_ptr--;
if (path_ptr == orig_ptr) path_ptr++;
goto term;
}
if (!strncmp(path, "../", 3)) {
while (*path_ptr != '/') path_ptr--;
if (path_ptr == orig_ptr) path_ptr++;
path += 2;
*path_ptr = 0;
continue;
}
if (!strncmp(path, "./", 2)) {
path += 1;
continue;
}
if (((path_ptr - 1) != orig_ptr) && (*(path_ptr - 1) != '/')) {
*path_ptr = '/';
path_ptr++;
}
continue;
case '\0':
term:
if ((*(path_ptr - 1) == '/') && ((path_ptr - 1) != orig_ptr))
path_ptr--;
*path_ptr = 0;
return;
default:
*path_ptr = *path;
path++;
path_ptr++;
continue;
}
}
}

85
common/lib/panic.s2.c Normal file
View File

@ -0,0 +1,85 @@
#include <stddef.h>
#include <stdbool.h>
#include <stdnoreturn.h>
#include <lib/print.h>
#include <lib/real.h>
#include <lib/trace.h>
#if defined (UEFI)
# include <efi.h>
#endif
#include <lib/misc.h>
#include <lib/readline.h>
#include <lib/gterm.h>
#include <lib/term.h>
#include <mm/pmm.h>
#include <menu.h>
noreturn void panic(bool allow_menu, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
quiet = false;
if (
#if defined (BIOS)
stage3_loaded == true &&
#endif
term_backend == _NOT_READY) {
term_fallback();
}
if (
#if defined (BIOS)
stage3_loaded == true &&
#endif
term_backend != FALLBACK) {
print("\033[31mPANIC\033[37;1m\033[0m: ");
} else {
print("PANIC: ");
}
vprint(fmt, args);
va_end(args);
print("\n");
print_stacktrace(NULL);
if (
#if defined (BIOS)
stage3_loaded == true &&
#endif
allow_menu == true) {
print("Press a key to return to menu.");
getchar();
// This fixes a crash
term_notready();
menu(false);
/*
fb_clear(&fbinfo);
// release all uefi memory and return to firmware
pmm_release_uefi_mem();
gBS->Exit(efi_image_handle, EFI_ABORTED, 0, NULL);
*/
} else {
#if defined (BIOS)
print("Press CTRL+ALT+DEL to reboot.");
rm_hcf();
#elif defined (UEFI)
print("System halted.");
for (;;) {
#if defined (__x86_64__) || defined (__i386__)
asm ("hlt");
#elif defined (__aarch64__) || defined (__riscv64)
asm ("wfi");
#else
#error Unknown architecture
#endif
}
#endif
}
}

22
common/lib/part.c Normal file
View File

@ -0,0 +1,22 @@
#include <stddef.h>
#include <lib/part.h>
#include <lib/print.h>
#include <lib/readline.h>
void list_volumes(void) {
for (size_t i = 0; i < volume_index_i; i++) {
struct volume *v = volume_index[i];
print("index: %u\n", v->index);
print("is_optical: %u\n", v->is_optical);
print("partition: %u\n", v->partition);
print("fslabel: %s\n", v->fslabel);
print("sector_size: %u\n", v->sector_size);
print("max_partition: %d\n", v->max_partition);
print("first_sect: %U\n", v->first_sect);
print("sect_count: %U\n", v->sect_count);
if (i < volume_index_i - 1) {
print("--- Press a key to continue ---\n");
getchar();
}
}
}

112
common/lib/part.h Normal file
View File

@ -0,0 +1,112 @@
#ifndef __LIB__PART_H__
#define __LIB__PART_H__
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/guid.h>
#if defined (UEFI)
# include <efi.h>
# include <crypt/blake2b.h>
#endif
#define NO_PARTITION (-1)
#define INVALID_TABLE (-2)
#define END_OF_TABLE (-3)
struct volume {
#if defined (UEFI)
EFI_HANDLE efi_handle;
// Block storage
EFI_HANDLE efi_part_handle;
EFI_BLOCK_IO *block_io;
// PXE
EFI_PXE_BASE_CODE_PROTOCOL *pxe_base_code;
bool unique_sector_valid;
uint64_t unique_sector;
uint8_t unique_sector_b2b[BLAKE2B_OUT_BYTES];
#elif defined (BIOS)
int drive;
#endif
size_t fastest_xfer_size;
int index;
bool is_optical;
bool pxe;
int partition;
int sector_size;
struct volume *backing_dev;
int max_partition;
int cache_status;
uint8_t *cache;
uint64_t cached_block;
uint64_t first_sect;
uint64_t sect_count;
bool guid_valid;
struct guid guid;
bool part_guid_valid;
struct guid part_guid;
bool fslabel_valid;
char *fslabel;
};
void list_volumes(void);
bool is_valid_mbr(struct volume *volume);
extern struct volume **volume_index;
extern size_t volume_index_i;
bool gpt_get_guid(struct guid *guid, struct volume *volume);
uint32_t mbr_get_id(struct volume *volume);
int part_get(struct volume *part, struct volume *volume, int partition);
struct volume *volume_get_by_guid(struct guid *guid);
struct volume *volume_get_by_fslabel(char *fslabel);
struct volume *volume_get_by_coord(bool optical, int drive, int partition);
#if defined (BIOS)
struct volume *volume_get_by_bios_drive(int drive);
#endif
bool volume_read(struct volume *part, void *buffer, uint64_t loc, uint64_t count);
#define volume_iterate_parts(_VOLUME_, _BODY_) do { \
struct volume *_VOLUME = _VOLUME_; \
if (_VOLUME->pxe) { \
do { \
struct volume *_PART = _VOLUME; \
_BODY_ \
} while (0); \
} else { \
while (_VOLUME->backing_dev != NULL) { \
_VOLUME = _VOLUME->backing_dev; \
} \
\
int _PART_CNT = -1; \
for (size_t _PARTNO = 0; ; _PARTNO++) { \
if (_PART_CNT > _VOLUME->max_partition) \
break; \
\
struct volume *_PART = volume_get_by_coord(_VOLUME->is_optical, \
_VOLUME->index, _PARTNO); \
if (_PART == NULL) \
continue; \
\
_PART_CNT++; \
\
_BODY_ \
} \
} \
} while (0)
#endif

506
common/lib/part.s2.c Normal file
View File

@ -0,0 +1,506 @@
#include <stddef.h>
#include <stdint.h>
#include <lib/part.h>
#include <drivers/disk.h>
#if defined (BIOS)
# include <lib/real.h>
#endif
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/print.h>
#include <mm/pmm.h>
#include <fs/file.h>
enum {
CACHE_NOT_READY = 0,
CACHE_READY
};
static bool cache_block(struct volume *volume, uint64_t block) {
if (volume->cache_status == CACHE_READY && block == volume->cached_block)
return true;
volume->cache_status = CACHE_NOT_READY;
if (volume->cache == NULL)
volume->cache =
ext_mem_alloc(volume->fastest_xfer_size * volume->sector_size);
if (volume->first_sect % (volume->sector_size / 512)) {
return false;
}
uint64_t first_sect = volume->first_sect / (volume->sector_size / 512);
uint64_t xfer_size = volume->fastest_xfer_size;
for (;;) {
int ret = disk_read_sectors(volume, volume->cache,
first_sect + block * volume->fastest_xfer_size,
xfer_size);
switch (ret) {
case DISK_NO_MEDIA:
return false;
case DISK_SUCCESS:
goto disk_success;
}
xfer_size--;
if (xfer_size == 0) {
return false;
}
}
disk_success:
volume->cache_status = CACHE_READY;
volume->cached_block = block;
return true;
}
bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t count) {
if (volume->pxe) {
panic(false, "Attempted volume_read() on pxe");
}
uint64_t block_size = volume->fastest_xfer_size * volume->sector_size;
uint64_t progress = 0;
while (progress < count) {
uint64_t block = (loc + progress) / block_size;
if (!cache_block(volume, block))
return false;
uint64_t chunk = count - progress;
uint64_t offset = (loc + progress) % block_size;
if (chunk > block_size - offset)
chunk = block_size - offset;
memcpy(buffer + progress, &volume->cache[offset], chunk);
progress += chunk;
}
return true;
}
struct gpt_table_header {
// the head
char signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t crc32;
uint32_t _reserved0;
// the partitioning info
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
// the guid
struct guid disk_guid;
// entries related
uint64_t partition_entry_lba;
uint32_t number_of_partition_entries;
uint32_t size_of_partition_entry;
uint32_t partition_entry_array_crc32;
} __attribute__((packed));
struct gpt_entry {
struct guid partition_type_guid;
struct guid unique_partition_guid;
uint64_t starting_lba;
uint64_t ending_lba;
uint64_t attributes;
uint16_t partition_name[36];
} __attribute__((packed));
bool gpt_get_guid(struct guid *guid, struct volume *volume) {
struct gpt_table_header header = {0};
int lb_guesses[] = {
512,
4096
};
int lb_size = -1;
for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
// read header, located after the first block
volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header));
// check the header
// 'EFI PART'
if (strncmp(header.signature, "EFI PART", 8))
continue;
lb_size = lb_guesses[i];
break;
}
if (lb_size == -1) {
return false;
}
if (header.revision != 0x00010000)
return false;
*guid = header.disk_guid;
return true;
}
static int gpt_get_part(struct volume *ret, struct volume *volume, int partition) {
struct gpt_table_header header = {0};
int lb_guesses[] = {
512,
4096
};
int lb_size = -1;
for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
// read header, located after the first block
volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header));
// check the header
// 'EFI PART'
if (strncmp(header.signature, "EFI PART", 8))
continue;
lb_size = lb_guesses[i];
break;
}
if (lb_size == -1) {
return INVALID_TABLE;
}
if (header.revision != 0x00010000)
return INVALID_TABLE;
// parse the entries if reached here
if ((uint32_t)partition >= header.number_of_partition_entries)
return END_OF_TABLE;
struct gpt_entry entry = {0};
volume_read(volume, &entry,
(header.partition_entry_lba * lb_size) + (partition * sizeof(entry)),
sizeof(entry));
struct guid empty_guid = {0};
if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid)))
return NO_PARTITION;
#if defined (UEFI)
ret->efi_handle = volume->efi_handle;
ret->block_io = volume->block_io;
#elif defined (BIOS)
ret->drive = volume->drive;
#endif
ret->fastest_xfer_size = volume->fastest_xfer_size;
ret->index = volume->index;
ret->is_optical = volume->is_optical;
ret->partition = partition + 1;
ret->sector_size = volume->sector_size;
ret->first_sect = entry.starting_lba * (lb_size / 512);
ret->sect_count = ((entry.ending_lba - entry.starting_lba) + 1) * (lb_size / 512);
ret->backing_dev = volume;
struct guid guid;
if (!fs_get_guid(&guid, ret)) {
ret->guid_valid = false;
} else {
ret->guid_valid = true;
ret->guid = guid;
}
char *fslabel = fs_get_label(ret);
if (fslabel == NULL) {
ret->fslabel_valid = false;
} else {
ret->fslabel_valid = true;
ret->fslabel = fslabel;
}
ret->part_guid_valid = true;
ret->part_guid = entry.unique_partition_guid;
return 0;
}
struct mbr_entry {
uint8_t status;
uint8_t chs_first_sect[3];
uint8_t type;
uint8_t chs_last_sect[3];
uint32_t first_sect;
uint32_t sect_count;
} __attribute__((packed));
bool is_valid_mbr(struct volume *volume) {
// Check if actually valid mbr
uint16_t hint = 0;
volume_read(volume, &hint, 446, sizeof(uint8_t));
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
volume_read(volume, &hint, 462, sizeof(uint8_t));
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
volume_read(volume, &hint, 478, sizeof(uint8_t));
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
volume_read(volume, &hint, 494, sizeof(uint8_t));
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
char hintc[64];
volume_read(volume, hintc, 4, 8);
if (memcmp(hintc, "_ECH_FS_", 8) == 0)
return false;
volume_read(volume, hintc, 3, 4);
if (memcmp(hintc, "NTFS", 4) == 0)
return false;
volume_read(volume, hintc, 54, 3);
if (memcmp(hintc, "FAT", 3) == 0)
return false;
volume_read(volume, hintc, 82, 3);
if (memcmp(hintc, "FAT", 3) == 0)
return false;
volume_read(volume, hintc, 3, 5);
if (memcmp(hintc, "FAT32", 5) == 0)
return false;
volume_read(volume, &hint, 1080, sizeof(uint16_t));
if (hint == 0xef53)
return false;
return true;
}
uint32_t mbr_get_id(struct volume *volume) {
if (!is_valid_mbr(volume)) {
return 0;
}
uint32_t ret;
volume_read(volume, &ret, 0x1b8, sizeof(uint32_t));
return ret;
}
static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part,
int partition) {
struct mbr_entry entry;
uint64_t ebr_sector = 0;
for (int i = 0; i < partition; i++) {
uint64_t entry_offset = ebr_sector * 512 + 0x1ce;
volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry));
if (entry.type != 0x0f && entry.type != 0x05) {
return END_OF_TABLE;
}
ebr_sector = entry.first_sect;
}
uint64_t entry_offset = ebr_sector * 512 + 0x1be;
volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry));
if (entry.type == 0)
return NO_PARTITION;
#if defined (UEFI)
ret->efi_handle = extended_part->efi_handle;
ret->block_io = extended_part->block_io;
#elif defined (BIOS)
ret->drive = extended_part->drive;
#endif
ret->fastest_xfer_size = extended_part->fastest_xfer_size;
ret->index = extended_part->index;
ret->is_optical = extended_part->is_optical;
ret->partition = partition + 4 + 1;
ret->sector_size = extended_part->sector_size;
ret->first_sect = extended_part->first_sect + ebr_sector + entry.first_sect;
ret->sect_count = entry.sect_count;
ret->backing_dev = extended_part->backing_dev;
struct guid guid;
if (!fs_get_guid(&guid, ret)) {
ret->guid_valid = false;
} else {
ret->guid_valid = true;
ret->guid = guid;
}
char *fslabel = fs_get_label(ret);
if (fslabel == NULL) {
ret->fslabel_valid = false;
} else {
ret->fslabel_valid = true;
ret->fslabel = fslabel;
}
ret->part_guid_valid = false;
return 0;
}
static int mbr_get_part(struct volume *ret, struct volume *volume, int partition) {
if (!is_valid_mbr(volume)) {
return INVALID_TABLE;
}
struct mbr_entry entry;
if (partition > 3) {
for (int i = 0; i < 4; i++) {
uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * i;
volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry));
if (entry.type != 0x0f && entry.type != 0x05)
continue;
struct volume extended_part = {0};
#if defined (UEFI)
extended_part.efi_handle = volume->efi_handle;
extended_part.block_io = volume->block_io;
#elif defined (BIOS)
extended_part.drive = volume->drive;
#endif
extended_part.fastest_xfer_size = volume->fastest_xfer_size;
extended_part.index = volume->index;
extended_part.is_optical = volume->is_optical;
extended_part.partition = i + 1;
extended_part.sector_size = volume->sector_size;
extended_part.first_sect = entry.first_sect;
extended_part.sect_count = entry.sect_count;
extended_part.backing_dev = volume;
return mbr_get_logical_part(ret, &extended_part, partition - 4);
}
return END_OF_TABLE;
}
uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * partition;
volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry));
if (entry.type == 0)
return NO_PARTITION;
#if defined (UEFI)
ret->efi_handle = volume->efi_handle;
ret->block_io = volume->block_io;
#elif defined (BIOS)
ret->drive = volume->drive;
#endif
ret->fastest_xfer_size = volume->fastest_xfer_size;
ret->index = volume->index;
ret->is_optical = volume->is_optical;
ret->partition = partition + 1;
ret->sector_size = volume->sector_size;
ret->first_sect = entry.first_sect;
ret->sect_count = entry.sect_count;
ret->backing_dev = volume;
struct guid guid;
if (!fs_get_guid(&guid, ret)) {
ret->guid_valid = false;
} else {
ret->guid_valid = true;
ret->guid = guid;
}
char *fslabel = fs_get_label(ret);
if (fslabel == NULL) {
ret->fslabel_valid = false;
} else {
ret->fslabel_valid = true;
ret->fslabel = fslabel;
}
ret->part_guid_valid = false;
return 0;
}
int part_get(struct volume *part, struct volume *volume, int partition) {
int ret;
ret = gpt_get_part(part, volume, partition);
if (ret != INVALID_TABLE)
return ret;
ret = mbr_get_part(part, volume, partition);
if (ret != INVALID_TABLE)
return ret;
return INVALID_TABLE;
}
struct volume **volume_index = NULL;
size_t volume_index_i = 0;
struct volume *volume_get_by_guid(struct guid *guid) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->guid_valid
&& memcmp(&volume_index[i]->guid, guid, 16) == 0) {
return volume_index[i];
}
if (volume_index[i]->part_guid_valid
&& memcmp(&volume_index[i]->part_guid, guid, 16) == 0) {
return volume_index[i];
}
}
return NULL;
}
struct volume *volume_get_by_fslabel(char *fslabel) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->fslabel_valid
&& strcmp(volume_index[i]->fslabel, fslabel) == 0) {
return volume_index[i];
}
}
return NULL;
}
struct volume *volume_get_by_coord(bool optical, int drive, int partition) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->index == drive
&& volume_index[i]->is_optical == optical
&& volume_index[i]->partition == partition) {
return volume_index[i];
}
}
return NULL;
}
#if defined (BIOS)
struct volume *volume_get_by_bios_drive(int drive) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->drive == drive) {
return volume_index[i];
}
}
return NULL;
}
#endif

16
common/lib/print.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __LIB__PRINT_H__
#define __LIB__PRINT_H__
#include <stdarg.h>
#include <stdbool.h>
extern bool verbose;
void print(const char *fmt, ...);
void vprint(const char *fmt, va_list args);
#define printv(FMT, ...) do { \
if (verbose) print(FMT, ##__VA_ARGS__); \
} while (0)
#endif

257
common/lib/print.s2.c Normal file
View File

@ -0,0 +1,257 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <lib/print.h>
#include <lib/misc.h>
#include <lib/term.h>
#include <lib/libc.h>
#if defined (BIOS)
#include <lib/real.h>
#endif
#include <sys/cpu.h>
#include <drivers/serial.h>
#if defined (BIOS)
static void s2_print(const char *s, size_t len) {
for (size_t i = 0; i < len; i++) {
struct rm_regs r = {0};
char c = s[i];
switch (c) {
case '\n':
r.eax = 0x0e00 | '\r';
rm_int(0x10, &r, &r);
r = (struct rm_regs){0};
r.eax = 0x0e00 | '\n';
rm_int(0x10, &r, &r);
break;
default:
r.eax = 0x0e00 | s[i];
rm_int(0x10, &r, &r);
break;
}
}
}
#endif
static const char *base_digits = "0123456789abcdef";
#define PRINT_BUF_MAX 4096
static void prn_str(char *print_buf, size_t *print_buf_i, const char *string) {
size_t i;
for (i = 0; string[i]; i++) {
if (*print_buf_i == (PRINT_BUF_MAX - 1))
break;
print_buf[(*print_buf_i)++] = string[i];
}
print_buf[*print_buf_i] = 0;
}
static void prn_nstr(char *print_buf, size_t *print_buf_i, const char *string, size_t len) {
size_t i;
for (i = 0; i < len; i++) {
if (*print_buf_i == (PRINT_BUF_MAX - 1))
break;
print_buf[(*print_buf_i)++] = string[i];
}
print_buf[*print_buf_i] = 0;
}
static void prn_char(char *print_buf, size_t *print_buf_i, char c) {
if (*print_buf_i < (PRINT_BUF_MAX - 1)) {
print_buf[(*print_buf_i)++] = c;
}
print_buf[*print_buf_i] = 0;
}
static void prn_i(char *print_buf, size_t *print_buf_i, int64_t x) {
int i;
char buf[20] = {0};
if (!x) {
prn_char(print_buf, print_buf_i, '0');
return;
}
int sign = x < 0;
if (sign) x = -x;
for (i = 18; x; i--) {
buf[i] = (x % 10) + 0x30;
x = x / 10;
}
if (sign)
buf[i] = '-';
else
i++;
prn_str(print_buf, print_buf_i, buf + i);
}
static void prn_ui(char *print_buf, size_t *print_buf_i, uint64_t x) {
int i;
char buf[21] = {0};
if (!x) {
prn_char(print_buf, print_buf_i, '0');
return;
}
for (i = 19; x; i--) {
buf[i] = (x % 10) + 0x30;
x = x / 10;
}
i++;
prn_str(print_buf, print_buf_i, buf + i);
}
static void prn_x(char *print_buf, size_t *print_buf_i, uint64_t x) {
int i;
char buf[17] = {0};
if (!x) {
prn_str(print_buf, print_buf_i, "0x0");
return;
}
for (i = 15; x; i--) {
buf[i] = base_digits[(x % 16)];
x = x / 16;
}
i++;
prn_str(print_buf, print_buf_i, "0x");
prn_str(print_buf, print_buf_i, buf + i);
}
void print(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vprint(fmt, args);
va_end(args);
}
static char print_buf[PRINT_BUF_MAX];
void vprint(const char *fmt, va_list args) {
size_t print_buf_i = 0;
for (;;) {
while (*fmt && *fmt != '%')
prn_char(print_buf, &print_buf_i, *fmt++);
if (!*fmt++)
goto out;
switch (*fmt++) {
case 's': {
char *str = (char *)va_arg(args, const char *);
if (!str)
prn_str(print_buf, &print_buf_i, "(null)");
else
prn_str(print_buf, &print_buf_i, str); }
break;
case 'S': {
char *str = (char *)va_arg(args, const char *);
size_t str_len = va_arg(args, size_t);
if (!str)
prn_str(print_buf, &print_buf_i, "(null)");
else
prn_nstr(print_buf, &print_buf_i, str, str_len); }
break;
case 'd':
prn_i(print_buf, &print_buf_i, (int64_t)va_arg(args, int32_t));
break;
case 'u':
prn_ui(print_buf, &print_buf_i, (uint64_t)va_arg(args, uint32_t));
break;
case 'x':
prn_x(print_buf, &print_buf_i, (uint64_t)va_arg(args, uint32_t));
break;
case 'D':
prn_i(print_buf, &print_buf_i, va_arg(args, int64_t));
break;
case 'U':
prn_ui(print_buf, &print_buf_i, va_arg(args, uint64_t));
break;
case 'X':
prn_x(print_buf, &print_buf_i, va_arg(args, uint64_t));
break;
case 'p':
prn_x(print_buf, &print_buf_i, va_arg(args, uintptr_t));
break;
case 'c': {
char c = (char)va_arg(args, int);
prn_char(print_buf, &print_buf_i, c); }
break;
case '#': {
bool printed = false;
char *str = (char *)va_arg(args, const char *);
for (int i = (int)strlen(str) - 1; i >= 0; i--) {
if (str[i] != '#') {
continue;
}
prn_nstr(print_buf, &print_buf_i, str, i);
printed = true;
break;
}
if (!printed) {
prn_str(print_buf, &print_buf_i, str);
}
break;
}
default:
prn_char(print_buf, &print_buf_i, '?');
break;
}
}
out:
if (!quiet) {
#if defined (BIOS)
if (stage3_loaded) {
#endif
FOR_TERM(flanterm_write(TERM, print_buf, print_buf_i));
#if defined (BIOS)
} else {
s2_print(print_buf, print_buf_i);
}
#endif
}
for (size_t i = 0; i < print_buf_i; i++) {
#if defined (__x86_64__) || defined (__i386__)
if (E9_OUTPUT) {
outb(0xe9, print_buf[i]);
}
#endif
#if defined (BIOS)
if ((!quiet && serial) || COM_OUTPUT) {
switch (print_buf[i]) {
case '\n':
serial_out('\r');
serial_out('\n');
continue;
case '\e':
serial_out('\e');
continue;
}
if (!isprint(print_buf[i])) {
continue;
}
serial_out(print_buf[i]);
}
#endif
}
}

89
common/lib/rand.c Normal file
View File

@ -0,0 +1,89 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/misc.h>
#include <lib/print.h>
#include <lib/rand.h>
#include <sys/cpu.h>
#include <mm/pmm.h>
// TODO: Find where this mersenne twister implementation is inspired from
// and properly credit the original author(s).
static bool rand_initialised = false;
#define n ((int)624)
#define m ((int)397)
#define matrix_a ((uint32_t)0x9908b0df)
#define msb ((uint32_t)0x80000000)
#define lsbs ((uint32_t)0x7fffffff)
static uint32_t *status;
static int ctr;
static void init_rand(void) {
uint32_t seed = ((uint32_t)0xc597060c * (uint32_t)rdtsc())
* ((uint32_t)0xce86d624)
^ ((uint32_t)0xee0da130 * (uint32_t)rdtsc());
// TODO(qookie): aarch64 also has an optional HW random number generator
#if defined (__x86_64__) || defined(__i386__)
uint32_t eax, ebx, ecx, edx;
// Check for rdseed
if (cpuid(0x07, 0, &eax, &ebx, &ecx, &edx) && (ebx & (1 << 18))) {
seed *= (seed ^ rdseed(uint32_t));
} else if (cpuid(0x01, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 30))) {
seed *= (seed ^ rdrand(uint32_t));
}
#endif
status = ext_mem_alloc(n * sizeof(uint32_t));
srand(seed);
rand_initialised = true;
}
void srand(uint32_t s) {
status[0] = s;
for (ctr = 1; ctr < n; ctr++)
status[ctr] = (1812433253 * (status[ctr - 1] ^ (status[ctr - 1] >> 30)) + ctr);
}
uint32_t rand32(void) {
if (!rand_initialised)
init_rand();
const uint32_t mag01[2] = {0, matrix_a};
if (ctr >= n) {
for (int kk = 0; kk < n - m; kk++) {
uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs);
status[kk] = status[kk + m] ^ (y >> 1) ^ mag01[y & 1];
}
for (int kk = n - m; kk < n - 1; kk++) {
uint32_t y = (status[kk] & msb) | (status[kk + 1] & lsbs);
status[kk] = status[kk + (m - n)] ^ (y >> 1) ^ mag01[y & 1];
}
uint32_t y = (status[n - 1] & msb) | (status[0] & lsbs);
status[n - 1] = status[m - 1] ^ (y >> 1) ^ mag01[y & 1];
ctr = 0;
}
uint32_t res = status[ctr++];
res ^= (res >> 11);
res ^= (res << 7) & 0x9d2c5680;
res ^= (res << 15) & 0xefc60000;
res ^= (res >> 18);
return res;
}
uint64_t rand64(void) {
return (((uint64_t)rand32()) << 32) | (uint64_t)rand32();
}

11
common/lib/rand.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef __LIB__RAND_H__
#define __LIB__RAND_H__
#include <stdint.h>
void srand(uint32_t s);
uint32_t rand32(void);
uint64_t rand64(void);
#endif

479
common/lib/readline.c Normal file
View File

@ -0,0 +1,479 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/readline.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/term.h>
#include <lib/print.h>
#if defined (BIOS)
# include <lib/real.h>
#elif defined (UEFI)
# include <efi.h>
#endif
#include <drivers/serial.h>
#include <sys/cpu.h>
int getchar(void) {
for (;;) {
int ret = pit_sleep_and_quit_on_keypress(65535);
if (ret != 0) {
return ret;
}
}
}
int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) {
switch (scancode) {
#if defined (BIOS)
case 0x44:
return GETCHAR_F10;
case 0x4b:
return GETCHAR_CURSOR_LEFT;
case 0x4d:
return GETCHAR_CURSOR_RIGHT;
case 0x48:
return GETCHAR_CURSOR_UP;
case 0x50:
return GETCHAR_CURSOR_DOWN;
case 0x53:
return GETCHAR_DELETE;
case 0x4f:
return GETCHAR_END;
case 0x47:
return GETCHAR_HOME;
case 0x49:
return GETCHAR_PGUP;
case 0x51:
return GETCHAR_PGDOWN;
case 0x01:
return GETCHAR_ESCAPE;
#elif defined (UEFI)
case SCAN_F10:
return GETCHAR_F10;
case SCAN_LEFT:
return GETCHAR_CURSOR_LEFT;
case SCAN_RIGHT:
return GETCHAR_CURSOR_RIGHT;
case SCAN_UP:
return GETCHAR_CURSOR_UP;
case SCAN_DOWN:
return GETCHAR_CURSOR_DOWN;
case SCAN_DELETE:
return GETCHAR_DELETE;
case SCAN_END:
return GETCHAR_END;
case SCAN_HOME:
return GETCHAR_HOME;
case SCAN_PAGE_UP:
return GETCHAR_PGUP;
case SCAN_PAGE_DOWN:
return GETCHAR_PGDOWN;
case SCAN_ESC:
return GETCHAR_ESCAPE;
#endif
}
switch (ascii) {
case '\r':
return '\n';
case '\b':
return '\b';
}
if (shift_state & (GETCHAR_LCTRL | GETCHAR_RCTRL)) {
switch (ascii) {
case 'a': return GETCHAR_HOME;
case 'e': return GETCHAR_END;
case 'p': return GETCHAR_CURSOR_UP;
case 'n': return GETCHAR_CURSOR_DOWN;
case 'b': return GETCHAR_CURSOR_LEFT;
case 'f': return GETCHAR_CURSOR_RIGHT;
default: break;
}
}
// Guard against non-printable values
if (ascii < 0x20 || ascii > 0x7e) {
return -1;
}
return ascii;
}
#if defined (BIOS)
int _pit_sleep_and_quit_on_keypress(uint32_t ticks);
static int input_sequence(void) {
int val = 0;
for (;;) {
int ret = -1;
size_t retries = 0;
while (ret == -1 && retries < 1000000) {
ret = serial_in();
retries++;
}
if (ret == -1) {
return 0;
}
switch (ret) {
case 'A':
return GETCHAR_CURSOR_UP;
case 'B':
return GETCHAR_CURSOR_DOWN;
case 'C':
return GETCHAR_CURSOR_RIGHT;
case 'D':
return GETCHAR_CURSOR_LEFT;
case 'F':
return GETCHAR_END;
case 'H':
return GETCHAR_HOME;
}
if (ret > '9' || ret < '0') {
break;
}
val *= 10;
val += ret - '0';
}
switch (val) {
case 3:
return GETCHAR_DELETE;
case 5:
return GETCHAR_PGUP;
case 6:
return GETCHAR_PGDOWN;
case 21:
return GETCHAR_F10;
}
return 0;
}
int pit_sleep_and_quit_on_keypress(int seconds) {
if (!serial) {
return _pit_sleep_and_quit_on_keypress(seconds * 18);
}
for (int i = 0; i < seconds * 18; i++) {
int ret = _pit_sleep_and_quit_on_keypress(1);
if (ret != 0) {
return ret;
}
ret = serial_in();
if (ret != -1) {
again:
switch (ret) {
case '\r':
return '\n';
case 0x1b:
delay(10000);
ret = serial_in();
if (ret == -1) {
return GETCHAR_ESCAPE;
}
if (ret == '[') {
return input_sequence();
}
goto again;
case 0x7f:
return '\b';
}
return ret;
}
}
return 0;
}
#endif
#if defined (UEFI)
static int input_sequence(bool ext,
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto,
EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto) {
EFI_STATUS status;
EFI_KEY_DATA kd;
int val = 0;
for (;;) {
if (ext == false) {
status = sproto->ReadKeyStroke(sproto, &kd.Key);
} else {
status = exproto->ReadKeyStrokeEx(exproto, &kd);
}
if (status != EFI_SUCCESS) {
return 0;
}
switch (kd.Key.UnicodeChar) {
case 'A':
return GETCHAR_CURSOR_UP;
case 'B':
return GETCHAR_CURSOR_DOWN;
case 'C':
return GETCHAR_CURSOR_RIGHT;
case 'D':
return GETCHAR_CURSOR_LEFT;
case 'F':
return GETCHAR_END;
case 'H':
return GETCHAR_HOME;
}
if (kd.Key.UnicodeChar > '9' || kd.Key.UnicodeChar < '0') {
break;
}
val *= 10;
val += kd.Key.UnicodeChar - '0';
}
switch (val) {
case 3:
return GETCHAR_DELETE;
case 5:
return GETCHAR_PGUP;
case 6:
return GETCHAR_PGDOWN;
case 21:
return GETCHAR_F10;
}
return 0;
}
int pit_sleep_and_quit_on_keypress(int seconds) {
EFI_KEY_DATA kd;
UINTN which;
EFI_EVENT events[2];
EFI_GUID exproto_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
EFI_GUID sproto_guid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto = NULL;
EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto = NULL;
bool use_sproto = false;
if (gBS->HandleProtocol(gST->ConsoleInHandle, &exproto_guid, (void **)&exproto) != EFI_SUCCESS) {
if (gBS->HandleProtocol(gST->ConsoleInHandle, &sproto_guid, (void **)&sproto) != EFI_SUCCESS) {
if (gST->ConIn != NULL) {
sproto = gST->ConIn;
} else {
panic(false, "Your input device doesn't have an input protocol!");
}
}
events[0] = sproto->WaitForKey;
use_sproto = true;
} else {
events[0] = exproto->WaitForKeyEx;
}
gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
gBS->SetTimer(events[1], TimerRelative, 10000000 * seconds);
again:
memset(&kd, 0, sizeof(EFI_KEY_DATA));
gBS->WaitForEvent(2, events, &which);
if (which == 1) {
return 0;
}
EFI_STATUS status;
if (use_sproto) {
status = sproto->ReadKeyStroke(sproto, &kd.Key);
} else {
status = exproto->ReadKeyStrokeEx(exproto, &kd);
}
if (status != EFI_SUCCESS) {
goto again;
}
if ((kd.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) {
kd.KeyState.KeyShiftState = 0;
}
if (kd.Key.ScanCode == 0x08) {
return '\b';
}
if (kd.Key.ScanCode == SCAN_ESC) {
gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
gBS->SetTimer(events[1], TimerRelative, 100000);
gBS->WaitForEvent(2, events, &which);
if (which == 1) {
return GETCHAR_ESCAPE;
}
if (use_sproto) {
status = sproto->ReadKeyStroke(sproto, &kd.Key);
} else {
status = exproto->ReadKeyStrokeEx(exproto, &kd);
}
if (status != EFI_SUCCESS) {
goto again;
}
if (kd.Key.UnicodeChar == '[') {
return input_sequence(!use_sproto, exproto, sproto);
}
}
int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar,
kd.KeyState.KeyShiftState);
if (ret == -1) {
goto again;
}
return ret;
}
#endif
static void reprint_string(int x, int y, const char *s) {
size_t orig_x, orig_y;
FOR_TERM(TERM->cursor_enabled = false);
terms[0]->get_cursor_pos(terms[0], &orig_x, &orig_y);
set_cursor_pos_helper(x, y);
print("%s", s);
set_cursor_pos_helper(orig_x, orig_y);
FOR_TERM(TERM->cursor_enabled = true);
}
static void cursor_back(void) {
size_t x, y;
terms[0]->get_cursor_pos(terms[0], &x, &y);
if (x) {
x--;
} else if (y) {
y--;
x = terms[0]->cols - 1;
}
set_cursor_pos_helper(x, y);
}
static void cursor_fwd(void) {
size_t x, y;
terms[0]->get_cursor_pos(terms[0], &x, &y);
if (x < terms[0]->cols - 1) {
x++;
} else {
x = 0;
if (y < terms[0]->rows - 1) {
y++;
}
}
set_cursor_pos_helper(x, y);
}
void readline(const char *orig_str, char *buf, size_t limit) {
bool prev_autoflush = terms[0]->autoflush;
FOR_TERM(TERM->autoflush = false);
size_t orig_str_len = strlen(orig_str);
memmove(buf, orig_str, orig_str_len);
buf[orig_str_len] = 0;
size_t orig_x, orig_y;
terms[0]->get_cursor_pos(terms[0], &orig_x, &orig_y);
print("%s", orig_str);
for (size_t i = orig_str_len; ; ) {
FOR_TERM(TERM->double_buffer_flush(TERM));
int c = getchar();
switch (c) {
case GETCHAR_CURSOR_LEFT:
if (i) {
i--;
cursor_back();
}
continue;
case GETCHAR_CURSOR_RIGHT:
if (i < strlen(buf)) {
i++;
cursor_fwd();
}
continue;
case '\b':
if (i) {
i--;
cursor_back();
case GETCHAR_DELETE:;
size_t j;
if (buf[i] == 0) {
continue;
}
for (j = i; ; j++) {
buf[j] = buf[j+1];
if (!buf[j]) {
buf[j] = ' ';
break;
}
}
reprint_string(orig_x, orig_y, buf);
buf[j] = 0;
}
continue;
case '\n':
print("\n");
goto out;
case GETCHAR_END:
for (size_t j = 0; j < strlen(buf) - i; j++) {
cursor_fwd();
}
i = strlen(buf);
continue;
case GETCHAR_HOME:
for (size_t j = 0; j < i; j++) {
cursor_back();
}
i = 0;
continue;
default: {
if (strlen(buf) < limit - 1 && isprint(c)) {
for (size_t j = strlen(buf); ; j--) {
buf[j+1] = buf[j];
if (j == i)
break;
}
buf[i] = c;
i++;
size_t prev_x, prev_y;
terms[0]->get_cursor_pos(terms[0], &prev_x, &prev_y);
cursor_fwd();
reprint_string(orig_x, orig_y, buf);
// If cursor has wrapped around, move the line start position up one row
if (prev_x == terms[0]->cols - 1 && prev_y == terms[0]->rows - 1) {
orig_y--;
print("\n\e[J"); // Clear the bottom line
}
}
}
}
}
out:
FOR_TERM(TERM->double_buffer_flush(TERM));
FOR_TERM(TERM->autoflush = prev_autoflush);
}

29
common/lib/readline.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __LIB__READLINE_H__
#define __LIB__READLINE_H__
#include <stddef.h>
#define GETCHAR_CURSOR_LEFT (-10)
#define GETCHAR_CURSOR_RIGHT (-11)
#define GETCHAR_CURSOR_UP (-12)
#define GETCHAR_CURSOR_DOWN (-13)
#define GETCHAR_DELETE (-14)
#define GETCHAR_END (-15)
#define GETCHAR_HOME (-16)
#define GETCHAR_PGUP (-17)
#define GETCHAR_PGDOWN (-18)
#define GETCHAR_F10 (-19)
#define GETCHAR_ESCAPE (-20)
#if defined (BIOS)
# define GETCHAR_RCTRL 0x4
# define GETCHAR_LCTRL GETCHAR_RCTRL
#elif defined (UEFI)
# define GETCHAR_RCTRL EFI_RIGHT_CONTROL_PRESSED
# define GETCHAR_LCTRL EFI_LEFT_CONTROL_PRESSED
#endif
int getchar(void);
void readline(const char *orig_str, char *buf, size_t limit);
#endif

34
common/lib/real.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __LIB__REAL_H__
#define __LIB__REAL_H__
#include <stdint.h>
#include <stdnoreturn.h>
#define rm_seg(x) ((uint16_t)(((int)x & 0xffff0) >> 4))
#define rm_off(x) ((uint16_t)(((int)x & 0x0000f) >> 0))
#define rm_desegment(seg, off) (((uint32_t)(seg) << 4) + (uint32_t)(off))
#define EFLAGS_CF (1 << 0)
#define EFLAGS_ZF (1 << 6)
struct rm_regs {
uint16_t gs;
uint16_t fs;
uint16_t es;
uint16_t ds;
uint32_t eflags;
uint32_t ebp;
uint32_t edi;
uint32_t esi;
uint32_t edx;
uint32_t ecx;
uint32_t ebx;
uint32_t eax;
} __attribute__((packed));
void rm_int(uint8_t int_no, struct rm_regs *out_regs, struct rm_regs *in_regs);
noreturn void rm_hcf(void);
#endif

View File

@ -0,0 +1,168 @@
section .realmode
global rm_hcf
rm_hcf:
; Load BIOS IVT
lidt [.rm_idt]
; Jump to real mode
jmp 0x08:.bits16
.bits16:
bits 16
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
btr ax, 0
mov cr0, eax
jmp 0x00:.cszero
.cszero:
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
sti
.hang:
hlt
jmp .hang
bits 32
.rm_idt: dw 0x3ff
dd 0
global rm_int
rm_int:
; Self-modifying code: int $int_no
mov al, byte [esp+4]
mov byte [.int_no], al
; Save out_regs
mov eax, dword [esp+8]
mov dword [.out_regs], eax
; Save in_regs
mov eax, dword [esp+12]
mov dword [.in_regs], eax
; Save GDT in case BIOS overwrites it
sgdt [.gdt]
; Save IDT
sidt [.idt]
; Load BIOS IVT
lidt [.rm_idt]
; Save non-scratch GPRs
push ebx
push esi
push edi
push ebp
; Jump to real mode
jmp 0x08:.bits16
.bits16:
bits 16
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 0xfe
mov cr0, eax
jmp 0x00:.cszero
.cszero:
xor ax, ax
mov ss, ax
; Load in_regs
mov dword [ss:.esp], esp
mov esp, dword [ss:.in_regs]
pop gs
pop fs
pop es
pop ds
popfd
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
mov esp, dword [ss:.esp]
sti
; Indirect interrupt call
db 0xcd
.int_no:
db 0
cli
; Load out_regs
mov dword [ss:.esp], esp
mov esp, dword [ss:.out_regs]
lea esp, [esp + 10*4]
push eax
push ebx
push ecx
push edx
push esi
push edi
push ebp
pushfd
push ds
push es
push fs
push gs
mov esp, dword [ss:.esp]
; Restore GDT
o32 lgdt [ss:.gdt]
; Restore IDT
o32 lidt [ss:.idt]
; Jump back to pmode
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x18:.bits32
.bits32:
bits 32
mov ax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Restore non-scratch GPRs
pop ebp
pop edi
pop esi
pop ebx
; Exit
ret
align 16
.esp: dd 0
.out_regs: dd 0
.in_regs: dd 0
.gdt: dq 0
.idt: dq 0
.rm_idt: dw 0x3ff
dd 0
section .note.GNU-stack noalloc noexec nowrite progbits

View File

@ -0,0 +1,157 @@
section .realmode
int_08_ticks_counter: dd 0
int_08_callback: dd 0
int_08_isr:
bits 16
pushf
inc dword [cs:int_08_ticks_counter]
popf
jmp far [cs:int_08_callback]
bits 32
extern getchar_internal
global _pit_sleep_and_quit_on_keypress
_pit_sleep_and_quit_on_keypress:
; Hook int 0x08
mov edx, dword [0x08*4]
mov dword [int_08_callback], edx
mov dword [0x08*4], int_08_isr
; pit_ticks in edx
mov edx, dword [esp+4]
mov dword [int_08_ticks_counter], 0
; Save GDT in case BIOS overwrites it
sgdt [.gdt]
; Save IDT
sidt [.idt]
; Load BIOS IVT
lidt [.rm_idt]
; Save non-scratch GPRs
push ebx
push esi
push edi
push ebp
; Jump to real mode
jmp 0x08:.bits16
.bits16:
bits 16
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 0xfe
mov cr0, eax
jmp 0x00:.cszero
.cszero:
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
sti
mov byte [.mods], 0
mov byte [.ascii], 0
mov byte [.scan], 0
.loop:
cmp dword [int_08_ticks_counter], edx
je .done
push ecx
push edx
mov ah, 0x01
xor al, al
int 0x16
pop edx
pop ecx
jz .loop
; on keypress
xor ax, ax
int 0x16
mov byte [.ascii], al
mov byte [.scan], ah
mov ax, 0x0200
int 0x16
test al, 0x04
jz .done
; ctrl handling
mov byte [.mods], 0x04
add byte [.ascii], 0x60
.done:
cli
; Restore GDT
o32 lgdt [ss:.gdt]
; Restore IDT
o32 lidt [ss:.idt]
; Jump back to pmode
mov ebx, cr0
or bl, 1
mov cr0, ebx
jmp 0x18:.bits32
.bits32:
bits 32
mov bx, 0x20
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
mov ss, bx
; Restore non-scratch GPRs
pop ebp
pop edi
pop esi
pop ebx
; Dehook int 0x08
mov edx, dword [int_08_callback]
mov dword [0x08*4], edx
cmp byte [.scan], 0
je .fail
push dword [.mods]
push dword [.ascii]
push dword [.scan]
call getchar_internal
add esp, 3*4
ret
.fail:
xor eax, eax
ret
.gdt: dq 0
.idt: dq 0
.rm_idt: dw 0x3ff
dd 0
.mods: dd 0
.ascii: dd 0
.scan: dd 0
section .note.GNU-stack noalloc noexec nowrite progbits

View File

@ -0,0 +1,65 @@
#include <lib/macros.aarch64_asm.h>
.section .text
// noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr,
// uint64_t mair, uint64_t tcr, uint64_t ttbr0,
// uint64_t ttbr1, uint64_t target_x0)
// Potentially drop to EL1 from EL2 (and also disable trapping to EL2), then
// configure EL1 state and jump to kernel.
.global enter_in_el1
enter_in_el1:
msr spsel, #0
mov sp, x1
// Configure EL1 state
msr mair_el1, x3
msr tcr_el1, x4
msr ttbr0_el1, x5
msr ttbr1_el1, x6
msr sctlr_el1, x2
dsb sy
isb
PICK_EL x8, 0f, 1f
0:
// Enter kernel in EL1
mov x8, #0x3c4
msr spsr_el1, x8
msr elr_el1, x0
mov x0, x7
ZERO_REGS_EXCEPT_X0
eret
1:
// Configure EL2-specific state for EL1
// Don't trap counters to EL2
mrs x8, cnthctl_el2
orr x8, x8, #3
msr cnthctl_el2, x8
msr cntvoff_el2, xzr
// Enable AArch64 in EL1
ldr x8, =0x80000002
msr hcr_el2, x8
// Don't trap FP/SIMD to EL2
mov x8, #0x33FF
msr cptr_el2, x8
msr hstr_el2, xzr
// Enter kernel in EL1
mov x8, #0x3c4
msr spsr_el2, x8
msr elr_el2, x0
mov x0, x7
ZERO_REGS_EXCEPT_X0
eret
.section .note.GNU-stack,"",%progbits

View File

@ -0,0 +1,39 @@
section .rodata
invalid_idt:
dd 0, 0
section .text
extern flush_irqs
global common_spinup
bits 32
common_spinup:
cli
lidt [invalid_idt]
call flush_irqs
xor eax, eax
lldt ax
; We don't need the return address
add esp, 4
; Get function address
pop edi
; We don't need the argument count
add esp, 4
mov eax, 0x00000011
mov cr0, eax
xor eax, eax
mov cr4, eax
call edi
section .note.GNU-stack noalloc noexec nowrite progbits

View File

@ -0,0 +1,47 @@
.section .text
.global riscv_spinup
riscv_spinup:
.option norelax
csrci sstatus, 0x2
csrw sie, zero
csrw stvec, zero
mv t0, a0
mv sp, a1
csrw satp, a2
mv a0, zero
mv a1, zero
mv a2, zero
mv a3, zero
mv a4, zero
mv a5, zero
mv a6, zero
mv a7, zero
mv s0, zero
mv s1, zero
mv s2, zero
mv s3, zero
mv s4, zero
mv s5, zero
mv s6, zero
mv s7, zero
mv s8, zero
mv s9, zero
mv s10, zero
mv s11, zero
mv t1, zero
mv t2, zero
mv t3, zero
mv t4, zero
mv t5, zero
mv t6, zero
mv tp, zero
mv gp, zero
mv ra, zero
jr t0
.section .note.GNU-stack,"",%progbits

View File

@ -0,0 +1,64 @@
extern _GLOBAL_OFFSET_TABLE_
extern gdt
section .text
extern flush_irqs
global common_spinup
bits 32
common_spinup:
cli
push 0
push 0
lidt [esp]
add esp, 8
call .get_got
.get_got:
pop ebx
add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc
lgdt [ebx + gdt wrt ..gotoff]
push dword 0x18
call .p1
.p1:
pop eax
add eax, 6
push eax
retfd
.flush_cs:
mov eax, 0x20
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
call flush_irqs
xor eax, eax
lldt ax
; We don't need the return address
add esp, 4
; Get function address
pop edi
; We don't need the argument count
add esp, 4
mov eax, 0x00000011
mov cr0, eax
xor eax, eax
mov cr4, eax
call edi
section .note.GNU-stack noalloc noexec nowrite progbits

View File

@ -0,0 +1,97 @@
section .rodata
invalid_idt:
dq 0, 0
section .text
extern flush_irqs
%macro push32 1
sub rsp, 4
mov dword [rsp], %1
%endmacro
extern gdt
global common_spinup
bits 64
common_spinup:
cli
lgdt [rel gdt]
lidt [rel invalid_idt]
lea rbx, [rel .reload_cs]
push 0x28
push rbx
retfq
.reload_cs:
mov eax, 0x30
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
push r8
push r9
push rcx
push rdx
call flush_irqs
pop rdx
pop rcx
pop r9
pop r8
mov rbp, rsp
cmp esi, 4
jle .no_stack_args
.push_stack_args:
dec esi
mov eax, [rbp + 8 + rsi*8]
push32 eax
test esi, esi
jnz .push_stack_args
.no_stack_args:
push32 r9d
push32 r8d
push32 ecx
push32 edx
lea rbx, [rel .go_32]
push 0x18
push rbx
retfq
bits 32
.go_32:
mov eax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
xor eax, eax
lldt ax
mov eax, 0x00000011
mov cr0, eax
mov ecx, 0xc0000080
xor eax, eax
xor edx, edx
wrmsr
xor eax, eax
mov cr4, eax
call edi
section .note.GNU-stack noalloc noexec nowrite progbits

387
common/lib/term.c Normal file
View File

@ -0,0 +1,387 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/term.h>
#include <lib/real.h>
#include <lib/misc.h>
#include <mm/pmm.h>
#include <drivers/vga_textmode.h>
#include <flanterm/backends/fb.h>
#if defined (BIOS)
int current_video_mode = -1;
#endif
struct flanterm_context **terms = NULL;
size_t terms_i = 0;
int term_backend = _NOT_READY;
void term_notready(void) {
for (size_t i = 0; i < terms_i; i++) {
struct flanterm_context *term = terms[i];
term->deinit(term, pmm_free);
}
pmm_free(terms, terms_i * sizeof(void *));
terms_i = 0;
terms = NULL;
term_backend = _NOT_READY;
}
// --- fallback ---
#if defined (BIOS)
static void fallback_raw_putchar(struct flanterm_context *ctx, uint8_t c) {
(void)ctx;
struct rm_regs r = {0};
r.eax = 0x0e00 | c;
rm_int(0x10, &r, &r);
}
static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y);
static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y);
static void fallback_clear(struct flanterm_context *ctx, bool move) {
(void)ctx;
size_t x, y;
fallback_get_cursor_pos(NULL, &x, &y);
struct rm_regs r = {0};
rm_int(0x11, &r, &r);
switch ((r.eax >> 4) & 3) {
case 0:
r.eax = 3;
break;
case 1:
r.eax = 1;
break;
case 2:
r.eax = 3;
break;
case 3:
r.eax = 7;
break;
}
rm_int(0x10, &r, &r);
if (move) {
x = y = 0;
}
fallback_set_cursor_pos(NULL, x, y);
}
static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) {
(void)ctx;
struct rm_regs r = {0};
r.eax = 0x0200;
r.ebx = 0;
r.edx = (y << 8) + x;
rm_int(0x10, &r, &r);
}
static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) {
(void)ctx;
struct rm_regs r = {0};
r.eax = 0x0300;
r.ebx = 0;
rm_int(0x10, &r, &r);
*x = r.edx & 0xff;
*y = r.edx >> 8;
}
static void fallback_scroll(struct flanterm_context *ctx) {
(void)ctx;
size_t x, y;
fallback_get_cursor_pos(NULL, &x, &y);
fallback_set_cursor_pos(NULL, ctx->cols - 1, ctx->rows - 1);
fallback_raw_putchar(NULL, ' ');
fallback_set_cursor_pos(NULL, x, y);
}
#elif defined (UEFI)
static size_t cursor_x = 0, cursor_y = 0;
static void fallback_scroll(struct flanterm_context *ctx) {
(void)ctx;
gST->ConOut->SetCursorPosition(gST->ConOut, ctx->cols - 1, ctx->rows - 1);
CHAR16 string[2];
string[0] = ' ';
string[1] = 0;
gST->ConOut->OutputString(gST->ConOut, string);
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
}
static void fallback_raw_putchar(struct flanterm_context *ctx, uint8_t c) {
if (!ctx->scroll_enabled && cursor_x == ctx->cols - 1 && cursor_y == ctx->rows - 1) {
return;
}
gST->ConOut->EnableCursor(gST->ConOut, true);
CHAR16 string[2];
string[0] = c;
string[1] = 0;
gST->ConOut->OutputString(gST->ConOut, string);
if (++cursor_x >= ctx->cols) {
cursor_x = 0;
if (++cursor_y >= ctx->rows) {
cursor_y--;
}
}
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
}
static void fallback_clear(struct flanterm_context *ctx, bool move) {
(void)ctx;
gST->ConOut->ClearScreen(gST->ConOut);
if (move) {
cursor_x = cursor_y = 0;
}
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
}
static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) {
(void)ctx;
if (x >= ctx->cols || y >= ctx->rows) {
return;
}
gST->ConOut->SetCursorPosition(gST->ConOut, x, y);
cursor_x = x;
cursor_y = y;
}
static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) {
(void)ctx;
*x = cursor_x;
*y = cursor_y;
}
#endif
static bool dummy_handle(void) {
return true;
}
void term_fallback(void) {
term_notready();
#if defined (UEFI)
if (!efi_boot_services_exited) {
#endif
terms = ext_mem_alloc(sizeof(void *));
terms_i = 1;
terms[0] = ext_mem_alloc(sizeof(struct flanterm_context));
struct flanterm_context *term = terms[0];
fallback_clear(NULL, true);
term->set_text_fg = (void *)dummy_handle;
term->set_text_bg = (void *)dummy_handle;
term->set_text_fg_bright = (void *)dummy_handle;
term->set_text_bg_bright = (void *)dummy_handle;
term->set_text_fg_rgb = (void *)dummy_handle;
term->set_text_bg_rgb = (void *)dummy_handle;
term->set_text_fg_default = (void *)dummy_handle;
term->set_text_bg_default = (void *)dummy_handle;
term->move_character = (void *)dummy_handle;
term->revscroll = (void *)dummy_handle;
term->swap_palette = (void *)dummy_handle;
term->save_state = (void *)dummy_handle;
term->restore_state = (void *)dummy_handle;
term->double_buffer_flush = (void *)dummy_handle;
term->full_refresh = (void *)dummy_handle;
term->deinit = (void *)dummy_handle;
term->raw_putchar = fallback_raw_putchar;
term->clear = fallback_clear;
term->set_cursor_pos = fallback_set_cursor_pos;
term->get_cursor_pos = fallback_get_cursor_pos;
term->scroll = fallback_scroll;
#if defined (UEFI)
UINTN uefi_term_x_size, uefi_term_y_size;
gST->ConOut->QueryMode(gST->ConOut, gST->ConOut->Mode->Mode, &uefi_term_x_size, &uefi_term_y_size);
term->cols = uefi_term_x_size;
term->rows = uefi_term_y_size;
#elif defined (BIOS)
term->cols = 80;
term->rows = 25;
#endif
term_backend = FALLBACK;
flanterm_context_reinit(term);
#if defined (UEFI)
}
#endif
}
extern void reset_term(void);
extern void set_cursor_pos_helper(size_t x, size_t y);
#if defined (__i386__)
#define TERM_XFER_CHUNK 8192
static uint8_t xfer_buf[TERM_XFER_CHUNK];
#endif
static uint64_t context_size(struct flanterm_context *term) {
switch (term_backend) {
#if defined (BIOS)
case TEXTMODE:
return sizeof(struct textmode_context) + (VD_ROWS * VD_COLS) * 2;
#endif
case GTERM: {
struct flanterm_fb_context *ctx = (void *)term;
return sizeof(struct flanterm_fb_context) +
ctx->font_bits_size +
ctx->font_bool_size +
ctx->canvas_size +
ctx->grid_size +
ctx->queue_size +
ctx->map_size;
}
default:
return 0;
}
}
static void context_save(struct flanterm_context *term, uint64_t buf) {
switch (term_backend) {
#if defined (BIOS)
case TEXTMODE: {
struct textmode_context *ctx = (void *)term;
memcpy32to64(buf, (uintptr_t)ctx, sizeof(struct textmode_context));
buf += sizeof(struct textmode_context);
memcpy32to64(buf, (uintptr_t)ctx->back_buffer, VD_ROWS * VD_COLS);
buf += VD_ROWS * VD_COLS;
memcpy32to64(buf, (uintptr_t)ctx->front_buffer, VD_ROWS * VD_COLS);
buf += VD_ROWS * VD_COLS;
break;
}
#endif
case GTERM: {
struct flanterm_fb_context *ctx = (void *)term;
memcpy32to64(buf, (uintptr_t)ctx, sizeof(struct flanterm_fb_context));
buf += sizeof(struct flanterm_fb_context);
memcpy32to64(buf, (uintptr_t)ctx->font_bits, ctx->font_bits_size);
buf += ctx->font_bits_size;
memcpy32to64(buf, (uintptr_t)ctx->font_bool, ctx->font_bool_size);
buf += ctx->font_bool_size;
memcpy32to64(buf, (uintptr_t)ctx->canvas, ctx->canvas_size);
buf += ctx->canvas_size;
memcpy32to64(buf, (uintptr_t)ctx->grid, ctx->grid_size);
buf += ctx->grid_size;
memcpy32to64(buf, (uintptr_t)ctx->queue, ctx->queue_size);
buf += ctx->queue_size;
memcpy32to64(buf, (uintptr_t)ctx->map, ctx->map_size);
buf += ctx->map_size;
break;
}
}
}
static void context_restore(struct flanterm_context *term, uint64_t buf) {
switch (term_backend) {
#if defined (BIOS)
case TEXTMODE: {
struct textmode_context *ctx = (void *)term;
memcpy32to64((uintptr_t)ctx, buf, sizeof(struct textmode_context));
buf += sizeof(struct textmode_context);
memcpy32to64((uintptr_t)ctx->back_buffer, buf, VD_ROWS * VD_COLS);
buf += VD_ROWS * VD_COLS;
memcpy32to64((uintptr_t)ctx->front_buffer, buf, VD_ROWS * VD_COLS);
buf += VD_ROWS * VD_COLS;
break;
}
#endif
case GTERM: {
struct flanterm_fb_context *ctx = (void *)term;
memcpy32to64((uintptr_t)ctx, buf, sizeof(struct flanterm_fb_context));
buf += sizeof(struct flanterm_fb_context);
memcpy32to64((uintptr_t)ctx->font_bits, buf, ctx->font_bits_size);
buf += ctx->font_bits_size;
memcpy32to64((uintptr_t)ctx->font_bool, buf, ctx->font_bool_size);
buf += ctx->font_bool_size;
memcpy32to64((uintptr_t)ctx->canvas, buf, ctx->canvas_size);
buf += ctx->canvas_size;
memcpy32to64((uintptr_t)ctx->grid, buf, ctx->grid_size);
buf += ctx->grid_size;
memcpy32to64((uintptr_t)ctx->queue, buf, ctx->queue_size);
buf += ctx->queue_size;
memcpy32to64((uintptr_t)ctx->map, buf, ctx->map_size);
buf += ctx->map_size;
break;
}
}
}
void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count) {
switch (count) {
case TERM_OOB_OUTPUT_GET: {
memcpy32to64(buf, (uint64_t)(uintptr_t)&term->oob_output, sizeof(uint64_t));
return;
}
case TERM_OOB_OUTPUT_SET: {
memcpy32to64((uint64_t)(uintptr_t)&term->oob_output, buf, sizeof(uint64_t));
return;
}
case TERM_CTX_SIZE: {
uint64_t ret = context_size(term);
memcpy32to64(buf, (uint64_t)(uintptr_t)&ret, sizeof(uint64_t));
return;
}
case TERM_CTX_SAVE: {
context_save(term, buf);
return;
}
case TERM_CTX_RESTORE: {
context_restore(term, buf);
return;
}
case TERM_FULL_REFRESH: {
term->full_refresh(term);
return;
}
}
bool native = false;
#if defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
native = true;
#elif !defined (__i386__)
#error Unknown architecture
#endif
bool autoflush = term->autoflush;
term->autoflush = false;
if (native) {
const char *s = (const char *)(uintptr_t)buf;
flanterm_write(term, s, count);
} else {
#if defined (__i386__)
while (count != 0) {
uint64_t chunk;
if (count > TERM_XFER_CHUNK) {
chunk = TERM_XFER_CHUNK;
} else {
chunk = count;
}
memcpy32to64((uint64_t)(uintptr_t)xfer_buf, buf, chunk);
flanterm_write(term, (const char *)xfer_buf, chunk);
count -= chunk;
buf += chunk;
}
#endif
}
if (autoflush) {
term->double_buffer_flush(term);
}
term->autoflush = autoflush;
}

59
common/lib/term.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef __LIB__TERM_H__
#define __LIB__TERM_H__
#include <stddef.h>
#include <stdint.h>
#include <lib/print.h>
#include <flanterm/flanterm.h>
enum {
_NOT_READY,
GTERM,
TEXTMODE,
FALLBACK
};
#if defined (BIOS)
extern int current_video_mode;
#endif
extern struct flanterm_context **terms;
extern size_t terms_i;
extern int term_backend;
#define TERM_CTX_SIZE ((uint64_t)(-1))
#define TERM_CTX_SAVE ((uint64_t)(-2))
#define TERM_CTX_RESTORE ((uint64_t)(-3))
#define TERM_FULL_REFRESH ((uint64_t)(-4))
#define TERM_OOB_OUTPUT_GET ((uint64_t)(-10))
#define TERM_OOB_OUTPUT_SET ((uint64_t)(-11))
#define FOR_TERM(...) do { \
for (size_t FOR_TERM_i = 0; FOR_TERM_i < terms_i; FOR_TERM_i++) { \
struct flanterm_context *TERM = terms[FOR_TERM_i]; \
__VA_ARGS__ \
; \
} \
} while (0)
inline void reset_term(void) {
for (size_t i = 0; i < terms_i; i++) {
struct flanterm_context *term = terms[i];
print("\e[2J\e[H");
flanterm_context_reinit(term);
term->cursor_enabled = true;
term->double_buffer_flush(term);
}
}
inline void set_cursor_pos_helper(size_t x, size_t y) {
print("\e[%u;%uH", (int)y + 1, (int)x + 1);
}
void term_notready(void);
void term_fallback(void);
void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count);
#endif

75
common/lib/time.c Normal file
View File

@ -0,0 +1,75 @@
#include <stddef.h>
#include <stdint.h>
#include <lib/time.h>
#if defined (BIOS)
# include <lib/real.h>
#elif defined (UEFI)
# include <efi.h>
#endif
#include <lib/misc.h>
// Julian date calculation from https://en.wikipedia.org/wiki/Julian_day
static int get_jdn(int days, int months, int years) {
return (1461 * (years + 4800 + (months - 14)/12))/4 + (367 *
(months - 2 - 12 * ((months - 14)/12)))/12 -
(3 * ((years + 4900 + (months - 14)/12)/100))/4
+ days - 32075;
}
static uint64_t get_unix_epoch(uint8_t seconds, uint8_t minutes, uint8_t hours,
uint8_t days, uint8_t months, uint16_t years) {
uint64_t jdn_current = get_jdn(days, months, years);
uint64_t jdn_1970 = get_jdn(1, 1, 1970);
uint64_t jdn_diff = jdn_current - jdn_1970;
return (jdn_diff * (60 * 60 * 24)) + hours * 3600 + minutes * 60 + seconds;
}
#if defined (BIOS)
uint64_t time(void) {
struct rm_regs r;
again:
r = (struct rm_regs){0};
r.eax = 0x0400;
rm_int(0x1a, &r, &r);
uint8_t day = bcd_to_int( r.edx & 0x00ff);
uint8_t month = bcd_to_int((r.edx & 0xff00) >> 8);
uint16_t year = bcd_to_int( r.ecx & 0x00ff) +
/* century */ bcd_to_int((r.ecx & 0xff00) >> 8) * 100;
r = (struct rm_regs){0};
r.eax = 0x0200;
rm_int(0x1a, &r, &r);
uint8_t second = bcd_to_int((r.edx & 0xff00) >> 8);
uint8_t minute = bcd_to_int( r.ecx & 0x00ff);
uint8_t hour = bcd_to_int((r.ecx & 0xff00) >> 8);
// Check RTC day wraparound
r = (struct rm_regs){0};
r.eax = 0x0400;
rm_int(0x1a, &r, &r);
if (bcd_to_int(r.edx & 0x00ff) != day) {
goto again;
}
return get_unix_epoch(second, minute, hour, day, month, year);
}
#endif
#if defined (UEFI)
uint64_t time(void) {
EFI_TIME time;
EFI_STATUS status = gRT->GetTime(&time, NULL);
if (status != 0) {
return 0;
}
return get_unix_epoch(time.Second, time.Minute, time.Hour,
time.Day, time.Month, time.Year);
}
#endif

8
common/lib/time.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __LIB__TIME_H__
#define __LIB__TIME_H__
#include <stdint.h>
uint64_t time(void);
#endif

8
common/lib/trace.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __LIB__TRACE_H__
#define __LIB__TRACE_H__
#include <stddef.h>
void print_stacktrace(size_t *base_ptr);
#endif

85
common/lib/trace.s2.c Normal file
View File

@ -0,0 +1,85 @@
#include <stddef.h>
#include <stdint.h>
#include <lib/trace.h>
#include <lib/misc.h>
#include <lib/config.h>
#include <lib/print.h>
#include <lib/uri.h>
#include <fs/file.h>
#include <mm/pmm.h>
#if defined (BIOS)
extern symbol stage2_map;
#elif defined (UEFI)
extern symbol __slide;
#endif
extern symbol full_map;
static char *trace_address(size_t *off, size_t addr) {
char *limine_map;
#if defined (BIOS)
if (!stage3_loaded)
limine_map = stage2_map;
else
limine_map = full_map;
#elif defined (UEFI)
limine_map = full_map;
addr -= (size_t)__slide;
#endif
uintptr_t prev_addr = 0;
char *prev_sym = NULL;
for (size_t i = 0;;) {
if (*((uintptr_t *)&limine_map[i]) >= addr) {
*off = addr - prev_addr;
return prev_sym;
}
prev_addr = *((uintptr_t *)&limine_map[i]);
i += sizeof(uintptr_t);
prev_sym = &limine_map[i];
while (limine_map[i++] != 0);
}
}
void print_stacktrace(size_t *base_ptr) {
if (base_ptr == NULL) {
asm volatile (
#if defined (__i386__)
"movl %%ebp, %0"
#elif defined (__x86_64__)
"movq %%rbp, %0"
#elif defined (__aarch64__)
"mov %0, x29"
#elif defined (__riscv64)
"mv %0, fp; addi %0, %0, -16"
#endif
: "=r"(base_ptr)
:: "memory"
);
}
print("Stacktrace:\n");
for (;;) {
size_t old_bp = base_ptr[0];
size_t ret_addr = base_ptr[1];
if (!ret_addr)
break;
size_t off;
char *name = trace_address(&off, ret_addr);
if (name)
print(" [%p] <%s+%p>\n", ret_addr, name, off);
else
print(" [%p]\n", ret_addr);
if (!old_bp)
break;
#if defined (__riscv)
base_ptr = (void *)(old_bp - 16);
#else
base_ptr = (void*)old_bp;
#endif
}
print("End of trace. ");
}

291
common/lib/uri.c Normal file
View File

@ -0,0 +1,291 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/uri.h>
#include <lib/misc.h>
#include <lib/part.h>
#include <lib/libc.h>
#include <fs/file.h>
#include <mm/pmm.h>
#include <lib/print.h>
#include <pxe/tftp.h>
#include <compress/gzip.h>
#include <menu.h>
#include <lib/readline.h>
#include <crypt/blake2b.h>
// A URI takes the form of: resource://root/path#hash
// The following function splits up a URI into its componenets
bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash) {
size_t length = strlen(uri) + 1;
char *buf = ext_mem_alloc(length);
memcpy(buf, uri, length);
uri = buf;
*resource = *root = *path = NULL;
// Get resource
for (size_t i = 0; ; i++) {
if (strlen(uri + i) < 3)
return false;
if (!memcmp(uri + i, "://", 3)) {
*resource = uri;
uri[i] = 0;
uri += i + 3;
break;
}
}
// Get root
for (size_t i = 0; ; i++) {
if (uri[i] == 0)
return false;
if (uri[i] == '/') {
*root = uri;
uri[i] = 0;
uri += i + 1;
break;
}
}
// Get path
if (*uri == 0)
return false;
*path = uri;
// Get hash
for (int i = (int)strlen(uri) - 1; i >= 0; i--) {
if (uri[i] != '#') {
continue;
}
uri[i++] = 0;
if (hash != NULL) {
*hash = uri + i;
}
if (strlen(uri + i) != 128) {
panic(true, "Blake2b hash must be 128 characters long");
return false;
}
break;
}
return true;
}
static bool parse_bios_partition(char *loc, int *drive, int *partition) {
uint64_t val;
for (size_t i = 0; ; i++) {
if (loc[i] == 0)
return false;
if (loc[i] == ':') {
loc[i] = 0;
if (*loc == 0) {
panic(true, "Drive number cannot be omitted for hdd:// and odd://");
} else {
val = strtoui(loc, NULL, 10);
if (val < 1 || val > 256) {
panic(true, "Drive number outside range 1-256");
}
*drive = val;
}
loc += i + 1;
break;
}
}
val = strtoui(loc, NULL, 10);
if (val > 256) {
panic(true, "Partition number outside range 0-256");
}
*partition = val;
return true;
}
static struct file_handle *uri_hdd_dispatch(char *loc, char *path) {
int drive, partition;
if (!parse_bios_partition(loc, &drive, &partition))
return NULL;
struct volume *volume = volume_get_by_coord(false, drive, partition);
if (volume == NULL)
return NULL;
return fopen(volume, path);
}
static struct file_handle *uri_odd_dispatch(char *loc, char *path) {
int drive, partition;
if (!parse_bios_partition(loc, &drive, &partition))
return NULL;
struct volume *volume = volume_get_by_coord(true, drive, partition);
if (volume == NULL)
return NULL;
return fopen(volume, path);
}
static struct file_handle *uri_guid_dispatch(char *guid_str, char *path) {
struct guid guid;
if (!string_to_guid_be(&guid, guid_str))
return NULL;
struct volume *volume = volume_get_by_guid(&guid);
if (volume == NULL) {
if (!string_to_guid_mixed(&guid, guid_str))
return NULL;
volume = volume_get_by_guid(&guid);
if (volume == NULL)
return NULL;
}
return fopen(volume, path);
}
static struct file_handle *uri_fslabel_dispatch(char *fslabel, char *path) {
struct volume *volume = volume_get_by_fslabel(fslabel);
if (volume == NULL) {
return NULL;
}
return fopen(volume, path);
}
static struct file_handle *uri_tftp_dispatch(char *root, char *path) {
uint32_t ip;
if (!strcmp(root, "")) {
ip = 0;
} else {
if (inet_pton(root, &ip)) {
panic(true, "tftp: Invalid ipv4 address: %s", root);
}
}
struct file_handle *ret;
if ((ret = tftp_open(boot_volume, root, path)) == NULL) {
return NULL;
}
return ret;
}
static struct file_handle *uri_boot_dispatch(char *s_part, char *path) {
if (boot_volume->pxe)
return uri_tftp_dispatch(s_part, path);
int partition;
if (s_part[0] != '\0') {
uint64_t val = strtoui(s_part, NULL, 10);
if (val > 256) {
panic(true, "Partition number outside range 0-256");
}
partition = val;
} else {
partition = boot_volume->partition;
}
struct volume *volume = volume_get_by_coord(boot_volume->is_optical,
boot_volume->index, partition);
if (volume == NULL)
return NULL;
return fopen(volume, path);
}
struct file_handle *uri_open(char *uri) {
struct file_handle *ret;
char *resource = NULL, *root = NULL, *path = NULL, *hash = NULL;
if (!uri_resolve(uri, &resource, &root, &path, &hash)) {
return NULL;
}
if (resource == NULL) {
panic(true, "No resource specified for URI `%#`.", uri);
}
bool compressed = false;
if (*resource == '$') {
compressed = true;
resource++;
}
if (!strcmp(resource, "bios")) {
panic(true, "bios:// resource is no longer supported. Check CONFIG.md for hdd:// and odd://");
} else if (!strcmp(resource, "hdd")) {
ret = uri_hdd_dispatch(root, path);
} else if (!strcmp(resource, "odd")) {
ret = uri_odd_dispatch(root, path);
} else if (!strcmp(resource, "boot")) {
ret = uri_boot_dispatch(root, path);
} else if (!strcmp(resource, "guid")) {
ret = uri_guid_dispatch(root, path);
} else if (!strcmp(resource, "uuid")) {
ret = uri_guid_dispatch(root, path);
} else if (!strcmp(resource, "fslabel")) {
ret = uri_fslabel_dispatch(root, path);
} else if (!strcmp(resource, "tftp")) {
ret = uri_tftp_dispatch(root, path);
} else {
panic(true, "Resource `%s` not valid.", resource);
}
if (hash != NULL && ret != NULL) {
uint8_t out_buf[BLAKE2B_OUT_BYTES];
void *file_buf = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
blake2b(out_buf, file_buf, ret->size);
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
for (size_t i = 0; i < sizeof(hash_buf); i++) {
hash_buf[i] = digit_to_int(hash[i * 2]) << 4 | digit_to_int(hash[i * 2 + 1]);
}
if (memcmp(hash_buf, out_buf, sizeof(out_buf)) != 0) {
if (hash_mismatch_panic) {
panic(true, "Blake2b hash for URI `%#` does not match!", uri);
} else {
print("WARNING: Blake2b hash for URI `%#` does not match!\n"
" Press Y to continue, press any other key to return to menu...", uri);
char ch = getchar();
if (ch != 'Y' && ch != 'y') {
menu(false);
}
print("\n");
}
}
}
if (compressed && ret != NULL) {
struct file_handle *compressed_fd = ext_mem_alloc(sizeof(struct file_handle));
void *src = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
if ((compressed_fd->fd = gzip_uncompress(src, ret->size, &compressed_fd->size)) == NULL) {
panic(true, "GZip error");
}
compressed_fd->vol = ret->vol;
compressed_fd->path = ext_mem_alloc(ret->path_len);
memcpy(compressed_fd->path, ret->path, ret->path_len);
compressed_fd->path_len = ret->path_len;
compressed_fd->is_memfile = true;
uint64_t src_size = ret->size;
fclose(ret);
pmm_free(src, src_size);
ret = compressed_fd;
}
return ret;
}

Some files were not shown because too many files have changed in this diff Show More