Add limine sources

This commit is contained in:
Zhirik1337 2026-03-13 14:19:46 +00:00
parent ccb89c6037
commit 2bb7471dbb
221 changed files with 36911 additions and 0 deletions

18
limine/.editorconfig Normal file
View file

@ -0,0 +1,18 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
max_line_length = 80
[{Makefile,GNUmakefile*}]
indent_style = tab
[{*.toml,*.yml}]
indent_size = 2

View file

@ -0,0 +1,91 @@
name: Binary release
on:
push:
tags:
- 'v*'
jobs:
build:
name: Build and upload binary artifacts
runs-on: codeberg-medium
container: archlinux:latest
steps:
- name: Create resolv.conf
run: |
set -ex
echo 'nameserver 8.8.8.8' >/etc/resolv.conf
echo 'nameserver 8.8.4.4' >>/etc/resolv.conf
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs base-devel gnupg git autoconf automake nasm curl mtools llvm clang lld mingw-w64-gcc
- name: Import GPG public key
run: gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821
- name: Import GPG private key
run: echo "$MINTSUKI_PRIVATE_KEY" | gpg --batch --import
env:
MINTSUKI_PRIVATE_KEY: ${{ secrets.MINTSUKI_PRIVATE_KEY }}
- name: Checkout code
uses: https://code.forgejo.org/actions/checkout@v6
with:
fetch-depth: '0'
- name: Git config
run: |
set -e
git config --global --add safe.directory "$FORGEJO_WORKSPACE"
git config --global user.name 'Mintsuki'
git config --global user.email 'mintsuki@protonmail.com'
git config --global user.signingkey 05D29860D0A0668AAEFB9D691F3C021BECA23821
- name: Get tag name
run: echo "TAG_NAME=$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" >> $FORGEJO_ENV
- name: Get branch name
run: echo "BRANCH_NAME=$(echo "$TAG_NAME" | grep -o 'v[0-9]\+\.')x" >> $FORGEJO_ENV
- name: Regenerate
run: ./bootstrap
- name: Create build dir
run: mkdir -p build
- name: Configure
run: cd build && ../configure --enable-all
- name: Build the bootloader
run: make -C build -j$(nproc)
- 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: Remove limine-bios-hdd.bin
run: rm build/bin/limine-bios-hdd.bin
- name: Push binaries to binary branch
run: |
set -e
git remote set-url origin https://x-access-token:${{ secrets.FORGEJO_TOKEN }}@codeberg.org/Limine/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" -S
git push origin $BRANCH_NAME-binary
git tag $TAG_NAME-binary -s -m $TAG_NAME-binary
git push origin $TAG_NAME-binary

View file

@ -0,0 +1,51 @@
name: Check for compilation failures
on: [push, pull_request]
jobs:
build:
name: Check for compilation failures
runs-on: codeberg-small
container: archlinux:latest
steps:
- name: Create resolv.conf
run: |
set -ex
echo 'nameserver 8.8.8.8' >/etc/resolv.conf
echo 'nameserver 8.8.4.4' >>/etc/resolv.conf
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs base-devel git autoconf automake nasm curl mtools llvm clang lld
- name: Checkout code
uses: https://code.forgejo.org/actions/checkout@v6
- name: Build the bootloader (LLVM, default)
run: ./bootstrap && ./configure --enable-werror --enable-all && make all && make distclean
- name: Set cross GCC version
run: echo "GCC_VERSION=15.2.0" >> $FORGEJO_ENV
- name: Download GCC cross toolchains
run: |
set -e
for i in aarch64 loongarch64 riscv64 x86_64; do
( curl -Lo x86_64-gcc-${{ env.GCC_VERSION }}-nolibc-$i-linux.tar.gz https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/${{ env.GCC_VERSION }}/x86_64-gcc-${{ env.GCC_VERSION }}-nolibc-$i-linux.tar.gz && tar -xf x86_64-gcc-${{ env.GCC_VERSION }}-nolibc-$i-linux.tar.gz ) &
WAITING_ON_PIDS="$WAITING_ON_PIDS $!"
done
for pid in $WAITING_ON_PIDS; do
wait $pid
done
- name: Build the bootloader (GNU, x86)
run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/x86_64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=x86_64-linux- --enable-werror --enable-bios --enable-uefi-ia32 --enable-uefi-x86-64 && make all -j$(nproc) && make distclean
- name: Build the bootloader (GNU, aarch64)
run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/aarch64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=aarch64-linux- --enable-werror --enable-uefi-aarch64 && make all -j$(nproc) && make distclean
- name: Build the bootloader (GNU, riscv64)
run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/riscv64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=riscv64-linux- --enable-werror --enable-uefi-riscv64 && make all -j$(nproc) && make distclean
- name: Build the bootloader (GNU, loongarch64)
run: export PATH="$(pwd -P)"/gcc-${{ env.GCC_VERSION }}-nolibc/loongarch64-linux/bin:"$PATH" && ./configure TOOLCHAIN_FOR_TARGET=loongarch64-linux- --enable-werror --enable-uefi-loongarch64 && make all -j$(nproc) && make distclean

View file

@ -0,0 +1,15 @@
name: Check that the PR is targetting trunk
on: [ pull_request ]
jobs:
pr_branch_check:
name: Check that the PR is targetting trunk
runs-on: codeberg-tiny
steps:
- name: Check that the PR is targetting trunk
if: ${{ forge.base_ref != 'trunk' }}
run: |
set -e
echo "The PR is not targetting the trunk branch, please fix that."
false

View file

@ -0,0 +1,24 @@
name: QA
on: [ merge_group, push, pull_request ]
jobs:
spellcheck:
name: Spellcheck
runs-on: codeberg-tiny
container: archlinux:latest
steps:
- name: Create resolv.conf
run: |
set -ex
echo 'nameserver 8.8.8.8' >/etc/resolv.conf
echo 'nameserver 8.8.4.4' >>/etc/resolv.conf
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs git typos
- name: Checkout code
uses: https://code.forgejo.org/actions/checkout@v6
- name: Run spellchecker
run: typos

View file

