Compare commits
No commits in common. "v5.x-branch-binary" and "v5.x-branch" have entirely different histories.
v5.x-branc
...
v5.x-branc
|
@ -0,0 +1,31 @@
|
|||
name: Check for compilation failures
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Check for compilation failures
|
||||
runs-on: ubuntu-latest
|
||||
container: archlinux:latest
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S base-devel git autoconf automake nasm curl mtools llvm clang lld aarch64-linux-gnu-gcc riscv64-linux-gnu-gcc
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git config
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Build the bootloader (LLVM)
|
||||
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=llvm --enable-werror --enable-all && make all && make maintainer-clean
|
||||
|
||||
- name: Build the bootloader (GNU, x86)
|
||||
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=gnu --enable-werror --enable-bios --enable-uefi-ia32 --enable-uefi-x86-64 && make all && make maintainer-clean
|
||||
|
||||
- name: Build the bootloader (GNU, aarch64)
|
||||
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=aarch64-linux-gnu --enable-werror --enable-uefi-aarch64 && make all && make maintainer-clean
|
||||
|
||||
- name: Build the bootloader (GNU, riscv64)
|
||||
run: ./bootstrap && ./configure TOOLCHAIN_FOR_TARGET=riscv64-linux-gnu --enable-werror --enable-uefi-riscv64 && make all && make maintainer-clean
|
|
@ -0,0 +1,97 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and upload artifacts
|
||||
runs-on: ubuntu-latest
|
||||
container: archlinux:latest
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: pacman --noconfirm -Syu && pacman --needed --noconfirm -S base-devel git autoconf automake nasm curl mtools llvm clang lld mingw-w64-gcc
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Git config
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Get tag name
|
||||
run: echo "TAG_NAME=$(git describe --exact-match --tags $(git log -n1 --pretty='%h'))" >> $GITHUB_ENV
|
||||
|
||||
- name: Get branch name
|
||||
run: echo "BRANCH_NAME=$(echo "$TAG_NAME" | grep -o 'v[0-9]\+\.')x-branch" >> $GITHUB_ENV
|
||||
|
||||
- name: Regenerate
|
||||
run: ./bootstrap
|
||||
|
||||
- name: Create build dir
|
||||
run: mkdir -p build
|
||||
|
||||
- name: Configure
|
||||
run: cd build && ../configure --enable-all TOOLCHAIN_FOR_TARGET=llvm
|
||||
|
||||
- name: Build the bootloader
|
||||
run: make -C build
|
||||
|
||||
- name: Clean limine
|
||||
run: rm build/bin/limine
|
||||
|
||||
- name: Build limine for Windows
|
||||
run: make -C build/bin CC="i686-w64-mingw32-gcc" CFLAGS="-O2 -pipe" CPPFLAGS="-D__USE_MINGW_ANSI_STDIO" limine
|
||||
|
||||
- name: Strip limine for Windows
|
||||
run: i686-w64-mingw32-strip build/bin/limine.exe
|
||||
|
||||
- name: Copy LICENSE to bin
|
||||
run: cp COPYING build/bin/LICENSE
|
||||
|
||||
- name: Copy install-sh to bin
|
||||
run: cp build-aux/install-sh build/bin/
|
||||
|
||||
- name: Copy limine.h to bin
|
||||
run: cp limine.h build/bin/
|
||||
|
||||
- name: Remove limine-bios-hdd.bin
|
||||
run: rm build/bin/limine-bios-hdd.bin
|
||||
|
||||
- name: Push binaries to binary branch
|
||||
run: |
|
||||
git config user.name 'mintsuki'
|
||||
git config user.email 'mintsuki@users.noreply.github.com'
|
||||
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/limine-bootloader/limine.git
|
||||
git fetch --all
|
||||
git checkout $BRANCH_NAME-binary || git checkout --orphan $BRANCH_NAME-binary
|
||||
rm -rf $(ls -a | grep -v '^\.git$' | grep -v '^\.\.$' | grep -v '^\.$' | grep -v '^build$')
|
||||
cp -r build/bin/. ./
|
||||
rm -rf build
|
||||
git add -f .
|
||||
git commit -m "Binary release $TAG_NAME"
|
||||
git push origin $BRANCH_NAME-binary
|
||||
git tag $TAG_NAME-binary
|
||||
git push origin $BRANCH_NAME-binary --tags
|
||||
|
||||
- name: Cleanup source tree
|
||||
run: git checkout $TAG_NAME && rm -rf * && git checkout .
|
||||
|
||||
- name: Package release tarball
|
||||
run: ./bootstrap && ./configure --enable-all && make dist
|
||||
|
||||
- name: Create release notes
|
||||
run: echo "Binary release can be found at https://github.com/limine-bootloader/limine/tree/$TAG_NAME-binary" > rel_notes.txt
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body_path: rel_notes.txt
|
||||
files: |
|
||||
limine-*.tar.*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -1,2 +1,48 @@
|
|||
limine
|
||||
limine.exe
|
||||
/bin
|
||||
/build
|
||||
/toolchain-files
|
||||
/limine-efi
|
||||
/freestanding-headers
|
||||
/libgcc-binaries
|
||||
/common/flanterm
|
||||
/common/stb/stb_image.h
|
||||
/decompressor/tinf
|
||||
/ovmf*
|
||||
*.o
|
||||
*.d
|
||||
*.a
|
||||
*.exe
|
||||
*.EFI
|
||||
*.bin
|
||||
*.bin.gz
|
||||
*.tar.xz
|
||||
*.tar.gz
|
||||
*.elf
|
||||
*.hdd
|
||||
*.iso
|
||||
*.sys
|
||||
/bochsout.txt
|
||||
/bx_enh_dbg.ini
|
||||
/.vscode
|
||||
/test_image
|
||||
!/common/font.bin
|
||||
/freestanding-toolchain
|
||||
/configure
|
||||
/configure.ac.save
|
||||
/INSTALL
|
||||
/build-aux
|
||||
/aclocal.m4
|
||||
/*~
|
||||
/config.status
|
||||
/config.log
|
||||
/autom4te.cache
|
||||
/man/man1/limine.1
|
||||
/GNUmakefile
|
||||
/config.h
|
||||
/common-bios
|
||||
/common-uefi-ia32
|
||||
/common-uefi-x86-64
|
||||
/common-uefi-aarch64
|
||||
/common-uefi-riscv64
|
||||
/decompressor-build
|
||||
/stage1.stamp
|
||||
|
|
BIN
BOOTAA64.EFI
BIN
BOOTAA64.EFI
Binary file not shown.
BIN
BOOTIA32.EFI
BIN
BOOTIA32.EFI
Binary file not shown.
BIN
BOOTRISCV64.EFI
BIN
BOOTRISCV64.EFI
Binary file not shown.
BIN
BOOTX64.EFI
BIN
BOOTX64.EFI
Binary file not shown.
|
@ -0,0 +1,202 @@
|
|||
# Limine configuration file
|
||||
|
||||
## Location of the config file
|
||||
|
||||
Limine scans for a config file on *the boot drive*. Every partition on the boot drive
|
||||
is scanned sequentially - first partition first (or, on UEFI, the partition containing the
|
||||
EFI executable of the booted Limine is scanned first), last partition last - for the presence
|
||||
of either a `/limine.cfg`, `/limine/limine.cfg`, `/boot/limine.cfg`, `/boot/limine/limine.cfg`,
|
||||
or a `/EFI/BOOT/limine.cfg` file, in that order.
|
||||
|
||||
Once the file is located, Limine will use it as its config file. Other possible
|
||||
candidates in subsequent partitions or directories are ignored.
|
||||
|
||||
It is thus imperative that the intended config file is placed in a location that will
|
||||
not be shadowed by another potentially candidate config file.
|
||||
|
||||
## Structure of the config file
|
||||
|
||||
The Limine configuration file is comprised of *assignments* and *entries*.
|
||||
Comments begin in '#'.
|
||||
|
||||
### Entries and sub-entries
|
||||
|
||||
*Entries* describe boot *entries* which the user can select in the *boot menu*.
|
||||
|
||||
An *entry* is simply a line starting with `:` followed by a newline-terminated
|
||||
string.
|
||||
Any *locally assignable* key that comes after it, and before another *entry*, or
|
||||
the end of the file, will be tied to the *entry*.
|
||||
|
||||
An *entry* can be a directory, meaning it can hold sub-entries. In order for an
|
||||
entry to become a directory, it needs to have a sub-entry following right after it.
|
||||
|
||||
A *sub-entry* is an entry with a number of `:` greater than 1 prepended to it.
|
||||
Each `:` represents 1 level deeper down the tree hierarchy of directories and
|
||||
entries.
|
||||
|
||||
Directories can be expanded (meaning they will not show up as collapsed in the
|
||||
menu) by default if a `+` is put between the `:`s and the beginning of the entry's name.
|
||||
|
||||
### Assignments
|
||||
|
||||
*Assignments* are simple `KEY=VALUE` style assignments.
|
||||
`VALUE` can have spaces and `=` symbols, without requiring quotations. New lines
|
||||
are delimiters.
|
||||
|
||||
Some *assignments* are part of an entry (*local*), some other assignments are *global*.
|
||||
*Global assignments* can appear anywhere in the file and are not part of an entry,
|
||||
although usually one would put them at the beginning of the config.
|
||||
Some *local assignments* are shared between entries using any *protocol*, while other
|
||||
*local assignments* are specific to a given *protocol*.
|
||||
|
||||
Some keys take *URIs* as values; these are described in the next section.
|
||||
|
||||
*Globally assignable* keys are:
|
||||
* `TIMEOUT` - Specifies the timeout in seconds before the first *entry* is automatically booted. If set to `no`, disable automatic boot. If set to `0`, boots default entry instantly (see `DEFAULT_ENTRY` key).
|
||||
* `QUIET` - If set to `yes`, enable quiet mode, where all screen output except panics and important warnings is suppressed. If `TIMEOUT` is not 0, the `TIMEOUT` still occurs, and pressing any key during the timeout will reveal the menu and disable quiet mode.
|
||||
* `SERIAL` - If set to `yes`, enable serial I/O for the bootloader.
|
||||
* `DEFAULT_ENTRY` - 1-based entry index of the entry which will be automatically selected at startup. If unspecified, it is `1`.
|
||||
* `GRAPHICS` - If set to `no`, force CGA text mode for the boot menu, else use a video mode. Ignored with Limine UEFI.
|
||||
* `VERBOSE` - If set to `yes`, print additional information during boot. Defaults to not verbose.
|
||||
* `RANDOMISE_MEMORY` - If set to `yes`, randomise the contents of RAM at bootup in order to find bugs related to non zeroed memory or for security reasons. This option will slow down boot time significantly. For the BIOS port of Limine, this will only randomise memory below 4GiB.
|
||||
* `RANDOMIZE_MEMORY` - Alias of `RANDOMISE_MEMORY`.
|
||||
* `HASH_MISMATCH_PANIC` - If set to `no`, do not panic if there is a hash mismatch for a file, but print a warning instead.
|
||||
|
||||
Limine interface control options.
|
||||
|
||||
* `INTERFACE_RESOLUTION` - Specify screen resolution to be used by the Limine interface (menu, editor, console...) in the form `<width>x<height>`. This will *only* affect the Limine interface, not any booted OS. If not specified, Limine will pick a resolution automatically. If the resolution is not available, Limine will pick another one automatically. Ignored if using text mode.
|
||||
* `INTERFACE_BRANDING` - A string that will be displayed on top of the Limine interface.
|
||||
* `INTERFACE_BRANDING_COLOUR` - A value between 0 and 7 specifying the colour of the branding string. Default is cyan (6).
|
||||
* `INTERFACE_BRANDING_COLOR` - Alias of `INTERFACE_BRANDING_COLOUR`.
|
||||
* `INTERFACE_HELP_HIDDEN` - Hides the help text located at the top of the screen showing the key bindings.
|
||||
|
||||
Limine graphical terminal control options. They are ignored if using text mode.
|
||||
|
||||
* `TERM_FONT` - URI path to a font file to be used instead of the default one for the menu and terminal. The font file must be a code page 437 character set comprised of 256 consecutive glyph bitmaps. Each glyph's bitmap must be expressed left to right (1 byte per row), and top to bottom (16 bytes per whole glyph by default; see `TERM_FONT_SIZE`). See e.g. the [VGA text mode font collection](https://github.com/viler-int10h/vga-text-mode-fonts) for fonts.
|
||||
* `TERM_FONT_SIZE` - The size of the font in dots, which must correspond to the font file or the display will be garbled. Note that glyphs are always one byte wide, and columns over 8 are empty. Many fonts may be used in both 8- and 9-dot wide variants. Defaults to `8x16`. Ignored if `TERM_FONT` not set or if the font fails to load.
|
||||
* `TERM_FONT_SCALE` - Scaling for the font in the x and y directions. `2x2` would display the font in double size, which is useful on high-DPI displays at native resolution. `2x1` only makes the font twice as wide, similar to the VGA 40 column mode. `4x2` might be good for a narrow font on a high resolution display. Values over 8 are disallowed. Default is no scaling, i.e. `1x1`.
|
||||
* `TERM_FONT_SPACING` - Horizontal spacing, in pixels, between glyphs on screen. It is equivalent to setting a font width of `<specified width>+<this value>`, except this value is preserved even in case font loading fails, and it also applies to the built-in Limine font. Defaults to 1. 0 is allowed.
|
||||
* `TERM_PALETTE` - Specifies the colour palette used by the terminal (RRGGBB). It is a `;` separated array of 8 colours: black, red, green, brown, blue, magenta, cyan, and gray. Ignored if not using a graphical terminal.
|
||||
* `TERM_PALETTE_BRIGHT` - Specifies the bright colour palette used by the terminal (RRGGBB). It is a `;` separated array of 8 bright colours: dark gray, bright red, bright green, yellow, bright blue, bright magenta, bright cyan, and white. Ignored if not using a graphical terminal.
|
||||
* `TERM_BACKGROUND` - Terminal text background colour (TTRRGGBB). TT stands for transparency.
|
||||
* `TERM_FOREGROUND` - Terminal text foreground colour (RRGGBB).
|
||||
* `TERM_BACKGROUND_BRIGHT` - Terminal text background bright colour (RRGGBB).
|
||||
* `TERM_FOREGROUND_BRIGHT` - Terminal text foreground bright colour (RRGGBB).
|
||||
* `TERM_MARGIN` - Set the amount of margin around the terminal.
|
||||
* `TERM_MARGIN_GRADIENT` - Set the thickness in pixel for the gradient around the terminal.
|
||||
* `TERM_WALLPAPER` - URI where to find the file to use as wallpaper. BMP, PNG, and JPEG formats are supported.
|
||||
* `TERM_WALLPAPER_STYLE` - The style which will be used to display the wallpaper image: `tiled`, `centered`, or `stretched`. Default is `stretched`.
|
||||
* `TERM_BACKDROP` - When the background style is `centered`, this specifies the colour of the backdrop for parts of the screen not covered by the background image, in RRGGBB format.
|
||||
|
||||
Editor control options.
|
||||
|
||||
* `EDITOR_ENABLED` - If set to `no`, the editor will not be accessible. Defaults to `yes`.
|
||||
* `EDITOR_HIGHLIGHTING` - If set to `no`, syntax highlighting in the editor will be disabled. Defaults to `yes`.
|
||||
* `EDITOR_VALIDATION` - If set to `no`, the editor will not alert you about invalid keys / syntax errors. Defaults to `yes`.
|
||||
|
||||
*Locally assignable (non protocol specific)* keys are:
|
||||
* `COMMENT` - An optional comment string that will be displayed by the bootloader on the menu when an entry is selected.
|
||||
* `PROTOCOL` - The boot protocol that will be used to boot the kernel. Valid protocols are: `linux`, `limine`, `chainload`, `chainload_next`, `multiboot` (or `multiboot1`), and `multiboot2`.
|
||||
* `CMDLINE` - The command line string to be passed to the kernel/executable. Can be omitted.
|
||||
* `KERNEL_CMDLINE` - Alias of `CMDLINE`.
|
||||
|
||||
*Locally assignable (protocol specific)* keys are:
|
||||
* Linux protocol:
|
||||
* `KERNEL_PATH` - The URI path of the kernel.
|
||||
* `MODULE_PATH` - The URI path to a module (such as initramfs).
|
||||
|
||||
Note that one can define this last variable multiple times to specify multiple
|
||||
modules.
|
||||
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
|
||||
* `TEXTMODE` - If set to `yes`, prefer text mode. (BIOS only)
|
||||
|
||||
* Limine protocol:
|
||||
* `KERNEL_PATH` - The URI path of the kernel.
|
||||
* `MODULE_PATH` - The URI path to a module.
|
||||
* `MODULE_CMDLINE` - A command line to be passed to a module.
|
||||
|
||||
**Note:** One can define these 2 last variable multiple times to specify multiple
|
||||
modules.
|
||||
The entries will be matched in order. E.g.: The 1st module path entry will be matched
|
||||
to the 1st module string entry that appear, and so on.
|
||||
|
||||
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
|
||||
* `KASLR` - For relocatable kernels, if set to `no`, disable kernel address space layout randomisation. KASLR is enabled by default.
|
||||
* `TERM_CONFIG_OVERRIDE` - If set to `yes`, override the terminal configuration for this entry. Resets all the `TERM_*` assignments to default and allows setting them anew inside the entry. Note that without this, Limine will never look for terminal configuration settings inside entries.
|
||||
|
||||
* Chainload protocol on BIOS:
|
||||
* `DRIVE` - The 1-based drive to chainload, if omitted, assume boot drive.
|
||||
* `PARTITION` - The 1-based partition to chainload, if omitted, or set to 0, chainload drive (MBR).
|
||||
* `MBR_ID` - Optional. If passed, use an MBR ID (32-bit hex value) to identify the drive containing the volume to chainload. Overrides `DRIVE`, if present, but does *not* override `PARTITION`.
|
||||
* `GPT_UUID` or `GPT_GUID` - Optional. If passed, use the GPT GUID to identify the drive containing the volume to chainload. Overrides `DRIVE` and `MBR_ID`, if present, but does *not* override `PARTITION`.
|
||||
|
||||
* Chainload protocol on UEFI:
|
||||
* `IMAGE_PATH` - URI of the EFI application to chainload.
|
||||
* `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
|
||||
|
||||
* chainload_next protocol:
|
||||
This protocol does not specify any locally assignable key on BIOS. Will boot the next bootable drive found in the system, if there is one.
|
||||
|
||||
* `RESOLUTION` - For UEFI, the resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
|
||||
|
||||
* multiboot1 and multiboot2 protocols:
|
||||
* `KERNEL_PATH` - The URI path of the kernel.
|
||||
* `MODULE_PATH` - The URI path to a module.
|
||||
* `MODULE_STRING` - A string to be passed to a module.
|
||||
|
||||
**Note:** One can define these 2 last variable multiple times to specify multiple
|
||||
modules.
|
||||
The entries will be matched in order. E.g.: the 1st module path entry will be matched
|
||||
to the 1st module string entry that appear, and so on.
|
||||
|
||||
* `RESOLUTION` - The resolution to be used should the kernel request a graphical framebuffer. This setting takes the form of `<width>x<height>x<bpp>` and *overrides* any resolution requested by the kernel. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
|
||||
* `TEXTMODE` - If set to `yes`, prefer text mode. (BIOS only)
|
||||
|
||||
## URIs
|
||||
|
||||
A URI is a path that Limine uses to locate resources in the whole system. It is
|
||||
comprised of a *resource*, a *root*, and a *path*. It takes the form of:
|
||||
```
|
||||
resource://root/path
|
||||
```
|
||||
|
||||
The format for `root` changes depending on the resource used.
|
||||
|
||||
A resource can be one of the following:
|
||||
* `boot` - If booted off PXE this is an alias of `tftp`. Else the `root` is the 1-based decimal value representing the partition on the boot drive (values of 5+ for MBR logical partitions). If omitted, the partition containing the configuration file on the boot drive is used. For example: `boot://2/...` will use partition 2 of the boot drive and `boot:///...` will use the partition containing the config file on the boot drive.
|
||||
* `hdd` - Hard disk drives. The `root` takes the form of `drive:partition`; for example: `hdd://3:1/...` would use hard drive 3, partition 1. Partitions and drives are both 1-based (partition values of 5+ for MBR logical partitions). Omitting the partition is possible; for example: `hdd://2:/...`. Omitting the partition will access the entire volume instead of a specific partition (useful for unpartitioned media).
|
||||
* `odd` - Optical disk drives (CDs/DVDs/...). The `root` takes the form of `drive:partition`; for example: `odd://3:1/...` would use optical drive 3, partition 1. Partitions and drives are both 1-based (partition values of 5+ for MBR logical partitions). Omitting the partition is possible; for example: `odd://2:/...`. Omitting the partition will access the entire volume instead of a specific partition (useful for unpartitioned media, which is often the case for optical media).
|
||||
* `guid` - The `root` takes the form of a GUID/UUID, such as `guid://736b5698-5ae1-4dff-be2c-ef8f44a61c52/...`. The GUID is that of either a filesystem, when available, or a GPT partition GUID, when using GPT, in a unified namespace.
|
||||
* `uuid` - Alias of `guid`.
|
||||
* `fslabel` - The `root` is the name of the filesystem label of a partition.
|
||||
* `tftp` - The `root` is the IP address of the tftp server to load the file from. If the root is left empty (`tftp:///...`) the file will be loaded from the server Limine booted from. This resource is only available when booting off PXE.
|
||||
|
||||
A URI can optionally be suffixed with a blake2b hash for the referenced file,
|
||||
by appending a pound character (`#`) followed by the blake2b hash.
|
||||
E.g.: `boot:///somemodule.gz#ca6914d2...446b470a`.
|
||||
|
||||
A URI can optionally be prefixed by a `$` character to indicate that the file
|
||||
pointed to be the URI is a gzip-compressed payload to be uncompressed on the
|
||||
fly. E.g.: `$boot:///somemodule.gz`.
|
||||
|
||||
## Macros
|
||||
|
||||
Macros are strings that can be arbitrarily assigned to represent other strings. For example:
|
||||
```
|
||||
${MY_MACRO}=Some text
|
||||
```
|
||||
|
||||
Now, whenever `${MY_MACRO}` is used in the config file (except for an assignment as above), it will
|
||||
be replaced by the text `Some text`. For example:
|
||||
```
|
||||
CMDLINE=something before ${MY_MACRO} something after
|
||||
```
|
||||
|
||||
Macros must always be placed inside `${...}` where `...` is the arbitrary macro name.
|
||||
|
||||
### Built-in macros
|
||||
|
||||
Limine automatically defines these macros:
|
||||
|
||||
* `ARCH` - This built-in macro expands to the architecture of the machine. Possible values are: `x86-64`, `ia-32`, `aarch64`, `riscv64`. In the case of IA-32, BIOS or UEFI, the macro will always expand to `x86-64` if the 64-bit extensions are available, else `ia-32`.
|
|
@ -0,0 +1,396 @@
|
|||
override MAKEFLAGS += -rR
|
||||
|
||||
override prefix := @prefix@
|
||||
override exec_prefix := @exec_prefix@
|
||||
override PACKAGE_TARNAME := @PACKAGE_TARNAME@
|
||||
|
||||
override bindir := @bindir@
|
||||
override datarootdir := @datarootdir@
|
||||
override includedir := @includedir@
|
||||
override datarootdir := @datarootdir@
|
||||
override mandir := @mandir@
|
||||
override docdir := @docdir@
|
||||
|
||||
override STRIP := @STRIP@
|
||||
|
||||
DESTDIR ?=
|
||||
|
||||
override BUILDDIR := @BUILDDIR@
|
||||
override BINDIR := $(BUILDDIR)/bin
|
||||
|
||||
override SRCDIR := @SRCDIR@
|
||||
|
||||
override SPACE := $(subst ,, )
|
||||
override COMMA := ,
|
||||
|
||||
MKESCAPE = $(subst $(SPACE),\ ,$(1))
|
||||
SHESCAPE = $(subst ','\'',$(1))
|
||||
NASMESCAPE = $(subst ','"'$(COMMA) \"'\"$(COMMA) '"',$(1))
|
||||
|
||||
define DEFAULT_VAR =
|
||||
ifeq ($(origin $1),default)
|
||||
override $(1) := $(2)
|
||||
endif
|
||||
ifeq ($(origin $1),undefined)
|
||||
override $(1) := $(2)
|
||||
endif
|
||||
endef
|
||||
|
||||
override BUILD_BIOS := @BUILD_BIOS@
|
||||
override BUILD_UEFI_X86_64 := @BUILD_UEFI_X86_64@
|
||||
override BUILD_UEFI_IA32 := @BUILD_UEFI_IA32@
|
||||
override BUILD_UEFI_AARCH64 := @BUILD_UEFI_AARCH64@
|
||||
override BUILD_UEFI_RISCV64 := @BUILD_UEFI_RISCV64@
|
||||
override BUILD_UEFI_CD := @BUILD_UEFI_CD@
|
||||
override BUILD_BIOS_PXE := @BUILD_BIOS_PXE@
|
||||
override BUILD_BIOS_CD := @BUILD_BIOS_CD@
|
||||
|
||||
override DEFAULT_CC := @CC@
|
||||
$(eval $(call DEFAULT_VAR,CC,$(DEFAULT_CC)))
|
||||
export CC
|
||||
|
||||
override MKDIR_P := @MKDIR_P@
|
||||
export MKDIR_P
|
||||
|
||||
override INSTALL := @INSTALL@
|
||||
export INSTALL
|
||||
|
||||
override INSTALL_PROGRAM := @INSTALL_PROGRAM@
|
||||
export INSTALL_PROGRAM
|
||||
|
||||
override INSTALL_DATA := @INSTALL_DATA@
|
||||
export INSTALL_DATA
|
||||
|
||||
override GREP := @GREP@
|
||||
export GREP
|
||||
|
||||
override SED := @SED@
|
||||
export SED
|
||||
|
||||
override AWK := @AWK@
|
||||
export AWK
|
||||
|
||||
override DEFAULT_CPPFLAGS := @CPPFLAGS@
|
||||
$(eval $(call DEFAULT_VAR,CPPFLAGS,$(DEFAULT_CPPFLAGS)))
|
||||
override CPPFLAGS := @PKGCONF_CPPFLAGS@ $(CPPFLAGS)
|
||||
export CPPFLAGS
|
||||
override DEFAULT_CFLAGS := @CFLAGS@
|
||||
$(eval $(call DEFAULT_VAR,CFLAGS,$(DEFAULT_CFLAGS)))
|
||||
override CFLAGS += @PKGCONF_CFLAGS@
|
||||
export CFLAGS
|
||||
override DEFAULT_LDFLAGS := @LDFLAGS@
|
||||
$(eval $(call DEFAULT_VAR,LDFLAGS,$(DEFAULT_LDFLAGS)))
|
||||
export LDFLAGS
|
||||
override DEFAULT_LIBS := @LIBS@
|
||||
$(eval $(call DEFAULT_VAR,LIBS,$(DEFAULT_LIBS)))
|
||||
override LIBS += @PKGCONF_LIBS@
|
||||
export LIBS
|
||||
|
||||
override WERROR_FLAG := @WERROR_FLAG@
|
||||
export WERROR_FLAG
|
||||
|
||||
override DEFAULT_CFLAGS_FOR_TARGET := @CFLAGS_FOR_TARGET@
|
||||
$(eval $(call DEFAULT_VAR,CFLAGS_FOR_TARGET,$(DEFAULT_CFLAGS_FOR_TARGET)))
|
||||
export CFLAGS_FOR_TARGET
|
||||
override DEFAULT_CPPFLAGS_FOR_TARGET := @CPPFLAGS_FOR_TARGET@
|
||||
$(eval $(call DEFAULT_VAR,CPPFLAGS_FOR_TARGET,$(DEFAULT_CPPFLAGS_FOR_TARGET)))
|
||||
export CPPFLAGS_FOR_TARGET
|
||||
override DEFAULT_LDFLAGS_FOR_TARGET := @LDFLAGS_FOR_TARGET@
|
||||
$(eval $(call DEFAULT_VAR,LDFLAGS_FOR_TARGET,$(DEFAULT_LDFLAGS_FOR_TARGET)))
|
||||
export LDFLAGS_FOR_TARGET
|
||||
|
||||
override LIMINE_VERSION := @PACKAGE_VERSION@
|
||||
|
||||
override STAGE1_FILES := $(shell find '$(call SHESCAPE,$(SRCDIR))/stage1' -type f -name '*.asm')
|
||||
|
||||
.PHONY: all
|
||||
all: $(call MKESCAPE,$(BINDIR))/Makefile
|
||||
$(MAKE) all1
|
||||
|
||||
.PHONY: all1
|
||||
all1: $(BUILD_UEFI_X86_64) $(BUILD_UEFI_IA32) $(BUILD_UEFI_AARCH64) $(BUILD_UEFI_RISCV64) $(BUILD_BIOS)
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine'
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h: $(call MKESCAPE,$(BINDIR))/limine-bios-hdd.bin
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
cd '$(call SHESCAPE,$(BINDIR))' && '$(call SHESCAPE,$(SRCDIR))/host/hgen.sh' >limine-bios-hdd.h
|
||||
|
||||
ifeq ($(BUILD_BIOS),limine-bios)
|
||||
$(call MKESCAPE,$(BINDIR))/limine: $(call MKESCAPE,$(BINDIR))/Makefile $(call MKESCAPE,$(SRCDIR))/host/limine.c $(call MKESCAPE,$(BINDIR))/limine-bios-hdd.h
|
||||
else
|
||||
override LIMINE_NO_BIOS := -DLIMINE_NO_BIOS
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/limine: $(call MKESCAPE,$(BINDIR))/Makefile $(call MKESCAPE,$(SRCDIR))/host/limine.c
|
||||
endif
|
||||
$(SED) 's/%VERSION%/@PACKAGE_VERSION@/g;s/%COPYRIGHT%/@LIMINE_COPYRIGHT@/g' <'$(call SHESCAPE,$(SRCDIR))/host/limine.c' >'$(call SHESCAPE,$(BINDIR))/limine.c'
|
||||
CPPFLAGS="$(CPPFLAGS) $(LIMINE_NO_BIOS)" $(MAKE) -C '$(call SHESCAPE,$(BINDIR))' limine
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/Makefile: $(call MKESCAPE,$(SRCDIR))/host/Makefile $(call MKESCAPE,$(SRCDIR))/host/.gitignore
|
||||
mkdir -p '$(call SHESCAPE,$(BINDIR))'
|
||||
cp '$(call SHESCAPE,$(SRCDIR))/host/Makefile' '$(call SHESCAPE,$(SRCDIR))/host/.gitignore' '$(call SHESCAPE,$(BINDIR))/'
|
||||
|
||||
.PHONY: limine
|
||||
limine:
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine'
|
||||
|
||||
.PHONY: clean
|
||||
clean: limine-bios-clean limine-uefi-ia32-clean limine-uefi-x86-64-clean limine-uefi-aarch64-clean limine-uefi-riscv64-clean
|
||||
rm -rf '$(call SHESCAPE,$(BINDIR))' '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(docdir))'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/COPYING' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/CONFIG.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/PROTOCOL.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/PHILOSOPHY.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/README.md' '$(call SHESCAPE,$(DESTDIR)$(docdir))/'
|
||||
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BUILDDIR))/man/man1/limine.1' '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/'
|
||||
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(datarootdir))'
|
||||
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine'
|
||||
ifeq ($(BUILD_BIOS),limine-bios)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios.sys' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifneq ($(BUILD_BIOS_CD),no)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios-cd.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifneq ($(BUILD_UEFI_CD),no)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifneq ($(BUILD_BIOS_PXE),no)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/limine-bios-pxe.bin' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifeq ($(BUILD_UEFI_AARCH64),limine-uefi-aarch64)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifeq ($(BUILD_UEFI_RISCV64),limine-uefi-riscv64)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifeq ($(BUILD_UEFI_X86_64),limine-uefi-x86-64)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
ifeq ($(BUILD_UEFI_IA32),limine-uefi-ia32)
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(BINDIR))/BOOTIA32.EFI' '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine/'
|
||||
endif
|
||||
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(includedir))'
|
||||
$(INSTALL_DATA) '$(call SHESCAPE,$(SRCDIR))/limine.h' '$(call SHESCAPE,$(DESTDIR)$(includedir))/'
|
||||
$(INSTALL) -d '$(call SHESCAPE,$(DESTDIR)$(bindir))'
|
||||
$(INSTALL_PROGRAM) '$(call SHESCAPE,$(BINDIR))/limine' '$(call SHESCAPE,$(DESTDIR)$(bindir))/'
|
||||
|
||||
.PHONY: install-strip
|
||||
install-strip: install
|
||||
$(STRIP) '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine'
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/COPYING'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/CONFIG.md'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/PROTOCOL.md'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/PHILOSOPHY.md'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(docdir))/README.md'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(mandir))/man1/limine.1'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(bindir))/limine'
|
||||
rm -rf '$(call SHESCAPE,$(DESTDIR)$(datarootdir))/limine'
|
||||
rm -f '$(call SHESCAPE,$(DESTDIR)$(includedir))/limine.h'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/stage1.stamp: $(STAGE1_FILES) $(call MKESCAPE,$(BUILDDIR))/decompressor-build/decompressor.bin $(call MKESCAPE,$(BUILDDIR))/common-bios/stage2.bin.gz
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
cd '$(call SHESCAPE,$(SRCDIR))/stage1/hdd' && nasm bootsect.asm -Wall $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-hdd.bin'
|
||||
ifneq ($(BUILD_BIOS_CD),no)
|
||||
cd '$(call SHESCAPE,$(SRCDIR))/stage1/cd' && nasm bootsect.asm -Wall $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-cd.bin'
|
||||
endif
|
||||
ifneq ($(BUILD_BIOS_PXE),no)
|
||||
cd '$(call SHESCAPE,$(SRCDIR))/stage1/pxe' && nasm bootsect.asm -Wall $(WERROR_FLAG) -fbin -DBUILDDIR="'"'$(call NASMESCAPE,$(BUILDDIR))'"'" -o '$(call SHESCAPE,$(BINDIR))/limine-bios-pxe.bin'
|
||||
endif
|
||||
cp '$(call SHESCAPE,$(BUILDDIR))/common-bios/limine-bios.sys' '$(call SHESCAPE,$(BINDIR))/'
|
||||
touch '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
|
||||
|
||||
.PHONY: limine-bios
|
||||
limine-bios: common-bios decompressor
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/stage1.stamp'
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/limine-uefi-cd.bin: $(if $(BUILD_UEFI_IA32),$(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI) $(if $(BUILD_UEFI_X86_64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI) $(if $(BUILD_UEFI_AARCH64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI) $(if $(BUILD_UEFI_RISCV64),$(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI)
|
||||
ifneq ($(BUILD_UEFI_CD),no)
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
rm -f '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
|
||||
dd if=/dev/zero of='$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' bs=512 count=2880 2>/dev/null
|
||||
( mformat -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' -f 1440 :: && \
|
||||
mmd -D s -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' ::/EFI && \
|
||||
mmd -D s -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' ::/EFI/BOOT && \
|
||||
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' ] && \
|
||||
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' ::/EFI/BOOT ) || true ) && \
|
||||
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' ] && \
|
||||
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' ::/EFI/BOOT ) || true ) && \
|
||||
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' ] && \
|
||||
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' ::/EFI/BOOT ) || true ) && \
|
||||
( ( [ -f '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' ] && \
|
||||
mcopy -D o -i '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin' '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' ::/EFI/BOOT ) || true ) \
|
||||
) || rm -f '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
|
||||
endif
|
||||
|
||||
.PHONY: limine-uefi-cd
|
||||
limine-uefi-cd:
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/limine-uefi-cd.bin'
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/BOOTX64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64/BOOTX64.EFI' '$(call SHESCAPE,$(BINDIR))/'
|
||||
|
||||
.PHONY: limine-uefi-x86-64
|
||||
limine-uefi-x86-64:
|
||||
$(MAKE) common-uefi-x86-64
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTX64.EFI'
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/BOOTIA32.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32/BOOTIA32.EFI' '$(call SHESCAPE,$(BINDIR))/'
|
||||
|
||||
.PHONY: limine-uefi-ia32
|
||||
limine-uefi-ia32:
|
||||
$(MAKE) common-uefi-ia32
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTIA32.EFI'
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/BOOTAA64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64/BOOTAA64.EFI' '$(call SHESCAPE,$(BINDIR))/'
|
||||
|
||||
.PHONY: limine-uefi-aarch64
|
||||
limine-uefi-aarch64:
|
||||
$(MAKE) common-uefi-aarch64
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTAA64.EFI'
|
||||
|
||||
$(call MKESCAPE,$(BINDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BINDIR))'
|
||||
cp '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64/BOOTRISCV64.EFI' '$(call SHESCAPE,$(BINDIR))/'
|
||||
|
||||
.PHONY: limine-uefi-riscv64
|
||||
limine-uefi-riscv64:
|
||||
$(MAKE) common-uefi-riscv64
|
||||
$(MAKE) '$(call SHESCAPE,$(BINDIR))/BOOTRISCV64.EFI'
|
||||
|
||||
.PHONY: limine-bios-clean
|
||||
limine-bios-clean: common-bios-clean decompressor-clean
|
||||
|
||||
.PHONY: limine-uefi-x86-64-clean
|
||||
limine-uefi-x86-64-clean: common-uefi-x86-64-clean
|
||||
|
||||
.PHONY: limine-uefi-ia32-clean
|
||||
limine-uefi-ia32-clean: common-uefi-ia32-clean
|
||||
|
||||
.PHONY: limine-uefi-aarch64-clean
|
||||
limine-uefi-aarch64-clean: common-uefi-aarch64-clean
|
||||
|
||||
.PHONY: limine-uefi-riscv64-clean
|
||||
limine-uefi-riscv64-clean: common-uefi-riscv64-clean
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
|
||||
cp -r '$(call SHESCAPE,$(SRCDIR))'/.git '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"/
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && git checkout .
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && git log --oneline --decorate > ChangeLog
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)" && ./bootstrap
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/common/flanterm/.git"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/freestanding-headers/.git"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.git"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/.gitignore"
|
||||
libgcc_needed="i686 x86_64-no-red-zone aarch64 riscv64-softfloat"; \
|
||||
for f in $$libgcc_needed; do \
|
||||
mv '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a" '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a.save"; \
|
||||
done; \
|
||||
rm '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries"/*.a; \
|
||||
for f in $$libgcc_needed; do \
|
||||
mv '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a.save" '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/libgcc-binaries/libgcc-$$f.a"; \
|
||||
done
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/limine-efi/.git"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/limine-efi/.gitignore"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/.git"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/.gitignore"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/.github"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/autom4te.cache"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/test"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/test.mk"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/screenshot.png"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/bochsrc"
|
||||
echo "$(LIMINE_VERSION)" > '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)/version"
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && tar -Jcf "limine-$(LIMINE_VERSION).tar.xz" "limine-$(LIMINE_VERSION)"
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && tar -zcf "limine-$(LIMINE_VERSION).tar.gz" "limine-$(LIMINE_VERSION)"
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))'/"limine-$(LIMINE_VERSION)"
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -rf ovmf* config.log config.status GNUmakefile config.h toolchain-files man/man1/limine.1
|
||||
|
||||
.PHONY: maintainer-clean
|
||||
maintainer-clean: distclean
|
||||
cd '$(call SHESCAPE,$(SRCDIR))' && rm -rf common/flanterm common/stb/stb_image.h decompressor/tinf freestanding-headers libgcc-binaries limine-efi freestanding-toolchain configure INSTALL build-aux *'~' autom4te.cache aclocal.m4 *.tar.xz *.tar.gz
|
||||
|
||||
.PHONY: common-uefi-x86-64
|
||||
common-uefi-x86-64:
|
||||
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
|
||||
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-x86_64-toolchain.mk' \
|
||||
TARGET=uefi-x86-64 \
|
||||
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64'
|
||||
|
||||
.PHONY: common-uefi-x86-64-clean
|
||||
common-uefi-x86-64-clean:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-x86-64'
|
||||
|
||||
.PHONY: common-uefi-aarch64
|
||||
common-uefi-aarch64:
|
||||
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
|
||||
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-aarch64-toolchain.mk' \
|
||||
TARGET=uefi-aarch64 \
|
||||
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64'
|
||||
|
||||
.PHONY: common-uefi-aarch64-clean
|
||||
common-uefi-aarch64-clean:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-aarch64'
|
||||
|
||||
.PHONY: common-uefi-riscv64
|
||||
common-uefi-riscv64:
|
||||
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
|
||||
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-riscv64-toolchain.mk' \
|
||||
TARGET=uefi-riscv64 \
|
||||
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64'
|
||||
|
||||
.PHONY: common-uefi-riscv64-clean
|
||||
common-uefi-riscv64-clean:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-riscv64'
|
||||
|
||||
.PHONY: common-uefi-ia32
|
||||
common-uefi-ia32:
|
||||
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
|
||||
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/uefi-i686-toolchain.mk' \
|
||||
TARGET=uefi-ia32 \
|
||||
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32'
|
||||
|
||||
.PHONY: common-uefi-ia32-clean
|
||||
common-uefi-ia32-clean:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-uefi-ia32'
|
||||
|
||||
.PHONY: common-bios
|
||||
common-bios:
|
||||
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/common' all \
|
||||
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/bios-i686-toolchain.mk' \
|
||||
TARGET=bios \
|
||||
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/common-bios'
|
||||
|
||||
.PHONY: common-bios-clean
|
||||
common-bios-clean:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))/common-bios'
|
||||
|
||||
.PHONY: decompressor
|
||||
decompressor:
|
||||
$(MAKE) -C '$(call SHESCAPE,$(SRCDIR))/decompressor' all \
|
||||
TOOLCHAIN_FILE='$(call SHESCAPE,$(BUILDDIR))/toolchain-files/bios-i686-toolchain.mk' \
|
||||
BUILDDIR='$(call SHESCAPE,$(BUILDDIR))/decompressor-build'
|
||||
|
||||
.PHONY: decompressor-clean
|
||||
decompressor-clean:
|
||||
rm -rf '$(call SHESCAPE,$(BUILDDIR))/decompressor-build'
|
||||
|
||||
-include test.mk
|
|
@ -0,0 +1,31 @@
|
|||
# Limine's Design Philosophy
|
||||
|
||||
### Why not support filesystem X or feature Y? (eg: LUKS, LVM)
|
||||
|
||||
The idea with Limine is to remove the responsibility of parsing filesystems and formats, aside from the bare minimum necessities (eg: FAT*, ISO9660),
|
||||
from the bootloader itself.
|
||||
It is a needless duplication of efforts to have bootloaders support all possible filesystems and formats, and it leads to massive, bloated
|
||||
bootloaders as a result (eg: GRUB2).
|
||||
What is needed is to simply make sure the bootloader is capable of reading its own files, configuration, and be able to load kernel/module files
|
||||
from disk. The kernel should be responsible for parsing everything else as it sees fit.
|
||||
|
||||
### What about LUKS? What about security? Encrypt the kernel!
|
||||
|
||||
Simply put, this is unnecessary. Putting the kernel/modules in a readable FAT32 partition and letting Limine know about their BLAKE2B checksums
|
||||
in the config file provides as much security as encrypting the kernel does.
|
||||
|
||||
### What? But what if someone modifies the config file! Ha! You clearly have not thought about that!
|
||||
|
||||
We have. While this is a pointless effort on legacy x86 BIOS, it is a reasonable expectation on UEFI systems with Secure Boot. Limine provides a
|
||||
way to modify its own EFI executable to bake in the BLAKE2B checksum of the config file itself. The EFI executable can then get signed with
|
||||
a key added to the firmware's keychain. This prevents modifications to the config file (and in turn the checksums contained there)
|
||||
from going unnoticed.
|
||||
|
||||
### What about ext2/3/4? Why is that supported then?
|
||||
|
||||
This is explicitly against the philosophy, but it is a pragmatic compromise since a lot of Linux distros and setups expect it to "work that way".
|
||||
|
||||
### But I don't want to have a separate FAT boot partition! I don't want it!!!
|
||||
|
||||
Well tough luck. It is `$year_following_2012` now and most PCs are equipped with UEFI and simply won't boot without a FAT EFI system partition
|
||||
anyways. It is not unreasonable to share the EFI system partition with the OS's /boot and store kernels and initramfses there.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,251 @@
|
|||
# Limine
|
||||
|
||||
### What is Limine?
|
||||
|
||||
Limine (pronounced as shown [here](https://www.merriam-webster.com/dictionary/in%20limine))
|
||||
is a modern, advanced, portable, multiprotocol bootloader, also used
|
||||
as the reference implementation for the [Limine boot protocol](/PROTOCOL.md).
|
||||
|
||||
### Donate
|
||||
|
||||
If you want to support the work I (@mintsuki) do on Limine, feel free to donate to me on Liberapay:
|
||||
<a href="https://liberapay.com/mintsuki/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
|
||||
|
||||
Donations welcome, but absolutely not mandatory!
|
||||
|
||||
### Limine's boot menu
|
||||
|
||||
![Reference screenshot](/screenshot.png?raw=true "Reference screenshot")
|
||||
|
||||
[Photo by Pixabay](https://www.pexels.com/photo/painting-of-black-cloud-during-sunset-164175/)
|
||||
|
||||
### Supported architectures
|
||||
* IA-32 (32-bit x86)
|
||||
* x86-64
|
||||
* aarch64 (arm64)
|
||||
* riscv64
|
||||
|
||||
### Supported boot protocols
|
||||
* Linux
|
||||
* [Limine](/PROTOCOL.md)
|
||||
* Multiboot 1
|
||||
* Multiboot 2
|
||||
* Chainloading
|
||||
|
||||
### Supported partitioning schemes
|
||||
* MBR
|
||||
* GPT
|
||||
* Unpartitioned media
|
||||
|
||||
### Supported filesystems
|
||||
* ext2/3/4
|
||||
* FAT12/16/32
|
||||
* ISO9660 (CDs/DVDs)
|
||||
|
||||
If your filesystem isn't listed here, please read [the philosophy](/PHILOSOPHY.md) first, especially before
|
||||
opening issues or pull requests related to this.
|
||||
|
||||
### Minimum system requirements
|
||||
For 32-bit x86 systems, support is only ensured starting with those with
|
||||
Pentium Pro (i686) class CPUs.
|
||||
|
||||
All x86-64, aarch64, and riscv64 (UEFI) systems are supported.
|
||||
|
||||
## Packaging status
|
||||
|
||||
[![Packaging status](https://repology.org/badge/vertical-allrepos/limine.svg)](https://repology.org/project/limine/versions)
|
||||
|
||||
## Binary releases
|
||||
|
||||
For convenience, for point releases, binaries are distributed. These binaries
|
||||
are shipped in the `-binary` branches and tags of this repository
|
||||
(see [branches](https://github.com/limine-bootloader/limine/branches/all) and
|
||||
[tags](https://github.com/limine-bootloader/limine/tags)).
|
||||
|
||||
For example, to clone the latest binary release of the `v5.x` branch one can do
|
||||
```bash
|
||||
git clone https://github.com/limine-bootloader/limine.git --branch=v5.x-branch-binary --depth=1
|
||||
```
|
||||
or, to clone a specific binary point release (for example `v5.20231024.0`)
|
||||
```bash
|
||||
git clone https://github.com/limine-bootloader/limine.git --branch=v5.20231024.0-binary --depth=1
|
||||
```
|
||||
|
||||
In order to rebuild host utilities like `limine`, simply run `make` in the binary
|
||||
release directory.
|
||||
|
||||
Host utility binaries are provided for Windows.
|
||||
|
||||
## Building the bootloader
|
||||
|
||||
*The following steps are not necessary if cloning a binary release. If so, skip to*
|
||||
*"Installing Limine binaries".*
|
||||
|
||||
### Prerequisites
|
||||
|
||||
In order to build Limine, the following programs have to be installed:
|
||||
common UNIX tools (also known as `coreutils`),
|
||||
`GNU make`, `grep`, `sed`, `find`, `awk`, `gzip`, `nasm`, `mtools`
|
||||
(optional, necessary to build `limine-uefi-cd.bin`).
|
||||
Furthermore, `gcc` or `llvm/clang` must also be installed, alongside
|
||||
the respective binutils.
|
||||
|
||||
### Configure
|
||||
|
||||
If using a release tarball (recommended, see https://github.com/limine-bootloader/limine/releases),
|
||||
run `./configure` directly.
|
||||
|
||||
If checking out from the repository, run `./bootstrap` first in order to download the
|
||||
necessary dependencies and generate the configure script (`GNU autoconf` and `GNU automake` required).
|
||||
|
||||
`./configure` takes arguments and environment variables; for more information on
|
||||
these, run `./configure --help`.
|
||||
|
||||
**`./configure` by default does not build any Limine port. Make sure to read the**
|
||||
**output of `./configure --help` and enable any or all ports!**
|
||||
|
||||
Limine supports both in-tree and out-of-tree builds. Simply run the `configure`
|
||||
script from the directory you wish to execute the build in. The following `make`
|
||||
commands are supposed to be run inside the build directory.
|
||||
|
||||
### Building Limine
|
||||
|
||||
To build Limine, run:
|
||||
```bash
|
||||
make # (or gmake where applicable)
|
||||
```
|
||||
|
||||
The generated bootloader files are going to be in `./bin`.
|
||||
|
||||
## Installing Limine binaries
|
||||
|
||||
This step is optional as the bootloader binaries can be used from the `./bin` or
|
||||
release directory just fine. This step will only install them to `share`, `include`, and
|
||||
`bin` directories in the specified prefix (default is `/usr/local`, see
|
||||
`./configure --help`, or the `PREFIX` variable if installing from a binary release).
|
||||
|
||||
To install Limine, run:
|
||||
```bash
|
||||
make install # (or gmake where applicable)
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
### UEFI
|
||||
The `BOOT*.EFI` files are valid EFI applications that can be simply copied to
|
||||
the `/EFI/BOOT` directory of a FAT formatted EFI system partition. These files can
|
||||
be installed there and coexist with a BIOS installation of Limine (see below) so
|
||||
that the disk will be bootable on both BIOS and UEFI systems.
|
||||
|
||||
The boot device must contain the `limine.cfg` files in
|
||||
either the root, `limine`, `boot`, or `boot/limine` directory of one of the
|
||||
partitions, formatted with a supported file system (the ESP partition is recommended).
|
||||
|
||||
### Secure Boot
|
||||
Limine can be booted with secure boot if the executable is signed and the key used to
|
||||
sign it is added to the firmware's keychain. This should be done in combination with enrolling
|
||||
the BLAKE2B hash of the Limine config file into the Limine EFI executable image itself for
|
||||
verification purposes.
|
||||
For more information see the `limine enroll-config` program and [the philosophy](/PHILOSOPHY.md).
|
||||
|
||||
### BIOS/MBR
|
||||
In order to install Limine on a MBR device (which can just be a raw image file),
|
||||
run `limine bios-install` as such:
|
||||
|
||||
```bash
|
||||
limine bios-install <path to device/image>
|
||||
```
|
||||
|
||||
The boot device must contain the `limine-bios.sys` and `limine.cfg` files in
|
||||
either the root, `limine`, `boot`, or `boot/limine` directory of one of the
|
||||
partitions, formatted with a supported file system.
|
||||
|
||||
### BIOS/GPT
|
||||
If using a GPT formatted device, there are 2 options one can follow for
|
||||
installation:
|
||||
* Specifying a dedicated stage 2 partition.
|
||||
* Letting `limine bios-install` attempt to embed stage 2 within GPT structures.
|
||||
|
||||
In case one wants to specify a stage 2 partition, create a partition on the GPT
|
||||
device of at least 32KiB in size, and pass the 1-based number of the partition
|
||||
to `limine bios-install` as a second argument; such as:
|
||||
|
||||
```bash
|
||||
limine bios-install <path to device/image> <1-based stage 2 partition number>
|
||||
```
|
||||
|
||||
In case one wants to let `limine bios-install` embed stage 2 within GPT's structures,
|
||||
simply omit the partition number, and invoke `limine bios-install` the same as one
|
||||
would do for an MBR partitioned device.
|
||||
|
||||
The boot device must contain the `limine-bios.sys` and `limine.cfg` files in
|
||||
either the root, `limine`, `boot`, or `boot/limine` directory of one of the
|
||||
partitions, formatted with a supported file system.
|
||||
|
||||
### BIOS/UEFI hybrid ISO creation
|
||||
In order to create a hybrid ISO with Limine, place the
|
||||
`limine-uefi-cd.bin`, `limine-bios-cd.bin`, `limine-bios.sys`, and `limine.cfg` files
|
||||
into a directory which will serve as the root of the created ISO.
|
||||
(`limine-bios.sys` and `limine.cfg` must either be in the root, `limine`, `boot`, or
|
||||
`boot/limine` directory; `limine-uefi-cd.bin` and `limine-bios-cd.bin` can reside
|
||||
anywhere).
|
||||
|
||||
After that, create a `<ISO root directory>/EFI/BOOT` directory and copy the
|
||||
relevant Limine EFI executables over (such as `BOOTX64.EFI`).
|
||||
|
||||
Place any other file you want to be on the final ISO in said directory, then
|
||||
run:
|
||||
```
|
||||
xorriso -as mkisofs -b <relative path of limine-bios-cd.bin> \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
--efi-boot <relative path of limine-uefi-cd.bin> \
|
||||
-efi-boot-part --efi-boot-image --protective-msdos-label \
|
||||
<root directory> -o image.iso
|
||||
```
|
||||
|
||||
*Note: `xorriso` is required.*
|
||||
|
||||
And do not forget to also run `limine bios-install` on the generated image:
|
||||
```
|
||||
limine bios-install image.iso
|
||||
```
|
||||
|
||||
`<relative path of limine-bios-cd.bin>` is the relative path of
|
||||
`limine-bios-cd.bin` inside the root directory.
|
||||
For example, if it was copied in `<root directory>/boot/limine-bios-cd.bin`,
|
||||
it would be `boot/limine-bios-cd.bin`.
|
||||
|
||||
`<relative path of limine-uefi-cd.bin>` is the relative path of
|
||||
`limine-uefi-cd.bin` inside the root directory.
|
||||
For example, if it was copied in
|
||||
`<root directory>/boot/limine-uefi-cd.bin`, it would be
|
||||
`boot/limine-uefi-cd.bin`.
|
||||
|
||||
### BIOS/PXE boot
|
||||
The `limine-bios-pxe.bin` binary is a valid PXE boot image.
|
||||
In order to boot Limine from PXE it is necessary to setup a DHCP server with
|
||||
support for PXE booting. This can either be accomplished using a single DHCP
|
||||
server or your existing DHCP server and a proxy DHCP server such as dnsmasq.
|
||||
|
||||
`limine.cfg` and `limine-bios.sys` are expected to be on the server used for boot.
|
||||
|
||||
### UEFI/PXE boot
|
||||
The `BOOT*.EFI` files are compatible with UEFI PXE.
|
||||
The steps needed to boot Limine are the same as with BIOS PXE,
|
||||
except that the `limine-bios.sys` file is not needed on the server.
|
||||
|
||||
### Configuration
|
||||
The `limine.cfg` file contains Limine's configuration.
|
||||
|
||||
An example `limine.cfg` file can be found in [`test/limine.cfg`](https://github.com/limine-bootloader/limine/blob/trunk/test/limine.cfg).
|
||||
|
||||
More info on the format of `limine.cfg` can be found in [`CONFIG.md`](https://github.com/limine-bootloader/limine/blob/trunk/CONFIG.md).
|
||||
|
||||
## Acknowledgments
|
||||
Limine uses a stripped-down version of [tinf](https://github.com/jibsen/tinf) for early GZIP decompression.
|
||||
|
||||
Limine relies on [stb_image](https://github.com/nothings/stb/blob/dev/stb_image.h) for runtime GZIP decompression and image loading.
|
||||
|
||||
## Discord server
|
||||
We have a [Discord server](https://discord.gg/QEeZMz4) if you need support,
|
||||
info, or you just want to hang out with us.
|
|
@ -0,0 +1,17 @@
|
|||
cpu: count=2, reset_on_triple_fault=0
|
||||
|
||||
display_library: x, options="gui_debug"
|
||||
|
||||
megs: 512
|
||||
|
||||
clock: sync=realtime, time0=local
|
||||
|
||||
ata0-master: type=disk, path="test.hdd", mode=flat
|
||||
|
||||
boot: c
|
||||
|
||||
log: ./bochsout.txt
|
||||
|
||||
mouse: enabled=0
|
||||
|
||||
magic_break: enabled=1
|
|
@ -0,0 +1,34 @@
|
|||
#! /bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
srcdir="$(dirname "$0")"
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
cd "$srcdir"
|
||||
|
||||
if [ -z "$BOOTSTRAP_NO_SHALLOW_CLONES" ]; then
|
||||
SHALLOW_CLONE_FLAG="--depth=1"
|
||||
fi
|
||||
|
||||
[ -d common/flanterm ] || git clone https://github.com/mintsuki/flanterm.git common/flanterm $SHALLOW_CLONE_FLAG
|
||||
[ -f common/stb/stb_image.h ] || ( curl -Lo common/stb/stb_image.h https://github.com/nothings/stb/raw/dev/stb_image.h && patch -p0 < common/stb_image.patch )
|
||||
[ -d decompressor/tinf ] || (
|
||||
set -e
|
||||
mkdir -p decompressor/tinf
|
||||
curl -Lo decompressor/tinf/tinf.h https://github.com/jibsen/tinf/raw/master/src/tinf.h
|
||||
curl -Lo decompressor/tinf/tinflate.c https://github.com/jibsen/tinf/raw/master/src/tinflate.c
|
||||
curl -Lo decompressor/tinf/tinfgzip.c https://github.com/jibsen/tinf/raw/master/src/tinfgzip.c
|
||||
patch -p0 < decompressor/tinf.patch
|
||||
)
|
||||
[ -f freestanding-toolchain ] || ( curl -Lo freestanding-toolchain https://github.com/mintsuki/freestanding-toolchain/raw/trunk/freestanding-toolchain && chmod +x freestanding-toolchain )
|
||||
[ -d freestanding-headers ] || git clone https://github.com/mintsuki/freestanding-headers.git $SHALLOW_CLONE_FLAG
|
||||
[ -d limine-efi ] || git clone https://github.com/limine-bootloader/limine-efi.git $SHALLOW_CLONE_FLAG
|
||||
[ -d libgcc-binaries ] || git clone https://github.com/mintsuki/libgcc-binaries.git $SHALLOW_CLONE_FLAG
|
||||
|
||||
AUTOMAKE_LIBDIR="$(automake --print-libdir)"
|
||||
cp "${AUTOMAKE_LIBDIR}/INSTALL" ./
|
||||
mkdir -p build-aux
|
||||
cp "${AUTOMAKE_LIBDIR}/install-sh" build-aux/
|
||||
|
||||
autoreconf -fvi -Wall
|
|
@ -0,0 +1,626 @@
|
|||
override MAKEFLAGS += -rR
|
||||
|
||||
include $(TOOLCHAIN_FILE)
|
||||
export CC_FOR_TARGET
|
||||
export LD_FOR_TARGET
|
||||
export OBJDUMP_FOR_TARGET
|
||||
export OBJCOPY_FOR_TARGET
|
||||
export READELF_FOR_TARGET
|
||||
|
||||
TARGET ?=
|
||||
BUILDDIR ?=
|
||||
|
||||
override SRCDIR := $(shell pwd -P)
|
||||
|
||||
override SPACE := $(subst ,, )
|
||||
|
||||
MKESCAPE = $(subst $(SPACE),\ ,$(1))
|
||||
SHESCAPE = $(subst ','\'',$(1))
|
||||
OBJESCAPE = $(subst .a ,.a' ',$(subst .o ,.o' ',$(call SHESCAPE,$(1))))
|
||||
|
||||
ifeq ($(call MKESCAPE,$(BUILDDIR)),)
|
||||
$(error BUILDDIR not specified)
|
||||
endif
|
||||
|
||||
COM_OUTPUT ?= false
|
||||
E9_OUTPUT ?= false
|
||||
|
||||
override S2CFLAGS := -Os
|
||||
|
||||
override BASE_CFLAGS := $(CFLAGS_FOR_TARGET)
|
||||
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-g \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wvla \
|
||||
$(WERROR_FLAG) \
|
||||
-std=gnu11 \
|
||||
-nostdinc \
|
||||
-ffreestanding \
|
||||
-fno-stack-protector \
|
||||
-fno-stack-check \
|
||||
-fno-omit-frame-pointer \
|
||||
-fno-strict-aliasing \
|
||||
-fno-lto
|
||||
|
||||
override CPPFLAGS_FOR_TARGET := \
|
||||
-I../freestanding-headers \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/..' \
|
||||
-I. \
|
||||
$(CPPFLAGS_FOR_TARGET) \
|
||||
-DCOM_OUTPUT=$(COM_OUTPUT) \
|
||||
-DE9_OUTPUT=$(E9_OUTPUT) \
|
||||
-MMD \
|
||||
-MP
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/./flanterm/backends/fb.o: override CPPFLAGS_FOR_TARGET += \
|
||||
-DFLANTERM_FB_DISABLE_BUMP_ALLOC \
|
||||
-DFLANTERM_FB_SUPPORT_BPP
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-fno-PIE \
|
||||
-fno-PIC \
|
||||
-m32 \
|
||||
-march=i686 \
|
||||
-mno-80387
|
||||
override CPPFLAGS_FOR_TARGET := \
|
||||
$(CPPFLAGS_FOR_TARGET) \
|
||||
-DBIOS
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-x86-64)
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-fPIE \
|
||||
-fshort-wchar \
|
||||
-m64 \
|
||||
-march=x86-64 \
|
||||
-mno-80387 \
|
||||
-mno-mmx \
|
||||
-mno-sse \
|
||||
-mno-sse2 \
|
||||
-mno-red-zone
|
||||
override CPPFLAGS_FOR_TARGET := \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/x86_64' \
|
||||
$(CPPFLAGS_FOR_TARGET) \
|
||||
-DUEFI
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-ia32)
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-fPIE \
|
||||
-fshort-wchar \
|
||||
-m32 \
|
||||
-march=i686 \
|
||||
-mno-80387
|
||||
override CPPFLAGS_FOR_TARGET := \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/ia32' \
|
||||
$(CPPFLAGS_FOR_TARGET) \
|
||||
-DUEFI
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-aarch64)
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-fPIE \
|
||||
-fshort-wchar \
|
||||
-mgeneral-regs-only
|
||||
override CPPFLAGS_FOR_TARGET := \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/aarch64' \
|
||||
$(CPPFLAGS_FOR_TARGET) \
|
||||
-DUEFI
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-riscv64)
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-fPIE \
|
||||
-fshort-wchar
|
||||
|
||||
ifeq ($(CC_FOR_TARGET_IS_CLANG),yes)
|
||||
override CFLAGS_FOR_TARGET += -march=rv64imac
|
||||
else
|
||||
override CFLAGS_FOR_TARGET += -march=rv64imac_zicsr_zifencei
|
||||
endif
|
||||
|
||||
override CFLAGS_FOR_TARGET += \
|
||||
-mabi=lp64 \
|
||||
-mno-relax
|
||||
|
||||
override CPPFLAGS_FOR_TARGET := \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc' \
|
||||
-I'$(call SHESCAPE,$(BUILDDIR))/limine-efi/inc/riscv64' \
|
||||
$(CPPFLAGS_FOR_TARGET) \
|
||||
-DUEFI \
|
||||
-D__riscv64
|
||||
endif
|
||||
|
||||
override LDFLAGS_FOR_TARGET += \
|
||||
-nostdlib \
|
||||
-z max-page-size=0x1000
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
override LDFLAGS_FOR_TARGET += \
|
||||
-m elf_i386 \
|
||||
-static \
|
||||
--build-id=sha1
|
||||
|
||||
ifeq ($(LD_FOR_TARGET_HAS_NO_PIE),yes)
|
||||
override LDFLAGS_FOR_TARGET += -no-pie
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-x86-64)
|
||||
override LDFLAGS_FOR_TARGET += \
|
||||
-m elf_x86_64 \
|
||||
-static \
|
||||
-pie \
|
||||
--no-dynamic-linker \
|
||||
-z text
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-ia32)
|
||||
override LDFLAGS_FOR_TARGET += \
|
||||
-m elf_i386 \
|
||||
-static \
|
||||
-pie \
|
||||
--no-dynamic-linker \
|
||||
-z text
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-aarch64)
|
||||
override LDFLAGS_FOR_TARGET += \
|
||||
-m aarch64elf \
|
||||
-static \
|
||||
-pie \
|
||||
--no-dynamic-linker \
|
||||
-z text
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-riscv64)
|
||||
override LDFLAGS_FOR_TARGET += \
|
||||
-m elf64lriscv \
|
||||
--no-relax \
|
||||
-static \
|
||||
-pie \
|
||||
--no-dynamic-linker \
|
||||
-z text
|
||||
endif
|
||||
|
||||
override C_FILES := $(shell find . -type f -name '*.c')
|
||||
ifeq ($(TARGET),bios)
|
||||
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
|
||||
override ASM32_FILES := $(shell find . -type f -name '*.asm_ia32')
|
||||
override ASMB_FILES := $(shell find . -type f -name '*.asm_bios_ia32')
|
||||
|
||||
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASMB_FILES:.asm_bios_ia32=.o) $(ASMX86_FILES:.asm_x86=.o))
|
||||
override OBJ_S2 := $(filter %.s2.o,$(OBJ))
|
||||
endif
|
||||
ifeq ($(TARGET),uefi-x86-64)
|
||||
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
|
||||
override ASM64_FILES := $(shell find . -type f -name '*.asm_x86_64')
|
||||
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_x86_64')
|
||||
|
||||
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_x86_64=.o) $(ASM64U_FILES:.asm_uefi_x86_64=.o) $(ASMX86_FILES:.asm_x86=.o))
|
||||
endif
|
||||
ifeq ($(TARGET),uefi-ia32)
|
||||
override ASMX86_FILES := $(shell find . -type f -name '*.asm_x86')
|
||||
override ASM32_FILES := $(shell find . -type f -name '*.asm_ia32')
|
||||
override ASM32U_FILES := $(shell find . -type f -name '*.asm_uefi_ia32')
|
||||
|
||||
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM32_FILES:.asm_ia32=.o) $(ASM32U_FILES:.asm_uefi_ia32=.o) $(ASMX86_FILES:.asm_x86=.o))
|
||||
endif
|
||||
ifeq ($(TARGET),uefi-aarch64)
|
||||
override ASM64_FILES := $(shell find . -type f -name '*.asm_aarch64')
|
||||
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_aarch64')
|
||||
|
||||
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_aarch64=.o) $(ASM64U_FILES:.asm_uefi_aarch64=.o))
|
||||
endif
|
||||
ifeq ($(TARGET),uefi-riscv64)
|
||||
override ASM64_FILES := $(shell find . -type f -name '*.asm_riscv64')
|
||||
override ASM64U_FILES := $(shell find . -type f -name '*.asm_uefi_riscv64')
|
||||
|
||||
override OBJ := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.o) $(ASM64_FILES:.asm_riscv64=.o) $(ASM64U_FILES:.asm_uefi_riscv64=.o))
|
||||
endif
|
||||
|
||||
override HEADER_DEPS := $(addprefix $(call MKESCAPE,$(BUILDDIR))/, $(C_FILES:.c=.d))
|
||||
|
||||
.PHONY: all
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
all: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys $(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz
|
||||
else ifeq ($(TARGET),uefi-x86-64)
|
||||
all: $(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI
|
||||
else ifeq ($(TARGET),uefi-ia32)
|
||||
all: $(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI
|
||||
else ifeq ($(TARGET),uefi-aarch64)
|
||||
all: $(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI
|
||||
else ifeq ($(TARGET),uefi-riscv64)
|
||||
all: $(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/stage2.bin.gz: $(call MKESCAPE,$(BUILDDIR))/stage2.bin
|
||||
gzip -n -9 < '$(call SHESCAPE,$<)' > '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/stage2.bin: $(call MKESCAPE,$(BUILDDIR))/limine-bios.sys
|
||||
dd if='$(call SHESCAPE,$<)' bs=$$(( 0x$$("$(READELF_FOR_TARGET)" -S '$(call SHESCAPE,$(BUILDDIR))/limine.elf' | $(GREP) '\.text\.stage3' | $(SED) 's/^.*] //' | $(AWK) '{print $$3}' | $(SED) 's/^0*//') - 0xf000 )) count=1 of='$(call SHESCAPE,$@)' 2>/dev/null
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/stage2.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' stage2 32 '\.text\.stage2'
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/stage2.map.S' -o '$(call SHESCAPE,$@)'
|
||||
rm -f '$(call SHESCAPE,$(BUILDDIR))/stage2.map.S' '$(call SHESCAPE,$(BUILDDIR))/stage2.map.d'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nos3map.elf
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 32 '\.text'
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
|
||||
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-bios.sys: $(call MKESCAPE,$(BUILDDIR))/limine_stage2only.elf $(call MKESCAPE,$(BUILDDIR))/limine.elf
|
||||
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$(BUILDDIR))/limine.elf' '$(call SHESCAPE,$@)'
|
||||
chmod -x '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_stage2only.ld: linker_bios.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_STAGE2ONLY linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_stage2only.elf: $(OBJ_S2) ../libgcc-binaries/libgcc-i686.a
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld'
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_stage2only.ld' -o '$(call SHESCAPE,$@)' || \
|
||||
( echo "This error may mean that stage 2 was trying to use stage 3 symbols before loading stage 3" && \
|
||||
false )
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_nos2map.ld: linker_bios.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP -DLINKER_NOS2MAP linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/empty:
|
||||
touch '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(OBJ) ../libgcc-binaries/libgcc-i686.a
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/empty'
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld'
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' -o '$(call SHESCAPE,$@)'
|
||||
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin'
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \
|
||||
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o
|
||||
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin'
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \
|
||||
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nos2map.ld' -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_bios.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_nos3map.elf: $(OBJ) ../libgcc-binaries/libgcc-i686.a $(call MKESCAPE,$(BUILDDIR))/stage2.map.o
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/empty'
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' -o '$(call SHESCAPE,$@)'
|
||||
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin'
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \
|
||||
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o
|
||||
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin'
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \
|
||||
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_bios.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef linker_bios.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(OBJ) ../libgcc-binaries/libgcc-i686.a $(call MKESCAPE,$(BUILDDIR))/stage2.map.o $(call MKESCAPE,$(BUILDDIR))/full.map.o
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/empty'
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)'
|
||||
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.bin'
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s2.bin build-id.s2.o && \
|
||||
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s2.o
|
||||
$(OBJCOPY_FOR_TARGET) -O binary --only-section=.note.gnu.build-id '$(call SHESCAPE,$@)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.bin'
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
$(OBJCOPY_FOR_TARGET) -I binary -B i386 -O elf32-i386 build-id.s3.bin build-id.s3.o && \
|
||||
$(OBJCOPY_FOR_TARGET) --add-section .note.GNU-stack='$(call SHESCAPE,$(BUILDDIR))/empty' --set-section-flags .note.GNU-stack=noload,readonly build-id.s3.o
|
||||
$(LD_FOR_TARGET) '$(call OBJESCAPE,$^)' '$(call SHESCAPE,$(BUILDDIR))/build-id.s2.o' '$(call SHESCAPE,$(BUILDDIR))/build-id.s3.o' $(LDFLAGS_FOR_TARGET) -T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' -o '$(call SHESCAPE,$@)'
|
||||
|
||||
endif
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi: ../limine-efi/*
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
cp -r ../limine-efi '$(call SHESCAPE,$(BUILDDIR))/'
|
||||
|
||||
ifeq ($(TARGET),uefi-x86-64)
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
|
||||
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/BOOTX64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
|
||||
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
|
||||
chmod -x '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-x86_64.S.o: limine-efi
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_x86_64.c.o: limine-efi
|
||||
|
||||
.PHONY: limine-efi
|
||||
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
|
||||
CC="$(CC_FOR_TARGET)" \
|
||||
CFLAGS="$(BASE_CFLAGS)" \
|
||||
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
|
||||
ARCH=x86_64
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_x86_64.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_x86_64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-x86_64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_x86_64.c.o $(OBJ) ../libgcc-binaries/libgcc-x86_64-no-red-zone.a
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_x86_64.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_x86_64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-x86_64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_x86_64.c.o $(OBJ) ../libgcc-binaries/libgcc-x86_64-no-red-zone.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-aarch64)
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
|
||||
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/BOOTAA64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
|
||||
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
|
||||
chmod -x '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-aarch64.S.o: limine-efi
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_aarch64.c.o: limine-efi
|
||||
|
||||
.PHONY: limine-efi
|
||||
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
|
||||
CC="$(CC_FOR_TARGET)" \
|
||||
CFLAGS="$(BASE_CFLAGS)" \
|
||||
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
|
||||
ARCH=aarch64
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_aarch64.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_aarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-aarch64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_aarch64.c.o $(OBJ) ../libgcc-binaries/libgcc-aarch64.a
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_aarch64.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_aarch64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-aarch64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_aarch64.c.o $(OBJ) ../libgcc-binaries/libgcc-aarch64.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-riscv64)
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 64 '\.text'
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
|
||||
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/BOOTRISCV64.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
|
||||
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
|
||||
chmod -x '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.S.o: limine-efi
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.c.o: limine-efi
|
||||
|
||||
.PHONY: limine-efi
|
||||
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
|
||||
CC="$(CC_FOR_TARGET)" \
|
||||
CFLAGS="$(BASE_CFLAGS)" \
|
||||
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
|
||||
ARCH=riscv64
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_riscv64.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.c.o $(OBJ) ../libgcc-binaries/libgcc-riscv64-softfloat.a
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_riscv64.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_riscv64.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-riscv64.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_riscv64.c.o $(OBJ) ../libgcc-binaries/libgcc-riscv64-softfloat.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-ia32)
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/full.map.o: $(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf
|
||||
cd '$(call SHESCAPE,$(BUILDDIR))' && \
|
||||
'$(call SHESCAPE,$(SRCDIR))/gensyms.sh' '$(call SHESCAPE,$<)' full 32 '\.text'
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$(BUILDDIR))/full.map.S' -o '$(call SHESCAPE,$@)'
|
||||
rm -f '$(call SHESCAPE,$(BUILDDIR))/full.map.S' '$(call SHESCAPE,$(BUILDDIR))/full.map.d'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/BOOTIA32.EFI: $(call MKESCAPE,$(BUILDDIR))/limine.elf
|
||||
$(OBJCOPY_FOR_TARGET) -O binary '$(call SHESCAPE,$<)' '$(call SHESCAPE,$@)'
|
||||
chmod -x '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-ia32.S.o: limine-efi
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_ia32.c.o: limine-efi
|
||||
|
||||
.PHONY: limine-efi
|
||||
limine-efi: $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MAKE) -C '$(call SHESCAPE,$(BUILDDIR))/limine-efi/gnuefi' \
|
||||
CC="$(CC_FOR_TARGET)" \
|
||||
CFLAGS="$(BASE_CFLAGS)" \
|
||||
CPPFLAGS='-nostdinc -I$(call SHESCAPE,$(SRCDIR))/../freestanding-headers' \
|
||||
ARCH=ia32
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker_nomap.ld: linker_uefi_ia32.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef -DLINKER_NOMAP linker_uefi_ia32.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine_nomap.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-ia32.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_ia32.c.o $(OBJ) ../libgcc-binaries/libgcc-i686.a
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker_nomap.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/linker.ld: linker_uefi_ia32.ld.in
|
||||
$(MKDIR_P) '$(call SHESCAPE,$(BUILDDIR))'
|
||||
$(CC_FOR_TARGET) -x c -E -P -undef linker_uefi_ia32.ld.in -o '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/limine.elf: $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/crt0-efi-ia32.S.o $(call MKESCAPE,$(BUILDDIR))/limine-efi/gnuefi/reloc_ia32.c.o $(OBJ) ../libgcc-binaries/libgcc-i686.a $(call MKESCAPE,$(BUILDDIR))/full.map.o
|
||||
$(MAKE) '$(call SHESCAPE,$(BUILDDIR))/linker.ld'
|
||||
$(LD_FOR_TARGET) \
|
||||
-T'$(call SHESCAPE,$(BUILDDIR))/linker.ld' \
|
||||
'$(call OBJESCAPE,$^)' $(LDFLAGS_FOR_TARGET) -o '$(call SHESCAPE,$@)'
|
||||
|
||||
endif
|
||||
|
||||
-include $(HEADER_DEPS)
|
||||
|
||||
ifeq ($(TARGET),uefi-x86-64)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-aarch64)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-riscv64)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-ia32)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c $(call MKESCAPE,$(BUILDDIR))/limine-efi
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.c
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
-include $(HEADER_DEPS)
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.s2.o: %.s2.c
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(S2CFLAGS) $(CPPFLAGS_FOR_TARGET) -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
-include $(HEADER_DEPS)
|
||||
|
||||
ifeq ($(TARGET),bios)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_ia32
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_bios_ia32
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-x86-64)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86_64
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf64 -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_x86_64
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf64 -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf64 -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-aarch64)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_aarch64
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_aarch64
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-riscv64)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_riscv64
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_riscv64
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
$(CC_FOR_TARGET) $(CFLAGS_FOR_TARGET) $(CPPFLAGS_FOR_TARGET) -x assembler-with-cpp -c '$(call SHESCAPE,$<)' -o '$(call SHESCAPE,$@)'
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),uefi-ia32)
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_ia32
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_uefi_ia32
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
|
||||
|
||||
$(call MKESCAPE,$(BUILDDIR))/%.o: %.asm_x86
|
||||
$(MKDIR_P) "$$(dirname '$(call SHESCAPE,$@)')"
|
||||
nasm '$(call SHESCAPE,$<)' -F dwarf -g -Wall $(WERROR_FLAG) -f elf32 -o '$(call SHESCAPE,$@)'
|
||||
endif
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* originally from tinfgzip - tiny gzip decompressor
|
||||
*
|
||||
* Copyright (c) 2003-2019 Joergen Ibsen
|
||||
* Copyright (c) 2023 mintsuki and contributors to the Limine project
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must
|
||||
* not claim that you wrote the original software. If you use this
|
||||
* software in a product, an acknowledgment in the product
|
||||
* documentation would be appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must
|
||||
* not be misrepresented as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source
|
||||
* distribution.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
typedef enum {
|
||||
FTEXT = 1,
|
||||
FHCRC = 2,
|
||||
FEXTRA = 4,
|
||||
FNAME = 8,
|
||||
FCOMMENT = 16
|
||||
} gzip_flag;
|
||||
|
||||
void *gzip_uncompress(const void *source, uint64_t sourceLen, uint64_t *outsize) {
|
||||
const uint8_t *src = (const uint8_t *) source;
|
||||
const uint8_t *start;
|
||||
int res;
|
||||
uint8_t flg;
|
||||
|
||||
/* -- Check header -- */
|
||||
|
||||
/* Check room for at least 10 byte header and 8 byte trailer */
|
||||
if (sourceLen < 18) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check id bytes */
|
||||
if (src[0] != 0x1F || src[1] != 0x8B) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check method is deflate */
|
||||
if (src[2] != 8) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get flag byte */
|
||||
flg = src[3];
|
||||
|
||||
/* Check that reserved bits are zero */
|
||||
if (flg & 0xE0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -- Find start of compressed data -- */
|
||||
|
||||
/* Skip base header of 10 bytes */
|
||||
start = src + 10;
|
||||
|
||||
/* Skip extra data if present */
|
||||
if (flg & FEXTRA) {
|
||||
uint64_t xlen = *((uint16_t *)start);
|
||||
|
||||
if (xlen > sourceLen - 12) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start += xlen + 2;
|
||||
}
|
||||
|
||||
/* Skip file name if present */
|
||||
if (flg & FNAME) {
|
||||
do {
|
||||
if (((uint64_t)(start - src)) >= sourceLen) {
|
||||
return NULL;
|
||||
}
|
||||
} while (*start++);
|
||||
}
|
||||
|
||||
/* Skip file comment if present */
|
||||
if (flg & FCOMMENT) {
|
||||
do {
|
||||
if (((uint64_t)(start - src)) >= sourceLen) {
|
||||
return NULL;
|
||||
}
|
||||
} while (*start++);
|
||||
}
|
||||
|
||||
if (flg & FHCRC) {
|
||||
start += 2;
|
||||
}
|
||||
|
||||
/* -- Get decompressed length -- */
|
||||
|
||||
uint32_t dlen = *((uint32_t *)&src[sourceLen - 4]);
|
||||
|
||||
/* -- Decompress data -- */
|
||||
|
||||
if ((src + sourceLen) - start < 8) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *buf = ext_mem_alloc(dlen);
|
||||
|
||||
res = stbi_zlib_decode_noheader_buffer(buf, dlen, (const char *)start, (src + sourceLen) - start - 8);
|
||||
|
||||
if (res == -1) {
|
||||
pmm_free(buf, dlen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*outsize = (uint64_t)dlen;
|
||||
return buf;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef __COMPRESS__GZIP_H__
|
||||
#define __COMPRESS__GZIP_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void *gzip_uncompress(const void *source, uint64_t sourceLen, uint64_t *outsize);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,85 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <config.h>
|
||||
#include <console.h>
|
||||
#include <menu.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/readline.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/term.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/config.h>
|
||||
|
||||
static void console_help(void) {
|
||||
print(
|
||||
"Available commands:\n"
|
||||
"exit -- Exit Limine console.\n"
|
||||
"clear -- Clear the console.\n"
|
||||
"%s"
|
||||
"lsvol -- List volumes.\n"
|
||||
"firmware -- Show firmware type.\n"
|
||||
#if defined (UEFI)
|
||||
"slide -- Print load slide offset.\n"
|
||||
#endif
|
||||
"version -- Print version.\n"
|
||||
"copyright -- Print copyright.\n"
|
||||
"help -- Print this help message.\n",
|
||||
editor_enabled ? "editor -- Open an empty boot entry editor.\n" : ""
|
||||
);
|
||||
}
|
||||
|
||||
#if defined (UEFI)
|
||||
extern symbol __slide;
|
||||
#endif
|
||||
|
||||
void console(void) {
|
||||
print("Welcome to the Limine console.\nType 'help' for more information.\n\n");
|
||||
|
||||
char *prompt = ext_mem_alloc(256);
|
||||
|
||||
for (;;) {
|
||||
print(">>> ");
|
||||
readline("", prompt, 256);
|
||||
|
||||
if (strcmp(prompt, "help") == 0) {
|
||||
console_help();
|
||||
} else if (strcmp(prompt, "exit") == 0) {
|
||||
break;
|
||||
} else if (strcmp(prompt, "clear") == 0) {
|
||||
print("\e[2J\e[H");
|
||||
} else if (strcmp(prompt, "lsvol") == 0) {
|
||||
list_volumes();
|
||||
} else if (editor_enabled && strcmp(prompt, "editor") == 0) {
|
||||
char *new_entry = config_entry_editor("New Entry", "");
|
||||
if (new_entry != NULL) {
|
||||
config_ready = true;
|
||||
boot(new_entry);
|
||||
}
|
||||
} else if (strcmp(prompt, "firmware") == 0) {
|
||||
#if defined (BIOS)
|
||||
print("BIOS\n");
|
||||
#elif defined (UEFI)
|
||||
print("UEFI\n");
|
||||
#else
|
||||
print("unknown\n");
|
||||
#endif
|
||||
#if defined (UEFI)
|
||||
} else if (strcmp(prompt, "slide") == 0) {
|
||||
print("%p\n", __slide);
|
||||
#endif
|
||||
} else if (strcmp(prompt, "version") == 0) {
|
||||
print(LIMINE_VERSION "\n");
|
||||
} else if (strcmp(prompt, "copyright") == 0) {
|
||||
print(LIMINE_COPYRIGHT "\n");
|
||||
print("Limine is distributed under the terms of the BSD-2-Clause license.\n");
|
||||
print("There is ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n");
|
||||
} else if (*prompt != 0) {
|
||||
print("Invalid command: `%s`.\n", prompt);
|
||||
}
|
||||
}
|
||||
|
||||
reset_term();
|
||||
pmm_free(prompt, 256);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef __CONSOLE_H__
|
||||
#define __CONSOLE_H__
|
||||
|
||||
void console(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,209 @@
|
|||
// This blake2b implementation comes from the GNU coreutils project.
|
||||
// https://github.com/coreutils/coreutils/blob/master/src/blake2/blake2b-ref.c
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <crypt/blake2b.h>
|
||||
#include <lib/libc.h>
|
||||
|
||||
#define BLAKE2B_BLOCK_BYTES 128
|
||||
#define BLAKE2B_KEY_BYTES 64
|
||||
#define BLAKE2B_SALT_BYTES 16
|
||||
#define BLAKE2B_PERSONAL_BYTES 16
|
||||
|
||||
static const uint64_t blake2b_iv[8] = {
|
||||
0x6a09e667f3bcc908,
|
||||
0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b,
|
||||
0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1,
|
||||
0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b,
|
||||
0x5be0cd19137e2179,
|
||||
};
|
||||
|
||||
static const uint8_t blake2b_sigma[12][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||
};
|
||||
|
||||
struct blake2b_state {
|
||||
uint64_t h[8];
|
||||
uint64_t t[2];
|
||||
uint64_t f[2];
|
||||
uint8_t buf[BLAKE2B_BLOCK_BYTES];
|
||||
size_t buf_len;
|
||||
uint8_t last_node;
|
||||
};
|
||||
|
||||
struct blake2b_param {
|
||||
uint8_t digest_length;
|
||||
uint8_t key_length;
|
||||
uint8_t fan_out;
|
||||
uint8_t depth;
|
||||
uint32_t leaf_length;
|
||||
uint32_t node_offset;
|
||||
uint32_t xof_length;
|
||||
uint8_t node_depth;
|
||||
uint8_t inner_length;
|
||||
uint8_t reserved[14];
|
||||
uint8_t salt[BLAKE2B_SALT_BYTES];
|
||||
uint8_t personal[BLAKE2B_PERSONAL_BYTES];
|
||||
} __attribute__((packed));
|
||||
|
||||
static void blake2b_increment_counter(struct blake2b_state *state, uint64_t inc) {
|
||||
state->t[0] += inc;
|
||||
state->t[1] += state->t[0] < inc;
|
||||
}
|
||||
|
||||
static inline uint64_t rotr64(uint64_t w, unsigned c) {
|
||||
return (w >> c) | (w << (64 - c));
|
||||
}
|
||||
|
||||
#define G(r, i, a, b, c, d) do { \
|
||||
a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
|
||||
d = rotr64(d ^ a, 32); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 24); \
|
||||
a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
|
||||
d = rotr64(d ^ a, 16); \
|
||||
c = c + d; \
|
||||
b = rotr64(b ^ c, 63); \
|
||||
} while (0)
|
||||
|
||||
#define ROUND(r) do { \
|
||||
G(r, 0, v[0], v[4], v[8], v[12]); \
|
||||
G(r, 1, v[1], v[5], v[9], v[13]); \
|
||||
G(r, 2, v[2], v[6], v[10], v[14]); \
|
||||
G(r, 3, v[3], v[7], v[11], v[15]); \
|
||||
G(r, 4, v[0], v[5], v[10], v[15]); \
|
||||
G(r, 5, v[1], v[6], v[11], v[12]); \
|
||||
G(r, 6, v[2], v[7], v[8], v[13]); \
|
||||
G(r, 7, v[3], v[4], v[9], v[14]); \
|
||||
} while (0)
|
||||
|
||||
static void blake2b_compress(struct blake2b_state *state, const uint8_t block[static BLAKE2B_BLOCK_BYTES]) {
|
||||
uint64_t m[16];
|
||||
uint64_t v[16];
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
m[i] = *(uint64_t *)(block + i * sizeof(m[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
v[i] = state->h[i];
|
||||
}
|
||||
|
||||
v[8] = blake2b_iv[0];
|
||||
v[9] = blake2b_iv[1];
|
||||
v[10] = blake2b_iv[2];
|
||||
v[11] = blake2b_iv[3];
|
||||
v[12] = blake2b_iv[4] ^ state->t[0];
|
||||
v[13] = blake2b_iv[5] ^ state->t[1];
|
||||
v[14] = blake2b_iv[6] ^ state->f[0];
|
||||
v[15] = blake2b_iv[7] ^ state->f[1];
|
||||
|
||||
ROUND(0);
|
||||
ROUND(1);
|
||||
ROUND(2);
|
||||
ROUND(3);
|
||||
ROUND(4);
|
||||
ROUND(5);
|
||||
ROUND(6);
|
||||
ROUND(7);
|
||||
ROUND(8);
|
||||
ROUND(9);
|
||||
ROUND(10);
|
||||
ROUND(11);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
state->h[i] = state->h[i] ^ v[i] ^ v[i + 8];
|
||||
}
|
||||
}
|
||||
|
||||
#undef G
|
||||
#undef ROUND
|
||||
|
||||
static void blake2b_init(struct blake2b_state *state) {
|
||||
struct blake2b_param param = {0};
|
||||
|
||||
param.digest_length = BLAKE2B_OUT_BYTES;
|
||||
param.fan_out = 1;
|
||||
param.depth = 1;
|
||||
|
||||
memset(state, 0, sizeof(struct blake2b_state));
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
state->h[i] = blake2b_iv[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
state->h[i] ^= *(uint64_t *)((void *)¶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 += fill;
|
||||
in_len -= fill;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(state->buf + state->buf_len, in, in_len);
|
||||
state->buf_len += in_len;
|
||||
}
|
||||
|
||||
static void blake2b_final(struct blake2b_state *state, void *out) {
|
||||
uint8_t buffer[BLAKE2B_OUT_BYTES] = {0};
|
||||
|
||||
blake2b_increment_counter(state, state->buf_len);
|
||||
state->f[0] = (uint64_t)-1;
|
||||
memset(state->buf + state->buf_len, 0, BLAKE2B_BLOCK_BYTES - state->buf_len);
|
||||
blake2b_compress(state, state->buf);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
*(uint64_t *)(buffer + sizeof(state->h[i]) * i) = state->h[i];
|
||||
}
|
||||
|
||||
memcpy(out, buffer, BLAKE2B_OUT_BYTES);
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
}
|
||||
|
||||
void blake2b(void *out, const void *in, size_t in_len) {
|
||||
struct blake2b_state state = {0};
|
||||
|
||||
blake2b_init(&state);
|
||||
blake2b_update(&state, in, in_len);
|
||||
blake2b_final(&state, out);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,672 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdalign.h>
|
||||
#include <drivers/disk.h>
|
||||
#include <lib/libc.h>
|
||||
#if defined (BIOS)
|
||||
# include <lib/real.h>
|
||||
#elif defined (UEFI)
|
||||
# include <efi.h>
|
||||
# include <crypt/blake2b.h>
|
||||
#endif
|
||||
#include <lib/misc.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/rand.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <pxe/pxe.h>
|
||||
|
||||
#define DEFAULT_FASTEST_XFER_SIZE 64
|
||||
#define MAX_FASTEST_XFER_SIZE 512
|
||||
|
||||
#define MAX_VOLUMES 64
|
||||
|
||||
#if defined (BIOS)
|
||||
|
||||
struct bios_drive_params {
|
||||
uint16_t buf_size;
|
||||
uint16_t info_flags;
|
||||
uint32_t cyl;
|
||||
uint32_t heads;
|
||||
uint32_t sects;
|
||||
uint64_t lba_count;
|
||||
uint16_t bytes_per_sect;
|
||||
uint32_t edd;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dap {
|
||||
uint16_t size;
|
||||
uint16_t count;
|
||||
uint16_t offset;
|
||||
uint16_t segment;
|
||||
uint64_t lba;
|
||||
};
|
||||
|
||||
#define XFER_BUF_SIZE (xfer_sizes[SIZEOF_ARRAY(xfer_sizes) - 1] * 512)
|
||||
static const size_t xfer_sizes[] = { 1, 2, 4, 8, 16, 24, 32, 48, 64 };
|
||||
static uint8_t *xfer_buf = NULL;
|
||||
|
||||
static size_t fastest_xfer_size(struct volume *volume) {
|
||||
struct dap dap = {0};
|
||||
|
||||
if (xfer_buf == NULL)
|
||||
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
|
||||
|
||||
size_t fastest_size = 1;
|
||||
uint64_t last_speed = (uint64_t)-1;
|
||||
|
||||
for (size_t i = 0; i < SIZEOF_ARRAY(xfer_sizes); i++) {
|
||||
if (xfer_sizes[i] * volume->sector_size > XFER_BUF_SIZE) {
|
||||
break;
|
||||
}
|
||||
|
||||
dap.size = 16;
|
||||
dap.count = xfer_sizes[i];
|
||||
dap.segment = rm_seg(xfer_buf);
|
||||
dap.offset = rm_off(xfer_buf);
|
||||
dap.lba = 0;
|
||||
|
||||
uint64_t start_timestamp = rdtsc();
|
||||
for (size_t j = 0; j < XFER_BUF_SIZE / 512; j += xfer_sizes[i]) {
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x4200;
|
||||
r.edx = volume->drive;
|
||||
r.esi = (uint32_t)rm_off(&dap);
|
||||
r.ds = rm_seg(&dap);
|
||||
rm_int(0x13, &r, &r);
|
||||
if (r.eflags & EFLAGS_CF) {
|
||||
int ah = (r.eax >> 8) & 0xff;
|
||||
print("Disk error %x. Drive %x", ah, volume->drive);
|
||||
return 8;
|
||||
}
|
||||
dap.lba += xfer_sizes[i];
|
||||
}
|
||||
uint64_t end_timestamp = rdtsc();
|
||||
|
||||
uint64_t speed = end_timestamp - start_timestamp;
|
||||
|
||||
if (speed < last_speed) {
|
||||
last_speed = speed;
|
||||
fastest_size = xfer_sizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return fastest_size;
|
||||
}
|
||||
|
||||
int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
|
||||
struct dap dap = {0};
|
||||
|
||||
if (count * volume->sector_size > XFER_BUF_SIZE)
|
||||
panic(false, "XFER");
|
||||
|
||||
if (xfer_buf == NULL)
|
||||
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
|
||||
|
||||
dap.size = 16;
|
||||
dap.count = count;
|
||||
dap.segment = rm_seg(xfer_buf);
|
||||
dap.offset = rm_off(xfer_buf);
|
||||
dap.lba = block;
|
||||
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x4200;
|
||||
r.edx = volume->drive;
|
||||
r.esi = (uint32_t)rm_off(&dap);
|
||||
r.ds = rm_seg(&dap);
|
||||
|
||||
rm_int(0x13, &r, &r);
|
||||
|
||||
if (r.eflags & EFLAGS_CF) {
|
||||
return DISK_FAILURE;
|
||||
}
|
||||
|
||||
if (buf != NULL)
|
||||
memcpy(buf, xfer_buf, count * volume->sector_size);
|
||||
|
||||
return DISK_SUCCESS;
|
||||
}
|
||||
|
||||
static int disk_write_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
|
||||
struct dap dap = {0};
|
||||
|
||||
if (count * volume->sector_size > XFER_BUF_SIZE)
|
||||
panic(false, "XFER");
|
||||
|
||||
if (xfer_buf == NULL)
|
||||
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
|
||||
|
||||
dap.size = 16;
|
||||
dap.count = count;
|
||||
dap.segment = rm_seg(xfer_buf);
|
||||
dap.offset = rm_off(xfer_buf);
|
||||
dap.lba = block;
|
||||
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x4301;
|
||||
r.edx = volume->drive;
|
||||
r.esi = (uint32_t)rm_off(&dap);
|
||||
r.ds = rm_seg(&dap);
|
||||
|
||||
if (buf != NULL)
|
||||
memcpy(xfer_buf, buf, count * volume->sector_size);
|
||||
|
||||
rm_int(0x13, &r, &r);
|
||||
|
||||
if (r.eflags & EFLAGS_CF) {
|
||||
return DISK_FAILURE;
|
||||
}
|
||||
|
||||
return DISK_SUCCESS;
|
||||
}
|
||||
|
||||
static bool detect_sector_size(struct volume *volume) {
|
||||
struct dap dap = {0};
|
||||
|
||||
if (xfer_buf == NULL)
|
||||
xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
|
||||
|
||||
dap.size = 16;
|
||||
dap.count = 1;
|
||||
dap.segment = rm_seg(xfer_buf);
|
||||
dap.offset = rm_off(xfer_buf);
|
||||
dap.lba = 0;
|
||||
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x4200;
|
||||
r.edx = volume->drive;
|
||||
r.esi = (uint32_t)rm_off(&dap);
|
||||
r.ds = rm_seg(&dap);
|
||||
|
||||
struct rm_regs r_copy = r;
|
||||
struct dap dap_copy = dap;
|
||||
|
||||
memset(xfer_buf, 0, XFER_BUF_SIZE);
|
||||
|
||||
rm_int(0x13, &r, &r);
|
||||
|
||||
if (r.eflags & EFLAGS_CF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sector_size_a = 0;
|
||||
for (long i = XFER_BUF_SIZE - 1; i >= 0; i--) {
|
||||
if (xfer_buf[i] != 0) {
|
||||
sector_size_a = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = r_copy;
|
||||
dap = dap_copy;
|
||||
|
||||
memset(xfer_buf, 0xff, XFER_BUF_SIZE);
|
||||
|
||||
rm_int(0x13, &r, &r);
|
||||
|
||||
if (r.eflags & EFLAGS_CF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sector_size_b = 0;
|
||||
for (long i = XFER_BUF_SIZE - 1; i >= 0; i--) {
|
||||
if (xfer_buf[i] != 0xff) {
|
||||
sector_size_b = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
volume->sector_size = sector_size_a > sector_size_b ? sector_size_a : sector_size_b;
|
||||
|
||||
if (volume->sector_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void disk_create_index(void) {
|
||||
volume_index = ext_mem_alloc(sizeof(struct volume) * MAX_VOLUMES);
|
||||
|
||||
int optical_indices = 1, hdd_indices = 1;
|
||||
|
||||
for (uint8_t drive = 0x80; drive < 0xf0; drive++) {
|
||||
struct rm_regs r = {0};
|
||||
struct bios_drive_params drive_params;
|
||||
|
||||
r.eax = 0x4800;
|
||||
r.edx = drive;
|
||||
r.ds = rm_seg(&drive_params);
|
||||
r.esi = rm_off(&drive_params);
|
||||
|
||||
drive_params.buf_size = sizeof(struct bios_drive_params);
|
||||
|
||||
rm_int(0x13, &r, &r);
|
||||
|
||||
if (r.eflags & EFLAGS_CF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct volume *block = ext_mem_alloc(sizeof(struct volume));
|
||||
|
||||
block->drive = drive;
|
||||
block->partition = 0;
|
||||
block->first_sect = 0;
|
||||
block->sect_count = drive_params.lba_count;
|
||||
block->max_partition = -1;
|
||||
|
||||
if (!detect_sector_size(block)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (disk_read_sectors(block, xfer_buf, 0, 1) != DISK_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
block->is_optical = disk_write_sectors(block, xfer_buf, 0, 1) != DISK_SUCCESS;
|
||||
|
||||
if (block->is_optical) {
|
||||
block->index = optical_indices++;
|
||||
} else {
|
||||
block->index = hdd_indices++;
|
||||
}
|
||||
|
||||
block->fastest_xfer_size = fastest_xfer_size(block);
|
||||
|
||||
if (gpt_get_guid(&block->guid, block)) {
|
||||
block->guid_valid = true;
|
||||
}
|
||||
|
||||
if (volume_index_i == MAX_VOLUMES) {
|
||||
print("WARNING: TOO MANY VOLUMES!");
|
||||
return;
|
||||
}
|
||||
volume_index[volume_index_i++] = block;
|
||||
|
||||
for (int part = 0; ; part++) {
|
||||
struct volume *p = ext_mem_alloc(sizeof(struct volume));
|
||||
int ret = part_get(p, block, part);
|
||||
|
||||
if (ret == END_OF_TABLE || ret == INVALID_TABLE)
|
||||
break;
|
||||
if (ret == NO_PARTITION)
|
||||
continue;
|
||||
|
||||
if (volume_index_i == MAX_VOLUMES) {
|
||||
print("WARNING: TOO MANY VOLUMES!");
|
||||
return;
|
||||
}
|
||||
volume_index[volume_index_i++] = p;
|
||||
|
||||
block->max_partition++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (UEFI)
|
||||
|
||||
int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
|
||||
EFI_STATUS status;
|
||||
|
||||
status = volume->block_io->ReadBlocks(volume->block_io,
|
||||
volume->block_io->Media->MediaId,
|
||||
block, count * volume->sector_size, buf);
|
||||
|
||||
switch (status) {
|
||||
case EFI_SUCCESS: return DISK_SUCCESS;
|
||||
case EFI_NO_MEDIA: return DISK_NO_MEDIA;
|
||||
default: return DISK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
static struct volume *pxe_from_efi_handle(EFI_HANDLE efi_handle) {
|
||||
static struct volume *vol = NULL;
|
||||
|
||||
// There's only one PXE volume
|
||||
if (vol) {
|
||||
return vol;
|
||||
}
|
||||
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_GUID pxe_base_code_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID;
|
||||
EFI_PXE_BASE_CODE *pxe_base_code = NULL;
|
||||
|
||||
status = gBS->HandleProtocol(efi_handle, &pxe_base_code_guid, (void **)&pxe_base_code);
|
||||
if (status) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!pxe_base_code->Mode->DhcpDiscoverValid) {
|
||||
print("PXE somehow didn't use DHCP?\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pxe_base_code->Mode->UsingIpv6) {
|
||||
print("Sorry, unsupported: PXE IPv6\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vol = pxe_bind_volume(efi_handle, pxe_base_code);
|
||||
return vol;
|
||||
}
|
||||
|
||||
static alignas(4096) uint8_t unique_sector_pool[4096];
|
||||
|
||||
struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle) {
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_GUID block_io_guid = BLOCK_IO_PROTOCOL;
|
||||
EFI_BLOCK_IO *block_io = NULL;
|
||||
|
||||
status = gBS->HandleProtocol(efi_handle, &block_io_guid, (void **)&block_io);
|
||||
if (status) {
|
||||
return pxe_from_efi_handle(efi_handle);
|
||||
}
|
||||
|
||||
block_io->Media->WriteCaching = false;
|
||||
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
if (volume_index[i]->unique_sector_valid == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (volume_index[i]->unique_sector % block_io->Media->BlockSize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t unique_sector = volume_index[i]->unique_sector / block_io->Media->BlockSize;
|
||||
|
||||
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId,
|
||||
unique_sector,
|
||||
4096,
|
||||
unique_sector_pool);
|
||||
if (status != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t b2b[BLAKE2B_OUT_BYTES];
|
||||
blake2b(b2b, unique_sector_pool, 4096);
|
||||
|
||||
if (memcmp(b2b, volume_index[i]->unique_sector_b2b, BLAKE2B_OUT_BYTES) == 0) {
|
||||
return volume_index[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to read-back method
|
||||
|
||||
uint64_t signature = rand64();
|
||||
uint64_t new_signature;
|
||||
do { new_signature = rand64(); } while (new_signature == signature);
|
||||
uint64_t orig;
|
||||
|
||||
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
orig = *(uint64_t *)unique_sector_pool;
|
||||
if (status) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*(uint64_t *)unique_sector_pool = signature;
|
||||
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct volume *ret = NULL;
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
uint64_t compare;
|
||||
|
||||
status = volume_index[i]->block_io->ReadBlocks(volume_index[i]->block_io,
|
||||
volume_index[i]->block_io->Media->MediaId,
|
||||
(volume_index[i]->first_sect * 512) / volume_index[i]->sector_size,
|
||||
4096, unique_sector_pool);
|
||||
compare = *(uint64_t *)unique_sector_pool;
|
||||
if (status) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (compare == signature) {
|
||||
// Double check
|
||||
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
break;
|
||||
}
|
||||
*(uint64_t *)unique_sector_pool = new_signature;
|
||||
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
break;
|
||||
}
|
||||
|
||||
status = volume_index[i]->block_io->ReadBlocks(volume_index[i]->block_io,
|
||||
volume_index[i]->block_io->Media->MediaId,
|
||||
(volume_index[i]->first_sect * 512) / volume_index[i]->sector_size,
|
||||
4096, unique_sector_pool);
|
||||
compare = *(uint64_t *)unique_sector_pool;
|
||||
if (status) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (compare == new_signature) {
|
||||
ret = volume_index[i];
|
||||
break;
|
||||
}
|
||||
|
||||
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
break;
|
||||
}
|
||||
*(uint64_t *)unique_sector_pool = signature;
|
||||
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
return NULL;
|
||||
}
|
||||
*(uint64_t *)unique_sector_pool = orig;
|
||||
status = block_io->WriteBlocks(block_io, block_io->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ret != NULL) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct volume *volume_by_unique_sector(uint64_t sect, void *b2b) {
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
if (volume_index[i]->unique_sector_valid == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (volume_index[i]->unique_sector == sect
|
||||
&& memcmp(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES) == 0) {
|
||||
return volume_index[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define UNIQUE_SECT_MAX_SEARCH_RANGE 0x1000
|
||||
|
||||
static void find_unique_sectors(void) {
|
||||
EFI_STATUS status;
|
||||
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
for (size_t j = 0; j < UNIQUE_SECT_MAX_SEARCH_RANGE; j++) {
|
||||
if ((volume_index[i]->first_sect * 512) % volume_index[i]->block_io->Media->BlockSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t first_sect = (volume_index[i]->first_sect * 512) / volume_index[i]->block_io->Media->BlockSize;
|
||||
|
||||
status = volume_index[i]->block_io->ReadBlocks(
|
||||
volume_index[i]->block_io,
|
||||
volume_index[i]->block_io->Media->MediaId,
|
||||
first_sect + j,
|
||||
4096,
|
||||
unique_sector_pool);
|
||||
if (status != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t b2b[BLAKE2B_OUT_BYTES];
|
||||
blake2b(b2b, unique_sector_pool, 4096);
|
||||
|
||||
uint64_t uniq = (uint64_t)j * volume_index[i]->block_io->Media->BlockSize;
|
||||
|
||||
if (volume_by_unique_sector(uniq, b2b) == NULL) {
|
||||
volume_index[i]->unique_sector_valid = true;
|
||||
volume_index[i]->unique_sector = uniq;
|
||||
memcpy(volume_index[i]->unique_sector_b2b, b2b, BLAKE2B_OUT_BYTES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void find_part_handles(EFI_HANDLE *handles, size_t handle_count) {
|
||||
for (size_t i = 0; i < handle_count; i++) {
|
||||
struct volume *vol = disk_volume_from_efi_handle(handles[i]);
|
||||
if (vol == NULL) {
|
||||
continue;
|
||||
}
|
||||
vol->efi_part_handle = handles[i];
|
||||
}
|
||||
}
|
||||
|
||||
void disk_create_index(void) {
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_HANDLE tmp_handles[1];
|
||||
|
||||
EFI_GUID block_io_guid = BLOCK_IO_PROTOCOL;
|
||||
EFI_HANDLE *handles = tmp_handles;
|
||||
UINTN handles_size = sizeof(tmp_handles);
|
||||
|
||||
status = gBS->LocateHandle(ByProtocol, &block_io_guid, NULL, &handles_size, handles);
|
||||
|
||||
// we only care about the first handle, so ignore if we get EFI_BUFFER_TOO_SMALL
|
||||
if (status != EFI_BUFFER_TOO_SMALL && status != EFI_SUCCESS) {
|
||||
EFI_GUID pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID;
|
||||
status = gBS->LocateHandle(ByProtocol, &pxe_guid, NULL, &handles_size, handles);
|
||||
// likewise, all that matters is that the protocol is present
|
||||
if (status == EFI_BUFFER_TOO_SMALL || status == EFI_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
handles = ext_mem_alloc(handles_size);
|
||||
|
||||
status = gBS->LocateHandle(ByProtocol, &block_io_guid, NULL, &handles_size, handles);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
fail:
|
||||
panic(false, "LocateHandle for BLOCK_IO_PROTOCOL failed. Machine not supported by Limine UEFI.");
|
||||
}
|
||||
|
||||
volume_index = ext_mem_alloc(sizeof(struct volume) * MAX_VOLUMES);
|
||||
|
||||
int optical_indices = 1, hdd_indices = 1;
|
||||
|
||||
size_t handle_count = handles_size / sizeof(EFI_HANDLE);
|
||||
|
||||
for (size_t i = 0; i < handle_count; i++) {
|
||||
EFI_BLOCK_IO *drive = NULL;
|
||||
|
||||
status = gBS->HandleProtocol(handles[i], &block_io_guid, (void **)&drive);
|
||||
|
||||
if (status != 0 || drive == NULL || drive->Media->LastBlock == 0)
|
||||
continue;
|
||||
|
||||
if (drive->Media->LogicalPartition)
|
||||
continue;
|
||||
|
||||
drive->Media->WriteCaching = false;
|
||||
|
||||
status = drive->ReadBlocks(drive, drive->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
if (status) {
|
||||
continue;
|
||||
}
|
||||
|
||||
status = drive->WriteBlocks(drive, drive->Media->MediaId, 0, 4096, unique_sector_pool);
|
||||
|
||||
struct volume *block = ext_mem_alloc(sizeof(struct volume));
|
||||
|
||||
if (status || drive->Media->ReadOnly) {
|
||||
block->index = optical_indices++;
|
||||
block->is_optical = true;
|
||||
} else {
|
||||
block->index = hdd_indices++;
|
||||
}
|
||||
|
||||
block->efi_handle = handles[i];
|
||||
block->block_io = drive;
|
||||
block->partition = 0;
|
||||
block->sector_size = drive->Media->BlockSize;
|
||||
block->first_sect = 0;
|
||||
block->sect_count = drive->Media->LastBlock + 1;
|
||||
block->max_partition = -1;
|
||||
|
||||
if (drive->Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION3) {
|
||||
block->fastest_xfer_size = drive->Media->OptimalTransferLengthGranularity;
|
||||
}
|
||||
|
||||
if (block->fastest_xfer_size == 0) {
|
||||
block->fastest_xfer_size = DEFAULT_FASTEST_XFER_SIZE;
|
||||
} else if (block->fastest_xfer_size >= MAX_FASTEST_XFER_SIZE) {
|
||||
block->fastest_xfer_size = MAX_FASTEST_XFER_SIZE;
|
||||
}
|
||||
|
||||
if (gpt_get_guid(&block->guid, block)) {
|
||||
block->guid_valid = true;
|
||||
}
|
||||
|
||||
if (volume_index_i == MAX_VOLUMES) {
|
||||
print("WARNING: TOO MANY VOLUMES!");
|
||||
return;
|
||||
}
|
||||
volume_index[volume_index_i++] = block;
|
||||
|
||||
for (int part = 0; ; part++) {
|
||||
struct volume _p = {0};
|
||||
|
||||
int ret = part_get(&_p, block, part);
|
||||
|
||||
if (ret == END_OF_TABLE || ret == INVALID_TABLE)
|
||||
break;
|
||||
if (ret == NO_PARTITION)
|
||||
continue;
|
||||
|
||||
struct volume *p = ext_mem_alloc(sizeof(struct volume));
|
||||
memcpy(p, &_p, sizeof(struct volume));
|
||||
|
||||
if (volume_index_i == MAX_VOLUMES) {
|
||||
print("WARNING: TOO MANY VOLUMES!");
|
||||
return;
|
||||
}
|
||||
volume_index[volume_index_i++] = p;
|
||||
|
||||
block->max_partition++;
|
||||
}
|
||||
}
|
||||
|
||||
find_unique_sectors();
|
||||
find_part_handles(handles, handle_count);
|
||||
|
||||
pmm_free(handles, handles_size);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,84 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <drivers/gop.h>
|
||||
#include <drivers/edid.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/print.h>
|
||||
|
||||
#if defined (BIOS)
|
||||
|
||||
#include <lib/real.h>
|
||||
|
||||
struct edid_info_struct *get_edid_info(void) {
|
||||
static struct edid_info_struct *buf = NULL;
|
||||
|
||||
if (buf == NULL)
|
||||
buf = conv_mem_alloc(sizeof(struct edid_info_struct));
|
||||
|
||||
struct rm_regs r = {0};
|
||||
|
||||
r.eax = 0x4f15;
|
||||
r.ebx = 0x0001;
|
||||
r.edi = (uint32_t)rm_off(buf);
|
||||
r.ds = (uint32_t)rm_seg(buf);
|
||||
r.es = r.ds;
|
||||
rm_int(0x10, &r, &r);
|
||||
|
||||
if ((r.eax & 0x00ff) != 0x4f)
|
||||
goto fail;
|
||||
if ((r.eax & 0xff00) != 0)
|
||||
goto fail;
|
||||
|
||||
for (size_t i = 0; i < sizeof(struct edid_info_struct); i++)
|
||||
if (((uint8_t *)buf)[i] != 0)
|
||||
goto success;
|
||||
|
||||
fail:
|
||||
printv("edid: Could not fetch EDID data.\n");
|
||||
return NULL;
|
||||
|
||||
success:
|
||||
printv("edid: Success.\n");
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (UEFI)
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
struct edid_info_struct *get_edid_info(EFI_HANDLE gop_handle) {
|
||||
struct edid_info_struct *buf = ext_mem_alloc(sizeof(struct edid_info_struct));
|
||||
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_EDID_ACTIVE_PROTOCOL *edid = NULL;
|
||||
EFI_GUID edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID;
|
||||
|
||||
status = gBS->HandleProtocol(gop_handle, &edid_guid, (void **)&edid);
|
||||
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
if (edid->SizeOfEdid < sizeof(struct edid_info_struct))
|
||||
goto fail;
|
||||
|
||||
memcpy(buf, edid->Edid, sizeof(struct edid_info_struct));
|
||||
|
||||
for (size_t i = 0; i < sizeof(struct edid_info_struct); i++)
|
||||
if (((uint8_t *)buf)[i] != 0)
|
||||
goto success;
|
||||
|
||||
fail:
|
||||
printv("edid: Could not fetch EDID data.\n");
|
||||
return NULL;
|
||||
|
||||
success:
|
||||
printv("edid: Success.\n");
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -0,0 +1,340 @@
|
|||
#if defined (UEFI)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <efi.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/term.h>
|
||||
#include <drivers/gop.h>
|
||||
#include <drivers/edid.h>
|
||||
#include <lib/print.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
static uint16_t linear_masks_to_bpp(uint32_t red_mask, uint32_t green_mask,
|
||||
uint32_t blue_mask, uint32_t alpha_mask) {
|
||||
uint32_t compound_mask = red_mask | green_mask | blue_mask | alpha_mask;
|
||||
uint16_t ret = 32;
|
||||
while ((compound_mask & (1 << 31)) == 0) {
|
||||
ret--;
|
||||
compound_mask <<= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void linear_mask_to_mask_shift(
|
||||
uint8_t *mask, uint8_t *shift, uint32_t linear_mask) {
|
||||
*shift = 0;
|
||||
while ((linear_mask & 1) == 0) {
|
||||
(*shift)++;
|
||||
linear_mask >>= 1;
|
||||
}
|
||||
*mask = 0;
|
||||
while ((linear_mask & 1) == 1) {
|
||||
(*mask)++;
|
||||
linear_mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Most of this code taken from https://wiki.osdev.org/GOP
|
||||
|
||||
static bool mode_to_fb_info(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, size_t mode) {
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
|
||||
UINTN mode_info_size;
|
||||
|
||||
status = gop->QueryMode(gop, mode, &mode_info_size, &mode_info);
|
||||
|
||||
if (status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode_info->PixelFormat) {
|
||||
case PixelBlueGreenRedReserved8BitPerColor:
|
||||
ret->framebuffer_bpp = 32;
|
||||
ret->red_mask_size = 8;
|
||||
ret->red_mask_shift = 16;
|
||||
ret->green_mask_size = 8;
|
||||
ret->green_mask_shift = 8;
|
||||
ret->blue_mask_size = 8;
|
||||
ret->blue_mask_shift = 0;
|
||||
break;
|
||||
case PixelRedGreenBlueReserved8BitPerColor:
|
||||
ret->framebuffer_bpp = 32;
|
||||
ret->red_mask_size = 8;
|
||||
ret->red_mask_shift = 0;
|
||||
ret->green_mask_size = 8;
|
||||
ret->green_mask_shift = 8;
|
||||
ret->blue_mask_size = 8;
|
||||
ret->blue_mask_shift = 16;
|
||||
break;
|
||||
case PixelBitMask:
|
||||
ret->framebuffer_bpp = linear_masks_to_bpp(
|
||||
mode_info->PixelInformation.RedMask,
|
||||
mode_info->PixelInformation.GreenMask,
|
||||
mode_info->PixelInformation.BlueMask,
|
||||
mode_info->PixelInformation.ReservedMask);
|
||||
linear_mask_to_mask_shift(&ret->red_mask_size,
|
||||
&ret->red_mask_shift,
|
||||
mode_info->PixelInformation.RedMask);
|
||||
linear_mask_to_mask_shift(&ret->green_mask_size,
|
||||
&ret->green_mask_shift,
|
||||
mode_info->PixelInformation.GreenMask);
|
||||
linear_mask_to_mask_shift(&ret->blue_mask_size,
|
||||
&ret->blue_mask_shift,
|
||||
mode_info->PixelInformation.BlueMask);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
ret->memory_model = 0x06;
|
||||
ret->framebuffer_pitch = mode_info->PixelsPerScanLine * (ret->framebuffer_bpp / 8);
|
||||
ret->framebuffer_width = mode_info->HorizontalResolution;
|
||||
ret->framebuffer_height = mode_info->VerticalResolution;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gop_force_16 = false;
|
||||
|
||||
static bool try_mode(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
|
||||
size_t mode, uint64_t width, uint64_t height, int bpp,
|
||||
struct fb_info *fbs, size_t fbs_count) {
|
||||
EFI_STATUS status;
|
||||
|
||||
if (!mode_to_fb_info(ret, gop, mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (width != 0 && height != 0 && bpp != 0) {
|
||||
if (ret->framebuffer_width != width
|
||||
|| ret->framebuffer_height != height
|
||||
|| ret->framebuffer_bpp != bpp) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (gop_force_16) {
|
||||
if (ret->framebuffer_width >= 65536
|
||||
|| ret->framebuffer_height >= 65536
|
||||
|| ret->framebuffer_pitch >= 65536) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fbs_count; i++) {
|
||||
if (gop->Mode->FrameBufferBase == fbs[i].framebuffer_addr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printv("gop: Found matching mode %x, attempting to set...\n", mode);
|
||||
|
||||
if (mode == gop->Mode->Mode) {
|
||||
printv("gop: Mode was already set, perfect!\n");
|
||||
} else {
|
||||
status = gop->SetMode(gop, mode);
|
||||
|
||||
if (status) {
|
||||
printv("gop: Failed to set video mode %x, moving on...\n", mode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ret->framebuffer_addr = gop->Mode->FrameBufferBase;
|
||||
|
||||
fb_clear(ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct fb_info *get_mode_list(size_t *count, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop) {
|
||||
UINTN modes_count = gop->Mode->MaxMode;
|
||||
|
||||
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
|
||||
|
||||
size_t actual_count = 0;
|
||||
for (size_t i = 0; i < modes_count; i++) {
|
||||
if (mode_to_fb_info(&ret[actual_count], gop, i)) {
|
||||
actual_count++;
|
||||
}
|
||||
}
|
||||
|
||||
struct fb_info *tmp = ext_mem_alloc(actual_count * sizeof(struct fb_info));
|
||||
memcpy(tmp, ret, actual_count * sizeof(struct fb_info));
|
||||
|
||||
pmm_free(ret, modes_count * sizeof(struct fb_info));
|
||||
ret = tmp;
|
||||
|
||||
*count = modes_count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_PRESET_MODES 128
|
||||
no_unwind static int preset_modes[MAX_PRESET_MODES];
|
||||
no_unwind static bool preset_modes_initialised = false;
|
||||
|
||||
void init_gop(struct fb_info **ret, size_t *_fbs_count,
|
||||
uint64_t target_width, uint64_t target_height, uint16_t target_bpp) {
|
||||
if (preset_modes_initialised == false) {
|
||||
for (size_t i = 0; i < MAX_PRESET_MODES; i++) {
|
||||
preset_modes[i] = -1;
|
||||
}
|
||||
preset_modes_initialised = true;
|
||||
}
|
||||
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_HANDLE tmp_handles[1];
|
||||
|
||||
EFI_HANDLE *handles = tmp_handles;
|
||||
UINTN handles_size = sizeof(EFI_HANDLE);
|
||||
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
|
||||
status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles);
|
||||
|
||||
if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
|
||||
*_fbs_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
handles = ext_mem_alloc(handles_size);
|
||||
|
||||
status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pmm_free(handles, handles_size);
|
||||
*_fbs_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t handles_count = handles_size / sizeof(EFI_HANDLE);
|
||||
|
||||
*ret = ext_mem_alloc(handles_count * sizeof(struct fb_info));
|
||||
|
||||
const struct resolution fallback_resolutions[] = {
|
||||
{ 0, 0, 0 }, // Overridden by EDID
|
||||
{ 0, 0, 0 }, // Overridden by preset
|
||||
{ 1024, 768, 32 },
|
||||
{ 800, 600, 32 },
|
||||
{ 640, 480, 32 },
|
||||
{ 1024, 768, 24 },
|
||||
{ 800, 600, 24 },
|
||||
{ 640, 480, 24 },
|
||||
{ 1024, 768, 16 },
|
||||
{ 800, 600, 16 },
|
||||
{ 640, 480, 16 }
|
||||
};
|
||||
|
||||
size_t fbs_count = 0;
|
||||
for (size_t i = 0; i < handles_count && i < MAX_PRESET_MODES; i++) {
|
||||
struct fb_info *fb = &(*ret)[fbs_count];
|
||||
|
||||
uint64_t _target_width = target_width;
|
||||
uint64_t _target_height = target_height;
|
||||
uint64_t _target_bpp = target_bpp;
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
|
||||
|
||||
status = gBS->HandleProtocol(handles[i], &gop_guid, (void **)&gop);
|
||||
if (status != EFI_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
|
||||
UINTN mode_info_size;
|
||||
|
||||
status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode,
|
||||
&mode_info_size, &mode_info);
|
||||
|
||||
if (status == EFI_NOT_STARTED) {
|
||||
status = gop->SetMode(gop, 0);
|
||||
if (status) {
|
||||
continue;
|
||||
}
|
||||
status = gop->QueryMode(gop, gop->Mode == NULL ? 0 : gop->Mode->Mode,
|
||||
&mode_info_size, &mode_info);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preset_modes[i] == -1) {
|
||||
preset_modes[i] = gop->Mode->Mode;
|
||||
}
|
||||
|
||||
fb->edid = get_edid_info(handles[i]);
|
||||
|
||||
UINTN modes_count = gop->Mode->MaxMode;
|
||||
|
||||
size_t current_fallback = 0;
|
||||
|
||||
if (!_target_width || !_target_height || !_target_bpp) {
|
||||
goto fallback;
|
||||
} else {
|
||||
printv("gop: Requested resolution of %ux%ux%u\n",
|
||||
_target_width, _target_height, _target_bpp);
|
||||
}
|
||||
|
||||
retry:
|
||||
for (size_t j = 0; j < modes_count; j++) {
|
||||
if (try_mode(fb, gop, j, _target_width, _target_height, _target_bpp, *ret, fbs_count)) {
|
||||
goto success;
|
||||
}
|
||||
}
|
||||
|
||||
fallback:
|
||||
if (current_fallback == 0) {
|
||||
current_fallback++;
|
||||
|
||||
if (fb->edid != NULL) {
|
||||
uint64_t edid_width = (uint64_t)fb->edid->det_timing_desc1[2];
|
||||
edid_width += ((uint64_t)fb->edid->det_timing_desc1[4] & 0xf0) << 4;
|
||||
uint64_t edid_height = (uint64_t)fb->edid->det_timing_desc1[5];
|
||||
edid_height += ((uint64_t)fb->edid->det_timing_desc1[7] & 0xf0) << 4;
|
||||
if (edid_width >= mode_info->HorizontalResolution
|
||||
&& edid_height >= mode_info->VerticalResolution) {
|
||||
_target_width = edid_width;
|
||||
_target_height = edid_height;
|
||||
_target_bpp = 32;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_fallback == 1) {
|
||||
current_fallback++;
|
||||
|
||||
if (try_mode(fb, gop, preset_modes[i], 0, 0, 0, *ret, fbs_count)) {
|
||||
goto success;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) {
|
||||
current_fallback++;
|
||||
|
||||
_target_width = fallback_resolutions[current_fallback].width;
|
||||
_target_height = fallback_resolutions[current_fallback].height;
|
||||
_target_bpp = fallback_resolutions[current_fallback].bpp;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
success:;
|
||||
size_t mode_count;
|
||||
fb->mode_list = get_mode_list(&mode_count, gop);
|
||||
fb->mode_count = mode_count;
|
||||
|
||||
fbs_count++;
|
||||
}
|
||||
|
||||
pmm_free(handles, handles_size);
|
||||
|
||||
gop_force_16 = false;
|
||||
|
||||
*_fbs_count = fbs_count;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __DRIVERS__SERIAL_H__
|
||||
#define __DRIVERS__SERIAL_H__
|
||||
|
||||
#if defined (BIOS)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void serial_out(uint8_t b);
|
||||
int serial_in(void);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,46 @@
|
|||
#if defined (BIOS)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <drivers/serial.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <lib/misc.h>
|
||||
|
||||
static bool serial_initialised = false;
|
||||
|
||||
static void serial_initialise(void) {
|
||||
if (serial_initialised) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Init com1
|
||||
outb(0x3f8 + 3, 0x00);
|
||||
outb(0x3f8 + 1, 0x00);
|
||||
outb(0x3f8 + 3, 0x80);
|
||||
outb(0x3f8 + 0, 0x0c); // 9600 baud
|
||||
outb(0x3f8 + 1, 0x00);
|
||||
outb(0x3f8 + 3, 0x03);
|
||||
outb(0x3f8 + 2, 0xc7);
|
||||
outb(0x3f8 + 4, 0x0b);
|
||||
|
||||
serial_initialised = true;
|
||||
}
|
||||
|
||||
void serial_out(uint8_t b) {
|
||||
serial_initialise();
|
||||
|
||||
while ((inb(0x3f8 + 5) & 0x20) == 0);
|
||||
outb(0x3f8, b);
|
||||
}
|
||||
|
||||
int serial_in(void) {
|
||||
serial_initialise();
|
||||
|
||||
if ((inb(0x3f8 + 5) & 0x01) == 0) {
|
||||
return -1;
|
||||
}
|
||||
return inb(0x3f8);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,303 @@
|
|||
#if defined (BIOS)
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <drivers/vbe.h>
|
||||
#include <drivers/edid.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/real.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/image.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/uri.h>
|
||||
#include <lib/term.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
struct vbe_info_struct {
|
||||
char signature[4];
|
||||
uint8_t version_min;
|
||||
uint8_t version_maj;
|
||||
uint16_t oem_off;
|
||||
uint16_t oem_seg;
|
||||
uint32_t capabilities;
|
||||
uint16_t vid_modes_off;
|
||||
uint16_t vid_modes_seg;
|
||||
uint16_t vid_mem_blocks;
|
||||
uint16_t software_rev;
|
||||
uint16_t vendor_off;
|
||||
uint16_t vendor_seg;
|
||||
uint16_t prod_name_off;
|
||||
uint16_t prod_name_seg;
|
||||
uint16_t prod_rev_off;
|
||||
uint16_t prod_rev_seg;
|
||||
uint8_t reserved[222];
|
||||
uint8_t oem_data[256];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vbe_mode_info_struct {
|
||||
uint16_t mode_attributes;
|
||||
uint8_t wina_attributes;
|
||||
uint8_t winb_attributes;
|
||||
uint16_t win_granularity;
|
||||
uint16_t win_size;
|
||||
uint16_t wina_segment;
|
||||
uint16_t winb_segment;
|
||||
uint32_t win_farptr;
|
||||
uint16_t bytes_per_scanline;
|
||||
|
||||
uint16_t res_x;
|
||||
uint16_t res_y;
|
||||
uint8_t charsize_x;
|
||||
uint8_t charsize_y;
|
||||
uint8_t plane_count;
|
||||
uint8_t bpp;
|
||||
uint8_t bank_count;
|
||||
uint8_t memory_model;
|
||||
uint8_t bank_size;
|
||||
uint8_t image_count;
|
||||
uint8_t reserved0;
|
||||
|
||||
uint8_t red_mask_size;
|
||||
uint8_t red_mask_shift;
|
||||
uint8_t green_mask_size;
|
||||
uint8_t green_mask_shift;
|
||||
uint8_t blue_mask_size;
|
||||
uint8_t blue_mask_shift;
|
||||
uint8_t rsvd_mask_size;
|
||||
uint8_t rsvd_mask_shift;
|
||||
uint8_t direct_color_info;
|
||||
|
||||
uint32_t framebuffer_addr;
|
||||
uint8_t reserved1[6];
|
||||
|
||||
uint16_t lin_bytes_per_scanline;
|
||||
uint8_t banked_image_count;
|
||||
uint8_t lin_image_count;
|
||||
uint8_t lin_red_mask_size;
|
||||
uint8_t lin_red_mask_shift;
|
||||
uint8_t lin_green_mask_size;
|
||||
uint8_t lin_green_mask_shift;
|
||||
uint8_t lin_blue_mask_size;
|
||||
uint8_t lin_blue_mask_shift;
|
||||
uint8_t lin_rsvd_mask_size;
|
||||
uint8_t lin_rsvd_mask_shift;
|
||||
uint32_t max_pixel_clock;
|
||||
|
||||
uint8_t reserved2[189];
|
||||
} __attribute__((packed));
|
||||
|
||||
static void get_vbe_info(struct vbe_info_struct *buf) {
|
||||
struct rm_regs r = {0};
|
||||
|
||||
r.eax = 0x4f00;
|
||||
r.edi = (uint32_t)buf;
|
||||
rm_int(0x10, &r, &r);
|
||||
}
|
||||
|
||||
static void get_vbe_mode_info(struct vbe_mode_info_struct *buf,
|
||||
uint16_t mode) {
|
||||
struct rm_regs r = {0};
|
||||
|
||||
r.eax = 0x4f01;
|
||||
r.ecx = (uint32_t)mode;
|
||||
r.edi = (uint32_t)buf;
|
||||
rm_int(0x10, &r, &r);
|
||||
}
|
||||
|
||||
static int set_vbe_mode(uint16_t mode) {
|
||||
struct rm_regs r = {0};
|
||||
|
||||
r.eax = 0x4f02;
|
||||
r.ebx = (uint32_t)mode | (1 << 14);
|
||||
rm_int(0x10, &r, &r);
|
||||
|
||||
return r.eax & 0xff;
|
||||
}
|
||||
|
||||
struct fb_info *vbe_get_mode_list(size_t *count) {
|
||||
struct vbe_info_struct vbe_info;
|
||||
get_vbe_info(&vbe_info);
|
||||
|
||||
uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
|
||||
vbe_info.vid_modes_off);
|
||||
|
||||
size_t modes_count = 0;
|
||||
for (size_t i = 0; vid_modes[i] != 0xffff; i++) {
|
||||
struct vbe_mode_info_struct vbe_mode_info;
|
||||
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
|
||||
|
||||
// We only support RGB for now
|
||||
if (vbe_mode_info.memory_model != 0x06)
|
||||
continue;
|
||||
// We only support linear modes
|
||||
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
|
||||
continue;
|
||||
|
||||
modes_count++;
|
||||
}
|
||||
|
||||
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
|
||||
|
||||
for (size_t i = 0, j = 0; vid_modes[i] != 0xffff; i++) {
|
||||
struct vbe_mode_info_struct vbe_mode_info;
|
||||
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
|
||||
|
||||
// We only support RGB for now
|
||||
if (vbe_mode_info.memory_model != 0x06)
|
||||
continue;
|
||||
// We only support linear modes
|
||||
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
|
||||
continue;
|
||||
|
||||
ret[j].memory_model = vbe_mode_info.memory_model;
|
||||
|
||||
ret[j].framebuffer_width = vbe_mode_info.res_x;
|
||||
ret[j].framebuffer_height = vbe_mode_info.res_y;
|
||||
ret[j].framebuffer_bpp = vbe_mode_info.bpp;
|
||||
|
||||
if (vbe_info.version_maj < 3) {
|
||||
ret[j].framebuffer_pitch = vbe_mode_info.bytes_per_scanline;
|
||||
ret[j].red_mask_size = vbe_mode_info.red_mask_size;
|
||||
ret[j].red_mask_shift = vbe_mode_info.red_mask_shift;
|
||||
ret[j].green_mask_size = vbe_mode_info.green_mask_size;
|
||||
ret[j].green_mask_shift = vbe_mode_info.green_mask_shift;
|
||||
ret[j].blue_mask_size = vbe_mode_info.blue_mask_size;
|
||||
ret[j].blue_mask_shift = vbe_mode_info.blue_mask_shift;
|
||||
} else {
|
||||
ret[j].framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline;
|
||||
ret[j].red_mask_size = vbe_mode_info.lin_red_mask_size;
|
||||
ret[j].red_mask_shift = vbe_mode_info.lin_red_mask_shift;
|
||||
ret[j].green_mask_size = vbe_mode_info.lin_green_mask_size;
|
||||
ret[j].green_mask_shift = vbe_mode_info.lin_green_mask_shift;
|
||||
ret[j].blue_mask_size = vbe_mode_info.lin_blue_mask_size;
|
||||
ret[j].blue_mask_shift = vbe_mode_info.lin_blue_mask_shift;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
*count = modes_count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool init_vbe(struct fb_info *ret,
|
||||
uint16_t target_width, uint16_t target_height, uint16_t target_bpp) {
|
||||
printv("vbe: Initialising...\n");
|
||||
|
||||
size_t current_fallback = 0;
|
||||
|
||||
struct vbe_info_struct vbe_info;
|
||||
get_vbe_info(&vbe_info);
|
||||
|
||||
printv("vbe: Version: %u.%u\n", vbe_info.version_maj, vbe_info.version_min);
|
||||
printv("vbe: OEM: %s\n", (char *)rm_desegment(vbe_info.oem_seg, vbe_info.oem_off));
|
||||
printv("vbe: Graphics vendor: %s\n", (char *)rm_desegment(vbe_info.vendor_seg, vbe_info.vendor_off));
|
||||
printv("vbe: Product name: %s\n", (char *)rm_desegment(vbe_info.prod_name_seg, vbe_info.prod_name_off));
|
||||
printv("vbe: Product revision: %s\n", (char *)rm_desegment(vbe_info.prod_rev_seg, vbe_info.prod_rev_off));
|
||||
|
||||
uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
|
||||
vbe_info.vid_modes_off);
|
||||
|
||||
struct resolution fallback_resolutions[] = {
|
||||
{ 1024, 768, 32 },
|
||||
{ 800, 600, 32 },
|
||||
{ 640, 480, 32 },
|
||||
{ 1024, 768, 24 },
|
||||
{ 800, 600, 24 },
|
||||
{ 640, 480, 24 },
|
||||
{ 1024, 768, 16 },
|
||||
{ 800, 600, 16 },
|
||||
{ 640, 480, 16 }
|
||||
};
|
||||
|
||||
if (!target_width || !target_height || !target_bpp) {
|
||||
struct edid_info_struct *edid_info = get_edid_info();
|
||||
if (edid_info != NULL) {
|
||||
int edid_width = (int)edid_info->det_timing_desc1[2];
|
||||
edid_width += ((int)edid_info->det_timing_desc1[4] & 0xf0) << 4;
|
||||
int edid_height = (int)edid_info->det_timing_desc1[5];
|
||||
edid_height += ((int)edid_info->det_timing_desc1[7] & 0xf0) << 4;
|
||||
if (edid_width && edid_height) {
|
||||
target_width = edid_width;
|
||||
target_height = edid_height;
|
||||
target_bpp = 32;
|
||||
printv("vbe: EDID detected screen resolution of %ux%u\n",
|
||||
target_width, target_height);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
goto fallback;
|
||||
} else {
|
||||
printv("vbe: Requested resolution of %ux%ux%u\n",
|
||||
target_width, target_height, target_bpp);
|
||||
}
|
||||
|
||||
retry:
|
||||
for (size_t i = 0; vid_modes[i] != 0xffff; i++) {
|
||||
struct vbe_mode_info_struct vbe_mode_info;
|
||||
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
|
||||
if (vbe_mode_info.res_x == target_width
|
||||
&& vbe_mode_info.res_y == target_height
|
||||
&& vbe_mode_info.bpp == target_bpp) {
|
||||
// We only support RGB for now
|
||||
if (vbe_mode_info.memory_model != 0x06)
|
||||
continue;
|
||||
// We only support linear modes
|
||||
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
|
||||
continue;
|
||||
printv("vbe: Found matching mode %x, attempting to set...\n", vid_modes[i]);
|
||||
if (vid_modes[i] == current_video_mode) {
|
||||
printv("vbe: Mode was already set, perfect!\n");
|
||||
} else if (set_vbe_mode(vid_modes[i]) == 0x01) {
|
||||
current_video_mode = -1;
|
||||
printv("vbe: Failed to set video mode %x, moving on...\n", vid_modes[i]);
|
||||
continue;
|
||||
}
|
||||
current_video_mode = vid_modes[i];
|
||||
|
||||
printv("vbe: Framebuffer address: %x\n", vbe_mode_info.framebuffer_addr);
|
||||
ret->memory_model = vbe_mode_info.memory_model;
|
||||
ret->framebuffer_addr = vbe_mode_info.framebuffer_addr;
|
||||
ret->framebuffer_width = vbe_mode_info.res_x;
|
||||
ret->framebuffer_height = vbe_mode_info.res_y;
|
||||
ret->framebuffer_bpp = vbe_mode_info.bpp;
|
||||
if (vbe_info.version_maj < 3) {
|
||||
ret->framebuffer_pitch = vbe_mode_info.bytes_per_scanline;
|
||||
ret->red_mask_size = vbe_mode_info.red_mask_size;
|
||||
ret->red_mask_shift = vbe_mode_info.red_mask_shift;
|
||||
ret->green_mask_size = vbe_mode_info.green_mask_size;
|
||||
ret->green_mask_shift = vbe_mode_info.green_mask_shift;
|
||||
ret->blue_mask_size = vbe_mode_info.blue_mask_size;
|
||||
ret->blue_mask_shift = vbe_mode_info.blue_mask_shift;
|
||||
} else {
|
||||
ret->framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline;
|
||||
ret->red_mask_size = vbe_mode_info.lin_red_mask_size;
|
||||
ret->red_mask_shift = vbe_mode_info.lin_red_mask_shift;
|
||||
ret->green_mask_size = vbe_mode_info.lin_green_mask_size;
|
||||
ret->green_mask_shift = vbe_mode_info.lin_green_mask_shift;
|
||||
ret->blue_mask_size = vbe_mode_info.lin_blue_mask_size;
|
||||
ret->blue_mask_shift = vbe_mode_info.lin_blue_mask_shift;
|
||||
}
|
||||
|
||||
fb_clear(ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fallback:
|
||||
if (current_fallback < SIZEOF_ARRAY(fallback_resolutions)) {
|
||||
target_width = fallback_resolutions[current_fallback].width;
|
||||
target_height = fallback_resolutions[current_fallback].height;
|
||||
target_bpp = fallback_resolutions[current_fallback].bpp;
|
||||
current_fallback++;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -0,0 +1,368 @@
|
|||
#if defined (BIOS)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <drivers/vga_textmode.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <lib/real.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/term.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
|
||||
|
||||
static void draw_cursor(struct textmode_context *ctx) {
|
||||
uint8_t pal = ctx->back_buffer[ctx->cursor_offset + 1];
|
||||
ctx->video_mem[ctx->cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
|
||||
}
|
||||
|
||||
static void text_save_state(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->saved_state_text_palette = ctx->text_palette;
|
||||
ctx->saved_state_cursor_offset = ctx->cursor_offset;
|
||||
}
|
||||
|
||||
static void text_restore_state(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = ctx->saved_state_text_palette;
|
||||
ctx->cursor_offset = ctx->saved_state_cursor_offset;
|
||||
}
|
||||
|
||||
static void text_swap_palette(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette << 4) | (ctx->text_palette >> 4);
|
||||
}
|
||||
|
||||
static void text_scroll(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
// move the text up by one row
|
||||
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
|
||||
i < (_ctx->scroll_bottom_margin - 1) * VD_COLS; i++) {
|
||||
ctx->back_buffer[i] = ctx->back_buffer[i + VD_COLS];
|
||||
}
|
||||
// clear the last line of the screen
|
||||
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS;
|
||||
i < _ctx->scroll_bottom_margin * VD_COLS; i += 2) {
|
||||
ctx->back_buffer[i] = ' ';
|
||||
ctx->back_buffer[i + 1] = ctx->text_palette;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_revscroll(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
// move the text up by one row
|
||||
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
|
||||
ctx->back_buffer[i + VD_COLS] = ctx->back_buffer[i];
|
||||
if (i == _ctx->scroll_top_margin * VD_COLS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// clear the first line of the screen
|
||||
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
|
||||
i < (_ctx->scroll_top_margin + 1) * VD_COLS; i += 2) {
|
||||
ctx->back_buffer[i] = ' ';
|
||||
ctx->back_buffer[i + 1] = ctx->text_palette;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_clear(struct flanterm_context *_ctx, bool move) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
|
||||
ctx->back_buffer[i] = ' ';
|
||||
ctx->back_buffer[i + 1] = ctx->text_palette;
|
||||
}
|
||||
if (move) {
|
||||
ctx->cursor_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_full_refresh(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
|
||||
ctx->video_mem[i] = ctx->front_buffer[i];
|
||||
ctx->back_buffer[i] = ctx->front_buffer[i];
|
||||
}
|
||||
|
||||
if (_ctx->cursor_enabled) {
|
||||
draw_cursor(ctx);
|
||||
ctx->old_cursor_offset = ctx->cursor_offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_double_buffer_flush(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
if (_ctx->cursor_enabled) {
|
||||
draw_cursor(ctx);
|
||||
}
|
||||
|
||||
if (ctx->cursor_offset != ctx->old_cursor_offset || _ctx->cursor_enabled == false) {
|
||||
ctx->video_mem[ctx->old_cursor_offset + 1] = ctx->back_buffer[ctx->old_cursor_offset + 1];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
|
||||
if (ctx->back_buffer[i] == ctx->front_buffer[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx->front_buffer[i] = ctx->back_buffer[i];
|
||||
|
||||
if (_ctx->cursor_enabled && i == ctx->cursor_offset + 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx->video_mem[i] = ctx->back_buffer[i];
|
||||
}
|
||||
|
||||
if (_ctx->cursor_enabled) {
|
||||
ctx->old_cursor_offset = ctx->cursor_offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
*x = (ctx->cursor_offset % VD_COLS) / 2;
|
||||
*y = ctx->cursor_offset / VD_COLS;
|
||||
}
|
||||
|
||||
static void text_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS
|
||||
|| new_x >= VD_COLS / 2 || new_y >= VD_ROWS) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->back_buffer[new_y * VD_COLS + new_x * 2] = ctx->back_buffer[old_y * VD_COLS + old_x * 2];
|
||||
}
|
||||
|
||||
static void text_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
if (x >= VD_COLS / 2) {
|
||||
if ((int)x < 0) {
|
||||
x = 0;
|
||||
} else {
|
||||
x = VD_COLS / 2 - 1;
|
||||
}
|
||||
}
|
||||
if (y >= VD_ROWS) {
|
||||
if ((int)y < 0) {
|
||||
y = 0;
|
||||
} else {
|
||||
y = VD_ROWS - 1;
|
||||
}
|
||||
}
|
||||
ctx->cursor_offset = y * VD_COLS + x * 2;
|
||||
}
|
||||
|
||||
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
|
||||
|
||||
static void text_set_text_fg(struct flanterm_context *_ctx, size_t fg) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0xf0) | ansi_colours[fg];
|
||||
}
|
||||
|
||||
static void text_set_text_bg(struct flanterm_context *_ctx, size_t bg) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0x0f) | (ansi_colours[bg] << 4);
|
||||
}
|
||||
|
||||
static void text_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
|
||||
}
|
||||
|
||||
static void text_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
|
||||
}
|
||||
|
||||
static void text_set_text_fg_rgb(struct flanterm_context *ctx, uint32_t n) {
|
||||
(void)ctx;
|
||||
(void)n;
|
||||
}
|
||||
|
||||
static void text_set_text_bg_rgb(struct flanterm_context *ctx, uint32_t n) {
|
||||
(void)ctx;
|
||||
(void)n;
|
||||
}
|
||||
|
||||
static void text_set_text_fg_default(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0xf0) | 7;
|
||||
}
|
||||
|
||||
static void text_set_text_bg_default(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette &= 0x0f;
|
||||
}
|
||||
|
||||
static void text_set_text_fg_default_bright(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0xf0) | (7 | (1 << 3));
|
||||
}
|
||||
|
||||
static void text_set_text_bg_default_bright(struct flanterm_context *_ctx) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
ctx->text_palette = (ctx->text_palette & 0x0f) | ((1 << 3) << 4);
|
||||
}
|
||||
|
||||
static void text_putchar(struct flanterm_context *_ctx, uint8_t c) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
ctx->back_buffer[ctx->cursor_offset] = c;
|
||||
ctx->back_buffer[ctx->cursor_offset + 1] = ctx->text_palette;
|
||||
if (ctx->cursor_offset / VD_COLS == _ctx->scroll_bottom_margin - 1
|
||||
&& ctx->cursor_offset % VD_COLS == VD_COLS - 2) {
|
||||
if (_ctx->scroll_enabled) {
|
||||
text_scroll(_ctx);
|
||||
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
|
||||
}
|
||||
} else if (ctx->cursor_offset >= (VIDEO_BOTTOM - 1)) {
|
||||
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
|
||||
} else {
|
||||
ctx->cursor_offset += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void text_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) {
|
||||
struct textmode_context *ctx = (void *)_ctx;
|
||||
|
||||
if (ctx->back_buffer != NULL) {
|
||||
_free(ctx->back_buffer, VD_ROWS * VD_COLS);
|
||||
ctx->back_buffer = NULL;
|
||||
}
|
||||
|
||||
if (ctx->front_buffer != NULL) {
|
||||
_free(ctx->front_buffer, VD_ROWS * VD_COLS);
|
||||
ctx->front_buffer = NULL;
|
||||
}
|
||||
|
||||
pmm_free(ctx, sizeof(struct textmode_context));
|
||||
}
|
||||
|
||||
void vga_textmode_init(bool managed) {
|
||||
term_notready();
|
||||
|
||||
if (quiet) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_video_mode != 0x3) {
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x0003;
|
||||
rm_int(0x10, &r, &r);
|
||||
|
||||
current_video_mode = 0x3;
|
||||
}
|
||||
|
||||
terms = ext_mem_alloc(sizeof(void *));
|
||||
terms_i = 1;
|
||||
|
||||
terms[0] = ext_mem_alloc(sizeof(struct textmode_context));
|
||||
|
||||
struct flanterm_context *term = terms[0];
|
||||
struct textmode_context *ctx = (void *)term;
|
||||
|
||||
if (ctx->back_buffer == NULL) {
|
||||
ctx->back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
|
||||
} else {
|
||||
memset(ctx->back_buffer, 0, VD_ROWS * VD_COLS);
|
||||
}
|
||||
if (ctx->front_buffer == NULL) {
|
||||
ctx->front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
|
||||
} else {
|
||||
memset(ctx->front_buffer, 0, VD_ROWS * VD_COLS);
|
||||
}
|
||||
|
||||
ctx->cursor_offset = 0;
|
||||
ctx->text_palette = 0x07;
|
||||
|
||||
ctx->video_mem = (volatile uint8_t *)0xb8000;
|
||||
|
||||
text_clear(term, false);
|
||||
|
||||
// VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
|
||||
|
||||
if (!managed) {
|
||||
term->cursor_enabled = false;
|
||||
|
||||
outb(0x3d4, 0x0a);
|
||||
outb(0x3d5, (inb(0x3d5) & 0xc0) | 14);
|
||||
outb(0x3d4, 0x0b);
|
||||
outb(0x3d5, (inb(0x3d5) & 0xe0) | 15);
|
||||
outb(0x3d4, 0x0f);
|
||||
outb(0x3d5, 0);
|
||||
outb(0x3d4, 0x0e);
|
||||
outb(0x3d5, 0);
|
||||
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x0200;
|
||||
rm_int(0x10, &r, &r);
|
||||
} else {
|
||||
outb(0x3d4, 0x0a);
|
||||
outb(0x3d5, 0x20);
|
||||
}
|
||||
|
||||
text_double_buffer_flush(term);
|
||||
|
||||
if (managed && serial) {
|
||||
term->cols = 80;
|
||||
term->rows = 24;
|
||||
} else {
|
||||
term->cols = 80;
|
||||
term->rows = 25;
|
||||
}
|
||||
|
||||
term->raw_putchar = text_putchar;
|
||||
term->clear = text_clear;
|
||||
term->set_cursor_pos = text_set_cursor_pos;
|
||||
term->get_cursor_pos = text_get_cursor_pos;
|
||||
term->set_text_fg = text_set_text_fg;
|
||||
term->set_text_bg = text_set_text_bg;
|
||||
term->set_text_fg_bright = text_set_text_fg_bright;
|
||||
term->set_text_bg_bright = text_set_text_bg_bright;
|
||||
term->set_text_fg_rgb = text_set_text_fg_rgb;
|
||||
term->set_text_bg_rgb = text_set_text_bg_rgb;
|
||||
term->set_text_fg_default = text_set_text_fg_default;
|
||||
term->set_text_bg_default = text_set_text_bg_default;
|
||||
term->set_text_fg_default_bright = text_set_text_fg_default_bright;
|
||||
term->set_text_bg_default_bright = text_set_text_bg_default_bright;
|
||||
term->move_character = text_move_character;
|
||||
term->scroll = text_scroll;
|
||||
term->revscroll = text_revscroll;
|
||||
term->swap_palette = text_swap_palette;
|
||||
term->save_state = text_save_state;
|
||||
term->restore_state = text_restore_state;
|
||||
term->double_buffer_flush = text_double_buffer_flush;
|
||||
term->full_refresh = text_full_refresh;
|
||||
term->deinit = text_deinit;
|
||||
|
||||
flanterm_context_reinit(term);
|
||||
|
||||
if (!managed) {
|
||||
term->cursor_enabled = false;
|
||||
}
|
||||
|
||||
term->full_refresh(term);
|
||||
|
||||
if (!managed) {
|
||||
term->deinit(term, pmm_free);
|
||||
pmm_free(terms, sizeof(void *));
|
||||
terms_i = 0;
|
||||
terms = NULL;
|
||||
term_backend = _NOT_READY;
|
||||
} else {
|
||||
term_backend = TEXTMODE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef __DRIVERS__VGA_TEXTMODE_H__
|
||||
#define __DRIVERS__VGA_TEXTMODE_H__
|
||||
|
||||
#if defined (BIOS)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <flanterm/flanterm.h>
|
||||
|
||||
#define VD_COLS (80 * 2)
|
||||
#define VD_ROWS 25
|
||||
|
||||
struct textmode_context {
|
||||
struct flanterm_context term;
|
||||
|
||||
volatile uint8_t *video_mem;
|
||||
|
||||
uint8_t *back_buffer;
|
||||
uint8_t *front_buffer;
|
||||
|
||||
size_t cursor_offset;
|
||||
size_t old_cursor_offset;
|
||||
bool cursor_status;
|
||||
uint8_t text_palette;
|
||||
|
||||
uint8_t saved_state_text_palette;
|
||||
size_t saved_state_cursor_offset;
|
||||
};
|
||||
|
||||
void vga_textmode_init(bool managed);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,128 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <lib/term.h>
|
||||
#include <lib/real.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/trace.h>
|
||||
#include <sys/e820.h>
|
||||
#include <sys/a20.h>
|
||||
#include <lib/print.h>
|
||||
#include <fs/file.h>
|
||||
#include <lib/elf.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <protos/linux.h>
|
||||
#include <protos/chainload.h>
|
||||
#include <menu.h>
|
||||
#include <pxe/pxe.h>
|
||||
#include <pxe/tftp.h>
|
||||
#include <drivers/disk.h>
|
||||
#include <sys/idt.h>
|
||||
#include <sys/cpu.h>
|
||||
|
||||
struct volume *boot_volume;
|
||||
|
||||
#if defined (BIOS)
|
||||
|
||||
bool stage3_loaded = false;
|
||||
static bool stage3_found = false;
|
||||
|
||||
extern symbol stage3_addr;
|
||||
extern symbol limine_bios_sys_size;
|
||||
extern symbol build_id_s2;
|
||||
extern symbol build_id_s3;
|
||||
|
||||
static bool stage3_init(struct volume *part) {
|
||||
struct file_handle *stage3;
|
||||
|
||||
bool old_cif = case_insensitive_fopen;
|
||||
case_insensitive_fopen = true;
|
||||
if ((stage3 = fopen(part, "/limine-bios.sys")) == NULL
|
||||
&& (stage3 = fopen(part, "/limine/limine-bios.sys")) == NULL
|
||||
&& (stage3 = fopen(part, "/boot/limine-bios.sys")) == NULL
|
||||
&& (stage3 = fopen(part, "/boot/limine/limine-bios.sys")) == NULL) {
|
||||
case_insensitive_fopen = old_cif;
|
||||
return false;
|
||||
}
|
||||
case_insensitive_fopen = old_cif;
|
||||
|
||||
stage3_found = true;
|
||||
|
||||
if (stage3->size != (size_t)limine_bios_sys_size) {
|
||||
print("limine-bios.sys size incorrect.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fread(stage3, stage3_addr,
|
||||
(uintptr_t)stage3_addr - 0xf000,
|
||||
stage3->size - ((uintptr_t)stage3_addr - 0xf000));
|
||||
|
||||
fclose(stage3);
|
||||
|
||||
if (memcmp(build_id_s2 + 16, build_id_s3 + 16, 20) != 0) {
|
||||
print("limine-bios.sys build ID mismatch.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
stage3_loaded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum {
|
||||
BOOTED_FROM_HDD = 0,
|
||||
BOOTED_FROM_PXE = 1,
|
||||
BOOTED_FROM_CD = 2
|
||||
};
|
||||
|
||||
noreturn void entry(uint8_t boot_drive, int boot_from) {
|
||||
// XXX DO NOT MOVE A20 ENABLE CALL
|
||||
if (!a20_enable()) {
|
||||
panic(false, "Could not enable A20 line");
|
||||
}
|
||||
|
||||
init_e820();
|
||||
init_memmap();
|
||||
|
||||
init_idt();
|
||||
|
||||
disk_create_index();
|
||||
|
||||
if (boot_from == BOOTED_FROM_HDD || boot_from == BOOTED_FROM_CD) {
|
||||
boot_volume = volume_get_by_bios_drive(boot_drive);
|
||||
} else if (boot_from == BOOTED_FROM_PXE) {
|
||||
pxe_init();
|
||||
boot_volume = pxe_bind_volume();
|
||||
}
|
||||
|
||||
if (boot_volume == NULL) {
|
||||
panic(false, "Could not determine boot drive");
|
||||
}
|
||||
|
||||
volume_iterate_parts(boot_volume,
|
||||
if (stage3_init(_PART)) {
|
||||
break;
|
||||
}
|
||||
);
|
||||
|
||||
if (!stage3_found) {
|
||||
print("\n"
|
||||
"!! Stage 3 file not found!\n"
|
||||
"!! Have you copied limine-bios.sys to the root, /boot, /limine, or /boot/limine\n"
|
||||
"!! directories of one of the partitions on the boot device?\n\n");
|
||||
}
|
||||
|
||||
if (!stage3_loaded) {
|
||||
panic(false, "Failed to load stage 3.");
|
||||
}
|
||||
|
||||
term_fallback();
|
||||
|
||||
stage3_common();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <lib/term.h>
|
||||
#include <lib/real.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/trace.h>
|
||||
#include <sys/e820.h>
|
||||
#include <sys/a20.h>
|
||||
#include <sys/idt.h>
|
||||
#include <sys/gdt.h>
|
||||
#include <lib/print.h>
|
||||
#include <fs/file.h>
|
||||
#include <lib/elf.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <menu.h>
|
||||
#include <pxe/pxe.h>
|
||||
#include <pxe/tftp.h>
|
||||
#include <drivers/disk.h>
|
||||
#include <sys/lapic.h>
|
||||
#include <lib/readline.h>
|
||||
#include <sys/cpu.h>
|
||||
|
||||
void stage3_common(void);
|
||||
|
||||
#if defined (UEFI)
|
||||
extern symbol __slide;
|
||||
|
||||
noreturn void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
|
||||
gST = SystemTable;
|
||||
gBS = SystemTable->BootServices;
|
||||
gRT = SystemTable->RuntimeServices;
|
||||
efi_image_handle = ImageHandle;
|
||||
|
||||
EFI_STATUS status;
|
||||
|
||||
gST->ConOut->EnableCursor(gST->ConOut, false);
|
||||
|
||||
init_memmap();
|
||||
|
||||
term_fallback();
|
||||
|
||||
status = gBS->SetWatchdogTimer(0, 0x10000, 0, NULL);
|
||||
if (status) {
|
||||
print("WARNING: Failed to disable watchdog timer!\n");
|
||||
}
|
||||
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
init_gdt();
|
||||
#endif
|
||||
|
||||
#if defined (__x86_64__)
|
||||
if ((uintptr_t)__slide >= 0x100000000) {
|
||||
panic(false, "Limine does not support being loaded above 4GiB");
|
||||
}
|
||||
#endif
|
||||
|
||||
disk_create_index();
|
||||
|
||||
boot_volume = NULL;
|
||||
|
||||
EFI_HANDLE current_handle = ImageHandle;
|
||||
for (size_t j = 0; j < 25; j++) {
|
||||
if (current_handle == NULL) {
|
||||
could_not_match:
|
||||
print("WARNING: Could not meaningfully match the boot device handle with a volume.\n");
|
||||
print(" Using the first volume containing a Limine configuration!\n");
|
||||
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
struct file_handle *f;
|
||||
|
||||
bool old_cif = case_insensitive_fopen;
|
||||
case_insensitive_fopen = true;
|
||||
if ((f = fopen(volume_index[i], "/limine.cfg")) == NULL
|
||||
&& (f = fopen(volume_index[i], "/limine/limine.cfg")) == NULL
|
||||
&& (f = fopen(volume_index[i], "/boot/limine.cfg")) == NULL
|
||||
&& (f = fopen(volume_index[i], "/boot/limine/limine.cfg")) == NULL
|
||||
&& (f = fopen(volume_index[i], "/EFI/BOOT/limine.cfg")) == NULL) {
|
||||
case_insensitive_fopen = old_cif;
|
||||
continue;
|
||||
}
|
||||
case_insensitive_fopen = old_cif;
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (volume_index[i]->backing_dev != NULL) {
|
||||
boot_volume = volume_index[i]->backing_dev;
|
||||
} else {
|
||||
boot_volume = volume_index[i];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (boot_volume != NULL) {
|
||||
stage3_common();
|
||||
}
|
||||
|
||||
panic(false, "No volume contained a Limine configuration file");
|
||||
}
|
||||
|
||||
EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
|
||||
|
||||
status = gBS->HandleProtocol(current_handle, &loaded_img_prot_guid,
|
||||
(void **)&loaded_image);
|
||||
|
||||
if (status) {
|
||||
goto could_not_match;
|
||||
}
|
||||
|
||||
boot_volume = disk_volume_from_efi_handle(loaded_image->DeviceHandle);
|
||||
|
||||
if (boot_volume != NULL) {
|
||||
stage3_common();
|
||||
}
|
||||
|
||||
current_handle = loaded_image->ParentHandle;
|
||||
}
|
||||
|
||||
goto could_not_match;
|
||||
}
|
||||
#endif
|
||||
|
||||
noreturn void stage3_common(void) {
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
init_flush_irqs();
|
||||
init_io_apics();
|
||||
#endif
|
||||
|
||||
#if defined (__riscv)
|
||||
#if defined (UEFI)
|
||||
RISCV_EFI_BOOT_PROTOCOL *rv_proto = get_riscv_boot_protocol();
|
||||
if (rv_proto == NULL || rv_proto->GetBootHartId(rv_proto, &bsp_hartid) != EFI_SUCCESS) {
|
||||
panic(false, "failed to get BSP's hartid");
|
||||
}
|
||||
#else
|
||||
#error riscv: only UEFI is supported
|
||||
#endif
|
||||
init_riscv();
|
||||
#endif
|
||||
|
||||
term_notready();
|
||||
|
||||
menu(true);
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __FS__EXT2_H__
|
||||
#define __FS__EXT2_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <lib/part.h>
|
||||
#include <fs/file.h>
|
||||
|
||||
bool ext2_get_guid(struct guid *guid, struct volume *part);
|
||||
char *ext2_get_label(struct volume *part);
|
||||
|
||||
struct file_handle *ext2_open(struct volume *part, const char *path);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,685 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <fs/ext2.h>
|
||||
#include <drivers/disk.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/print.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
/* Superblock Fields */
|
||||
struct ext2_superblock {
|
||||
uint32_t s_inodes_count;
|
||||
uint32_t s_blocks_count;
|
||||
uint32_t s_r_blocks_count;
|
||||
uint32_t s_free_blocks_count;
|
||||
uint32_t s_free_inodes_count;
|
||||
uint32_t s_first_data_block;
|
||||
uint32_t s_log_block_size;
|
||||
uint32_t s_log_frag_size;
|
||||
uint32_t s_blocks_per_group;
|
||||
uint32_t s_frags_per_group;
|
||||
uint32_t s_inodes_per_group;
|
||||
uint32_t s_mtime;
|
||||
uint32_t s_wtime;
|
||||
|
||||
uint16_t s_mnt_count;
|
||||
uint16_t s_max_mnt_count;
|
||||
uint16_t s_magic;
|
||||
uint16_t s_state;
|
||||
uint16_t s_errors;
|
||||
uint16_t s_minor_rev_level;
|
||||
|
||||
uint32_t s_lastcheck;
|
||||
uint32_t s_checkinterval;
|
||||
uint32_t s_creator_os;
|
||||
uint32_t s_rev_level;
|
||||
uint16_t s_def_resuid;
|
||||
uint16_t s_def_gid;
|
||||
|
||||
// if version number >= 1, we have to use the ext2 extended superblock as well
|
||||
|
||||
/* Extended Superblock */
|
||||
uint32_t s_first_ino;
|
||||
|
||||
uint16_t s_inode_size;
|
||||
uint16_t s_block_group_nr;
|
||||
|
||||
uint32_t s_feature_compat;
|
||||
uint32_t s_feature_incompat;
|
||||
uint32_t s_feature_ro_compat;
|
||||
|
||||
uint64_t s_uuid[2];
|
||||
uint8_t s_volume_name[16];
|
||||
|
||||
uint64_t s_last_mounted[8];
|
||||
|
||||
uint32_t compression_info;
|
||||
uint8_t prealloc_blocks;
|
||||
uint8_t prealloc_dir_blocks;
|
||||
uint16_t reserved_gdt_blocks;
|
||||
uint8_t journal_uuid[16];
|
||||
uint32_t journal_inum;
|
||||
uint32_t journal_dev;
|
||||
uint32_t last_orphan;
|
||||
uint32_t hash_seed[4];
|
||||
uint8_t def_hash_version;
|
||||
uint8_t jnl_backup_type;
|
||||
uint16_t group_desc_size;
|
||||
uint32_t default_mount_opts;
|
||||
uint32_t first_meta_bg;
|
||||
uint32_t mkfs_time;
|
||||
uint32_t jnl_blocks[17];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext2_linux {
|
||||
uint8_t frag_num;
|
||||
uint8_t frag_size;
|
||||
|
||||
uint16_t reserved_16;
|
||||
uint16_t user_id_high;
|
||||
uint16_t group_id_high;
|
||||
|
||||
uint32_t reserved_32;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext2_inode {
|
||||
uint16_t i_mode;
|
||||
uint16_t i_uid;
|
||||
|
||||
uint32_t i_size;
|
||||
uint32_t i_atime;
|
||||
uint32_t i_ctime;
|
||||
uint32_t i_mtime;
|
||||
uint32_t i_dtime;
|
||||
|
||||
uint16_t i_gid;
|
||||
uint16_t i_links_count;
|
||||
|
||||
uint32_t i_blocks_count;
|
||||
uint32_t i_flags;
|
||||
uint32_t i_osd1;
|
||||
uint32_t i_blocks[15];
|
||||
uint32_t i_generation;
|
||||
|
||||
/* EXT2 v >= 1.0 */
|
||||
uint32_t i_eab;
|
||||
uint32_t i_maj;
|
||||
|
||||
/* EXT2 vAll */
|
||||
uint32_t i_frag_block;
|
||||
|
||||
struct ext2_linux i_osd2;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext2_file_handle {
|
||||
struct volume *part;
|
||||
struct ext2_superblock sb;
|
||||
int size;
|
||||
struct ext2_inode root_inode;
|
||||
struct ext2_inode inode;
|
||||
uint64_t block_size;
|
||||
uint32_t *alloc_map;
|
||||
};
|
||||
|
||||
/* Inode types */
|
||||
#define S_IFIFO 0x1000
|
||||
#define S_IFCHR 0x2000
|
||||
#define S_IFDIR 0x4000
|
||||
#define S_IFBLK 0x6000
|
||||
#define S_IFREG 0x8000
|
||||
#define S_IFLNK 0xa000
|
||||
#define S_IFSOCK 0xc000
|
||||
|
||||
#define FMT_MASK 0xf000
|
||||
|
||||
/* EXT2 Filesystem States */
|
||||
#define EXT2_FS_UNRECOVERABLE_ERRORS 3
|
||||
|
||||
/* Ext2 incompatible features */
|
||||
#define EXT2_IF_COMPRESSION 0x01
|
||||
#define EXT2_IF_EXTENTS 0x40
|
||||
#define EXT2_IF_64BIT 0x80
|
||||
#define EXT2_IF_INLINE_DATA 0x8000
|
||||
#define EXT2_IF_ENCRYPT 0x10000
|
||||
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
|
||||
|
||||
/* Ext4 flags */
|
||||
#define EXT4_EXTENTS_FLAG 0x80000
|
||||
|
||||
#define EXT2_S_MAGIC 0xEF53
|
||||
|
||||
/* EXT2 Block Group Descriptor */
|
||||
struct ext2_bgd {
|
||||
uint32_t bg_block_bitmap;
|
||||
uint32_t bg_inode_bitmap;
|
||||
uint32_t bg_inode_table;
|
||||
|
||||
uint16_t bg_free_blocks_count;
|
||||
uint16_t bg_free_inodes_count;
|
||||
uint16_t bg_dirs_count;
|
||||
|
||||
uint16_t reserved[7];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext4_bgd {
|
||||
uint32_t bg_block_bitmap;
|
||||
uint32_t bg_inode_bitmap;
|
||||
uint32_t bg_inode_table;
|
||||
|
||||
uint16_t bg_free_blocks_count;
|
||||
uint16_t bg_free_inodes_count;
|
||||
uint16_t bg_dirs_count;
|
||||
|
||||
uint16_t pad;
|
||||
uint32_t reserved[3];
|
||||
uint32_t block_id_hi;
|
||||
uint32_t inode_id_hi;
|
||||
uint32_t inode_table_id_hi;
|
||||
uint16_t free_blocks_hi;
|
||||
uint16_t free_inodes_hi;
|
||||
uint16_t used_dirs_hi;
|
||||
uint16_t pad2;
|
||||
uint32_t reserved2[3];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* EXT2 Inode Types */
|
||||
#define EXT2_INO_DIRECTORY 0x4000
|
||||
|
||||
/* EXT2 Directory Entry */
|
||||
struct ext2_dir_entry {
|
||||
uint32_t inode;
|
||||
uint16_t rec_len;
|
||||
uint8_t name_len;
|
||||
uint8_t type;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext4_extent_header {
|
||||
uint16_t magic;
|
||||
uint16_t entries;
|
||||
uint16_t max;
|
||||
uint16_t depth;
|
||||
uint16_t generation;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext4_extent {
|
||||
uint32_t block;
|
||||
uint16_t len;
|
||||
uint16_t start_hi;
|
||||
uint32_t start;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ext4_extent_idx {
|
||||
uint32_t block;
|
||||
uint32_t leaf;
|
||||
uint16_t leaf_hi;
|
||||
uint16_t empty;
|
||||
} __attribute__((packed));
|
||||
|
||||
static int inode_read(void *buf, uint64_t loc, uint64_t count,
|
||||
struct ext2_inode *inode, struct ext2_file_handle *fd,
|
||||
uint32_t *alloc_map);
|
||||
static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handle *fd, const char *path);
|
||||
|
||||
// parse an inode given the partition base and inode number
|
||||
static bool ext2_get_inode(struct ext2_inode *ret,
|
||||
struct ext2_file_handle *fd, uint64_t inode) {
|
||||
if (inode == 0)
|
||||
return false;
|
||||
|
||||
struct ext2_superblock *sb = &fd->sb;
|
||||
|
||||
//determine if we need to use 64 bit inode ids
|
||||
bool bit64 = false;
|
||||
if (sb->s_rev_level != 0
|
||||
&& (sb->s_feature_incompat & (EXT2_IF_64BIT))
|
||||
&& sb->group_desc_size != 0
|
||||
&& ((sb->group_desc_size & (sb->group_desc_size - 1)) == 0)) {
|
||||
if(sb->group_desc_size > 32) {
|
||||
bit64 = true;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t ino_blk_grp = (inode - 1) / sb->s_inodes_per_group;
|
||||
const uint64_t ino_tbl_idx = (inode - 1) % sb->s_inodes_per_group;
|
||||
|
||||
const uint64_t block_size = ((uint64_t)1024 << sb->s_log_block_size);
|
||||
uint64_t ino_offset;
|
||||
const uint64_t bgd_start_offset = block_size >= 2048 ? block_size : block_size * 2;
|
||||
const uint64_t ino_size = sb->s_rev_level == 0 ? sizeof(struct ext2_inode) : sb->s_inode_size;
|
||||
|
||||
if (!bit64) {
|
||||
struct ext2_bgd target_descriptor;
|
||||
const uint64_t bgd_offset = bgd_start_offset + (sizeof(struct ext2_bgd) * ino_blk_grp);
|
||||
|
||||
volume_read(fd->part, &target_descriptor, bgd_offset, sizeof(struct ext2_bgd));
|
||||
|
||||
ino_offset = ((target_descriptor.bg_inode_table) * block_size) +
|
||||
(ino_size * ino_tbl_idx);
|
||||
} else {
|
||||
struct ext4_bgd target_descriptor;
|
||||
const uint64_t bgd_offset = bgd_start_offset + (sizeof(struct ext4_bgd) * ino_blk_grp);
|
||||
|
||||
volume_read(fd->part, &target_descriptor, bgd_offset, sizeof(struct ext4_bgd));
|
||||
|
||||
ino_offset = ((target_descriptor.bg_inode_table | (bit64 ? ((uint64_t)target_descriptor.inode_id_hi << 32) : 0)) * block_size) +
|
||||
(ino_size * ino_tbl_idx);
|
||||
}
|
||||
|
||||
volume_read(fd->part, ret, ino_offset, sizeof(struct ext2_inode));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t *create_alloc_map(struct ext2_file_handle *fd,
|
||||
struct ext2_inode *inode) {
|
||||
if (inode->i_flags & EXT4_EXTENTS_FLAG)
|
||||
return NULL;
|
||||
|
||||
size_t entries_per_block = fd->block_size / sizeof(uint32_t);
|
||||
|
||||
// Cache the map of blocks
|
||||
uint32_t *alloc_map = ext_mem_alloc(inode->i_blocks_count * sizeof(uint32_t));
|
||||
for (uint32_t i = 0; i < inode->i_blocks_count; i++) {
|
||||
uint32_t block = i;
|
||||
if (block < 12) {
|
||||
// Direct block
|
||||
alloc_map[i] = inode->i_blocks[block];
|
||||
} else {
|
||||
// Indirect block
|
||||
block -= 12;
|
||||
if (block >= entries_per_block) {
|
||||
// Double indirect block
|
||||
block -= entries_per_block;
|
||||
uint32_t index = block / entries_per_block;
|
||||
uint32_t indirect_block;
|
||||
if (index >= entries_per_block) {
|
||||
uint32_t first_index = index / entries_per_block;
|
||||
uint32_t first_indirect_block;
|
||||
volume_read(
|
||||
fd->part, &first_indirect_block,
|
||||
inode->i_blocks[14] * fd->block_size + first_index * sizeof(uint32_t),
|
||||
sizeof(uint32_t)
|
||||
);
|
||||
uint32_t second_index = index % entries_per_block;
|
||||
volume_read(
|
||||
fd->part, &indirect_block,
|
||||
first_indirect_block * fd->block_size + second_index * sizeof(uint32_t),
|
||||
sizeof(uint32_t)
|
||||
);
|
||||
} else {
|
||||
volume_read(
|
||||
fd->part, &indirect_block,
|
||||
inode->i_blocks[13] * fd->block_size + index * sizeof(uint32_t),
|
||||
sizeof(uint32_t)
|
||||
);
|
||||
}
|
||||
for (uint32_t j = 0; j < entries_per_block; j++) {
|
||||
if (i + j >= inode->i_blocks_count)
|
||||
return alloc_map;
|
||||
volume_read(
|
||||
fd->part, &alloc_map[i + j],
|
||||
indirect_block * fd->block_size + j * sizeof(uint32_t),
|
||||
sizeof(uint32_t)
|
||||
);
|
||||
}
|
||||
i += entries_per_block - 1;
|
||||
} else {
|
||||
// Single indirect block
|
||||
volume_read(
|
||||
fd->part, &alloc_map[i],
|
||||
inode->i_blocks[12] * fd->block_size + block * sizeof(uint32_t),
|
||||
sizeof(uint32_t)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return alloc_map;
|
||||
}
|
||||
|
||||
static bool symlink_to_inode(struct ext2_inode *inode, struct ext2_file_handle *fd,
|
||||
const char *cwd, size_t cwd_len) {
|
||||
// I cannot find whether this is 0-terminated or not, so I'm gonna take the
|
||||
// safe route here and assume it is not.
|
||||
if (inode->i_size < 59) {
|
||||
struct ext2_dir_entry dir;
|
||||
char *symlink = (char *)inode->i_blocks;
|
||||
symlink[59] = 0;
|
||||
|
||||
char *abs = ext_mem_alloc(4096);
|
||||
char *cwd_copy = ext_mem_alloc(cwd_len + 1);
|
||||
memcpy(cwd_copy, cwd, cwd_len);
|
||||
get_absolute_path(abs, symlink, cwd_copy);
|
||||
|
||||
pmm_free(cwd_copy, cwd_len + 1);
|
||||
|
||||
if (!ext2_parse_dirent(&dir, fd, abs)) {
|
||||
pmm_free(abs, 4096);
|
||||
return false;
|
||||
}
|
||||
pmm_free(abs, 4096);
|
||||
|
||||
ext2_get_inode(inode, fd, dir.inode);
|
||||
return true;
|
||||
} else {
|
||||
print("ext2: Symlinks with destination paths longer than 60 chars unsupported\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ext2_parse_dirent(struct ext2_dir_entry *dir, struct ext2_file_handle *fd, const char *path) {
|
||||
if (*path != '/') {
|
||||
panic(true, "ext2: Path does not start in /");
|
||||
}
|
||||
|
||||
path++;
|
||||
|
||||
struct ext2_inode current_inode = fd->root_inode;
|
||||
|
||||
bool escape = false;
|
||||
static char token[256];
|
||||
|
||||
bool ret;
|
||||
|
||||
const char *cwd = path - 1; // because /
|
||||
size_t cwd_len = 1;
|
||||
size_t next_cwd_len = cwd_len;
|
||||
|
||||
next:
|
||||
memset(token, 0, 256);
|
||||
|
||||
for (size_t i = 0; i < 255 && *path != '/' && *path != '\0'; i++, path++, next_cwd_len++)
|
||||
token[i] = *path;
|
||||
|
||||
if (*path == '\0')
|
||||
escape = true;
|
||||
else
|
||||
path++, next_cwd_len++;
|
||||
|
||||
uint32_t *alloc_map = create_alloc_map(fd, ¤t_inode);
|
||||
|
||||
for (uint32_t i = 0; i < current_inode.i_size; ) {
|
||||
// preliminary read
|
||||
inode_read(dir, i, sizeof(struct ext2_dir_entry),
|
||||
¤t_inode, fd, alloc_map);
|
||||
|
||||
// name read
|
||||
char *name = ext_mem_alloc(dir->name_len + 1);
|
||||
|
||||
memset(name, 0, dir->name_len + 1);
|
||||
inode_read(name, i + sizeof(struct ext2_dir_entry), dir->name_len,
|
||||
¤t_inode, fd, alloc_map);
|
||||
|
||||
int (*strcmpfn)(const char *, const char *) = case_insensitive_fopen ? strcasecmp : strcmp;
|
||||
|
||||
int test = strcmpfn(token, name);
|
||||
pmm_free(name, dir->name_len + 1);
|
||||
|
||||
if (test == 0) {
|
||||
if (escape) {
|
||||
ret = true;
|
||||
goto out;
|
||||
} else {
|
||||
// update the current inode
|
||||
ext2_get_inode(¤t_inode, fd, dir->inode);
|
||||
while ((current_inode.i_mode & FMT_MASK) != S_IFDIR) {
|
||||
if ((current_inode.i_mode & FMT_MASK) == S_IFLNK) {
|
||||
if (!symlink_to_inode(¤t_inode, fd, cwd, cwd_len)) {
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
print("ext2: Part of path is not directory nor symlink\n");
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t));
|
||||
cwd_len = next_cwd_len;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
i += dir->rec_len;
|
||||
}
|
||||
|
||||
ret = false;
|
||||
|
||||
out:
|
||||
pmm_free(alloc_map, current_inode.i_blocks_count * sizeof(uint32_t));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ext2_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
|
||||
static void ext2_close(struct file_handle *file);
|
||||
|
||||
struct file_handle *ext2_open(struct volume *part, const char *path) {
|
||||
struct ext2_file_handle *ret = ext_mem_alloc(sizeof(struct ext2_file_handle));
|
||||
|
||||
ret->part = part;
|
||||
|
||||
volume_read(ret->part, &ret->sb, 1024, sizeof(struct ext2_superblock));
|
||||
|
||||
struct ext2_superblock *sb = &ret->sb;
|
||||
|
||||
if (sb->s_magic != EXT2_S_MAGIC) {
|
||||
pmm_free(ret, sizeof(struct ext2_file_handle));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sb->s_rev_level != 0 &&
|
||||
(sb->s_feature_incompat & EXT2_IF_COMPRESSION ||
|
||||
sb->s_feature_incompat & EXT2_IF_INLINE_DATA ||
|
||||
sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)) {
|
||||
print("ext2: filesystem has unsupported features %x\n", sb->s_feature_incompat);
|
||||
pmm_free(ret, sizeof(struct ext2_file_handle));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sb->s_rev_level != 0 && sb->s_feature_incompat & EXT2_IF_ENCRYPT) {
|
||||
print("ext2: WARNING: File system has encryption feature on, stuff may misbehave\n");
|
||||
}
|
||||
|
||||
if (sb->s_state == EXT2_FS_UNRECOVERABLE_ERRORS) {
|
||||
print("ext2: unrecoverable errors found\n");
|
||||
pmm_free(ret, sizeof(struct ext2_file_handle));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->block_size = ((uint64_t)1024 << ret->sb.s_log_block_size);
|
||||
|
||||
ext2_get_inode(&ret->root_inode, ret, 2);
|
||||
|
||||
struct ext2_dir_entry entry;
|
||||
|
||||
size_t cwd_len = 0;
|
||||
char *cwd = ext_mem_alloc(4096);
|
||||
for (int i = strlen(path) - 1; i > 0; i--) {
|
||||
if (path[i] == '/' || path[i] == 0) {
|
||||
cwd_len = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memcpy(cwd, path, cwd_len);
|
||||
|
||||
if (!ext2_parse_dirent(&entry, ret, path)) {
|
||||
pmm_free(cwd, 4096);
|
||||
pmm_free(ret, sizeof(struct ext2_file_handle));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ext2_get_inode(&ret->inode, ret, entry.inode);
|
||||
|
||||
while ((ret->inode.i_mode & FMT_MASK) != S_IFREG) {
|
||||
if ((ret->inode.i_mode & FMT_MASK) == S_IFLNK) {
|
||||
if (!symlink_to_inode(&ret->inode, ret, cwd, cwd_len)) {
|
||||
pmm_free(cwd, 4096);
|
||||
pmm_free(ret, sizeof(struct ext2_file_handle));
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
print("ext2: Entity is not regular file nor symlink\n");
|
||||
pmm_free(cwd, 4096);
|
||||
pmm_free(ret, sizeof(struct ext2_file_handle));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pmm_free(cwd, 4096);
|
||||
|
||||
ret->size = ret->inode.i_size;
|
||||
|
||||
ret->alloc_map = create_alloc_map(ret, &ret->inode);
|
||||
|
||||
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
|
||||
|
||||
handle->fd = ret;
|
||||
handle->read = (void *)ext2_read;
|
||||
handle->close = (void *)ext2_close;
|
||||
handle->size = ret->size;
|
||||
handle->vol = part;
|
||||
#if defined (UEFI)
|
||||
handle->efi_part_handle = part->efi_part_handle;
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void ext2_close(struct file_handle *file) {
|
||||
struct ext2_file_handle *f = file->fd;
|
||||
if (f->alloc_map != NULL) {
|
||||
pmm_free(f->alloc_map, f->inode.i_blocks_count * sizeof(uint32_t));
|
||||
}
|
||||
pmm_free(f, sizeof(struct ext2_file_handle));
|
||||
}
|
||||
|
||||
static void ext2_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
|
||||
struct ext2_file_handle *f = file->fd;
|
||||
inode_read(buf, loc, count, &f->inode, f, f->alloc_map);
|
||||
}
|
||||
|
||||
static struct ext4_extent_header *ext4_find_leaf(struct ext4_extent_header *ext_block, uint32_t read_block, uint64_t block_size, struct volume *part) {
|
||||
struct ext4_extent_idx *index;
|
||||
|
||||
void *buf = ext_mem_alloc(block_size);
|
||||
memcpy(buf, ext_block, block_size);
|
||||
ext_block = buf;
|
||||
|
||||
for (;;) {
|
||||
index = (struct ext4_extent_idx *)((size_t)ext_block + 12);
|
||||
|
||||
#define EXT4_EXT_MAGIC 0xf30a
|
||||
if (ext_block->magic != EXT4_EXT_MAGIC)
|
||||
panic(false, "invalid extent magic");
|
||||
|
||||
if (ext_block->depth == 0) {
|
||||
return ext_block;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ext_block->entries; i++) {
|
||||
if (read_block < index[i].block)
|
||||
break;
|
||||
}
|
||||
|
||||
if (--i < 0)
|
||||
panic(false, "extent not found");
|
||||
|
||||
uint64_t block = ((uint64_t)index[i].leaf_hi << 32) | index[i].leaf;
|
||||
|
||||
volume_read(part, buf, (block * block_size), block_size);
|
||||
ext_block = buf;
|
||||
}
|
||||
}
|
||||
|
||||
static int inode_read(void *buf, uint64_t loc, uint64_t count,
|
||||
struct ext2_inode *inode, struct ext2_file_handle *fd,
|
||||
uint32_t *alloc_map) {
|
||||
for (uint64_t progress = 0; progress < count;) {
|
||||
uint64_t block = (loc + progress) / fd->block_size;
|
||||
|
||||
uint64_t chunk = count - progress;
|
||||
uint64_t offset = (loc + progress) % fd->block_size;
|
||||
if (chunk > fd->block_size - offset)
|
||||
chunk = fd->block_size - offset;
|
||||
|
||||
uint32_t block_index;
|
||||
|
||||
if (inode->i_flags & EXT4_EXTENTS_FLAG) {
|
||||
struct ext4_extent_header *leaf;
|
||||
struct ext4_extent *ext;
|
||||
int i;
|
||||
|
||||
leaf = ext4_find_leaf((struct ext4_extent_header *)inode->i_blocks, block, fd->block_size, fd->part);
|
||||
|
||||
if (!leaf)
|
||||
panic(false, "invalid extent");
|
||||
ext = (struct ext4_extent*)((size_t)leaf + 12);
|
||||
|
||||
for (i = 0; i < leaf->entries; i++) {
|
||||
if (block < ext[i].block) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (--i >= 0) {
|
||||
block -= ext[i].block;
|
||||
if (block >= ext[i].len) {
|
||||
panic(false, "block longer than extent");
|
||||
} else {
|
||||
uint64_t start = ((uint64_t)ext[i].start_hi << 32) + ext[i].start;
|
||||
block_index = start + block;
|
||||
}
|
||||
} else {
|
||||
panic(false, "extent for block not found");
|
||||
}
|
||||
|
||||
pmm_free(leaf, fd->block_size);
|
||||
} else {
|
||||
block_index = alloc_map[block];
|
||||
}
|
||||
|
||||
volume_read(fd->part, buf + progress, (block_index * fd->block_size) + offset, chunk);
|
||||
|
||||
progress += chunk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ext2_get_guid(struct guid *guid, struct volume *part) {
|
||||
struct ext2_superblock sb;
|
||||
volume_read(part, &sb, 1024, sizeof(struct ext2_superblock));
|
||||
|
||||
if (sb.s_magic != EXT2_S_MAGIC)
|
||||
return false;
|
||||
|
||||
((uint64_t *)guid)[0] = sb.s_uuid[0];
|
||||
((uint64_t *)guid)[1] = sb.s_uuid[1];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char *ext2_get_label(struct volume *part) {
|
||||
struct ext2_superblock sb;
|
||||
volume_read(part, &sb, 1024, sizeof(struct ext2_superblock));
|
||||
|
||||
if (sb.s_magic != EXT2_S_MAGIC) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sb.s_rev_level < 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t label_len = strlen((char *)sb.s_volume_name);
|
||||
if (label_len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
char *ret = ext_mem_alloc(label_len + 1);
|
||||
strcpy(ret, (char *)sb.s_volume_name);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,528 @@
|
|||
#include <fs/fat32.h>
|
||||
#include <lib/misc.h>
|
||||
#include <drivers/disk.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/print.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define FAT32_LFN_MAX_ENTRIES 20
|
||||
#define FAT32_LFN_MAX_FILENAME_LENGTH (FAT32_LFN_MAX_ENTRIES * 13 + 1)
|
||||
|
||||
#define FAT32_ATTRIBUTE_SUBDIRECTORY 0x10
|
||||
#define FAT32_LFN_ATTRIBUTE 0x0F
|
||||
#define FAT32_ATTRIBUTE_VOLLABEL 0x08
|
||||
|
||||
struct fat32_context {
|
||||
struct volume *part;
|
||||
int type;
|
||||
char *label;
|
||||
uint16_t bytes_per_sector;
|
||||
uint8_t sectors_per_cluster;
|
||||
uint16_t reserved_sectors;
|
||||
uint8_t number_of_fats;
|
||||
uint32_t hidden_sectors;
|
||||
uint32_t sectors_per_fat;
|
||||
uint32_t fat_start_lba;
|
||||
uint32_t data_start_lba;
|
||||
uint32_t root_directory_cluster;
|
||||
uint16_t root_entries;
|
||||
uint32_t root_start;
|
||||
uint32_t root_size;
|
||||
};
|
||||
|
||||
struct fat32_file_handle {
|
||||
struct fat32_context context;
|
||||
uint32_t first_cluster;
|
||||
uint32_t size_bytes;
|
||||
uint32_t size_clusters;
|
||||
uint32_t *cluster_chain;
|
||||
size_t chain_len;
|
||||
};
|
||||
|
||||
struct fat32_bpb {
|
||||
union {
|
||||
struct {
|
||||
uint8_t jump[3];
|
||||
char oem[8];
|
||||
uint16_t bytes_per_sector;
|
||||
uint8_t sectors_per_cluster;
|
||||
uint16_t reserved_sectors;
|
||||
uint8_t fats_count;
|
||||
uint16_t root_entries_count;
|
||||
uint16_t sectors_count_16;
|
||||
uint8_t media_descriptor_type;
|
||||
uint16_t sectors_per_fat_16;
|
||||
uint16_t sectors_per_track;
|
||||
uint16_t heads_count;
|
||||
uint32_t hidden_sectors_count;
|
||||
uint32_t sectors_count_32;
|
||||
uint32_t sectors_per_fat_32;
|
||||
uint16_t flags;
|
||||
uint16_t fat_version_number;
|
||||
uint32_t root_directory_cluster;
|
||||
uint16_t fs_info_sector;
|
||||
uint16_t backup_boot_sector;
|
||||
uint8_t reserved[12];
|
||||
uint8_t drive_number;
|
||||
uint8_t nt_flags;
|
||||
uint8_t signature;
|
||||
uint32_t volume_serial_number;
|
||||
char label[11];
|
||||
char system_identifier[8];
|
||||
} __attribute__((packed));
|
||||
uint8_t padding[512];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct fat32_directory_entry {
|
||||
char file_name_and_ext[8 + 3];
|
||||
uint8_t attribute;
|
||||
uint8_t file_data_1[8];
|
||||
uint16_t cluster_num_high;
|
||||
uint8_t file_data_2[4];
|
||||
uint16_t cluster_num_low;
|
||||
uint32_t file_size_bytes;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct fat32_lfn_entry {
|
||||
uint8_t sequence_number;
|
||||
char name1[10];
|
||||
uint8_t attribute;
|
||||
uint8_t type;
|
||||
uint8_t dos_checksum;
|
||||
char name2[12];
|
||||
uint16_t first_cluster;
|
||||
char name3[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name);
|
||||
|
||||
static int fat32_init_context(struct fat32_context* context, struct volume *part) {
|
||||
context->part = part;
|
||||
|
||||
struct fat32_bpb bpb;
|
||||
volume_read(context->part, &bpb, 0, sizeof(struct fat32_bpb));
|
||||
|
||||
// Checks for FAT12/16
|
||||
if (strncmp((((void *)&bpb) + 0x36), "FAT", 3) == 0) {
|
||||
goto valid;
|
||||
}
|
||||
|
||||
// Checks for FAT32
|
||||
if (strncmp((((void *)&bpb) + 0x52), "FAT", 3) == 0) {
|
||||
goto valid;
|
||||
}
|
||||
|
||||
// Checks for FAT32 (with 64-bit sector count)
|
||||
if (strncmp((((void *)&bpb) + 0x03), "FAT32", 5) == 0) {
|
||||
goto valid;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
valid:;
|
||||
// The following mess to identify the FAT type is from the FAT spec
|
||||
// at paragraph 3.5
|
||||
size_t root_dir_sects = ((bpb.root_entries_count * 32) + (bpb.bytes_per_sector - 1)) / bpb.bytes_per_sector;
|
||||
|
||||
size_t data_sects = (bpb.sectors_count_16 ?: bpb.sectors_count_32) - (bpb.reserved_sectors + (bpb.fats_count * (bpb.sectors_per_fat_16 ?: bpb.sectors_per_fat_32)) + root_dir_sects);
|
||||
|
||||
size_t clusters_count = data_sects / bpb.sectors_per_cluster;
|
||||
|
||||
if (clusters_count < 4085) {
|
||||
context->type = 12;
|
||||
} else if (clusters_count < 65525) {
|
||||
context->type = 16;
|
||||
} else {
|
||||
context->type = 32;
|
||||
}
|
||||
|
||||
context->bytes_per_sector = bpb.bytes_per_sector;
|
||||
context->sectors_per_cluster = bpb.sectors_per_cluster;
|
||||
context->reserved_sectors = bpb.reserved_sectors;
|
||||
context->number_of_fats = bpb.fats_count;
|
||||
context->hidden_sectors = bpb.hidden_sectors_count;
|
||||
context->sectors_per_fat = context->type == 32 ? bpb.sectors_per_fat_32 : bpb.sectors_per_fat_16;
|
||||
context->root_directory_cluster = bpb.root_directory_cluster;
|
||||
context->fat_start_lba = bpb.reserved_sectors;
|
||||
context->root_entries = bpb.root_entries_count;
|
||||
context->root_start = context->reserved_sectors + context->number_of_fats * context->sectors_per_fat;
|
||||
context->root_size = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), context->bytes_per_sector);
|
||||
switch (context->type) {
|
||||
case 12:
|
||||
case 16:
|
||||
context->data_start_lba = context->root_start + context->root_size;
|
||||
break;
|
||||
case 32:
|
||||
context->data_start_lba = context->root_start;
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// get the volume label
|
||||
struct fat32_directory_entry _current_directory;
|
||||
struct fat32_directory_entry *current_directory;
|
||||
|
||||
switch (context->type) {
|
||||
case 12:
|
||||
case 16:
|
||||
current_directory = NULL;
|
||||
break;
|
||||
case 32:
|
||||
_current_directory.cluster_num_low = context->root_directory_cluster & 0xFFFF;
|
||||
_current_directory.cluster_num_high = context->root_directory_cluster >> 16;
|
||||
current_directory = &_current_directory;
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
char *vol_label;
|
||||
if (fat32_open_in(context, current_directory, (struct fat32_directory_entry *)&vol_label, NULL) == 0) {
|
||||
context->label = vol_label;
|
||||
} else {
|
||||
context->label = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cluster_from_map(struct fat32_context *context, uint32_t cluster, uint32_t *out) {
|
||||
switch (context->type) {
|
||||
case 12: {
|
||||
*out = 0;
|
||||
uint16_t tmp = 0;
|
||||
volume_read(context->part, &tmp, context->fat_start_lba * context->bytes_per_sector + (cluster + cluster / 2), sizeof(uint16_t));
|
||||
if (cluster % 2 == 0) {
|
||||
*out = tmp & 0xfff;
|
||||
} else {
|
||||
*out = tmp >> 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
*out = 0;
|
||||
volume_read(context->part, out, context->fat_start_lba * context->bytes_per_sector + cluster * sizeof(uint16_t), sizeof(uint16_t));
|
||||
break;
|
||||
case 32:
|
||||
volume_read(context->part, out, context->fat_start_lba * context->bytes_per_sector + cluster * sizeof(uint32_t), sizeof(uint32_t));
|
||||
*out &= 0x0fffffff;
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t *cache_cluster_chain(struct fat32_context *context,
|
||||
uint32_t initial_cluster,
|
||||
size_t *_chain_length) {
|
||||
uint32_t cluster_limit = (context->type == 12 ? 0xfef : 0)
|
||||
| (context->type == 16 ? 0xffef : 0)
|
||||
| (context->type == 32 ? 0xfffffef : 0);
|
||||
if (initial_cluster < 0x2 || initial_cluster > cluster_limit)
|
||||
return NULL;
|
||||
uint32_t cluster = initial_cluster;
|
||||
size_t chain_length;
|
||||
for (chain_length = 1; ; chain_length++) {
|
||||
read_cluster_from_map(context, cluster, &cluster);
|
||||
if (cluster < 0x2 || cluster > cluster_limit)
|
||||
break;
|
||||
}
|
||||
uint32_t *cluster_chain = ext_mem_alloc(chain_length * sizeof(uint32_t));
|
||||
cluster = initial_cluster;
|
||||
for (size_t i = 0; i < chain_length; i++) {
|
||||
cluster_chain[i] = cluster;
|
||||
read_cluster_from_map(context, cluster, &cluster);
|
||||
}
|
||||
*_chain_length = chain_length;
|
||||
return cluster_chain;
|
||||
}
|
||||
|
||||
static bool read_cluster_chain(struct fat32_context *context,
|
||||
uint32_t *cluster_chain,
|
||||
void *buf, uint64_t loc, uint64_t count) {
|
||||
size_t block_size = context->sectors_per_cluster * context->bytes_per_sector;
|
||||
for (uint64_t progress = 0; progress < count;) {
|
||||
uint64_t block = (loc + progress) / block_size;
|
||||
|
||||
uint64_t chunk = count - progress;
|
||||
uint64_t offset = (loc + progress) % block_size;
|
||||
if (chunk > block_size - offset)
|
||||
chunk = block_size - offset;
|
||||
|
||||
uint64_t base = ((uint64_t)context->data_start_lba + (cluster_chain[block] - 2) * context->sectors_per_cluster) * context->bytes_per_sector;
|
||||
volume_read(context->part, buf + progress, base + offset, chunk);
|
||||
|
||||
progress += chunk;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy ucs-2 characters to char*
|
||||
static void fat32_lfncpy(char* destination, const void* source, unsigned int size) {
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
// ignore high bytes
|
||||
*(((uint8_t*) destination) + i) = *(((uint8_t*) source) + (i * 2));
|
||||
}
|
||||
}
|
||||
|
||||
static bool fat32_filename_to_8_3(char *dest, const char *src) {
|
||||
int i = 0, j = 0;
|
||||
bool ext = false;
|
||||
|
||||
for (size_t k = 0; k < 8+3; k++)
|
||||
dest[k] = ' ';
|
||||
|
||||
while (src[i]) {
|
||||
if (src[i] == '.') {
|
||||
if (ext) {
|
||||
// This is a double extension here, just give up.
|
||||
return false;
|
||||
}
|
||||
ext = true;
|
||||
j = 8;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (j >= 8+3 || (j >= 8 && !ext)) {
|
||||
// Filename too long, give up.
|
||||
return false;
|
||||
}
|
||||
dest[j++] = toupper(src[i++]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name) {
|
||||
size_t block_size = context->sectors_per_cluster * context->bytes_per_sector;
|
||||
char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0};
|
||||
|
||||
size_t dir_chain_len;
|
||||
struct fat32_directory_entry *directory_entries;
|
||||
|
||||
if (directory != NULL) {
|
||||
uint32_t current_cluster_number = directory->cluster_num_low;
|
||||
if (context->type == 32)
|
||||
current_cluster_number |= (uint32_t)directory->cluster_num_high << 16;
|
||||
|
||||
uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len);
|
||||
|
||||
if (directory_cluster_chain == NULL)
|
||||
return -1;
|
||||
|
||||
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
|
||||
|
||||
read_cluster_chain(context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size);
|
||||
|
||||
pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
|
||||
} else {
|
||||
dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size);
|
||||
|
||||
directory_entries = ext_mem_alloc(dir_chain_len * block_size);
|
||||
|
||||
volume_read(context->part, directory_entries, context->root_start * context->bytes_per_sector, context->root_entries * sizeof(struct fat32_directory_entry));
|
||||
}
|
||||
|
||||
int ret;
|
||||
|
||||
for (size_t i = 0; i < (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry); i++) {
|
||||
if (directory_entries[i].file_name_and_ext[0] == 0x00) {
|
||||
// no more entries here
|
||||
break;
|
||||
}
|
||||
|
||||
if (name == NULL) {
|
||||
if (directory_entries[i].attribute != FAT32_ATTRIBUTE_VOLLABEL) {
|
||||
continue;
|
||||
}
|
||||
char *r = ext_mem_alloc(12);
|
||||
memcpy(r, directory_entries[i].file_name_and_ext, 11);
|
||||
// remove trailing spaces
|
||||
for (int j = 10; j >= 0; j--) {
|
||||
if (r[j] == ' ') {
|
||||
r[j] = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*((char **)file) = r;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (directory_entries[i].attribute == FAT32_LFN_ATTRIBUTE) {
|
||||
struct fat32_lfn_entry* lfn = (struct fat32_lfn_entry*) &directory_entries[i];
|
||||
|
||||
if (lfn->sequence_number & 0b01000000) {
|
||||
// this lfn is the first entry in the table, clear the lfn buffer
|
||||
memset(current_lfn, ' ', sizeof(current_lfn));
|
||||
}
|
||||
|
||||
const unsigned int lfn_index = ((lfn->sequence_number & 0b00011111) - 1U) * 13U;
|
||||
if (lfn_index >= FAT32_LFN_MAX_ENTRIES * 13) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fat32_lfncpy(current_lfn + lfn_index + 00, lfn->name1, 5);
|
||||
fat32_lfncpy(current_lfn + lfn_index + 05, lfn->name2, 6);
|
||||
fat32_lfncpy(current_lfn + lfn_index + 11, lfn->name3, 2);
|
||||
|
||||
if (lfn_index != 0)
|
||||
continue;
|
||||
|
||||
// remove trailing spaces
|
||||
for (int j = SIZEOF_ARRAY(current_lfn) - 2; j >= -1; j--) {
|
||||
if (j == -1 || current_lfn[j] != ' ') {
|
||||
current_lfn[j + 1] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int (*strcmpfn)(const char *, const char *) = case_insensitive_fopen ? strcasecmp : strcmp;
|
||||
|
||||
if (strcmpfn(current_lfn, name) == 0) {
|
||||
*file = directory_entries[i+1];
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (directory_entries[i].attribute & (1 << 3)) {
|
||||
// It is a volume label, skip
|
||||
continue;
|
||||
}
|
||||
// SFN
|
||||
char fn[8+3];
|
||||
if (!fat32_filename_to_8_3(fn, name)) {
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) {
|
||||
*file = directory_entries[i];
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
// file not found
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
pmm_free(directory_entries, dir_chain_len * block_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *fat32_get_label(struct volume *part) {
|
||||
struct fat32_context context;
|
||||
if (fat32_init_context(&context, part) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context.label;
|
||||
}
|
||||
|
||||
static void fat32_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
|
||||
static void fat32_close(struct file_handle *file);
|
||||
|
||||
struct file_handle *fat32_open(struct volume *part, const char *path) {
|
||||
struct fat32_context context;
|
||||
int r = fat32_init_context(&context, part);
|
||||
|
||||
if (r) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct fat32_directory_entry _current_directory;
|
||||
struct fat32_directory_entry *current_directory;
|
||||
struct fat32_directory_entry current_file;
|
||||
unsigned int current_index = 0;
|
||||
char current_part[FAT32_LFN_MAX_FILENAME_LENGTH];
|
||||
|
||||
// skip trailing slashes
|
||||
while (path[current_index] == '/') {
|
||||
current_index++;
|
||||
}
|
||||
|
||||
// walk down the directory tree
|
||||
switch (context.type) {
|
||||
case 12:
|
||||
case 16:
|
||||
current_directory = NULL;
|
||||
break;
|
||||
case 32:
|
||||
_current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF;
|
||||
_current_directory.cluster_num_high = context.root_directory_cluster >> 16;
|
||||
current_directory = &_current_directory;
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
bool expect_directory = false;
|
||||
|
||||
for (unsigned int i = 0; i < SIZEOF_ARRAY(current_part); i++) {
|
||||
if (path[i + current_index] == 0) {
|
||||
memcpy(current_part, path + current_index, i);
|
||||
current_part[i] = 0;
|
||||
expect_directory = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (path[i + current_index] == '/') {
|
||||
memcpy(current_part, path + current_index, i);
|
||||
current_part[i] = 0;
|
||||
current_index += i + 1;
|
||||
expect_directory = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((r = fat32_open_in(&context, current_directory, ¤t_file, current_part)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (expect_directory) {
|
||||
_current_directory = current_file;
|
||||
current_directory = &_current_directory;
|
||||
} else {
|
||||
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
|
||||
struct fat32_file_handle *ret = ext_mem_alloc(sizeof(struct fat32_file_handle));
|
||||
|
||||
ret->context = context;
|
||||
ret->first_cluster = current_file.cluster_num_low;
|
||||
if (context.type == 32)
|
||||
ret->first_cluster |= (uint64_t)current_file.cluster_num_high << 16;
|
||||
ret->size_clusters = DIV_ROUNDUP(current_file.file_size_bytes, context.bytes_per_sector);
|
||||
ret->size_bytes = current_file.file_size_bytes;
|
||||
ret->cluster_chain = cache_cluster_chain(&context, ret->first_cluster, &ret->chain_len);
|
||||
|
||||
handle->fd = (void *)ret;
|
||||
handle->read = (void *)fat32_read;
|
||||
handle->close = (void *)fat32_close;
|
||||
handle->size = ret->size_bytes;
|
||||
handle->vol = part;
|
||||
#if defined (UEFI)
|
||||
handle->efi_part_handle = part->efi_part_handle;
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fat32_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
|
||||
struct fat32_file_handle *f = file->fd;
|
||||
read_cluster_chain(&f->context, f->cluster_chain, buf, loc, count);
|
||||
}
|
||||
|
||||
static void fat32_close(struct file_handle *file) {
|
||||
struct fat32_file_handle *f = file->fd;
|
||||
pmm_free(f->cluster_chain, f->chain_len * sizeof(uint32_t));
|
||||
pmm_free(f, sizeof(struct fat32_file_handle));
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef __FS__FILE_H__
|
||||
#define __FS__FILE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/part.h>
|
||||
#if defined (UEFI)
|
||||
# include <efi.h>
|
||||
#endif
|
||||
|
||||
extern bool case_insensitive_fopen;
|
||||
|
||||
bool fs_get_guid(struct guid *guid, struct volume *part);
|
||||
char *fs_get_label(struct volume *part);
|
||||
|
||||
struct file_handle {
|
||||
bool is_memfile;
|
||||
bool readall;
|
||||
struct volume *vol;
|
||||
char *path;
|
||||
size_t path_len;
|
||||
void *fd;
|
||||
void (*read)(void *fd, void *buf, uint64_t loc, uint64_t count);
|
||||
void (*close)(void *fd);
|
||||
uint64_t size;
|
||||
#if defined (UEFI)
|
||||
EFI_HANDLE efi_part_handle;
|
||||
#endif
|
||||
bool pxe;
|
||||
uint32_t pxe_ip;
|
||||
uint16_t pxe_port;
|
||||
};
|
||||
|
||||
struct file_handle *fopen(struct volume *part, const char *filename);
|
||||
void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count);
|
||||
void fclose(struct file_handle *fd);
|
||||
void *freadall(struct file_handle *fd, uint32_t type);
|
||||
void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,119 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/ext2.h>
|
||||
#include <fs/fat32.h>
|
||||
#include <fs/iso9660.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/misc.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/libc.h>
|
||||
#include <pxe/tftp.h>
|
||||
|
||||
char *fs_get_label(struct volume *part) {
|
||||
char *ret;
|
||||
|
||||
if ((ret = fat32_get_label(part)) != NULL) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = ext2_get_label(part)) != NULL) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool fs_get_guid(struct guid *guid, struct volume *part) {
|
||||
if (ext2_get_guid(guid, part) == true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool case_insensitive_fopen = false;
|
||||
|
||||
struct file_handle *fopen(struct volume *part, const char *filename) {
|
||||
size_t filename_new_len = strlen(filename) + 2;
|
||||
char *filename_new = ext_mem_alloc(filename_new_len);
|
||||
|
||||
if (filename[0] != '/') {
|
||||
filename_new[0] = '/';
|
||||
strcpy(&filename_new[1], filename);
|
||||
} else {
|
||||
strcpy(filename_new, filename);
|
||||
}
|
||||
|
||||
filename = filename_new;
|
||||
|
||||
struct file_handle *ret;
|
||||
|
||||
if (part->pxe) {
|
||||
if ((ret = tftp_open(part, "", filename)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = ext2_open(part, filename)) != NULL) {
|
||||
goto success;
|
||||
}
|
||||
if ((ret = iso9660_open(part, filename)) != NULL) {
|
||||
goto success;
|
||||
}
|
||||
if ((ret = fat32_open(part, filename)) != NULL) {
|
||||
goto success;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
success:
|
||||
ret->path = (char *)filename;
|
||||
ret->path_len = filename_new_len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fclose(struct file_handle *fd) {
|
||||
if (fd->is_memfile) {
|
||||
if (fd->readall == false) {
|
||||
pmm_free(fd->fd, fd->size);
|
||||
}
|
||||
} else {
|
||||
fd->close(fd);
|
||||
}
|
||||
pmm_free(fd->path, fd->path_len);
|
||||
pmm_free(fd, sizeof(struct file_handle));
|
||||
}
|
||||
|
||||
void fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count) {
|
||||
if (fd->is_memfile) {
|
||||
memcpy(buf, fd->fd + loc, count);
|
||||
} else {
|
||||
fd->read(fd, buf, loc, count);
|
||||
}
|
||||
}
|
||||
|
||||
void *freadall(struct file_handle *fd, uint32_t type) {
|
||||
return freadall_mode(fd, type, false);
|
||||
}
|
||||
|
||||
void *freadall_mode(struct file_handle *fd, uint32_t type, bool allow_high_allocs) {
|
||||
if (fd->is_memfile) {
|
||||
if (fd->readall) {
|
||||
return fd->fd;
|
||||
}
|
||||
memmap_alloc_range((uint64_t)(size_t)fd->fd, ALIGN_UP(fd->size, 4096), type, 0, true, false, false);
|
||||
fd->readall = true;
|
||||
return fd->fd;
|
||||
} else {
|
||||
void *ret = ext_mem_alloc_type_aligned_mode(fd->size, type, 4096, allow_high_allocs);
|
||||
fd->read(fd, ret, 0, fd->size);
|
||||
fd->close(fd);
|
||||
fd->fd = ret;
|
||||
fd->readall = true;
|
||||
fd->is_memfile = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,297 @@
|
|||
#include <fs/iso9660.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/libc.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
#define ISO9660_SECTOR_SIZE (2 << 10)
|
||||
|
||||
struct iso9660_context {
|
||||
struct volume *vol;
|
||||
void *root;
|
||||
uint32_t root_size;
|
||||
};
|
||||
|
||||
struct iso9660_file_handle {
|
||||
struct iso9660_context *context;
|
||||
uint32_t LBA;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
#define ISO9660_FIRST_VOLUME_DESCRIPTOR 0x10
|
||||
#define ISO9660_VOLUME_DESCRIPTOR_SIZE ISO9660_SECTOR_SIZE
|
||||
#define ROCK_RIDGE_MAX_FILENAME 255
|
||||
|
||||
// --- Both endian structures ---
|
||||
struct BE16_t { uint16_t little, big; } __attribute__((packed));
|
||||
struct BE32_t { uint32_t little, big; } __attribute__((packed));
|
||||
|
||||
// --- Directory entries ---
|
||||
struct iso9660_directory_entry {
|
||||
uint8_t length;
|
||||
uint8_t extended_attribute_length;
|
||||
struct BE32_t extent;
|
||||
struct BE32_t extent_size;
|
||||
uint8_t datetime[7];
|
||||
uint8_t flags;
|
||||
uint8_t interleaved_unit_size;
|
||||
uint8_t interleaved_gap_size;
|
||||
struct BE16_t volume_seq;
|
||||
uint8_t filename_size;
|
||||
char name[];
|
||||
} __attribute__((packed));
|
||||
|
||||
// --- Volume descriptors ---
|
||||
// VDT = Volume Descriptor Type
|
||||
enum {
|
||||
ISO9660_VDT_BOOT_RECORD,
|
||||
ISO9660_VDT_PRIMARY,
|
||||
ISO9660_VDT_SUPPLEMENTARY,
|
||||
ISO9660_VDT_PARTITION_DESCRIPTOR,
|
||||
ISO9660_VDT_TERMINATOR = 255
|
||||
};
|
||||
|
||||
struct iso9660_volume_descriptor {
|
||||
uint8_t type;
|
||||
char identifier[5];
|
||||
uint8_t version;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct iso9660_primary_volume {
|
||||
struct iso9660_volume_descriptor volume_descriptor;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint8_t unused0[1];
|
||||
char system_identifier[32];
|
||||
char volume_identifier[32];
|
||||
uint8_t unused1[8];
|
||||
struct BE32_t space_size;
|
||||
uint8_t unused2[32];
|
||||
struct BE16_t set_size;
|
||||
struct BE16_t volume_seq;
|
||||
struct BE16_t LBA_size;
|
||||
struct BE32_t path_table_size;
|
||||
|
||||
uint32_t LBA_path_table_little;
|
||||
uint32_t LBA_optional_path_table_little;
|
||||
uint32_t LBA_path_table_big;
|
||||
uint32_t LBA_optional_path_table_big;
|
||||
|
||||
struct iso9660_directory_entry root;
|
||||
} __attribute__((packed));
|
||||
|
||||
uint8_t padding[2041];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
// --- Implementation ---
|
||||
struct iso9660_contexts_node {
|
||||
struct iso9660_context context;
|
||||
struct iso9660_contexts_node *next;
|
||||
};
|
||||
|
||||
static struct iso9660_contexts_node *contexts = NULL;
|
||||
|
||||
static void iso9660_find_PVD(struct iso9660_volume_descriptor *desc, struct volume *vol) {
|
||||
uint32_t lba = ISO9660_FIRST_VOLUME_DESCRIPTOR;
|
||||
while (true) {
|
||||
volume_read(vol, desc, lba * ISO9660_SECTOR_SIZE, ISO9660_SECTOR_SIZE);
|
||||
|
||||
switch (desc->type) {
|
||||
case ISO9660_VDT_PRIMARY:
|
||||
return;
|
||||
case ISO9660_VDT_TERMINATOR:
|
||||
panic(false, "ISO9660: no primary volume descriptor");
|
||||
break;
|
||||
}
|
||||
|
||||
++lba;
|
||||
}
|
||||
}
|
||||
|
||||
static void iso9660_cache_root(struct volume *vol,
|
||||
void **root,
|
||||
uint32_t *root_size) {
|
||||
struct iso9660_primary_volume pv;
|
||||
iso9660_find_PVD((struct iso9660_volume_descriptor *)&pv, vol);
|
||||
|
||||
*root_size = pv.root.extent_size.little;
|
||||
*root = ext_mem_alloc(*root_size);
|
||||
volume_read(vol, *root, pv.root.extent.little * ISO9660_SECTOR_SIZE, *root_size);
|
||||
}
|
||||
|
||||
static struct iso9660_context *iso9660_get_context(struct volume *vol) {
|
||||
struct iso9660_contexts_node *current = contexts;
|
||||
while (current) {
|
||||
if (current->context.vol == vol)
|
||||
return ¤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, struct iso9660_directory_entry *entry) {
|
||||
unsigned char* sysarea = ((unsigned char*)entry) + sizeof(struct iso9660_directory_entry) + entry->filename_size;
|
||||
int sysarea_len = entry->length - sizeof(struct iso9660_directory_entry) - entry->filename_size;
|
||||
if ((entry->filename_size & 0x1) == 0) {
|
||||
sysarea++;
|
||||
sysarea_len--;
|
||||
}
|
||||
|
||||
int rrnamelen = 0;
|
||||
while ((sysarea_len >= 4) && ((sysarea[3] == 1) || (sysarea[2] == 2))) {
|
||||
if (sysarea[0] == 'N' && sysarea[1] == 'M') {
|
||||
rrnamelen = sysarea[2] - 5;
|
||||
break;
|
||||
}
|
||||
sysarea_len -= sysarea[2];
|
||||
sysarea += sysarea[2];
|
||||
}
|
||||
|
||||
size_t name_len = 0;
|
||||
if (rrnamelen) {
|
||||
/* rock ridge naming scheme */
|
||||
name_len = rrnamelen;
|
||||
memcpy(buf, sysarea + 5, name_len);
|
||||
buf[name_len] = 0;
|
||||
return true;
|
||||
} else {
|
||||
name_len = entry->filename_size;
|
||||
size_t j;
|
||||
for (j = 0; j < name_len; j++) {
|
||||
if (entry->name[j] == ';')
|
||||
break;
|
||||
if (entry->name[j] == '.' && entry->name[j+1] == ';')
|
||||
break;
|
||||
buf[j] = entry->name[j];
|
||||
}
|
||||
buf[j] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct iso9660_directory_entry *iso9660_find(void *buffer, uint32_t size, const char *filename) {
|
||||
while (size) {
|
||||
struct iso9660_directory_entry *entry = buffer;
|
||||
|
||||
if (entry->length == 0) {
|
||||
if (size <= ISO9660_SECTOR_SIZE)
|
||||
return NULL;
|
||||
size_t prev_size = size;
|
||||
size = ALIGN_DOWN(size, ISO9660_SECTOR_SIZE);
|
||||
buffer += prev_size - size;
|
||||
continue;
|
||||
}
|
||||
|
||||
char entry_filename[128];
|
||||
bool rr = load_name(entry_filename, entry);
|
||||
|
||||
if (rr && !case_insensitive_fopen) {
|
||||
if (strcmp(filename, entry_filename) == 0) {
|
||||
return buffer;
|
||||
}
|
||||
} else {
|
||||
if (strcasecmp(filename, entry_filename) == 0) {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
size -= entry->length;
|
||||
buffer += entry->length;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void iso9660_read(struct file_handle *handle, void *buf, uint64_t loc, uint64_t count);
|
||||
static void iso9660_close(struct file_handle *file);
|
||||
|
||||
struct file_handle *iso9660_open(struct volume *vol, const char *path) {
|
||||
char buf[6];
|
||||
const uint64_t signature = ISO9660_FIRST_VOLUME_DESCRIPTOR * ISO9660_SECTOR_SIZE + 1;
|
||||
volume_read(vol, buf, signature, 5);
|
||||
buf[5] = '\0';
|
||||
if (strcmp(buf, "CD001") != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct iso9660_file_handle *ret = ext_mem_alloc(sizeof(struct iso9660_file_handle));
|
||||
|
||||
ret->context = iso9660_get_context(vol);
|
||||
|
||||
while (*path == '/')
|
||||
++path;
|
||||
|
||||
struct iso9660_directory_entry *current = ret->context->root;
|
||||
uint32_t current_size = ret->context->root_size;
|
||||
|
||||
bool first = true;
|
||||
|
||||
uint32_t next_sector = 0;
|
||||
uint32_t next_size = 0;
|
||||
|
||||
char filename[ROCK_RIDGE_MAX_FILENAME];
|
||||
while (true) {
|
||||
char *aux = filename;
|
||||
while (!(*path == '/' || *path == '\0'))
|
||||
*aux++ = *path++;
|
||||
*aux = '\0';
|
||||
|
||||
struct iso9660_directory_entry *entry = iso9660_find(current, current_size, filename);
|
||||
if (!entry) {
|
||||
pmm_free(ret, sizeof(struct iso9660_file_handle));
|
||||
return NULL; // Not found :(
|
||||
}
|
||||
|
||||
next_sector = entry->extent.little;
|
||||
next_size = entry->extent_size.little;
|
||||
|
||||
if (*path++ == '\0')
|
||||
break; // Found :)
|
||||
|
||||
if (!first) {
|
||||
pmm_free(current, current_size);
|
||||
}
|
||||
|
||||
current_size = next_size;
|
||||
current = ext_mem_alloc(current_size);
|
||||
|
||||
first = false;
|
||||
|
||||
volume_read(vol, current, next_sector * ISO9660_SECTOR_SIZE, current_size);
|
||||
}
|
||||
|
||||
ret->LBA = next_sector;
|
||||
ret->size = next_size;
|
||||
|
||||
struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle));
|
||||
|
||||
handle->fd = ret;
|
||||
handle->read = (void *)iso9660_read;
|
||||
handle->close = (void *)iso9660_close;
|
||||
handle->size = ret->size;
|
||||
handle->vol = vol;
|
||||
#if defined (UEFI)
|
||||
handle->efi_part_handle = vol->efi_part_handle;
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void iso9660_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
|
||||
struct iso9660_file_handle *f = file->fd;
|
||||
volume_read(f->context->vol, buf, f->LBA * ISO9660_SECTOR_SIZE + loc, count);
|
||||
}
|
||||
|
||||
static void iso9660_close(struct file_handle *file) {
|
||||
pmm_free(file->fd, sizeof(struct iso9660_file_handle));
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
|
||||
TMP0="$(mktemp)"
|
||||
|
||||
cat >"$TMP0" <<EOF
|
||||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
set -o pipefail 2>/dev/null
|
||||
EOF
|
||||
|
||||
chmod +x "$TMP0"
|
||||
|
||||
"$TMP0" && set -o pipefail
|
||||
|
||||
rm "$TMP0"
|
||||
|
||||
TMP1="$(mktemp)"
|
||||
TMP2="$(mktemp)"
|
||||
TMP3="$(mktemp)"
|
||||
TMP4="$(mktemp)"
|
||||
|
||||
trap "rm -f '$TMP1' '$TMP2' '$TMP3' '$TMP4'; trap - EXIT; exit" EXIT INT TERM QUIT HUP
|
||||
|
||||
"$OBJDUMP_FOR_TARGET" -t "$1" | ( "$SED" '/[[:<:]]d[[:>:]]/d' 2>/dev/null || "$SED" '/\bd\b/d' ) | sort > "$TMP1"
|
||||
"$GREP" "F $4" < "$TMP1" | cut -d' ' -f1 > "$TMP2"
|
||||
"$GREP" "F $4" < "$TMP1" | "$AWK" 'NF{ print $NF }' > "$TMP3"
|
||||
|
||||
echo ".section .$2_map" > "$TMP4"
|
||||
echo ".globl $2_map" >> "$TMP4"
|
||||
echo "$2_map:" >> "$TMP4"
|
||||
|
||||
if [ "$3" = "32" ]; then
|
||||
paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.long 0x/g;s/$/"/g;s/#/\
|
||||
.asciz "/g' >> "$TMP4"
|
||||
echo ".long 0xffffffff" >> "$TMP4"
|
||||
elif [ "$3" = "64" ]; then
|
||||
paste -d'#' "$TMP2" "$TMP3" | "$SED" 's/^/.quad 0x/g;s/$/"/g;s/#/\
|
||||
.asciz "/g' >> "$TMP4"
|
||||
echo ".quad 0xffffffffffffffff" >> "$TMP4"
|
||||
fi
|
||||
|
||||
echo '.section .note.GNU-stack,"",%progbits' >> "$TMP4"
|
||||
|
||||
mv "$TMP4" "$2.map.S"
|
|
@ -0,0 +1,240 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/acpi.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/print.h>
|
||||
|
||||
// Following function based on https://github.com/managarm/lai/blob/master/helpers/pc-bios.c's function lai_bios_calc_checksum()
|
||||
uint8_t acpi_checksum(void *ptr, size_t size) {
|
||||
uint8_t sum = 0, *_ptr = ptr;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
sum += _ptr[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
#if defined (BIOS)
|
||||
|
||||
void *acpi_get_rsdp(void) {
|
||||
size_t ebda = EBDA;
|
||||
|
||||
for (size_t i = ebda; i < 0x100000; i += 16) {
|
||||
if (i == ebda + 1024) {
|
||||
// We probed the 1st KiB of the EBDA as per spec, move onto 0xe0000
|
||||
i = 0xe0000;
|
||||
}
|
||||
if (!memcmp((char *)i, "RSD PTR ", 8)
|
||||
&& !acpi_checksum((void *)i, 20)) {
|
||||
printv("acpi: Found RSDP at %p\n", i);
|
||||
return (void *)i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Returns the RSDP v1 pointer if avaliable or else NULL.
|
||||
void *acpi_get_rsdp_v1(void) {
|
||||
// In BIOS according to the ACPI spec (see ACPI 6.2 section
|
||||
// 5.2.5.1 'Finding the RSDP on IA-PC Systems') it either contains
|
||||
// the RSDP or the XSDP and it cannot contain both. So, we directly
|
||||
// use acpi_get_rsdp function to find the RSDP and if it has the correct
|
||||
// revision, return it.
|
||||
struct rsdp *rsdp = acpi_get_rsdp();
|
||||
|
||||
if (rsdp != NULL && rsdp->rev < 2)
|
||||
return rsdp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void acpi_get_smbios(void **smbios32, void **smbios64) {
|
||||
*smbios32 = NULL;
|
||||
*smbios64 = NULL;
|
||||
|
||||
for (size_t i = 0xf0000; i < 0x100000; i += 16) {
|
||||
struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)i;
|
||||
|
||||
if (!memcmp(ptr->anchor_str, "_SM_", 4) &&
|
||||
!acpi_checksum((void *)ptr, ptr->length)) {
|
||||
printv("acpi: Found SMBIOS 32-bit entry point at %p\n", i);
|
||||
*smbios32 = (void *)ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0xf0000; i < 0x100000; i += 16) {
|
||||
struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)i;
|
||||
|
||||
if (!memcmp(ptr->anchor_str, "_SM3_", 5) &&
|
||||
!acpi_checksum((void *)ptr, ptr->length)) {
|
||||
printv("acpi: Found SMBIOS 64-bit entry point at %p\n", i);
|
||||
*smbios64 = (void *)ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (UEFI)
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
void *acpi_get_rsdp(void) {
|
||||
EFI_GUID acpi_2_guid = ACPI_20_TABLE_GUID;
|
||||
EFI_GUID acpi_1_guid = ACPI_TABLE_GUID;
|
||||
|
||||
void *rsdp = NULL;
|
||||
|
||||
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
|
||||
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
|
||||
|
||||
bool is_xsdp = memcmp(&cur_table->VendorGuid, &acpi_2_guid, sizeof(EFI_GUID)) == 0;
|
||||
bool is_rsdp = memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) == 0;
|
||||
|
||||
if (!is_xsdp && !is_rsdp)
|
||||
continue;
|
||||
|
||||
if ((is_xsdp && acpi_checksum(cur_table->VendorTable, sizeof(struct rsdp)) != 0) || // XSDP is 36 bytes wide
|
||||
(is_rsdp && acpi_checksum(cur_table->VendorTable, 20) != 0)) // RSDP is 20 bytes wide
|
||||
continue;
|
||||
|
||||
printv("acpi: Found %s at %p\n", is_xsdp ? "XSDP" : "RSDP", cur_table->VendorTable);
|
||||
|
||||
// We want to return the XSDP if it exists rather then returning
|
||||
// the RSDP. We need to add a check for that since the table entries
|
||||
// are not in the same order for all EFI systems since it might be the
|
||||
// case where the RSDP ocurs before the XSDP.
|
||||
if (is_xsdp) {
|
||||
rsdp = (void *)cur_table->VendorTable;
|
||||
break; // Found it!.
|
||||
} else {
|
||||
// Found the RSDP but we continue to loop since we might
|
||||
// find the XSDP.
|
||||
rsdp = (void *)cur_table->VendorTable;
|
||||
}
|
||||
}
|
||||
|
||||
return rsdp;
|
||||
}
|
||||
|
||||
/// Returns the RSDP v1 pointer if avaliable or else NULL.
|
||||
void *acpi_get_rsdp_v1(void) {
|
||||
// To maintain GRUB compatibility we will need to probe for the RSDP
|
||||
// again since UEFI can contain both XSDP and RSDP (see ACPI 6.2 section
|
||||
// 5.2.5.2 'Finding the RSDP on UEFI Enabled Systems') and in the acpi_get_rsdp
|
||||
// function we look for the RSDP with the latest revision.
|
||||
EFI_GUID acpi_1_guid = ACPI_TABLE_GUID;
|
||||
|
||||
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
|
||||
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
|
||||
|
||||
if (memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) != 0)
|
||||
continue;
|
||||
|
||||
if (acpi_checksum(cur_table->VendorTable, 20) != 0)
|
||||
continue;
|
||||
|
||||
return (void *)cur_table->VendorTable;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void acpi_get_smbios(void **smbios32, void **smbios64) {
|
||||
*smbios32 = NULL;
|
||||
*smbios64 = NULL;
|
||||
|
||||
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
|
||||
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
|
||||
EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
|
||||
|
||||
if (memcmp(&cur_table->VendorGuid, &smbios_guid, sizeof(EFI_GUID)) != 0)
|
||||
continue;
|
||||
|
||||
struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)cur_table->VendorTable;
|
||||
|
||||
if (acpi_checksum((void *)ptr, ptr->length) != 0)
|
||||
continue;
|
||||
|
||||
printv("acpi: Found SMBIOS 32-bit entry point at %X\n", ptr);
|
||||
|
||||
*smbios32 = (void *)ptr;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
|
||||
EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
|
||||
EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
|
||||
|
||||
if (memcmp(&cur_table->VendorGuid, &smbios3_guid, sizeof(EFI_GUID)) != 0)
|
||||
continue;
|
||||
|
||||
struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)cur_table->VendorTable;
|
||||
|
||||
if (acpi_checksum((void *)ptr, ptr->length) != 0)
|
||||
continue;
|
||||
|
||||
printv("acpi: Found SMBIOS 64-bit entry point at %X\n", ptr);
|
||||
|
||||
*smbios64 = (void *)ptr;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// Returns the RSDP v2 pointer if avaliable or else NULL.
|
||||
void *acpi_get_rsdp_v2(void) {
|
||||
// Since the acpi_get_rsdp function already looks for the XSDP we can
|
||||
// just check if it has the correct revision and return the pointer :^)
|
||||
struct rsdp *rsdp = acpi_get_rsdp();
|
||||
|
||||
if (rsdp != NULL && rsdp->rev >= 2)
|
||||
return rsdp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *acpi_get_table(const char *signature, int index) {
|
||||
int cnt = 0;
|
||||
|
||||
struct rsdp *rsdp = acpi_get_rsdp();
|
||||
if (rsdp == NULL)
|
||||
return NULL;
|
||||
|
||||
bool use_xsdt = false;
|
||||
if (rsdp->rev >= 2 && rsdp->xsdt_addr)
|
||||
use_xsdt = true;
|
||||
|
||||
struct rsdt *rsdt;
|
||||
if (use_xsdt)
|
||||
rsdt = (struct rsdt *)(uintptr_t)rsdp->xsdt_addr;
|
||||
else
|
||||
rsdt = (struct rsdt *)(uintptr_t)rsdp->rsdt_addr;
|
||||
|
||||
size_t entry_count =
|
||||
(rsdt->header.length - sizeof(struct sdt)) / (use_xsdt ? 8 : 4);
|
||||
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
struct sdt *ptr;
|
||||
if (use_xsdt)
|
||||
ptr = (struct sdt *)(uintptr_t)((uint64_t *)rsdt->ptrs_start)[i];
|
||||
else
|
||||
ptr = (struct sdt *)(uintptr_t)((uint32_t *)rsdt->ptrs_start)[i];
|
||||
|
||||
if (!memcmp(ptr->signature, signature, 4)
|
||||
&& !acpi_checksum(ptr, ptr->length)
|
||||
&& cnt++ == index) {
|
||||
printv("acpi: Found \"%s\" at %x\n", signature, ptr);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
printv("acpi: \"%s\" not found\n", signature);
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
#ifndef __LIB__ACPI_H__
|
||||
#define __LIB__ACPI_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/cpu.h>
|
||||
|
||||
#define EBDA (ebda_get())
|
||||
|
||||
#if defined (BIOS)
|
||||
static inline uintptr_t ebda_get(void) {
|
||||
uintptr_t ebda = (uintptr_t)mminw(0x40e) << 4;
|
||||
|
||||
// Sanity checks
|
||||
if (ebda < 0x80000 || ebda >= 0xa0000) {
|
||||
ebda = 0x80000;
|
||||
}
|
||||
|
||||
return ebda;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sdt {
|
||||
char signature[4];
|
||||
uint32_t length;
|
||||
uint8_t rev;
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
char oem_table_id[8];
|
||||
uint32_t oem_rev;
|
||||
char creator_id[4];
|
||||
uint32_t creator_rev;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct rsdp {
|
||||
char signature[8];
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t rev;
|
||||
uint32_t rsdt_addr;
|
||||
// Revision 2 only after this comment
|
||||
uint32_t length;
|
||||
uint64_t xsdt_addr;
|
||||
uint8_t ext_checksum;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct rsdt {
|
||||
struct sdt header;
|
||||
char ptrs_start[];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct smbios_entry_point_32 {
|
||||
char anchor_str[4];
|
||||
/// This value summed with all the values of the table.
|
||||
uint8_t checksum;
|
||||
/// Length of the entry point table.
|
||||
uint8_t length;
|
||||
/// Major version of SMBIOS.
|
||||
uint8_t major_version;
|
||||
/// Minor version of SMBIOS.
|
||||
uint8_t minor_version;
|
||||
/// Size of the largest SMBIOS structure, in bytes, and encompasses the
|
||||
/// 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 largest SMBIOS structure, in bytes, and encompasses the
|
||||
/// structure’s formatted area and text strings
|
||||
uint16_t max_structure_size;
|
||||
/// 64-bit physical starting address of the read-only SMBIOS Structure
|
||||
/// Table.
|
||||
uint64_t table_address;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct madt {
|
||||
struct sdt header;
|
||||
uint32_t local_controller_addr;
|
||||
uint32_t flags;
|
||||
char madt_entries_begin[];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct madt_header {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct madt_lapic {
|
||||
struct madt_header header;
|
||||
uint8_t acpi_processor_uid;
|
||||
uint8_t lapic_id;
|
||||
uint32_t flags;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct madt_x2apic {
|
||||
struct madt_header header;
|
||||
uint8_t reserved[2];
|
||||
uint32_t x2apic_id;
|
||||
uint32_t flags;
|
||||
uint32_t acpi_processor_uid;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct madt_io_apic {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t apic_id;
|
||||
uint8_t reserved;
|
||||
uint32_t address;
|
||||
uint32_t gsib;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct madt_gicc {
|
||||
struct madt_header header;
|
||||
uint8_t reserved1[2];
|
||||
uint32_t iface_no;
|
||||
uint32_t acpi_uid;
|
||||
uint32_t flags;
|
||||
uint32_t parking_ver;
|
||||
uint32_t perf_gsiv;
|
||||
uint64_t parking_addr;
|
||||
uint64_t gicc_base_addr;
|
||||
uint64_t gicv_base_addr;
|
||||
uint64_t gich_base_addr;
|
||||
uint32_t vgic_maint_gsiv;
|
||||
uint64_t gicr_base_addr;
|
||||
uint64_t mpidr;
|
||||
uint8_t power_eff_class;
|
||||
uint8_t reserved2;
|
||||
uint16_t spe_overflow_gsiv;
|
||||
} __attribute__((packed));
|
||||
|
||||
// Reference: https://github.com/riscv-non-isa/riscv-acpi/issues/15
|
||||
struct madt_riscv_intc {
|
||||
struct madt_header header;
|
||||
uint8_t version;
|
||||
uint8_t reserved;
|
||||
uint32_t flags;
|
||||
uint64_t hartid;
|
||||
uint32_t acpi_processor_uid;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define MADT_RISCV_INTC_ENABLED ((uint32_t)1 << 0)
|
||||
#define MADT_RISCV_INTC_ONLINE_CAPABLE ((uint32_t)1 << 1)
|
||||
|
||||
uint8_t acpi_checksum(void *ptr, size_t size);
|
||||
void *acpi_get_rsdp(void);
|
||||
|
||||
void *acpi_get_rsdp_v1(void);
|
||||
void *acpi_get_rsdp_v2(void);
|
||||
|
||||
void *acpi_get_table(const char *signature, int index);
|
||||
void acpi_get_smbios(void **smbios32, void **smbios64);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,471 @@
|
|||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/readline.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <fs/file.h>
|
||||
#include <lib/print.h>
|
||||
#include <pxe/tftp.h>
|
||||
#include <crypt/blake2b.h>
|
||||
#include <sys/cpu.h>
|
||||
|
||||
#define CONFIG_B2SUM_SIGNATURE "++CONFIG_B2SUM_SIGNATURE++"
|
||||
#define CONFIG_B2SUM_EMPTY "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
const char *config_b2sum = CONFIG_B2SUM_SIGNATURE CONFIG_B2SUM_EMPTY;
|
||||
|
||||
static bool config_get_entry_name(char *ret, size_t index, size_t limit);
|
||||
static char *config_get_entry(size_t *size, size_t index);
|
||||
|
||||
#define SEPARATOR '\n'
|
||||
|
||||
bool config_ready = false;
|
||||
no_unwind bool bad_config = false;
|
||||
|
||||
static char *config_addr;
|
||||
|
||||
int init_config_disk(struct volume *part) {
|
||||
struct file_handle *f;
|
||||
|
||||
bool old_cif = case_insensitive_fopen;
|
||||
case_insensitive_fopen = true;
|
||||
if ((f = fopen(part, "/limine.cfg")) == NULL
|
||||
&& (f = fopen(part, "/limine/limine.cfg")) == NULL
|
||||
&& (f = fopen(part, "/boot/limine.cfg")) == NULL
|
||||
&& (f = fopen(part, "/boot/limine/limine.cfg")) == NULL
|
||||
&& (f = fopen(part, "/EFI/BOOT/limine.cfg")) == NULL) {
|
||||
case_insensitive_fopen = old_cif;
|
||||
return -1;
|
||||
}
|
||||
case_insensitive_fopen = old_cif;
|
||||
|
||||
size_t config_size = f->size + 2;
|
||||
config_addr = ext_mem_alloc(config_size);
|
||||
|
||||
fread(f, config_addr, 0, f->size);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return init_config(config_size);
|
||||
}
|
||||
|
||||
#define NOT_CHILD (-1)
|
||||
#define DIRECT_CHILD 0
|
||||
#define INDIRECT_CHILD 1
|
||||
|
||||
static int is_child(char *buf, size_t limit,
|
||||
size_t current_depth, size_t index) {
|
||||
if (!config_get_entry_name(buf, index, limit))
|
||||
return NOT_CHILD;
|
||||
if (strlen(buf) < current_depth + 1)
|
||||
return NOT_CHILD;
|
||||
for (size_t j = 0; j < current_depth; j++)
|
||||
if (buf[j] != ':')
|
||||
return NOT_CHILD;
|
||||
if (buf[current_depth] == ':')
|
||||
return INDIRECT_CHILD;
|
||||
return DIRECT_CHILD;
|
||||
}
|
||||
|
||||
static bool is_directory(char *buf, size_t limit,
|
||||
size_t current_depth, size_t index) {
|
||||
switch (is_child(buf, limit, current_depth + 1, index + 1)) {
|
||||
default:
|
||||
case NOT_CHILD:
|
||||
return false;
|
||||
case INDIRECT_CHILD:
|
||||
bad_config = true;
|
||||
panic(true, "config: Malformed config file. Parentless child.");
|
||||
case DIRECT_CHILD:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static struct menu_entry *create_menu_tree(struct menu_entry *parent,
|
||||
size_t current_depth, size_t index) {
|
||||
struct menu_entry *root = NULL, *prev = NULL;
|
||||
|
||||
for (size_t i = index; ; i++) {
|
||||
static char name[64];
|
||||
|
||||
switch (is_child(name, 64, current_depth, i)) {
|
||||
case NOT_CHILD:
|
||||
return root;
|
||||
case INDIRECT_CHILD:
|
||||
continue;
|
||||
case DIRECT_CHILD:
|
||||
break;
|
||||
}
|
||||
|
||||
struct menu_entry *entry = ext_mem_alloc(sizeof(struct menu_entry));
|
||||
|
||||
if (root == NULL)
|
||||
root = entry;
|
||||
|
||||
config_get_entry_name(name, i, 64);
|
||||
|
||||
bool default_expanded = name[current_depth] == '+';
|
||||
|
||||
strcpy(entry->name, name + current_depth + default_expanded);
|
||||
entry->parent = parent;
|
||||
|
||||
size_t entry_size;
|
||||
char *config_entry = config_get_entry(&entry_size, i);
|
||||
entry->body = ext_mem_alloc(entry_size + 1);
|
||||
memcpy(entry->body, config_entry, entry_size);
|
||||
entry->body[entry_size] = 0;
|
||||
|
||||
if (is_directory(name, 64, current_depth, i)) {
|
||||
entry->sub = create_menu_tree(entry, current_depth + 1, i + 1);
|
||||
entry->expanded = default_expanded;
|
||||
}
|
||||
|
||||
char *comment = config_get_value(entry->body, 0, "COMMENT");
|
||||
if (comment != NULL) {
|
||||
entry->comment = comment;
|
||||
}
|
||||
|
||||
if (prev != NULL)
|
||||
prev->next = entry;
|
||||
prev = entry;
|
||||
}
|
||||
}
|
||||
|
||||
struct menu_entry *menu_tree = NULL;
|
||||
|
||||
struct macro {
|
||||
char name[1024];
|
||||
char value[2048];
|
||||
struct macro *next;
|
||||
};
|
||||
|
||||
static struct macro *macros = NULL;
|
||||
|
||||
int init_config(size_t config_size) {
|
||||
config_b2sum += sizeof(CONFIG_B2SUM_SIGNATURE) - 1;
|
||||
|
||||
if (memcmp((void *)config_b2sum, CONFIG_B2SUM_EMPTY, 128) != 0) {
|
||||
editor_enabled = false;
|
||||
|
||||
uint8_t out_buf[BLAKE2B_OUT_BYTES];
|
||||
blake2b(out_buf, config_addr, config_size - 2);
|
||||
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
|
||||
|
||||
for (size_t i = 0; i < BLAKE2B_OUT_BYTES; i++) {
|
||||
hash_buf[i] = digit_to_int(config_b2sum[i * 2]) << 4 | digit_to_int(config_b2sum[i * 2 + 1]);
|
||||
}
|
||||
|
||||
if (memcmp(hash_buf, out_buf, BLAKE2B_OUT_BYTES) != 0) {
|
||||
panic(false, "!!! CHECKSUM MISMATCH FOR CONFIG FILE !!!");
|
||||
}
|
||||
}
|
||||
|
||||
// add trailing newline if not present
|
||||
config_addr[config_size - 2] = '\n';
|
||||
|
||||
// remove windows carriage returns and spaces at the start of lines, if any
|
||||
for (size_t i = 0; i < config_size; i++) {
|
||||
size_t skip = 0;
|
||||
while ((config_addr[i + skip] == '\r')
|
||||
|| ((!i || config_addr[i - 1] == '\n')
|
||||
&& (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t'))) {
|
||||
skip++;
|
||||
}
|
||||
if (skip) {
|
||||
for (size_t j = i; j < config_size - skip; j++)
|
||||
config_addr[j] = config_addr[j + skip];
|
||||
config_size -= skip;
|
||||
}
|
||||
}
|
||||
|
||||
// Load macros
|
||||
struct macro *arch_macro = ext_mem_alloc(sizeof(struct macro));
|
||||
strcpy(arch_macro->name, "ARCH");
|
||||
#if defined (__x86_64__)
|
||||
strcpy(arch_macro->value, "x86-64");
|
||||
#elif defined (__i386__)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
|
||||
strcpy(arch_macro->value, "ia-32");
|
||||
} else {
|
||||
strcpy(arch_macro->value, "x86-64");
|
||||
}
|
||||
}
|
||||
#elif defined (__aarch64__)
|
||||
strcpy(arch_macro->value, "aarch64");
|
||||
#elif defined (__riscv64)
|
||||
strcpy(arch_macro->value, "riscv64");
|
||||
#else
|
||||
#error "Unspecified architecture"
|
||||
#endif
|
||||
arch_macro->next = macros;
|
||||
macros = arch_macro;
|
||||
|
||||
for (size_t i = 0; i < config_size;) {
|
||||
if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0)
|
||||
|| (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) {
|
||||
struct macro *macro = ext_mem_alloc(sizeof(struct macro));
|
||||
|
||||
i += i ? 3 : 2;
|
||||
size_t j;
|
||||
for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
|
||||
macro->name[j] = config_addr[i];
|
||||
}
|
||||
|
||||
if (config_addr[i] == '\n' || config_addr[i] == 0 || config_addr[i+1] != '=') {
|
||||
continue;
|
||||
}
|
||||
i += 2;
|
||||
|
||||
macro->name[j] = 0;
|
||||
|
||||
for (j = 0; config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
|
||||
macro->value[j] = config_addr[i];
|
||||
}
|
||||
macro->value[j] = 0;
|
||||
|
||||
macro->next = macros;
|
||||
macros = macro;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Expand macros
|
||||
if (macros != NULL) {
|
||||
size_t new_config_size = config_size * 4;
|
||||
char *new_config = ext_mem_alloc(new_config_size);
|
||||
|
||||
size_t i, in;
|
||||
for (i = 0, in = 0; i < config_size;) {
|
||||
if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0)
|
||||
|| (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) {
|
||||
size_t orig_i = i;
|
||||
i += i ? 3 : 2;
|
||||
while (config_addr[i++] != '}') {
|
||||
if (i >= config_size) {
|
||||
bad_config = true;
|
||||
panic(true, "config: Malformed macro usage");
|
||||
}
|
||||
}
|
||||
if (config_addr[i++] != '=') {
|
||||
i = orig_i;
|
||||
goto next;
|
||||
}
|
||||
while (config_addr[i] != '\n' && config_addr[i] != 0) {
|
||||
i++;
|
||||
if (i >= config_size) {
|
||||
bad_config = true;
|
||||
panic(true, "config: Malformed macro usage");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
next:
|
||||
if (config_size - i >= 2 && memcmp(config_addr + i, "${", 2) == 0) {
|
||||
char *macro_name = ext_mem_alloc(1024);
|
||||
i += 2;
|
||||
size_t j;
|
||||
for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
|
||||
macro_name[j] = config_addr[i];
|
||||
}
|
||||
if (config_addr[i] != '}') {
|
||||
bad_config = true;
|
||||
panic(true, "config: Malformed macro usage");
|
||||
}
|
||||
i++;
|
||||
macro_name[j] = 0;
|
||||
char *macro_value = "";
|
||||
struct macro *macro = macros;
|
||||
for (;;) {
|
||||
if (macro == NULL) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(macro->name, macro_name) == 0) {
|
||||
macro_value = macro->value;
|
||||
break;
|
||||
}
|
||||
macro = macro->next;
|
||||
}
|
||||
pmm_free(macro_name, 1024);
|
||||
for (j = 0; macro_value[j] != 0; j++, in++) {
|
||||
if (in >= new_config_size) {
|
||||
goto overflow;
|
||||
}
|
||||
new_config[in] = macro_value[j];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in >= new_config_size) {
|
||||
overflow:
|
||||
bad_config = true;
|
||||
panic(true, "config: Macro-induced buffer overflow");
|
||||
}
|
||||
new_config[in++] = config_addr[i++];
|
||||
}
|
||||
|
||||
pmm_free(config_addr, config_size);
|
||||
|
||||
config_addr = new_config;
|
||||
config_size = in;
|
||||
|
||||
// Free macros
|
||||
struct macro *macro = macros;
|
||||
for (;;) {
|
||||
if (macro == NULL) {
|
||||
break;
|
||||
}
|
||||
struct macro *next = macro->next;
|
||||
pmm_free(macro, sizeof(struct macro));
|
||||
macro = next;
|
||||
}
|
||||
}
|
||||
|
||||
config_ready = true;
|
||||
|
||||
menu_tree = create_menu_tree(NULL, 1, 0);
|
||||
|
||||
size_t s;
|
||||
char *c = config_get_entry(&s, 0);
|
||||
while (*c != ':') {
|
||||
c--;
|
||||
}
|
||||
if (c > config_addr) {
|
||||
c[-1] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool config_get_entry_name(char *ret, size_t index, size_t limit) {
|
||||
if (!config_ready)
|
||||
return false;
|
||||
|
||||
char *p = config_addr;
|
||||
|
||||
for (size_t i = 0; i <= index; i++) {
|
||||
while (*p != ':') {
|
||||
if (!*p)
|
||||
return false;
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
if ((p - 1) != config_addr && *(p - 2) != '\n')
|
||||
i--;
|
||||
}
|
||||
|
||||
p--;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < (limit - 1); i++) {
|
||||
if (p[i] == SEPARATOR)
|
||||
break;
|
||||
ret[i] = p[i];
|
||||
}
|
||||
|
||||
ret[i] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *config_get_entry(size_t *size, size_t index) {
|
||||
if (!config_ready)
|
||||
return NULL;
|
||||
|
||||
char *ret;
|
||||
char *p = config_addr;
|
||||
|
||||
for (size_t i = 0; i <= index; i++) {
|
||||
while (*p != ':') {
|
||||
if (!*p)
|
||||
return NULL;
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
if ((p - 1) != config_addr && *(p - 2) != '\n')
|
||||
i--;
|
||||
}
|
||||
|
||||
do {
|
||||
p++;
|
||||
} while (*p != '\n');
|
||||
|
||||
ret = p;
|
||||
|
||||
cont:
|
||||
while (*p != ':' && *p)
|
||||
p++;
|
||||
|
||||
if (*p && *(p - 1) != '\n') {
|
||||
p++;
|
||||
goto cont;
|
||||
}
|
||||
|
||||
*size = p - ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *lastkey;
|
||||
|
||||
struct conf_tuple config_get_tuple(const char *config, size_t index,
|
||||
const char *key1, const char *key2) {
|
||||
struct conf_tuple conf_tuple;
|
||||
|
||||
conf_tuple.value1 = config_get_value(config, index, key1);
|
||||
if (conf_tuple.value1 == NULL) {
|
||||
return (struct conf_tuple){0};
|
||||
}
|
||||
|
||||
conf_tuple.value2 = config_get_value(lastkey, 0, key2);
|
||||
|
||||
const char *lk1 = lastkey;
|
||||
|
||||
const char *next_value1 = config_get_value(config, index + 1, key1);
|
||||
|
||||
const char *lk2 = lastkey;
|
||||
|
||||
if (conf_tuple.value2 != NULL && next_value1 != NULL) {
|
||||
if ((uintptr_t)lk1 > (uintptr_t)lk2) {
|
||||
conf_tuple.value2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return conf_tuple;
|
||||
}
|
||||
|
||||
char *config_get_value(const char *config, size_t index, const char *key) {
|
||||
if (!key || !config_ready)
|
||||
return NULL;
|
||||
|
||||
if (config == NULL)
|
||||
config = config_addr;
|
||||
|
||||
size_t key_len = strlen(key);
|
||||
|
||||
for (size_t i = 0; config[i]; i++) {
|
||||
if (!strncmp(&config[i], key, key_len) && config[i + key_len] == '=') {
|
||||
if (i && config[i - 1] != SEPARATOR)
|
||||
continue;
|
||||
if (index--)
|
||||
continue;
|
||||
i += key_len + 1;
|
||||
size_t value_len;
|
||||
for (value_len = 0;
|
||||
config[i + value_len] != SEPARATOR && config[i + value_len];
|
||||
value_len++);
|
||||
char *buf = ext_mem_alloc(value_len + 1);
|
||||
memcpy(buf, config + i, value_len);
|
||||
lastkey = config + i;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef __LIB__CONFIG_H__
|
||||
#define __LIB__CONFIG_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/part.h>
|
||||
|
||||
extern bool config_ready;
|
||||
extern bool bad_config;
|
||||
|
||||
struct menu_entry {
|
||||
char name[64];
|
||||
char *comment;
|
||||
struct menu_entry *parent;
|
||||
struct menu_entry *sub;
|
||||
bool expanded;
|
||||
char *body;
|
||||
struct menu_entry *next;
|
||||
};
|
||||
|
||||
struct conf_tuple {
|
||||
char *value1;
|
||||
char *value2;
|
||||
};
|
||||
|
||||
extern struct menu_entry *menu_tree;
|
||||
|
||||
int init_config_disk(struct volume *part);
|
||||
int init_config(size_t config_size);
|
||||
|
||||
char *config_get_value(const char *config, size_t index, const char *key);
|
||||
struct conf_tuple config_get_tuple(const char *config, size_t index,
|
||||
const char *key1, const char *key2);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,765 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <lib/misc.h>
|
||||
#include <sys/cpu.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/elf.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/rand.h>
|
||||
#include <lib/elsewhere.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <fs/file.h>
|
||||
|
||||
#define PT_LOAD 0x00000001
|
||||
#define PT_DYNAMIC 0x00000002
|
||||
#define PT_INTERP 0x00000003
|
||||
#define PT_PHDR 0x00000006
|
||||
|
||||
#define DT_NULL 0x00000000
|
||||
#define DT_NEEDED 0x00000001
|
||||
#define DT_RELA 0x00000007
|
||||
#define DT_RELASZ 0x00000008
|
||||
#define DT_RELAENT 0x00000009
|
||||
#define DT_FLAGS_1 0x6ffffffb
|
||||
|
||||
#define DF_1_PIE 0x08000000
|
||||
|
||||
#define ABI_SYSV 0x00
|
||||
#define ARCH_X86_64 0x3e
|
||||
#define ARCH_X86_32 0x03
|
||||
#define ARCH_AARCH64 0xb7
|
||||
#define ARCH_RISCV 0xf3
|
||||
#define BITS_LE 0x01
|
||||
#define ELFCLASS64 0x02
|
||||
#define ET_DYN 0x0003
|
||||
#define SHT_RELA 0x00000004
|
||||
#define R_X86_64_RELATIVE 0x00000008
|
||||
#define R_AARCH64_RELATIVE 0x00000403
|
||||
#define R_RISCV_RELATIVE 0x00000003
|
||||
|
||||
/* Indices into identification array */
|
||||
#define EI_CLASS 4
|
||||
#define EI_DATA 5
|
||||
#define EI_VERSION 6
|
||||
#define EI_OSABI 7
|
||||
|
||||
struct elf32_hdr {
|
||||
uint8_t ident[16];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version;
|
||||
uint32_t entry;
|
||||
uint32_t phoff;
|
||||
uint32_t shoff;
|
||||
uint32_t flags;
|
||||
uint16_t hdr_size;
|
||||
uint16_t phdr_size;
|
||||
uint16_t ph_num;
|
||||
uint16_t shdr_size;
|
||||
uint16_t sh_num;
|
||||
uint16_t shstrndx;
|
||||
};
|
||||
|
||||
struct elf64_phdr {
|
||||
uint32_t p_type;
|
||||
uint32_t p_flags;
|
||||
uint64_t p_offset;
|
||||
uint64_t p_vaddr;
|
||||
uint64_t p_paddr;
|
||||
uint64_t p_filesz;
|
||||
uint64_t p_memsz;
|
||||
uint64_t p_align;
|
||||
};
|
||||
|
||||
struct elf32_phdr {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
|
||||
struct elf64_rela {
|
||||
uint64_t r_addr;
|
||||
uint32_t r_info;
|
||||
uint32_t r_symbol;
|
||||
uint64_t r_addend;
|
||||
};
|
||||
|
||||
struct elf64_dyn {
|
||||
uint64_t d_tag;
|
||||
uint64_t d_un;
|
||||
};
|
||||
|
||||
int elf_bits(uint8_t *elf) {
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
|
||||
printv("elf: Not a valid ELF file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (hdr->machine) {
|
||||
case ARCH_X86_64:
|
||||
case ARCH_AARCH64:
|
||||
return 64;
|
||||
case ARCH_RISCV:
|
||||
return (hdr->ident[EI_CLASS] == ELFCLASS64) ? 64 : 32;
|
||||
case ARCH_X86_32:
|
||||
return 32;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf) {
|
||||
struct elf_section_hdr_info info = {0};
|
||||
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
info.num = hdr->sh_num;
|
||||
info.section_entry_size = hdr->shdr_size;
|
||||
info.str_section_idx = hdr->shstrndx;
|
||||
info.section_offset = hdr->shoff;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf) {
|
||||
struct elf_section_hdr_info info = {0};
|
||||
|
||||
struct elf32_hdr *hdr = (void *)elf;
|
||||
|
||||
info.num = hdr->sh_num;
|
||||
info.section_entry_size = hdr->shdr_size;
|
||||
info.str_section_idx = hdr->shstrndx;
|
||||
info.section_offset = hdr->shoff;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool elf64_is_relocatable(uint8_t *elf, struct elf64_hdr *hdr) {
|
||||
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
|
||||
}
|
||||
|
||||
// Find DYN segment
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_DYNAMIC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint16_t j = 0; j < phdr->p_filesz / sizeof(struct elf64_dyn); j++) {
|
||||
struct elf64_dyn *dyn = (void *)elf + (phdr->p_offset + j * sizeof(struct elf64_dyn));
|
||||
|
||||
switch (dyn->d_tag) {
|
||||
case DT_FLAGS_1:
|
||||
if (dyn->d_un & DF_1_PIE) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DT_RELA:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool elf64_apply_relocations(uint8_t *elf, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
|
||||
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
|
||||
}
|
||||
|
||||
// Find DYN segment
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_DYNAMIC)
|
||||
continue;
|
||||
|
||||
uint64_t rela_offset = 0;
|
||||
uint64_t rela_size = 0;
|
||||
uint64_t rela_ent = 0;
|
||||
for (uint16_t j = 0; j < phdr->p_filesz / sizeof(struct elf64_dyn); j++) {
|
||||
struct elf64_dyn *dyn = (void *)elf + (phdr->p_offset + j * sizeof(struct elf64_dyn));
|
||||
|
||||
switch (dyn->d_tag) {
|
||||
case DT_RELA:
|
||||
rela_offset = dyn->d_un;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
rela_ent = dyn->d_un;
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
rela_size = dyn->d_un;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rela_offset == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rela_ent != sizeof(struct elf64_rela)) {
|
||||
print("elf: Unknown sh_entsize for RELA section!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint16_t j = 0; j < hdr->ph_num; j++) {
|
||||
struct elf64_phdr *_phdr = (void *)elf + (hdr->phoff + j * hdr->phdr_size);
|
||||
|
||||
if (_phdr->p_vaddr <= rela_offset && _phdr->p_vaddr + _phdr->p_filesz > rela_offset) {
|
||||
rela_offset -= _phdr->p_vaddr;
|
||||
rela_offset += _phdr->p_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a RELA header, get and apply all relocations
|
||||
for (uint64_t offset = 0; offset < rela_size; offset += rela_ent) {
|
||||
struct elf64_rela *relocation = (void *)elf + (rela_offset + offset);
|
||||
|
||||
switch (relocation->r_info) {
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
case R_X86_64_RELATIVE:
|
||||
#elif defined (__aarch64__)
|
||||
case R_AARCH64_RELATIVE:
|
||||
#elif defined (__riscv64)
|
||||
case R_RISCV_RELATIVE:
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
{
|
||||
// Relocation is before buffer
|
||||
if (relocation->r_addr < vaddr)
|
||||
continue;
|
||||
|
||||
// Relocation is after buffer
|
||||
if (vaddr + size < relocation->r_addr + 8)
|
||||
continue;
|
||||
|
||||
// It's inside it, calculate where it is
|
||||
uint64_t *ptr = (uint64_t *)((uint8_t *)buffer - vaddr + relocation->r_addr);
|
||||
|
||||
// Write the relocated value
|
||||
*ptr = slide + relocation->r_addend;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
print("elf: Unknown RELA type: %x\n", relocation->r_info);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide) {
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
|
||||
printv("elf: Not a valid ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->ident[EI_DATA] != BITS_LE) {
|
||||
printv("elf: Not a Little-endian ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
if (hdr->machine != ARCH_X86_64) {
|
||||
printv("elf: Not an x86-64 ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
#elif defined (__aarch64__)
|
||||
if (hdr->machine != ARCH_AARCH64) {
|
||||
printv("elf: Not an aarch64 ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
#elif defined (__riscv64)
|
||||
if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) {
|
||||
printv("elf: Not a riscv64 ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
if (hdr->sh_num == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->shdr_size < sizeof(struct elf64_shdr)) {
|
||||
panic(true, "elf: shdr_size < sizeof(struct elf64_shdr)");
|
||||
}
|
||||
|
||||
struct elf64_shdr *shstrtab = (void *)elf + (hdr->shoff + hdr->shstrndx * hdr->shdr_size);
|
||||
|
||||
char *names = (void *)elf + shstrtab->sh_offset;
|
||||
|
||||
for (uint16_t i = 0; i < hdr->sh_num; i++) {
|
||||
struct elf64_shdr *section = (void *)elf + (hdr->shoff + i * hdr->shdr_size);
|
||||
|
||||
if (strcmp(&names[section->sh_name], name) == 0) {
|
||||
if (limit == 0) {
|
||||
*(void **)buffer = ext_mem_alloc(section->sh_size);
|
||||
buffer = *(void **)buffer;
|
||||
limit = section->sh_size;
|
||||
}
|
||||
if (section->sh_size > limit) {
|
||||
return false;
|
||||
}
|
||||
memcpy(buffer, elf + section->sh_offset, section->sh_size);
|
||||
return elf64_apply_relocations(elf, hdr, buffer, section->sh_addr, section->sh_size, slide);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint64_t elf64_max_align(uint8_t *elf) {
|
||||
uint64_t ret = 0;
|
||||
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr->p_align > ret) {
|
||||
ret = phdr->p_align;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
panic(true, "elf: Executable has no loadable segments");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct elf_range **_ranges, uint64_t *_ranges_count) {
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
uint64_t ranges_count = 0;
|
||||
|
||||
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ranges_count++;
|
||||
}
|
||||
|
||||
if (ranges_count == 0) {
|
||||
panic(true, "elf: No higher half PHDRs exist");
|
||||
}
|
||||
|
||||
struct elf_range *ranges = ext_mem_alloc(ranges_count * sizeof(struct elf_range));
|
||||
|
||||
size_t r = 0;
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t load_addr = phdr->p_vaddr + slide;
|
||||
uint64_t this_top = load_addr + phdr->p_memsz;
|
||||
|
||||
ranges[r].base = load_addr & ~(phdr->p_align - 1);
|
||||
ranges[r].length = ALIGN_UP(this_top - ranges[r].base, phdr->p_align);
|
||||
ranges[r].permissions = phdr->p_flags & 0b111;
|
||||
|
||||
r++;
|
||||
}
|
||||
|
||||
*_ranges_count = ranges_count;
|
||||
*_ranges = ranges;
|
||||
}
|
||||
|
||||
bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct elf_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) {
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
|
||||
printv("elf: Not a valid ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->ident[EI_DATA] != BITS_LE) {
|
||||
panic(true, "elf: Not a Little-endian ELF file.\n");
|
||||
}
|
||||
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
if (hdr->machine != ARCH_X86_64) {
|
||||
panic(true, "elf: Not an x86-64 ELF file.\n");
|
||||
}
|
||||
#elif defined (__aarch64__)
|
||||
if (hdr->machine != ARCH_AARCH64) {
|
||||
panic(true, "elf: Not an aarch64 ELF file.\n");
|
||||
}
|
||||
#elif defined (__riscv64)
|
||||
if (hdr->machine != ARCH_RISCV && hdr->ident[EI_CLASS] == ELFCLASS64) {
|
||||
panic(true, "elf: Not a riscv64 ELF file.\n");
|
||||
}
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
if (is_reloc) {
|
||||
*is_reloc = false;
|
||||
}
|
||||
|
||||
uint64_t slide = 0;
|
||||
size_t try_count = 0;
|
||||
size_t max_simulated_tries = 0x10000;
|
||||
|
||||
uint64_t entry = hdr->entry;
|
||||
|
||||
uint64_t max_align = elf64_max_align(elf);
|
||||
|
||||
uint64_t image_size = 0;
|
||||
|
||||
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
|
||||
}
|
||||
|
||||
uint64_t min_vaddr = (uint64_t)-1;
|
||||
uint64_t max_vaddr = 0;
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Drop entries not in the higher half
|
||||
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for overlapping phdrs
|
||||
for (uint16_t j = 0; j < hdr->ph_num; j++) {
|
||||
struct elf64_phdr *phdr_in = (void *)elf + (hdr->phoff + j * hdr->phdr_size);
|
||||
|
||||
if (phdr_in->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Drop entries not in the higher half
|
||||
if (phdr_in->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr_in == phdr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((phdr_in->p_vaddr >= phdr->p_vaddr
|
||||
&& phdr_in->p_vaddr < phdr->p_vaddr + phdr->p_memsz)
|
||||
||
|
||||
(phdr_in->p_vaddr + phdr_in->p_memsz > phdr->p_vaddr
|
||||
&& phdr_in->p_vaddr + phdr_in->p_memsz <= phdr->p_vaddr + phdr->p_memsz)) {
|
||||
panic(true, "elf: Attempted to load ELF file with overlapping PHDRs (%u and %u overlap)", i, j);
|
||||
}
|
||||
|
||||
if (ranges != NULL) {
|
||||
uint64_t page_rounded_base = ALIGN_DOWN(phdr->p_vaddr, 4096);
|
||||
uint64_t page_rounded_top = ALIGN_UP(phdr->p_vaddr + phdr->p_memsz, 4096);
|
||||
uint64_t page_rounded_base_in = ALIGN_DOWN(phdr_in->p_vaddr, 4096);
|
||||
uint64_t page_rounded_top_in = ALIGN_UP(phdr_in->p_vaddr + phdr_in->p_memsz, 4096);
|
||||
|
||||
if ((page_rounded_base >= page_rounded_base_in
|
||||
&& page_rounded_base < page_rounded_top_in)
|
||||
||
|
||||
(page_rounded_top > page_rounded_base_in
|
||||
&& page_rounded_top <= page_rounded_top_in)) {
|
||||
if ((phdr->p_flags & 0b111) != (phdr_in->p_flags & 0b111)) {
|
||||
panic(true, "elf: Attempted to load ELF file with PHDRs with different permissions sharing the same memory page.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (phdr->p_vaddr < min_vaddr) {
|
||||
min_vaddr = phdr->p_vaddr;
|
||||
}
|
||||
|
||||
if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
|
||||
max_vaddr = phdr->p_vaddr + phdr->p_memsz;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_vaddr == 0 || min_vaddr == (uint64_t)-1) {
|
||||
panic(true, "elf: No higher half PHDRs exist");
|
||||
}
|
||||
|
||||
image_size = max_vaddr - min_vaddr;
|
||||
|
||||
*physical_base = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, max_align);
|
||||
*virtual_base = min_vaddr;
|
||||
|
||||
if (_image_size) {
|
||||
*_image_size = image_size;
|
||||
}
|
||||
|
||||
if (elf64_is_relocatable(elf, hdr)) {
|
||||
if (is_reloc) {
|
||||
*is_reloc = true;
|
||||
}
|
||||
}
|
||||
|
||||
again:
|
||||
if (*is_reloc && kaslr) {
|
||||
slide = rand32() & ~(max_align - 1);
|
||||
|
||||
if ((*virtual_base - FIXED_HIGHER_HALF_OFFSET_64) + slide + image_size >= 0x80000000) {
|
||||
if (++try_count == max_simulated_tries) {
|
||||
panic(true, "elf: Image wants to load too high");
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t bss_size = 0;
|
||||
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Drop entries not in the higher half
|
||||
if (phdr->p_vaddr < FIXED_HIGHER_HALF_OFFSET_64) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
if (phdr->p_filesz > phdr->p_memsz) {
|
||||
panic(true, "elf: p_filesz > p_memsz");
|
||||
}
|
||||
|
||||
uint64_t load_addr = *physical_base + (phdr->p_vaddr - *virtual_base);
|
||||
|
||||
#if defined (__aarch64__)
|
||||
uint64_t this_top = load_addr + phdr->p_memsz;
|
||||
|
||||
uint64_t mem_base, mem_size;
|
||||
|
||||
mem_base = load_addr & ~(phdr->p_align - 1);
|
||||
mem_size = this_top - mem_base;
|
||||
#endif
|
||||
|
||||
memcpy((void *)(uintptr_t)load_addr, elf + (phdr->p_offset), phdr->p_filesz);
|
||||
|
||||
bss_size = phdr->p_memsz - phdr->p_filesz;
|
||||
|
||||
if (!elf64_apply_relocations(elf, hdr, (void *)(uintptr_t)load_addr, phdr->p_vaddr, phdr->p_memsz, slide)) {
|
||||
panic(true, "elf: Failed to apply relocations");
|
||||
}
|
||||
|
||||
#if defined (__aarch64__)
|
||||
clean_dcache_poc(mem_base, mem_base + mem_size);
|
||||
inval_icache_pou(mem_base, mem_base + mem_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_image_size_before_bss != NULL) {
|
||||
*_image_size_before_bss = image_size - bss_size;
|
||||
}
|
||||
|
||||
*virtual_base += slide;
|
||||
*entry_point = entry + slide;
|
||||
if (_slide) {
|
||||
*_slide = slide;
|
||||
}
|
||||
|
||||
if (ranges_count != NULL && ranges != NULL) {
|
||||
elf64_get_ranges(elf, slide, ranges, ranges_count);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
|
||||
struct elsewhere_range **ranges,
|
||||
uint64_t *ranges_count) {
|
||||
struct elf32_hdr *hdr = (void *)elf;
|
||||
|
||||
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
|
||||
printv("elf: Not a valid ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->ident[EI_DATA] != BITS_LE) {
|
||||
printv("elf: Not a Little-endian ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->machine != ARCH_X86_32) {
|
||||
printv("elf: Not an x86_32 ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*entry_point = hdr->entry;
|
||||
bool entry_adjusted = false;
|
||||
|
||||
if (hdr->phdr_size < sizeof(struct elf32_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf32_phdr)");
|
||||
}
|
||||
|
||||
*ranges_count = 0;
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf32_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
*ranges_count += 1;
|
||||
}
|
||||
|
||||
*ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * *ranges_count);
|
||||
|
||||
size_t cur_entry = 0;
|
||||
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf32_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
// Sanity checks
|
||||
if (phdr->p_filesz > phdr->p_memsz) {
|
||||
panic(true, "elf: p_filesz > p_memsz");
|
||||
}
|
||||
|
||||
void *elsewhere = ext_mem_alloc(phdr->p_memsz);
|
||||
|
||||
memcpy(elsewhere, elf + phdr->p_offset, phdr->p_filesz);
|
||||
|
||||
if (!entry_adjusted
|
||||
&& *entry_point >= phdr->p_vaddr
|
||||
&& *entry_point < (phdr->p_vaddr + phdr->p_memsz)) {
|
||||
*entry_point -= phdr->p_vaddr;
|
||||
*entry_point += phdr->p_paddr;
|
||||
entry_adjusted = true;
|
||||
}
|
||||
|
||||
(*ranges)[cur_entry].elsewhere = (uintptr_t)elsewhere;
|
||||
(*ranges)[cur_entry].target = phdr->p_paddr;
|
||||
(*ranges)[cur_entry].length = phdr->p_memsz;
|
||||
|
||||
cur_entry++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
|
||||
struct elsewhere_range **ranges,
|
||||
uint64_t *ranges_count) {
|
||||
struct elf64_hdr *hdr = (void *)elf;
|
||||
|
||||
if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
|
||||
printv("elf: Not a valid ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->ident[EI_DATA] != BITS_LE) {
|
||||
printv("elf: Not a Little-endian ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hdr->machine != ARCH_X86_64) {
|
||||
printv("elf: Not an x86-64 ELF file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*entry_point = hdr->entry;
|
||||
bool entry_adjusted = false;
|
||||
|
||||
if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
|
||||
panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
|
||||
}
|
||||
|
||||
*ranges_count = 0;
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
*ranges_count += 1;
|
||||
}
|
||||
|
||||
*ranges = ext_mem_alloc(sizeof(struct elsewhere_range) * *ranges_count);
|
||||
|
||||
size_t cur_entry = 0;
|
||||
|
||||
for (uint16_t i = 0; i < hdr->ph_num; i++) {
|
||||
struct elf64_phdr *phdr = (void *)elf + (hdr->phoff + i * hdr->phdr_size);
|
||||
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
// Sanity checks
|
||||
if (phdr->p_filesz > phdr->p_memsz) {
|
||||
panic(true, "elf: p_filesz > p_memsz");
|
||||
}
|
||||
|
||||
void *elsewhere = ext_mem_alloc(phdr->p_memsz);
|
||||
|
||||
memcpy(elsewhere, elf + phdr->p_offset, phdr->p_filesz);
|
||||
|
||||
if (!entry_adjusted
|
||||
&& *entry_point >= phdr->p_vaddr
|
||||
&& *entry_point < (phdr->p_vaddr + phdr->p_memsz)) {
|
||||
*entry_point -= phdr->p_vaddr;
|
||||
*entry_point += phdr->p_paddr;
|
||||
entry_adjusted = true;
|
||||
}
|
||||
|
||||
(*ranges)[cur_entry].elsewhere = (uintptr_t)elsewhere;
|
||||
(*ranges)[cur_entry].target = phdr->p_paddr;
|
||||
(*ranges)[cur_entry].length = phdr->p_memsz;
|
||||
|
||||
cur_entry++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
#ifndef __LIB__ELF_H__
|
||||
#define __LIB__ELF_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/elsewhere.h>
|
||||
|
||||
#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
|
||||
|
||||
#define ELF_PF_X 1
|
||||
#define ELF_PF_W 2
|
||||
#define ELF_PF_R 4
|
||||
|
||||
struct elf_range {
|
||||
uint64_t base;
|
||||
uint64_t length;
|
||||
uint64_t permissions;
|
||||
};
|
||||
|
||||
struct elf_section_hdr_info {
|
||||
uint32_t section_entry_size;
|
||||
uint32_t str_section_idx;
|
||||
uint32_t num;
|
||||
uint32_t section_offset;
|
||||
};
|
||||
|
||||
int elf_bits(uint8_t *elf);
|
||||
|
||||
struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf);
|
||||
struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf);
|
||||
|
||||
bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide);
|
||||
bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct elf_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
|
||||
|
||||
bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
|
||||
struct elsewhere_range **ranges,
|
||||
uint64_t *ranges_count);
|
||||
bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
|
||||
struct elsewhere_range **ranges,
|
||||
uint64_t *ranges_count);
|
||||
|
||||
struct elf64_hdr {
|
||||
uint8_t ident[16];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version;
|
||||
uint64_t entry;
|
||||
uint64_t phoff;
|
||||
uint64_t shoff;
|
||||
uint32_t flags;
|
||||
uint16_t hdr_size;
|
||||
uint16_t phdr_size;
|
||||
uint16_t ph_num;
|
||||
uint16_t shdr_size;
|
||||
uint16_t sh_num;
|
||||
uint16_t shstrndx;
|
||||
};
|
||||
|
||||
struct elf64_shdr {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint64_t sh_flags;
|
||||
uint64_t sh_addr;
|
||||
uint64_t sh_offset;
|
||||
uint64_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint64_t sh_addralign;
|
||||
uint64_t sh_entsize;
|
||||
};
|
||||
|
||||
struct elf32_shdr {
|
||||
uint32_t sh_name;
|
||||
uint32_t sh_type;
|
||||
uint32_t sh_flags;
|
||||
uint32_t sh_addr;
|
||||
uint32_t sh_offset;
|
||||
uint32_t sh_size;
|
||||
uint32_t sh_link;
|
||||
uint32_t sh_info;
|
||||
uint32_t sh_addralign;
|
||||
uint32_t sh_entsize;
|
||||
};
|
||||
|
||||
struct elf64_sym {
|
||||
uint32_t st_name;
|
||||
uint8_t st_info;
|
||||
uint8_t st_other;
|
||||
uint16_t st_shndx;
|
||||
uint64_t st_value;
|
||||
uint64_t st_size;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/elsewhere.h>
|
||||
#include <lib/misc.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
static bool elsewhere_overlap_check(uint64_t base1, uint64_t top1,
|
||||
uint64_t base2, uint64_t top2) {
|
||||
return ((base1 >= base2 && base1 < top2)
|
||||
|| (top1 > base2 && top1 <= top2));
|
||||
}
|
||||
|
||||
bool elsewhere_append(
|
||||
bool flexible_target,
|
||||
struct elsewhere_range *ranges, uint64_t *ranges_count,
|
||||
void *elsewhere, uint64_t *target, size_t t_length) {
|
||||
// original target of -1 means "allocate after top of all ranges"
|
||||
// flexible target is ignored
|
||||
flexible_target = true;
|
||||
if (*target == (uint64_t)-1) {
|
||||
uint64_t top = 0;
|
||||
|
||||
for (size_t i = 0; i < *ranges_count; i++) {
|
||||
uint64_t r_top = ranges[i].target + ranges[i].length;
|
||||
|
||||
if (top < r_top) {
|
||||
top = r_top;
|
||||
}
|
||||
}
|
||||
|
||||
*target = ALIGN_UP(top, 4096);
|
||||
}
|
||||
|
||||
uint64_t max_retries = 0x10000;
|
||||
|
||||
retry:
|
||||
if (max_retries-- == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < *ranges_count; i++) {
|
||||
uint64_t t_top = *target + t_length;
|
||||
|
||||
// Does it overlap with other elsewhere ranges targets?
|
||||
{
|
||||
uint64_t base = ranges[i].target;
|
||||
uint64_t length = ranges[i].length;
|
||||
uint64_t top = base + length;
|
||||
|
||||
if (elsewhere_overlap_check(base, top, *target, t_top)) {
|
||||
if (!flexible_target) {
|
||||
return false;
|
||||
}
|
||||
*target = ALIGN_UP(top, 4096);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
// Does it overlap with other elsewhere ranges sources?
|
||||
{
|
||||
uint64_t base = ranges[i].elsewhere;
|
||||
uint64_t length = ranges[i].length;
|
||||
uint64_t top = base + length;
|
||||
|
||||
if (elsewhere_overlap_check(base, top, *target, t_top)) {
|
||||
if (!flexible_target) {
|
||||
return false;
|
||||
}
|
||||
*target += 0x1000;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it is memory that actually exists.
|
||||
if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE,
|
||||
MEMMAP_USABLE, false, true, false)) {
|
||||
if (!memmap_alloc_range(*target, t_length, MEMMAP_BOOTLOADER_RECLAIMABLE,
|
||||
MEMMAP_BOOTLOADER_RECLAIMABLE, false, true, false)) {
|
||||
if (!flexible_target) {
|
||||
return false;
|
||||
}
|
||||
*target += 0x1000;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the elsewhere range
|
||||
ranges[*ranges_count].elsewhere = (uintptr_t)elsewhere;
|
||||
ranges[*ranges_count].target = *target;
|
||||
ranges[*ranges_count].length = t_length;
|
||||
*ranges_count += 1;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,58 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/fb.h>
|
||||
#include <drivers/vbe.h>
|
||||
#include <drivers/gop.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
void fb_init(struct fb_info **ret, size_t *_fbs_count,
|
||||
uint64_t target_width, uint64_t target_height, uint16_t target_bpp) {
|
||||
#if defined (BIOS)
|
||||
*ret = ext_mem_alloc(sizeof(struct fb_info));
|
||||
if (init_vbe(*ret, target_width, target_height, target_bpp)) {
|
||||
*_fbs_count = 1;
|
||||
|
||||
(*ret)->edid = get_edid_info();
|
||||
size_t mode_count;
|
||||
(*ret)->mode_list = vbe_get_mode_list(&mode_count);
|
||||
(*ret)->mode_count = mode_count;
|
||||
} else {
|
||||
*_fbs_count = 0;
|
||||
pmm_free(*ret, sizeof(struct fb_info));
|
||||
}
|
||||
#elif defined (UEFI)
|
||||
init_gop(ret, _fbs_count, target_width, target_height, target_bpp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void fb_clear(struct fb_info *fb) {
|
||||
for (size_t y = 0; y < fb->framebuffer_height; y++) {
|
||||
switch (fb->framebuffer_bpp) {
|
||||
case 32: {
|
||||
uint32_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
|
||||
size_t row = (y * fb->framebuffer_pitch) / 4;
|
||||
for (size_t x = 0; x < fb->framebuffer_width; x++) {
|
||||
fbp[row + x] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
uint16_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
|
||||
size_t row = (y * fb->framebuffer_pitch) / 2;
|
||||
for (size_t x = 0; x < fb->framebuffer_width; x++) {
|
||||
fbp[row + x] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
uint8_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
|
||||
size_t row = y * fb->framebuffer_pitch;
|
||||
for (size_t x = 0; x < fb->framebuffer_width * fb->framebuffer_bpp; x++) {
|
||||
fbp[row + x] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef __LIB__FB_H__
|
||||
#define __LIB__FB_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <drivers/edid.h>
|
||||
|
||||
struct resolution {
|
||||
uint64_t width;
|
||||
uint64_t height;
|
||||
uint16_t bpp;
|
||||
};
|
||||
|
||||
struct fb_info {
|
||||
uint64_t framebuffer_pitch;
|
||||
uint64_t framebuffer_width;
|
||||
uint64_t framebuffer_height;
|
||||
uint16_t framebuffer_bpp;
|
||||
uint8_t memory_model;
|
||||
uint8_t red_mask_size;
|
||||
uint8_t red_mask_shift;
|
||||
uint8_t green_mask_size;
|
||||
uint8_t green_mask_shift;
|
||||
uint8_t blue_mask_size;
|
||||
uint8_t blue_mask_shift;
|
||||
|
||||
uint64_t framebuffer_addr;
|
||||
|
||||
struct edid_info_struct *edid;
|
||||
|
||||
uint64_t mode_count;
|
||||
struct fb_info *mode_list;
|
||||
};
|
||||
|
||||
void fb_init(struct fb_info **ret, size_t *_fbs_count,
|
||||
uint64_t target_width, uint64_t target_height, uint16_t target_bpp);
|
||||
|
||||
void fb_clear(struct fb_info *fb);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,785 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <lib/gterm.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/uri.h>
|
||||
#include <lib/fb.h>
|
||||
#include <lib/image.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <flanterm/flanterm.h>
|
||||
#define FLANTERM_FB_SUPPORT_BPP
|
||||
#include <flanterm/backends/fb.h>
|
||||
#include <lib/term.h>
|
||||
|
||||
// Builtin font originally taken from:
|
||||
// https://github.com/viler-int10h/vga-text-mode-fonts/raw/master/FONTS/PC-OTHER/TOSH-SAT.F16
|
||||
static const uint8_t builtin_font[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81, 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00,
|
||||
0x00, 0x3c, 0x7e, 0xff, 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x1e, 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12, 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x30, 0x38, 0x2c, 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00,
|
||||
0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00,
|
||||
0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e, 0xfe, 0x7e, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
|
||||
0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda, 0x6c, 0x06, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc0, 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||
0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x30, 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xe0, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x38, 0x6c, 0x38, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x0c, 0x38,
|
||||
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x38, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36, 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1e, 0x3c, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x30, 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00,
|
||||
0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x76, 0xdc, 0x00, 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00,
|
||||
0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
|
||||
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
|
||||
0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
|
||||
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xd8, 0xc0, 0xc0, 0x00,
|
||||
0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78, 0xc0, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00,
|
||||
0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static struct image *background;
|
||||
|
||||
static size_t margin = 64;
|
||||
static size_t margin_gradient = 4;
|
||||
|
||||
static uint32_t default_bg, default_fg;
|
||||
static uint32_t default_bg_bright, default_fg_bright;
|
||||
|
||||
static size_t bg_canvas_size;
|
||||
static uint32_t *bg_canvas;
|
||||
|
||||
#define A(rgb) (uint8_t)(rgb >> 24)
|
||||
#define R(rgb) (uint8_t)(rgb >> 16)
|
||||
#define G(rgb) (uint8_t)(rgb >> 8)
|
||||
#define B(rgb) (uint8_t)(rgb)
|
||||
#define ARGB(a, r, g, b) (a << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF)
|
||||
|
||||
static inline uint32_t colour_blend(uint32_t fg, uint32_t bg) {
|
||||
unsigned alpha = 255 - A(fg);
|
||||
unsigned inv_alpha = A(fg) + 1;
|
||||
|
||||
uint8_t r = (uint8_t)((alpha * R(fg) + inv_alpha * R(bg)) / 256);
|
||||
uint8_t g = (uint8_t)((alpha * G(fg) + inv_alpha * G(bg)) / 256);
|
||||
uint8_t b = (uint8_t)((alpha * B(fg) + inv_alpha * B(bg)) / 256);
|
||||
|
||||
return ARGB(0, r, g, b);
|
||||
}
|
||||
|
||||
static uint32_t blend_gradient_from_box(struct fb_info *fb, size_t x, size_t y, uint32_t bg_px, uint32_t hex) {
|
||||
size_t distance, x_distance, y_distance;
|
||||
size_t gradient_stop_x = fb->framebuffer_width - margin;
|
||||
size_t gradient_stop_y = fb->framebuffer_height - margin;
|
||||
|
||||
if (x < margin)
|
||||
x_distance = margin - x;
|
||||
else
|
||||
x_distance = x - gradient_stop_x;
|
||||
|
||||
if (y < margin)
|
||||
y_distance = margin - y;
|
||||
else
|
||||
y_distance = y - gradient_stop_y;
|
||||
|
||||
if (x >= margin && x < gradient_stop_x) {
|
||||
distance = y_distance;
|
||||
} else if (y >= margin && y < gradient_stop_y) {
|
||||
distance = x_distance;
|
||||
} else {
|
||||
distance = sqrt((uint64_t)x_distance * (uint64_t)x_distance
|
||||
+ (uint64_t)y_distance * (uint64_t)y_distance);
|
||||
}
|
||||
|
||||
if (distance > margin_gradient)
|
||||
return bg_px;
|
||||
|
||||
uint8_t gradient_step = (0xff - A(hex)) / margin_gradient;
|
||||
uint8_t new_alpha = A(hex) + gradient_step * distance;
|
||||
|
||||
return colour_blend((hex & 0xffffff) | (new_alpha << 24), bg_px);
|
||||
}
|
||||
|
||||
typedef size_t fixedp6; // the last 6 bits are the fixed point part
|
||||
static size_t fixedp6_to_int(fixedp6 value) { return value / 64; }
|
||||
static fixedp6 int_to_fixedp6(size_t value) { return value * 64; }
|
||||
|
||||
// Draw rect at coordinates, copying from the image to the fb and canvas, applying fn on every pixel
|
||||
__attribute__((always_inline)) static inline void genloop(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend, uint32_t (*blend)(struct fb_info *fb, size_t x, size_t y, uint32_t orig)) {
|
||||
uint8_t *img = background->img;
|
||||
const size_t img_width = background->img_width, img_height = background->img_height, img_pitch = background->pitch, colsize = background->bpp / 8;
|
||||
|
||||
switch (background->type) {
|
||||
case IMAGE_TILED:
|
||||
for (size_t y = ystart; y < yend; y++) {
|
||||
size_t image_y = y % img_height, image_x = xstart % img_width;
|
||||
const size_t off = img_pitch * image_y;
|
||||
size_t canvas_off = fb->framebuffer_width * y;
|
||||
for (size_t x = xstart; x < xend; x++) {
|
||||
uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off);
|
||||
uint32_t i = blend(fb, x, y, img_pixel);
|
||||
bg_canvas[canvas_off + x] = i;
|
||||
if (image_x++ == img_width) image_x = 0; // image_x = x % img_width, but modulo is too expensive
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_CENTERED:
|
||||
for (size_t y = ystart; y < yend; y++) {
|
||||
size_t image_y = y - background->y_displacement;
|
||||
const size_t off = img_pitch * image_y;
|
||||
size_t canvas_off = fb->framebuffer_width * y;
|
||||
if (image_y >= background->y_size) { /* external part */
|
||||
for (size_t x = xstart; x < xend; x++) {
|
||||
uint32_t i = blend(fb, x, y, background->back_colour);
|
||||
bg_canvas[canvas_off + x] = i;
|
||||
}
|
||||
}
|
||||
else { /* internal part */
|
||||
for (size_t x = xstart; x < xend; x++) {
|
||||
size_t image_x = (x - background->x_displacement);
|
||||
bool x_external = image_x >= background->x_size;
|
||||
uint32_t img_pixel = *(uint32_t*)(img + image_x * colsize + off);
|
||||
uint32_t i = blend(fb, x, y, x_external ? background->back_colour : img_pixel);
|
||||
bg_canvas[canvas_off + x] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// For every pixel, ratio = img_width / gterm_width, img_x = x * ratio, x = (xstart + i)
|
||||
// hence x = xstart * ratio + i * ratio
|
||||
// so you can set x = xstart * ratio, and increment by ratio at each iteration
|
||||
case IMAGE_STRETCHED:
|
||||
for (size_t y = ystart; y < yend; y++) {
|
||||
size_t img_y = (y * img_height) / fb->framebuffer_height; // calculate Y with full precision
|
||||
size_t off = img_pitch * img_y;
|
||||
size_t canvas_off = fb->framebuffer_width * y;
|
||||
|
||||
size_t ratio = int_to_fixedp6(img_width) / fb->framebuffer_width;
|
||||
fixedp6 img_x = ratio * xstart;
|
||||
for (size_t x = xstart; x < xend; x++) {
|
||||
uint32_t img_pixel = *(uint32_t*)(img + fixedp6_to_int(img_x) * colsize + off);
|
||||
uint32_t i = blend(fb, x, y, img_pixel);
|
||||
bg_canvas[canvas_off + x] = i;
|
||||
img_x += ratio;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t blend_external(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { (void)fb; (void)x; (void)y; return orig; }
|
||||
static uint32_t blend_internal(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { (void)fb; (void)x; (void)y; return colour_blend(default_bg, orig); }
|
||||
static uint32_t blend_margin(struct fb_info *fb, size_t x, size_t y, uint32_t orig) { return blend_gradient_from_box(fb, x, y, orig, default_bg); }
|
||||
|
||||
static void loop_external(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_external); }
|
||||
static void loop_margin(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_margin); }
|
||||
static void loop_internal(struct fb_info *fb, size_t xstart, size_t xend, size_t ystart, size_t yend) { genloop(fb, xstart, xend, ystart, yend, blend_internal); }
|
||||
|
||||
static void generate_canvas(struct fb_info *fb) {
|
||||
if (background) {
|
||||
bg_canvas_size = fb->framebuffer_width * fb->framebuffer_height * sizeof(uint32_t);
|
||||
bg_canvas = ext_mem_alloc(bg_canvas_size);
|
||||
|
||||
int64_t margin_no_gradient = (int64_t)margin - margin_gradient;
|
||||
|
||||
if (margin_no_gradient < 0) {
|
||||
margin_no_gradient = 0;
|
||||
}
|
||||
|
||||
size_t scan_stop_x = fb->framebuffer_width - margin_no_gradient;
|
||||
size_t scan_stop_y = fb->framebuffer_height - margin_no_gradient;
|
||||
|
||||
loop_external(fb, 0, fb->framebuffer_width, 0, margin_no_gradient);
|
||||
loop_external(fb, 0, fb->framebuffer_width, scan_stop_y, fb->framebuffer_height);
|
||||
loop_external(fb, 0, margin_no_gradient, margin_no_gradient, scan_stop_y);
|
||||
loop_external(fb, scan_stop_x, fb->framebuffer_width, margin_no_gradient, scan_stop_y);
|
||||
|
||||
size_t gradient_stop_x = fb->framebuffer_width - margin;
|
||||
size_t gradient_stop_y = fb->framebuffer_height - margin;
|
||||
|
||||
if (margin_gradient) {
|
||||
loop_margin(fb, margin_no_gradient, scan_stop_x, margin_no_gradient, margin);
|
||||
loop_margin(fb, margin_no_gradient, scan_stop_x, gradient_stop_y, scan_stop_y);
|
||||
loop_margin(fb, margin_no_gradient, margin, margin, gradient_stop_y);
|
||||
loop_margin(fb, gradient_stop_x, scan_stop_x, margin, gradient_stop_y);
|
||||
}
|
||||
|
||||
loop_internal(fb, margin, gradient_stop_x, margin, gradient_stop_y);
|
||||
} else {
|
||||
bg_canvas = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool gterm_init(struct fb_info **_fbs, size_t *_fbs_count,
|
||||
char *config, size_t width, size_t height) {
|
||||
static struct fb_info *fbs;
|
||||
static size_t fbs_count;
|
||||
|
||||
static bool prev_valid = false;
|
||||
static char *prev_config;
|
||||
static size_t prev_width, prev_height;
|
||||
|
||||
if (prev_valid && config == prev_config && width == prev_width && height == prev_height) {
|
||||
*_fbs = fbs;
|
||||
*_fbs_count = fbs_count;
|
||||
reset_term();
|
||||
return true;
|
||||
}
|
||||
|
||||
prev_valid = false;
|
||||
|
||||
if (quiet) {
|
||||
term_notready();
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined (UEFI)
|
||||
if (serial || COM_OUTPUT) {
|
||||
term_fallback();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
term_notready();
|
||||
|
||||
// We force bpp to 32
|
||||
fb_init(&fbs, &fbs_count, width, height, 32);
|
||||
|
||||
if (_fbs != NULL) {
|
||||
*_fbs = fbs;
|
||||
}
|
||||
if (_fbs_count != NULL) {
|
||||
*_fbs_count = fbs_count;
|
||||
}
|
||||
|
||||
if (fbs_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// default scheme
|
||||
margin = 64;
|
||||
margin_gradient = 4;
|
||||
|
||||
uint32_t ansi_colours[8];
|
||||
|
||||
ansi_colours[0] = 0x00000000; // black
|
||||
ansi_colours[1] = 0x00aa0000; // red
|
||||
ansi_colours[2] = 0x0000aa00; // green
|
||||
ansi_colours[3] = 0x00aa5500; // brown
|
||||
ansi_colours[4] = 0x000000aa; // blue
|
||||
ansi_colours[5] = 0x00aa00aa; // magenta
|
||||
ansi_colours[6] = 0x0000aaaa; // cyan
|
||||
ansi_colours[7] = 0x00aaaaaa; // grey
|
||||
|
||||
char *colours = config_get_value(config, 0, "TERM_PALETTE");
|
||||
if (colours != NULL) {
|
||||
const char *first = colours;
|
||||
size_t i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
const char *last;
|
||||
uint32_t col = strtoui(first, &last, 16);
|
||||
if (first == last)
|
||||
break;
|
||||
ansi_colours[i] = col & 0xffffff;
|
||||
if (*last == 0)
|
||||
break;
|
||||
first = last + 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ansi_bright_colours[8];
|
||||
|
||||
ansi_bright_colours[0] = 0x00555555; // black
|
||||
ansi_bright_colours[1] = 0x00ff5555; // red
|
||||
ansi_bright_colours[2] = 0x0055ff55; // green
|
||||
ansi_bright_colours[3] = 0x00ffff55; // brown
|
||||
ansi_bright_colours[4] = 0x005555ff; // blue
|
||||
ansi_bright_colours[5] = 0x00ff55ff; // magenta
|
||||
ansi_bright_colours[6] = 0x0055ffff; // cyan
|
||||
ansi_bright_colours[7] = 0x00ffffff; // grey
|
||||
|
||||
char *bright_colours = config_get_value(config, 0, "TERM_PALETTE_BRIGHT");
|
||||
if (bright_colours != NULL) {
|
||||
const char *first = bright_colours;
|
||||
size_t i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
const char *last;
|
||||
uint32_t col = strtoui(first, &last, 16);
|
||||
if (first == last)
|
||||
break;
|
||||
ansi_bright_colours[i] = col & 0xffffff;
|
||||
if (*last == 0)
|
||||
break;
|
||||
first = last + 1;
|
||||
}
|
||||
}
|
||||
|
||||
default_bg = 0x00000000; // background (black)
|
||||
default_fg = 0x00aaaaaa; // foreground (grey)
|
||||
|
||||
default_bg_bright = 0x00555555; // background (black)
|
||||
default_fg_bright = 0x00ffffff; // foreground (grey)
|
||||
|
||||
char *theme_background = config_get_value(config, 0, "TERM_BACKGROUND");
|
||||
if (theme_background != NULL) {
|
||||
default_bg = strtoui(theme_background, NULL, 16);
|
||||
}
|
||||
|
||||
char *theme_foreground = config_get_value(config, 0, "TERM_FOREGROUND");
|
||||
if (theme_foreground != NULL) {
|
||||
default_fg = strtoui(theme_foreground, NULL, 16) & 0xffffff;
|
||||
}
|
||||
|
||||
char *theme_background_bright = config_get_value(config, 0, "TERM_BACKGROUND_BRIGHT");
|
||||
if (theme_background_bright != NULL) {
|
||||
default_bg_bright = strtoui(theme_background_bright, NULL, 16);
|
||||
}
|
||||
|
||||
char *theme_foreground_bright = config_get_value(config, 0, "TERM_FOREGROUND_BRIGHT");
|
||||
if (theme_foreground_bright != NULL) {
|
||||
default_fg_bright = strtoui(theme_foreground_bright, NULL, 16);
|
||||
}
|
||||
|
||||
background = NULL;
|
||||
char *background_path = config_get_value(config, 0, "TERM_WALLPAPER");
|
||||
if (background_path != NULL) {
|
||||
struct file_handle *bg_file;
|
||||
if ((bg_file = uri_open(background_path)) != NULL) {
|
||||
background = image_open(bg_file);
|
||||
fclose(bg_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (background == NULL) {
|
||||
margin = 0;
|
||||
margin_gradient = 0;
|
||||
} else {
|
||||
if (theme_background == NULL) {
|
||||
default_bg = 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
char *theme_margin = config_get_value(config, 0, "TERM_MARGIN");
|
||||
if (theme_margin != NULL) {
|
||||
margin = strtoui(theme_margin, NULL, 10);
|
||||
}
|
||||
|
||||
char *theme_margin_gradient = config_get_value(config, 0, "TERM_MARGIN_GRADIENT");
|
||||
if (theme_margin_gradient != NULL) {
|
||||
margin_gradient = strtoui(theme_margin_gradient, NULL, 10);
|
||||
}
|
||||
|
||||
size_t font_width = 8;
|
||||
size_t font_height = 16;
|
||||
size_t font_size = (font_width * font_height * FLANTERM_FB_FONT_GLYPHS) / 8;
|
||||
|
||||
#define FONT_MAX 16384
|
||||
uint8_t *font = ext_mem_alloc(FONT_MAX);
|
||||
|
||||
memcpy(font, builtin_font, 4096);
|
||||
|
||||
size_t tmp_font_width, tmp_font_height;
|
||||
|
||||
char *menu_font_size = config_get_value(config, 0, "TERM_FONT_SIZE");
|
||||
if (menu_font_size != NULL) {
|
||||
parse_resolution(&tmp_font_width, &tmp_font_height, NULL, menu_font_size);
|
||||
|
||||
size_t tmp_font_size = (tmp_font_width * tmp_font_height * FLANTERM_FB_FONT_GLYPHS) / 8;
|
||||
|
||||
if (tmp_font_size > FONT_MAX) {
|
||||
print("Font would be too large (%u bytes, %u bytes allowed). Not loading.\n", tmp_font_size, FONT_MAX);
|
||||
goto no_load_font;
|
||||
}
|
||||
|
||||
font_size = tmp_font_size;
|
||||
}
|
||||
|
||||
char *menu_font = config_get_value(config, 0, "TERM_FONT");
|
||||
if (menu_font != NULL) {
|
||||
struct file_handle *f;
|
||||
if ((f = uri_open(menu_font)) == NULL) {
|
||||
print("menu: Could not open font file.\n");
|
||||
} else {
|
||||
fread(f, font, 0, font_size);
|
||||
if (menu_font_size != NULL) {
|
||||
font_width = tmp_font_width;
|
||||
font_height = tmp_font_height;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
no_load_font:;
|
||||
size_t font_spacing = 1;
|
||||
char *font_spacing_str = config_get_value(config, 0, "TERM_FONT_SPACING");
|
||||
if (font_spacing_str != NULL) {
|
||||
font_spacing = strtoui(font_spacing_str, NULL, 10);
|
||||
}
|
||||
|
||||
size_t font_scale_x = 1;
|
||||
size_t font_scale_y = 1;
|
||||
bool font_scale_is_default = true;
|
||||
|
||||
char *menu_font_scale = config_get_value(config, 0, "TERM_FONT_SCALE");
|
||||
if (menu_font_scale != NULL) {
|
||||
parse_resolution(&font_scale_x, &font_scale_y, NULL, menu_font_scale);
|
||||
if (font_scale_x > 8 || font_scale_y > 8) {
|
||||
font_scale_x = 1;
|
||||
font_scale_y = 1;
|
||||
} else {
|
||||
font_scale_is_default = false;
|
||||
}
|
||||
}
|
||||
|
||||
terms_i = 0;
|
||||
terms = ext_mem_alloc(fbs_count * sizeof(void *));
|
||||
|
||||
for (size_t i = 0; i < fbs_count; i++) {
|
||||
struct fb_info *fb = &fbs[i];
|
||||
|
||||
// Ensure that this framebuffer uses 32-bits per pixel.
|
||||
if (fb->framebuffer_bpp != 32) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (background != NULL) {
|
||||
char *background_layout = config_get_value(config, 0, "TERM_WALLPAPER_STYLE");
|
||||
if (background_layout != NULL && strcmp(background_layout, "centered") == 0) {
|
||||
char *background_colour = config_get_value(config, 0, "TERM_BACKDROP");
|
||||
if (background_colour == NULL)
|
||||
background_colour = "0";
|
||||
uint32_t bg_col = strtoui(background_colour, NULL, 16);
|
||||
image_make_centered(background, fb->framebuffer_width, fb->framebuffer_height, bg_col);
|
||||
} else if (background_layout != NULL && strcmp(background_layout, "tiled") == 0) {
|
||||
} else {
|
||||
image_make_stretched(background, fb->framebuffer_width, fb->framebuffer_height);
|
||||
}
|
||||
}
|
||||
|
||||
generate_canvas(fb);
|
||||
|
||||
if (font_scale_is_default) {
|
||||
if (fb->framebuffer_width >= (1920 + 1920 / 3) && fb->framebuffer_height >= (1080 + 1080 / 3)) {
|
||||
font_scale_x = 2;
|
||||
font_scale_y = 2;
|
||||
}
|
||||
if (fb->framebuffer_width >= (3840 + 3840 / 3) && fb->framebuffer_height >= (2160 + 2160 / 3)) {
|
||||
font_scale_x = 4;
|
||||
font_scale_y = 4;
|
||||
}
|
||||
}
|
||||
|
||||
terms[terms_i] = flanterm_fb_init(ext_mem_alloc,
|
||||
pmm_free,
|
||||
(void *)(uintptr_t)fb->framebuffer_addr,
|
||||
fb->framebuffer_width, fb->framebuffer_height, fb->framebuffer_pitch,
|
||||
fb->red_mask_size, fb->red_mask_shift,
|
||||
fb->green_mask_size, fb->green_mask_shift,
|
||||
fb->blue_mask_size, fb->blue_mask_shift,
|
||||
bg_canvas,
|
||||
ansi_colours, ansi_bright_colours,
|
||||
&default_bg, &default_fg,
|
||||
&default_bg_bright, &default_fg_bright,
|
||||
font, font_width, font_height, font_spacing,
|
||||
font_scale_x, font_scale_y,
|
||||
margin);
|
||||
|
||||
if (terms[terms_i] != NULL) {
|
||||
terms_i++;
|
||||
}
|
||||
|
||||
if (bg_canvas != NULL) {
|
||||
pmm_free(bg_canvas, bg_canvas_size);
|
||||
}
|
||||
}
|
||||
|
||||
pmm_free(font, FONT_MAX);
|
||||
|
||||
if (background != NULL) {
|
||||
image_close(background);
|
||||
background = NULL;
|
||||
}
|
||||
|
||||
if (terms_i == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < terms_i; i++) {
|
||||
struct flanterm_context *term = terms[i];
|
||||
|
||||
if (serial) {
|
||||
term->cols = term->cols > 80 ? 80 : term->cols;
|
||||
term->rows = term->rows > 24 ? 24 : term->rows;
|
||||
}
|
||||
}
|
||||
|
||||
size_t min_cols = (size_t)-1;
|
||||
size_t min_rows = (size_t)-1;
|
||||
|
||||
for (size_t i = 0; i < terms_i; i++) {
|
||||
struct flanterm_context *term = terms[i];
|
||||
|
||||
if (term->cols < min_cols) {
|
||||
min_cols = term->cols;
|
||||
}
|
||||
|
||||
if (term->rows < min_rows) {
|
||||
min_rows = term->rows;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < terms_i; i++) {
|
||||
struct flanterm_context *term = terms[i];
|
||||
|
||||
term->cols = min_cols;
|
||||
term->rows = min_rows;
|
||||
|
||||
flanterm_context_reinit(term);
|
||||
}
|
||||
|
||||
term_backend = GTERM;
|
||||
|
||||
prev_config = config;
|
||||
prev_height = height;
|
||||
prev_width = width;
|
||||
prev_valid = true;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,69 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/guid.h>
|
||||
#include <lib/misc.h>
|
||||
|
||||
bool is_valid_guid(const char *s) {
|
||||
for (size_t i = 0; ; i++) {
|
||||
switch (i) {
|
||||
case 8:
|
||||
case 13:
|
||||
case 18:
|
||||
case 23:
|
||||
if (s[i] != '-')
|
||||
return false;
|
||||
break;
|
||||
case 36:
|
||||
return s[i] == 0;
|
||||
default:
|
||||
if (digit_to_int(s[i]) == -1)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void guid_convert_le_cluster(uint8_t *dest, const char *s, int len) {
|
||||
size_t p = 0;
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
int val = digit_to_int(s[i]);
|
||||
|
||||
i % 2 ? (dest[p] = val) : (dest[p++] |= val << 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void guid_convert_be_cluster(uint8_t *dest, const char *s, int len) {
|
||||
size_t p = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
int val = digit_to_int(s[i]);
|
||||
|
||||
i % 2 ? (dest[p++] |= val) : (dest[p] = val << 4);
|
||||
}
|
||||
}
|
||||
|
||||
bool string_to_guid_be(struct guid *guid, const char *s) {
|
||||
if (!is_valid_guid(s))
|
||||
return false;
|
||||
|
||||
guid_convert_be_cluster((uint8_t *)guid + 0, s + 0, 8);
|
||||
guid_convert_be_cluster((uint8_t *)guid + 4, s + 9, 4);
|
||||
guid_convert_be_cluster((uint8_t *)guid + 6, s + 14, 4);
|
||||
guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4);
|
||||
guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_guid_mixed(struct guid *guid, const char *s) {
|
||||
if (!is_valid_guid(s))
|
||||
return false;
|
||||
|
||||
guid_convert_le_cluster((uint8_t *)guid + 0, s + 0, 8);
|
||||
guid_convert_le_cluster((uint8_t *)guid + 4, s + 9, 4);
|
||||
guid_convert_le_cluster((uint8_t *)guid + 6, s + 14, 4);
|
||||
guid_convert_be_cluster((uint8_t *)guid + 8, s + 19, 4);
|
||||
guid_convert_be_cluster((uint8_t *)guid + 10, s + 24, 12);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef __LIB__GUID_H__
|
||||
#define __LIB__GUID_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct guid {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint16_t c;
|
||||
uint8_t d[8];
|
||||
};
|
||||
|
||||
bool is_valid_guid(const char *s);
|
||||
bool string_to_guid_be(struct guid *guid, const char *s);
|
||||
bool string_to_guid_mixed(struct guid *guid, const char *s);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <lib/image.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/misc.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour) {
|
||||
image->type = IMAGE_CENTERED;
|
||||
|
||||
image->x_displacement = frame_x_size / 2 - image->x_size / 2;
|
||||
image->y_displacement = frame_y_size / 2 - image->y_size / 2;
|
||||
image->back_colour = back_colour;
|
||||
}
|
||||
|
||||
|
||||
void image_make_stretched(struct image *image, int new_x_size, int new_y_size) {
|
||||
image->type = IMAGE_STRETCHED;
|
||||
|
||||
image->x_size = new_x_size;
|
||||
image->y_size = new_y_size;
|
||||
}
|
||||
|
||||
struct image *image_open(struct file_handle *file) {
|
||||
struct image *image = ext_mem_alloc(sizeof(struct image));
|
||||
|
||||
image->type = IMAGE_TILED;
|
||||
|
||||
void *src = ext_mem_alloc(file->size);
|
||||
|
||||
fread(file, src, 0, file->size);
|
||||
|
||||
int x, y, bpp;
|
||||
|
||||
image->img = stbi_load_from_memory(src, file->size, &x, &y, &bpp, 4);
|
||||
|
||||
pmm_free(src, file->size);
|
||||
|
||||
if (image->img == NULL) {
|
||||
pmm_free(image, sizeof(struct image));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Convert ABGR to XRGB
|
||||
uint32_t *pptr = (void *)image->img;
|
||||
for (int i = 0; i < x * y; i++) {
|
||||
pptr[i] = (pptr[i] & 0x0000ff00) | ((pptr[i] & 0x00ff0000) >> 16) | ((pptr[i] & 0x000000ff) << 16);
|
||||
}
|
||||
|
||||
image->x_size = x;
|
||||
image->y_size = y;
|
||||
image->pitch = x * 4;
|
||||
image->bpp = 32;
|
||||
image->img_width = x;
|
||||
image->img_height = y;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void image_close(struct image *image) {
|
||||
stbi_image_free(image->img);
|
||||
pmm_free(image, sizeof(struct image));
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef __LIB__IMAGE_H__
|
||||
#define __LIB__IMAGE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <fs/file.h>
|
||||
|
||||
struct image {
|
||||
size_t x_size;
|
||||
size_t y_size;
|
||||
int type;
|
||||
uint8_t *img;
|
||||
int bpp;
|
||||
int pitch;
|
||||
size_t img_width; // x_size = scaled size, img_width = bitmap size
|
||||
size_t img_height;
|
||||
size_t x_displacement;
|
||||
size_t y_displacement;
|
||||
uint32_t back_colour;
|
||||
};
|
||||
|
||||
enum {
|
||||
IMAGE_TILED,
|
||||
IMAGE_CENTERED,
|
||||
IMAGE_STRETCHED
|
||||
};
|
||||
|
||||
void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour);
|
||||
void image_make_stretched(struct image *image, int new_x_size, int new_y_size);
|
||||
struct image *image_open(struct file_handle *file);
|
||||
void image_close(struct image *image);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef __LIB__LIBC_H__
|
||||
#define __LIB__LIBC_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool isprint(int c);
|
||||
bool isspace(int c);
|
||||
bool isalpha(int c);
|
||||
bool isdigit(int c);
|
||||
|
||||
int toupper(int c);
|
||||
int tolower(int c);
|
||||
|
||||
int abs(int i);
|
||||
|
||||
void *memset(void *, int, size_t);
|
||||
void *memcpy(void *, const void *, size_t);
|
||||
int memcmp(const void *, const void *, size_t);
|
||||
void *memmove(void *, const void *, size_t);
|
||||
|
||||
char *strcpy(char *, const char *);
|
||||
char *strncpy(char *, const char *, size_t);
|
||||
size_t strlen(const char *);
|
||||
int strcmp(const char *, const char *);
|
||||
int strcasecmp(const char *, const char *);
|
||||
int strncmp(const char *, const char *, size_t);
|
||||
int strncasecmp(const char *, const char *, size_t);
|
||||
int inet_pton(const char *src, void *dst);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,134 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <lib/libc.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/misc.h>
|
||||
|
||||
bool isprint(int c) {
|
||||
return c >= ' ' && c <= '~';
|
||||
}
|
||||
|
||||
bool isspace(int c) {
|
||||
return (c >= '\t' && c <= 0xD) || c == ' ';
|
||||
}
|
||||
|
||||
bool isalpha(int c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
bool isdigit(int c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
int toupper(int c) {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
return c - 0x20;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int tolower(int c) {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return c + 0x20;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int abs(int i) {
|
||||
return i < 0 ? -i : i;
|
||||
}
|
||||
|
||||
char *strcpy(char *dest, const char *src) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; src[i]; i++)
|
||||
dest[i] = src[i];
|
||||
|
||||
dest[i] = 0;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *strncpy(char *dest, const char *src, size_t n) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n && src[i]; i++)
|
||||
dest[i] = src[i];
|
||||
for ( ; i < n; i++)
|
||||
dest[i] = 0;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int strcmp(const char *s1, const char *s2) {
|
||||
for (size_t i = 0; ; i++) {
|
||||
char c1 = s1[i], c2 = s2[i];
|
||||
if (c1 != c2)
|
||||
return c1 < c2 ? -1 : 1;
|
||||
if (!c1)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int strcasecmp(const char *s1, const char *s2) {
|
||||
for (size_t i = 0; ; i++) {
|
||||
char c1 = s1[i], c2 = s2[i];
|
||||
if (tolower(c1) != tolower(c2))
|
||||
return c1 < c2 ? -1 : 1;
|
||||
if (!c1)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int strncmp(const char *s1, const char *s2, size_t n) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char c1 = s1[i], c2 = s2[i];
|
||||
if (c1 != c2)
|
||||
return c1 < c2 ? -1 : 1;
|
||||
if (!c1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strncasecmp(const char *s1, const char *s2, size_t n) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char c1 = s1[i], c2 = s2[i];
|
||||
if (tolower(c1) != tolower(c2))
|
||||
return c1 < c2 ? -1 : 1;
|
||||
if (!c1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t strlen(const char *str) {
|
||||
size_t len;
|
||||
|
||||
for (len = 0; str[len]; len++);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int inet_pton(const char *src, void *dst) {
|
||||
uint8_t array[4];
|
||||
const char *current = src;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const char *newcur;
|
||||
uint64_t value = strtoui(current, &newcur, 10);
|
||||
if (current == newcur)
|
||||
return -1;
|
||||
current = newcur;
|
||||
if (*current == 0 && i < 3)
|
||||
return -1;
|
||||
if (value > 255)
|
||||
return -1;
|
||||
current++;
|
||||
array[i] = value;
|
||||
}
|
||||
memcpy(dst, array, 4);
|
||||
return 0;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,57 @@
|
|||
section .text
|
||||
|
||||
global memcpy
|
||||
memcpy:
|
||||
mov rcx, rdx
|
||||
mov rax, rdi
|
||||
rep movsb
|
||||
ret
|
||||
|
||||
global memset
|
||||
memset:
|
||||
push rdi
|
||||
mov rax, rsi
|
||||
mov rcx, rdx
|
||||
rep stosb
|
||||
pop rax
|
||||
ret
|
||||
|
||||
global memmove
|
||||
memmove:
|
||||
mov rcx, rdx
|
||||
mov rax, rdi
|
||||
|
||||
cmp rdi, rsi
|
||||
ja .copy_backwards
|
||||
|
||||
rep movsb
|
||||
jmp .done
|
||||
|
||||
.copy_backwards:
|
||||
lea rdi, [rdi+rcx-1]
|
||||
lea rsi, [rsi+rcx-1]
|
||||
std
|
||||
rep movsb
|
||||
cld
|
||||
|
||||
.done:
|
||||
ret
|
||||
|
||||
global memcmp
|
||||
memcmp:
|
||||
mov rcx, rdx
|
||||
repe cmpsb
|
||||
je .equal
|
||||
|
||||
mov al, byte [rdi-1]
|
||||
sub al, byte [rsi-1]
|
||||
movsx rax, al
|
||||
jmp .done
|
||||
|
||||
.equal:
|
||||
xor eax, eax
|
||||
|
||||
.done:
|
||||
ret
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
@ -0,0 +1,111 @@
|
|||
section .text
|
||||
|
||||
global memcpy
|
||||
memcpy:
|
||||
push esi
|
||||
push edi
|
||||
mov eax, dword [esp+12]
|
||||
mov edi, eax
|
||||
mov esi, dword [esp+16]
|
||||
mov ecx, dword [esp+20]
|
||||
rep movsb
|
||||
pop edi
|
||||
pop esi
|
||||
ret
|
||||
|
||||
global memset
|
||||
memset:
|
||||
push edi
|
||||
mov edx, dword [esp+8]
|
||||
mov edi, edx
|
||||
mov eax, dword [esp+12]
|
||||
mov ecx, dword [esp+16]
|
||||
rep stosb
|
||||
mov eax, edx
|
||||
pop edi
|
||||
ret
|
||||
|
||||
global memmove
|
||||
memmove:
|
||||
push esi
|
||||
push edi
|
||||
mov eax, dword [esp+12]
|
||||
mov edi, eax
|
||||
mov esi, dword [esp+16]
|
||||
mov ecx, dword [esp+20]
|
||||
|
||||
cmp edi, esi
|
||||
ja .copy_backwards
|
||||
|
||||
rep movsb
|
||||
jmp .done
|
||||
|
||||
.copy_backwards:
|
||||
lea edi, [edi+ecx-1]
|
||||
lea esi, [esi+ecx-1]
|
||||
std
|
||||
rep movsb
|
||||
cld
|
||||
|
||||
.done:
|
||||
pop edi
|
||||
pop esi
|
||||
ret
|
||||
|
||||
global memcmp
|
||||
memcmp:
|
||||
push esi
|
||||
push edi
|
||||
mov edi, dword [esp+12]
|
||||
mov esi, dword [esp+16]
|
||||
mov ecx, dword [esp+20]
|
||||
repe cmpsb
|
||||
je .equal
|
||||
mov al, byte [edi-1]
|
||||
sub al, byte [esi-1]
|
||||
movsx eax, al
|
||||
jmp .done
|
||||
|
||||
.equal:
|
||||
xor eax, eax
|
||||
|
||||
.done:
|
||||
pop edi
|
||||
pop esi
|
||||
ret
|
||||
|
||||
global memcpy32to64
|
||||
memcpy32to64:
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
push esi
|
||||
push edi
|
||||
|
||||
push 0x28
|
||||
call .p1
|
||||
.p1:
|
||||
add dword [esp], .mode64 - .p1
|
||||
retfd
|
||||
|
||||
bits 64
|
||||
.mode64:
|
||||
mov rdi, [rbp + 8]
|
||||
mov rsi, [rbp + 16]
|
||||
mov rcx, [rbp + 24]
|
||||
rep movsb
|
||||
|
||||
push 0x18
|
||||
call .p2
|
||||
.p2:
|
||||
add qword [rsp], .mode32 - .p2
|
||||
retfq
|
||||
|
||||
bits 32
|
||||
.mode32:
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
@ -0,0 +1,57 @@
|
|||
#if !defined (__x86_64__) && !defined (__i386__)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void *memcpy(void *dest, const void *src, size_t n) {
|
||||
uint8_t *pdest = (uint8_t *)dest;
|
||||
const uint8_t *psrc = (const uint8_t *)src;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
pdest[i] = psrc[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
uint8_t *p = (uint8_t *)s;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
p[i] = (uint8_t)c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t n) {
|
||||
uint8_t *pdest = (uint8_t *)dest;
|
||||
const uint8_t *psrc = (const uint8_t *)src;
|
||||
|
||||
if (src > dest) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
pdest[i] = psrc[i];
|
||||
}
|
||||
} else if (src < dest) {
|
||||
for (size_t i = n; i > 0; i--) {
|
||||
pdest[i-1] = psrc[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int memcmp(const void *s1, const void *s2, size_t n) {
|
||||
const uint8_t *p1 = (const uint8_t *)s1;
|
||||
const uint8_t *p2 = (const uint8_t *)s2;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
return p1[i] < p2[i] ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,317 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/trace.h>
|
||||
#include <lib/real.h>
|
||||
#include <fs/file.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
#if defined (UEFI)
|
||||
EFI_SYSTEM_TABLE *gST;
|
||||
EFI_BOOT_SERVICES *gBS;
|
||||
EFI_RUNTIME_SERVICES *gRT;
|
||||
EFI_HANDLE efi_image_handle;
|
||||
EFI_MEMORY_DESCRIPTOR *efi_mmap = NULL;
|
||||
UINTN efi_mmap_size = 0, efi_desc_size = 0;
|
||||
UINT32 efi_desc_ver = 0;
|
||||
#endif
|
||||
|
||||
bool editor_enabled = true;
|
||||
bool help_hidden = false;
|
||||
|
||||
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf) {
|
||||
size_t res[3] = {0};
|
||||
|
||||
const char *first = buf;
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
const char *last;
|
||||
size_t x = strtoui(first, &last, 10);
|
||||
if (first == last)
|
||||
break;
|
||||
res[i] = x;
|
||||
if (*last == 0)
|
||||
break;
|
||||
first = last + 1;
|
||||
}
|
||||
|
||||
if (res[0] == 0 || res[1] == 0)
|
||||
return false;
|
||||
|
||||
if (res[2] == 0)
|
||||
res[2] = 32;
|
||||
|
||||
*width = res[0], *height = res[1];
|
||||
if (bpp != NULL)
|
||||
*bpp = res[2];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This integer sqrt implementation has been adapted from:
|
||||
// https://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
|
||||
uint64_t sqrt(uint64_t a_nInput) {
|
||||
uint64_t op = a_nInput;
|
||||
uint64_t res = 0;
|
||||
uint64_t one = (uint64_t)1 << 62;
|
||||
|
||||
// "one" starts at the highest power of four <= than the argument.
|
||||
while (one > op) {
|
||||
one >>= 2;
|
||||
}
|
||||
|
||||
while (one != 0) {
|
||||
if (op >= res + one) {
|
||||
op = op - (res + one);
|
||||
res = res + 2 * one;
|
||||
}
|
||||
res >>= 1;
|
||||
one >>= 2;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t get_trailing_zeros(uint64_t val) {
|
||||
for (size_t i = 0; i < 64; i++) {
|
||||
if ((val & 1) != 0) {
|
||||
return i;
|
||||
}
|
||||
val >>= 1;
|
||||
}
|
||||
return 64;
|
||||
}
|
||||
|
||||
uint32_t oct2bin(uint8_t *str, uint32_t max) {
|
||||
uint32_t value = 0;
|
||||
while (max-- > 0) {
|
||||
value <<= 3;
|
||||
value += *str++ - '0';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t hex2bin(uint8_t *str, uint32_t size) {
|
||||
uint32_t value = 0;
|
||||
while (size-- > 0) {
|
||||
value <<= 4;
|
||||
if (*str >= '0' && *str <= '9')
|
||||
value += (uint32_t)((*str) - '0');
|
||||
else if (*str >= 'A' && *str <= 'F')
|
||||
value += (uint32_t)((*str) - 'A' + 10);
|
||||
else if (*str >= 'a' && *str <= 'f')
|
||||
value += (uint32_t)((*str) - 'a' + 10);
|
||||
str++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
#if defined (UEFI)
|
||||
|
||||
#if defined (__riscv)
|
||||
|
||||
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void) {
|
||||
EFI_GUID boot_proto_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
|
||||
RISCV_EFI_BOOT_PROTOCOL *proto;
|
||||
|
||||
// LocateProtocol() is available from EFI version 1.1
|
||||
if (gBS->Hdr.Revision >= ((1 << 16) | 10)) {
|
||||
if (gBS->LocateProtocol(&boot_proto_guid, NULL, (void **)&proto) == EFI_SUCCESS) {
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
|
||||
UINTN bufsz = 0;
|
||||
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, NULL) != EFI_BUFFER_TOO_SMALL)
|
||||
return NULL;
|
||||
|
||||
EFI_HANDLE *handles_buf = ext_mem_alloc(bufsz);
|
||||
if (handles_buf == NULL)
|
||||
return NULL;
|
||||
|
||||
if (bufsz < sizeof(EFI_HANDLE))
|
||||
goto error;
|
||||
|
||||
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, handles_buf) != EFI_SUCCESS)
|
||||
goto error;
|
||||
|
||||
if (gBS->HandleProtocol(handles_buf[0], &boot_proto_guid, (void **)&proto) != EFI_SUCCESS)
|
||||
goto error;
|
||||
|
||||
pmm_free(handles_buf, bufsz);
|
||||
return proto;
|
||||
|
||||
error:
|
||||
pmm_free(handles_buf, bufsz);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
no_unwind bool efi_boot_services_exited = false;
|
||||
|
||||
bool efi_exit_boot_services(void) {
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR tmp_mmap[1];
|
||||
efi_mmap_size = sizeof(tmp_mmap);
|
||||
UINTN mmap_key = 0;
|
||||
|
||||
gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver);
|
||||
|
||||
efi_mmap_size += 4096;
|
||||
|
||||
status = gBS->FreePool(efi_mmap);
|
||||
if (status) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&efi_mmap);
|
||||
if (status) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR *efi_copy;
|
||||
status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size * 2, (void **)&efi_copy);
|
||||
if (status) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const size_t EFI_COPY_MAX_ENTRIES = (efi_mmap_size * 2) / efi_desc_size;
|
||||
|
||||
size_t retries = 0;
|
||||
|
||||
retry:
|
||||
status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver);
|
||||
if (retries == 0 && status) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Be gone, UEFI!
|
||||
status = gBS->ExitBootServices(efi_image_handle, mmap_key);
|
||||
if (status) {
|
||||
if (retries == 128) {
|
||||
goto fail;
|
||||
}
|
||||
retries++;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
asm volatile ("cli" ::: "memory");
|
||||
#elif defined (__aarch64__)
|
||||
asm volatile ("msr daifset, #15" ::: "memory");
|
||||
#elif defined (__riscv64)
|
||||
asm volatile ("csrci sstatus, 0x2" ::: "memory");
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
// Go through new EFI memmap and free up bootloader entries
|
||||
size_t entry_count = efi_mmap_size / efi_desc_size;
|
||||
|
||||
size_t efi_copy_i = 0;
|
||||
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
EFI_MEMORY_DESCRIPTOR *orig_entry = (void *)efi_mmap + i * efi_desc_size;
|
||||
EFI_MEMORY_DESCRIPTOR *new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
||||
|
||||
memcpy(new_entry, orig_entry, efi_desc_size);
|
||||
|
||||
uint64_t base = orig_entry->PhysicalStart;
|
||||
uint64_t length = orig_entry->NumberOfPages * 4096;
|
||||
uint64_t top = base + length;
|
||||
|
||||
// Find for a match in the untouched memory map
|
||||
for (size_t j = 0; j < untouched_memmap_entries; j++) {
|
||||
if (untouched_memmap[j].type != MEMMAP_USABLE)
|
||||
continue;
|
||||
|
||||
if (top > untouched_memmap[j].base && top <= untouched_memmap[j].base + untouched_memmap[j].length) {
|
||||
if (untouched_memmap[j].base < base) {
|
||||
new_entry->NumberOfPages = (base - untouched_memmap[j].base) / 4096;
|
||||
|
||||
efi_copy_i++;
|
||||
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
||||
panic(false, "efi: New memory map exhausted");
|
||||
}
|
||||
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
||||
memcpy(new_entry, orig_entry, efi_desc_size);
|
||||
|
||||
new_entry->NumberOfPages -= (base - untouched_memmap[j].base) / 4096;
|
||||
new_entry->PhysicalStart = base;
|
||||
new_entry->VirtualStart = new_entry->PhysicalStart;
|
||||
|
||||
length = new_entry->NumberOfPages * 4096;
|
||||
top = base + length;
|
||||
}
|
||||
|
||||
if (untouched_memmap[j].base > base) {
|
||||
new_entry->NumberOfPages = (untouched_memmap[j].base - base) / 4096;
|
||||
|
||||
efi_copy_i++;
|
||||
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
||||
panic(false, "efi: New memory map exhausted");
|
||||
}
|
||||
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
||||
memcpy(new_entry, orig_entry, efi_desc_size);
|
||||
|
||||
new_entry->NumberOfPages -= (untouched_memmap[j].base - base) / 4096;
|
||||
new_entry->PhysicalStart = untouched_memmap[j].base;
|
||||
new_entry->VirtualStart = new_entry->PhysicalStart;
|
||||
|
||||
base = new_entry->PhysicalStart;
|
||||
length = new_entry->NumberOfPages * 4096;
|
||||
top = base + length;
|
||||
}
|
||||
|
||||
if (length < untouched_memmap[j].length) {
|
||||
panic(false, "efi: Memory map corruption");
|
||||
}
|
||||
|
||||
new_entry->Type = EfiConventionalMemory;
|
||||
|
||||
if (length == untouched_memmap[j].length) {
|
||||
// It's a perfect match!
|
||||
break;
|
||||
}
|
||||
|
||||
new_entry->NumberOfPages = untouched_memmap[j].length / 4096;
|
||||
|
||||
efi_copy_i++;
|
||||
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
||||
panic(false, "efi: New memory map exhausted");
|
||||
}
|
||||
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
||||
memcpy(new_entry, orig_entry, efi_desc_size);
|
||||
|
||||
new_entry->NumberOfPages = (length - untouched_memmap[j].length) / 4096;
|
||||
new_entry->PhysicalStart = base + untouched_memmap[j].length;
|
||||
new_entry->VirtualStart = new_entry->PhysicalStart;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
efi_copy_i++;
|
||||
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
||||
panic(false, "efi: New memory map exhausted");
|
||||
}
|
||||
}
|
||||
|
||||
efi_mmap = efi_copy;
|
||||
efi_mmap_size = efi_copy_i * efi_desc_size;
|
||||
|
||||
efi_boot_services_exited = true;
|
||||
|
||||
printv("efi: Exited boot services.\n");
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
panic(false, "efi: Failed to exit boot services");
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef __LIB__MISC_H__
|
||||
#define __LIB__MISC_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <fs/file.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/libc.h>
|
||||
#if defined (UEFI)
|
||||
# include <efi.h>
|
||||
# if defined (__riscv64)
|
||||
# include <protocol/riscv/efiboot.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (UEFI)
|
||||
extern EFI_SYSTEM_TABLE *gST;
|
||||
extern EFI_BOOT_SERVICES *gBS;
|
||||
extern EFI_RUNTIME_SERVICES *gRT;
|
||||
extern EFI_HANDLE efi_image_handle;
|
||||
extern EFI_MEMORY_DESCRIPTOR *efi_mmap;
|
||||
extern UINTN efi_mmap_size, efi_desc_size;
|
||||
extern UINT32 efi_desc_ver;
|
||||
|
||||
extern bool efi_boot_services_exited;
|
||||
bool efi_exit_boot_services(void);
|
||||
#endif
|
||||
|
||||
extern const char bsd_2_clause[];
|
||||
|
||||
extern struct volume *boot_volume;
|
||||
|
||||
#if defined (BIOS)
|
||||
extern bool stage3_loaded;
|
||||
#endif
|
||||
|
||||
extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic;
|
||||
|
||||
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf);
|
||||
|
||||
void get_absolute_path(char *path_ptr, const char *path, const char *pwd);
|
||||
|
||||
uint32_t oct2bin(uint8_t *str, uint32_t max);
|
||||
uint32_t hex2bin(uint8_t *str, uint32_t size);
|
||||
|
||||
uint64_t sqrt(uint64_t a_nInput);
|
||||
size_t get_trailing_zeros(uint64_t val);
|
||||
|
||||
int digit_to_int(char c);
|
||||
uint8_t bcd_to_int(uint8_t val);
|
||||
uint8_t int_to_bcd(uint8_t val);
|
||||
|
||||
noreturn void panic(bool allow_menu, const char *fmt, ...);
|
||||
|
||||
int pit_sleep_and_quit_on_keypress(int seconds);
|
||||
|
||||
uint64_t strtoui(const char *s, const char **end, int base);
|
||||
|
||||
#if defined (__i386__)
|
||||
void memcpy32to64(uint64_t, uint64_t, uint64_t);
|
||||
#elif defined (__x86_64__) || defined (__aarch64__) || defined(__riscv64)
|
||||
# define memcpy32to64(X, Y, Z) memcpy((void *)(uintptr_t)(X), (void *)(uintptr_t)(Y), Z)
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
#define DIV_ROUNDUP(a, b) ({ \
|
||||
__auto_type DIV_ROUNDUP_a = (a); \
|
||||
__auto_type DIV_ROUNDUP_b = (b); \
|
||||
(DIV_ROUNDUP_a + (DIV_ROUNDUP_b - 1)) / DIV_ROUNDUP_b; \
|
||||
})
|
||||
|
||||
#define ALIGN_UP(x, a) ({ \
|
||||
__auto_type ALIGN_UP_value = (x); \
|
||||
__auto_type ALIGN_UP_align = (a); \
|
||||
ALIGN_UP_value = DIV_ROUNDUP(ALIGN_UP_value, ALIGN_UP_align) * ALIGN_UP_align; \
|
||||
ALIGN_UP_value; \
|
||||
})
|
||||
|
||||
#define ALIGN_DOWN(x, a) ({ \
|
||||
__auto_type ALIGN_DOWN_value = (x); \
|
||||
__auto_type ALIGN_DOWN_align = (a); \
|
||||
ALIGN_DOWN_value = (ALIGN_DOWN_value / ALIGN_DOWN_align) * ALIGN_DOWN_align; \
|
||||
ALIGN_DOWN_value; \
|
||||
})
|
||||
|
||||
#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
typedef char symbol[];
|
||||
|
||||
noreturn void stage3_common(void);
|
||||
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
noreturn void common_spinup(void *fnptr, int args, ...);
|
||||
#elif defined (__aarch64__)
|
||||
noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr,
|
||||
uint64_t mair, uint64_t tcr, uint64_t ttbr0,
|
||||
uint64_t ttbr1, uint64_t target_x0);
|
||||
#elif defined (__riscv64)
|
||||
noreturn void riscv_spinup(uint64_t entry, uint64_t sp, uint64_t satp);
|
||||
#if defined (UEFI)
|
||||
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void);
|
||||
#endif
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
#define no_unwind __attribute__((section(".no_unwind")))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,110 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/print.h>
|
||||
|
||||
bool verbose = false;
|
||||
bool quiet = false;
|
||||
bool serial = false;
|
||||
bool hash_mismatch_panic = false;
|
||||
|
||||
uint8_t bcd_to_int(uint8_t val) {
|
||||
return (val & 0x0f) + ((val & 0xf0) >> 4) * 10;
|
||||
}
|
||||
uint8_t int_to_bcd(uint8_t val) {
|
||||
return (val % 10) | (val / 10) << 4;
|
||||
}
|
||||
|
||||
int digit_to_int(char c) {
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return (c - 'a') + 10;
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return (c - 'A') + 10;
|
||||
}
|
||||
if (c >= '0' && c <= '9'){
|
||||
return c - '0';
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t strtoui(const char *s, const char **end, int base) {
|
||||
uint64_t n = 0;
|
||||
for (size_t i = 0; ; i++) {
|
||||
int d = digit_to_int(s[i]);
|
||||
if (d == -1) {
|
||||
if (end != NULL)
|
||||
*end = &s[i];
|
||||
break;
|
||||
}
|
||||
n = n * base + d;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void get_absolute_path(char *path_ptr, const char *path, const char *pwd) {
|
||||
char *orig_ptr = path_ptr;
|
||||
|
||||
if (!*path) {
|
||||
strcpy(path_ptr, pwd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*path != '/') {
|
||||
strcpy(path_ptr, pwd);
|
||||
path_ptr += strlen(path_ptr);
|
||||
} else {
|
||||
*path_ptr = '/';
|
||||
path_ptr++;
|
||||
path++;
|
||||
}
|
||||
|
||||
goto first_run;
|
||||
|
||||
for (;;) {
|
||||
switch (*path) {
|
||||
case '/':
|
||||
path++;
|
||||
first_run:
|
||||
if (*path == '/') continue;
|
||||
if ((!strncmp(path, ".\0", 2))
|
||||
|| (!strncmp(path, "./\0", 3))) {
|
||||
goto term;
|
||||
}
|
||||
if ((!strncmp(path, "..\0", 3))
|
||||
|| (!strncmp(path, "../\0", 4))) {
|
||||
while (*path_ptr != '/') path_ptr--;
|
||||
if (path_ptr == orig_ptr) path_ptr++;
|
||||
goto term;
|
||||
}
|
||||
if (!strncmp(path, "../", 3)) {
|
||||
while (*path_ptr != '/') path_ptr--;
|
||||
if (path_ptr == orig_ptr) path_ptr++;
|
||||
path += 2;
|
||||
*path_ptr = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(path, "./", 2)) {
|
||||
path += 1;
|
||||
continue;
|
||||
}
|
||||
if (((path_ptr - 1) != orig_ptr) && (*(path_ptr - 1) != '/')) {
|
||||
*path_ptr = '/';
|
||||
path_ptr++;
|
||||
}
|
||||
continue;
|
||||
case '\0':
|
||||
term:
|
||||
if ((*(path_ptr - 1) == '/') && ((path_ptr - 1) != orig_ptr))
|
||||
path_ptr--;
|
||||
*path_ptr = 0;
|
||||
return;
|
||||
default:
|
||||
*path_ptr = *path;
|
||||
path++;
|
||||
path_ptr++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdnoreturn.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/real.h>
|
||||
#include <lib/trace.h>
|
||||
#if defined (UEFI)
|
||||
# include <efi.h>
|
||||
#endif
|
||||
#include <lib/misc.h>
|
||||
#include <lib/readline.h>
|
||||
#include <lib/gterm.h>
|
||||
#include <lib/term.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <menu.h>
|
||||
|
||||
noreturn void panic(bool allow_menu, const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
quiet = false;
|
||||
|
||||
if (
|
||||
#if defined (BIOS)
|
||||
stage3_loaded == true &&
|
||||
#endif
|
||||
term_backend == _NOT_READY) {
|
||||
term_fallback();
|
||||
}
|
||||
|
||||
if (
|
||||
#if defined (BIOS)
|
||||
stage3_loaded == true &&
|
||||
#endif
|
||||
term_backend != FALLBACK) {
|
||||
print("\033[31mPANIC\033[37;1m\033[0m: ");
|
||||
} else {
|
||||
print("PANIC: ");
|
||||
}
|
||||
vprint(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
print("\n");
|
||||
print_stacktrace(NULL);
|
||||
|
||||
if (
|
||||
#if defined (BIOS)
|
||||
stage3_loaded == true &&
|
||||
#endif
|
||||
allow_menu == true) {
|
||||
print("Press a key to return to menu.");
|
||||
|
||||
getchar();
|
||||
|
||||
// This fixes a crash
|
||||
term_notready();
|
||||
|
||||
menu(false);
|
||||
/*
|
||||
fb_clear(&fbinfo);
|
||||
|
||||
// release all uefi memory and return to firmware
|
||||
pmm_release_uefi_mem();
|
||||
gBS->Exit(efi_image_handle, EFI_ABORTED, 0, NULL);
|
||||
*/
|
||||
} else {
|
||||
#if defined (BIOS)
|
||||
print("Press CTRL+ALT+DEL to reboot.");
|
||||
rm_hcf();
|
||||
#elif defined (UEFI)
|
||||
print("System halted.");
|
||||
for (;;) {
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
asm ("hlt");
|
||||
#elif defined (__aarch64__) || defined (__riscv64)
|
||||
asm ("wfi");
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#include <stddef.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/readline.h>
|
||||
|
||||
void list_volumes(void) {
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
struct volume *v = volume_index[i];
|
||||
print("index: %u\n", v->index);
|
||||
print("is_optical: %u\n", v->is_optical);
|
||||
print("partition: %u\n", v->partition);
|
||||
print("fslabel: %s\n", v->fslabel);
|
||||
print("sector_size: %u\n", v->sector_size);
|
||||
print("max_partition: %d\n", v->max_partition);
|
||||
print("first_sect: %U\n", v->first_sect);
|
||||
print("sect_count: %U\n", v->sect_count);
|
||||
if (i < volume_index_i - 1) {
|
||||
print("--- Press a key to continue ---\n");
|
||||
getchar();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef __LIB__PART_H__
|
||||
#define __LIB__PART_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/guid.h>
|
||||
#if defined (UEFI)
|
||||
# include <efi.h>
|
||||
# include <crypt/blake2b.h>
|
||||
#endif
|
||||
|
||||
#define NO_PARTITION (-1)
|
||||
#define INVALID_TABLE (-2)
|
||||
#define END_OF_TABLE (-3)
|
||||
|
||||
struct volume {
|
||||
#if defined (UEFI)
|
||||
EFI_HANDLE efi_handle;
|
||||
|
||||
// Block storage
|
||||
EFI_HANDLE efi_part_handle;
|
||||
EFI_BLOCK_IO *block_io;
|
||||
|
||||
// PXE
|
||||
EFI_PXE_BASE_CODE_PROTOCOL *pxe_base_code;
|
||||
|
||||
bool unique_sector_valid;
|
||||
uint64_t unique_sector;
|
||||
uint8_t unique_sector_b2b[BLAKE2B_OUT_BYTES];
|
||||
#elif defined (BIOS)
|
||||
int drive;
|
||||
#endif
|
||||
|
||||
size_t fastest_xfer_size;
|
||||
|
||||
int index;
|
||||
|
||||
bool is_optical;
|
||||
bool pxe;
|
||||
|
||||
int partition;
|
||||
int sector_size;
|
||||
struct volume *backing_dev;
|
||||
|
||||
int max_partition;
|
||||
|
||||
int cache_status;
|
||||
uint8_t *cache;
|
||||
uint64_t cached_block;
|
||||
|
||||
uint64_t first_sect;
|
||||
uint64_t sect_count;
|
||||
|
||||
bool guid_valid;
|
||||
struct guid guid;
|
||||
bool part_guid_valid;
|
||||
struct guid part_guid;
|
||||
bool fslabel_valid;
|
||||
char *fslabel;
|
||||
};
|
||||
|
||||
void list_volumes(void);
|
||||
bool is_valid_mbr(struct volume *volume);
|
||||
|
||||
extern struct volume **volume_index;
|
||||
extern size_t volume_index_i;
|
||||
|
||||
bool gpt_get_guid(struct guid *guid, struct volume *volume);
|
||||
uint32_t mbr_get_id(struct volume *volume);
|
||||
|
||||
int part_get(struct volume *part, struct volume *volume, int partition);
|
||||
|
||||
struct volume *volume_get_by_guid(struct guid *guid);
|
||||
struct volume *volume_get_by_fslabel(char *fslabel);
|
||||
struct volume *volume_get_by_coord(bool optical, int drive, int partition);
|
||||
#if defined (BIOS)
|
||||
struct volume *volume_get_by_bios_drive(int drive);
|
||||
#endif
|
||||
|
||||
bool volume_read(struct volume *part, void *buffer, uint64_t loc, uint64_t count);
|
||||
|
||||
#define volume_iterate_parts(_VOLUME_, _BODY_) do { \
|
||||
struct volume *_VOLUME = _VOLUME_; \
|
||||
if (_VOLUME->pxe) { \
|
||||
do { \
|
||||
struct volume *_PART = _VOLUME; \
|
||||
_BODY_ \
|
||||
} while (0); \
|
||||
} else { \
|
||||
while (_VOLUME->backing_dev != NULL) { \
|
||||
_VOLUME = _VOLUME->backing_dev; \
|
||||
} \
|
||||
\
|
||||
int _PART_CNT = -1; \
|
||||
for (size_t _PARTNO = 0; ; _PARTNO++) { \
|
||||
if (_PART_CNT > _VOLUME->max_partition) \
|
||||
break; \
|
||||
\
|
||||
struct volume *_PART = volume_get_by_coord(_VOLUME->is_optical, \
|
||||
_VOLUME->index, _PARTNO); \
|
||||
if (_PART == NULL) \
|
||||
continue; \
|
||||
\
|
||||
_PART_CNT++; \
|
||||
\
|
||||
_BODY_ \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,506 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <lib/part.h>
|
||||
#include <drivers/disk.h>
|
||||
#if defined (BIOS)
|
||||
# include <lib/real.h>
|
||||
#endif
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/print.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <fs/file.h>
|
||||
|
||||
enum {
|
||||
CACHE_NOT_READY = 0,
|
||||
CACHE_READY
|
||||
};
|
||||
|
||||
static bool cache_block(struct volume *volume, uint64_t block) {
|
||||
if (volume->cache_status == CACHE_READY && block == volume->cached_block)
|
||||
return true;
|
||||
|
||||
volume->cache_status = CACHE_NOT_READY;
|
||||
|
||||
if (volume->cache == NULL)
|
||||
volume->cache =
|
||||
ext_mem_alloc(volume->fastest_xfer_size * volume->sector_size);
|
||||
|
||||
if (volume->first_sect % (volume->sector_size / 512)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t first_sect = volume->first_sect / (volume->sector_size / 512);
|
||||
|
||||
uint64_t xfer_size = volume->fastest_xfer_size;
|
||||
|
||||
for (;;) {
|
||||
int ret = disk_read_sectors(volume, volume->cache,
|
||||
first_sect + block * volume->fastest_xfer_size,
|
||||
xfer_size);
|
||||
|
||||
switch (ret) {
|
||||
case DISK_NO_MEDIA:
|
||||
return false;
|
||||
case DISK_SUCCESS:
|
||||
goto disk_success;
|
||||
}
|
||||
|
||||
xfer_size--;
|
||||
if (xfer_size == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
disk_success:
|
||||
volume->cache_status = CACHE_READY;
|
||||
volume->cached_block = block;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t count) {
|
||||
if (volume->pxe) {
|
||||
panic(false, "Attempted volume_read() on pxe");
|
||||
}
|
||||
|
||||
uint64_t block_size = volume->fastest_xfer_size * volume->sector_size;
|
||||
|
||||
uint64_t progress = 0;
|
||||
while (progress < count) {
|
||||
uint64_t block = (loc + progress) / block_size;
|
||||
|
||||
if (!cache_block(volume, block))
|
||||
return false;
|
||||
|
||||
uint64_t chunk = count - progress;
|
||||
uint64_t offset = (loc + progress) % block_size;
|
||||
if (chunk > block_size - offset)
|
||||
chunk = block_size - offset;
|
||||
|
||||
memcpy(buffer + progress, &volume->cache[offset], chunk);
|
||||
progress += chunk;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct gpt_table_header {
|
||||
// the head
|
||||
char signature[8];
|
||||
uint32_t revision;
|
||||
uint32_t header_size;
|
||||
uint32_t crc32;
|
||||
uint32_t _reserved0;
|
||||
|
||||
// the partitioning info
|
||||
uint64_t my_lba;
|
||||
uint64_t alternate_lba;
|
||||
uint64_t first_usable_lba;
|
||||
uint64_t last_usable_lba;
|
||||
|
||||
// the guid
|
||||
struct guid disk_guid;
|
||||
|
||||
// entries related
|
||||
uint64_t partition_entry_lba;
|
||||
uint32_t number_of_partition_entries;
|
||||
uint32_t size_of_partition_entry;
|
||||
uint32_t partition_entry_array_crc32;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gpt_entry {
|
||||
struct guid partition_type_guid;
|
||||
|
||||
struct guid unique_partition_guid;
|
||||
|
||||
uint64_t starting_lba;
|
||||
uint64_t ending_lba;
|
||||
|
||||
uint64_t attributes;
|
||||
|
||||
uint16_t partition_name[36];
|
||||
} __attribute__((packed));
|
||||
|
||||
bool gpt_get_guid(struct guid *guid, struct volume *volume) {
|
||||
struct gpt_table_header header = {0};
|
||||
|
||||
int lb_guesses[] = {
|
||||
512,
|
||||
4096
|
||||
};
|
||||
int lb_size = -1;
|
||||
|
||||
for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
|
||||
// read header, located after the first block
|
||||
volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header));
|
||||
|
||||
// check the header
|
||||
// 'EFI PART'
|
||||
if (strncmp(header.signature, "EFI PART", 8))
|
||||
continue;
|
||||
|
||||
lb_size = lb_guesses[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (lb_size == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.revision != 0x00010000)
|
||||
return false;
|
||||
|
||||
*guid = header.disk_guid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int gpt_get_part(struct volume *ret, struct volume *volume, int partition) {
|
||||
struct gpt_table_header header = {0};
|
||||
|
||||
int lb_guesses[] = {
|
||||
512,
|
||||
4096
|
||||
};
|
||||
int lb_size = -1;
|
||||
|
||||
for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
|
||||
// read header, located after the first block
|
||||
volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header));
|
||||
|
||||
// check the header
|
||||
// 'EFI PART'
|
||||
if (strncmp(header.signature, "EFI PART", 8))
|
||||
continue;
|
||||
|
||||
lb_size = lb_guesses[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (lb_size == -1) {
|
||||
return INVALID_TABLE;
|
||||
}
|
||||
|
||||
if (header.revision != 0x00010000)
|
||||
return INVALID_TABLE;
|
||||
|
||||
// parse the entries if reached here
|
||||
if ((uint32_t)partition >= header.number_of_partition_entries)
|
||||
return END_OF_TABLE;
|
||||
|
||||
struct gpt_entry entry = {0};
|
||||
volume_read(volume, &entry,
|
||||
(header.partition_entry_lba * lb_size) + (partition * sizeof(entry)),
|
||||
sizeof(entry));
|
||||
|
||||
struct guid empty_guid = {0};
|
||||
if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid)))
|
||||
return NO_PARTITION;
|
||||
|
||||
#if defined (UEFI)
|
||||
ret->efi_handle = volume->efi_handle;
|
||||
ret->block_io = volume->block_io;
|
||||
#elif defined (BIOS)
|
||||
ret->drive = volume->drive;
|
||||
#endif
|
||||
ret->fastest_xfer_size = volume->fastest_xfer_size;
|
||||
ret->index = volume->index;
|
||||
ret->is_optical = volume->is_optical;
|
||||
ret->partition = partition + 1;
|
||||
ret->sector_size = volume->sector_size;
|
||||
ret->first_sect = entry.starting_lba * (lb_size / 512);
|
||||
ret->sect_count = ((entry.ending_lba - entry.starting_lba) + 1) * (lb_size / 512);
|
||||
ret->backing_dev = volume;
|
||||
|
||||
struct guid guid;
|
||||
if (!fs_get_guid(&guid, ret)) {
|
||||
ret->guid_valid = false;
|
||||
} else {
|
||||
ret->guid_valid = true;
|
||||
ret->guid = guid;
|
||||
}
|
||||
|
||||
char *fslabel = fs_get_label(ret);
|
||||
if (fslabel == NULL) {
|
||||
ret->fslabel_valid = false;
|
||||
} else {
|
||||
ret->fslabel_valid = true;
|
||||
ret->fslabel = fslabel;
|
||||
}
|
||||
|
||||
ret->part_guid_valid = true;
|
||||
ret->part_guid = entry.unique_partition_guid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mbr_entry {
|
||||
uint8_t status;
|
||||
uint8_t chs_first_sect[3];
|
||||
uint8_t type;
|
||||
uint8_t chs_last_sect[3];
|
||||
uint32_t first_sect;
|
||||
uint32_t sect_count;
|
||||
} __attribute__((packed));
|
||||
|
||||
bool is_valid_mbr(struct volume *volume) {
|
||||
// Check if actually valid mbr
|
||||
uint16_t hint = 0;
|
||||
|
||||
volume_read(volume, &hint, 446, sizeof(uint8_t));
|
||||
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
|
||||
return false;
|
||||
volume_read(volume, &hint, 462, sizeof(uint8_t));
|
||||
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
|
||||
return false;
|
||||
volume_read(volume, &hint, 478, sizeof(uint8_t));
|
||||
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
|
||||
return false;
|
||||
volume_read(volume, &hint, 494, sizeof(uint8_t));
|
||||
if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
|
||||
return false;
|
||||
|
||||
char hintc[64];
|
||||
volume_read(volume, hintc, 4, 8);
|
||||
if (memcmp(hintc, "_ECH_FS_", 8) == 0)
|
||||
return false;
|
||||
volume_read(volume, hintc, 3, 4);
|
||||
if (memcmp(hintc, "NTFS", 4) == 0)
|
||||
return false;
|
||||
volume_read(volume, hintc, 54, 3);
|
||||
if (memcmp(hintc, "FAT", 3) == 0)
|
||||
return false;
|
||||
volume_read(volume, hintc, 82, 3);
|
||||
if (memcmp(hintc, "FAT", 3) == 0)
|
||||
return false;
|
||||
volume_read(volume, hintc, 3, 5);
|
||||
if (memcmp(hintc, "FAT32", 5) == 0)
|
||||
return false;
|
||||
volume_read(volume, &hint, 1080, sizeof(uint16_t));
|
||||
if (hint == 0xef53)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t mbr_get_id(struct volume *volume) {
|
||||
if (!is_valid_mbr(volume)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ret;
|
||||
volume_read(volume, &ret, 0x1b8, sizeof(uint32_t));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part,
|
||||
int partition) {
|
||||
struct mbr_entry entry;
|
||||
|
||||
uint64_t ebr_sector = 0;
|
||||
|
||||
for (int i = 0; i < partition; i++) {
|
||||
uint64_t entry_offset = ebr_sector * 512 + 0x1ce;
|
||||
|
||||
volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry));
|
||||
|
||||
if (entry.type != 0x0f && entry.type != 0x05) {
|
||||
return END_OF_TABLE;
|
||||
}
|
||||
|
||||
ebr_sector = entry.first_sect;
|
||||
}
|
||||
|
||||
uint64_t entry_offset = ebr_sector * 512 + 0x1be;
|
||||
|
||||
volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry));
|
||||
|
||||
if (entry.type == 0)
|
||||
return NO_PARTITION;
|
||||
|
||||
#if defined (UEFI)
|
||||
ret->efi_handle = extended_part->efi_handle;
|
||||
ret->block_io = extended_part->block_io;
|
||||
#elif defined (BIOS)
|
||||
ret->drive = extended_part->drive;
|
||||
#endif
|
||||
ret->fastest_xfer_size = extended_part->fastest_xfer_size;
|
||||
ret->index = extended_part->index;
|
||||
ret->is_optical = extended_part->is_optical;
|
||||
ret->partition = partition + 4 + 1;
|
||||
ret->sector_size = extended_part->sector_size;
|
||||
ret->first_sect = extended_part->first_sect + ebr_sector + entry.first_sect;
|
||||
ret->sect_count = entry.sect_count;
|
||||
ret->backing_dev = extended_part->backing_dev;
|
||||
|
||||
struct guid guid;
|
||||
if (!fs_get_guid(&guid, ret)) {
|
||||
ret->guid_valid = false;
|
||||
} else {
|
||||
ret->guid_valid = true;
|
||||
ret->guid = guid;
|
||||
}
|
||||
|
||||
char *fslabel = fs_get_label(ret);
|
||||
if (fslabel == NULL) {
|
||||
ret->fslabel_valid = false;
|
||||
} else {
|
||||
ret->fslabel_valid = true;
|
||||
ret->fslabel = fslabel;
|
||||
}
|
||||
|
||||
ret->part_guid_valid = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mbr_get_part(struct volume *ret, struct volume *volume, int partition) {
|
||||
if (!is_valid_mbr(volume)) {
|
||||
return INVALID_TABLE;
|
||||
}
|
||||
|
||||
struct mbr_entry entry;
|
||||
|
||||
if (partition > 3) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * i;
|
||||
|
||||
volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry));
|
||||
|
||||
if (entry.type != 0x0f && entry.type != 0x05)
|
||||
continue;
|
||||
|
||||
struct volume extended_part = {0};
|
||||
|
||||
#if defined (UEFI)
|
||||
extended_part.efi_handle = volume->efi_handle;
|
||||
extended_part.block_io = volume->block_io;
|
||||
#elif defined (BIOS)
|
||||
extended_part.drive = volume->drive;
|
||||
#endif
|
||||
extended_part.fastest_xfer_size = volume->fastest_xfer_size;
|
||||
extended_part.index = volume->index;
|
||||
extended_part.is_optical = volume->is_optical;
|
||||
extended_part.partition = i + 1;
|
||||
extended_part.sector_size = volume->sector_size;
|
||||
extended_part.first_sect = entry.first_sect;
|
||||
extended_part.sect_count = entry.sect_count;
|
||||
extended_part.backing_dev = volume;
|
||||
|
||||
return mbr_get_logical_part(ret, &extended_part, partition - 4);
|
||||
}
|
||||
|
||||
return END_OF_TABLE;
|
||||
}
|
||||
|
||||
uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * partition;
|
||||
|
||||
volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry));
|
||||
|
||||
if (entry.type == 0)
|
||||
return NO_PARTITION;
|
||||
|
||||
#if defined (UEFI)
|
||||
ret->efi_handle = volume->efi_handle;
|
||||
ret->block_io = volume->block_io;
|
||||
#elif defined (BIOS)
|
||||
ret->drive = volume->drive;
|
||||
#endif
|
||||
ret->fastest_xfer_size = volume->fastest_xfer_size;
|
||||
ret->index = volume->index;
|
||||
ret->is_optical = volume->is_optical;
|
||||
ret->partition = partition + 1;
|
||||
ret->sector_size = volume->sector_size;
|
||||
ret->first_sect = entry.first_sect;
|
||||
ret->sect_count = entry.sect_count;
|
||||
ret->backing_dev = volume;
|
||||
|
||||
struct guid guid;
|
||||
if (!fs_get_guid(&guid, ret)) {
|
||||
ret->guid_valid = false;
|
||||
} else {
|
||||
ret->guid_valid = true;
|
||||
ret->guid = guid;
|
||||
}
|
||||
|
||||
char *fslabel = fs_get_label(ret);
|
||||
if (fslabel == NULL) {
|
||||
ret->fslabel_valid = false;
|
||||
} else {
|
||||
ret->fslabel_valid = true;
|
||||
ret->fslabel = fslabel;
|
||||
}
|
||||
|
||||
ret->part_guid_valid = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int part_get(struct volume *part, struct volume *volume, int partition) {
|
||||
int ret;
|
||||
|
||||
ret = gpt_get_part(part, volume, partition);
|
||||
if (ret != INVALID_TABLE)
|
||||
return ret;
|
||||
|
||||
ret = mbr_get_part(part, volume, partition);
|
||||
if (ret != INVALID_TABLE)
|
||||
return ret;
|
||||
|
||||
return INVALID_TABLE;
|
||||
}
|
||||
|
||||
struct volume **volume_index = NULL;
|
||||
size_t volume_index_i = 0;
|
||||
|
||||
struct volume *volume_get_by_guid(struct guid *guid) {
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
if (volume_index[i]->guid_valid
|
||||
&& memcmp(&volume_index[i]->guid, guid, 16) == 0) {
|
||||
return volume_index[i];
|
||||
}
|
||||
if (volume_index[i]->part_guid_valid
|
||||
&& memcmp(&volume_index[i]->part_guid, guid, 16) == 0) {
|
||||
return volume_index[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct volume *volume_get_by_fslabel(char *fslabel) {
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
if (volume_index[i]->fslabel_valid
|
||||
&& strcmp(volume_index[i]->fslabel, fslabel) == 0) {
|
||||
return volume_index[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct volume *volume_get_by_coord(bool optical, int drive, int partition) {
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
if (volume_index[i]->index == drive
|
||||
&& volume_index[i]->is_optical == optical
|
||||
&& volume_index[i]->partition == partition) {
|
||||
return volume_index[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined (BIOS)
|
||||
struct volume *volume_get_by_bios_drive(int drive) {
|
||||
for (size_t i = 0; i < volume_index_i; i++) {
|
||||
if (volume_index[i]->drive == drive) {
|
||||
return volume_index[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
|
@ -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
|
|
@ -0,0 +1,257 @@
|
|||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/term.h>
|
||||
#include <lib/libc.h>
|
||||
#if defined (BIOS)
|
||||
#include <lib/real.h>
|
||||
#endif
|
||||
#include <sys/cpu.h>
|
||||
#include <drivers/serial.h>
|
||||
|
||||
#if defined (BIOS)
|
||||
static void s2_print(const char *s, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
struct rm_regs r = {0};
|
||||
char c = s[i];
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
r.eax = 0x0e00 | '\r';
|
||||
rm_int(0x10, &r, &r);
|
||||
r = (struct rm_regs){0};
|
||||
r.eax = 0x0e00 | '\n';
|
||||
rm_int(0x10, &r, &r);
|
||||
break;
|
||||
default:
|
||||
r.eax = 0x0e00 | s[i];
|
||||
rm_int(0x10, &r, &r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *base_digits = "0123456789abcdef";
|
||||
|
||||
#define PRINT_BUF_MAX 4096
|
||||
|
||||
static void prn_str(char *print_buf, size_t *print_buf_i, const char *string) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; string[i]; i++) {
|
||||
if (*print_buf_i == (PRINT_BUF_MAX - 1))
|
||||
break;
|
||||
print_buf[(*print_buf_i)++] = string[i];
|
||||
}
|
||||
|
||||
print_buf[*print_buf_i] = 0;
|
||||
}
|
||||
|
||||
static void prn_nstr(char *print_buf, size_t *print_buf_i, const char *string, size_t len) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (*print_buf_i == (PRINT_BUF_MAX - 1))
|
||||
break;
|
||||
print_buf[(*print_buf_i)++] = string[i];
|
||||
}
|
||||
|
||||
print_buf[*print_buf_i] = 0;
|
||||
}
|
||||
|
||||
static void prn_char(char *print_buf, size_t *print_buf_i, char c) {
|
||||
if (*print_buf_i < (PRINT_BUF_MAX - 1)) {
|
||||
print_buf[(*print_buf_i)++] = c;
|
||||
}
|
||||
|
||||
print_buf[*print_buf_i] = 0;
|
||||
}
|
||||
|
||||
static void prn_i(char *print_buf, size_t *print_buf_i, int64_t x) {
|
||||
int i;
|
||||
char buf[20] = {0};
|
||||
|
||||
if (!x) {
|
||||
prn_char(print_buf, print_buf_i, '0');
|
||||
return;
|
||||
}
|
||||
|
||||
int sign = x < 0;
|
||||
if (sign) x = -x;
|
||||
|
||||
for (i = 18; x; i--) {
|
||||
buf[i] = (x % 10) + 0x30;
|
||||
x = x / 10;
|
||||
}
|
||||
if (sign)
|
||||
buf[i] = '-';
|
||||
else
|
||||
i++;
|
||||
|
||||
prn_str(print_buf, print_buf_i, buf + i);
|
||||
}
|
||||
|
||||
static void prn_ui(char *print_buf, size_t *print_buf_i, uint64_t x) {
|
||||
int i;
|
||||
char buf[21] = {0};
|
||||
|
||||
if (!x) {
|
||||
prn_char(print_buf, print_buf_i, '0');
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 19; x; i--) {
|
||||
buf[i] = (x % 10) + 0x30;
|
||||
x = x / 10;
|
||||
}
|
||||
|
||||
i++;
|
||||
prn_str(print_buf, print_buf_i, buf + i);
|
||||
}
|
||||
|
||||
static void prn_x(char *print_buf, size_t *print_buf_i, uint64_t x) {
|
||||
int i;
|
||||
char buf[17] = {0};
|
||||
|
||||
if (!x) {
|
||||
prn_str(print_buf, print_buf_i, "0x0");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 15; x; i--) {
|
||||
buf[i] = base_digits[(x % 16)];
|
||||
x = x / 16;
|
||||
}
|
||||
|
||||
i++;
|
||||
prn_str(print_buf, print_buf_i, "0x");
|
||||
prn_str(print_buf, print_buf_i, buf + i);
|
||||
}
|
||||
|
||||
void print(const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vprint(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static char print_buf[PRINT_BUF_MAX];
|
||||
|
||||
void vprint(const char *fmt, va_list args) {
|
||||
size_t print_buf_i = 0;
|
||||
|
||||
for (;;) {
|
||||
while (*fmt && *fmt != '%')
|
||||
prn_char(print_buf, &print_buf_i, *fmt++);
|
||||
|
||||
if (!*fmt++)
|
||||
goto out;
|
||||
|
||||
switch (*fmt++) {
|
||||
case 's': {
|
||||
char *str = (char *)va_arg(args, const char *);
|
||||
if (!str)
|
||||
prn_str(print_buf, &print_buf_i, "(null)");
|
||||
else
|
||||
prn_str(print_buf, &print_buf_i, str); }
|
||||
break;
|
||||
case 'S': {
|
||||
char *str = (char *)va_arg(args, const char *);
|
||||
size_t str_len = va_arg(args, size_t);
|
||||
if (!str)
|
||||
prn_str(print_buf, &print_buf_i, "(null)");
|
||||
else
|
||||
prn_nstr(print_buf, &print_buf_i, str, str_len); }
|
||||
break;
|
||||
case 'd':
|
||||
prn_i(print_buf, &print_buf_i, (int64_t)va_arg(args, int32_t));
|
||||
break;
|
||||
case 'u':
|
||||
prn_ui(print_buf, &print_buf_i, (uint64_t)va_arg(args, uint32_t));
|
||||
break;
|
||||
case 'x':
|
||||
prn_x(print_buf, &print_buf_i, (uint64_t)va_arg(args, uint32_t));
|
||||
break;
|
||||
case 'D':
|
||||
prn_i(print_buf, &print_buf_i, va_arg(args, int64_t));
|
||||
break;
|
||||
case 'U':
|
||||
prn_ui(print_buf, &print_buf_i, va_arg(args, uint64_t));
|
||||
break;
|
||||
case 'X':
|
||||
prn_x(print_buf, &print_buf_i, va_arg(args, uint64_t));
|
||||
break;
|
||||
case 'p':
|
||||
prn_x(print_buf, &print_buf_i, va_arg(args, uintptr_t));
|
||||
break;
|
||||
case 'c': {
|
||||
char c = (char)va_arg(args, int);
|
||||
prn_char(print_buf, &print_buf_i, c); }
|
||||
break;
|
||||
case '#': {
|
||||
bool printed = false;
|
||||
char *str = (char *)va_arg(args, const char *);
|
||||
for (int i = (int)strlen(str) - 1; i >= 0; i--) {
|
||||
if (str[i] != '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
prn_nstr(print_buf, &print_buf_i, str, i);
|
||||
printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!printed) {
|
||||
prn_str(print_buf, &print_buf_i, str);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
prn_char(print_buf, &print_buf_i, '?');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (!quiet) {
|
||||
#if defined (BIOS)
|
||||
if (stage3_loaded) {
|
||||
#endif
|
||||
FOR_TERM(flanterm_write(TERM, print_buf, print_buf_i));
|
||||
#if defined (BIOS)
|
||||
} else {
|
||||
s2_print(print_buf, print_buf_i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < print_buf_i; i++) {
|
||||
#if defined (__x86_64__) || defined (__i386__)
|
||||
if (E9_OUTPUT) {
|
||||
outb(0xe9, print_buf[i]);
|
||||
}
|
||||
#endif
|
||||
#if defined (BIOS)
|
||||
if ((!quiet && serial) || COM_OUTPUT) {
|
||||
switch (print_buf[i]) {
|
||||
case '\n':
|
||||
serial_out('\r');
|
||||
serial_out('\n');
|
||||
continue;
|
||||
case '\e':
|
||||
serial_out('\e');
|
||||
continue;
|
||||
}
|
||||
if (!isprint(print_buf[i])) {
|
||||
continue;
|
||||
}
|
||||
serial_out(print_buf[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,479 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <lib/readline.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/term.h>
|
||||
#include <lib/print.h>
|
||||
#if defined (BIOS)
|
||||
# include <lib/real.h>
|
||||
#elif defined (UEFI)
|
||||
# include <efi.h>
|
||||
#endif
|
||||
#include <drivers/serial.h>
|
||||
#include <sys/cpu.h>
|
||||
|
||||
int getchar(void) {
|
||||
for (;;) {
|
||||
int ret = pit_sleep_and_quit_on_keypress(65535);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) {
|
||||
switch (scancode) {
|
||||
#if defined (BIOS)
|
||||
case 0x44:
|
||||
return GETCHAR_F10;
|
||||
case 0x4b:
|
||||
return GETCHAR_CURSOR_LEFT;
|
||||
case 0x4d:
|
||||
return GETCHAR_CURSOR_RIGHT;
|
||||
case 0x48:
|
||||
return GETCHAR_CURSOR_UP;
|
||||
case 0x50:
|
||||
return GETCHAR_CURSOR_DOWN;
|
||||
case 0x53:
|
||||
return GETCHAR_DELETE;
|
||||
case 0x4f:
|
||||
return GETCHAR_END;
|
||||
case 0x47:
|
||||
return GETCHAR_HOME;
|
||||
case 0x49:
|
||||
return GETCHAR_PGUP;
|
||||
case 0x51:
|
||||
return GETCHAR_PGDOWN;
|
||||
case 0x01:
|
||||
return GETCHAR_ESCAPE;
|
||||
#elif defined (UEFI)
|
||||
case SCAN_F10:
|
||||
return GETCHAR_F10;
|
||||
case SCAN_LEFT:
|
||||
return GETCHAR_CURSOR_LEFT;
|
||||
case SCAN_RIGHT:
|
||||
return GETCHAR_CURSOR_RIGHT;
|
||||
case SCAN_UP:
|
||||
return GETCHAR_CURSOR_UP;
|
||||
case SCAN_DOWN:
|
||||
return GETCHAR_CURSOR_DOWN;
|
||||
case SCAN_DELETE:
|
||||
return GETCHAR_DELETE;
|
||||
case SCAN_END:
|
||||
return GETCHAR_END;
|
||||
case SCAN_HOME:
|
||||
return GETCHAR_HOME;
|
||||
case SCAN_PAGE_UP:
|
||||
return GETCHAR_PGUP;
|
||||
case SCAN_PAGE_DOWN:
|
||||
return GETCHAR_PGDOWN;
|
||||
case SCAN_ESC:
|
||||
return GETCHAR_ESCAPE;
|
||||
#endif
|
||||
}
|
||||
switch (ascii) {
|
||||
case '\r':
|
||||
return '\n';
|
||||
case '\b':
|
||||
return '\b';
|
||||
}
|
||||
|
||||
if (shift_state & (GETCHAR_LCTRL | GETCHAR_RCTRL)) {
|
||||
switch (ascii) {
|
||||
case 'a': return GETCHAR_HOME;
|
||||
case 'e': return GETCHAR_END;
|
||||
case 'p': return GETCHAR_CURSOR_UP;
|
||||
case 'n': return GETCHAR_CURSOR_DOWN;
|
||||
case 'b': return GETCHAR_CURSOR_LEFT;
|
||||
case 'f': return GETCHAR_CURSOR_RIGHT;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Guard against non-printable values
|
||||
if (ascii < 0x20 || ascii > 0x7e) {
|
||||
return -1;
|
||||
}
|
||||
return ascii;
|
||||
}
|
||||
|
||||
#if defined (BIOS)
|
||||
int _pit_sleep_and_quit_on_keypress(uint32_t ticks);
|
||||
|
||||
static int input_sequence(void) {
|
||||
int val = 0;
|
||||
|
||||
for (;;) {
|
||||
int ret = -1;
|
||||
size_t retries = 0;
|
||||
|
||||
while (ret == -1 && retries < 1000000) {
|
||||
ret = serial_in();
|
||||
retries++;
|
||||
}
|
||||
if (ret == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case 'A':
|
||||
return GETCHAR_CURSOR_UP;
|
||||
case 'B':
|
||||
return GETCHAR_CURSOR_DOWN;
|
||||
case 'C':
|
||||
return GETCHAR_CURSOR_RIGHT;
|
||||
case 'D':
|
||||
return GETCHAR_CURSOR_LEFT;
|
||||
case 'F':
|
||||
return GETCHAR_END;
|
||||
case 'H':
|
||||
return GETCHAR_HOME;
|
||||
}
|
||||
|
||||
if (ret > '9' || ret < '0') {
|
||||
break;
|
||||
}
|
||||
|
||||
val *= 10;
|
||||
val += ret - '0';
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case 3:
|
||||
return GETCHAR_DELETE;
|
||||
case 5:
|
||||
return GETCHAR_PGUP;
|
||||
case 6:
|
||||
return GETCHAR_PGDOWN;
|
||||
case 21:
|
||||
return GETCHAR_F10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pit_sleep_and_quit_on_keypress(int seconds) {
|
||||
if (!serial) {
|
||||
return _pit_sleep_and_quit_on_keypress(seconds * 18);
|
||||
}
|
||||
|
||||
for (int i = 0; i < seconds * 18; i++) {
|
||||
int ret = _pit_sleep_and_quit_on_keypress(1);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = serial_in();
|
||||
|
||||
if (ret != -1) {
|
||||
again:
|
||||
switch (ret) {
|
||||
case '\r':
|
||||
return '\n';
|
||||
case 0x1b:
|
||||
delay(10000);
|
||||
ret = serial_in();
|
||||
if (ret == -1) {
|
||||
return GETCHAR_ESCAPE;
|
||||
}
|
||||
if (ret == '[') {
|
||||
return input_sequence();
|
||||
}
|
||||
goto again;
|
||||
case 0x7f:
|
||||
return '\b';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (UEFI)
|
||||
static int input_sequence(bool ext,
|
||||
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto,
|
||||
EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto) {
|
||||
EFI_STATUS status;
|
||||
EFI_KEY_DATA kd;
|
||||
|
||||
int val = 0;
|
||||
|
||||
for (;;) {
|
||||
if (ext == false) {
|
||||
status = sproto->ReadKeyStroke(sproto, &kd.Key);
|
||||
} else {
|
||||
status = exproto->ReadKeyStrokeEx(exproto, &kd);
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (kd.Key.UnicodeChar) {
|
||||
case 'A':
|
||||
return GETCHAR_CURSOR_UP;
|
||||
case 'B':
|
||||
return GETCHAR_CURSOR_DOWN;
|
||||
case 'C':
|
||||
return GETCHAR_CURSOR_RIGHT;
|
||||
case 'D':
|
||||
return GETCHAR_CURSOR_LEFT;
|
||||
case 'F':
|
||||
return GETCHAR_END;
|
||||
case 'H':
|
||||
return GETCHAR_HOME;
|
||||
}
|
||||
|
||||
if (kd.Key.UnicodeChar > '9' || kd.Key.UnicodeChar < '0') {
|
||||
break;
|
||||
}
|
||||
|
||||
val *= 10;
|
||||
val += kd.Key.UnicodeChar - '0';
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case 3:
|
||||
return GETCHAR_DELETE;
|
||||
case 5:
|
||||
return GETCHAR_PGUP;
|
||||
case 6:
|
||||
return GETCHAR_PGDOWN;
|
||||
case 21:
|
||||
return GETCHAR_F10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pit_sleep_and_quit_on_keypress(int seconds) {
|
||||
EFI_KEY_DATA kd;
|
||||
|
||||
UINTN which;
|
||||
|
||||
EFI_EVENT events[2];
|
||||
|
||||
EFI_GUID exproto_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
|
||||
EFI_GUID sproto_guid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
|
||||
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto = NULL;
|
||||
EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto = NULL;
|
||||
|
||||
bool use_sproto = false;
|
||||
|
||||
if (gBS->HandleProtocol(gST->ConsoleInHandle, &exproto_guid, (void **)&exproto) != EFI_SUCCESS) {
|
||||
if (gBS->HandleProtocol(gST->ConsoleInHandle, &sproto_guid, (void **)&sproto) != EFI_SUCCESS) {
|
||||
if (gST->ConIn != NULL) {
|
||||
sproto = gST->ConIn;
|
||||
} else {
|
||||
panic(false, "Your input device doesn't have an input protocol!");
|
||||
}
|
||||
}
|
||||
|
||||
events[0] = sproto->WaitForKey;
|
||||
|
||||
use_sproto = true;
|
||||
} else {
|
||||
events[0] = exproto->WaitForKeyEx;
|
||||
}
|
||||
|
||||
gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
|
||||
|
||||
gBS->SetTimer(events[1], TimerRelative, 10000000 * seconds);
|
||||
|
||||
again:
|
||||
memset(&kd, 0, sizeof(EFI_KEY_DATA));
|
||||
|
||||
gBS->WaitForEvent(2, events, &which);
|
||||
|
||||
if (which == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EFI_STATUS status;
|
||||
if (use_sproto) {
|
||||
status = sproto->ReadKeyStroke(sproto, &kd.Key);
|
||||
} else {
|
||||
status = exproto->ReadKeyStrokeEx(exproto, &kd);
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
if ((kd.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) {
|
||||
kd.KeyState.KeyShiftState = 0;
|
||||
}
|
||||
|
||||
if (kd.Key.ScanCode == 0x08) {
|
||||
return '\b';
|
||||
}
|
||||
|
||||
if (kd.Key.ScanCode == SCAN_ESC) {
|
||||
gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
|
||||
|
||||
gBS->SetTimer(events[1], TimerRelative, 100000);
|
||||
|
||||
gBS->WaitForEvent(2, events, &which);
|
||||
|
||||
if (which == 1) {
|
||||
return GETCHAR_ESCAPE;
|
||||
}
|
||||
|
||||
if (use_sproto) {
|
||||
status = sproto->ReadKeyStroke(sproto, &kd.Key);
|
||||
} else {
|
||||
status = exproto->ReadKeyStrokeEx(exproto, &kd);
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (kd.Key.UnicodeChar == '[') {
|
||||
return input_sequence(!use_sproto, exproto, sproto);
|
||||
}
|
||||
}
|
||||
|
||||
int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar,
|
||||
kd.KeyState.KeyShiftState);
|
||||
|
||||
if (ret == -1) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void reprint_string(int x, int y, const char *s) {
|
||||
size_t orig_x, orig_y;
|
||||
FOR_TERM(TERM->cursor_enabled = false);
|
||||
terms[0]->get_cursor_pos(terms[0], &orig_x, &orig_y);
|
||||
set_cursor_pos_helper(x, y);
|
||||
print("%s", s);
|
||||
set_cursor_pos_helper(orig_x, orig_y);
|
||||
FOR_TERM(TERM->cursor_enabled = true);
|
||||
}
|
||||
|
||||
static void cursor_back(void) {
|
||||
size_t x, y;
|
||||
terms[0]->get_cursor_pos(terms[0], &x, &y);
|
||||
if (x) {
|
||||
x--;
|
||||
} else if (y) {
|
||||
y--;
|
||||
x = terms[0]->cols - 1;
|
||||
}
|
||||
set_cursor_pos_helper(x, y);
|
||||
}
|
||||
|
||||
static void cursor_fwd(void) {
|
||||
size_t x, y;
|
||||
terms[0]->get_cursor_pos(terms[0], &x, &y);
|
||||
if (x < terms[0]->cols - 1) {
|
||||
x++;
|
||||
} else {
|
||||
x = 0;
|
||||
if (y < terms[0]->rows - 1) {
|
||||
y++;
|
||||
}
|
||||
}
|
||||
set_cursor_pos_helper(x, y);
|
||||
}
|
||||
|
||||
void readline(const char *orig_str, char *buf, size_t limit) {
|
||||
bool prev_autoflush = terms[0]->autoflush;
|
||||
FOR_TERM(TERM->autoflush = false);
|
||||
|
||||
size_t orig_str_len = strlen(orig_str);
|
||||
memmove(buf, orig_str, orig_str_len);
|
||||
buf[orig_str_len] = 0;
|
||||
|
||||
size_t orig_x, orig_y;
|
||||
terms[0]->get_cursor_pos(terms[0], &orig_x, &orig_y);
|
||||
|
||||
print("%s", orig_str);
|
||||
|
||||
for (size_t i = orig_str_len; ; ) {
|
||||
FOR_TERM(TERM->double_buffer_flush(TERM));
|
||||
int c = getchar();
|
||||
switch (c) {
|
||||
case GETCHAR_CURSOR_LEFT:
|
||||
if (i) {
|
||||
i--;
|
||||
cursor_back();
|
||||
}
|
||||
continue;
|
||||
case GETCHAR_CURSOR_RIGHT:
|
||||
if (i < strlen(buf)) {
|
||||
i++;
|
||||
cursor_fwd();
|
||||
}
|
||||
continue;
|
||||
case '\b':
|
||||
if (i) {
|
||||
i--;
|
||||
cursor_back();
|
||||
case GETCHAR_DELETE:;
|
||||
size_t j;
|
||||
if (buf[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
for (j = i; ; j++) {
|
||||
buf[j] = buf[j+1];
|
||||
if (!buf[j]) {
|
||||
buf[j] = ' ';
|
||||
break;
|
||||
}
|
||||
}
|
||||
reprint_string(orig_x, orig_y, buf);
|
||||
buf[j] = 0;
|
||||
}
|
||||
continue;
|
||||
case '\n':
|
||||
print("\n");
|
||||
goto out;
|
||||
case GETCHAR_END:
|
||||
for (size_t j = 0; j < strlen(buf) - i; j++) {
|
||||
cursor_fwd();
|
||||
}
|
||||
i = strlen(buf);
|
||||
continue;
|
||||
case GETCHAR_HOME:
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
cursor_back();
|
||||
}
|
||||
i = 0;
|
||||
continue;
|
||||
default: {
|
||||
if (strlen(buf) < limit - 1 && isprint(c)) {
|
||||
for (size_t j = strlen(buf); ; j--) {
|
||||
buf[j+1] = buf[j];
|
||||
if (j == i)
|
||||
break;
|
||||
}
|
||||
buf[i] = c;
|
||||
i++;
|
||||
size_t prev_x, prev_y;
|
||||
terms[0]->get_cursor_pos(terms[0], &prev_x, &prev_y);
|
||||
cursor_fwd();
|
||||
reprint_string(orig_x, orig_y, buf);
|
||||
// If cursor has wrapped around, move the line start position up one row
|
||||
if (prev_x == terms[0]->cols - 1 && prev_y == terms[0]->rows - 1) {
|
||||
orig_y--;
|
||||
print("\n\e[J"); // Clear the bottom line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
FOR_TERM(TERM->double_buffer_flush(TERM));
|
||||
FOR_TERM(TERM->autoflush = prev_autoflush);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef __LIB__READLINE_H__
|
||||
#define __LIB__READLINE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define GETCHAR_CURSOR_LEFT (-10)
|
||||
#define GETCHAR_CURSOR_RIGHT (-11)
|
||||
#define GETCHAR_CURSOR_UP (-12)
|
||||
#define GETCHAR_CURSOR_DOWN (-13)
|
||||
#define GETCHAR_DELETE (-14)
|
||||
#define GETCHAR_END (-15)
|
||||
#define GETCHAR_HOME (-16)
|
||||
#define GETCHAR_PGUP (-17)
|
||||
#define GETCHAR_PGDOWN (-18)
|
||||
#define GETCHAR_F10 (-19)
|
||||
#define GETCHAR_ESCAPE (-20)
|
||||
|
||||
#if defined (BIOS)
|
||||
# define GETCHAR_RCTRL 0x4
|
||||
# define GETCHAR_LCTRL GETCHAR_RCTRL
|
||||
#elif defined (UEFI)
|
||||
# define GETCHAR_RCTRL EFI_RIGHT_CONTROL_PRESSED
|
||||
# define GETCHAR_LCTRL EFI_LEFT_CONTROL_PRESSED
|
||||
#endif
|
||||
|
||||
int getchar(void);
|
||||
void readline(const char *orig_str, char *buf, size_t limit);
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,65 @@
|
|||
#include <lib/macros.aarch64_asm.h>
|
||||
|
||||
.section .text
|
||||
|
||||
// noreturn void enter_in_el1(uint64_t entry, uint64_t sp, uint64_t sctlr,
|
||||
// uint64_t mair, uint64_t tcr, uint64_t ttbr0,
|
||||
// uint64_t ttbr1, uint64_t target_x0)
|
||||
// Potentially drop to EL1 from EL2 (and also disable trapping to EL2), then
|
||||
// configure EL1 state and jump to kernel.
|
||||
|
||||
.global enter_in_el1
|
||||
enter_in_el1:
|
||||
msr spsel, #0
|
||||
mov sp, x1
|
||||
|
||||
// Configure EL1 state
|
||||
msr mair_el1, x3
|
||||
msr tcr_el1, x4
|
||||
msr ttbr0_el1, x5
|
||||
msr ttbr1_el1, x6
|
||||
msr sctlr_el1, x2
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
PICK_EL x8, 0f, 1f
|
||||
0:
|
||||
// Enter kernel in EL1
|
||||
mov x8, #0x3c4
|
||||
msr spsr_el1, x8
|
||||
msr elr_el1, x0
|
||||
|
||||
mov x0, x7
|
||||
ZERO_REGS_EXCEPT_X0
|
||||
|
||||
eret
|
||||
|
||||
1:
|
||||
// Configure EL2-specific state for EL1
|
||||
|
||||
// Don't trap counters to EL2
|
||||
mrs x8, cnthctl_el2
|
||||
orr x8, x8, #3
|
||||
msr cnthctl_el2, x8
|
||||
msr cntvoff_el2, xzr
|
||||
|
||||
// Enable AArch64 in EL1
|
||||
ldr x8, =0x80000002
|
||||
msr hcr_el2, x8
|
||||
|
||||
// Don't trap FP/SIMD to EL2
|
||||
mov x8, #0x33FF
|
||||
msr cptr_el2, x8
|
||||
msr hstr_el2, xzr
|
||||
|
||||
// Enter kernel in EL1
|
||||
mov x8, #0x3c4
|
||||
msr spsr_el2, x8
|
||||
msr elr_el2, x0
|
||||
|
||||
mov x0, x7
|
||||
ZERO_REGS_EXCEPT_X0
|
||||
|
||||
eret
|
||||
|
||||
.section .note.GNU-stack,"",%progbits
|
|
@ -0,0 +1,39 @@
|
|||
section .rodata
|
||||
|
||||
invalid_idt:
|
||||
dd 0, 0
|
||||
|
||||
section .text
|
||||
|
||||
extern flush_irqs
|
||||
|
||||
global common_spinup
|
||||
bits 32
|
||||
common_spinup:
|
||||
cli
|
||||
|
||||
lidt [invalid_idt]
|
||||
|
||||
call flush_irqs
|
||||
|
||||
xor eax, eax
|
||||
lldt ax
|
||||
|
||||
; We don't need the return address
|
||||
add esp, 4
|
||||
|
||||
; Get function address
|
||||
pop edi
|
||||
|
||||
; We don't need the argument count
|
||||
add esp, 4
|
||||
|
||||
mov eax, 0x00000011
|
||||
mov cr0, eax
|
||||
|
||||
xor eax, eax
|
||||
mov cr4, eax
|
||||
|
||||
call edi
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
.section .text
|
||||
|
||||
.global riscv_spinup
|
||||
riscv_spinup:
|
||||
.option norelax
|
||||
csrci sstatus, 0x2
|
||||
csrw sie, zero
|
||||
csrw stvec, zero
|
||||
|
||||
mv t0, a0
|
||||
mv sp, a1
|
||||
csrw satp, a2
|
||||
|
||||
mv a0, zero
|
||||
mv a1, zero
|
||||
mv a2, zero
|
||||
mv a3, zero
|
||||
mv a4, zero
|
||||
mv a5, zero
|
||||
mv a6, zero
|
||||
mv a7, zero
|
||||
mv s0, zero
|
||||
mv s1, zero
|
||||
mv s2, zero
|
||||
mv s3, zero
|
||||
mv s4, zero
|
||||
mv s5, zero
|
||||
mv s6, zero
|
||||
mv s7, zero
|
||||
mv s8, zero
|
||||
mv s9, zero
|
||||
mv s10, zero
|
||||
mv s11, zero
|
||||
mv t1, zero
|
||||
mv t2, zero
|
||||
mv t3, zero
|
||||
mv t4, zero
|
||||
mv t5, zero
|
||||
mv t6, zero
|
||||
mv tp, zero
|
||||
mv gp, zero
|
||||
mv ra, zero
|
||||
|
||||
jr t0
|
||||
|
||||
.section .note.GNU-stack,"",%progbits
|
|
@ -0,0 +1,64 @@
|
|||
extern _GLOBAL_OFFSET_TABLE_
|
||||
|
||||
extern gdt
|
||||
|
||||
section .text
|
||||
|
||||
extern flush_irqs
|
||||
|
||||
global common_spinup
|
||||
bits 32
|
||||
common_spinup:
|
||||
cli
|
||||
|
||||
push 0
|
||||
push 0
|
||||
lidt [esp]
|
||||
add esp, 8
|
||||
|
||||
call .get_got
|
||||
.get_got:
|
||||
pop ebx
|
||||
add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_got wrt ..gotpc
|
||||
|
||||
lgdt [ebx + gdt wrt ..gotoff]
|
||||
|
||||
push dword 0x18
|
||||
call .p1
|
||||
.p1:
|
||||
pop eax
|
||||
add eax, 6
|
||||
push eax
|
||||
retfd
|
||||
|
||||
.flush_cs:
|
||||
mov eax, 0x20
|
||||
mov ds, eax
|
||||
mov es, eax
|
||||
mov fs, eax
|
||||
mov gs, eax
|
||||
mov ss, eax
|
||||
|
||||
call flush_irqs
|
||||
|
||||
xor eax, eax
|
||||
lldt ax
|
||||
|
||||
; We don't need the return address
|
||||
add esp, 4
|
||||
|
||||
; Get function address
|
||||
pop edi
|
||||
|
||||
; We don't need the argument count
|
||||
add esp, 4
|
||||
|
||||
mov eax, 0x00000011
|
||||
mov cr0, eax
|
||||
|
||||
xor eax, eax
|
||||
mov cr4, eax
|
||||
|
||||
call edi
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
@ -0,0 +1,97 @@
|
|||
section .rodata
|
||||
|
||||
invalid_idt:
|
||||
dq 0, 0
|
||||
|
||||
section .text
|
||||
|
||||
extern flush_irqs
|
||||
|
||||
%macro push32 1
|
||||
sub rsp, 4
|
||||
mov dword [rsp], %1
|
||||
%endmacro
|
||||
|
||||
extern gdt
|
||||
|
||||
global common_spinup
|
||||
bits 64
|
||||
common_spinup:
|
||||
cli
|
||||
|
||||
lgdt [rel gdt]
|
||||
lidt [rel invalid_idt]
|
||||
|
||||
lea rbx, [rel .reload_cs]
|
||||
|
||||
push 0x28
|
||||
push rbx
|
||||
retfq
|
||||
.reload_cs:
|
||||
mov eax, 0x30
|
||||
mov ds, eax
|
||||
mov es, eax
|
||||
mov fs, eax
|
||||
mov gs, eax
|
||||
mov ss, eax
|
||||
|
||||
push r8
|
||||
push r9
|
||||
push rcx
|
||||
push rdx
|
||||
call flush_irqs
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r9
|
||||
pop r8
|
||||
|
||||
mov rbp, rsp
|
||||
|
||||
cmp esi, 4
|
||||
jle .no_stack_args
|
||||
|
||||
.push_stack_args:
|
||||
dec esi
|
||||
mov eax, [rbp + 8 + rsi*8]
|
||||
push32 eax
|
||||
test esi, esi
|
||||
jnz .push_stack_args
|
||||
|
||||
.no_stack_args:
|
||||
push32 r9d
|
||||
push32 r8d
|
||||
push32 ecx
|
||||
push32 edx
|
||||
|
||||
lea rbx, [rel .go_32]
|
||||
|
||||
push 0x18
|
||||
push rbx
|
||||
retfq
|
||||
|
||||
bits 32
|
||||
.go_32:
|
||||
mov eax, 0x20
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
xor eax, eax
|
||||
lldt ax
|
||||
|
||||
mov eax, 0x00000011
|
||||
mov cr0, eax
|
||||
|
||||
mov ecx, 0xc0000080
|
||||
xor eax, eax
|
||||
xor edx, edx
|
||||
wrmsr
|
||||
|
||||
xor eax, eax
|
||||
mov cr4, eax
|
||||
|
||||
call edi
|
||||
|
||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
@ -0,0 +1,387 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <lib/term.h>
|
||||
#include <lib/real.h>
|
||||
#include <lib/misc.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <drivers/vga_textmode.h>
|
||||
#include <flanterm/backends/fb.h>
|
||||
|
||||
#if defined (BIOS)
|
||||
int current_video_mode = -1;
|
||||
#endif
|
||||
|
||||
struct flanterm_context **terms = NULL;
|
||||
size_t terms_i = 0;
|
||||
|
||||
int term_backend = _NOT_READY;
|
||||
|
||||
void term_notready(void) {
|
||||
for (size_t i = 0; i < terms_i; i++) {
|
||||
struct flanterm_context *term = terms[i];
|
||||
|
||||
term->deinit(term, pmm_free);
|
||||
}
|
||||
|
||||
pmm_free(terms, terms_i * sizeof(void *));
|
||||
|
||||
terms_i = 0;
|
||||
terms = NULL;
|
||||
|
||||
term_backend = _NOT_READY;
|
||||
}
|
||||
|
||||
// --- fallback ---
|
||||
|
||||
#if defined (BIOS)
|
||||
static void fallback_raw_putchar(struct flanterm_context *ctx, uint8_t c) {
|
||||
(void)ctx;
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x0e00 | c;
|
||||
rm_int(0x10, &r, &r);
|
||||
}
|
||||
|
||||
static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y);
|
||||
static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y);
|
||||
|
||||
static void fallback_clear(struct flanterm_context *ctx, bool move) {
|
||||
(void)ctx;
|
||||
size_t x, y;
|
||||
fallback_get_cursor_pos(NULL, &x, &y);
|
||||
struct rm_regs r = {0};
|
||||
rm_int(0x11, &r, &r);
|
||||
switch ((r.eax >> 4) & 3) {
|
||||
case 0:
|
||||
r.eax = 3;
|
||||
break;
|
||||
case 1:
|
||||
r.eax = 1;
|
||||
break;
|
||||
case 2:
|
||||
r.eax = 3;
|
||||
break;
|
||||
case 3:
|
||||
r.eax = 7;
|
||||
break;
|
||||
}
|
||||
rm_int(0x10, &r, &r);
|
||||
if (move) {
|
||||
x = y = 0;
|
||||
}
|
||||
fallback_set_cursor_pos(NULL, x, y);
|
||||
}
|
||||
|
||||
static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) {
|
||||
(void)ctx;
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x0200;
|
||||
r.ebx = 0;
|
||||
r.edx = (y << 8) + x;
|
||||
rm_int(0x10, &r, &r);
|
||||
}
|
||||
|
||||
static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) {
|
||||
(void)ctx;
|
||||
struct rm_regs r = {0};
|
||||
r.eax = 0x0300;
|
||||
r.ebx = 0;
|
||||
rm_int(0x10, &r, &r);
|
||||
*x = r.edx & 0xff;
|
||||
*y = r.edx >> 8;
|
||||
}
|
||||
|
||||
static void fallback_scroll(struct flanterm_context *ctx) {
|
||||
(void)ctx;
|
||||
size_t x, y;
|
||||
fallback_get_cursor_pos(NULL, &x, &y);
|
||||
fallback_set_cursor_pos(NULL, ctx->cols - 1, ctx->rows - 1);
|
||||
fallback_raw_putchar(NULL, ' ');
|
||||
fallback_set_cursor_pos(NULL, x, y);
|
||||
}
|
||||
|
||||
#elif defined (UEFI)
|
||||
|
||||
static size_t cursor_x = 0, cursor_y = 0;
|
||||
|
||||
static void fallback_scroll(struct flanterm_context *ctx) {
|
||||
(void)ctx;
|
||||
gST->ConOut->SetCursorPosition(gST->ConOut, ctx->cols - 1, ctx->rows - 1);
|
||||
CHAR16 string[2];
|
||||
string[0] = ' ';
|
||||
string[1] = 0;
|
||||
gST->ConOut->OutputString(gST->ConOut, string);
|
||||
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
|
||||
}
|
||||
|
||||
static void fallback_raw_putchar(struct flanterm_context *ctx, uint8_t c) {
|
||||
if (!ctx->scroll_enabled && cursor_x == ctx->cols - 1 && cursor_y == ctx->rows - 1) {
|
||||
return;
|
||||
}
|
||||
gST->ConOut->EnableCursor(gST->ConOut, true);
|
||||
CHAR16 string[2];
|
||||
string[0] = c;
|
||||
string[1] = 0;
|
||||
gST->ConOut->OutputString(gST->ConOut, string);
|
||||
if (++cursor_x >= ctx->cols) {
|
||||
cursor_x = 0;
|
||||
if (++cursor_y >= ctx->rows) {
|
||||
cursor_y--;
|
||||
}
|
||||
}
|
||||
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
|
||||
}
|
||||
|
||||
static void fallback_clear(struct flanterm_context *ctx, bool move) {
|
||||
(void)ctx;
|
||||
gST->ConOut->ClearScreen(gST->ConOut);
|
||||
if (move) {
|
||||
cursor_x = cursor_y = 0;
|
||||
}
|
||||
gST->ConOut->SetCursorPosition(gST->ConOut, cursor_x, cursor_y);
|
||||
}
|
||||
|
||||
static void fallback_set_cursor_pos(struct flanterm_context *ctx, size_t x, size_t y) {
|
||||
(void)ctx;
|
||||
if (x >= ctx->cols || y >= ctx->rows) {
|
||||
return;
|
||||
}
|
||||
gST->ConOut->SetCursorPosition(gST->ConOut, x, y);
|
||||
cursor_x = x;
|
||||
cursor_y = y;
|
||||
}
|
||||
|
||||
static void fallback_get_cursor_pos(struct flanterm_context *ctx, size_t *x, size_t *y) {
|
||||
(void)ctx;
|
||||
*x = cursor_x;
|
||||
*y = cursor_y;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool dummy_handle(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void term_fallback(void) {
|
||||
term_notready();
|
||||
|
||||
#if defined (UEFI)
|
||||
if (!efi_boot_services_exited) {
|
||||
#endif
|
||||
|
||||
terms = ext_mem_alloc(sizeof(void *));
|
||||
terms_i = 1;
|
||||
|
||||
terms[0] = ext_mem_alloc(sizeof(struct flanterm_context));
|
||||
|
||||
struct flanterm_context *term = terms[0];
|
||||
|
||||
fallback_clear(NULL, true);
|
||||
|
||||
term->set_text_fg = (void *)dummy_handle;
|
||||
term->set_text_bg = (void *)dummy_handle;
|
||||
term->set_text_fg_bright = (void *)dummy_handle;
|
||||
term->set_text_bg_bright = (void *)dummy_handle;
|
||||
term->set_text_fg_rgb = (void *)dummy_handle;
|
||||
term->set_text_bg_rgb = (void *)dummy_handle;
|
||||
term->set_text_fg_default = (void *)dummy_handle;
|
||||
term->set_text_bg_default = (void *)dummy_handle;
|
||||
term->move_character = (void *)dummy_handle;
|
||||
term->revscroll = (void *)dummy_handle;
|
||||
term->swap_palette = (void *)dummy_handle;
|
||||
term->save_state = (void *)dummy_handle;
|
||||
term->restore_state = (void *)dummy_handle;
|
||||
term->double_buffer_flush = (void *)dummy_handle;
|
||||
term->full_refresh = (void *)dummy_handle;
|
||||
term->deinit = (void *)dummy_handle;
|
||||
|
||||
term->raw_putchar = fallback_raw_putchar;
|
||||
term->clear = fallback_clear;
|
||||
term->set_cursor_pos = fallback_set_cursor_pos;
|
||||
term->get_cursor_pos = fallback_get_cursor_pos;
|
||||
term->scroll = fallback_scroll;
|
||||
#if defined (UEFI)
|
||||
UINTN uefi_term_x_size, uefi_term_y_size;
|
||||
gST->ConOut->QueryMode(gST->ConOut, gST->ConOut->Mode->Mode, &uefi_term_x_size, &uefi_term_y_size);
|
||||
term->cols = uefi_term_x_size;
|
||||
term->rows = uefi_term_y_size;
|
||||
#elif defined (BIOS)
|
||||
term->cols = 80;
|
||||
term->rows = 25;
|
||||
#endif
|
||||
term_backend = FALLBACK;
|
||||
flanterm_context_reinit(term);
|
||||
#if defined (UEFI)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void reset_term(void);
|
||||
extern void set_cursor_pos_helper(size_t x, size_t y);
|
||||
|
||||
#if defined (__i386__)
|
||||
#define TERM_XFER_CHUNK 8192
|
||||
|
||||
static uint8_t xfer_buf[TERM_XFER_CHUNK];
|
||||
#endif
|
||||
|
||||
static uint64_t context_size(struct flanterm_context *term) {
|
||||
switch (term_backend) {
|
||||
#if defined (BIOS)
|
||||
case TEXTMODE:
|
||||
return sizeof(struct textmode_context) + (VD_ROWS * VD_COLS) * 2;
|
||||
#endif
|
||||
case GTERM: {
|
||||
struct flanterm_fb_context *ctx = (void *)term;
|
||||
return sizeof(struct flanterm_fb_context) +
|
||||
ctx->font_bits_size +
|
||||
ctx->font_bool_size +
|
||||
ctx->canvas_size +
|
||||
ctx->grid_size +
|
||||
ctx->queue_size +
|
||||
ctx->map_size;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void context_save(struct flanterm_context *term, uint64_t buf) {
|
||||
switch (term_backend) {
|
||||
#if defined (BIOS)
|
||||
case TEXTMODE: {
|
||||
struct textmode_context *ctx = (void *)term;
|
||||
memcpy32to64(buf, (uintptr_t)ctx, sizeof(struct textmode_context));
|
||||
buf += sizeof(struct textmode_context);
|
||||
memcpy32to64(buf, (uintptr_t)ctx->back_buffer, VD_ROWS * VD_COLS);
|
||||
buf += VD_ROWS * VD_COLS;
|
||||
memcpy32to64(buf, (uintptr_t)ctx->front_buffer, VD_ROWS * VD_COLS);
|
||||
buf += VD_ROWS * VD_COLS;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case GTERM: {
|
||||
struct flanterm_fb_context *ctx = (void *)term;
|
||||
memcpy32to64(buf, (uintptr_t)ctx, sizeof(struct flanterm_fb_context));
|
||||
buf += sizeof(struct flanterm_fb_context);
|
||||
memcpy32to64(buf, (uintptr_t)ctx->font_bits, ctx->font_bits_size);
|
||||
buf += ctx->font_bits_size;
|
||||
memcpy32to64(buf, (uintptr_t)ctx->font_bool, ctx->font_bool_size);
|
||||
buf += ctx->font_bool_size;
|
||||
memcpy32to64(buf, (uintptr_t)ctx->canvas, ctx->canvas_size);
|
||||
buf += ctx->canvas_size;
|
||||
memcpy32to64(buf, (uintptr_t)ctx->grid, ctx->grid_size);
|
||||
buf += ctx->grid_size;
|
||||
memcpy32to64(buf, (uintptr_t)ctx->queue, ctx->queue_size);
|
||||
buf += ctx->queue_size;
|
||||
memcpy32to64(buf, (uintptr_t)ctx->map, ctx->map_size);
|
||||
buf += ctx->map_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void context_restore(struct flanterm_context *term, uint64_t buf) {
|
||||
switch (term_backend) {
|
||||
#if defined (BIOS)
|
||||
case TEXTMODE: {
|
||||
struct textmode_context *ctx = (void *)term;
|
||||
memcpy32to64((uintptr_t)ctx, buf, sizeof(struct textmode_context));
|
||||
buf += sizeof(struct textmode_context);
|
||||
memcpy32to64((uintptr_t)ctx->back_buffer, buf, VD_ROWS * VD_COLS);
|
||||
buf += VD_ROWS * VD_COLS;
|
||||
memcpy32to64((uintptr_t)ctx->front_buffer, buf, VD_ROWS * VD_COLS);
|
||||
buf += VD_ROWS * VD_COLS;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case GTERM: {
|
||||
struct flanterm_fb_context *ctx = (void *)term;
|
||||
memcpy32to64((uintptr_t)ctx, buf, sizeof(struct flanterm_fb_context));
|
||||
buf += sizeof(struct flanterm_fb_context);
|
||||
memcpy32to64((uintptr_t)ctx->font_bits, buf, ctx->font_bits_size);
|
||||
buf += ctx->font_bits_size;
|
||||
memcpy32to64((uintptr_t)ctx->font_bool, buf, ctx->font_bool_size);
|
||||
buf += ctx->font_bool_size;
|
||||
memcpy32to64((uintptr_t)ctx->canvas, buf, ctx->canvas_size);
|
||||
buf += ctx->canvas_size;
|
||||
memcpy32to64((uintptr_t)ctx->grid, buf, ctx->grid_size);
|
||||
buf += ctx->grid_size;
|
||||
memcpy32to64((uintptr_t)ctx->queue, buf, ctx->queue_size);
|
||||
buf += ctx->queue_size;
|
||||
memcpy32to64((uintptr_t)ctx->map, buf, ctx->map_size);
|
||||
buf += ctx->map_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count) {
|
||||
switch (count) {
|
||||
case TERM_OOB_OUTPUT_GET: {
|
||||
memcpy32to64(buf, (uint64_t)(uintptr_t)&term->oob_output, sizeof(uint64_t));
|
||||
return;
|
||||
}
|
||||
case TERM_OOB_OUTPUT_SET: {
|
||||
memcpy32to64((uint64_t)(uintptr_t)&term->oob_output, buf, sizeof(uint64_t));
|
||||
return;
|
||||
}
|
||||
case TERM_CTX_SIZE: {
|
||||
uint64_t ret = context_size(term);
|
||||
memcpy32to64(buf, (uint64_t)(uintptr_t)&ret, sizeof(uint64_t));
|
||||
return;
|
||||
}
|
||||
case TERM_CTX_SAVE: {
|
||||
context_save(term, buf);
|
||||
return;
|
||||
}
|
||||
case TERM_CTX_RESTORE: {
|
||||
context_restore(term, buf);
|
||||
return;
|
||||
}
|
||||
case TERM_FULL_REFRESH: {
|
||||
term->full_refresh(term);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool native = false;
|
||||
#if defined (__x86_64__) || defined (__aarch64__) || defined (__riscv64)
|
||||
native = true;
|
||||
#elif !defined (__i386__)
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
bool autoflush = term->autoflush;
|
||||
term->autoflush = false;
|
||||
|
||||
if (native) {
|
||||
const char *s = (const char *)(uintptr_t)buf;
|
||||
|
||||
flanterm_write(term, s, count);
|
||||
} else {
|
||||
#if defined (__i386__)
|
||||
while (count != 0) {
|
||||
uint64_t chunk;
|
||||
if (count > TERM_XFER_CHUNK) {
|
||||
chunk = TERM_XFER_CHUNK;
|
||||
} else {
|
||||
chunk = count;
|
||||
}
|
||||
|
||||
memcpy32to64((uint64_t)(uintptr_t)xfer_buf, buf, chunk);
|
||||
|
||||
flanterm_write(term, (const char *)xfer_buf, chunk);
|
||||
|
||||
count -= chunk;
|
||||
buf += chunk;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (autoflush) {
|
||||
term->double_buffer_flush(term);
|
||||
}
|
||||
|
||||
term->autoflush = autoflush;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef __LIB__TERM_H__
|
||||
#define __LIB__TERM_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <lib/print.h>
|
||||
#include <flanterm/flanterm.h>
|
||||
|
||||
enum {
|
||||
_NOT_READY,
|
||||
GTERM,
|
||||
TEXTMODE,
|
||||
FALLBACK
|
||||
};
|
||||
|
||||
#if defined (BIOS)
|
||||
extern int current_video_mode;
|
||||
#endif
|
||||
|
||||
extern struct flanterm_context **terms;
|
||||
extern size_t terms_i;
|
||||
|
||||
extern int term_backend;
|
||||
|
||||
#define TERM_CTX_SIZE ((uint64_t)(-1))
|
||||
#define TERM_CTX_SAVE ((uint64_t)(-2))
|
||||
#define TERM_CTX_RESTORE ((uint64_t)(-3))
|
||||
#define TERM_FULL_REFRESH ((uint64_t)(-4))
|
||||
#define TERM_OOB_OUTPUT_GET ((uint64_t)(-10))
|
||||
#define TERM_OOB_OUTPUT_SET ((uint64_t)(-11))
|
||||
|
||||
#define FOR_TERM(...) do { \
|
||||
for (size_t FOR_TERM_i = 0; FOR_TERM_i < terms_i; FOR_TERM_i++) { \
|
||||
struct flanterm_context *TERM = terms[FOR_TERM_i]; \
|
||||
__VA_ARGS__ \
|
||||
; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
inline void reset_term(void) {
|
||||
for (size_t i = 0; i < terms_i; i++) {
|
||||
struct flanterm_context *term = terms[i];
|
||||
|
||||
print("\e[2J\e[H");
|
||||
flanterm_context_reinit(term);
|
||||
term->cursor_enabled = true;
|
||||
term->double_buffer_flush(term);
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_cursor_pos_helper(size_t x, size_t y) {
|
||||
print("\e[%u;%uH", (int)y + 1, (int)x + 1);
|
||||
}
|
||||
|
||||
void term_notready(void);
|
||||
void term_fallback(void);
|
||||
void _term_write(struct flanterm_context *term, uint64_t buf, uint64_t count);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <lib/time.h>
|
||||
#if defined (BIOS)
|
||||
# include <lib/real.h>
|
||||
#elif defined (UEFI)
|
||||
# include <efi.h>
|
||||
#endif
|
||||
#include <lib/misc.h>
|
||||
|
||||
// Julian date calculation from https://en.wikipedia.org/wiki/Julian_day
|
||||
static int get_jdn(int days, int months, int years) {
|
||||
return (1461 * (years + 4800 + (months - 14)/12))/4 + (367 *
|
||||
(months - 2 - 12 * ((months - 14)/12)))/12 -
|
||||
(3 * ((years + 4900 + (months - 14)/12)/100))/4
|
||||
+ days - 32075;
|
||||
}
|
||||
|
||||
static uint64_t get_unix_epoch(uint8_t seconds, uint8_t minutes, uint8_t hours,
|
||||
uint8_t days, uint8_t months, uint16_t years) {
|
||||
uint64_t jdn_current = get_jdn(days, months, years);
|
||||
uint64_t jdn_1970 = get_jdn(1, 1, 1970);
|
||||
|
||||
uint64_t jdn_diff = jdn_current - jdn_1970;
|
||||
|
||||
return (jdn_diff * (60 * 60 * 24)) + hours * 3600 + minutes * 60 + seconds;
|
||||
}
|
||||
|
||||
#if defined (BIOS)
|
||||
uint64_t time(void) {
|
||||
struct rm_regs r;
|
||||
|
||||
again:
|
||||
r = (struct rm_regs){0};
|
||||
r.eax = 0x0400;
|
||||
rm_int(0x1a, &r, &r);
|
||||
|
||||
uint8_t day = bcd_to_int( r.edx & 0x00ff);
|
||||
uint8_t month = bcd_to_int((r.edx & 0xff00) >> 8);
|
||||
uint16_t year = bcd_to_int( r.ecx & 0x00ff) +
|
||||
/* century */ bcd_to_int((r.ecx & 0xff00) >> 8) * 100;
|
||||
|
||||
r = (struct rm_regs){0};
|
||||
r.eax = 0x0200;
|
||||
rm_int(0x1a, &r, &r);
|
||||
|
||||
uint8_t second = bcd_to_int((r.edx & 0xff00) >> 8);
|
||||
uint8_t minute = bcd_to_int( r.ecx & 0x00ff);
|
||||
uint8_t hour = bcd_to_int((r.ecx & 0xff00) >> 8);
|
||||
|
||||
// Check RTC day wraparound
|
||||
r = (struct rm_regs){0};
|
||||
r.eax = 0x0400;
|
||||
rm_int(0x1a, &r, &r);
|
||||
if (bcd_to_int(r.edx & 0x00ff) != day) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
return get_unix_epoch(second, minute, hour, day, month, year);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined (UEFI)
|
||||
uint64_t time(void) {
|
||||
EFI_TIME time;
|
||||
EFI_STATUS status = gRT->GetTime(&time, NULL);
|
||||
|
||||
if (status != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get_unix_epoch(time.Second, time.Minute, time.Hour,
|
||||
time.Day, time.Month, time.Year);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef __LIB__TIME_H__
|
||||
#define __LIB__TIME_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t time(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef __LIB__TRACE_H__
|
||||
#define __LIB__TRACE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void print_stacktrace(size_t *base_ptr);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,85 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <lib/trace.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/config.h>
|
||||
#include <lib/print.h>
|
||||
#include <lib/uri.h>
|
||||
#include <fs/file.h>
|
||||
#include <mm/pmm.h>
|
||||
|
||||
#if defined (BIOS)
|
||||
extern symbol stage2_map;
|
||||
#elif defined (UEFI)
|
||||
extern symbol __slide;
|
||||
#endif
|
||||
|
||||
extern symbol full_map;
|
||||
|
||||
static char *trace_address(size_t *off, size_t addr) {
|
||||
char *limine_map;
|
||||
|
||||
#if defined (BIOS)
|
||||
if (!stage3_loaded)
|
||||
limine_map = stage2_map;
|
||||
else
|
||||
limine_map = full_map;
|
||||
#elif defined (UEFI)
|
||||
limine_map = full_map;
|
||||
|
||||
addr -= (size_t)__slide;
|
||||
#endif
|
||||
|
||||
uintptr_t prev_addr = 0;
|
||||
char *prev_sym = NULL;
|
||||
|
||||
for (size_t i = 0;;) {
|
||||
if (*((uintptr_t *)&limine_map[i]) >= addr) {
|
||||
*off = addr - prev_addr;
|
||||
return prev_sym;
|
||||
}
|
||||
prev_addr = *((uintptr_t *)&limine_map[i]);
|
||||
i += sizeof(uintptr_t);
|
||||
prev_sym = &limine_map[i];
|
||||
while (limine_map[i++] != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void print_stacktrace(size_t *base_ptr) {
|
||||
if (base_ptr == NULL) {
|
||||
asm volatile (
|
||||
#if defined (__i386__)
|
||||
"movl %%ebp, %0"
|
||||
#elif defined (__x86_64__)
|
||||
"movq %%rbp, %0"
|
||||
#elif defined (__aarch64__)
|
||||
"mov %0, x29"
|
||||
#elif defined (__riscv64)
|
||||
"mv %0, fp; addi %0, %0, -16"
|
||||
#endif
|
||||
: "=r"(base_ptr)
|
||||
:: "memory"
|
||||
);
|
||||
}
|
||||
print("Stacktrace:\n");
|
||||
for (;;) {
|
||||
size_t old_bp = base_ptr[0];
|
||||
size_t ret_addr = base_ptr[1];
|
||||
if (!ret_addr)
|
||||
break;
|
||||
size_t off;
|
||||
char *name = trace_address(&off, ret_addr);
|
||||
if (name)
|
||||
print(" [%p] <%s+%p>\n", ret_addr, name, off);
|
||||
else
|
||||
print(" [%p]\n", ret_addr);
|
||||
if (!old_bp)
|
||||
break;
|
||||
#if defined (__riscv)
|
||||
base_ptr = (void *)(old_bp - 16);
|
||||
#else
|
||||
base_ptr = (void*)old_bp;
|
||||
#endif
|
||||
}
|
||||
print("End of trace. ");
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <lib/uri.h>
|
||||
#include <lib/misc.h>
|
||||
#include <lib/part.h>
|
||||
#include <lib/libc.h>
|
||||
#include <fs/file.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <lib/print.h>
|
||||
#include <pxe/tftp.h>
|
||||
#include <compress/gzip.h>
|
||||
#include <menu.h>
|
||||
#include <lib/readline.h>
|
||||
#include <crypt/blake2b.h>
|
||||
|
||||
// A URI takes the form of: resource://root/path#hash
|
||||
// The following function splits up a URI into its componenets
|
||||
bool uri_resolve(char *uri, char **resource, char **root, char **path, char **hash) {
|
||||
size_t length = strlen(uri) + 1;
|
||||
char *buf = ext_mem_alloc(length);
|
||||
memcpy(buf, uri, length);
|
||||
uri = buf;
|
||||
|
||||
*resource = *root = *path = NULL;
|
||||
|
||||
// Get resource
|
||||
for (size_t i = 0; ; i++) {
|
||||
if (strlen(uri + i) < 3)
|
||||
return false;
|
||||
|
||||
if (!memcmp(uri + i, "://", 3)) {
|
||||
*resource = uri;
|
||||
uri[i] = 0;
|
||||
uri += i + 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get root
|
||||
for (size_t i = 0; ; i++) {
|
||||
if (uri[i] == 0)
|
||||
return false;
|
||||
|
||||
if (uri[i] == '/') {
|
||||
*root = uri;
|
||||
uri[i] = 0;
|
||||
uri += i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get path
|
||||
if (*uri == 0)
|
||||
return false;
|
||||
*path = uri;
|
||||
|
||||
// Get hash
|
||||
for (int i = (int)strlen(uri) - 1; i >= 0; i--) {
|
||||
if (uri[i] != '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
uri[i++] = 0;
|
||||
|
||||
if (hash != NULL) {
|
||||
*hash = uri + i;
|
||||
}
|
||||
|
||||
if (strlen(uri + i) != 128) {
|
||||
panic(true, "Blake2b hash must be 128 characters long");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_bios_partition(char *loc, int *drive, int *partition) {
|
||||
uint64_t val;
|
||||
|
||||
for (size_t i = 0; ; i++) {
|
||||
if (loc[i] == 0)
|
||||
return false;
|
||||
|
||||
if (loc[i] == ':') {
|
||||
loc[i] = 0;
|
||||
if (*loc == 0) {
|
||||
panic(true, "Drive number cannot be omitted for hdd:// and odd://");
|
||||
} else {
|
||||
val = strtoui(loc, NULL, 10);
|
||||
if (val < 1 || val > 256) {
|
||||
panic(true, "Drive number outside range 1-256");
|
||||
}
|
||||
*drive = val;
|
||||
}
|
||||
loc += i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
val = strtoui(loc, NULL, 10);
|
||||
if (val > 256) {
|
||||
panic(true, "Partition number outside range 0-256");
|
||||
}
|
||||
*partition = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct file_handle *uri_hdd_dispatch(char *loc, char *path) {
|
||||
int drive, partition;
|
||||
|
||||
if (!parse_bios_partition(loc, &drive, &partition))
|
||||
return NULL;
|
||||
|
||||
struct volume *volume = volume_get_by_coord(false, drive, partition);
|
||||
|
||||
if (volume == NULL)
|
||||
return NULL;
|
||||
|
||||
return fopen(volume, path);
|
||||
}
|
||||
|
||||
static struct file_handle *uri_odd_dispatch(char *loc, char *path) {
|
||||
int drive, partition;
|
||||
|
||||
if (!parse_bios_partition(loc, &drive, &partition))
|
||||
return NULL;
|
||||
|
||||
struct volume *volume = volume_get_by_coord(true, drive, partition);
|
||||
|
||||
if (volume == NULL)
|
||||
return NULL;
|
||||
|
||||
return fopen(volume, path);
|
||||
}
|
||||
|
||||
static struct file_handle *uri_guid_dispatch(char *guid_str, char *path) {
|
||||
struct guid guid;
|
||||
if (!string_to_guid_be(&guid, guid_str))
|
||||
return NULL;
|
||||
|
||||
struct volume *volume = volume_get_by_guid(&guid);
|
||||
if (volume == NULL) {
|
||||
if (!string_to_guid_mixed(&guid, guid_str))
|
||||
return NULL;
|
||||
|
||||
volume = volume_get_by_guid(&guid);
|
||||
if (volume == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fopen(volume, path);
|
||||
}
|
||||
|
||||
static struct file_handle *uri_fslabel_dispatch(char *fslabel, char *path) {
|
||||
struct volume *volume = volume_get_by_fslabel(fslabel);
|
||||
if (volume == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fopen(volume, path);
|
||||
}
|
||||
|
||||
static struct file_handle *uri_tftp_dispatch(char *root, char *path) {
|
||||
uint32_t ip;
|
||||
if (!strcmp(root, "")) {
|
||||
ip = 0;
|
||||
} else {
|
||||
if (inet_pton(root, &ip)) {
|
||||
panic(true, "tftp: Invalid ipv4 address: %s", root);
|
||||
}
|
||||
}
|
||||
|
||||
struct file_handle *ret;
|
||||
if ((ret = tftp_open(boot_volume, root, path)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_handle *uri_boot_dispatch(char *s_part, char *path) {
|
||||
if (boot_volume->pxe)
|
||||
return uri_tftp_dispatch(s_part, path);
|
||||
|
||||
int partition;
|
||||
|
||||
if (s_part[0] != '\0') {
|
||||
uint64_t val = strtoui(s_part, NULL, 10);
|
||||
if (val > 256) {
|
||||
panic(true, "Partition number outside range 0-256");
|
||||
}
|
||||
partition = val;
|
||||
} else {
|
||||
partition = boot_volume->partition;
|
||||
}
|
||||
|
||||
struct volume *volume = volume_get_by_coord(boot_volume->is_optical,
|
||||
boot_volume->index, partition);
|
||||
if (volume == NULL)
|
||||
return NULL;
|
||||
|
||||
return fopen(volume, path);
|
||||
}
|
||||
|
||||
struct file_handle *uri_open(char *uri) {
|
||||
struct file_handle *ret;
|
||||
|
||||
char *resource = NULL, *root = NULL, *path = NULL, *hash = NULL;
|
||||
if (!uri_resolve(uri, &resource, &root, &path, &hash)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (resource == NULL) {
|
||||
panic(true, "No resource specified for URI `%#`.", uri);
|
||||
}
|
||||
|
||||
bool compressed = false;
|
||||
if (*resource == '$') {
|
||||
compressed = true;
|
||||
resource++;
|
||||
}
|
||||
|
||||
if (!strcmp(resource, "bios")) {
|
||||
panic(true, "bios:// resource is no longer supported. Check CONFIG.md for hdd:// and odd://");
|
||||
} else if (!strcmp(resource, "hdd")) {
|
||||
ret = uri_hdd_dispatch(root, path);
|
||||
} else if (!strcmp(resource, "odd")) {
|
||||
ret = uri_odd_dispatch(root, path);
|
||||
} else if (!strcmp(resource, "boot")) {
|
||||
ret = uri_boot_dispatch(root, path);
|
||||
} else if (!strcmp(resource, "guid")) {
|
||||
ret = uri_guid_dispatch(root, path);
|
||||
} else if (!strcmp(resource, "uuid")) {
|
||||
ret = uri_guid_dispatch(root, path);
|
||||
} else if (!strcmp(resource, "fslabel")) {
|
||||
ret = uri_fslabel_dispatch(root, path);
|
||||
} else if (!strcmp(resource, "tftp")) {
|
||||
ret = uri_tftp_dispatch(root, path);
|
||||
} else {
|
||||
panic(true, "Resource `%s` not valid.", resource);
|
||||
}
|
||||
|
||||
if (hash != NULL && ret != NULL) {
|
||||
uint8_t out_buf[BLAKE2B_OUT_BYTES];
|
||||
void *file_buf = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
|
||||
blake2b(out_buf, file_buf, ret->size);
|
||||
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
|
||||
|
||||
for (size_t i = 0; i < sizeof(hash_buf); i++) {
|
||||
hash_buf[i] = digit_to_int(hash[i * 2]) << 4 | digit_to_int(hash[i * 2 + 1]);
|
||||
}
|
||||
|
||||
if (memcmp(hash_buf, out_buf, sizeof(out_buf)) != 0) {
|
||||
if (hash_mismatch_panic) {
|
||||
panic(true, "Blake2b hash for URI `%#` does not match!", uri);
|
||||
} else {
|
||||
print("WARNING: Blake2b hash for URI `%#` does not match!\n"
|
||||
" Press Y to continue, press any other key to return to menu...", uri);
|
||||
|
||||
char ch = getchar();
|
||||
if (ch != 'Y' && ch != 'y') {
|
||||
menu(false);
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compressed && ret != NULL) {
|
||||
struct file_handle *compressed_fd = ext_mem_alloc(sizeof(struct file_handle));
|
||||
void *src = freadall(ret, MEMMAP_BOOTLOADER_RECLAIMABLE);
|
||||
if ((compressed_fd->fd = gzip_uncompress(src, ret->size, &compressed_fd->size)) == NULL) {
|
||||
panic(true, "GZip error");
|
||||
}
|
||||
compressed_fd->vol = ret->vol;
|
||||
compressed_fd->path = ext_mem_alloc(ret->path_len);
|
||||
memcpy(compressed_fd->path, ret->path, ret->path_len);
|
||||
compressed_fd->path_len = ret->path_len;
|
||||
compressed_fd->is_memfile = true;
|
||||
uint64_t src_size = ret->size;
|
||||
fclose(ret);
|
||||
pmm_free(src, src_size);
|
||||
ret = compressed_fd;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue