Add limine sources
This commit is contained in:
parent
ccb89c6037
commit
2bb7471dbb
221 changed files with 36911 additions and 0 deletions
18
limine/.editorconfig
Normal file
18
limine/.editorconfig
Normal 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
|
||||
91
limine/.forgejo/workflows/binary-release.yml
Normal file
91
limine/.forgejo/workflows/binary-release.yml
Normal 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
|
||||
51
limine/.forgejo/workflows/check.yml
Normal file
51
limine/.forgejo/workflows/check.yml
Normal 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
|
||||
15
limine/.forgejo/workflows/pr_branch_check.yml
Normal file
15
limine/.forgejo/workflows/pr_branch_check.yml
Normal 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
|
||||
24
limine/.forgejo/workflows/qa.yml
Normal file
24
limine/.forgejo/workflows/qa.yml
Normal 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
|
||||
100
limine/.forgejo/workflows/release.yml
Normal file
100
limine/.forgejo/workflows/release.yml
Normal 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
75
limine/.gitignore
vendored
Normal 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
24
limine/.typos.toml
Normal 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
67
limine/3RDPARTY.md
Normal 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
364
limine/CONFIG.md
Normal 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
22
limine/COPYING
Normal 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
1882
limine/ChangeLog
Normal file
File diff suppressed because it is too large
Load diff
35
limine/FAQ.md
Normal file
35
limine/FAQ.md
Normal 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
401
limine/GNUmakefile.in
Normal 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
50
limine/INSTALL.md
Normal 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)
|
||||
```
|
||||
|
|
@ -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
102
limine/README.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Limine [](https://matrix.to/#/#limine:matrix.org) [](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
|
||||
|
||||

|
||||
|
||||
[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.
|
||||
|
||||
[](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
107
limine/USAGE.md
Normal 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
7
limine/bochsrc
Normal 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
170
limine/bootstrap
Executable 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
675
limine/common/common.mk
Normal 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
|
||||
220
limine/common/crypt/blake2b.c
Normal file
220
limine/common/crypt/blake2b.c
Normal 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 *)¶m + 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);
|
||||
}
|
||||
10
limine/common/crypt/blake2b.h
Normal file
10
limine/common/crypt/blake2b.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef CRYPT__BLAKE2B_H__
|
||||
#define CRYPT__BLAKE2B_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define BLAKE2B_OUT_BYTES 64
|
||||
|
||||
void blake2b(void *out, const void *in, size_t in_len);
|
||||
|
||||
#endif
|
||||
26
limine/common/drivers/disk.h
Normal file
26
limine/common/drivers/disk.h
Normal 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
|
||||
846
limine/common/drivers/disk.s2.c
Normal file
846
limine/common/drivers/disk.s2.c
Normal 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
|
||||
85
limine/common/drivers/edid.c
Normal file
85
limine/common/drivers/edid.c
Normal 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
|
||||
43
limine/common/drivers/edid.h
Normal file
43
limine/common/drivers/edid.h
Normal 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
380
limine/common/drivers/gop.c
Normal 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
|
||||
18
limine/common/drivers/gop.h
Normal file
18
limine/common/drivers/gop.h
Normal 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
|
||||
50
limine/common/drivers/serial.c
Normal file
50
limine/common/drivers/serial.c
Normal 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
|
||||
15
limine/common/drivers/serial.h
Normal file
15
limine/common/drivers/serial.h
Normal 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
364
limine/common/drivers/vbe.c
Normal 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
|
||||
14
limine/common/drivers/vbe.h
Normal file
14
limine/common/drivers/vbe.h
Normal 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
|
||||
369
limine/common/drivers/vga_textmode.c
Normal file
369
limine/common/drivers/vga_textmode.c
Normal 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
|
||||
35
limine/common/drivers/vga_textmode.h
Normal file
35
limine/common/drivers/vga_textmode.h
Normal 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
|
||||
12
limine/common/efi_thunk.asm_uefi_aarch64
Normal file
12
limine/common/efi_thunk.asm_uefi_aarch64
Normal 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
|
||||
10
limine/common/efi_thunk.asm_uefi_ia32
Normal file
10
limine/common/efi_thunk.asm_uefi_ia32
Normal 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
|
||||
10
limine/common/efi_thunk.asm_uefi_loongarch64
Normal file
10
limine/common/efi_thunk.asm_uefi_loongarch64
Normal 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
|
||||
11
limine/common/efi_thunk.asm_uefi_riscv64
Normal file
11
limine/common/efi_thunk.asm_uefi_riscv64
Normal 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
|
||||
10
limine/common/efi_thunk.asm_uefi_x86_64
Normal file
10
limine/common/efi_thunk.asm_uefi_x86_64
Normal 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
134
limine/common/entry.s2.c
Normal 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
202
limine/common/entry.s3.c
Normal 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);
|
||||
}
|
||||
31
limine/common/entry_asm.s2.asm_bios_ia32
Normal file
31
limine/common/entry_asm.s2.asm_bios_ia32
Normal 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
11
limine/common/fs/fat32.h
Normal 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
733
limine/common/fs/fat32.s2.c
Normal 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, ¤t_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
45
limine/common/fs/file.h
Normal 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
184
limine/common/fs/file.s2.c
Normal 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;
|
||||
}
|
||||
}
|
||||
10
limine/common/fs/iso9660.h
Normal file
10
limine/common/fs/iso9660.h
Normal 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
|
||||
544
limine/common/fs/iso9660.s2.c
Normal file
544
limine/common/fs/iso9660.s2.c
Normal 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 ¤t->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
51
limine/common/gensyms.sh
Executable 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
475
limine/common/lib/acpi.c
Normal 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
205
limine/common/lib/acpi.h
Normal 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
|
||||
/// structure’s 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
90
limine/common/lib/bli.c
Normal 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
11
limine/common/lib/bli.h
Normal 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
756
limine/common/lib/config.c
Normal 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;
|
||||
}
|
||||
36
limine/common/lib/config.h
Normal file
36
limine/common/lib/config.h
Normal 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
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
87
limine/common/lib/elf.h
Normal 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
|
||||
100
limine/common/lib/elsewhere.c
Normal file
100
limine/common/lib/elsewhere.c
Normal 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;
|
||||
}
|
||||
19
limine/common/lib/elsewhere.h
Normal file
19
limine/common/lib/elsewhere.h
Normal 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
65
limine/common/lib/fb.c
Normal 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
43
limine/common/lib/fb.h
Normal 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
44
limine/common/lib/fdt.c
Normal 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
15
limine/common/lib/fdt.h
Normal 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
365
limine/common/lib/getchar.c
Normal 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
|
||||
28
limine/common/lib/getchar.h
Normal file
28
limine/common/lib/getchar.h
Normal 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
907
limine/common/lib/gterm.c
Normal 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
11
limine/common/lib/gterm.h
Normal 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
95
limine/common/lib/guid.c
Normal 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
20
limine/common/lib/guid.h
Normal 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
65
limine/common/lib/image.c
Normal 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
32
limine/common/lib/image.h
Normal 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
131
limine/common/lib/libc.c
Normal 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
36
limine/common/lib/libc.h
Normal 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
149
limine/common/lib/libc.s2.c
Normal 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;
|
||||
}
|
||||
52
limine/common/lib/macros.aarch64_asm.h
Normal file
52
limine/common/lib/macros.aarch64_asm.h
Normal 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
|
||||
53
limine/common/lib/memory.s2.c
Normal file
53
limine/common/lib/memory.s2.c
Normal 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
417
limine/common/lib/misc.c
Normal 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
130
limine/common/lib/misc.h
Normal 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
129
limine/common/lib/misc.s2.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
limine/common/lib/panic.s2.c
Normal file
89
limine/common/lib/panic.s2.c
Normal 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
110
limine/common/lib/part.h
Normal 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
645
limine/common/lib/part.s2.c
Normal 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, §_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
496
limine/common/lib/pe.c
Normal 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 = §ions[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 = §ions[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 = §ions[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
12
limine/common/lib/pe.h
Normal 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
16
limine/common/lib/print.h
Normal 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
|
||||
235
limine/common/lib/print.s2.c
Normal file
235
limine/common/lib/print.s2.c
Normal 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
89
limine/common/lib/rand.c
Normal 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
11
limine/common/lib/rand.h
Normal 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
34
limine/common/lib/real.h
Normal 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
|
||||
168
limine/common/lib/real.s2.asm_bios_ia32
Normal file
168
limine/common/lib/real.s2.asm_bios_ia32
Normal 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
|
||||
157
limine/common/lib/sleep.asm_bios_ia32
Normal file
157
limine/common/lib/sleep.asm_bios_ia32
Normal 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
|
||||
113
limine/common/lib/spinup.asm_aarch64
Normal file
113
limine/common/lib/spinup.asm_aarch64
Normal 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
|
||||
49
limine/common/lib/spinup.asm_bios_ia32
Normal file
49
limine/common/lib/spinup.asm_bios_ia32
Normal 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
|
||||
110
limine/common/lib/spinup.asm_loongarch64
Normal file
110
limine/common/lib/spinup.asm_loongarch64
Normal 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
|
||||
55
limine/common/lib/spinup.asm_riscv64
Normal file
55
limine/common/lib/spinup.asm_riscv64
Normal 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
|
||||
74
limine/common/lib/spinup.asm_uefi_ia32
Normal file
74
limine/common/lib/spinup.asm_uefi_ia32
Normal 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
|
||||
111
limine/common/lib/spinup.asm_uefi_x86_64
Normal file
111
limine/common/lib/spinup.asm_uefi_x86_64
Normal 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
|
||||
6
limine/common/lib/stb_image.c
Normal file
6
limine/common/lib/stb_image.c
Normal 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
311
limine/common/lib/term.c
Normal 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
Loading…
Reference in a new issue