@ -0,0 +1,100 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
build:
name: Build and upload release tarball
runs-on: codeberg-small
container: archlinux:latest
steps:
- name: Create resolv.conf
run: |
set -ex
echo 'nameserver 8.8.8.8' >/etc/resolv.conf
echo 'nameserver 8.8.4.4' >>/etc/resolv.conf
- name: Install dependencies
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S nodejs jq curl tea base-devel gnupg gzip bzip2 xz git autoconf automake nasm curl mtools llvm clang lld github-cli
- name: Import GPG public key
run: gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821
- name: Import GPG private key
run: echo "$MINTSUKI_PRIVATE_KEY" | gpg --batch --import
env:
MINTSUKI_PRIVATE_KEY: ${{ secrets.MINTSUKI_PRIVATE_KEY }}
- name: Checkout code
uses: https://code.forgejo.org/actions/checkout@v6
- name: Package release tarball
run: |
set -e
./bootstrap
./configure
make dist
mkdir dist-output
mv limine-*.tar.* dist-output/
- name: Sign release tarball
run: |
set -e
for f in dist-output/*; do
gpg --batch --default-key 05D29860D0A0668AAEFB9D691F3C021BECA23821 --detach-sign $f
done
- name: Release
uses: https://code.forgejo.org/actions/forgejo-release@v2.7.3
with:
direction: upload
hide-archive-link: true
release-dir: dist-output
release-notes: |
Changelog can be found [here](https://codeberg.org/Limine/Limine/src/tag/${{ forge.ref_name }}/ChangeLog).
Binary release can be found [here](https://codeberg.org/Limine/Limine/src/tag/${{ forge.ref_name }}-binary).
Tarballs are signed using key ID `05D29860D0A0668AAEFB9D691F3C021BECA23821` which can be obtained from a keyserver such as `keyserver.ubuntu.com`.
Import the public key with:
```bash
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821
```
In order to verify the tarball with the given signature, do:
```bash
gpg --verify <tarball sig file> <associated tarball>
```
- name: GitHub mirror release
env:
GITHUB_TOKEN: ${{ secrets.MINTSUKI_GH_TOKEN }}
run: |
set -e
cat >release_notes.txt <<'EOF'
Changelog can be found [here](https://github.com/limine-bootloader/limine/blob/${{ forge.ref_name }}/ChangeLog).
Binary release can be found [here](https://github.com/limine-bootloader/limine/tree/${{ forge.ref_name }}-binary).
Tarballs are signed using key ID `05D29860D0A0668AAEFB9D691F3C021BECA23821` which can be obtained from a keyserver such as `keyserver.ubuntu.com`.
Import the public key with:
```bash
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 05D29860D0A0668AAEFB9D691F3C021BECA23821
```
In order to verify the tarball with the given signature, do:
```bash
gpg --verify <tarball sig file> <associated tarball>
```
EOF
gh release create "${{ forge.ref_name }}" \
--repo "limine-bootloader/limine" \
--title "${{ forge.ref_name }}" \
--notes-file "release_notes.txt" \
dist-output/*

75
limine/.gitignore vendored Normal file
View file

@ -0,0 +1,75 @@
# We don't want to ignore the following files
!.clang-format
!.editorconfig
!.gitattributes
!.gitignore
!.typos.toml
# Generated files
*.o
*.d
*.a
*.exe
*.EFI
*.bin
*.bin.gz
*.tar*
*.elf
*.hdd
*.iso
*.sys
*.dtb
*~
/bin
/build
/limine-protocol
/picoefi
/freestnd-c-hdrs
/flanterm
/common/lib/stb_image.h.nopatch
/common/lib/stb_image.h
/common/cc-runtime.s2.c
/cc-runtime
/decompressor/tinf
/decompressor/cc-runtime.c
/libfdt
/tinf
/edk2-ovmf
/bochsout.txt
/bx_enh_dbg.ini
/test_image
/configure
/configure.ac.save
/timestamps
/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
/common-uefi-loongarch64
/decompressor-build
/stage1.stamp
# For local development
/.vscode
/.gdb_history
/tags
/TAGS
# Clang's compilation database file
compile_commands.json
# clangd caches
/.clangd
/.cache/clangd

24
limine/.typos.toml Normal file
View file

@ -0,0 +1,24 @@
# Configuration for https://github.com/crate-ci/typos
[files]
extend-exclude = [
# Typos already uses .gitignore. These excludes are additional:
]
[default.extend-words]
hda = "hda"
Nd = "Nd"
sie = "sie"
SIE = "SIE"
ist = "ist"
rela = "rela"
RELA = "RELA"
guid = "guid"
GUID = "GUID"
pn = "pn"
hd = "hd"
htpt = "htpt"
CMO = "CMO"
extint = "extint"
[default.extend-identifiers]

67
limine/3RDPARTY.md Normal file
View file

@ -0,0 +1,67 @@
# 3rd Party Software Acknowledgments
The Limine project depends on several other projects.
(For readers with access to source code, know that these are pulled in by the
`./bootstrap` script, or, in the case of release tarballs, are shipped
alongside the core Limine code in the tarballs themselves, similar to
`./bootstrap` having already been run.)
These additional projects are NOT covered by the License as contained inside
the `COPYING` file as present at the root of the source tree, or, for installed
copies, present at `${DOCDIR}/COPYING` (assuming the file has not been
otherwise removed by the packager). These are instead licensed as described by
each individual project's documentation present in each project's dedicated
subdirectory or license header(s) in the source tree. For readers without access
to the source code, one can read the following for a quick overview of licenses
that Limine is distributed under:
A non-binding, informal summary of all projects Limine depends on, and the
licenses used by said projects, in SPDX format, is as follows:
- [cc-runtime](https://codeberg.org/OSDev/cc-runtime)
(Apache-2.0 WITH LLVM-exception) is used to provide runtime libgcc-like
routines.
- [0BSD Freestanding C Headers](https://codeberg.org/OSDev/freestnd-c-hdrs-0bsd)
(0BSD) provide GCC and Clang compatible freestanding C headers.
- [Limine Boot Protocol](https://codeberg.org/Limine/limine-protocol)
(0BSD) has the C/C++ header and the specification text of the Limine Boot
Protocol.
- [PicoEFI](https://codeberg.org/PicoEFI/PicoEFI) (multiple licenses, see list
below) provides headers and build-time support for UEFI.
- BSD-2-Clause
- BSD-2-Clause-Patent
- BSD-3-Clause
- LicenseRef-scancode-bsd-no-disclaimer-unmodified
- MIT
For more information about the
LicenseRef-scancode-bsd-no-disclaimer-unmodified license used by parts of
PicoEFI, see
https://scancode-licensedb.aboutcode.org/bsd-no-disclaimer-unmodified.html
and the LicenseRef file
[here](LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt),
in case of viewing this file from inside the source tree, alternatively at
`${DOCDIR}/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt`
in case of installed copies, assuming the file has not been otherwise
removed by the packager.
- [tinf](https://github.com/jibsen/tinf) (Zlib) is used in early x86 BIOS
stages for GZIP decompression of stage2.
- [Flanterm](https://codeberg.org/Mintsuki/Flanterm) (BSD-2-Clause) is used for
text related screen drawing.
- [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) (MIT) is
used for wallpaper image loading.
- [libfdt](https://codeberg.org/OSDev/libfdt) (BSD-2-Clause) is used for
manipulating Flat Device Trees.
Note that some of these projects, or parts of them, are provided under
dual-licensing, in which case, in the above list, the only license mentioned is
the one chosen by the Limine developers. Refer to each individual project's
documentation for details.

364
limine/CONFIG.md Normal file
View file

@ -0,0 +1,364 @@
# Limine configuration file
## Location of the config file
For EFI-booted Limine, `<EFI app path>/limine.conf` is taken into account
first. On BIOS, or on EFI if that file is not found, Limine scans for the
config file on *the boot drive*. Every partition of the boot drive is scanned
sequentially - first partition first (or, on EFI, the partition containing the
EFI executable of the booted Limine is scanned first), last partition last -
for the presence of either a `/boot/limine/limine.conf`, `/boot/limine.conf`,
`/limine/limine.conf`, or a `/limine.conf` 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 candidate config file.
### Config via SMBIOS
Alternatively, if present, Limine considers first and foremost configurations
supplied to it as SMBIOS OEM String entries (Type 11). Such configurations are
accepted if the first string of such an entry starts with the prefix of
`limine:config:`. The rest of the string is taken as the config file. If such a
configuration is found, no further scanning for config files is done. As such,
in this SMBIOS-provided config file scenario, the `boot():` drive is undefined
on BIOS, and set to the boot device of Limine on UEFI.
## Structure of the config file
The Limine configuration file is comprised of *menu entries* and *options*.
Comments begin in '#' and can only be on their own lines.
### Menu entries and sub-entries
*Menu entries* describe *entries* which the user can select in the *boot menu*.
A *menu entry* is opened by a line starting with `/` followed by a
newline-terminated string, that being the title of the entry which the user
will see.
Any *local option* that comes after it, and before another *menu entry*, or
the end of the file, will be part of that *menu entry*.
A *menu 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 `comment` option may be present between the beginning of the directory entry
and the beginning of the sub-entry).
A *sub-entry* is a menu entry started 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 title.
### Options
*Options* are simple `option_name: string...` style "assignments".
The string can have spaces and other special characters, without requiring
quotations. New lines are delimiters. Option names are not case sensitive.
Some *options* are part of an entry (*local*), some other options are *global*.
*Global options* 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 options* work the same between entries using any *protocol*, while
other *local options* are specific to a given *protocol*.
Some options take *paths* as strings; these are described in the next section.
*Global options* are:
Miscellaneous:
* `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` option).
* `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.
* `serial_baudrate` - If `serial` is set to `yes`, this specifies the baudrate
to use for serial I/O. Defaults to `115200`. BIOS only, ignored with Limine
UEFI.
* `global_dtb` - If set, use this DTB instead of the firmware-provided DTB for
Limine itself, as well as for any booted entry whose protocol supports DTBs
and the DTB is not locally overridden with `dtb_path`.
* `default_entry` - 1-based entry index of the entry which will be
automatically selected at startup. If unspecified, it is `1`.
* `remember_last_entry` - If set to `yes`, remember last booted entry.
(UEFI only).
* `graphics` - If set to `no`, force CGA text mode for the boot menu, else use
a video mode. Ignored with Limine UEFI.
* `wallpaper` - Path to a file to use as a wallpaper. BMP, PNG, and JPEG
formats are supported. There can be multiple of this option, in which case
the wallpaper will be randomly selected from the provided options.
* `wallpaper_style` - The style which will be used to display the wallpaper
image: `tiled`, `centered`, or `stretched`. Default is `stretched`.
* `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.
* `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_rotation` - Specifies the rotation of the Limine interface.
It can be any of the following values: `0`, `90`, `180`, `270`. Default is `0`.
* `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. See below for a table of colours. Default is `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.
* `interface_help_colour` - A value between 0 and 7 specifying the colour
of the help strings. See below for a table of colours. Default is `2`.
* `interface_help_color` - Alias of `interface_help_colour`.
| Code | Color |
|------|---------|
| 0 | Black |
| 1 | Red |
| 2 | Green |
| 3 | Yellow |
| 4 | Blue |
| 5 | Magenta |
| 6 | Cyan |
| 7 | Gray |
Limine graphical terminal control options:
These are ignored if using text mode.
* `term_font` - 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](https://github.com/viler-int10h/vga-text-mode-fonts)
collection for fonts.
* `term_font_size` - The size of each glyph of the font in dots, which must
correspond to the font file, or display will be garbled or loading issues
will occur. Since it is assumed that all fonts are of width 8, the first
value of the pair (AKA the `8` in `8x16`) is effectively ignored. To set
horizontal spacing between glyphs on screen, see `term_font_spacing`.
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. 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.
Editor control options:
* `editor_enabled` - If set to `no`, the editor will not be accessible.
Defaults to `yes` unless a config hash is enrolled.
* `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 options or syntax errors. Defaults to `yes`.
*Locally assignable (non protocol specific) options* 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/executable. Valid protocols are: `linux`, `limine`, `multiboot`
(or `multiboot1`), `multiboot2`, `efi`, and `bios`.
* `cmdline` - The command line string to be passed to the kernel/executable.
Can be omitted.
* `kernel_cmdline` - Alias of `cmdline`.
> **NOTE:** `uefi` and `efi_chainload` are aliases of the `efi` protocol
> option. `bios_chainload` is an alias of the `bios` protocol option.
> **NOTE:** BIOS chainloading entries will be hidden when booting using UEFI
> and vice-versa.
*Locally assignable (protocol specific) options* are:
* Linux protocol:
* `path` - The path of the kernel.
* `kernel_path` - Alias of `path`.
* `module_path` - The path to a module (such as initramfs). This option can
be specified 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)
* `dtb_path` - A device tree blob to pass instead of the one provided by the
firmware.
* Limine protocol:
* `path` - The path of the executable.
* `kernel_path` - Alias of `path`.
* `module_path` - The path to a module. This option can be specified multiple
times to specify multiple modules.
* `module_string` - A string to be associated with a module. This option can
also be specified multiple times. It applies to the module described by the
last module option specified.
* `module_cmdline` - Alias of `module_string`.
* `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 executables, if set to `yes`, enable kernel
address space layout randomisation. KASLR is disabled by default.
* `randomise_hhdm_base` - If set to `yes`, randomise the base address of the
higher half direct map. If set to `no`, do not. By default it is `yes` if
KASLR is supported and enabled, else it is `no`.
* `randomize_hhdm_base` - Alias of `randomise_hhdm_base`.
* `max_paging_mode`, `min_paging_mode` - Limit the maximum and minimum paging
modes to one of the following:
- x86-64 and aarch64: `4level`, `5level`.
- riscv64: `sv39`, `sv48`, `sv57`.
- loongarch64: `4level`.
* `paging_mode` - Equivalent to setting both `max_paging_mode` and
`min_paging_mode` to the same value.
* `dtb_path` - A device tree blob to pass instead of the one provided by the
firmware.
* multiboot1 and multiboot2 protocols:
* `path` - The path of the executable.
* `kernel_path` - Alias of `path`.
* `module_path` - The path to a module. This option can be specified multiple
times to specify multiple modules.
* `module_string` - A string to be passed to a module. This option can also
be specified multiple times. It applies to the module described by the last
module option specified.
* `resolution` - The resolution to be used should the executable request a
graphical framebuffer. This setting takes the form of
`<width>x<height>x<bpp>` and *overrides* any resolution requested by the
executable. 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)
* EFI Chainload protocol:
* `path` - Path of the EFI application to chainload.
* `image_path` - Alias of `path`.
* `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.
* BIOS Chainload protocol:
* `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`.
## Paths
A Limine path is used to locate files in the whole system. It is comprised of
a *resource*, a *resource argument*, and a *path*. It takes the form of:
```
resource(argument):/path
```
The format for `argument` 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 `argument` 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 `argument` 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 `argument` 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 `argument` 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 `argument` is the name of the filesystem label of a
partition.
* `tftp` - The `argument` is the IP address of the tftp server to load the file
from. If the argument 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 path 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.tar#ca6914d2...446b470a`.
## 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`, `loongarch64`.
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`.
* `FW_TYPE` - This built-in macro expands to `UEFI` if booted using UEFI
firmware, or `BIOS` if booted using legacy x86 BIOS.

22
limine/COPYING Normal file
View file

@ -0,0 +1,22 @@
Copyright (C) 2019-2026 Mintsuki and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1882
limine/ChangeLog Normal file

File diff suppressed because it is too large Load diff

35
limine/FAQ.md Normal file
View file

@ -0,0 +1,35 @@
# Frequently Asked Questions
### 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 if a malicious actor modifies the config file?
While this is a pointless effort on legacy x86 BIOS, it is a reasonable
expectation to secure the boot sequence 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.
### I do not want to have a separate FAT boot partition! What can I do?
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, initramfses, and any other files needed for boot there.

401
limine/GNUmakefile.in Normal file
View file

@ -0,0 +1,401 @@
.SUFFIXES:
override SOURCE_DATE_EPOCH := @SOURCE_DATE_EPOCH@
export SOURCE_DATE_EPOCH
override SOURCE_DATE_EPOCH_TOUCH := @SOURCE_DATE_EPOCH_TOUCH@
override PACKAGE_TARNAME := @PACKAGE_TARNAME@
override PACKAGE_VERSION := @PACKAGE_VERSION@
override DIST_OUTPUT := $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)
override prefix := @prefix@
override exec_prefix := @exec_prefix@
override bindir := @bindir@
override datarootdir := @datarootdir@
override mandir := @mandir@
override docdir := @docdir@
override BUILDDIR := @BUILDDIR@
override BINDIR := $(BUILDDIR)/bin
override SRCDIR := @SRCDIR@
override SPACE := $(subst ,, )
override COMMA := ,
override MKESCAPE = $(subst $(SPACE),\ ,$(1))
override SHESCAPE = $(subst ','\'',$(1))
override NASMESCAPE = $(subst ','"'$(COMMA) \"'\"$(COMMA) '"',$(1))
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_LOONGARCH64 := @BUILD_UEFI_LOONGARCH64@
override BUILD_UEFI_CD := @BUILD_UEFI_CD@
override BUILD_BIOS_PXE := @BUILD_BIOS_PXE@
override BUILD_BIOS_CD := @BUILD_BIOS_CD@
INSTALL := @INSTALL@
INSTALL_PROGRAM := @INSTALL_PROGRAM@
INSTALL_DATA := @INSTALL_DATA@
STRIP := @STRIP@
MKDIR_P := @MKDIR_P@
export MKDIR_P
GREP := @GREP@
export GREP
SED := @SED@
export SED
AWK := @AWK@
export AWK
CC := @CC@
CPPFLAGS := @CPPFLAGS@
CFLAGS := @CFLAGS@
LDFLAGS := @LDFLAGS@
LIBS := @LIBS@
CC_FOR_TARGET := @CC_FOR_TARGET@
export CC_FOR_TARGET
LD_FOR_TARGET := @LD_FOR_TARGET@
export LD_FOR_TARGET
OBJCOPY_FOR_TARGET := @OBJCOPY_FOR_TARGET@
export OBJCOPY_FOR_TARGET
OBJDUMP_FOR_TARGET := @OBJDUMP_FOR_TARGET@
export OBJDUMP_FOR_TARGET
READELF_FOR_TARGET := @READELF_FOR_TARGET@
export READELF_FOR_TARGET
override WERROR_FLAG := @WERROR_FLAG@
export WERROR_FLAG
CFLAGS_FOR_TARGET := @CFLAGS_FOR_TARGET@
export CFLAGS_FOR_TARGET
CPPFLAGS_FOR_TARGET := @CPPFLAGS_FOR_TARGET@
export CPPFLAGS_FOR_TARGET
LDFLAGS_FOR_TARGET := @LDFLAGS_FOR_TARGET@
export LDFLAGS_FOR_TARGET
NASMFLAGS_FOR_TARGET := @NASMFLAGS_FOR_TARGET@
export NASMFLAGS_FOR_TARGET
override STAGE1_FILES := $(shell find '$(call SHESCAPE,$(SRCDIR))/stage1' -type f -name '*.asm' | LC_ALL=C sort)
.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_UEFI_LOONGARCH64) $(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
override LIMINE_NO_BIOS :=
ifneq ($(BUILD_BIOS),limine-bios)
override LIMINE_NO_BIOS := -DLIMINE_NO_BIOS
endif
$(call MKESCAPE,$(BINDIR))/limine: $(call MKESCAPE,$(BINDIR))/Makefile $(call MKESCAPE,$(SRCDIR))/host/limine.c $(if $(filter $(BUILD_BIOS),limine-bios),$(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h)
$(SED) 's/%VERSION%/@PACKAGE_VERSION@/g;s/%COPYRIGHT%/@LIMINE_COPYRIGHT@/g' <'$(call SHESCAPE,$(SRCDIR))/host/limine.c' >'$(call SHESCAPE,$(BINDIR))/limine.c'
$(MAKE) -C '$(call SHESCAPE,$(BINDIR))' limine \
CC="$(CC)" \
CFLAGS="$(CFLAGS) -Wall -Wextra $(WERROR_FLAG)" \
CPPFLAGS='$(CPPFLAGS) $(LIMINE_NO_BIOS) -DLIMINE_DATADIR=\"$(call SHESCAPE,$(datarootdir))/limine\"' \
LDFLAGS="$(LDFLAGS)" \
LIBS="$(LIBS)"
$(call MKESCAPE,$(BINDIR))/Makefile: $(call MKESCAPE,$(SRCDIR))/host/host.mk $(call MKESCAPE,$(SRCDIR))/host/.gitignore
mkdir -p '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(SRCDIR))/host/host.mk' '$(call SHESCAPE,$(BINDIR))/Makefile'
cp '$(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 limine-uefi-loongarch64-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) -d '$(call SHESCAPE,$(DESTDIR)$(docdir))/LICENSES'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt' '$(call SHESCAPE,$(DESTDIR)$(docdir))/LICENSES/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/3RDPARTY.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/CONFIG.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/FAQ.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/USAGE.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_LOONGARCH64),limine-uefi-loongarch64)
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTLOONGARCH64.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)$(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 -rf '$(call SHESCAPE,$(DESTDIR)$(docdir))/LICENSES'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/3RDPARTY.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/CONFIG.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/FAQ.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/USAGE.md'
rm -f '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/limine.1'
rm -f '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine'
rm -rf '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine'
$(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 -w-unknown-warning -w-reloc $(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 -w-unknown-warning -w-reloc $(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 -w-unknown-warning -w-reloc $(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) $(if $(BUILD_UEFI_LOONGARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.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=5760 2>/dev/null
mformat -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' -f 2880 -N 12345678 ::
LIMINE_UEFI_CD_TMP="$$(mktemp -d)"; \
mkdir -p "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT; \
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' "$$LIMINE_UEFI_CD_TMP"/EFI/BOOT/ 2>/dev/null; \
find "$$LIMINE_UEFI_CD_TMP" -exec touch -t $(SOURCE_DATE_EPOCH_TOUCH) '{}' + && \
mcopy -D o -s -m -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' "$$LIMINE_UEFI_CD_TMP"/EFI :: && \
rm -rf "$$LIMINE_UEFI_CD_TMP"
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'
$(call MKESCAPE,$(BINDIR))/BOOTLOONGARCH64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64/BOOTLOONGARCH64.EFI' '$(call SHESCAPE,$(BINDIR))/'
.PHONY: limine-uefi-loongarch64
limine-uefi-loongarch64:
$(MAKE) common-uefi-loongarch64
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTLOONGARCH64.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: limine-uefi-loongarch64-clean
limine-uefi-loongarch64-clean: common-uefi-loongarch64-clean
.PHONY: dist
dist:
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)"
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)"
cp -r '$(call SHESCAPE,$(SRCDIR))'/.git '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)"/
cd '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" && git checkout .
cd '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)" && ./bootstrap
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/flanterm/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/flanterm/.gitignore"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/freestnd-c-hdrs/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/limine-protocol/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/picoefi/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/picoefi/.gitignore"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/cc-runtime"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/libfdt/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/tinf"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/common/lib/stb_image.h.nopatch"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/.git"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/.gitignore"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/.forgejo"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/README.md"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/autom4te.cache"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/test"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/test.mk"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/logo.png"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/screenshot.png"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/bochsrc"
echo "$(PACKAGE_VERSION)" > '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)/version"
cd '$(call SHESCAPE,$(BUILDDIR))' && tar -cf "$(DIST_OUTPUT).tar" "$(DIST_OUTPUT)"
cd '$(call SHESCAPE,$(BUILDDIR))' && gzip < "$(DIST_OUTPUT).tar" > "$(DIST_OUTPUT).tar.gz"
cd '$(call SHESCAPE,$(BUILDDIR))' && ( bzip2 < "$(DIST_OUTPUT).tar" > "$(DIST_OUTPUT).tar.bz2" || rm -f "$(DIST_OUTPUT).tar.bz2" )
cd '$(call SHESCAPE,$(BUILDDIR))' && ( xz < "$(DIST_OUTPUT).tar" > "$(DIST_OUTPUT).tar.xz" || rm -f "$(DIST_OUTPUT).tar.xz" )
cd '$(call SHESCAPE,$(BUILDDIR))' && rm "$(DIST_OUTPUT).tar"
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"$(DIST_OUTPUT)"
.PHONY: distclean
distclean: clean
rm -rf edk2-ovmf config.log config.status GNUmakefile config.h man/man1/limine.1
.PHONY: maintainer-clean
maintainer-clean: distclean
cd '$(call SHESCAPE,$(SRCDIR))' && rm -rf flanterm common/lib/stb_image.h.nopatch common/lib/stb_image.h decompressor/tinf tinf libfdt freestnd-c-hdrs cc-runtime common/cc-runtime.s2.c decompressor/cc-runtime.c limine-protocol picoefi configure timestamps build-aux *'~' autom4te.cache aclocal.m4 *.tar*
.PHONY: common-uefi-x86-64
common-uefi-x86-64:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.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' -f common.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' -f common.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-loongarch64
common-uefi-loongarch64:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.mk \
TARGET=uefi-loongarch64 \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64'
.PHONY: common-uefi-loongarch64-clean
common-uefi-loongarch64-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-loongarch64'
.PHONY: common-uefi-ia32
common-uefi-ia32:
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' -f common.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' -f common.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' -f decompressor.mk \
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/decompressor-build'
.PHONY: decompressor-clean
decompressor-clean:
rm -rf '$(call SHESCAPE,$(BUILDDIR))/decompressor-build'
-include test.mk

50
limine/INSTALL.md Normal file
View file

@ -0,0 +1,50 @@
# Build and Install Instructions
> **NOTE:** This document is about building and installing Limine.
> For information about deployment for usage, see [USAGE.md](USAGE.md).
## 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://codeberg.org/Limine/Limine/releases), run `./configure` directly.
If checking out from the repository, run `./bootstrap` first in order to
download the necessary [dependencies](3RDPARTY.md) and generate the configure
script (`GNU autoconf` required).
`./configure` takes arguments and environment variables; for more information
on these, run `./configure --help`.
> **NOTE:** `./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
To build Limine, run:
```bash
make # (or gmake where applicable)
```
## Installing
This step will install Limine files to `share`, `include`, and `bin`
directories in the specified prefix (default is `/usr/local`, see
`./configure --help`.
To install Limine, run:
```bash
make install # (or gmake where applicable)
```

View file

@ -0,0 +1,7 @@
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice and this list of conditions, without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.

102
limine/README.md Normal file
View file

@ -0,0 +1,102 @@
# Limine [![Matrix Server](https://img.shields.io/matrix/limine:matrix.org?color=000000&label=Matrix&logo=matrix)](https://matrix.to/#/#limine:matrix.org) [![Fluxer](https://img.shields.io/badge/Fluxer-Chat-4f3cc9?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij4KICA8cGF0aCBkPSJNMyA3LjVjMi41LTMgNS0zIDcuNSAwczUgMyA3LjUgMCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiLz4KICA8cGF0aCBkPSJNMyAxNi41YzIuNS0zIDUtMyA3LjUgMHM1IDMgNy41IDAiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBmaWxsPSJub25lIi8+Cjwvc3ZnPgo=&style=flat)](https://fluxer.gg/ZRviNMvT)
<p align="center">
<img src="https://codeberg.org/Limine/Limine/raw/branch/trunk/logo.png" alt="Limine's logo"/>
</p>
### What is Limine?
Limine (pronounced as demonstrated [here](https://www.merriam-webster.com/dictionary/in%20limine))
is a modern, advanced, portable, multiprotocol bootloader and boot manager, also used
as the reference implementation for the [Limine boot protocol](https://codeberg.org/Limine/limine-protocol/src/branch/trunk/PROTOCOL.md).
### Community, Support, and Donations
#### Donate
If you want to support the work I ([@mintsuki](https://codeberg.org/Mintsuki)) do on Limine, feel free to donate to me on Liberapay:
<p><a href="https://liberapay.com/mintsuki/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></p>
Donations welcome, but absolutely not mandatory!
#### Community
We have a Matrix room at [`#limine:matrix.org`](https://matrix.to/#/#limine:matrix.org)
and a [Fluxer community](https://fluxer.gg/ZRviNMvT) if you need support, info, or you just want to hang out with us.
### Limine's boot menu
![Reference screenshot](screenshot.png?raw=true "Reference screenshot")
[Photo by Pixabay](https://www.pexels.com/photo/brown-leafed-tree-on-open-field-under-white-clouds-and-blue-sky-35857/)
### Supported architectures
* IA-32 (32-bit x86)
* x86-64
* aarch64 (arm64)
* riscv64
* loongarch64 (experimental support!)
### Supported boot protocols
* Linux
* [Limine](https://codeberg.org/Limine/limine-protocol/src/branch/trunk/PROTOCOL.md)
* Multiboot 1
* Multiboot 2
* Chainloading
### Supported partitioning schemes
* MBR
* GPT
* Unpartitioned media
### Supported filesystems
* FAT12/16/32
* ISO9660 (CDs/DVDs)
If your filesystem isn't listed here, please read [the FAQ](FAQ.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, riscv64 and loongarch64 (UEFI) systems are supported.
## Packaging status
All Limine releases since 7.x use [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for their naming.
[![Packaging status](https://repology.org/badge/vertical-allrepos/limine.svg?columns=3)](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://codeberg.org/Limine/Limine/branches) and
[tags](https://codeberg.org/Limine/Limine/tags)).
For example, to clone the latest binary release of the `10.x` branch, one can do:
```bash
git clone https://codeberg.org/Limine/Limine.git --branch=v10.x-binary --depth=1
```
or, to clone a specific binary point release (for example `10.8.5`):
```bash
git clone https://codeberg.org/Limine/Limine.git --branch=v10.8.5-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.
## Build and Install Instructions
*The following steps are not necessary if cloning a binary release.*
See [INSTALL.md](INSTALL.md).
## Usage
See [USAGE.md](USAGE.md).
## 3rd Party Software Acknowledgments
See [3RDPARTY.md](3RDPARTY.md).

107
limine/USAGE.md Normal file
View file

@ -0,0 +1,107 @@
# Usage
> **NOTE:** The Limine files referred to here are those contained inside
> ${PREFIX}/share/, installed there as a product of the steps described in
> [INSTALL.md](INSTALL.md).
## 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.
A valid config file should also be provided as described in
[CONFIG.md](CONFIG.md).
## 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 FAQ](FAQ.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.conf` files in
either a `boot/limine`, `boot`, `limine`, or root directory of one of the
partitions, formatted with a supported file system. See [CONFIG.md](CONFIG.md).
## BIOS/GPT
If using a GPT formatted device, create a partition on the GPT device (usually
of "BIOS boot" type) 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>
```
The boot device must contain the `limine-bios.sys` and `limine.conf` files in
either a `boot/limine`, `boot`, `limine`, or root directory of one of the
partitions, formatted with a supported file system. See [CONFIG.md](CONFIG.md).
## 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.conf` files into a directory which will serve as the root of the
created ISO.
(`limine-bios.sys` and `limine.conf` 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 -R -r -J -b <relative path of limine-bios-cd.bin> \
-no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
-apm-block-size 2048 --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.conf` 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.conf` file contains Limine's configuration.
More info on the format of `limine.conf` can be found in
[`CONFIG.md`](CONFIG.md).

7
limine/bochsrc Normal file
View file

@ -0,0 +1,7 @@
cpu: count=2, reset_on_triple_fault=0
memory: guest=1024, host=1024
ata0-master: type=disk, path="test.hdd", mode=flat
boot: c
clock: sync=realtime, rtc_sync=1, time0=utc
port_e9_hack: enabled=1
magic_break: enabled=1

170
limine/bootstrap Executable file
View file

@ -0,0 +1,170 @@
#! /bin/sh
set -ex
srcdir="$(dirname "$0")"
test -z "$srcdir" && srcdir=.
: "${AUTORECONF:=autoreconf}"
: "${AUTOMAKE:=automake}"
cd "$srcdir"
AUXFILES="config.guess config.sub install-sh"
clone_repo_commit() {
if test -d "$2/.git"; then
git -C "$2" reset --hard
git -C "$2" clean -fd
if ! git -C "$2" -c advice.detachedHead=false checkout $3; then
rm -rf "$2"
fi
else
if test -d "$2"; then
set +x
echo "error: '$2' is not a Git repository" 1>&2
exit 1
fi
fi
if ! test -d "$2"; then
git clone $1 "$2"
if ! git -C "$2" -c advice.detachedHead=false checkout $3; then
rm -rf "$2"
exit 1
fi
fi
}
download_by_hash() {
DOWNLOAD_COMMAND="curl -Lo"
if ! command -v "${DOWNLOAD_COMMAND%% *}" >/dev/null 2>&1; then
DOWNLOAD_COMMAND="wget -O"
if ! command -v "${DOWNLOAD_COMMAND%% *}" >/dev/null 2>&1; then
set +x
echo "error: Neither curl nor wget found" 1>&2
exit 1
fi
fi
SHA256_COMMAND="sha256sum"
if ! command -v "${SHA256_COMMAND%% *}" >/dev/null 2>&1; then
SHA256_COMMAND="sha256"
if ! command -v "${SHA256_COMMAND%% *}" >/dev/null 2>&1; then
set +x
echo "error: Cannot find sha256(sum) command" 1>&2
exit 1
fi
fi
if ! test -f "$2" || ! $SHA256_COMMAND "$2" | grep $3 >/dev/null 2>&1; then
rm -f "$2"
mkdir -p "$2" && rm -rf "$2"
$DOWNLOAD_COMMAND "$2" $1
if ! $SHA256_COMMAND "$2" | grep $3 >/dev/null 2>&1; then
set +x
echo "error: Cannot download file '$2' by hash" 1>&2
echo "incorrect hash:" 1>&2
$SHA256_COMMAND "$2" 1>&2
rm -f "$2"
exit 1
fi
fi
}
if ! test -f version; then
clone_repo_commit \
https://codeberg.org/OSDev/freestnd-c-hdrs-0bsd.git \
freestnd-c-hdrs \
097259a899d30f0a4b7a694de2de5fdda942e923
clone_repo_commit \
https://codeberg.org/OSDev/cc-runtime.git \
cc-runtime \
dae79833b57a01b9fd3e359ee31def69f5ae899b
cp cc-runtime/src/cc-runtime.c common/cc-runtime.s2.c
cp cc-runtime/src/cc-runtime.c decompressor/cc-runtime.c
clone_repo_commit \
https://codeberg.org/Limine/limine-protocol.git \
limine-protocol \
e42d010a761e4e9ac6bad1b578110ba72c61e1d9
clone_repo_commit \
https://codeberg.org/PicoEFI/PicoEFI.git \
picoefi \
8b79fdaa72ee548a8ea24e3dc4d87bf281312865
clone_repo_commit \
https://github.com/jibsen/tinf.git \
tinf \
57ffa1f1d5e3dde19011b2127bd26d01689b694b
mkdir -p decompressor/tinf
cp tinf/src/tinf.h tinf/src/tinflate.c tinf/src/tinfgzip.c decompressor/tinf/
patch -p0 < decompressor/tinf.patch
rm -f decompressor/tinf/*.orig
clone_repo_commit \
https://codeberg.org/Mintsuki/Flanterm.git \
flanterm \
5d93c648bee81ce7d19f7a6671ec2bc3aaa834f2
download_by_hash \
https://github.com/nothings/stb/raw/5c205738c191bcb0abc65c4febfa9bd25ff35234/stb_image.h \
common/lib/stb_image.h.nopatch \
594c2fe35d49488b4382dbfaec8f98366defca819d916ac95becf3e75f4200b3
cp common/lib/stb_image.h.nopatch common/lib/stb_image.h
patch -p0 < common/stb_image.patch
rm -f common/lib/stb_image.h.orig
clone_repo_commit \
https://codeberg.org/OSDev/libfdt.git \
libfdt \
7bf94e6347129d17eca263112296ad170dec28a9
fi
# Create timestamps file
if test -d .git && git log -1 >/dev/null 2>&1; then
cat >timestamps <<EOF
REGEN_DATE="$(git log -1 --pretty=%cd --date='format:%B %Y')"
SOURCE_DATE_EPOCH="$(git log -1 --pretty=%ct)"
SOURCE_DATE_EPOCH_TOUCH="$(git log -1 --pretty=%cI | head -c 16 | sed 's/-//g;s/T//g;s/://g')"
EOF
else
if ! test -f timestamps; then
cat >timestamps <<EOF
REGEN_DATE="UNVERSIONED"
SOURCE_DATE_EPOCH="1546297200"
SOURCE_DATE_EPOCH_TOUCH="201901010000"
EOF
fi
fi
for auxfile in $AUXFILES; do
rm -f build-aux/$auxfile
done
$AUTORECONF -fvi -Wall
# Older versions of autoreconf have a bug where they do not
# install auxiliary files, sometimes... Check if that is the
# case and work around...
for auxfile in $AUXFILES; do
if ! test -f build-aux/$auxfile; then
if ! $AUTOMAKE --print-libdir >/dev/null 2>&1; then
set +x
echo "error: Broken autoreconf detected, but missing or broken automake." 1>&2
echo " Please make sure automake is installed and working." 1>&2
exit 1
fi
AUTOMAKE_LIBDIR="$($AUTOMAKE --print-libdir)"
if test -z "$AUTOMAKE_LIBDIR"; then
# Assume `true` was passed as $AUTOMAKE
continue
fi
mkdir -p build-aux
cp -v "$AUTOMAKE_LIBDIR/$auxfile" build-aux/
chmod +x build-aux/$auxfile
fi
done
set +x
printf "\nSource tree bootstrapped successfully!\n"

675
limine/common/common.mk Normal file
View file

@ -0,0 +1,675 @@
.SUFFIXES:
override SRCDIR := $(shell pwd -P)
override SPACE := $(subst ,, )
override MKESCAPE = $(subst $(SPACE),\ ,$(1))
override SHESCAPE = $(subst ','\'',$(1))
override OBJESCAPE = $(subst .a ,.a' ',$(subst .o ,.o' ',$(call SHESCAPE,$(1))))
override CC_FOR_TARGET_IS_CLANG := $(shell ! $(CC_FOR_TARGET) --version 2>/dev/null | $(GREP) -q '^Target: '; echo $$?)
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 \
-ffunction-sections \
-fdata-sections \
-fno-stack-protector \
-fno-stack-check \
-fno-omit-frame-pointer \
-fno-strict-aliasing \
-fno-lto
override CPPFLAGS_FOR_TARGET := \
-I . \
-I libc-compat \
-I ../limine-protocol/include \
-I ../flanterm/src \
-I ../libfdt/src \
-I '$(call SHESCAPE,$(BUILDDIR))/..' \
-isystem ../freestnd-c-hdrs/include \
$(CPPFLAGS_FOR_TARGET) \
-DCOM_OUTPUT=$(COM_OUTPUT) \
-DE9_OUTPUT=$(E9_OUTPUT) \
-DFLANTERM_IN_FLANTERM \
-MMD \
-MP
$(call MKESCAPE,$(BUILDDIR))/libfdt/src/fdt_overlay.o: override CFLAGS_FOR_TARGET += \
-Wno-unused-parameter
$(call MKESCAPE,$(BUILDDIR))/flanterm/src/flanterm_backends/fb.o: override CPPFLAGS_FOR_TARGET += \
-DFLANTERM_FB_DISABLE_BUMP_ALLOC
override NASMFLAGS_FOR_TARGET += \
-g \
-Wall \
-w-unknown-warning \
-w-reloc \
$(WERROR_FLAG)
override NASMFLAGS_FOR_TARGET := \
$(patsubst -g,-g -F dwarf,$(NASMFLAGS_FOR_TARGET))
ifeq ($(TARGET),bios)
ifeq ($(CC_FOR_TARGET_IS_CLANG),1)
override CC_FOR_TARGET += \
-target i686-unknown-none-elf
endif
override CFLAGS_FOR_TARGET += \
-fno-PIC \
-m32 \
-march=i686 \
-mabi=sysv \
-mno-80387 \
-mno-mmx
override CPPFLAGS_FOR_TARGET := \
$(CPPFLAGS_FOR_TARGET) \
-DBIOS
override NASMFLAGS_FOR_TARGET := \
-f elf32 \
$(NASMFLAGS_FOR_TARGET) \
-DIA32_TARGET \
-DBIOS
endif
ifeq ($(TARGET),uefi-x86-64)
ifeq ($(CC_FOR_TARGET_IS_CLANG),1)
override CC_FOR_TARGET += \
-target x86_64-unknown-none-elf
endif
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-m64 \
-march=x86-64 \
-mabi=sysv \
-mno-80387 \
-mno-mmx \
-mno-sse \
-mno-sse2 \
-mno-red-zone
override CPPFLAGS_FOR_TARGET := \
-I ../picoefi/inc \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
override NASMFLAGS_FOR_TARGET := \
-f elf64 \
$(NASMFLAGS_FOR_TARGET) \
-DX86_64_TARGET \
-DUEFI
endif
ifeq ($(TARGET),uefi-ia32)
ifeq ($(CC_FOR_TARGET_IS_CLANG),1)
override CC_FOR_TARGET += \
-target i686-unknown-none-elf
endif
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-m32 \
-march=i686 \
-mabi=sysv \
-mno-80387 \
-mno-mmx
override CPPFLAGS_FOR_TARGET := \
-I ../picoefi/inc \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
override NASMFLAGS_FOR_TARGET := \
-f elf32 \
$(NASMFLAGS_FOR_TARGET) \
-DIA32_TARGET \
-DUEFI
endif
ifeq ($(TARGET),uefi-aarch64)
ifeq ($(CC_FOR_TARGET_IS_CLANG),1)
override CC_FOR_TARGET += \
-target aarch64-unknown-none-elf
endif
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-mcpu=generic \
-march=armv8-a+nofp+nosimd \
-mgeneral-regs-only
override CPPFLAGS_FOR_TARGET := \
-I ../picoefi/inc \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
endif
ifeq ($(TARGET),uefi-riscv64)
ifeq ($(CC_FOR_TARGET_IS_CLANG),1)
override CC_FOR_TARGET += \
-target riscv64-unknown-none-elf
endif
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-march=rv64imac_zicsr_zifencei \
-mabi=lp64 \
-mno-relax
override CPPFLAGS_FOR_TARGET := \
-I ../picoefi/inc \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
endif
ifeq ($(TARGET),uefi-loongarch64)
ifeq ($(CC_FOR_TARGET_IS_CLANG),1)
override CC_FOR_TARGET += \
-target loongarch64-unknown-none-elf
endif
override CFLAGS_FOR_TARGET += \
-fPIE \
-fshort-wchar \
-march=loongarch64 \
-mabi=lp64s \
-mfpu=none \
-msimd=none
override CPPFLAGS_FOR_TARGET := \
-I ../picoefi/inc \
$(CPPFLAGS_FOR_TARGET) \
-DUEFI
endif
override LDFLAGS_FOR_TARGET += \
-nostdlib \
-z max-page-size=0x1000 \
--gc-sections
ifeq ($(TARGET),bios)
override LDFLAGS_FOR_TARGET += \
-m elf_i386 \
-static \
--build-id=sha1
endif
ifeq ($(TARGET),uefi-x86-64)
override LDFLAGS_FOR_TARGET += \
-m elf_x86_64 \
-pie \
-z text
endif
ifeq ($(TARGET),uefi-ia32)
override LDFLAGS_FOR_TARGET += \
-m elf_i386 \
-pie \
-z text
endif
ifeq ($(TARGET),uefi-aarch64)
override LDFLAGS_FOR_TARGET += \
-m aarch64elf \
-pie \
-z text
endif
ifeq ($(TARGET),uefi-riscv64)
override LDFLAGS_FOR_TARGET += \
-m elf64lriscv \
--no-relax \
-pie \
-z text
endif
ifeq ($(TARGET),uefi-loongarch64)
override LDFLAGS_FOR_TARGET += \
-m elf64loongarch \
-pie \
-z text
endif
ifeq ($(TARGET),bios)
override C_FILES := $(shell cd .. && find common flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort)
override S_FILES := $(shell cd .. && find common -type f -name '*.S' | LC_ALL=C sort)
override ASMX86_FILES := $(shell cd .. && find common -type f -name '*.asm_x86' | LC_ALL=C sort)
override ASM32_FILES := $(shell cd .. && find common -type f -name '*.asm_ia32' | LC_ALL=C sort)
override ASMB_FILES := $(shell cd .. && find common -type f -name '*.asm_bios_ia32' | LC_ALL=C sort)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.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 C_FILES := $(shell cd .. && find common picoefi/x86_64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort)
override S_FILES := $(shell cd .. && find common picoefi/x86_64 -type f -name '*.S' | LC_ALL=C sort)
override ASMX86_FILES := $(shell cd .. && find common -type f -name '*.asm_x86' | LC_ALL=C sort)
override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_x86_64' | LC_ALL=C sort)
override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_x86_64' | LC_ALL=C sort)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.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 C_FILES := $(shell cd .. && find common picoefi/ia32 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort)
override S_FILES := $(shell cd .. && find common picoefi/ia32 -type f -name '*.S' | LC_ALL=C sort)
override ASMX86_FILES := $(shell cd .. && find common -type f -name '*.asm_x86' | LC_ALL=C sort)
override ASM32_FILES := $(shell cd .. && find common -type f -name '*.asm_ia32' | LC_ALL=C sort)
override ASM32U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_ia32' | LC_ALL=C sort)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASM32U_FILES:.asm_uefi_ia32=.o) $(ASMX86_FILES:.asm_x86=.o))
endif
ifeq ($(TARGET),uefi-aarch64)
override C_FILES := $(shell cd .. && find common picoefi/aarch64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort)
override S_FILES := $(shell cd .. && find common picoefi/aarch64 -type f -name '*.S' | LC_ALL=C sort)
override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_aarch64' | LC_ALL=C sort)
override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_aarch64' | LC_ALL=C sort)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_aarch64=.o) $(ASM64U_FILES:.asm_uefi_aarch64=.o))
endif
ifeq ($(TARGET),uefi-riscv64)
override C_FILES := $(shell cd .. && find common picoefi/riscv64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort)
override S_FILES := $(shell cd .. && find common picoefi/riscv64 -type f -name '*.S' | LC_ALL=C sort)
override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_riscv64' | LC_ALL=C sort)
override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_riscv64' | LC_ALL=C sort)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_riscv64=.o) $(ASM64U_FILES:.asm_uefi_riscv64=.o))
endif
ifeq ($(TARGET),uefi-loongarch64)
override C_FILES := $(shell cd .. && find common picoefi/loongarch64 flanterm/src libfdt/src -type f -name '*.c' | LC_ALL=C sort)
override S_FILES := $(shell cd .. && find common picoefi/loongarch64 -type f -name '*.S' | LC_ALL=C sort)
override ASM64_FILES := $(shell cd .. && find common -type f -name '*.asm_loongarch64' | LC_ALL=C sort)
override ASM64U_FILES := $(shell cd .. && find common -type f -name '*.asm_uefi_loongarch64' | LC_ALL=C sort)
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(S_FILES:.S=.o) $(ASM64_FILES:.asm_loongarch64=.o) $(ASM64U_FILES:.asm_uefi_loongarch64=.o))
endif
override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d) $(C_FILES:.S=.d))
.PHONY: all
ifeq ($(TARGET),bios)
all: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys $(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz
endif
ifeq ($(TARGET),uefi-x86-64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI
endif
ifeq ($(TARGET),uefi-ia32)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI
endif
ifeq ($(TARGET),uefi-aarch64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI
endif
ifeq ($(TARGET),uefi-riscv64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI
endif
ifeq ($(TARGET),uefi-loongarch64)
all: $(call MKESCAPE,$(BUILDDIR))/BOOTLOONGARCH64.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)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld'
$(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/empty'
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld'
$(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' -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) $(call MKESCAPE,$(BUILDDIR))/stage2.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/empty'
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' -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) $(call MKESCAPE,$(BUILDDIR))/stage2.map.o $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/empty'
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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) $(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)'
endif
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,$@)'
dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null
$(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: $(OBJ)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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,$@)'
dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null
$(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: $(OBJ)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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,$@)'
dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null
$(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: $(OBJ)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-loongarch64)
$(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))/BOOTLOONGARCH64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
chmod -x '$(call SHESCAPE,$@)'
dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_loongarch64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_loongarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_loongarch64.ld.in
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_loongarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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,$@)'
dd if=/dev/zero of='$(call SHESCAPE,$@)' bs=4096 count=0 seek=$$(( ($$(wc -c < '$(call SHESCAPE,$@)') + 4095) / 4096 )) 2>/dev/null
$(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: $(OBJ)
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -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: $(OBJ) $(call MKESCAPE,$(BUILDDIR))/full.map.o
$(MAKE) -f common.mk '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
$(LD_FOR_TARGET) \
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
$(LDFLAGS_FOR_TARGET) '$(call OBJESCAPE,$^)' -o '$(call SHESCAPE,$@)'
endif
-include $(HEADER_DEPS)
$(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,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.S
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
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
ifeq ($(TARGET),bios)
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_bios_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif
ifeq ($(TARGET),uefi-x86-64)
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86_64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_x86_64
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -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-loongarch64)
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_loongarch64
$(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_loongarch64
$(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,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_uefi_ia32
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
$(call MKESCAPE,$(BUILDDIR))/%.o: ../%.asm_x86
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
nasm '$(call SHESCAPE,$<)' $(NASMFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
endif

View file

@ -0,0 +1,220 @@
// 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));
// Safe unaligned load/store helpers for architectures with strict alignment (ARM64, RISC-V)
static inline uint64_t load64_le(const void *src) {
uint64_t val;
memcpy(&val, src, sizeof(val));
return val;
}
static inline void store64_le(void *dst, uint64_t val) {
memcpy(dst, &val, sizeof(val));
}
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] = load64_le(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] ^= load64_le((uint8_t *)&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 += BLAKE2B_BLOCK_BYTES;
in_len -= BLAKE2B_BLOCK_BYTES;
}
}
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++) {
store64_le(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);
}

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

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

View file

@ -0,0 +1,846 @@
#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
#if defined (BIOS)
struct dpte {
uint16_t io_port;
uint16_t control_port;
uint8_t head_reg_upper;
uint8_t bios_vendor_specific;
uint8_t irq_info;
uint8_t block_count_multiple;
uint8_t dma_info;
uint8_t pio_info;
uint16_t flags;
uint16_t reserved;
uint8_t revision;
uint8_t checksum;
} __attribute__((packed));
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;
uint16_t dpte_off;
uint16_t dpte_seg;
} __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 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) {
// Disk count (only non-removable) at 0040:0075
uint8_t bda_disk_count = mminb(rm_desegment(0x0040, 0x0075));
int optical_indices = 1, hdd_indices = 1, consumed_bda_disks = 0;
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;
}
bool is_removable = drive_params.info_flags & (1 << 2);
struct dpte *dpte = NULL;
if (drive_params.buf_size >= 0x1e
&& (drive_params.dpte_seg != 0x0000 || drive_params.dpte_off != 0x0000)
&& (drive_params.dpte_seg != 0xffff || drive_params.dpte_off != 0xffff)) {
dpte = (void *)rm_desegment(drive_params.dpte_seg, drive_params.dpte_off);
if ((dpte->control_port & 0xff00) != 0xa000) {
// Check for removable (5) or ATAPI (6)
is_removable = is_removable || ((dpte->flags & (1 << 5)) || (dpte->flags & (1 << 6)));
}
}
struct volume *block = ext_mem_alloc(sizeof(struct volume));
block->drive = drive;
block->partition = 0;
block->first_sect = 0;
block->max_partition = -1;
if (!detect_sector_size(block)) {
pmm_free(block, sizeof(struct volume));
continue;
}
// Normalize sect_count to 512-byte sectors for consistency with partitions
// Preserve (uint64_t)-1 sentinel value (means "unknown size")
if (drive_params.lba_count == (uint64_t)-1 || drive_params.lba_count == 0) {
block->sect_count = (uint64_t)-1;
} else {
block->sect_count = drive_params.lba_count * (block->sector_size / 512);
}
// Detect optical drives via DPTE ATAPI bit (bit 6) or sector size heuristic
bool is_atapi = (dpte != NULL && (dpte->flags & (1 << 6)));
block->is_optical = is_atapi || (block->sector_size == 2048 && is_removable);
if (!is_removable && !block->is_optical) {
if (consumed_bda_disks == bda_disk_count) {
pmm_free(block, sizeof(struct volume));
continue;
}
consumed_bda_disks++;
}
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;
}
volume_index = pmm_realloc(
volume_index,
volume_index_i * sizeof(void *),
(volume_index_i + 1) * sizeof(void *)
);
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) {
pmm_free(p, sizeof(struct volume));
break;
}
if (ret == NO_PARTITION) {
pmm_free(p, sizeof(struct volume));
continue;
}
volume_index = pmm_realloc(
volume_index,
volume_index_i * sizeof(void *),
(volume_index_i + 1) * sizeof(void *)
);
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;
}
#define UNIQUE_SECTOR_POOL_SIZE 65536
static uint8_t *unique_sector_pool;
static bool unique_sectors_calculated = false;
static void find_unique_sectors(void);
static struct volume *volume_by_unique_sector(void *b2b) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->unique_sector_valid == false) {
continue;
}
if (memcmp(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES) == 0) {
return volume_index[i];
}
}
return NULL;
}
// Search for matching hash including invalidated volumes (for collision detection)
static struct volume *volume_by_sector_hash(void *b2b) {
for (size_t i = 0; i < volume_index_i; i++) {
if (volume_index[i]->unique_sector_valid == false
&& memcmp(volume_index[i]->unique_sector_b2b, (uint8_t[BLAKE2B_OUT_BYTES]){0}, BLAKE2B_OUT_BYTES) == 0) {
// Hash was never set, skip
continue;
}
if (memcmp(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES) == 0) {
return volume_index[i];
}
}
return NULL;
}
static bool is_efi_handle_to_skip(EFI_HANDLE efi_handle) {
EFI_STATUS status;
EFI_GUID dp_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_DEVICE_PATH_PROTOCOL *dp = NULL;
EFI_GUID guids_to_skip[] = {
// skip 7CCE9C94-983F-4D0A-8143-B6C05545B223 since it is apparently used by exposed
// ROM devices that we do not want to touch
// (see https://github.com/limine-bootloader/limine/issues/521#issuecomment-3160168795)
{0x7CCE9C94, 0x983F, 0x4D0A, {0x81, 0x43, 0xB6, 0xC0, 0x55, 0x45, 0xB2, 0x23}},
};
status = gBS->HandleProtocol(efi_handle, &dp_guid, (void **)&dp);
if (status) {
return false;
}
for (;;) {
if (dp->Type == END_DEVICE_PATH_TYPE && dp->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
break;
}
uint16_t len = *(uint16_t *)dp->Length;
// Validate minimum device path node size before accessing type-specific data
if (len < sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
break; // Malformed device path node
}
if (dp->Type == HARDWARE_DEVICE_PATH && dp->SubType == HW_VENDOR_DP) {
// Vendor device path must be large enough to contain a GUID
if (len >= sizeof(EFI_DEVICE_PATH_PROTOCOL) + sizeof(EFI_GUID)) {
EFI_GUID *vendor_guid = (void *)dp + sizeof(EFI_DEVICE_PATH_PROTOCOL);
for (size_t i = 0; i < SIZEOF_ARRAY(guids_to_skip); i++) {
if (memcmp(vendor_guid, &guids_to_skip[i], sizeof(EFI_GUID)) == 0) {
return true;
}
}
}
}
dp = (void *)dp + len;
}
return false;
}
static bool is_efi_handle_optical(EFI_HANDLE efi_handle) {
EFI_STATUS status;
EFI_GUID dp_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_DEVICE_PATH_PROTOCOL *dp = NULL;
status = gBS->HandleProtocol(efi_handle, &dp_guid, (void **)&dp);
if (status) {
return false;
}
for (;;) {
if (dp->Type == END_DEVICE_PATH_TYPE && dp->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {
break;
}
if (dp->Type == MEDIA_DEVICE_PATH && dp->SubType == MEDIA_CDROM_DP) {
return true;
}
uint16_t len = *(uint16_t *)dp->Length;
if (len < sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
break; // Malformed device path node
}
dp = (void *)dp + len;
}
return false;
}
static EFI_DEVICE_PATH_PROTOCOL *get_device_path(EFI_HANDLE efi_handle) {
EFI_STATUS status;
EFI_GUID dp_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_DEVICE_PATH_PROTOCOL *dp = NULL;
status = gBS->HandleProtocol(efi_handle, &dp_guid, (void **)&dp);
if (status) {
return NULL;
}
return dp;
}
// Compare device paths up to (but not including) partition nodes
static bool device_paths_match_disk(EFI_DEVICE_PATH_PROTOCOL *dp1,
EFI_DEVICE_PATH_PROTOCOL *dp2) {
if (dp1 == NULL || dp2 == NULL) {
return false;
}
while (!IsDevicePathEnd(dp1) && !IsDevicePathEnd(dp2)) {
// Stop at partition nodes
if (dp1->Type == MEDIA_DEVICE_PATH &&
(dp1->SubType == MEDIA_HARDDRIVE_DP || dp1->SubType == MEDIA_CDROM_DP)) {
break;
}
if (dp2->Type == MEDIA_DEVICE_PATH &&
(dp2->SubType == MEDIA_HARDDRIVE_DP || dp2->SubType == MEDIA_CDROM_DP)) {
break;
}
uint16_t len1 = DevicePathNodeLength(dp1);
uint16_t len2 = DevicePathNodeLength(dp2);
if (len1 != len2) {
return false;
}
if (len1 < sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
return false;
}
if (memcmp(dp1, dp2, len1) != 0) {
return false;
}
dp1 = (void *)dp1 + len1;
dp2 = (void *)dp2 + len2;
}
return true;
}
static struct volume *volume_by_device_path(EFI_HANDLE query_handle) {
EFI_DEVICE_PATH_PROTOCOL *query_dp = get_device_path(query_handle);
if (query_dp == NULL) {
return NULL;
}
for (size_t i = 0; i < volume_index_i; i++) {
EFI_DEVICE_PATH_PROTOCOL *vol_dp = get_device_path(volume_index[i]->efi_handle);
if (vol_dp == NULL) {
continue;
}
if (device_paths_match_disk(query_dp, vol_dp)) {
// Convert first_sect from 512-byte sectors to device LBAs
int sector_size = volume_index[i]->sector_size;
if ((volume_index[i]->first_sect * 512) % sector_size) {
continue; // Misaligned, skip this volume
}
uint64_t first_sect_lba = (volume_index[i]->first_sect * 512) / sector_size;
EFI_DEVICE_PATH_PROTOCOL *qp = query_dp;
while (!IsDevicePathEnd(qp)) {
if (qp->Type == MEDIA_DEVICE_PATH && qp->SubType == MEDIA_HARDDRIVE_DP) {
uint16_t len = DevicePathNodeLength(qp);
// UEFI spec size is 42 bytes, but sizeof() may be larger due to padding
if (len < 42) {
break;
}
HARDDRIVE_DEVICE_PATH *query_hd = (HARDDRIVE_DEVICE_PATH *)qp;
if (first_sect_lba == query_hd->PartitionStart) {
return volume_index[i];
}
break;
}
if (qp->Type == MEDIA_DEVICE_PATH && qp->SubType == MEDIA_CDROM_DP) {
uint16_t len = DevicePathNodeLength(qp);
if (len < sizeof(CDROM_DEVICE_PATH)) {
break;
}
CDROM_DEVICE_PATH *query_cd = (CDROM_DEVICE_PATH *)qp;
if (first_sect_lba == query_cd->PartitionStart) {
return volume_index[i];
}
break;
}
uint16_t len = DevicePathNodeLength(qp);
if (len < sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
break;
}
qp = (void *)qp + len;
}
if (IsDevicePathEnd(qp) && volume_index[i]->partition == 0) {
return volume_index[i];
}
}
}
return NULL;
}
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;
if (is_efi_handle_to_skip(efi_handle)) {
return NULL;
}
status = gBS->HandleProtocol(efi_handle, &block_io_guid, (void **)&block_io);
if (status) {
return pxe_from_efi_handle(efi_handle);
}
// Try device path matching first (primary method)
struct volume *ret = volume_by_device_path(efi_handle);
if (ret != NULL) {
return ret;
}
// Fallback to unique sector matching
uint64_t bdev_size = ((uint64_t)block_io->Media->LastBlock + 1) * (uint64_t)block_io->Media->BlockSize;
if (bdev_size >= UNIQUE_SECTOR_POOL_SIZE) {
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId,
0,
UNIQUE_SECTOR_POOL_SIZE,
unique_sector_pool);
if (status == 0) {
find_unique_sectors();
uint8_t b2b[BLAKE2B_OUT_BYTES];
blake2b(b2b, unique_sector_pool, UNIQUE_SECTOR_POOL_SIZE);
ret = volume_by_unique_sector(b2b);
if (ret != NULL) {
// Verify size, block size, and partition status match
if (block_io->Media->BlockSize == (uint32_t)ret->sector_size
&& bdev_size == ret->sect_count * 512
&& block_io->Media->LogicalPartition == (ret->partition != 0)) {
return ret;
}
}
}
}
return NULL;
}
static void find_unique_sectors(void) {
if (unique_sectors_calculated) {
return;
}
unique_sectors_calculated = true;
EFI_STATUS status;
for (size_t i = 0; i < volume_index_i; i++) {
if ((volume_index[i]->first_sect * 512) % volume_index[i]->sector_size) {
continue;
}
size_t first_sect = (volume_index[i]->first_sect * 512) / volume_index[i]->sector_size;
// sect_count is always in 512-byte sectors
if (volume_index[i]->sect_count * 512 < UNIQUE_SECTOR_POOL_SIZE) {
continue;
}
status = volume_index[i]->block_io->ReadBlocks(
volume_index[i]->block_io,
volume_index[i]->block_io->Media->MediaId,
first_sect,
UNIQUE_SECTOR_POOL_SIZE,
unique_sector_pool);
if (status != 0) {
continue;
}
uint8_t b2b[BLAKE2B_OUT_BYTES];
blake2b(b2b, unique_sector_pool, UNIQUE_SECTOR_POOL_SIZE);
// Check for collision BEFORE storing hash (so we don't find ourselves)
// This searches all volumes including previously invalidated ones
struct volume *collision = volume_by_sector_hash(b2b);
// Always store the hash so future volumes can detect collisions
memcpy(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES);
if (collision == NULL) {
volume_index[i]->unique_sector_valid = true;
continue;
}
// Collision found - invalidate both volumes
collision->unique_sector_valid = false;
volume_index[i]->unique_sector_valid = false;
}
}
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;
unique_sector_pool = ext_mem_alloc(UNIQUE_SECTOR_POOL_SIZE);
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.");
}
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;
if (is_efi_handle_to_skip(handles[i])) {
continue;
}
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;
// Read test to ensure device is responsive (skipping this causes hangs on some systems)
status = drive->ReadBlocks(drive, drive->Media->MediaId, 0, 4096, unique_sector_pool);
if (status) {
continue;
}
if (drive->Media->BlockSize == 0) {
continue;
}
if (drive->Media->LastBlock == UINT64_MAX) {
continue;
}
struct volume *block = ext_mem_alloc(sizeof(struct volume));
bool is_optical = is_efi_handle_optical(handles[i]) ||
(drive->Media->ReadOnly && drive->Media->BlockSize == 2048);
if (is_optical) {
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;
// Normalize sect_count to 512-byte sectors for consistency with partitions
block->sect_count = (drive->Media->LastBlock + 1) * (drive->Media->BlockSize / 512);
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;
}
volume_index = pmm_realloc(
volume_index,
volume_index_i * sizeof(void *),
(volume_index_i + 1) * sizeof(void *)
);
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));
volume_index = pmm_realloc(
volume_index,
volume_index_i * sizeof(void *),
(volume_index_i + 1) * sizeof(void *)
);
volume_index[volume_index_i++] = p;
block->max_partition++;
}
}
find_part_handles(handles, handle_count);
pmm_free(handles, handles_size);
}
#endif

View file

@ -0,0 +1,85 @@
#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:
pmm_free(buf, sizeof(struct edid_info_struct));
printv("edid: Could not fetch EDID data.\n");
return NULL;
success:
printv("edid: Success.\n");
return buf;
}
#endif

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

380
limine/common/drivers/gop.c Normal file
View file

@ -0,0 +1,380 @@
#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;
*mask = 0;
if (linear_mask == 0) {
return;
}
while ((linear_mask & 1) == 0) {
(*shift)++;
linear_mask >>= 1;
}
while ((linear_mask & 1) == 1) {
(*mask)++;
linear_mask >>= 1;
}
}
static bool validate_pitch(struct fb_info *ret, size_t mode) {
uint64_t bytes_per_pixel = ret->framebuffer_bpp / 8;
if (bytes_per_pixel == 0
|| ret->framebuffer_pitch % bytes_per_pixel != 0
|| ret->framebuffer_pitch < ret->framebuffer_width * bytes_per_pixel) {
printv("gop: Mode %u has invalid pitch %u (width=%u, bpp=%u), skipping.\n",
(uint32_t)mode, (uint32_t)ret->framebuffer_pitch,
(uint32_t)ret->framebuffer_width, (uint32_t)ret->framebuffer_bpp);
return false;
}
return true;
}
// 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:
if ((mode_info->PixelInformation.RedMask
| mode_info->PixelInformation.GreenMask
| mode_info->PixelInformation.BlueMask
| mode_info->PixelInformation.ReservedMask) == 0) {
return false;
}
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;
if (!validate_pitch(ret, mode)) {
return false;
}
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,
bool *setmode_called) {
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", (uint64_t)mode);
if (mode == gop->Mode->Mode && *setmode_called) {
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", (uint64_t)mode);
return false;
}
*setmode_called = true;
}
// Recalculate pitch from gop->Mode->Info, as some firmware (e.g. Apple
// Macs) report incorrect PixelsPerScanLine via QueryMode.
ret->framebuffer_pitch = gop->Mode->Info->PixelsPerScanLine * (ret->framebuffer_bpp / 8);
if (!validate_pitch(ret, 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 = actual_count;
return ret;
}
#define MAX_PRESET_MODES 128
no_unwind static int preset_modes[MAX_PRESET_MODES];
no_unwind static bool setmode_called[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;
setmode_called[i] = false;
}
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;
}
setmode_called[i] = true;
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, &setmode_called[i])) {
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, &setmode_called[i])) {
goto success;
}
}
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;
}
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

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

View file

@ -0,0 +1,50 @@
#if defined (BIOS)
#include <stdint.h>
#include <stdbool.h>
#include <lib/misc.h>
#include <drivers/serial.h>
#include <sys/cpu.h>
static bool serial_initialised = false;
uint32_t serial_baudrate;
static void serial_initialise(void) {
if (serial_initialised || !serial) {
return;
}
// Init com1
outb(0x3f8 + 3, 0x00);
outb(0x3f8 + 1, 0x00);
outb(0x3f8 + 3, 0x80);
uint16_t divisor = (uint16_t)(115200 / serial_baudrate);
outb(0x3f8 + 0, divisor & 0xff);
outb(0x3f8 + 1, (divisor >> 8) & 0xff);
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

View file

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

364
limine/common/drivers/vbe.c Normal file
View file

@ -0,0 +1,364 @@
#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[190];
} __attribute__((packed));
static bool 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);
if ((r.eax & 0xff00) >> 8 != 0
|| (r.eax & 0x00ff) != 0x4f) {
return false;
}
return true;
}
static bool 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);
if ((r.eax & 0xff00) >> 8 != 0
|| (r.eax & 0x00ff) != 0x4f) {
return false;
}
return true;
}
static bool 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);
if ((r.eax & 0xff00) >> 8 != 0
|| (r.eax & 0x00ff) != 0x4f) {
return false;
}
return true;
}
// Maximum number of video modes to enumerate to prevent infinite loops
// from corrupted VBE mode lists without a proper 0xffff terminator
#define VBE_MAX_MODES 512
struct fb_info *vbe_get_mode_list(size_t *count) {
struct vbe_info_struct vbe_info;
if (!get_vbe_info(&vbe_info)) {
return NULL;
}
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; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) {
continue;
}
// 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;
uint16_t pitch = (vbe_info.version_maj < 3)
? vbe_mode_info.bytes_per_scanline
: vbe_mode_info.lin_bytes_per_scanline;
uint16_t bytes_per_pixel = vbe_mode_info.bpp / 8;
if (bytes_per_pixel == 0
|| pitch % bytes_per_pixel != 0
|| pitch < (uint32_t)vbe_mode_info.res_x * bytes_per_pixel)
continue;
modes_count++;
}
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
for (size_t i = 0, j = 0; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) {
continue;
}
// 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;
uint16_t pitch = (vbe_info.version_maj < 3)
? vbe_mode_info.bytes_per_scanline
: vbe_mode_info.lin_bytes_per_scanline;
uint16_t bytes_per_pixel = vbe_mode_info.bpp / 8;
if (bytes_per_pixel == 0
|| pitch % bytes_per_pixel != 0
|| pitch < (uint32_t)vbe_mode_info.res_x * bytes_per_pixel)
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;
if (!get_vbe_info(&vbe_info)) {
return false;
}
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; i < VBE_MAX_MODES && vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
if (!get_vbe_mode_info(&vbe_mode_info, vid_modes[i])) {
continue;
}
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])) {
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;
}
uint16_t bytes_per_pixel = ret->framebuffer_bpp / 8;
if (bytes_per_pixel == 0
|| ret->framebuffer_pitch % bytes_per_pixel != 0
|| ret->framebuffer_pitch < (uint32_t)ret->framebuffer_width * bytes_per_pixel) {
printv("vbe: Mode %x has invalid pitch %u (width=%u, bpp=%u), skipping.\n",
vid_modes[i], (uint32_t)ret->framebuffer_pitch,
(uint32_t)ret->framebuffer_width, (uint32_t)ret->framebuffer_bpp);
continue;
}
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

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,369 @@
#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];
ctx->back_buffer[new_y * VD_COLS + new_x * 2 + 1] = ctx->back_buffer[old_y * VD_COLS + old_x * 2 + 1];
}
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_size_t);
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.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,10 @@
.section .text
.global efi_main
.extern uefi_entry
efi_main:
move $fp, $r0
move $ra, $r0
b uefi_entry
.section .note.GNU-stack,"",%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

134
limine/common/entry.s2.c Normal file
View file

@ -0,0 +1,134 @@
#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 (true
&& (stage3 = fopen(part, "/boot/limine/limine-bios.sys")) == NULL
&& (stage3 = fopen(part, "/boot/limine-bios.sys")) == NULL
&& (stage3 = fopen(part, "/limine/limine-bios.sys")) == NULL
&& (stage3 = fopen(part, "/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");
}
calibrate_tsc();
uint64_t usec_at_entry = rdtsc_usec();
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();
usec_at_bootloader_entry = usec_at_entry;
stage3_common();
}
#endif

202
limine/common/entry.s3.c Normal file
View file

@ -0,0 +1,202 @@
#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 <lib/bli.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/getchar.h>
#include <sys/cpu.h>
void stage3_common(void);
#if defined (UEFI)
extern symbol __slide, __image_base, __image_end;
extern symbol _start;
noreturn void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
efi_image_handle = ImageHandle;
calibrate_tsc();
usec_at_bootloader_entry = rdtsc_usec();
EFI_STATUS status;
const char *deferred_error = NULL;
#if defined (__x86_64__)
if ((uintptr_t)__slide >= 0x100000000) {
size_t image_size = ALIGN_UP((uintptr_t)__image_end - (uintptr_t)__image_base, 4096);
size_t image_size_pages = ALIGN_UP((size_t)image_size, 4096) / 4096;
size_t new_base;
for (new_base = 0x1000; new_base + (size_t)image_size < 0x100000000; new_base += 0x1000) {
EFI_PHYSICAL_ADDRESS _new_base = (EFI_PHYSICAL_ADDRESS)new_base;
status = gBS->AllocatePages(AllocateAddress, EfiLoaderCode, image_size_pages, &_new_base);
if (status == 0) {
goto new_base_gotten;
}
}
deferred_error = "Limine does not support being loaded above 4GiB and no alternative loading spot found";
goto defer_error;
new_base_gotten:
memcpy((void *)new_base, __slide, (size_t)image_size);
__attribute__((ms_abi))
void (*new_entry_point)(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable);
new_entry_point = (void *)(new_base + ((uintptr_t)_start - (uintptr_t)__slide));
new_entry_point(ImageHandle, SystemTable);
__builtin_unreachable();
}
defer_error:
#endif
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 (deferred_error != NULL) {
panic(false, "%s", deferred_error);
}
#if defined (__x86_64__) || defined (__i386__)
init_gdt();
#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");
print("\n");
print("THIS IS A BUG! Please report this issue upstream.\n");
print("Press any key to continue...\n");
for (;;) {
int ret = pit_sleep_and_quit_on_keypress(65535);
if (ret != 0) {
break;
}
}
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 (
false
#if defined (UEFI)
|| (f = fopen(volume_index[i], "/EFI/limine/limine.conf")) != NULL
|| (f = fopen(volume_index[i], "/EFI/BOOT/limine.conf")) != NULL
#endif
|| (f = fopen(volume_index[i], "/boot/limine/limine.conf")) != NULL
|| (f = fopen(volume_index[i], "/boot/limine.conf")) != NULL
|| (f = fopen(volume_index[i], "/limine/limine.conf")) != NULL
|| (f = fopen(volume_index[i], "/limine.conf")) != NULL
) {
goto opened;
}
case_insensitive_fopen = old_cif;
continue;
opened:
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
#endif
term_notready();
#if defined (UEFI)
init_bli();
#endif
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

11
limine/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

733
limine/common/fs/fat32.s2.c Normal file
View file

@ -0,0 +1,733 @@
#include <stdint.h>
#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 *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;
if (!volume_read(context->part, &bpb, 0, sizeof(struct fat32_bpb))) {
return 1;
}
// Sanity check of bpb
// Checks for FAT12/16
if (strncmp((((void *)&bpb) + 0x36), "FAT", 3) == 0) {
goto signature_valid;
}
// Checks for FAT32
if (strncmp((((void *)&bpb) + 0x52), "FAT", 3) == 0) {
goto signature_valid;
}
// Checks for FAT32 (with 64-bit sector count)
if (strncmp((((void *)&bpb) + 0x03), "FAT32", 5) == 0) {
goto signature_valid;
}
return 1;
signature_valid:;
const uint8_t sector_per_cluster_valid_values[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
for (size_t i = 0; i < SIZEOF_ARRAY(sector_per_cluster_valid_values); i++) {
if (bpb.sectors_per_cluster == sector_per_cluster_valid_values[i]) {
goto sector_per_cluster_valid;
}
}
return 1;
sector_per_cluster_valid:;
const uint16_t bytes_per_sector_valid_values[] = { 512, 1024, 2048, 4096 };
for (size_t i = 0; i < SIZEOF_ARRAY(bytes_per_sector_valid_values); i++) {
if (bpb.bytes_per_sector == bytes_per_sector_valid_values[i]) {
goto bytes_per_sector_valid;
}
}
return 1;
bytes_per_sector_valid:;
// Validate fats_count (typically 1 or 2, but allow up to 4)
if (bpb.fats_count == 0 || bpb.fats_count > 4) {
return 1;
}
// The boot sector itself occupies at least sector 0
if (bpb.reserved_sectors == 0) {
return 1;
}
// 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;
// Calculate total sectors and metadata sectors separately to check for underflow
uint64_t total_sects = bpb.sectors_count_16 ? bpb.sectors_count_16 : bpb.sectors_count_32;
uint64_t sectors_per_fat = bpb.sectors_per_fat_16 ? bpb.sectors_per_fat_16 : bpb.sectors_per_fat_32;
uint64_t metadata_sects = (uint64_t)bpb.reserved_sectors + ((uint64_t)bpb.fats_count * sectors_per_fat) + root_dir_sects;
// Check for underflow before subtraction
if (metadata_sects >= total_sects) {
return 1; // Invalid filesystem: metadata exceeds total size
}
size_t data_sects = total_sects - metadata_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;
if (context->sectors_per_fat == 0) {
return 1;
}
context->root_directory_cluster = bpb.root_directory_cluster;
context->fat_start_lba = bpb.reserved_sectors;
context->root_entries = bpb.root_entries_count;
// FAT12/16 require a non-zero root directory entry count
if (context->type != 32 && context->root_entries == 0) {
return 1;
}
// Calculate root_start with overflow check
uint64_t root_start_64 = (uint64_t)context->reserved_sectors + (uint64_t)context->number_of_fats * context->sectors_per_fat;
if (root_start_64 > UINT32_MAX) {
return 1; // Overflow in root_start calculation
}
context->root_start = (uint32_t)root_start_64;
context->root_size = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), context->bytes_per_sector);
switch (context->type) {
case 12:
case 16:
// Check for overflow in data_start_lba calculation
if (__builtin_add_overflow(context->root_start, context->root_size, &context->data_start_lba)) {
return 1;
}
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) {
uint64_t fat_base = (uint64_t)context->fat_start_lba * context->bytes_per_sector;
uint64_t fat_size = (uint64_t)context->sectors_per_fat * context->bytes_per_sector;
switch (context->type) {
case 12: {
*out = 0;
uint16_t tmp = 0;
uint64_t offset = (uint64_t)cluster + (uint64_t)(cluster / 2);
// Ensure 2-byte reads won't exceed FAT table bounds
if (offset + sizeof(uint16_t) > fat_size) {
return -1;
}
if (!volume_read(context->part, &tmp, fat_base + offset, sizeof(uint16_t))) {
return -1;
}
if (cluster % 2 == 0) {
*out = tmp & 0xfff;
} else {
*out = tmp >> 4;
}
break;
}
case 16: {
*out = 0;
uint64_t offset = (uint64_t)cluster * sizeof(uint16_t);
if (offset + sizeof(uint16_t) > fat_size) {
return -1;
}
if (!volume_read(context->part, out, fat_base + offset, sizeof(uint16_t))) {
return -1;
}
break;
}
case 32: {
uint64_t offset = (uint64_t)cluster * sizeof(uint32_t);
if (offset + sizeof(uint32_t) > fat_size) {
return -1;
}
if (!volume_read(context->part, out, fat_base + offset, sizeof(uint32_t))) {
return -1;
}
*out &= 0x0fffffff;
break;
}
default:
__builtin_unreachable();
}
return 0;
}
// Maximum cluster chain length to prevent memory exhaustion (64MB of cluster chain data)
#define FAT32_MAX_CHAIN_LENGTH (64 * 1024 * 1024 / sizeof(uint32_t))
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;
// Limit chain length to prevent memory exhaustion from malicious filesystems
size_t max_clusters = cluster_limit - 1;
if (max_clusters > FAT32_MAX_CHAIN_LENGTH) {
max_clusters = FAT32_MAX_CHAIN_LENGTH;
}
uint32_t cluster = initial_cluster;
size_t chain_length;
for (chain_length = 1; chain_length <= max_clusters; chain_length++) {
if (read_cluster_from_map(context, cluster, &cluster) != 0) {
return NULL;
}
if (cluster < 0x2 || cluster > cluster_limit)
break;
}
if (chain_length > max_clusters) {
// Circular or corrupted cluster chain detected
return NULL;
}
size_t alloc_size;
if (__builtin_mul_overflow(chain_length, sizeof(uint32_t), &alloc_size)) {
return NULL;
}
uint32_t *cluster_chain = ext_mem_alloc(alloc_size);
cluster = initial_cluster;
for (size_t i = 0; i < chain_length; i++) {
cluster_chain[i] = cluster;
if (read_cluster_from_map(context, cluster, &cluster) != 0) {
pmm_free(cluster_chain, alloc_size);
return NULL;
}
}
*_chain_length = chain_length;
return cluster_chain;
}
static bool read_cluster_chain(struct fat32_context *context,
uint32_t *cluster_chain,
size_t chain_len,
void *buf, uint64_t loc, uint64_t count) {
uint64_t block_size = (uint64_t)context->sectors_per_cluster * (uint64_t)context->bytes_per_sector;
for (uint64_t progress = 0; progress < count;) {
uint64_t block = (loc + progress) / block_size;
// Bounds check: ensure block index is within cluster chain
if (block >= chain_len) {
return false;
}
// Validate cluster number before arithmetic to prevent underflow
uint32_t cluster = cluster_chain[block];
if (cluster < 2) {
return false;
}
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 + (uint64_t)(cluster - 2) * context->sectors_per_cluster) * context->bytes_per_sector;
if (!volume_read(context->part, buf + progress, base + offset, chunk)) {
return false;
}
progress += chunk;
}
return true;
}
// Copy ucs-2 characters to char*, with bounds checking
static void fat32_lfncpy(char* destination, size_t dest_size, size_t dest_offset,
const void* source, unsigned int size) {
for (unsigned int i = 0; i < size; i++) {
if (dest_offset + i >= dest_size) {
return; // Prevent buffer overflow
}
// ignore high bytes
*(((uint8_t*) destination) + dest_offset + 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;
// Check for integer overflow in allocation size
size_t alloc_size;
if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) {
// Limit directory size to 256MB to prevent memory exhaustion
pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
return -1;
}
directory_entries = ext_mem_alloc(alloc_size);
if (!read_cluster_chain(context, directory_cluster_chain, dir_chain_len, directory_entries, 0, alloc_size)) {
pmm_free(directory_entries, alloc_size);
pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
return -1;
}
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);
// Check for overflow
size_t alloc_size;
if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) {
return -1;
}
directory_entries = ext_mem_alloc(alloc_size);
if (!volume_read(context->part, directory_entries, (uint64_t)context->root_start * context->bytes_per_sector, context->root_entries * sizeof(struct fat32_directory_entry))) {
pmm_free(directory_entries, alloc_size);
return -1;
}
}
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 seq_num = lfn->sequence_number & 0b00011111;
if (seq_num == 0) {
continue; // Invalid sequence number, skip
}
const unsigned int lfn_index = (seq_num - 1U) * 13U;
if (lfn_index >= FAT32_LFN_MAX_ENTRIES * 13) {
continue;
}
fat32_lfncpy(current_lfn, sizeof(current_lfn), lfn_index + 0, lfn->name1, 5);
fat32_lfncpy(current_lfn, sizeof(current_lfn), lfn_index + 5, lfn->name2, 6);
fat32_lfncpy(current_lfn, sizeof(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) {
// Ensure i+1 is within bounds before accessing
if (i + 1 >= (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry)) {
ret = -1;
goto out;
}
// Validate that the next entry is a valid SFN entry (not LFN, deleted, or end-of-dir)
struct fat32_directory_entry *sfn_entry = &directory_entries[i+1];
if (sfn_entry->file_name_and_ext[0] == 0x00 ||
(uint8_t)sfn_entry->file_name_and_ext[0] == 0xE5 ||
sfn_entry->attribute == FAT32_LFN_ATTRIBUTE) {
// Corrupted LFN sequence - expected SFN entry not found
ret = -1;
goto out;
}
*file = *sfn_entry;
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;
bool found_terminator = false;
for (unsigned int i = 0; i < SIZEOF_ARRAY(current_part) - 1; i++) {
// Check for overflow before computing path index
unsigned int path_idx;
if (__builtin_add_overflow(i, current_index, &path_idx)) {
return NULL; // Path index would overflow
}
if (path[path_idx] == 0) {
memcpy(current_part, path + current_index, i);
current_part[i] = 0;
expect_directory = false;
found_terminator = true;
break;
}
if (path[path_idx] == '/') {
memcpy(current_part, path + current_index, i);
current_part[i] = 0;
// Check for overflow before updating current_index
unsigned int new_index;
if (__builtin_add_overflow(current_index, i + 1, &new_index)) {
return NULL; // current_index would overflow
}
current_index = new_index;
expect_directory = true;
found_terminator = true;
break;
}
}
// If loop completed without finding terminator, path component is too long
if (!found_terminator) {
return NULL;
}
if ((r = fat32_open_in(&context, current_directory, &current_file, current_part)) != 0) {
return NULL;
}
if (expect_directory) {
if (!(current_file.attribute & FAT32_ATTRIBUTE_SUBDIRECTORY)) {
return NULL;
}
_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_bytes = current_file.file_size_bytes;
// Initialize chain_len before calling cache_cluster_chain
// (cache_cluster_chain may return NULL without setting it for empty files)
ret->chain_len = 0;
ret->cluster_chain = cache_cluster_chain(&context, ret->first_cluster, &ret->chain_len);
if (ret->cluster_chain == NULL && ret->size_bytes != 0) {
pmm_free(ret, sizeof(struct fat32_file_handle));
pmm_free(handle, sizeof(struct file_handle));
return NULL;
}
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;
if (!read_cluster_chain(&f->context, f->cluster_chain, f->chain_len, buf, loc, count)) {
panic(false, "fat32: cluster chain read failed (corrupted filesystem?)");
}
}
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));
}

45
limine/common/fs/file.h Normal file
View file

@ -0,0 +1,45 @@
#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
#if defined (__i386__)
, void (*memcpy_to_64)(uint64_t dst, void *src, size_t count)
#endif
);
#endif

184
limine/common/fs/file.s2.c Normal file
View file

@ -0,0 +1,184 @@
#include <stddef.h>
#include <stdint.h>
#include <fs/file.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;
}
return NULL;
}
bool fs_get_guid(struct guid *guid, struct volume *part) {
(void)guid; (void)part;
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) {
goto err;
}
pmm_free(filename_new, filename_new_len);
return ret;
}
if ((ret = iso9660_open(part, filename)) != NULL) {
goto success;
}
if ((ret = fat32_open(part, filename)) != NULL) {
goto success;
}
err:
pmm_free(filename_new, filename_new_len);
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) {
if (loc >= fd->size || count > fd->size - loc) {
panic(false, "fread: attempted out of bounds read");
}
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
#if defined (__i386__)
, NULL
#endif
);
}
void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs
#if defined (__i386__)
, void (*memcpy_to_64)(uint64_t dst, void *src, size_t count)
#endif
) {
#if defined (__i386__)
static uint64_t high_ret;
if (memcpy_to_64 == NULL) {
allow_high_allocs = false;
}
#endif
if (fd->is_memfile) {
if (fd->readall) {
#if defined (__i386__)
if (allow_high_allocs == true) {
high_ret = (uintptr_t)fd->fd;
return &high_ret;
}
#endif
return fd->fd;
}
#if defined (UEFI) && defined (__x86_64__)
if (!allow_high_allocs && (uintptr_t)fd->fd >= 0x100000000) {
void *newptr = ext_mem_alloc_type(fd->size, type);
memcpy(newptr, fd->fd, fd->size);
pmm_free(fd->fd, fd->size);
fd->fd = newptr;
} else {
#endif
memmap_alloc_range((uint64_t)(size_t)fd->fd, ALIGN_UP(fd->size, 4096), type, 0, true, false, false);
#if defined (UEFI) && defined (__x86_64__)
}
#endif
fd->readall = true;
#if defined (__i386__)
if (allow_high_allocs == true) {
high_ret = (uintptr_t)fd->fd;
return &high_ret;
}
#endif
return fd->fd;
} else {
void *ret = ext_mem_alloc_type_aligned_mode(fd->size, type, 4096, allow_high_allocs);
#if defined (__i386__)
if (allow_high_allocs == true) {
high_ret = *(uint64_t *)ret;
if (high_ret < 0x100000000) {
ret = (void *)(uintptr_t)high_ret;
goto low_ret;
}
void *pool = ext_mem_alloc(0x100000);
for (uint64_t i = 0; i < fd->size; i += 0x100000) {
size_t count;
if (fd->size - i < 0x100000) {
count = fd->size - i;
} else {
count = 0x100000;
}
fd->read(fd, pool, i, count);
memcpy_to_64(high_ret + i, pool, count);
}
pmm_free(pool, 0x100000);
fd->close(fd);
return &high_ret;
}
low_ret:
#endif
fd->read(fd, ret, 0, fd->size);
fd->close(fd);
fd->fd = ret;
fd->readall = true;
fd->is_memfile = true;
#if defined (__i386__)
if (allow_high_allocs == true) {
return &high_ret;
}
#endif
return ret;
}
}

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

View file

@ -0,0 +1,544 @@
#include <stdint.h>
#include <stddef.h>
#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_extent {
uint32_t LBA;
uint32_t size;
};
struct iso9660_file_handle {
struct iso9660_context *context;
uint64_t total_size;
uint32_t extent_count;
struct iso9660_extent *extents;
};
#define ISO9660_FLAG_MULTI_EXTENT 0x80
#define ISO9660_FIRST_VOLUME_DESCRIPTOR 0x10
#define ISO9660_VOLUME_DESCRIPTOR_SIZE ISO9660_SECTOR_SIZE
#define ROCK_RIDGE_MAX_FILENAME 255
#define ISO9660_MAX_EXTENT_COUNT 65536
// --- 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;
// Maximum number of volume descriptors to scan before giving up
#define ISO9660_MAX_VOLUME_DESCRIPTORS 256
// Maximum directory size to prevent memory exhaustion (64MB)
#define ISO9660_MAX_DIR_SIZE (64 * 1024 * 1024)
static void iso9660_find_PVD(struct iso9660_primary_volume *desc, struct volume *vol) {
uint32_t lba = ISO9660_FIRST_VOLUME_DESCRIPTOR;
uint32_t max_lba = ISO9660_FIRST_VOLUME_DESCRIPTOR + ISO9660_MAX_VOLUME_DESCRIPTORS;
while (lba < max_lba) {
uint64_t offset = (uint64_t)lba * ISO9660_SECTOR_SIZE;
if (!volume_read(vol, desc, offset, sizeof(struct iso9660_primary_volume))) {
panic(false, "ISO9660: failed to read volume descriptor");
}
switch (desc->volume_descriptor.type) {
case ISO9660_VDT_PRIMARY:
return;
case ISO9660_VDT_TERMINATOR:
panic(false, "ISO9660: no primary volume descriptor");
break;
}
++lba;
}
panic(false, "ISO9660: exceeded maximum volume descriptor search limit");
}
static void iso9660_cache_root(struct volume *vol,
void **root,
uint32_t *root_size) {
struct iso9660_primary_volume pv;
iso9660_find_PVD(&pv, vol);
*root_size = pv.root.extent_size.little;
// Validate root directory size to prevent memory exhaustion
if (*root_size == 0 || *root_size > ISO9660_MAX_DIR_SIZE) {
panic(false, "ISO9660: Invalid root directory size");
}
*root = ext_mem_alloc(*root_size);
uint64_t offset = (uint64_t)pv.root.extent.little * ISO9660_SECTOR_SIZE;
if (!volume_read(vol, *root, offset, *root_size)) {
panic(false, "ISO9660: failed to read root directory");
}
}
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, size_t limit, struct iso9660_directory_entry *entry) {
unsigned char* sysarea = ((unsigned char*)entry) + sizeof(struct iso9660_directory_entry) + entry->filename_size;
// Validate entry->length is large enough
if (entry->length < sizeof(struct iso9660_directory_entry) + entry->filename_size) {
goto use_iso_name;
}
size_t sysarea_len = entry->length - sizeof(struct iso9660_directory_entry) - entry->filename_size;
if ((entry->filename_size & 0x1) == 0) {
if (sysarea_len == 0) {
goto use_iso_name;
}
sysarea++;
sysarea_len--;
}
int rrnamelen = 0;
unsigned char *nm_entry = NULL;
while ((sysarea_len >= 4) && (sysarea[3] == 1)) {
// Validate entry length doesn't exceed remaining sysarea
if (sysarea[2] > sysarea_len) {
break;
}
if (sysarea[0] == 'N' && sysarea[1] == 'M') {
// Validate Rock Ridge NM entry length
// sysarea[2] is total entry length, must be >= 5 (header) and within sysarea bounds
if (sysarea[2] >= 5 && sysarea[2] <= sysarea_len) {
rrnamelen = sysarea[2] - 5;
nm_entry = sysarea;
}
break;
}
// Prevent infinite loop from zero-length entry
if (sysarea[2] == 0) {
break;
}
sysarea_len -= sysarea[2];
sysarea += sysarea[2];
}
size_t name_len = 0;
if (rrnamelen > 0 && nm_entry != NULL) {
/* rock ridge naming scheme */
name_len = rrnamelen;
if (name_len >= limit) {
panic(false, "iso9660: Filename size exceeded");
}
memcpy(buf, nm_entry + 5, name_len);
buf[name_len] = 0;
return true;
}
use_iso_name:
name_len = entry->filename_size;
if (name_len >= limit) {
panic(false, "iso9660: Filename size exceeded");
}
// Validate that entry->length can actually hold the filename
// entry->length must be >= sizeof(struct) + filename_size for safe access
if (entry->length < sizeof(struct iso9660_directory_entry) + name_len) {
// Corrupted entry: claimed filename_size exceeds actual entry data
// Clamp name_len to what's actually available
if (entry->length <= sizeof(struct iso9660_directory_entry)) {
name_len = 0;
} else {
name_len = entry->length - sizeof(struct iso9660_directory_entry);
}
}
size_t j;
for (j = 0; j < name_len; j++) {
if (entry->name[j] == ';')
break;
if (entry->name[j] == '.' && j + 1 < name_len && entry->name[j+1] == ';')
break;
buf[j] = entry->name[j];
}
buf[j] = 0;
return false;
}
// Advance to the next directory entry in the buffer
// Returns NULL if no more entries or invalid entry
static struct iso9660_directory_entry *iso9660_next_entry(void *current, void *buffer_end) {
struct iso9660_directory_entry *entry = current;
if (entry->length == 0) {
// Skip to next sector boundary
uintptr_t current_addr = (uintptr_t)current;
uintptr_t next_sector = ALIGN_UP(current_addr + 1, ISO9660_SECTOR_SIZE);
if (next_sector >= (uintptr_t)buffer_end)
return NULL;
entry = (struct iso9660_directory_entry *)next_sector;
if (entry->length == 0)
return NULL;
return entry;
}
void *next = (uint8_t *)current + entry->length;
if (next >= buffer_end)
return NULL;
entry = next;
// Handle zero-length entries (padding at sector boundaries)
if (entry->length == 0) {
uintptr_t next_sector = ALIGN_UP((uintptr_t)next + 1, ISO9660_SECTOR_SIZE);
if (next_sector >= (uintptr_t)buffer_end)
return NULL;
entry = (struct iso9660_directory_entry *)next_sector;
if (entry->length == 0)
return NULL;
}
// Validate minimum entry size
if (entry->length < sizeof(struct iso9660_directory_entry))
return NULL;
return entry;
}
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);
// If size didn't change (was already aligned), force move to next sector
if (prev_size == size) {
if (size <= ISO9660_SECTOR_SIZE)
return NULL;
size -= ISO9660_SECTOR_SIZE;
buffer += ISO9660_SECTOR_SIZE;
} else {
buffer += prev_size - size;
}
continue;
}
// Validate entry->length doesn't exceed remaining buffer
if (entry->length > size) {
return NULL; // Corrupted directory entry
}
// Minimum valid directory entry size
if (entry->length < sizeof(struct iso9660_directory_entry)) {
return NULL; // Corrupted directory entry
}
char entry_filename[256];
bool rr = load_name(entry_filename, 256, 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;
if (!volume_read(vol, buf, signature, 5)) {
return NULL;
}
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) {
// Skip any consecutive slashes
while (*path == '/') {
path++;
}
// Check if we've reached the end of the path (handles trailing slashes)
if (*path == '\0') {
// Use the current directory's extent info
// For root, this was set from ret->context->root
// For subdirs, it was set from the last matched entry
if (!first) {
pmm_free(current, current_size);
}
pmm_free(ret, sizeof(struct iso9660_file_handle));
return NULL;
}
char *aux = filename;
char *aux_end = filename + ROCK_RIDGE_MAX_FILENAME - 1;
while (!(*path == '/' || *path == '\0')) {
if (aux >= aux_end) {
panic(false, "iso9660: Path component exceeds maximum length");
}
*aux++ = *path++;
}
*aux = '\0';
struct iso9660_directory_entry *entry = iso9660_find(current, current_size, filename);
if (!entry) {
if (!first) {
pmm_free(current, current_size);
}
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') {
// Found the file - collect all extents for multi-extent files
void *buffer_end = (uint8_t *)current + current_size;
// First pass: count extents and calculate total size
uint32_t extent_count = 1;
uint64_t total_size = entry->extent_size.little;
struct iso9660_directory_entry *e = entry;
while (e->flags & ISO9660_FLAG_MULTI_EXTENT) {
struct iso9660_directory_entry *next = iso9660_next_entry(e, buffer_end);
if (next == NULL)
break;
e = next;
extent_count++;
total_size += e->extent_size.little;
// Sanity check to prevent runaway on corrupted directories
if (extent_count >= ISO9660_MAX_EXTENT_COUNT) {
break;
}
}
// Allocate extent array
ret->extents = ext_mem_alloc(extent_count * sizeof(struct iso9660_extent));
ret->extent_count = extent_count;
ret->total_size = total_size;
// Second pass: populate extent array
e = entry;
for (uint32_t i = 0; i < extent_count; i++) {
ret->extents[i].LBA = e->extent.little;
ret->extents[i].size = e->extent_size.little;
if (i + 1 < extent_count) {
struct iso9660_directory_entry *next = iso9660_next_entry(e, buffer_end);
if (next == NULL)
break;
e = next;
}
}
// Free the directory buffer if we allocated one
if (!first) {
pmm_free(current, current_size);
}
goto setup_handle;
}
path++; // Skip the '/' separator
if (!first) {
pmm_free(current, current_size);
}
// Validate directory size to prevent memory exhaustion
if (next_size == 0 || next_size > ISO9660_MAX_DIR_SIZE) {
pmm_free(ret, sizeof(struct iso9660_file_handle));
return NULL;
}
current_size = next_size;
current = ext_mem_alloc(current_size);
first = false;
uint64_t dir_offset = (uint64_t)next_sector * ISO9660_SECTOR_SIZE;
if (!volume_read(vol, current, dir_offset, current_size)) {
pmm_free(current, current_size);
pmm_free(ret, sizeof(struct iso9660_file_handle));
return NULL;
}
}
setup_handle:;
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->total_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;
// Find which extent 'loc' falls into and read across extents as needed
uint64_t extent_start = 0;
for (uint32_t i = 0; i < f->extent_count && count > 0; i++) {
uint64_t extent_size = f->extents[i].size;
uint64_t extent_end = extent_start + extent_size;
if (loc < extent_end) {
// Read starts (or continues) in this extent
uint64_t offset_in_extent = (loc > extent_start) ? (loc - extent_start) : 0;
uint64_t bytes_available = extent_size - offset_in_extent;
uint64_t to_read = (count < bytes_available) ? count : bytes_available;
uint64_t disk_offset = (uint64_t)f->extents[i].LBA * ISO9660_SECTOR_SIZE + offset_in_extent;
if (!volume_read(f->context->vol, buf, disk_offset, to_read)) {
panic(false, "iso9660: failed to read file data");
}
buf = (uint8_t *)buf + to_read;
loc += to_read;
count -= to_read;
}
extent_start = extent_end;
}
if (count > 0) {
panic(false, "iso9660: read beyond end of file");
}
}
static void iso9660_close(struct file_handle *file) {
struct iso9660_file_handle *f = file->fd;
pmm_free(f->extents, f->extent_count * sizeof(struct iso9660_extent));
pmm_free(f, sizeof(struct iso9660_file_handle));
}

51
limine/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"' EXIT
"$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"

475
limine/common/lib/acpi.c Normal file
View file

@ -0,0 +1,475 @@
#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>
#include <mm/pmm.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 available 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 occurs 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 available 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 %p\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 %p\n", ptr);
*smbios64 = (void *)ptr;
break;
}
}
#endif
/// Returns the RSDP v2 pointer if available 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
&& (sizeof(uintptr_t) >= 8 || rsdp->xsdt_addr <= UINT32_MAX))
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;
if (rsdt == NULL) {
return NULL;
}
// Validate RSDT/XSDT header length
if (rsdt->header.length < sizeof(struct sdt)) {
printv("acpi: Invalid %s header length\n", use_xsdt ? "XSDT" : "RSDT");
return NULL;
}
size_t entry_size = use_xsdt ? 8 : 4;
size_t entry_count = (rsdt->header.length - sizeof(struct sdt)) / entry_size;
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 (ptr == NULL) {
continue;
}
if (!memcmp(ptr->signature, signature, 4)
&& !acpi_checksum(ptr, ptr->length)
&& cnt++ == index) {
printv("acpi: Found \"%s\" at %p\n", signature, ptr);
return ptr;
}
}
printv("acpi: \"%s\" not found\n", signature);
return NULL;
}
static bool acpi_padding_is_safe(uint64_t base, uint64_t length) {
if (length == 0) {
return true;
}
uint64_t top = base + length;
for (size_t i = 0; i < memmap_entries; i++) {
uint64_t entry_base = memmap[i].base;
uint64_t entry_top = entry_base + memmap[i].length;
if (entry_base >= top || entry_top <= base) {
continue;
}
if (memmap[i].type != MEMMAP_USABLE && memmap[i].type != MEMMAP_RESERVED) {
return false;
}
}
return true;
}
static void map_single_table(uint64_t addr, uint32_t len) {
#if defined (__i386__)
if (addr >= 0x100000000) {
print("acpi: warning: Cannot get length of ACPI table above 4GiB\n");
return;
}
#endif
uint32_t length = len != (uint32_t)-1 ? len : *(uint32_t *)(uintptr_t)(addr + 4);
uint64_t aligned_base = ALIGN_DOWN(addr, 4096);
uint64_t aligned_top = ALIGN_UP(addr + length, 4096);
if (!acpi_padding_is_safe(aligned_base, addr - aligned_base)) {
aligned_base = addr;
}
if (!acpi_padding_is_safe(addr + length, aligned_top - (addr + length))) {
aligned_top = addr + length;
}
uint64_t memmap_type = pmm_check_type(addr);
if (memmap_type != MEMMAP_ACPI_RECLAIMABLE && memmap_type != MEMMAP_ACPI_NVS) {
memmap_alloc_range(aligned_base, aligned_top - aligned_base, MEMMAP_RESERVED_MAPPED, 0, true, false, true);
}
}
void acpi_map_tables(void) {
struct rsdp *rsdp = acpi_get_rsdp();
if (rsdp == NULL)
return;
uint64_t rsdp_length;
if (rsdp->rev < 2) {
rsdp_length = 20;
} else {
rsdp_length = rsdp->length;
}
map_single_table((uintptr_t)rsdp, rsdp_length);
if (!(rsdp->rev >= 2 && rsdp->xsdt_addr)) {
goto no_xsdt;
}
struct rsdt *xsdt = (void *)(uintptr_t)rsdp->xsdt_addr;
if (xsdt->header.length < sizeof(struct sdt)) {
goto no_xsdt;
}
size_t xsdt_entry_count = (xsdt->header.length - sizeof(struct sdt)) / 8;
map_single_table((uintptr_t)xsdt, (uint32_t)-1);
for (size_t i = 0; i < xsdt_entry_count; i++) {
uint64_t entry = ((uint64_t *)xsdt->ptrs_start)[i];
if (entry == 0)
continue;
struct sdt *sdt = (void *)(uintptr_t)entry;
map_single_table((uintptr_t)sdt, (uint32_t)-1);
}
no_xsdt:;
if (rsdp->rsdt_addr == 0) {
goto no_rsdt;
}
struct rsdt *rsdt = (void *)(uintptr_t)rsdp->rsdt_addr;
if (rsdt->header.length < sizeof(struct sdt)) {
goto no_rsdt;
}
size_t rsdt_entry_count = (rsdt->header.length - sizeof(struct sdt)) / 4;
map_single_table((uintptr_t)rsdt, (uint32_t)-1);
for (size_t i = 0; i < rsdt_entry_count; i++) {
uint32_t entry = ((uint32_t *)rsdt->ptrs_start)[i];
if (entry == 0)
continue;
struct sdt *sdt = (void *)(uintptr_t)entry;
map_single_table((uintptr_t)sdt, (uint32_t)-1);
}
no_rsdt:;
uint8_t *fadt = acpi_get_table("FACP", 0);
if (fadt == NULL) {
return;
}
uint32_t fadt_length;
memcpy(&fadt_length, fadt + 4, sizeof(fadt_length));
// Read the single fields from the FADT without defining a struct for the whole table
if (fadt_length >= 132 + 8) {
uint64_t x_facs;
memcpy(&x_facs, fadt + 132, sizeof(x_facs));
if (x_facs != 0) {
map_single_table(x_facs, (uint32_t)-1);
}
}
if (fadt_length >= 140 + 8) {
uint64_t x_dsdt;
memcpy(&x_dsdt, fadt + 140, sizeof(x_dsdt));
if (x_dsdt != 0) {
map_single_table(x_dsdt, (uint32_t)-1);
}
}
if (fadt_length >= 36 + 4) {
uint32_t facs;
memcpy(&facs, fadt + 36, sizeof(facs));
if (facs != 0) {
map_single_table(facs, (uint32_t)-1);
}
}
if (fadt_length >= 40 + 4) {
uint32_t dsdt;
memcpy(&dsdt, fadt + 40, sizeof(dsdt));
if (dsdt != 0) {
map_single_table(dsdt, (uint32_t)-1);
}
}
}
void smbios_map_tables(void) {
void *smbios32_ptr = NULL, *smbios64_ptr = NULL;
acpi_get_smbios(&smbios32_ptr, &smbios64_ptr);
if (smbios32_ptr != NULL) {
struct smbios_entry_point_32 *smbios32 = smbios32_ptr;
map_single_table((uintptr_t)smbios32, smbios32->length);
if (smbios32->table_address != 0) {
map_single_table(smbios32->table_address, smbios32->table_length);
}
}
if (smbios64_ptr != NULL) {
struct smbios_entry_point_64 *smbios64 = smbios64_ptr;
map_single_table((uintptr_t)smbios64, smbios64->length);
if (smbios64->table_address != 0) {
map_single_table(smbios64->table_address, smbios64->table_maximum_size);
}
}
}
#if defined (UEFI)
void efi_map_runtime_entries(void) {
size_t entry_count = efi_mmap_size / efi_desc_size;
for (size_t i = 0; i < entry_count; i++) {
EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size;
if (entry->Type != EfiRuntimeServicesCode
&& entry->Type != EfiRuntimeServicesData) {
continue;
}
uint64_t base = entry->PhysicalStart;
uint64_t length;
if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &length)) {
continue;
}
memmap_alloc_range(base, length, MEMMAP_RESERVED_MAPPED, 0, true, false, true);
}
// Explicitly map the EFI system table and the data it references.
// The UEFI spec does not guarantee these reside in EfiRuntimeServicesData,
// so we map them separately to ensure they are always accessible via HHDM.
map_single_table((uintptr_t)gST, sizeof(*gST));
if (gST->RuntimeServices != NULL) {
map_single_table((uintptr_t)gST->RuntimeServices,
sizeof(*gST->RuntimeServices));
}
if (gST->ConfigurationTable != NULL && gST->NumberOfTableEntries > 0) {
uint64_t ct_size;
if (!__builtin_mul_overflow(gST->NumberOfTableEntries,
(uint64_t)sizeof(EFI_CONFIGURATION_TABLE), &ct_size)
&& ct_size <= UINT32_MAX) {
map_single_table((uintptr_t)gST->ConfigurationTable, (uint32_t)ct_size);
}
}
if (gST->FirmwareVendor != NULL) {
size_t len = 0;
while (gST->FirmwareVendor[len] != 0) {
len++;
}
map_single_table((uintptr_t)gST->FirmwareVendor,
(len + 1) * sizeof(*gST->FirmwareVendor));
}
}
#endif

205
limine/common/lib/acpi.h Normal file
View file

@ -0,0 +1,205 @@
#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 SMBIOS Structure Table, in bytes.
uint32_t table_maximum_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_lapic_nmi {
struct madt_header header; // type=4, length=6
uint8_t acpi_processor_uid; // 0xff = all processors
uint16_t flags; // MPS INTI flags
uint8_t lint; // 0 or 1
} __attribute__((packed));
struct madt_x2apic_nmi {
struct madt_header header; // type=0x0a, length=12
uint16_t flags; // MPS INTI flags
uint32_t acpi_processor_uid; // 0xffffffff = all processors
uint8_t lint; // 0 or 1
uint8_t reserved[3];
} __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);
void acpi_map_tables(void);
void smbios_map_tables(void);
#if defined (UEFI)
void efi_map_runtime_entries(void);
#endif
#endif

90
limine/common/lib/bli.c Normal file
View file

@ -0,0 +1,90 @@
#if defined (UEFI)
#include <stdint.h>
#include <stddef.h>
#include <config.h>
#include <sys/cpu.h>
#include <efi.h>
#include <lib/bli.h>
#include <lib/guid.h>
#include <lib/misc.h>
#define LIMINE_BRAND L"Limine " LIMINE_VERSION
static EFI_GUID bli_vendor_guid = { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } };
// The buffer must be at least 21 bytes long
void uint64_to_decwstr(uint64_t value, wchar_t *buf) {
wchar_t tmp[21];
size_t i = 0;
if (buf == NULL) {
return;
}
if (value == 0) {
buf[0] = '0';
buf[1] = '\0';
return;
}
// Convert digits in reverse order
while (value > 0) {
tmp[i++] = '0' + (value % 10);
value /= 10;
}
// Reverse the string into the buffer
for (size_t j = 0; j < i; j++) {
buf[j] = tmp[i - j - 1];
}
buf[i] = '\0';
}
void bli_set_loader_time(wchar_t *variable, uint64_t time) {
if (time == 0)
return;
wchar_t time_wstr[21];
uint64_to_decwstr(time, time_wstr);
size_t len = 0;
while (time_wstr[len] != L'\0') len++;
gRT->SetVariable(variable,
&bli_vendor_guid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
(len + 1) * sizeof(wchar_t),
time_wstr);
}
void init_bli(void) {
bli_set_loader_time(L"LoaderTimeInitUSec", usec_at_bootloader_entry);
gRT->SetVariable(L"LoaderInfo",
&bli_vendor_guid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
sizeof(LIMINE_BRAND),
LIMINE_BRAND);
char part_uuid_str[37];
guid_to_string(&boot_volume->part_guid, part_uuid_str);
// Convert part_uuid_str to a wide-char string
wchar_t part_uuid[37];
for (size_t i = 0; i < 37; i++) {
part_uuid[i] = (wchar_t) part_uuid_str[i];
}
gRT->SetVariable(L"LoaderDevicePartUUID",
&bli_vendor_guid,
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
sizeof(part_uuid),
part_uuid);
}
void bli_on_boot(void) {
bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec());
}
#endif

11
limine/common/lib/bli.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef LIB__BLI_H__
#define LIB__BLI_H__
#if defined (UEFI)
void init_bli(void);
void bli_on_boot(void);
#endif
#endif

756
limine/common/lib/config.c Normal file
View file

@ -0,0 +1,756 @@
#include <stddef.h>
#include <stdbool.h>
#include <lib/acpi.h>
#include <lib/config.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <lib/getchar.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;
#if defined (UEFI)
#define EFI_APP_PATH_LEN 128
static char efi_app_path[128] = {0};
static bool init_efi_app_path(size_t *len_out) {
EFI_STATUS status;
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
EFI_DEVICE_PATH_PROTOCOL *path;
CHAR16 *file_path, *p, *last_slash;
EFI_GUID loaded_image_protocol_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
status = gBS->HandleProtocol(efi_image_handle, &loaded_image_protocol_guid,
(void **)&loaded_image);
if (status != 0) {
return false;
}
path = loaded_image->FilePath;
while (!(path->Type == END_DEVICE_PATH_TYPE && path->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {
if (path->Type == MEDIA_DEVICE_PATH && path->SubType == MEDIA_FILEPATH_DP) {
goto found;
}
uint16_t node_length = *((uint16_t *)&path->Length[0]);
if (node_length < 4) {
return false;
}
path = (void *)path + node_length;
}
return false;
found:
file_path = (CHAR16 *)((void *)path + 4);
last_slash = NULL;
for (p = file_path; *p; p++) {
if (*p == L'\\') {
last_slash = p;
}
}
if (last_slash) {
size_t len = (last_slash - file_path) + 1;
if (len >= EFI_APP_PATH_LEN) {
len = EFI_APP_PATH_LEN - 1;
}
for (size_t i = 0; i < len; i++) {
efi_app_path[i] = (char)(file_path[i] & 0xff);
if (efi_app_path[i] == '\\') {
efi_app_path[i] = '/';
}
}
efi_app_path[len] = 0;
if (len_out != NULL) {
*len_out = len;
}
} else {
efi_app_path[0] = '/';
efi_app_path[1] = 0;
if (len_out != NULL) {
*len_out = 1;
}
}
return true;
}
#endif
int init_config_disk(struct volume *part) {
#if defined (UEFI)
bool use_default_efi_search_path = false;
size_t len;
if (!init_efi_app_path(&len)) {
use_default_efi_search_path = true;
} else {
if (len + sizeof("limine.conf") >= EFI_APP_PATH_LEN) {
use_default_efi_search_path = true;
} else {
strcpy(efi_app_path + len, "limine.conf");
}
}
#endif
struct file_handle *f;
bool old_cif = case_insensitive_fopen;
case_insensitive_fopen = true;
if (
false
#if defined (UEFI)
|| (f = fopen(part, use_default_efi_search_path ? "/EFI/BOOT/limine.conf" : efi_app_path)) != NULL
#endif
|| (f = fopen(part, "/boot/limine/limine.conf")) != NULL
|| (f = fopen(part, "/boot/limine.conf")) != NULL
|| (f = fopen(part, "/limine/limine.conf")) != NULL
|| (f = fopen(part, "/limine.conf")) != NULL
) {
goto opened;
}
case_insensitive_fopen = old_cif;
return -1;
opened:
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);
}
struct smbios_struct_header {
uint8_t type;
uint8_t length;
uint16_t handle;
} __attribute__((packed));
static size_t smbios_struct_size(struct smbios_struct_header *hdr, size_t remaining) {
// Validate minimum structure header size
if (remaining < sizeof(struct smbios_struct_header)) {
return 0;
}
if (hdr->length < sizeof(struct smbios_struct_header)) {
return 0; // Invalid structure
}
if (hdr->length > remaining) {
return 0; // Structure header claims more than remaining
}
const char *string_data = (void *)((uintptr_t)hdr + hdr->length);
size_t string_area_max = remaining - hdr->length;
size_t i = 1;
for (; i < string_area_max && (string_data[i - 1] != '\0' || string_data[i] != '\0'); i++);
if (i >= string_area_max) {
return 0; // Unterminated string area
}
return hdr->length + i + 1;
}
bool init_config_smbios(void) {
struct smbios_entry_point_32 *smbios_entry_32 = NULL;
struct smbios_entry_point_64 *smbios_entry_64 = NULL;
acpi_get_smbios((void **)&smbios_entry_32, (void **)&smbios_entry_64);
if (smbios_entry_32 == NULL && smbios_entry_64 == NULL) {
return false;
}
struct smbios_struct_header *hdr = NULL;
size_t struct_count = 0;
size_t table_length = 0;
if (smbios_entry_64) {
hdr = (void *)(uintptr_t) smbios_entry_64->table_address;
table_length = smbios_entry_64->table_maximum_size;
} else {
hdr = (void *)(uintptr_t) smbios_entry_32->table_address;
struct_count = smbios_entry_32->number_of_structures;
table_length = smbios_entry_32->table_length;
}
if (hdr == NULL || table_length == 0) {
return false;
}
size_t structure_bytes_processed = 0;
for (size_t struct_num = 0; hdr && (!struct_count || struct_num < struct_count); struct_num++) {
size_t remaining = table_length - structure_bytes_processed;
if (remaining < sizeof(struct smbios_struct_header)) {
return false;
}
if (hdr->type == 127)
return false;
size_t struct_size = smbios_struct_size(hdr, remaining);
if (struct_size == 0) {
return false; // Invalid structure
}
if (hdr->type == 11 && hdr->length >= sizeof(struct smbios_struct_header)) {
const char *string_data = (void *)((uintptr_t) hdr + hdr->length);
size_t string_area_size = struct_size - hdr->length;
size_t prefix_len = sizeof("limine:config:") - 1;
if (string_area_size > prefix_len && !strncmp(string_data, "limine:config:", prefix_len)) {
size_t total_len = strnlen(string_data, string_area_size);
if (total_len <= prefix_len)
continue;
size_t config_size = total_len - prefix_len + 2;
config_addr = ext_mem_alloc(config_size);
memcpy(config_addr, &string_data[prefix_len], config_size - 1);
config_addr[config_size - 1] = '\0';
return !init_config(config_size);
}
}
structure_bytes_processed += struct_size;
if (structure_bytes_processed >= table_length) {
return false;
}
hdr = (void *)((uintptr_t) hdr + struct_size);
}
return false;
}
#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] == '+';
char *n = &name[current_depth + default_expanded];
while (*n == ' ') {
n++;
}
size_t n_len = strlen(n);
if (n_len >= sizeof(entry->name)) {
n_len = sizeof(entry->name) - 1;
}
memcpy(entry->name, n, n_len);
entry->name[n_len] = 0;
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 = strdup(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';
size_t config_alloc_size = config_size;
// remove windows carriage returns and spaces at the start and end of lines, if any
for (size_t i = 0; i < config_size; i++) {
size_t skip = 0;
if (config_addr[i] == ' ' || config_addr[i] == '\t') {
while (i + skip < config_size && (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t')) {
skip++;
}
if (i + skip < config_size && config_addr[i + skip] == '\n') {
goto skip_loop;
}
skip = 0;
}
while (i + skip < config_size
&& ((config_addr[i + skip] == '\r')
|| ((!i || config_addr[i - 1] == '\n') && (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t')))
) {
skip++;
}
skip_loop:
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 (__riscv)
strcpy(arch_macro->value, "riscv64");
#elif defined (__loongarch64)
strcpy(arch_macro->value, "loongarch64");
#else
#error "Unspecified architecture"
#endif
arch_macro->next = macros;
macros = arch_macro;
struct macro *fw_type_macro = ext_mem_alloc(sizeof(struct macro));
strcpy(fw_type_macro->name, "FW_TYPE");
#if defined (UEFI)
strcpy(fw_type_macro->value, "UEFI");
#else
strcpy(fw_type_macro->value, "BIOS");
#endif
fw_type_macro->next = macros;
macros = fw_type_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++) {
if (j >= sizeof(macro->name) - 1) {
bad_config = true;
panic(true, "config: Macro name too long (max %U)", (uint64_t)(sizeof(macro->name) - 1));
}
macro->name[j] = config_addr[i];
}
if (config_addr[i] == '\n' || config_addr[i] == 0 || config_addr[i+1] != '=') {
pmm_free(macro, sizeof(struct macro));
continue;
}
i += 2;
macro->name[j] = 0;
for (j = 0; config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
if (j >= sizeof(macro->value) - 1) {
bad_config = true;
panic(true, "config: Macro value too long (max %U)", (uint64_t)(sizeof(macro->value) - 1));
}
macro->value[j] = config_addr[i];
}
macro->value[j] = 0;
macro->next = macros;
macros = macro;
continue;
}
i++;
}
// Expand macros
if (macros != NULL) {
// Check for overflow before multiplication
if (config_size > SIZE_MAX / 4) {
bad_config = true;
panic(true, "config: Config file too large for macro expansion");
}
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 (i < config_size && config_addr[i] != '}') {
i++;
}
if (i >= config_size) {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
i++; // skip '}'
if (i >= config_size || 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; j < 1023 && 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_alloc_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;
}
macros = NULL;
}
config_ready = true;
menu_tree = create_menu_tree(NULL, 1, 0);
size_t s;
char *c = config_get_entry(&s, 0);
if (c != NULL) {
while (*c != '/' && c > config_addr) {
c--;
}
if (*c == '/' && 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' && *p != '\0');
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) {
// Static buffers for return values.
// Callers must copy the result if they need persistence across calls.
#define CONF_TUPLE_BUF_SIZE 4096
static char value1_buf[CONF_TUPLE_BUF_SIZE];
static char value2_buf[CONF_TUPLE_BUF_SIZE];
struct conf_tuple conf_tuple;
char *tmp = config_get_value(config, index, key1);
if (tmp == NULL) {
return (struct conf_tuple){0};
}
size_t len = strlen(tmp);
if (len >= CONF_TUPLE_BUF_SIZE) {
len = CONF_TUPLE_BUF_SIZE - 1;
}
memcpy(value1_buf, tmp, len);
value1_buf[len] = '\0';
conf_tuple.value1 = value1_buf;
const char *lk1 = lastkey;
tmp = config_get_value(lk1, 0, key2);
if (tmp != NULL) {
len = strlen(tmp);
if (len >= CONF_TUPLE_BUF_SIZE) {
len = CONF_TUPLE_BUF_SIZE - 1;
}
memcpy(value2_buf, tmp, len);
value2_buf[len] = '\0';
conf_tuple.value2 = value2_buf;
} else {
conf_tuple.value2 = NULL;
}
const char *lk2 = lastkey;
const char *next_value1 = config_get_value(config, index + 1, key1);
const char *lk3 = lastkey;
if (conf_tuple.value2 != NULL && next_value1 != NULL) {
if ((uintptr_t)lk2 > (uintptr_t)lk3) {
conf_tuple.value2 = NULL;
}
}
return conf_tuple;
}
char *config_get_value(const char *config, size_t index, const char *key) {
// Static buffer for return values.
// Callers must copy the result if they need persistence across calls.
#define CONFIG_VALUE_BUF_SIZE 4096
static char buf[CONFIG_VALUE_BUF_SIZE];
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 (!strncasecmp(&config[i], key, key_len) && config[i + key_len] == ':') {
if (i && config[i - 1] != SEPARATOR)
continue;
if (index--)
continue;
i += key_len + 1;
while (config[i] == ' ' || config[i] == '\t') {
i++;
}
size_t value_len;
for (value_len = 0;
config[i + value_len] != SEPARATOR && config[i + value_len];
value_len++);
if (value_len >= CONFIG_VALUE_BUF_SIZE) {
value_len = CONFIG_VALUE_BUF_SIZE - 1;
}
memcpy(buf, config + i, value_len);
buf[value_len] = '\0';
lastkey = config + i;
return buf;
}
}
return NULL;
}

View file

@ -0,0 +1,36 @@
#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);
bool init_config_smbios(void);
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

1209
limine/common/lib/elf.c Normal file

File diff suppressed because it is too large Load diff

87
limine/common/lib/elf.h Normal file
View file

@ -0,0 +1,87 @@
#ifndef LIB__ELF_H__
#define LIB__ELF_H__
#include <stdint.h>
#include <stdbool.h>
#include <lib/elsewhere.h>
#include <lib/misc.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_section_hdr_info {
uint32_t section_entry_size;
uint32_t str_section_idx;
uint32_t num;
uint64_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, size_t file_size, void *buffer, const char *name, size_t limit, uint64_t slide);
bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_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, size_t file_size, uint64_t *entry_point,
struct elsewhere_range **ranges);
bool elf64_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point,
struct elsewhere_range **ranges);
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

View file

@ -0,0 +1,100 @@
#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 < top2 && base2 < top1);
}
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;
// Ensure allocation stays within 32-bit address space.
if (t_top > 0x100000000) {
return false;
}
// 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;
}

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

65
limine/common/lib/fb.c Normal file
View file

@ -0,0 +1,65 @@
#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>
struct fb_info *fb_fbs;
size_t fb_fbs_count = 0;
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
fb_fbs = *ret;
fb_fbs_count = *_fbs_count;
}
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;
size_t row_bytes = fb->framebuffer_width * (fb->framebuffer_bpp / 8);
for (size_t x = 0; x < row_bytes; x++) {
fbp[row + x] = 0;
}
break;
}
}
}
}

43
limine/common/lib/fb.h Normal file
View file

@ -0,0 +1,43 @@
#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;
};
extern struct fb_info *fb_fbs;
extern size_t fb_fbs_count;
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

44
limine/common/lib/fdt.c Normal file
View file

@ -0,0 +1,44 @@
#if !defined(__i386__) && !defined(__x86_64__)
#include <stdint.h>
#include <stddef.h>
#include <libfdt.h>
static int fdt_get_or_add_chosen_node(void *fdt) {
int offset = fdt_subnode_offset(fdt, 0, "chosen");
if (offset == -FDT_ERR_NOTFOUND) {
offset = fdt_add_subnode(fdt, 0, "chosen");
}
return offset;
}
int fdt_set_chosen_string(void *fdt, const char *name, const char *value) {
int chosen_offset = fdt_get_or_add_chosen_node(fdt);
if (chosen_offset < 0) {
return chosen_offset;
}
return fdt_setprop_string(fdt, chosen_offset, name, value);
}
int fdt_set_chosen_uint64(void *fdt, const char *name, uint64_t value) {
int chosen_offset = fdt_get_or_add_chosen_node(fdt);
if (chosen_offset < 0) {
return chosen_offset;
}
return fdt_setprop_u64(fdt, chosen_offset, name, value);
}
int fdt_set_chosen_uint32(void *fdt, const char *name, uint32_t value) {
int chosen_offset = fdt_get_or_add_chosen_node(fdt);
if (chosen_offset < 0) {
return chosen_offset;
}
return fdt_setprop_u32(fdt, chosen_offset, name, value);
}
#endif

15
limine/common/lib/fdt.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef LIB__FDT_H__
#define LIB__FDT_H__
#if !defined(__x86_64__) && !defined(__i386__)
#include <stddef.h>
#include <stdint.h>
int fdt_set_chosen_string(void *fdt, const char *name, const char *value);
int fdt_set_chosen_uint64(void *fdt, const char *name, uint64_t value);
int fdt_set_chosen_uint32(void *fdt, const char *name, uint32_t value);
#endif
#endif // LIB__FDT_H__

365
limine/common/lib/getchar.c Normal file
View file

@ -0,0 +1,365 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/getchar.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 '\n':
case '\r':
return '\n';
case '\b':
return '\b';
case '\t':
return '\t';
}
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:
stall(10);
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, (uint64_t)10000000 * seconds);
again:
memset(&kd, 0, sizeof(EFI_KEY_DATA));
gBS->WaitForEvent(2, events, &which);
if (which == 1) {
gBS->CloseEvent(events[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 (serial == true && kd.Key.ScanCode == 0x08) {
gBS->CloseEvent(events[1]);
return '\b';
}
if (kd.Key.ScanCode == SCAN_ESC) {
gBS->CloseEvent(events[1]);
gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
gBS->SetTimer(events[1], TimerRelative, 100000);
gBS->WaitForEvent(2, events, &which);
if (which == 1) {
gBS->CloseEvent(events[1]);
return GETCHAR_ESCAPE;
}
if (use_sproto) {
status = sproto->ReadKeyStroke(sproto, &kd.Key);
} else {
status = exproto->ReadKeyStrokeEx(exproto, &kd);
}
gBS->CloseEvent(events[1]);
gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
gBS->SetTimer(events[1], TimerRelative, (uint64_t)10000000 * seconds);
if (status != EFI_SUCCESS) {
goto again;
}
if (kd.Key.UnicodeChar == '[') {
gBS->CloseEvent(events[1]);
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;
}
gBS->CloseEvent(events[1]);
return ret;
}
#endif

View file

@ -0,0 +1,28 @@
#ifndef LIB__GETCHAR_H__
#define LIB__GETCHAR_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);
#endif

907
limine/common/lib/gterm.c Normal file
View file

@ -0,0 +1,907 @@
#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 <lib/rand.h>
#include <mm/pmm.h>
#include <flanterm.h>
#include <flanterm_backends/fb.h>
#include <lib/term.h>
#include <sys/cpu.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;
if (xstart > xend) {
size_t tmp = xstart;
xstart = xend;
xend = tmp;
}
if (ystart > yend) {
size_t tmp = ystart;
ystart = yend;
yend = tmp;
}
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++) {
int64_t image_y = (int64_t)y - background->y_displacement;
size_t canvas_off = fb->framebuffer_width * y;
if (image_y < 0 || (uint64_t)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 */
const size_t off = img_pitch * (size_t)image_y;
for (size_t x = xstart; x < xend; x++) {
uint32_t pixel;
int64_t image_x = (int64_t)x - background->x_displacement;
if (image_x < 0 || (uint64_t)image_x >= background->x_size) {
pixel = background->back_colour;
} else {
pixel = *(uint32_t*)(img + (size_t)image_x * colsize + off);
}
uint32_t i = blend(fb, x, y, 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) {
// Free previous canvas if it exists
if (bg_canvas != NULL) {
pmm_free(bg_canvas, bg_canvas_size);
}
bg_canvas_size = fb->framebuffer_width * fb->framebuffer_height * sizeof(uint32_t);
bg_canvas = ext_mem_alloc(bg_canvas_size);
// Clamp margin to half the framebuffer dimensions to prevent underflow
size_t max_margin = fb->framebuffer_width / 2;
if (fb->framebuffer_height / 2 < max_margin) {
max_margin = fb->framebuffer_height / 2;
}
size_t effective_margin = margin > max_margin ? max_margin : margin;
size_t effective_margin_gradient = margin_gradient > effective_margin ? effective_margin : margin_gradient;
int64_t margin_no_gradient = (int64_t)effective_margin - effective_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 - effective_margin;
size_t gradient_stop_y = fb->framebuffer_height - effective_margin;
if (effective_margin_gradient) {
loop_margin(fb, margin_no_gradient, scan_stop_x, margin_no_gradient, effective_margin);
loop_margin(fb, margin_no_gradient, scan_stop_x, gradient_stop_y, scan_stop_y);
loop_margin(fb, margin_no_gradient, effective_margin, effective_margin, gradient_stop_y);
loop_margin(fb, gradient_stop_x, scan_stop_x, effective_margin, gradient_stop_y);
}
loop_internal(fb, effective_margin, gradient_stop_x, effective_margin, gradient_stop_y);
} else {
bg_canvas = NULL;
}
}
#if defined (__riscv)
__attribute__((target("arch=+zicbom")))
static void riscv_flush_callback(volatile void *base, size_t length) {
const size_t cbom_block_size = 0x40;
uintptr_t start = ALIGN_DOWN((uintptr_t)base, cbom_block_size);
uintptr_t end = ALIGN_UP((uintptr_t)(base + length), cbom_block_size);
for (uintptr_t ptr = start; ptr < end; ptr += cbom_block_size) {
asm volatile("cbo.flush (%0)" :: "r"(ptr) : "memory");
}
}
static void riscv_flush_callback_nozicbom(volatile void *base, size_t length) {
(void)base;
(void)length;
// Without Zicbom, there is no portable instruction to flush dirty cache lines.
// Read through a dedicated eviction buffer to create cache pressure and displace
// dirty framebuffer lines. 128 KB covers typical RISC-V L1 D-caches (32-64 KB).
static volatile uint8_t *eviction_buf = NULL;
#define EVICTION_BUF_SIZE (128 * 1024)
if (eviction_buf == NULL) {
eviction_buf = ext_mem_alloc(EVICTION_BUF_SIZE);
}
volatile uint64_t *p = (volatile uint64_t *)eviction_buf;
for (size_t i = 0; i < EVICTION_BUF_SIZE / sizeof(uint64_t); i += (64 / sizeof(uint64_t))) {
(void)p[i];
}
asm volatile ("fence rw, rw" ::: "memory");
}
#elif defined (__aarch64__)
static void aarch64_flush_callback(volatile void *base, size_t length) {
clean_dcache_poc((uintptr_t)base, (uintptr_t)base + length);
}
#endif
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;
int fb_rotation = FLANTERM_FB_ROTATE_0;
char *rotation_str = config_get_value(config, 0, "INTERFACE_ROTATION");
if (rotation_str != NULL) {
int rotation_val = strtoui(rotation_str, NULL, 10);
switch (rotation_val) {
case 90: fb_rotation = FLANTERM_FB_ROTATE_90; break;
case 180: fb_rotation = FLANTERM_FB_ROTATE_180; break;
case 270: fb_rotation = FLANTERM_FB_ROTATE_270; break;
}
}
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);
}
size_t wallpaper_count = 0;
while (config_get_value(config, wallpaper_count, "WALLPAPER") != NULL)
wallpaper_count++;
background = NULL;
if (wallpaper_count > 0) {
char *background_path = config_get_value(config, rand32() % wallpaper_count, "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);
}
if (margin_gradient > margin) {
margin_gradient = margin;
}
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) {
if (!parse_resolution(&tmp_font_width, &tmp_font_height, NULL, menu_font_size)) {
print("Could not parse TERM_FONT_SIZE. Using default font.\n");
goto no_load_font;
}
if (tmp_font_width != 8) {
print("Font width must be 8, got %u. Using default font.\n", tmp_font_width);
goto no_load_font;
}
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", (uint64_t)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 {
if (font_size > f->size) {
print("Font size too large for provided font file. Not loading.\n");
fclose(f);
goto no_load_font;
}
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 (fb_rotation == FLANTERM_FB_ROTATE_90 || fb_rotation == FLANTERM_FB_ROTATE_270) {
uint64_t tmp = fb->framebuffer_width;
fb->framebuffer_width = fb->framebuffer_height;
fb->framebuffer_height = tmp;
}
if (background != NULL) {
char *background_layout = config_get_value(config, 0, "WALLPAPER_STYLE");
if (background_layout != NULL && strcmp(background_layout, "centered") == 0) {
char *background_colour = config_get_value(config, 0, "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) {
font_scale_x = 1;
font_scale_y = 1;
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;
}
}
if (fb_rotation == FLANTERM_FB_ROTATE_90 || fb_rotation == FLANTERM_FB_ROTATE_270) {
uint64_t tmp = fb->framebuffer_width;
fb->framebuffer_width = fb->framebuffer_height;
fb->framebuffer_height = tmp;
}
terms[terms_i] = flanterm_fb_init(ext_mem_alloc_size_t,
pmm_free_size_t,
(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, fb_rotation);
if (terms[terms_i] != NULL) {
terms_i++;
}
if (bg_canvas != NULL) {
pmm_free(bg_canvas, bg_canvas_size);
bg_canvas = NULL;
}
}
pmm_free(font, FONT_MAX);
if (background != NULL) {
image_close(background);
background = NULL;
}
if (terms_i == 0) {
pmm_free(terms, fbs_count * sizeof(void *));
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);
#if defined (__riscv)
if (riscv_check_isa_extension("zicbom", NULL, NULL)) {
flanterm_fb_set_flush_callback(term, riscv_flush_callback);
} else {
flanterm_fb_set_flush_callback(term, riscv_flush_callback_nozicbom);
}
#elif defined (__aarch64__)
flanterm_fb_set_flush_callback(term, aarch64_flush_callback);
#endif
}
term_backend = GTERM;
prev_config = config;
prev_height = height;
prev_width = width;
prev_valid = true;
return true;
}

11
limine/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

95
limine/common/lib/guid.c Normal file
View file

@ -0,0 +1,95 @@
#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;
}
static void uint_to_hex(uint32_t num, char *dest, int len) {
const char digits[] = "0123456789abcdef";
for (int i = 0; i < len; i++) {
dest[i] = digits[(num >> ((len - 1 - i) * 4)) & 0xf];
}
}
void guid_to_string(const struct guid *guid, char *s) {
uint_to_hex(guid->a, s, 8);
s[8] = '-';
uint_to_hex(guid->b, s + 9, 4);
s[13] = '-';
uint_to_hex(guid->c, s + 14, 4);
s[18] = '-';
uint_to_hex(guid->d[0], s + 19, 2);
uint_to_hex(guid->d[1], s + 21, 2);
s[23] = '-';
uint_to_hex(guid->d[2], s + 24, 2);
uint_to_hex(guid->d[3], s + 26, 2);
uint_to_hex(guid->d[4], s + 28, 2);
uint_to_hex(guid->d[5], s + 30, 2);
uint_to_hex(guid->d[6], s + 32, 2);
uint_to_hex(guid->d[7], s + 34, 2);
s[36] = 0;
}

20
limine/common/lib/guid.h Normal file
View file

@ -0,0 +1,20 @@
#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);
// Assumption: s must be big enough to fit 36 characters and a null byte
void guid_to_string(const struct guid *guid, char *s);
#endif

65
limine/common/lib/image.c Normal file
View file

@ -0,0 +1,65 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/image.h>
#include <lib/config.h>
#include <lib/misc.h>
#include <mm/pmm.h>
#include <lib/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 = (int64_t)frame_x_size / 2 - (int64_t)image->x_size / 2;
image->y_displacement = (int64_t)frame_y_size / 2 - (int64_t)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;
size_t pixel_count = (size_t)x * (size_t)y;
for (size_t i = 0; i < pixel_count; 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
limine/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;
int64_t x_displacement;
int64_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

131
limine/common/lib/libc.c Normal file
View file

@ -0,0 +1,131 @@
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <lib/libc.h>
#include <lib/misc.h>
#include <mm/pmm.h>
// Slightly adapted strtoul() implementation from FreeBSD.
// https://github.com/freebsd/freebsd-src/blob/de1aa3dab23c06fec962a14da3e7b4755c5880cf/lib/libc/stdlib/strtoul.c
unsigned long strtoul(const char *nptr, char **endptr, int base) {
const char *s;
unsigned long acc;
char c;
unsigned long cutoff;
int neg, any, cutlim;
s = nptr;
do {
c = *s++;
} while (isspace((unsigned char)c));
if (c == '-') {
neg = 1;
c = *s++;
} else {
neg = 0;
if (c == '+')
c = *s++;
}
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X') &&
((s[1] >= '0' && s[1] <= '9') ||
(s[1] >= 'A' && s[1] <= 'F') ||
(s[1] >= 'a' && s[1] <= 'f'))) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;
if (base < 2 || base > 36)
goto noconv;
cutoff = ULONG_MAX / base;
cutlim = ULONG_MAX % base;
for ( ; ; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'A' && c <= 'Z')
c -= 'A' - 10;
else if (c >= 'a' && c <= 'z')
c -= 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = ULONG_MAX;
//errno = ERANGE;
} else if (!any) {
noconv:
;//errno = EINVAL;
} else if (neg)
acc = -acc;
if (endptr != NULL)
*endptr = (char *)(any ? s - 1 : nptr);
return (acc);
}
size_t strnlen(const char *str, size_t maxlen) {
size_t len;
for (len = 0; len < maxlen && str[len]; len++);
return len;
}
void *memchr(const void *ptr, int ch, size_t n) {
uint8_t *p = (uint8_t *)ptr;
for (size_t i = 0; i < n; i++) {
if (p[i] == ch) {
return (void *)ptr + i;
}
}
return NULL;
}
char *strchr(const char *str, int ch) {
for (size_t i = 0; ; i++) {
if (str[i] == (char)ch) {
return (char *)str + i;
}
if (str[i] == '\0') {
return NULL;
}
}
}
char *strrchr(const char *str, int ch) {
char *p = NULL;
for (size_t i = 0; ; i++) {
if (str[i] == (char)ch) {
p = (char *)str + i;
}
if (str[i] == '\0') {
break;
}
}
return p;
}
char *strdup(const char *s) {
size_t len = strlen(s) + 1;
char *buf = ext_mem_alloc(len);
memcpy(buf, s, len);
return buf;
}

36
limine/common/lib/libc.h Normal file
View file

@ -0,0 +1,36 @@
#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 *restrict, const void *restrict, size_t);
int memcmp(const void *, const void *, size_t);
void *memmove(void *, const void *, size_t);
void *memchr(const void *, int, size_t);
char *strcpy(char *, const char *);
char *strncpy(char *, const char *, size_t);
char *strchr(const char *, int);
char *strrchr(const char *, int);
size_t strlen(const char *);
size_t strnlen(const char *, size_t);
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);
char *strdup(const char *);
#endif

149
limine/common/lib/libc.s2.c Normal file
View file

@ -0,0 +1,149 @@
#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++) {
unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i];
if (c1 != c2) {
return c1 < c2 ? -1 : 1;
}
if (c1 == 0) {
return 0;
}
}
}
int strcasecmp(const char *s1, const char *s2) {
for (size_t i = 0; ; i++) {
unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i];
if (tolower(c1) != tolower(c2)) {
return c1 < c2 ? -1 : 1;
}
if (c1 == 0) {
return 0;
}
}
}
int strncmp(const char *s1, const char *s2, size_t n) {
for (size_t i = 0; i < n; i++) {
unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i];
if (c1 != c2) {
return c1 < c2 ? -1 : 1;
}
if (c1 == 0) {
return 0;
}
}
return 0;
}
int strncasecmp(const char *s1, const char *s2, size_t n) {
for (size_t i = 0; i < n; i++) {
unsigned char c1 = ((unsigned char *)s1)[i], c2 = ((unsigned char *)s2)[i];
if (tolower(c1) != tolower(c2)) {
return c1 < c2 ? -1 : 1;
}
if (c1 == 0) {
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 (i < 3) {
// Expect '.' delimiter between octets
if (*current != '.')
return -1;
current++;
} else {
// After last octet, string must end
if (*current != 0)
return -1;
}
if (value > 255)
return -1;
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

View file

@ -0,0 +1,53 @@
#include <stdint.h>
#include <stddef.h>
void *memcpy(void *restrict dest, const void *restrict src, size_t n) {
uint8_t *restrict pdest = (uint8_t *restrict)dest;
const uint8_t *restrict psrc = (const uint8_t *restrict)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 ((uintptr_t)src > (uintptr_t)dest) {
for (size_t i = 0; i < n; i++) {
pdest[i] = psrc[i];
}
} else if ((uintptr_t)src < (uintptr_t)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;
}

417
limine/common/lib/misc.c Normal file
View file

@ -0,0 +1,417 @@
#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 <lib/config.h>
#include <lib/uri.h>
#include <lib/bli.h>
#include <fs/file.h>
#include <mm/pmm.h>
#include <libfdt.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, efi_mmap_key = 0;
UINT32 efi_desc_ver = 0;
#endif
bool editor_enabled = true;
bool help_hidden = false;
uint64_t usec_at_bootloader_entry;
#if defined (UEFI)
bool is_efi_serial_present(void) {
EFI_STATUS status;
EFI_SERIAL_IO_PROTOCOL *serial_io = NULL;
EFI_GUID serial_io_guid = EFI_SERIAL_IO_PROTOCOL_GUID;
status = gBS->LocateProtocol(&serial_io_guid, NULL, (void **)&serial_io);
if (status) {
return false;
}
if (serial_io == NULL) {
return false;
}
UINT32 control;
status = serial_io->GetControl(serial_io, &control);
if (status) {
return false;
}
return true;
}
#endif
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;
}
void *get_device_tree_blob(const char *config, size_t extra_size) {
int ret;
size_t size = 0;
void *dtb = NULL;
{
char *dtb_path = NULL;
bool soft_panic;
if (config != NULL) {
dtb_path = config_get_value(config, 0, "dtb_path");
soft_panic = true;
}
if (dtb_path == NULL) {
dtb_path = config_get_value(NULL, 0, "global_dtb");
soft_panic = false;
}
if (dtb_path != NULL) {
struct file_handle *dtb_file;
if ((dtb_file = uri_open(dtb_path)) == NULL)
panic(soft_panic, "dtb: Failed to open device tree blob with path `%#`. Is the path correct?", dtb_path);
dtb = freadall(dtb_file, MEMMAP_BOOTLOADER_RECLAIMABLE);
size = dtb_file->size;
fclose(dtb_file);
printv("dtb: loaded dtb at %p from file `%#`\n", dtb, dtb_path);
}
}
#if defined (UEFI)
if (!dtb) {
EFI_GUID dtb_guid = EFI_DTB_TABLE_GUID;
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
if (memcmp(&cur_table->VendorGuid, &dtb_guid, sizeof(EFI_GUID)))
continue;
size = fdt_totalsize(cur_table->VendorTable);
dtb = ext_mem_alloc(size);
ret = fdt_open_into(cur_table->VendorTable, dtb, size);
if (ret < 0) {
panic(true, "dtb: failed to resize new DTB");
}
printv("dtb: found dtb at %p via EFI\n", cur_table->VendorTable);
break;
}
}
#endif
if (extra_size == 0) {
return dtb;
}
if (dtb) {
printv("dtb: dtb has size %X\n", (uint64_t)size);
void *new_tab = ext_mem_alloc(size + extra_size);
ret = fdt_open_into(dtb, new_tab, size + extra_size);
if (ret < 0) {
panic(true, "dtb: failed to resize new DTB");
}
pmm_free(dtb, size);
return new_tab;
}
dtb = ext_mem_alloc(extra_size);
ret = fdt_create_empty_tree(dtb, extra_size);
if (ret < 0) {
panic(true, "dtb: failed to create a device tree blob: '%s'", fdt_strerror(ret));
}
ret = fdt_setprop_u32(dtb, 0, "#address-cells", 2);
if (ret < 0) {
panic(true, "dtb: failed to set #address-cells: '%s'", fdt_strerror(ret));
}
ret = fdt_setprop_u32(dtb, 0, "#size-cells", 1);
if (ret < 0) {
panic(true, "dtb: failed to set #size-cells: '%s'", fdt_strerror(ret));
}
return dtb;
}
#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);
gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &efi_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;
}
bli_on_boot();
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, &efi_mmap_key, &efi_desc_size, &efi_desc_ver);
if (retries == 0 && status) {
goto fail;
}
// Be gone, UEFI!
status = gBS->ExitBootServices(efi_image_handle, efi_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 (__riscv)
asm volatile ("csrci sstatus, 0x2" ::: "memory");
#elif defined (__loongarch64)
asm volatile ("csrxchg $r0, %0, 0x0" :: "r" (0x4) : "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;
if (orig_entry->NumberOfPages == 0) {
continue;
}
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 = 0;
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 = 0;
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 = 0;
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

130
limine/common/lib/misc.h Normal file
View file

@ -0,0 +1,130 @@
#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 (__riscv)
# include <efi/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, efi_mmap_key;
extern UINT32 efi_desc_ver;
extern bool efi_boot_services_exited;
bool efi_exit_boot_services(void);
bool is_efi_serial_present(void);
#endif
void *get_device_tree_blob(const char *config, size_t extra_size);
extern struct volume *boot_volume;
#if defined (BIOS)
extern bool stage3_loaded;
#endif
extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic;
extern uint64_t usec_at_bootloader_entry;
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf);
bool get_absolute_path(char *path_ptr, const char *path, const char *pwd, size_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);
#define MIN(a, b) ({ \
__auto_type MIN_a = (a); \
__auto_type MIN_b = (b); \
MIN_a > MIN_b ? MIN_b : MIN_a; \
})
#define MAX(a, b) ({ \
__auto_type MAX_a = (a); \
__auto_type MAX_b = (b); \
MAX_a > MAX_b ? MAX_a : MAX_b; \
})
#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 (__riscv)
noreturn void riscv_spinup(uint64_t entry, uint64_t sp, uint64_t satp, uint64_t direct_map_offset);
#if defined (UEFI)
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void);
#endif
#elif defined (__loongarch64)
noreturn void loongarch_spinup(uint64_t entry, uint64_t sp, uint64_t pgdl,
uint64_t pgdh, uint64_t direct_map_offset);
#else
#error Unknown architecture
#endif
#define no_unwind __attribute__((section(".no_unwind")))
#define MEM_RANGE_X 1
#define MEM_RANGE_W 2
#define MEM_RANGE_R 4
struct mem_range {
uint64_t base;
uint64_t length;
uint64_t permissions;
};
#endif

129
limine/common/lib/misc.s2.c Normal file
View file

@ -0,0 +1,129 @@
#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 || d >= base) {
if (end != NULL)
*end = &s[i];
break;
}
uint64_t mul_result;
if (__builtin_mul_overflow(n, (uint64_t)base, &mul_result)) {
if (end != NULL)
*end = &s[i];
return UINT64_MAX;
}
if (__builtin_add_overflow(mul_result, (uint64_t)d, &n)) {
if (end != NULL)
*end = &s[i];
return UINT64_MAX;
}
}
return n;
}
bool get_absolute_path(char *path_ptr, const char *path, const char *pwd, size_t size) {
char *orig_ptr = path_ptr;
char *end_ptr = path_ptr + size - 1;
if (size == 0) return false;
if (!*path) {
size_t pwd_len = strlen(pwd);
if (pwd_len >= size) return false;
memcpy(path_ptr, pwd, pwd_len + 1);
return true;
}
if (*path != '/') {
size_t pwd_len = strlen(pwd);
if (pwd_len >= size) return false;
memcpy(path_ptr, pwd, pwd_len + 1);
path_ptr += pwd_len;
} 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) != '/')) {
if (path_ptr >= end_ptr) return false;
*path_ptr = '/';
path_ptr++;
}
continue;
case '\0':
term:
if ((*(path_ptr - 1) == '/') && ((path_ptr - 1) != orig_ptr))
path_ptr--;
*path_ptr = 0;
return true;
default:
if (path_ptr >= end_ptr) return false;
*path_ptr = *path;
path++;
path_ptr++;
continue;
}
}
}

View file

@ -0,0 +1,89 @@
#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/getchar.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 &&
#elif defined (UEFI)
efi_boot_services_exited == false &&
#endif
allow_menu == true) {
print("Press a key to return to %s.", booting_from_editor ? "editor" : "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 (__riscv)
asm ("wfi");
#elif defined (__loongarch64)
asm ("idle 0");
#else
#error Unknown architecture
#endif
}
#endif
}
}

110
limine/common/lib/part.h Normal file
View file

@ -0,0 +1,110 @@
#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;
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;
};
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

645
limine/common/lib/part.s2.c Normal file
View file

@ -0,0 +1,645 @@
#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;
// Check for overflow in sector offset calculation
uint64_t block_offset;
if (__builtin_mul_overflow(block, volume->fastest_xfer_size, &block_offset)) {
return false;
}
uint64_t read_sector;
if (__builtin_add_overflow(first_sect, block_offset, &read_sector)) {
return false;
}
// Clamp xfer_size to remaining sectors in volume
if (volume->sect_count != (uint64_t)-1) {
uint64_t volume_sectors = volume->sect_count / (volume->sector_size / 512);
uint64_t end_sector;
if (__builtin_add_overflow(first_sect, volume_sectors, &end_sector)) {
end_sector = UINT64_MAX;
}
if (read_sector >= end_sector) {
return false;
}
uint64_t remaining = end_sector - read_sector;
if (xfer_size > remaining) {
xfer_size = remaining;
}
}
int ret = disk_read_sectors(volume, volume->cache, read_sector, xfer_size);
if (ret != DISK_SUCCESS) {
return false;
}
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");
}
if (volume->sect_count != (uint64_t)-1) {
// sect_count is always in 512-byte sectors for both whole disks and partitions
uint64_t part_size;
if (__builtin_mul_overflow(volume->sect_count, (uint64_t)512, &part_size)) {
return false;
}
if (loc >= part_size || count > part_size - loc) {
return false;
}
}
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
if (!volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header)))
continue;
// 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
if (!volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header)))
continue;
// 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;
// Validate partition entry size (must be at least as large as our struct)
uint32_t entry_size = header.size_of_partition_entry;
if (entry_size < sizeof(struct gpt_entry)) {
return INVALID_TABLE;
}
// Check for potential integer overflow in offset calculation
uint64_t entry_offset;
if (__builtin_mul_overflow((uint64_t)header.partition_entry_lba, (uint64_t)lb_size, &entry_offset)) {
return INVALID_TABLE; // Multiplication overflow
}
// Use actual entry size from header for offset calculation
uint64_t partition_offset = (uint64_t)partition * entry_size;
if (__builtin_add_overflow(entry_offset, partition_offset, &entry_offset)) {
return INVALID_TABLE; // Addition overflow would occur
}
struct gpt_entry entry = {0};
if (!volume_read(volume, &entry, entry_offset, sizeof(entry))) {
return END_OF_TABLE;
}
struct guid empty_guid = {0};
if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid)))
return NO_PARTITION;
// Validate that ending_lba >= starting_lba to prevent underflow
if (entry.ending_lba < entry.starting_lba) {
return NO_PARTITION; // Invalid partition geometry
}
// Calculate sector multiplier for lb_size conversion
uint64_t sect_multiplier = lb_size / 512;
// Check for overflow in first_sect calculation
uint64_t first_sect_result;
if (__builtin_mul_overflow(entry.starting_lba, sect_multiplier, &first_sect_result)) {
return NO_PARTITION; // Overflow in first_sect
}
// Check for overflow in sect_count calculation
// First compute partition size in logical blocks
// Check if +1 would overflow (ending_lba == UINT64_MAX)
uint64_t partition_size = entry.ending_lba - entry.starting_lba;
if (partition_size == UINT64_MAX) {
return NO_PARTITION; // Partition size +1 would overflow
}
uint64_t partition_blocks = partition_size + 1;
uint64_t sect_count_result;
if (__builtin_mul_overflow(partition_blocks, sect_multiplier, &sect_count_result)) {
return NO_PARTITION; // Overflow in sect_count
}
#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 = first_sect_result;
ret->sect_count = sect_count_result;
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;
if (!volume_read(volume, &hint, 446, sizeof(uint8_t)))
return false;
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
if (!volume_read(volume, &hint, 462, sizeof(uint8_t)))
return false;
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
if (!volume_read(volume, &hint, 478, sizeof(uint8_t)))
return false;
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
if (!volume_read(volume, &hint, 494, sizeof(uint8_t)))
return false;
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
return false;
char hintc[64];
if (!volume_read(volume, hintc, 3, 4))
return false;
if (memcmp(hintc, "NTFS", 4) == 0)
return false;
if (!volume_read(volume, hintc, 54, 3))
return false;
if (memcmp(hintc, "FAT", 3) == 0)
return false;
if (!volume_read(volume, hintc, 82, 3))
return false;
if (memcmp(hintc, "FAT", 3) == 0)
return false;
if (!volume_read(volume, hintc, 3, 5))
return false;
if (memcmp(hintc, "FAT32", 5) == 0)
return false;
if (!volume_read(volume, &hint, 1080, sizeof(uint16_t)))
return false;
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;
if (!volume_read(volume, &ret, 0x1b8, sizeof(uint32_t))) {
return 0;
}
return ret;
}
// Maximum number of logical partitions to prevent infinite loops from circular EBR chains
#define MAX_LOGICAL_PARTITIONS 256
static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part,
int partition) {
struct mbr_entry entry;
// Limit partition index to prevent excessive iteration
if (partition >= MAX_LOGICAL_PARTITIONS) {
return END_OF_TABLE;
}
uint64_t ebr_sector = 0;
uint64_t prev_ebr_sector = 0;
for (int i = 0; i < partition; i++) {
uint64_t entry_offset = ebr_sector * 512 + 0x1ce;
if (!volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry))) {
return END_OF_TABLE;
}
if (entry.type != 0x0f && entry.type != 0x05) {
return END_OF_TABLE;
}
prev_ebr_sector = ebr_sector;
ebr_sector = entry.first_sect;
// Detect circular chain: if new sector points to 0 or backwards, it's invalid
// (EBR sectors should always increase within the extended partition)
if (ebr_sector == 0 || (i > 0 && ebr_sector <= prev_ebr_sector)) {
return END_OF_TABLE; // Circular or corrupted EBR chain
}
// Also check that ebr_sector is within the extended partition bounds
if (ebr_sector >= extended_part->sect_count) {
return END_OF_TABLE; // EBR points outside extended partition
}
}
uint64_t entry_offset = ebr_sector * 512 + 0x1be;
if (!volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry))) {
return END_OF_TABLE;
}
if (entry.type == 0)
return NO_PARTITION;
// Validate sect_count is non-zero
if (entry.sect_count == 0) {
return NO_PARTITION;
}
// Check for overflow in first_sect calculation
uint64_t first_sect_64;
if (__builtin_add_overflow(extended_part->first_sect, ebr_sector, &first_sect_64)) {
return NO_PARTITION; // Addition overflow
}
if (__builtin_add_overflow(first_sect_64, (uint64_t)entry.first_sect, &first_sect_64)) {
return NO_PARTITION; // Addition overflow
}
uint64_t partition_end;
if (__builtin_add_overflow(first_sect_64, (uint64_t)entry.sect_count, &partition_end)) {
return NO_PARTITION; // Partition would overflow
}
#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 = first_sect_64;
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;
if (!volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry))) {
continue;
}
if (entry.type != 0x0f && entry.type != 0x05)
continue;
// Validate extended partition has non-zero size
if (entry.sect_count == 0) {
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;
if (!volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry))) {
return END_OF_TABLE;
}
if (entry.type == 0)
return NO_PARTITION;
// Validate sect_count is non-zero
if (entry.sect_count == 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;
// Validate partition index is non-negative
if (partition < 0) {
return NO_PARTITION;
}
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

496
limine/common/lib/pe.c Normal file
View file

@ -0,0 +1,496 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/misc.h>
#include <lib/libc.h>
#include <lib/pe.h>
#include <lib/print.h>
#include <lib/rand.h>
#include <mm/pmm.h>
#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
#define IMAGE_DOS_SIGNATURE 0x5a4d
typedef struct _IMAGE_DOS_HEADER {
uint16_t e_magic;
uint16_t e_cblp;
uint16_t e_cp;
uint16_t e_crlc;
uint16_t e_cparhdr;
uint16_t e_minalloc;
uint16_t e_maxalloc;
uint16_t e_ss;
uint16_t e_sp;
uint16_t e_csum;
uint16_t e_ip;
uint16_t e_cs;
uint16_t e_lfarlc;
uint16_t e_ovno;
uint16_t e_res[4];
uint16_t e_oemid;
uint16_t e_oeminfo;
uint16_t e_res2[10];
uint32_t e_lfanew;
} IMAGE_DOS_HEADER;
#define IMAGE_FILE_MACHINE_I386 0x14c
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
#define IMAGE_FILE_MACHINE_RISCV64 0x5064
#define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264
#define IMAGE_FILE_RELOCS_STRIPPED 1
#define IMAGE_FILE_EXECUTABLE_IMAGE 2
typedef struct {
uint16_t Machine;
uint16_t NumberOfSections;
uint32_t TimeDateStamp;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
uint16_t SizeOfOptionalHeader;
uint16_t Characteristics;
} IMAGE_FILE_HEADER;
typedef struct {
uint32_t VirtualAddress;
uint32_t Size;
} IMAGE_DATA_DIRECTORY;
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define IMAGE_DIRECTORY_ENTRY_TLS 9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
#define IMAGE_DIRECTORY_ENTRY_IAT 12
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
typedef struct {
uint16_t Magic;
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint;
uint32_t BaseOfCode;
uint64_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t CheckSum;
uint16_t Subsystem;
uint16_t DllCharacteristics;
uint64_t SizeOfStackReserve;
uint64_t SizeOfStackCommit;
uint64_t SizeOfHeapReserve;
uint64_t SizeOfHeapCommit;
uint32_t LoaderFlags;
uint32_t NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER64;
#define IMAGE_NT_SIGNATURE 0x4550
typedef struct {
uint32_t Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64;
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
#define IMAGE_SCN_MEM_READ 0x40000000
#define IMAGE_SCN_MEM_WRITE 0x80000000
typedef struct {
char Name[8];
uint32_t VirtualSize;
uint32_t VirtualAddress;
uint32_t SizeOfRawData;
uint32_t PointerToRawData;
uint32_t PointerToRelocations;
uint32_t PointerToLinenumbers;
uint16_t NumberOfRelocations;
uint16_t NumberOfLinenumbers;
uint32_t Characteristics;
} IMAGE_SECTION_HEADER;
typedef struct {
union {
uint32_t Characteristics;
uint32_t OriginalFirstThunk;
};
uint32_t TimeDateStamp;
uint32_t ForwarderChain;
uint32_t Name;
uint32_t FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
#define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGHLOW 3
#define IMAGE_REL_BASED_DIR64 10
typedef struct {
uint32_t VirtualAddress;
uint32_t SizeOfBlock;
} IMAGE_BASE_RELOCATION_BLOCK;
static void pe64_validate(uint8_t *image, size_t file_size) {
IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image;
if (file_size < sizeof(IMAGE_DOS_HEADER)) {
panic(true, "pe: File too small for DOS header");
}
if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) {
panic(true, "pe: Not a valid PE file");
}
if (file_size < sizeof(IMAGE_NT_HEADERS64)) {
panic(true, "pe: File too small for NT headers");
}
if (dos_hdr->e_lfanew > file_size - sizeof(IMAGE_NT_HEADERS64)) {
panic(true, "pe: e_lfanew offset out of bounds");
}
IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew);
if (nt_hdrs->Signature != IMAGE_NT_SIGNATURE) {
panic(true, "pe: Not a valid PE file");
}
if (nt_hdrs->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
panic(true, "pe: Not a valid PE32+ file");
}
#if defined(__x86_64__) || defined(__i386__)
if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
panic(true, "pe: Not an x86-64 PE file");
}
#elif defined(__aarch64__)
if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_ARM64) {
panic(true, "pe: Not an ARM64 PE file");
}
#elif defined (__riscv) && (__riscv_xlen == 64)
if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_RISCV64) {
panic(true, "pe: Not a RISC-V PE file");
}
#elif defined (__loongarch__) && (__loongarch_grlen == 64)
if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_LOONGARCH64) {
panic(true, "pe: Not a loongarch64 PE file");
}
#else
#error Unknown architecture
#endif
}
int pe_bits(uint8_t *image, size_t image_size) {
if (image_size < sizeof(IMAGE_DOS_HEADER)) {
return -1;
}
IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image;
if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) {
return -1;
}
if (image_size < sizeof(IMAGE_NT_HEADERS64)) {
return -1;
}
if ((size_t)dos_hdr->e_lfanew > image_size - sizeof(IMAGE_NT_HEADERS64)) {
return -1;
}
IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew);
if (nt_hdrs->Signature != IMAGE_NT_SIGNATURE) {
return -1;
}
switch (nt_hdrs->FileHeader.Machine) {
case IMAGE_FILE_MACHINE_I386:
return 32;
case IMAGE_FILE_MACHINE_AMD64:
case IMAGE_FILE_MACHINE_ARM64:
case IMAGE_FILE_MACHINE_RISCV64:
case IMAGE_FILE_MACHINE_LOONGARCH64:
return 64;
}
return -1;
}
bool pe64_load(uint8_t *image, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_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) {
pe64_validate(image, file_size);
IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image;
IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew);
// Validate SizeOfOptionalHeader doesn't cause sections pointer to go out of bounds
size_t sections_offset = dos_hdr->e_lfanew + sizeof(uint32_t) + sizeof(IMAGE_FILE_HEADER) + nt_hdrs->FileHeader.SizeOfOptionalHeader;
size_t sections_end = sections_offset + (size_t)nt_hdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
if (sections_end > file_size) {
panic(true, "pe: Section headers extend beyond file bounds");
}
IMAGE_SECTION_HEADER *sections = (IMAGE_SECTION_HEADER *)((uintptr_t)&nt_hdrs->OptionalHeader + nt_hdrs->FileHeader.SizeOfOptionalHeader);
bool is_reloc = true;
if (nt_hdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
is_reloc = false;
}
if (_is_reloc) {
*_is_reloc = is_reloc;
}
uint64_t image_base = nt_hdrs->OptionalHeader.ImageBase;
uint64_t image_size = nt_hdrs->OptionalHeader.SizeOfImage;
uint64_t alignment = nt_hdrs->OptionalHeader.SectionAlignment;
if (alignment > 1 && (alignment & (alignment - 1)) != 0) {
panic(true, "pe: SectionAlignment is not a power of 2");
}
bool lower_to_higher = false;
if (image_base < FIXED_HIGHER_HALF_OFFSET_64) {
if (!is_reloc) {
panic(true, "pe: Lower half images are not allowed");
}
lower_to_higher = true;
}
uint64_t slide = 0;
size_t try_count = 0;
size_t max_simulated_tries = 0x10000;
if (lower_to_higher) {
slide = FIXED_HIGHER_HALF_OFFSET_64 - image_base;
}
*physical_base = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, alignment);
*virtual_base = image_base;
// Validate SizeOfHeaders doesn't exceed file size or image size
if (nt_hdrs->OptionalHeader.SizeOfHeaders > file_size
|| nt_hdrs->OptionalHeader.SizeOfHeaders > image_size) {
panic(true, "pe: SizeOfHeaders exceeds file or image size");
}
memcpy((void *)(uintptr_t)*physical_base, image, nt_hdrs->OptionalHeader.SizeOfHeaders);
if (_image_size) {
*_image_size = image_size;
}
if (is_reloc && kaslr) {
again:
slide = (rand32() & ~(alignment - 1)) + (lower_to_higher ? FIXED_HIGHER_HALF_OFFSET_64 - image_base : 0);
if (*virtual_base + slide + image_size < 0xffffffff80000000 /* this comparison relies on overflow */) {
if (++try_count == max_simulated_tries) {
panic(true, "pe: Image wants to load too high");
}
goto again;
}
}
for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) {
IMAGE_SECTION_HEADER *section = &sections[i];
uintptr_t section_base = *physical_base + section->VirtualAddress;
uint32_t section_raw_size = section->VirtualSize < section->SizeOfRawData ? section->VirtualSize : section->SizeOfRawData;
// Validate section doesn't write past the image buffer
if ((uint64_t)section->VirtualAddress + section_raw_size > image_size) {
panic(true, "pe: Section %U exceeds image bounds", (uint64_t)i);
}
// Validate section data doesn't exceed file bounds
if ((uint64_t)section->PointerToRawData + section_raw_size > file_size) {
panic(true, "pe: Section %U data extends beyond file bounds", (uint64_t)i);
}
memcpy((void *)section_base, image + section->PointerToRawData, section_raw_size);
}
if (nt_hdrs->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_BASERELOC + 1) {
panic(true, "pe: NumberOfRvaAndSizes too small for import/reloc directories");
}
IMAGE_DATA_DIRECTORY *import_dir = &nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
IMAGE_DATA_DIRECTORY *reloc_dir = &nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (import_dir->Size != 0) {
if (import_dir->VirtualAddress >= image_size ||
sizeof(IMAGE_IMPORT_DESCRIPTOR) > image_size - import_dir->VirtualAddress) {
panic(true, "pe: Import directory VirtualAddress out of bounds");
}
IMAGE_IMPORT_DESCRIPTOR *import_desc = (IMAGE_IMPORT_DESCRIPTOR *)((uintptr_t)*physical_base + import_dir->VirtualAddress);
if (import_desc->Name != 0) {
panic(true, "pe: Kernel must not have any imports");
}
}
if (reloc_dir->VirtualAddress != 0) {
if (reloc_dir->VirtualAddress >= image_size ||
reloc_dir->Size > image_size - reloc_dir->VirtualAddress) {
panic(true, "pe: Relocation directory VirtualAddress out of bounds");
}
size_t reloc_block_offset = 0;
while (reloc_dir->Size - reloc_block_offset >= sizeof(IMAGE_BASE_RELOCATION_BLOCK)) {
IMAGE_BASE_RELOCATION_BLOCK *block = (IMAGE_BASE_RELOCATION_BLOCK *)((uintptr_t)*physical_base + reloc_dir->VirtualAddress + reloc_block_offset);
// Validate SizeOfBlock to prevent infinite loop (if 0) and underflow (if too small)
if (block->SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION_BLOCK)) {
panic(true, "pe: Invalid relocation block size");
}
if (block->SizeOfBlock > reloc_dir->Size - reloc_block_offset) {
panic(true, "pe: Relocation block size exceeds directory");
}
if (block->VirtualAddress >= image_size) {
panic(true, "pe: Relocation block VirtualAddress out of bounds");
}
uintptr_t block_base = *physical_base + block->VirtualAddress;
size_t entries = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION_BLOCK)) / sizeof(uint16_t);
uint16_t *relocs = (uint16_t *)(block + 1);
for (size_t i = 0; i < entries; i++) {
uint16_t type = relocs[i] >> 12;
uint16_t offset = relocs[i] & 0xfff;
if (type == IMAGE_REL_BASED_ABSOLUTE) {
continue;
}
size_t write_size;
switch (type) {
case IMAGE_REL_BASED_HIGHLOW:
if (lower_to_higher) {
panic(true, "pe: 32-bit relocations are incompatible with higher-half loading");
}
write_size = 4;
break;
case IMAGE_REL_BASED_DIR64: write_size = 8; break;
default:
panic(true, "pe: Unsupported relocation type %u", type);
__builtin_unreachable();
}
if ((uint64_t)block->VirtualAddress + offset + write_size > image_size) {
panic(true, "pe: Relocation offset out of bounds");
}
switch (type) {
case IMAGE_REL_BASED_HIGHLOW:
*(uint32_t *)(block_base + offset) += slide;
break;
case IMAGE_REL_BASED_DIR64:
*(uint64_t *)(block_base + offset) += slide;
break;
}
}
reloc_block_offset += block->SizeOfBlock;
}
}
if (image_size_before_bss) {
*image_size_before_bss = image_size;
}
*virtual_base += slide;
*entry_point = *virtual_base + nt_hdrs->OptionalHeader.AddressOfEntryPoint;
if (_slide) {
*_slide = slide;
}
if (_ranges && _ranges_count) {
size_t range_count = 0;
bool headers_within_section = false;
for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) {
IMAGE_SECTION_HEADER *section = &sections[i];
if (section->VirtualAddress == 0) {
headers_within_section = true;
}
range_count++;
}
if (!headers_within_section) {
range_count++;
}
struct mem_range *ranges = ext_mem_alloc(range_count * sizeof(struct mem_range));
*_ranges = ranges;
*_ranges_count = range_count;
size_t range_index = 0;
if (!headers_within_section) {
struct mem_range *range = &ranges[range_index++];
range->base = *virtual_base;
range->length = ALIGN_UP(nt_hdrs->OptionalHeader.SizeOfHeaders, 0x1000);
range->permissions = MEM_RANGE_R;
}
for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) {
IMAGE_SECTION_HEADER *section = &sections[i];
uintptr_t misalign = section->VirtualAddress % alignment;
struct mem_range *range = &ranges[range_index++];
range->base = *virtual_base + ALIGN_DOWN(section->VirtualAddress, alignment);
range->length = ALIGN_UP(section->VirtualSize + misalign, alignment);
if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
range->permissions |= MEM_RANGE_X;
}
if (section->Characteristics & IMAGE_SCN_MEM_WRITE) {
range->permissions |= MEM_RANGE_W;
}
if (section->Characteristics & IMAGE_SCN_MEM_READ) {
range->permissions |= MEM_RANGE_R;
}
}
}
return true;
}

12
limine/common/lib/pe.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef LIB__PE_H__
#define LIB__PE_H__
#include <stdint.h>
#include <stdbool.h>
#include <lib/misc.h>
int pe_bits(uint8_t *image, size_t image_size);
bool pe64_load(uint8_t *image, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_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);
#endif

16
limine/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

View file

@ -0,0 +1,235 @@
#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};
r.eax = 0x0e00 | s[i];
rm_int(0x10, &r, &r);
}
}
#endif
static const char *base_digits = "0123456789abcdef";
#define PRINT_BUF_MAX 4096
static void prn_char(char *print_buf, size_t *print_buf_i, char c) {
if (c == '\n') {
prn_char(print_buf, print_buf_i, '\r');
}
if (*print_buf_i < (PRINT_BUF_MAX - 1)) {
print_buf[(*print_buf_i)++] = c;
}
print_buf[*print_buf_i] = 0;
}
static void prn_str(char *print_buf, size_t *print_buf_i, const char *string) {
size_t i;
for (i = 0; string[i]; i++) {
prn_char(print_buf, print_buf_i, string[i]);
}
}
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++) {
prn_char(print_buf, print_buf_i, string[i]);
}
}
static void prn_i(char *print_buf, size_t *print_buf_i, int64_t x) {
int i;
char buf[21] = {0};
if (!x) {
prn_char(print_buf, print_buf_i, '0');
return;
}
int sign = x < 0;
uint64_t ux = sign ? (uint64_t)0 - (uint64_t)x : (uint64_t)x;
for (i = 19; ux; i--) {
buf[i] = (ux % 10) + 0x30;
ux = ux / 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 (stage3_loaded && ((!quiet && serial) || COM_OUTPUT)) {
if (!isprint(print_buf[i])) {
switch (print_buf[i]) {
case '\r': case '\n': case '\e': break;
default: continue;
}
}
serial_out(print_buf[i]);
}
#endif
}
}

89
limine/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
limine/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

34
limine/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,113 @@
#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 direct_map_offset)
// 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
PICK_EL x8, 0f, 2f
0:
// Switch to the new page tables
// Point the EL1t handler to the continuation, such that after we page fault,
// execution continues and the kernel is entered.
adrp x8, 1f
add x8, x8, #:lo12:1f
add x8, x8, x7
msr vbar_el1, x8
isb
dsb sy
isb
// Switch the page table registers
msr mair_el1, x3
msr tcr_el1, x4
msr ttbr0_el1, x5
msr ttbr1_el1, x6
msr sctlr_el1, x2
isb
dsb sy
isb
// Jump to the higher half mapping in case we didn't immediately crash
br x8
// Alignment required by VBAR register
.align 11
1:
// Zero out VBAR to avoid confusion
msr vbar_el1, xzr
// Enter kernel in EL1
mov x8, #0x3c4
msr spsr_el1, x8
msr elr_el1, x0
mov x0, xzr
ZERO_REGS_EXCEPT_X0
eret
2:
// Check HCR_EL2.E2H
mrs x8, hcr_el2
tbnz x8, #34, 3f
// Configure EL1 state (normal silicon)
msr mair_el1, x3
msr tcr_el1, x4
msr ttbr0_el1, x5
msr ttbr1_el1, x6
msr sctlr_el1, x2
dsb sy
isb
b 4f
3:
// Configure EL1 state (apple silicon)
msr s3_5_c10_c2_0, x3 // MAIR_EL12
msr s3_5_c2_c0_2, x4 // TCR_EL12
msr s3_5_c2_c0_0, x5 // TTBR0_EL12
msr s3_5_c2_c0_1, x6 // TTBR1_EL12
msr s3_5_c1_c0_0, x2 // SCTLR_EL12
dsb sy
isb
4:
// 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, xzr
ZERO_REGS_EXCEPT_X0
eret
.section .note.GNU-stack,"",%progbits

View file

@ -0,0 +1,49 @@
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
mov cr3, eax
; Clear TSS busy bit and load TR with base 0, limit 0
sub esp, 8
sgdt [esp]
mov eax, [esp + 2]
add esp, 8
mov byte [eax + 0x3d], 0x89
mov ax, 0x38
ltr ax
call edi
section .note.GNU-stack noalloc noexec nowrite progbits

View file

@ -0,0 +1,110 @@
.section .text
#define PAGE_SHIFT 12
#define PT_SHIFT (PAGE_SHIFT - 3)
#define PT_BASE(level) (PAGE_SHIFT + PT_SHIFT * (level))
#define MAKE_PWCL(Dir2_width, Dir2_base, Dirl_width, Dirl_base, PTwidth, PTbase) \
((Dir2_width) << 25) | ((Dir2_base) << 20) | ((Dirl_width) << 15) | \
((Dirl_base) << 10) | ((PTwidth) << 5) | ((PTbase) << 0)
#define MAKE_PWCH(Dir4_width, Dir4_base, Dir3_width, Dir3_base) \
((Dir4_width) << 18) | ((Dir4_base) << 12) | ((Dir3_width) << 6) | \
((Dir3_base) << 0)
#define CSR_CRMD 0x00
#define CSR_EENTRY 0xc
#define CSR_PGDL 0x19
#define CSR_PGDH 0x1a
#define CSR_PGD 0x1b
#define CSR_PWCL 0x1c
#define CSR_PWCH 0x1d
#define CSR_STLBPS 0x1e
#define CSR_TLBRENTRY 0x88
#define CSR_TLBRSAVE 0x8b
#define CSR_TLBREHI 0x8e
#define CSR_MERRENTRY 0x93
#define CSR_DMW0 0x180
#define CSR_DMW1 0x181
#define CSR_DMW2 0x182
#define CSR_DMW3 0x183
.global loongarch_spinup
loongarch_spinup:
li.d $t0, 0b010001 // MAT=01, PLV1..3=0, PLV0=1
csrwr $t0, CSR_DMW0
csrwr $zero, CSR_DMW1
csrwr $zero, CSR_DMW2
csrwr $zero, CSR_DMW3
li.d $t0, 0b010110000 // DATF=01, DATM=01, PG=1, DA=0, IE=0, PLV=00
csrwr $t0, CSR_CRMD
invtlb 0, $zero, $zero
li.d $t0, PAGE_SHIFT
csrwr $t0, CSR_STLBPS
csrwr $t0, CSR_TLBREHI
csrwr $a2, CSR_PGDL
csrwr $a3, CSR_PGDH
li.d $t0, MAKE_PWCL(PT_SHIFT, PT_BASE(2), PT_SHIFT, PT_BASE(1), PT_SHIFT, PT_BASE(0))
csrwr $t0, CSR_PWCL
li.d $t0, MAKE_PWCH(0, 0, PT_SHIFT, PT_BASE(3))
csrwr $t0, CSR_PWCH
la $t0, loongarch_handle_refill
csrwr $t0, CSR_TLBRENTRY
csrwr $zero, CSR_EENTRY
csrwr $zero, CSR_MERRENTRY
move $t0, $a0
move $sp, $a1
move $ra, $zero
move $tp, $zero
move $a0, $zero
move $a1, $zero
move $a2, $zero
move $a3, $zero
move $a4, $zero
move $a5, $zero
move $a6, $zero
move $a7, $zero
move $t1, $zero
move $t2, $zero
move $t3, $zero
move $t4, $zero
move $t5, $zero
move $t6, $zero
move $t7, $zero
move $t8, $zero
move $fp, $zero
move $s0, $zero
move $s1, $zero
move $s2, $zero
move $s3, $zero
move $s4, $zero
move $s5, $zero
move $s6, $zero
move $s7, $zero
move $s8, $zero
jirl $zero, $t0, 0
.global loongarch_handle_refill
.align 12
loongarch_handle_refill:
csrwr $t0, CSR_TLBRSAVE
csrrd $t0, CSR_PGD
lddir $t0, $t0, 3
lddir $t0, $t0, 2
lddir $t0, $t0, 1
ldpte $t0, 0
ldpte $t0, 1
tlbfill
csrrd $t0, CSR_TLBRSAVE
ertn
.section .note.GNU-stack,"",%progbits

View file

@ -0,0 +1,55 @@
.section .text
.global riscv_spinup
riscv_spinup:
.option norelax
csrci sstatus, 0x2
csrw sie, zero
lla t0, 0f
add t0, t0, a3
csrw stvec, t0
csrw satp, a2
sfence.vma
unimp
.align 4
0:
csrw stvec, zero
mv t0, a0
mv sp, a1
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,74 @@
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
mov cr3, eax
; Clear TSS busy bit and load TR with base 0, limit 0
sub esp, 8
sgdt [esp]
mov eax, [esp + 2]
add esp, 8
mov byte [eax + 0x3d], 0x89
mov ax, 0x38
ltr ax
call edi
section .note.GNU-stack noalloc noexec nowrite progbits

View file

@ -0,0 +1,111 @@
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
push rsi
push rdi
call flush_irqs
pop rdi
pop rsi
pop rdx
pop rcx
pop r9
pop r8
mov rbp, rsp
sub 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
mov cr3, eax
; Clear TSS busy bit and load TR with base 0, limit 0
sub esp, 8
sgdt [esp]
mov eax, [esp + 2]
add esp, 8
mov byte [eax + 0x3d], 0x89
mov ax, 0x38
ltr ax
call edi
section .note.GNU-stack noalloc noexec nowrite progbits

View file

@ -0,0 +1,6 @@
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-function"
#define STB_IMAGE_IMPLEMENTATION
#include <lib/stb_image.h>

311
limine/common/lib/term.c Normal file
View file

@ -0,0 +1,311 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/term.h>
#include <lib/real.h>
#include <lib/misc.h>
#include <lib/fb.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_size_t);
}
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;
UINTN uefi_x_size, uefi_y_size;
gST->ConOut->QueryMode(gST->ConOut, gST->ConOut->Mode->Mode, &uefi_x_size, &uefi_y_size);
gST->ConOut->SetCursorPosition(gST->ConOut, uefi_x_size - 1, uefi_y_size - 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;
}
static UINTN ansi_colours[] = {
EFI_BLACK,
EFI_RED,
EFI_GREEN,
EFI_YELLOW,
EFI_BLUE,
EFI_MAGENTA,
EFI_CYAN,
EFI_LIGHTGRAY
};
static UINTN conout_current_fg, conout_current_bg;
static void fallback_set_text_fg(struct flanterm_context *ctx, size_t fg) {
(void)ctx;
conout_current_fg = ansi_colours[fg];
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
static void fallback_set_text_bg(struct flanterm_context *ctx, size_t bg) {
(void)ctx;
conout_current_bg = ansi_colours[bg];
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
static void fallback_set_text_fg_bright(struct flanterm_context *ctx, size_t fg) {
(void)ctx;
conout_current_fg = ansi_colours[fg] | EFI_BRIGHT;
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
static void fallback_set_text_bg_bright(struct flanterm_context *ctx, size_t bg) {
(void)ctx;
// bg does not support bright
conout_current_bg = ansi_colours[bg];
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
static void fallback_set_text_fg_default(struct flanterm_context *ctx) {
(void)ctx;
conout_current_fg = EFI_LIGHTGRAY;
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
static void fallback_set_text_bg_default(struct flanterm_context *ctx) {
(void)ctx;
conout_current_bg = EFI_BLACK;
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
static void fallback_swap_palette(struct flanterm_context *ctx) {
(void)ctx;
UINTN tmp = conout_current_bg;
conout_current_bg = conout_current_fg;
conout_current_fg = tmp;
gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(conout_current_fg, conout_current_bg));
}
#endif
static bool dummy_handle(void) {
return true;
}
void term_fallback(void) {
term_notready();
terms = ext_mem_alloc(sizeof(void *));
terms_i = 1;
terms[0] = ext_mem_alloc(sizeof(struct flanterm_context));
struct flanterm_context *term = terms[0];
#if defined (UEFI)
if (!efi_boot_services_exited) {
#endif
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;
term->cols = 80;
term->rows = 24;
term_backend = FALLBACK;
flanterm_context_reinit(term);
#if defined (UEFI)
term->set_text_fg = fallback_set_text_fg;
term->set_text_bg = fallback_set_text_bg;
term->set_text_fg_bright = fallback_set_text_fg_bright;
term->set_text_bg_bright = fallback_set_text_bg_bright;
term->set_text_fg_default = fallback_set_text_fg_default;
term->set_text_bg_default = fallback_set_text_bg_default;
term->swap_palette = fallback_swap_palette;
term->set_text_fg_default(term);
term->set_text_bg_default(term);
} else {
if (fb_fbs_count == 0) {
goto fail;
}
terms[0] = flanterm_fb_init(ext_mem_alloc_size_t, pmm_free_size_t,
(void *)(uintptr_t)fb_fbs[0].framebuffer_addr, fb_fbs[0].framebuffer_width,
fb_fbs[0].framebuffer_height, fb_fbs[0].framebuffer_pitch,
fb_fbs[0].red_mask_size, fb_fbs[0].red_mask_shift,
fb_fbs[0].green_mask_size, fb_fbs[0].green_mask_shift,
fb_fbs[0].blue_mask_size, fb_fbs[0].blue_mask_shift,
NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, 0, 0, 1,
0, 0,
0,
FLANTERM_FB_ROTATE_0
);
}
return;
fail:
pmm_free(terms[0], sizeof(struct flanterm_context));
pmm_free(terms, sizeof(void *));
terms_i = 0;
terms = NULL;
#endif
}

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