pax_global_header00006660000000000000000000000064147144122310014511gustar00rootroot0000000000000052 comment=52c6c64d57b0cf3edc0aa963bbe9a4e0e6f51883 memtest86plus-7.20/000077500000000000000000000000001471441223100142015ustar00rootroot00000000000000memtest86plus-7.20/.github/000077500000000000000000000000001471441223100155415ustar00rootroot00000000000000memtest86plus-7.20/.github/dependabot.yml000066400000000000000000000003311471441223100203660ustar00rootroot00000000000000# Set update schedule for GitHub Actions version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: # Check for updates to GitHub Actions every weekday interval: "daily" memtest86plus-7.20/.github/workflows/000077500000000000000000000000001471441223100175765ustar00rootroot00000000000000memtest86plus-7.20/.github/workflows/Linux.yml000066400000000000000000000044721471441223100214270ustar00rootroot00000000000000name: Build and tests on: push: branches: - "*" paths-ignore: - "**/README.md" pull_request: branches: - "*" workflow_dispatch: jobs: build: name: ${{ matrix.os }} ${{ matrix.compiler }} ${{ matrix.arch }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: compiler: [gcc] os: [ubuntu-20.04, ubuntu-22.04] arch: [i386, x86_64, la64] steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Install Linux Dependencies run: | sudo apt-get update sudo apt-get install build-essential gcc-multilib clang libc6-dev-i386-cross dosfstools mtools xorriso -y if [ ${{ matrix.arch }} == 'la64' ]; then sudo mkdir /opt/LoongArch_Toolchains -p; cd /opt/LoongArch_Toolchains sudo wget https://github.com/YongbaoOS/Yongbao-Toolchains/releases/download/2024.8.6/x86_64-cross-tools-loongarch64-binutils_git60d4fed4e364-gcc_14.2.0.tar.xz sudo tar -xf x86_64-cross-tools-loongarch64-binutils_git60d4fed4e364-gcc_14.2.0.tar.xz sudo ln -s /opt/LoongArch_Toolchains/cross-tools/bin/loongarch64-unknown-linux-gnu-gcc /opt/LoongArch_Toolchains/cross-tools/bin/gcc sudo ln -s /opt/LoongArch_Toolchains/cross-tools/bin/loongarch64-unknown-linux-gnu-ld /opt/LoongArch_Toolchains/cross-tools/bin/ld sudo ln -s /opt/LoongArch_Toolchains/cross-tools/bin/loongarch64-unknown-linux-gnu-objcopy /opt/LoongArch_Toolchains/cross-tools/bin/objcopy fi - name: Clean up working-directory: ./ run: | if [ ${{ matrix.arch }} == 'i386' ]; then cd build32 elif [ ${{ matrix.arch }} == 'x86_64' ]; then cd build64 elif [ ${{ matrix.arch }} == 'la64' ]; then cd build64/la64 fi make clean - name: Build working-directory: ./ run: | if [ ${{ matrix.arch }} == 'i386' ]; then cd build32 elif [ ${{ matrix.arch }} == 'x86_64' ]; then cd build64 elif [ ${{ matrix.arch }} == 'la64' ]; then export PATH=/opt/LoongArch_Toolchains/cross-tools/bin/:$PATH cd build64/la64 fi make -j 2 CC="${{matrix.compiler}}" iso memtest86plus-7.20/.github/workflows/expired.yml000066400000000000000000000020731471441223100217630ustar00rootroot00000000000000name: 'Close stale issues and PRs' on: schedule: - cron: '0 0 */2 * *' jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} exempt-issue-milestones: 'future,alpha,beta,release' exempt-pr-milestones: 'bugfix,improvement' exempt-all-pr-assignees: true stale-issue-message: 'This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 30 days.' stale-pr-message: 'This PR is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 30 days.' close-issue-message: 'This issue was closed because it has been stalled for 30 days with no activity.' close-pr-message: 'This PR was closed because it has been stalled for 30 days with no activity.' days-before-issue-stale: 120 days-before-pr-stale: 120 days-before-issue-close: 30 days-before-pr-close: 30 memtest86plus-7.20/.gitignore000066400000000000000000000004231471441223100161700ustar00rootroot00000000000000# Prerequisites *.d # Preprocessed assembler *.s # Object files *.o # Generated files build_version.h gdbscript memtest_shared_debug.lds # Binaries memtest_shared memtest_shared.bin *.bin *.efi *.img *.iso *.mbr *.debug # Directories grub-iso html latex # OVMF OVMF* memtest86plus-7.20/LICENSE000066400000000000000000000432541471441223100152160ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. memtest86plus-7.20/README.md000066400000000000000000000704551471441223100154730ustar00rootroot00000000000000# Memtest86+ Memtest86+ is a free, open-source, stand-alone memory tester for x86 and x86-64 architecture computers. It provides a much more thorough memory check than that provided by BIOS memory tests. It is also able to access almost all the computer's memory, not being restricted by the memory used by the operating system and not depending on any underlying software like UEFI libraries. Memtest86+ can be loaded and run either directly by a PC BIOS (legacy or UEFI) or via an intermediate bootloader that supports the Linux 16-bit, 32-bit, 64-bit, or EFI handover boot protocol. It should work on any Pentium class or later 32-bit or 64-bit CPU. Binary releases (both stable and nightly dev builds) are available on [memtest.org](https://memtest.org). ## Table of Contents * [Origins](#origins) * [Licensing](#licensing) * [Build and Installation](#build-and-installation) * [Boot Options](#boot-options) * [Keyboard Selection](#keyboard-selection) * [Operation](#operation) * [Error Display](#error-reporting) * [Trouble-shooting Memory Errors](#trouble-shooting-memory-errors) * [Execution Time](#execution-time) * [Memtest86+ Test Algorithms](#memory-testing-philosophy) * [Individual Test Descriptions](#individual-test-descriptions) * [Known Limitations and Bugs](#known-limitations-and-bugs) * [Code Contributions](#code-contributions) * [Acknowledgments](#acknowledgments) ## Origins Memtest86+ v6.00 was based on PCMemTest, which was a fork and rewrite of the earlier Memtest86+ v5, which in turn was a fork of MemTest-86. The purpose of the PCMemTest rewrite was to: * make the code more readable and easier to maintain * make the code 64-bit clean and support UEFI boot * fix failures seen when building with newer versions of GCC In the process of creating PCMemTest, a number of features of Memtest86+ v5 that were not strictly required for testing the system memory were dropped. In particular, no attempt was made to measure the cache and main memory speed, or to identify and report the DRAM type. These features were added back and expanded in Memtest86+ v6.0 to create a unified, fully-featured release. ## Licensing Memtest86+ is released under the terms of the GNU General Public License version 2 (GPLv2). Other than the provisions of the GPL there are no restrictions for use, private or commercial. See the LICENSE file for details. ## Build and Installation Build is only tested on a Linux system, but should be possible on any system using the GNU toolchain and the ELF file format. The tools required are: * GCC * binutils * make * dosfstools and mtools (optional) * xorrisofs (optional) To build a 32-bit image, change directory into the `build32` directory and type `make`. The result is a `memtest.bin` binary image file which can be booted directly by a legacy BIOS (in floppy mode) or by an intermediate bootloader using the Linux 16-bit boot protocol and a `memtest.efi` binary image file which can be booted directly by a 32-bit UEFI BIOS. Either image can be booted by an intermediate bootloader using the Linux 32-bit or 32-bit EFI handover boot protocols. To build a 64-bit image, change directory into the `build64` directory and type `make`. The result is a `memtest.bin` binary image file which can be booted directly by a legacy BIOS (in floppy mode) or by an intermediate bootloader using the Linux 16-bit boot protocol and a `memtest.efi` binary image file which can be booted directly by a 64-bit UEFI BIOS. Either image can be booted by an intermediate bootloader using the Linux 32-bit, 64-bit, or 64-bit EFI handover boot protocols. In either case, to build an ISO image that can be used to create a bootable CD, DVD, or USB Flash drive, type `make iso`, The result is a `memtest.iso` ISO image file. This can then be written directly to a blank CD or DVD, or to a USB Flash drive, which can then be booted directly by a legacy or UEFI PC BIOS. Note that when writing to a USB Flash drive, the ISO image must be written directly ('dumped') to the raw device, either by using the `dd` command or by using a utility that provides the same functionality. When using an intermediate bootloader, either the `memtest.bin` file or the `memtest.efi` file should be stored in a disk partition the bootloader can access, and the bootloader configuration should be updated to boot from that file as if it were a Linux kernel with no initial RAM disk. Several boot command line options are recognised, as described below. If using the 16-bit boot protocol, Memtest86+ will use the display in text mode (640x400). If using the 32-bit or 64-bit boot protocols, Memtest86+ will use the display in either text mode or graphics mode, as specified in the `boot_params` struct passed to it by the bootloader. If in graphics mode, the supplied framebuffer must be at least 640x400 pixels; if larger, the display will be centred. If the system was booted in UEFI mode, graphics mode must be used. For test purposes, there is also an option to build an ISO image that uses GRUB as an intermediate bootloader. See the `Makefile` in the `build32` or `build64` directory for details. The ISO image is both legacy and UEFI bootable, so you need GRUB modules for both legacy and EFI boot installed on your build system (e.g. on Debian, the required GRUB modules are located in packages `grub-pc-bin`, `grub-efi-ia32-bin` and `grub-efi-amd64-bin`). You may need to adjust some path and file names in the Makefile to match the naming on your system. The GRUB configuration files contained in the `grub` directory are there for use on the test ISO, but also serve as an example of how to boot Memtest86+ from GRUB. ## Boot Options An intermediate bootloader may pass a boot command line to Memtest86+. The command line may contain one or more options, separated by spaces. Each option consists of an option name, optionally followed by an `=` sign and one or more parameters, separated by commas. The following options are recognised: * nosmp * disables ACPI table parsing and the use of multiple CPU cores * nobench * disables the integrated memory benchmark * nobigstatus * disables the big PASS/FAIL pop-up status display * nosm * disables SMBUS/SPD parsing, DMI decoding and memory benchmark * nomch * disables memory controller configuration polling * nopause * skips the pause for configuration at startup * keyboard=*type* * where *type* is one of * legacy * usb * both * screen.mode=*w*x*h* (EFI framebuffer only) * where *w*x*h* is the preferred screen resolution (e.g. 1024x768) * screen.mode=bios (EFI framebuffer only) * uses the default screen resolution set by the UEFI BIOS * screen.rhs-up (graphics mode only) * rotates the display clockwise by 90 degrees * screen.lhs-up (graphics mode only) * rotates the display anti-clockwise by 90 degrees * efidebug * displays information about the EFI framebuffer * usbdebug * pauses after probing for USB keyboards * usbinit=*mode* * where *mode* is one of * 1 = use the two-step init sequence for high speed devices * 2 = add a second USB reset in the init sequence * 3 = the combination of modes 1 and 2 * console=ttyS*x*,*y* * activate serial/tty console output, where *x* is one of the following IO port * 0 = 0x3F8 * 1 = 0x2F8 * 2 = 0x3E8 * 3 = 0x2E8 * and *y* is an optional baud rate to choose from the following list * 9600 * 19200 * 38400 * 57600 * 115200 (default if not specified or invalid) * 230400 * console=*x*,*y* * activate MMIO UART console, where *x* is the MMIO stride (reg. width) * mmio = 8-bit MMIO * mmio16 = 16-bit MMIO * mmio32 = 32-bit MMIO * and *y* is the MMIO address in hex. with `0x` prefix (eg: 0xFEDC9000) ## Keyboard Selection Memtest86+ supports both the legacy keyboard interface (using I/O ports 0x60 and 0x64) and USB keyboards (using its own USB device drivers). One or the other or both can be selected via the boot command line, If not specified on the command line, the default is to use both if the system was booted in UEFI mode, otherwise to only use the legacy interface. Older BIOSs usually support USB legacy keyboard emulation, which makes USB keyboards act like legacy keyboards connected to ports 0x60 and 0x64. This can often be enabled or disabled in the BIOS setup menus. If Memtest86+'s USB device drivers are enabled, they will override this and access any USB keyboards directly. The downside of that is that the USB controllers and device drivers require some memory to be reserved for their private use, which means that memory can't then be covered by the memory tests. So to maximise test coverage, if it is supported, enable USB legacy keyboard emulation and, if booting in UEFI mode, add `keyboard=legacy` on the boot command line. **NOTE**: Some UEFI BIOSs only support USB legacy keyboard emulation when you enable the Compatibility System Module (CSM) in the BIOS setup. Others only support it when actually booting in legacy mode. Many USB devices don't fully conform to the USB specification. If the USB keyboard probe hangs or fails to detect your keyboard, try the various workarounds provided by the "usbinit" boot option. **NOTE**: Hot-plugging is not currently supported by the Memtest86+ USB drivers. When using these, your USB keyboard should be plugged in before running Memtest86+ and should remain plugged in throughout the test. ## Display Rotation Some 2-in-1 machines use an LCD panel which is natively a portrait mode display but is mounted on its side when attached to the keyboard. When using the display in graphics mode, Memtest86+ can rotate its display to match. Add either the "screen.rhs-up" or the "screen.lhs-up" option on the boot command line, depending on the orientation of the LCD panel. When using the display in text mode, it is expected that the BIOS will take care of this automatically. ## Screen Resolution When booted in legacy mode, Memtest86+ will use the screen resolution that has been set by the BIOS or by the intermediate bootloader. When booted in UEFI mode, Memtest86+ will normally select the smallest available screen resolution that encompasses its 640x400 pixel display. Some BIOSs return incorrect information about the available display modes, so you can override this by adding the "screen.mode=" option on the boot command line. Note that when using display rotation, the specified screen resolution is for the unrotated display. ## Operation Once booted, Memtest86+ will initialise its display, then pause for a few seconds to allow the user to configure its operation. If no key is pressed, it will automatically start running all tests using a single CPU core, continuing indefinitely until the user reboots or halts the machine. At startup, and when running tests, Memtest86+ responds to the following keys: * F1 * enters the configuration menu * F2 * toggles use of multiple CPU cores (SMP) * Space * toggles scroll lock (stops/starts error message scrolling) * Enter * single message scroll (only when scroll lock enabled) * Escape * exits the test and reboots the machine Note that testing is stalled when scroll lock is enabled and the scroll region is full. The configuration menu allows the user to: * select which tests are run (default: all tests) * limit the address range over which tests are performed (default: all memory) * select the CPU sequencing mode (default: parallel) * parallel * each CPU core works in parallel on a subset of the memory region being tested * sequential * each CPU core works in turn on the full memory region being tested * round robin * a single CPU core works on the full memory region being tested, with a new CPU core being selected (in round-robin fashion) for each test * select the error reporting mode (default: individual errors) * error counts only * error summary * individual errors * BadRAM patterns * select which of the available CPU cores are used (at startup only) * a maximum of 256 CPU cores can be selected, due to memory and display limits * the bootstrap processor (BSP) cannot be deselected * enable or disable the temperature display (at startup only) * enable or disable boot tracing for debug (at startup only) * skip to the next test (when running tests) In all cases, the number keys may be used as alternatives to the function keys (1 = F1, 2 = F2, ... 0 = F10). ## Error Reporting The error reporting mode may be changed at any time without disrupting the current test sequence. Error statistics are collected regardless of the current error reporting mode (so switching to error summary mode will show the accumulated statistics since the current test sequence started). BadRAM patterns are only accumulated when in BadRAM mode. Any change to the selected tests, address range, or CPU sequencing mode will start a new test sequence and reset the error statistics. ### Error Counts Only The error counts only mode just displays the total number of errors found since the current test sequence started. ### Error Summary The error summary mode displays the following information: * Lowest Error Address * the lowest address that where an error has been reported * Highest Error Address * the highest address that where an error has been reported * Bits in Error Mask * a hexadecimal mask of all bits that have been in error * Bits in Error * total bits in error for all error instances and the min, max and average number of bits in error across each error instance * Max Contiguous Errors * the maximum of contiguous addresses with errors * Test Errors * the total number of errors for each individual test ### Individual Errors The individual error mode displays the following information for each error instance: * pCPU * the physical CPU core number that detected the error * Pass * the test pass number where the error occurred (a test pass is a single run over all the currently selected tests) * Test * the individual test number where the error occurred * Failing Address * the memory address where the error occurred * Expected * the hexadecimal data pattern expected to be found * Found * the hexadecimal data pattern read from the failing address * Err Bits (only in 32-bit builds) * a hexadecimal mask showing the bits in error ### BadRAM Patterns The BadRAM patterns mode accumulates and displays error patterns for use with the [Linux BadRAM feature](http://rick.vanrein.org/linux/badram/). Lines are printed in the form `badram=F1,M1,F2,M2...` In each `F,M` pair, the `F` represents a fault address and the `M` is a bitmask for that address. These patterns state that faults have occurred in addresses that equal F on all `1` bits in M. Such a pattern may capture more errors that actually exist, but at least all the errors are captured. These patterns have been designed to capture regular patterns of errors caused by the hardware structure in a terse syntax. The BadRAM patterns are grown incrementally rather than calculated from an overview of all errors. The number of pairs is constrained to ten for a number of practical reasons. As a result, handcrafting patterns from the output in address printing mode may, in exceptional cases, yield better results. **NOTE** As mentioned in the individual test descriptions, the walking-ones address test (test 0) and the block move test (test 7) do not contribute to the BadRAM patterns as these tests do not allow the exact address of the fault to be determined. ## Trouble-shooting Memory Errors Please be aware that not all errors reported by Memtest86+ are due to bad memory. The test implicitly tests the CPU, caches, and motherboard. It is impossible for the test to determine what causes the failure to occur. Most failures will be due to a problem with memory. When it is not, the only option is to replace parts until the failure is corrected. Once a memory error has been detected, determining the failing module is not a clear cut procedure. With the large number of motherboard vendors and possible combinations of memory slots it would be difficult if not impossible to assemble complete information about how a particular error would map to a failing memory module. However, there are steps that may be taken to determine the failing module. Here are some techniques that you may wish to use: * Removing modules * This is the simplest method for isolating a failing modules, but may only be employed when one or more modules can be removed from the system. By selectively removing modules from the system and then running the test you will be able to find the bad module(s). Be sure to note exactly which modules are in the system when the test passes and when the test fails. * Rotating modules * When none of the modules can be removed then you may wish to rotate modules to find the failing one. This technique can only be used if there are three or more modules in the system. Change the location of two modules at a time. For example put the module from slot 1 into slot 2 and put the module from slot 2 in slot 1. Run the test and if either the failing bit or address changes then you know that the failing module is one of the ones just moved. By using several combinations of module movement you should be able to determine which module is failing. * Replacing modules * If you are unable to use either of the previous techniques then you are left to selective replacement of modules to find the failure. Sometimes memory errors show up due to component incompatibility. A memory module may work fine in one system and not in another. This is not uncommon and is a source of confusion. The components are not necessarily bad but certain combinations may need to be avoided. In the vast majority of cases errors reported by Memtest86+ are valid. There are some systems that cause Memtest86+ to be confused about the size of memory and it will try to test non-existent memory. This will cause a large number of consecutive addresses to be reported as bad and generally there will be many bits in error. If you have a relatively small number of failing addresses and only one or two bits in error you can be certain that the errors are valid. Also intermittent errors are always valid. All valid memory errors should be corrected. It is possible that a particular error will never show up in normal operation. However, operating with marginal memory is risky and can result in data loss and even disk corruption. Memtest86+ can not diagnose many types of PC failures. For example a faulty CPU that causes your OS to crash will most likely just cause Memtest86+ to crash in the same way. ## Execution Time The time required for a complete pass of Memtest86+ will vary greatly depending on CPU speed, memory speed, and memory size. Memtest86+ executes indefinitely. The pass counter increments each time that all of the selected tests have been run. Generally a single pass is sufficient to catch all but the most obscure errors. However, for complete confidence when intermittent errors are suspected testing for a longer period is advised. ## Memory Testing Philosophy There are many good approaches for testing memory. However, many tests simply throw some patterns at memory without much thought or knowledge of memory architecture or how errors can best be detected. This works fine for hard memory failures but does little to find intermittent errors. BIOS based memory tests are useless for finding intermittent memory errors. Memory chips consist of a large array of tightly packed memory cells, one for each bit of data. The vast majority of the intermittent failures are a result of interaction between these memory cells. Often writing a memory cell can cause one of the adjacent cells to be written with the same data. An effective memory test attempts to test for this condition. Therefore, an ideal strategy for testing memory would be the following: 1. write a cell with a zero 2. write all of the adjacent cells with a one, one or more times 3. check that the first cell still has a zero It should be obvious that this strategy requires an exact knowledge of how the memory cells are laid out on the chip. In addition there is a never ending number of possible chip layouts for different chip types and manufacturers making this strategy impractical. However, there are testing algorithms that can approximate this ideal strategy. ## Memtest86+ Test Algorithms Memtest86+ uses two algorithms that provide a reasonable approximation of the ideal test strategy above. The first of these strategies is called moving inversions. The moving inversion tests work as follows: 1. Fill memory with a pattern 2. Starting at the lowest address 1. check that the pattern has not changed 2. write the pattern's complement 3. increment the address 4. repeat 2.1 to 2.3 3. Starting at the highest address 1. check that the pattern has not changed 2. write the pattern's complement 3. decrement the address 4. repeat 3.1 to 3.3 This algorithm is a good approximation of an ideal memory test but there are some limitations. Most high density chips today store data 4 to 16 bits wide. With chips that are more than one bit wide it is impossible to selectively read or write just one bit. This means that we cannot guarantee that all adjacent cells have been tested for interaction. In this case the best we can do is to use some patterns to ensure that all adjacent cells have at least been written with all possible one and zero combinations. It can also be seen that caching, buffering, and out of order execution will interfere with the moving inversions algorithm and make it less effective. It is possible to turn off caching but the memory buffering in new high performance chips cannot be disabled. To address this limitation a new algorithm called Modulo-20 was created. This algorithm is not affected by caching or buffering. The algorithm works as follows: 1. For starting offsets of 0 - 19 do 1. write every 20th location with a pattern 2. write all other locations with the pattern's complement 3. repeat 1.2 one or more times 4. check every 20th location for the pattern This algorithm accomplishes nearly the same level of adjacency testing as moving inversions but is not affected by caching or buffering. Since separate write passes (1.1, 1.2) and the read pass (1.4) are done for all of memory we can be assured that all of the buffers and cache have been flushed between passes. The selection of 20 as the stride size was somewhat arbitrary. Larger strides may be more effective but would take longer to execute. The choice of 20 seemed to be a reasonable compromise between speed and thoroughness. ## Individual Test Descriptions Memtest86+ executes a series of numbered tests to check for errors. These tests consist of a combination of test algorithm, data pattern and caching. The execution order for these tests were arranged so that errors will be detected as rapidly as possible. A description of each test follows. To allow testing of more than 4GB of memory on 32-bit CPUs, the physical address range is split into 1GB windows which are be mapped one at a time into a virtual memory window. Each 1GB window may contain one or more contiguous memory regions. For most tests, the test is performed on each memory region in turn. Caching is enabled for all but the first test. ### Test 0 : Address test, walking ones, no cache In each memory region in turn, tests all address bits by using a walking ones address pattern. Errors from this test are not used to calculate BadRAM patterns. ### Test 1 : Address test, own address in window In each memory region in turn, each address is written with its own address and then each address is checked for consistency. This test is performed sequentially with each available CPU, regardless of the CPU sequencing mode selected by the user. ### Test 2 : Address test, own address + window Across all memory regions, each address is written with its own virtual address plus the window number (for 32-bit images) or own physical address (for 64-bit images) and then each address is checked for consistency. This catches any errors in the high order address bits that would be missed when testing each window in turn. This test is performed sequentially with each available CPU, regardless of the CPU sequencing mode selected by the user. ### Test 3 : Moving inversions, ones & zeros In each memory region in turn, and for each pattern in turn, uses the moving inversions algorithm with patterns of all ones and all zeros. ### Test 4 : Moving inversions, 8 bit pattern In each memory region in turn, and for each pattern in turn, uses the moving inversions algorithm with patterns of 8-bit wide walking ones and walking zeros. ### Test 5 : Moving inversions, random pattern In each memory region in turn, and for each pattern in turn, uses the moving inversions algorithm with patterns of a random number and its complement. The random number is different on each test pass so multiple passes increase effectiveness. ### Test 6 : Moving inversions, 32/64 bit pattern In each memory region in turn, and for each pattern in turn, uses the moving inversions algorithm with patterns of 32-bit wide (on 32-bit builds) or 64-bit wide (on 64-bit builds) walking ones and walking zeros. Unlike previous tests, the pattern is rotated 1 bit on each successive address. ### Test 7 : Block move, 64 moves This test stresses memory by using block move (movs) instructions and is based on Robert Redelmeier's burnBX test. In each memory region in turn, memory is initialized with shifting patterns that are inverted every 8 bytes. Then blocks of memory are moved around using the movs instruction. After the moves are completed the data patterns are checked. Because the data is checked only after the memory moves are completed it is not possible to know where the error occurred. The addresses reported are only for where the bad pattern was found. In consequence, errors from this test are not used to calculate BadRAM patterns. ### Test 8 : Random number sequence In each memory region in turn, each address is written with a random number, then each address is checked for consistency and written with the complement of the original data, then each address is again checked for consistency. ### Test 9 : Modulo 20, random pattern In each memory region in turn, and for each pattern in turn, uses the Modulo-20 algorithm with patterns of a random number and its complement. The random number is different on each test pass so multiple passes increase effectiveness. ### Test 10 : Bit fade test, 2 patterns Across all memory regions, and for each pattern in turn, initialises each memory location with a pattern, sleeps for a period of time, then checks each memory location for consistency. The test is performed with patterns of all zeros and all ones. ## Known Limitations and Bugs Please see the list of [open issues](https://github.com/memtest86plus/memtest86plus/issues) and [enhancement requests](https://github.com/memtest86plus/memtest86plus/discussions) on GitHub. Feel free to submit bug reports! ## Code Contributions Code contributions are welcomed, either to fix bugs or to make enhancements. See the README_DEVEL.md in the doc directory for some basic guidelines. ## Acknowledgments Memtest86+ v6.0 was based on PCMemTest, developed by Martin Whitaker, which was based on Memtest86+ v5.01, developed by Samuel Demeulemeester, which in turn was based on Memtest86, developed by Chris Brady with the resources and assistance listed below: * The initial versions of the source files bootsect.S, setup.S, head.S and build.c are from the Linux 1.2.1 kernel and have been heavily modified. * Doug Sisk provided code to support a console connected via a serial port. (not currently used) * Code to create BadRAM patterns was provided by Rick van Rein. * The block move test is based on Robert Redelmeier's burnBX test. * Screen buffer code was provided by Jani Averbach. (not used by Memtest86+ v6.0) * Eric Biederman provided all of the feature content for version 3.0 plus many bugfixes and significant code cleanup. * Major enhancements to hardware detection and reporting in version 3.2, 3.3 and 3.4 provided by Samuel Demeulemeester (from Memtest86+ v1.11, v1.60 and v1.70). In addition, several bug fixes for Memtest86+ were imported from [anphsw/memtest86](https://github.com/anphsw/memtest86). memtest86plus-7.20/app/000077500000000000000000000000001471441223100147615ustar00rootroot00000000000000memtest86plus-7.20/app/badram.c000066400000000000000000000204561471441223100163620ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2024 Martin Whitaker. // // Derived from memtest86+ patn.c: // // MemTest86+ V1.60 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.x86-secret.com - http://www.memtest.org // ---------------------------------------------------- // Pattern extension for memtest86 // // Generates patterns for the Linux kernel's BadRAM extension that avoids // allocation of faulty pages. // // Released under version 2 of the Gnu Public License. // // By Rick van Rein, vanrein@zonnet.nl // // What it does: // - Keep track of a number of BadRAM patterns in an array; // - Combine new faulty addresses with it whenever possible; // - Keep masks as selective as possible by minimising resulting faults; // - Print a new pattern only when the pattern array is changed. #include #include #include "display.h" #include "badram.h" #include "memsize.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define MAX_PATTERNS 10 #define PATTERNS_SIZE (MAX_PATTERNS + 1) // DEFAULT_MASK covers a uintptr_t, since that is the testing granularity. #ifdef __x86_64__ #define DEFAULT_MASK (UINT64_MAX << 3) #else #define DEFAULT_MASK (UINT64_MAX << 2) #endif //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef struct { uint64_t addr; uint64_t mask; } pattern_t; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static pattern_t patterns[PATTERNS_SIZE]; static int num_patterns = 0; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ // New mask is 1 where both masks were 1 (b & d) and the addresses were equal ~(a ^ c). // If addresses were unequal the new mask must be 0 to allow for both values. #define COMBINE_MASK(a,b,c,d) ((b & d) & ~(a ^ c)) /* * Combine two addr/mask pairs to one addr/mask pair. */ static void combine(uint64_t addr1, uint64_t mask1, uint64_t addr2, uint64_t mask2, uint64_t *addr, uint64_t *mask) { *mask = COMBINE_MASK(addr1, mask1, addr2, mask2); *addr = addr1 | addr2; *addr &= *mask; // Normalise to ensure sorting on .addr will work as intended } /* * Count the number of addresses covered with a mask. */ static uint64_t addresses(uint64_t mask) { uint64_t ctr = 1; int i = 8*sizeof(uint64_t); while (i-- > 0) { if (! (mask & 1)) { ctr += ctr; } mask >>= 1; } return ctr; } /* * Count how many more addresses would be covered by addr1/mask1 when combined * with addr2/mask2. */ static uint64_t combi_cost(uint64_t addr1, uint64_t mask1, uint64_t addr2, uint64_t mask2) { uint64_t cost1 = addresses(mask1); uint64_t tmp, mask; combine(addr1, mask1, addr2, mask2, &tmp, &mask); return addresses(mask) - cost1; } /* * Determine if pattern is already covered by an existing pattern. * Return true if that's the case, else false. */ static bool is_covered(pattern_t pattern) { for (int i = 0; i < num_patterns; i++) { if (combi_cost(patterns[i].addr, patterns[i].mask, pattern.addr, pattern.mask) == 0) { return true; } } return false; } /* * Find the pair of entries that would be the cheapest to merge. * Assumes patterns is sorted by .addr asc and that for each index i, the cheapest entry to merge with is at i-1 or i+1. * Return -1 if <= 1 patterns exist, else the index of the first entry of the pair (the other being that + 1). */ static int cheapest_pair() { // This is guaranteed to be overwritten with >= 0 as long as num_patterns > 1 int merge_idx = -1; uint64_t min_cost = UINT64_MAX; for (int i = 0; i < num_patterns - 1; i++) { uint64_t tmp_cost = combi_cost( patterns[i].addr, patterns[i].mask, patterns[i+1].addr, patterns[i+1].mask ); if (tmp_cost <= min_cost) { min_cost = tmp_cost; merge_idx = i; } } return merge_idx; } /* * Remove entries at idx and idx+1. */ static void remove_pair(int idx) { for (int i = idx; i < num_patterns - 2; i++) { patterns[i] = patterns[i + 2]; } patterns[num_patterns - 1].addr = 0u; patterns[num_patterns - 1].mask = 0u; patterns[num_patterns - 2].addr = 0u; patterns[num_patterns - 2].mask = 0u; num_patterns -= 2; } /* * Get the combined entry of idx1 and idx2. */ static pattern_t combined_pattern(int idx1, int idx2) { pattern_t combined; combine( patterns[idx1].addr, patterns[idx1].mask, patterns[idx2].addr, patterns[idx2].mask, &combined.addr, &combined.mask ); return combined; } /* * Insert pattern at index idx, shuffling other entries on index towards the end. */ static void insert_at(pattern_t pattern, int idx) { // Move all entries >= idx one index towards the end to make space for the new entry for (int i = num_patterns - 1; i >= idx; i--) { patterns[i + 1] = patterns[i]; } patterns[idx] = pattern; num_patterns++; } /* * Insert entry (addr, mask) in patterns in an index i so that patterns[i-1].addr < patterns[i] * NOTE: Assumes patterns is already sorted by .addr asc! */ static void insert_sorted(pattern_t pattern) { // Normalise to ensure sorting on .addr will work as intended pattern.addr &= pattern.mask; // Find index to insert entry into int new_idx = num_patterns; for (int i = 0; i < num_patterns; i++) { if (pattern.addr < patterns[i].addr) { new_idx = i; break; } } insert_at(pattern, new_idx); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void badram_init(void) { num_patterns = 0; for (int idx = 0; idx < PATTERNS_SIZE; idx++) { patterns[idx].addr = 0u; patterns[idx].mask = 0u; } } bool badram_insert(testword_t page, testword_t offset) { pattern_t pattern = { .addr = ((uint64_t)page << PAGE_SHIFT) + offset, .mask = DEFAULT_MASK }; // If covered by existing entry we return immediately if (is_covered(pattern)) { return false; } // Add entry in order sorted by .addr asc insert_sorted(pattern); // If we have more patterns than the max we need to force a merge if (num_patterns > MAX_PATTERNS) { // Find the pair that is the cheapest to merge // merge_idx will be -1 if num_patterns < 2, but that means MAX_PATTERNS = 0 which is not a valid state anyway int merge_idx = cheapest_pair(); pattern_t combined = combined_pattern(merge_idx, merge_idx + 1); // Remove the source pair so that we can maintain order as combined does not necessarily belong in merge_idx remove_pair(merge_idx); insert_sorted(combined); } return true; } void badram_display(void) { if (num_patterns == 0) { return; } check_input(); clear_message_area(); display_pinned_message(0, 0, "BadRAM Patterns (excludes test 0 and test 7)"); display_pinned_message(1, 0, "--------------------------------------------"); scroll(); display_scrolled_message(0, "badram="); int col = 7; for (int i = 0; i < num_patterns; i++) { if (i > 0) { display_scrolled_message(col, ","); col++; } int text_width = 2 * (16 + 2) + 1; if (col > (SCREEN_WIDTH - text_width)) { scroll(); col = 7; } display_scrolled_message(col, "0x%08x%08x,0x%08x%08x", (uintptr_t)(patterns[i].addr >> 32), (uintptr_t)(patterns[i].addr & 0xFFFFFFFFU), (uintptr_t)(patterns[i].mask >> 32), (uintptr_t)(patterns[i].mask & 0xFFFFFFFFU)); col += text_width; } } memtest86plus-7.20/app/badram.h000066400000000000000000000012551471441223100163630ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BADRAM_H #define BADRAM_H /** * \file * * Provides functions for generating patterns for the Linux kernel BadRAM extension. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ #include #include #include "test.h" /** * Initialises the pattern array. */ void badram_init(void); /** * Inserts a single faulty address into the pattern array. Returns * true iff the array was changed. */ bool badram_insert(testword_t page, testword_t offset); /** * Displays the pattern array in the scrollable display region in the * format used by the Linux kernel. */ void badram_display(void); #endif // BADRAM_H memtest86plus-7.20/app/config.c000066400000000000000000000712311471441223100163760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. // Copyright (C) 2004-2022 Sam Demeulemeester. // // Derived from memtest86+ config.c: // // ---------------------------------------------------- // config.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "boot.h" #include "bootparams.h" #include "cpuinfo.h" #include "cpuid.h" #include "hwctrl.h" #include "keyboard.h" #include "memsize.h" #include "pmem.h" #include "serial.h" #include "screen.h" #include "smp.h" #include "usbhcd.h" #include "vmem.h" #include "read.h" #include "print.h" #include "string.h" #include "unistd.h" #include "display.h" #include "test.h" #include "tests.h" #include "config.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ // Origin and size of the pop-up window. #define POP_R 3 #define POP_C 21 #define POP_W 38 #define POP_H 18 #define POP_LAST_R (POP_R + POP_H - 1) #define POP_LAST_C (POP_C + POP_W - 1) #define POP_REGION POP_R, POP_C, POP_LAST_R, POP_LAST_C #define POP_LM (POP_C + 3) // Left margin #define POP_LI (POP_C + 5) // List indent #define SEL_W 32 #define SEL_H 2 #define SEL_AREA (SEL_W * SEL_H) //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static uint16_t popup_save_buffer[POP_W * POP_H]; //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ uintptr_t pm_limit_lower = 0; uintptr_t pm_limit_upper = 0; uintptr_t num_pages_to_test = 0; cpu_mode_t cpu_mode = PAR; error_mode_t error_mode = ERROR_MODE_NONE; cpu_state_t cpu_state[MAX_CPUS]; core_type_t hybrid_core_type[MAX_CPUS]; bool exclude_ecores = true; bool smp_enabled = true; bool enable_big_status = true; bool enable_temperature = true; bool enable_trace = false; bool enable_sm = true; bool enable_bench = true; bool enable_mch_read = true; bool enable_numa = false; bool enable_ecc_polling = false; bool pause_at_start = true; power_save_t power_save = POWER_SAVE_HIGH; bool enable_tty = false; uintptr_t tty_address = 0x3F8; // Legacy IO or MMIO Address accepted int tty_baud_rate = 115200; int tty_update_period = 2; // Update TTY every 2 seconds (default) uint32_t tty_mmio_ref_clk = UART_REF_CLK_MMIO; // Reference clock for MMIO (in Hz) int tty_mmio_stride = 4; // Stride for MMIO (register width in bytes) bool err_banner_redraw = false; // Redraw banner on new errors //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static void parse_serial_params(const char *params) { enable_tty = true; // No parameters passed (only "console"), use default if (params == NULL) { return; } // Check if console is MMIO and grab address and stride uintptr_t mmio_adr = 0; if (strncmp(params, "mmio,0x", 7) == 0) { mmio_adr = hexstr2int(params+7); tty_mmio_stride = 1; } else if (strncmp(params, "mmio16,0x", 9) == 0) { mmio_adr = hexstr2int(params+9); tty_mmio_stride = 2; } else if (strncmp(params, "mmio32,0x", 9) == 0) { mmio_adr = hexstr2int(params+9); tty_mmio_stride = 4; } if(strncmp(params, "mmio", 4) == 0) { if (mmio_adr > 0xFFFF) { tty_address = mmio_adr; } else { enable_tty = false; } return; } // No TTY port passed, use default ttyS0 if (strncmp(params, "ttyS", 5) == 0) { return; } // Configure TTY port or use default if (params[4] >= '0' && params[4] <= '3') { tty_address = serial_io_ports[params[4] - '0']; } else { return; } // No Baud Rate specified, use default if (params[5] != ',' || params[6] == '\0') { return; } switch (params[6]) { default: return; case '1': tty_baud_rate = (params[7] == '9') ? 19200 : 115200; tty_update_period = (params[7] == '9') ? 4 : 2; break; case '2': tty_baud_rate = 230400; tty_update_period = 2; break; case '3': tty_baud_rate = 38400; tty_update_period = 4; break; case '5': tty_baud_rate = 57600; tty_update_period = 3; break; case '7': tty_baud_rate = 76800; tty_update_period = 3; break; case '9': tty_baud_rate = 9600; tty_update_period = 5; break; } } static void parse_option(const char *option, const char *params) { if (option[0] == '\0') return; if (strncmp(option, "console", 8) == 0) { parse_serial_params(params); } else if (strncmp(option, "cpuseqmode", 11) == 0) { if (strncmp(params, "par", 4) == 0) { cpu_mode = PAR; } else if (strncmp(params, "seq", 4) == 0) { cpu_mode = SEQ; } else if (strncmp(params, "rr", 3) == 0 || strncmp(params, "one", 4) == 0) { cpu_mode = ONE; } } else if (strncmp(option, "reportmode", 11) == 0) { if (strncmp(params, "none", 5) == 0) { error_mode = ERROR_MODE_NONE; } else if (strncmp(params, "summary", 8) == 0) { error_mode = ERROR_MODE_SUMMARY; } else if (strncmp(params, "address", 8) == 0) { error_mode = ERROR_MODE_ADDRESS; } else if (strncmp(params, "badram", 7) == 0) { error_mode = ERROR_MODE_BADRAM; } } else if (strncmp(option, "keyboard", 9) == 0 && params != NULL) { if (strncmp(params, "legacy", 7) == 0) { keyboard_types = KT_LEGACY; } else if (strncmp(params, "usb", 4) == 0) { keyboard_types = KT_USB; } else if (strncmp(params, "both", 5) == 0) { keyboard_types = KT_USB|KT_LEGACY; } } else if (strncmp(option, "nobench", 8) == 0) { enable_bench = false; } else if (strncmp(option, "nobigstatus", 12) == 0) { enable_big_status = false; } else if (strncmp(option, "noehci", 7) == 0) { usb_init_options |= USB_IGNORE_EHCI; } else if (strncmp(option, "nomch", 6) == 0) { enable_mch_read = false; } else if (strncmp(option, "nopause", 8) == 0) { pause_at_start = false; } else if (strncmp(option, "nosm", 5) == 0) { enable_sm = false; } else if (strncmp(option, "nosmp", 6) == 0) { smp_enabled = false; } else if (strncmp(option, "numa", 5) == 0) { enable_numa = true; } else if (strncmp(option, "nonuma", 7) == 0) { enable_numa = false; } else if (strncmp(option, "powersave", 10) == 0) { if (strncmp(params, "off", 4) == 0) { power_save = POWER_SAVE_OFF; } else if (strncmp(params, "low", 4) == 0) { power_save = POWER_SAVE_LOW; } else if (strncmp(params, "high", 5) == 0) { power_save = POWER_SAVE_HIGH; } } else if (strncmp(option, "trace", 6) == 0) { enable_trace = true; } else if (strncmp(option, "usbdebug", 9) == 0) { usb_init_options |= USB_DEBUG; } else if (strncmp(option, "usbinit", 8) == 0) { if (strncmp(params, "1", 2) == 0) { usb_init_options |= USB_2_STEP_INIT; } else if (strncmp(params, "2", 2) == 0) { usb_init_options |= USB_EXTRA_RESET; } else if (strncmp(params, "3", 2) == 0) { usb_init_options |= USB_2_STEP_INIT|USB_EXTRA_RESET; } } } static void parse_command_line(char *cmd_line, int cmd_line_size) { const char *option = cmd_line; const char *params = NULL; for (int i = 0; i < cmd_line_size; i++) { switch (cmd_line[i]) { case '\0': parse_option(option, params); return; case ' ': cmd_line[i] = '\0'; parse_option(option, params); option = &cmd_line[i+1]; params = NULL; break; case '=': cmd_line[i] = '\0'; params = &cmd_line[i+1]; break; default: break; } } } static void display_initial_notice(void) { if (smp_enabled) { display_notice("Press to configure, to disable SMP, to start testing"); } else { display_notice("Press to configure, to enable SMP, to start testing "); } } static void update_num_pages_to_test(void) { num_pages_to_test = 0; for (int i = 0; i < pm_map_size; i++) { if (pm_map[i].start >= pm_limit_lower && pm_map[i].end <= pm_limit_upper) { num_pages_to_test += pm_map[i].end - pm_map[i].start; continue; } if (pm_map[i].start < pm_limit_lower) { if (pm_map[i].end < pm_limit_lower) { continue; } if (pm_map[i].end > pm_limit_upper) { num_pages_to_test += pm_limit_upper - pm_limit_lower; } else { num_pages_to_test += pm_map[i].end - pm_limit_lower; } continue; } if (pm_map[i].end > pm_limit_upper) { if (pm_map[i].start > pm_limit_upper) { continue; } num_pages_to_test += pm_limit_upper - pm_map[i].start; } } } static void clear_popup_row(int row) { clear_screen_region(row, POP_C, row, POP_LAST_C); } static void display_input_message(int row, const char *message) { clear_popup_row(row); prints(row, POP_LM, message); if (enable_tty) tty_send_region(POP_REGION); } static void display_error_message(int row, const char *message) { clear_popup_row(row); set_foreground_colour(YELLOW); prints(row, POP_LM, message); set_foreground_colour(WHITE); if (enable_tty) tty_send_region(POP_REGION); } static void display_selection_header(int row, int max_num, int offset) { int i; prints(row, POP_LM, "Current selection:"); if (max_num >= SEL_AREA) { prints(row, POP_LM+18, " (scroll U D)"); printc(row, POP_LM+28, 0x18); printc(row, POP_LM+30, 0x19); } row++; printi(row, POP_LM-2, offset, 3, false, false); offset++; for (i = 1; i < SEL_W && offset < max_num; i++) { printc(row, POP_LM+i, i%8 || (max_num < 16) ? 0xc4 : 0xc2); offset++; } if (i == SEL_W) { int data_rows = (max_num + SEL_W) / SEL_W; if (data_rows > SEL_H) { data_rows = SEL_H; } row += data_rows + 1; offset += SEL_W * (data_rows - 2); for (i = 0; i < (SEL_W - 1) && offset < max_num; i++) { printc(row, POP_LM+i, i==0 ? 0xc0 : i%8 ? 0xc4 : 0xc1); offset++; } } printi(row, POP_LM+i, offset, 3, false, true); } static void display_enabled(int row, int n, bool enabled) { if (n >= 0 && n < SEL_AREA) { printc(row + (n / SEL_W), POP_LM + (n % SEL_W), enabled ? '*' : '.'); } } static bool set_all_tests(bool enabled) { clear_popup_row(POP_R+14); for (int i = 0; i < NUM_TEST_PATTERNS; i++) { test_list[i].enabled = enabled; display_enabled(POP_R+12, i, enabled); } return true; } static bool add_or_remove_test(bool add) { display_input_message(POP_R+14, "Enter test #"); int n = read_value(POP_R+14, POP_LM+12, 2, 0); if (n < 0 || n >= NUM_TEST_PATTERNS) { display_error_message(POP_R+14, "Invalid test number"); return false; } test_list[n].enabled = add; display_enabled(POP_R+12, n, add); clear_popup_row(POP_R+14); return true; } static bool add_test_range() { display_input_message(POP_R+14, "Enter first test #"); int n1 = read_value(POP_R+14, POP_LM+18, 2, 0); if (n1 < 0 || n1 >= NUM_TEST_PATTERNS) { display_error_message(POP_R+14, "Invalid test number"); return false; } display_input_message(POP_R+14, "Enter last test #"); int n2 = read_value(POP_R+14, POP_LM+17, 2, 0); if (n2 < n1 || n2 >= NUM_TEST_PATTERNS) { display_error_message(POP_R+14, "Invalid test range"); return false; } for (int i = n1; i <= n2; i++) { test_list[i].enabled = true; display_enabled(POP_R+12, i, true); } clear_popup_row(POP_R+14); return true; } static void test_selection_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_LM, "Test Selection:"); prints(POP_R+3, POP_LI, " Clear selection"); prints(POP_R+4, POP_LI, " Remove one test"); prints(POP_R+5, POP_LI, " Add one test"); prints(POP_R+6, POP_LI, " Add test range"); prints(POP_R+7, POP_LI, " Add all tests"); prints(POP_R+8, POP_LI, " Exit menu"); display_selection_header(POP_R+10, NUM_TEST_PATTERNS - 1, 0); for (int i = 0; i < NUM_TEST_PATTERNS; i++) { display_enabled(POP_R+12, i, test_list[i].enabled); } bool tty_update = enable_tty; bool exit_menu = false; while (!exit_menu) { bool changed = false; if (tty_update) { tty_send_region(POP_REGION); } tty_update = enable_tty; switch (get_key()) { case '1': changed = set_all_tests(false); break; case '2': changed = add_or_remove_test(false); break; case '3': changed = add_or_remove_test(true); break; case '4': changed = add_test_range(); break; case '5': changed = set_all_tests(true); break; case '0': { clear_popup_row(POP_R+14); int num_selected = 0; for (int i = 0; i < NUM_TEST_PATTERNS; i++) { if (test_list[i].enabled) { num_selected++; } } if (num_selected > 0) { exit_menu = true; } else { display_error_message(POP_R+14, "You must select at least one test"); } } break; default: usleep(1000); tty_update = false; break; } if (changed) { restart = true; changed = false; } } clear_screen_region(POP_REGION); } static void address_range_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_LM, "Address Range:"); prints(POP_R+3, POP_LI, " Set lower limit"); prints(POP_R+4, POP_LI, " Set upper limit"); prints(POP_R+5, POP_LI, " Test all memory"); prints(POP_R+6, POP_LI, " Exit menu"); printf(POP_R+8, POP_LM, "Current range: %kB - %kB", pm_limit_lower << 2, pm_limit_upper << 2); bool tty_update = enable_tty; bool exit_menu = false; while (!exit_menu) { bool changed = false; if (tty_update) { tty_send_region(POP_REGION); } tty_update = enable_tty; switch (get_key()) { case '1': { display_input_message(POP_R+10, "Enter lower limit: "); uintptr_t page = read_value(POP_R+10, POP_LM+19, 15, -PAGE_SHIFT); if (page < pm_limit_upper) { clear_popup_row(POP_R+10); pm_limit_lower = page; changed = true; } else { display_error_message(POP_R+10, "Lower must be less than upper"); } } break; case '2': { display_input_message(POP_R+10, "Enter upper limit: "); uintptr_t page = read_value(POP_R+10, POP_LM+19, 15, -PAGE_SHIFT); if (page > pm_limit_lower) { clear_popup_row(POP_R+10); pm_limit_upper = page; changed = true; } else { display_error_message(POP_R+10, "Upper must be greater than lower"); } } break; case '3': clear_popup_row(POP_R+10); pm_limit_lower = 0; pm_limit_upper = pm_map[pm_map_size - 1].end; changed = true; break; case '0': exit_menu = true; break; default: usleep(1000); tty_update = false; break; } if (changed) { clear_popup_row(POP_R+8); printf(POP_R+8, POP_LM, "Current range: %kB - %kB", pm_limit_lower << 2, pm_limit_upper << 2); update_num_pages_to_test(); restart = true; changed = false; } } clear_screen_region(POP_REGION); } static void set_cpu_mode(cpu_mode_t mode) { printc(POP_R+3+cpu_mode, POP_LM, ' '); cpu_mode = mode; printc(POP_R+3+cpu_mode, POP_LM, '*'); } static void cpu_mode_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_LM, "CPU Sequencing Mode:"); prints(POP_R+3, POP_LI, " Parallel (PAR)"); prints(POP_R+4, POP_LI, " Sequential (SEQ)"); prints(POP_R+5, POP_LI, " Round robin (RR)"); prints(POP_R+6, POP_LI, " Exit menu"); printc(POP_R+3+cpu_mode, POP_LM, '*'); bool tty_update = enable_tty; bool exit_menu = false; while (!exit_menu) { int ch = get_key(); if (tty_update) { tty_send_region(POP_REGION); } tty_update = enable_tty; switch (ch) { case '1': case '2': case '3': set_cpu_mode(ch - '1'); break; case 'u': if (cpu_mode > 0) { set_cpu_mode(cpu_mode - 1); } break; case 'd': if (cpu_mode < 2) { set_cpu_mode(cpu_mode + 1); } break; case '0': exit_menu = true; break; default: usleep(1000); tty_update = false; break; } } clear_screen_region(POP_REGION); } static void set_error_mode(error_mode_t mode) { printc(POP_R+3+error_mode, POP_LM, ' '); error_mode = mode; printc(POP_R+3+error_mode, POP_LM, '*'); } static void error_mode_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_LM, "Error Reporting Mode:"); prints(POP_R+3, POP_LI, " Error counts only"); prints(POP_R+4, POP_LI, " Error summary"); prints(POP_R+5, POP_LI, " Individual errors"); prints(POP_R+6, POP_LI, " BadRAM patterns"); prints(POP_R+7, POP_LI, " Exit menu"); printc(POP_R+3+error_mode, POP_LM, '*'); bool tty_update = enable_tty; bool exit_menu = false; while (!exit_menu) { int ch = get_key(); if (tty_update) { tty_send_region(POP_REGION); } tty_update = enable_tty; switch (ch) { case '1': case '2': case '3': case '4': set_error_mode(ch - '1'); break; case 'u': if (error_mode > 0) { set_error_mode(error_mode - 1); } break; case 'd': if (error_mode < 3) { set_error_mode(error_mode + 1); } break; case '0': exit_menu = true; break; default: usleep(1000); tty_update = false; break; } } clear_screen_region(POP_REGION); } static bool set_all_cpus(cpu_state_t state, int display_offset) { clear_popup_row(POP_R+16); for (int i = 1; i < num_available_cpus; i++) { cpu_state[i] = state; display_enabled(POP_R+12, i - display_offset, state == CPU_STATE_ENABLED); } return true; } static bool add_or_remove_cpu(bool add, int display_offset) { display_input_message(POP_R+16, "Enter CPU #"); int n = read_value(POP_R+16, POP_LM+11, 4, 0); if (n < 1 || n >= num_available_cpus) { display_error_message(POP_R+16, "Invalid CPU number"); return false; } cpu_state[n] = add ? CPU_STATE_ENABLED : CPU_STATE_DISABLED; display_enabled(POP_R+12, n - display_offset, add); clear_popup_row(POP_R+16); return true; } static bool add_cpu_range(int display_offset) { display_input_message(POP_R+16, "Enter first CPU #"); int n1 = read_value(POP_R+16, POP_LM+17, 4, 0); if (n1 < 1 || n1 >= num_available_cpus) { display_error_message(POP_R+16, "Invalid CPU number"); return false; } display_input_message(POP_R+16, "Enter last CPU #"); int n2 = read_value(POP_R+16, POP_LM+16, 4, 0); if (n2 < n1 || n2 >= num_available_cpus) { display_error_message(POP_R+16, "Invalid CPU range"); return false; } for (int i = n1; i <= n2; i++) { cpu_state[i] = CPU_STATE_ENABLED; display_enabled(POP_R+12, i - display_offset, true); } clear_popup_row(POP_R+16); return true; } static void display_cpu_selection(int display_offset) { clear_screen_region(POP_R+11, POP_C, POP_LAST_R, POP_LAST_C); display_selection_header(POP_R+10, num_available_cpus - 1, display_offset); if (display_offset == 0) { printc(POP_R+12, POP_LM, 'B'); } for (int i = 1; i < num_available_cpus; i++) { display_enabled(POP_R+12, i - display_offset, cpu_state[i] == CPU_STATE_ENABLED); } } static void cpu_selection_menu(void) { int display_offset = 0; clear_screen_region(POP_REGION); prints(POP_R+1, POP_LM, "CPU Selection:"); prints(POP_R+3, POP_LI, " Clear selection"); prints(POP_R+4, POP_LI, " Remove one CPU"); prints(POP_R+5, POP_LI, " Add one CPU"); prints(POP_R+6, POP_LI, " Add CPU range"); prints(POP_R+7, POP_LI, " Add all CPUs"); if (cpuid_info.topology.is_hybrid) { if (exclude_ecores) { prints(POP_R+8, POP_LI, " Include E-Cores"); } else { prints(POP_R+8, POP_LI, " Exclude E-Cores"); } } prints(POP_R+9, POP_LI, " Exit menu"); display_cpu_selection(display_offset); bool exit_menu = false; while (!exit_menu) { bool changed = false; switch (get_key()) { case '1': changed = set_all_cpus(false, display_offset); break; case '2': changed = add_or_remove_cpu(false, display_offset); break; case '3': changed = add_or_remove_cpu(true, display_offset); break; case '4': changed = add_cpu_range(display_offset); break; case '5': changed = set_all_cpus(true, display_offset); break; case '6': exclude_ecores = !exclude_ecores; prints(POP_R+8, POP_LI+6, exclude_ecores ? "Exclude" : "Include"); break; case 'u': if (display_offset >= SEL_W) { display_offset -= SEL_W; display_cpu_selection(display_offset); } break; case 'd': if (display_offset < (num_available_cpus - SEL_AREA)) { display_offset += SEL_W; display_cpu_selection(display_offset); } break; case '0': clear_popup_row(POP_R+14); exit_menu = true; break; default: usleep(1000); break; } if (changed) { restart = true; changed = false; } } clear_screen_region(POP_REGION); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void config_init(void) { pm_limit_lower = 0; pm_limit_upper = pm_map[pm_map_size - 1].end; update_num_pages_to_test(); cpu_mode = PAR; error_mode = ERROR_MODE_ADDRESS; for (int i = 0; i < MAX_CPUS; i++) { cpu_state[i] = CPU_STATE_ENABLED; } power_save = POWER_SAVE_HIGH; const boot_params_t *boot_params = (boot_params_t *)boot_params_addr; uintptr_t cmd_line_addr = boot_params->cmd_line_ptr; if (cmd_line_addr != 0) { int cmd_line_size = boot_params->cmd_line_size; if (cmd_line_size == 0) cmd_line_size = 255; cmd_line_addr = map_region(cmd_line_addr, cmd_line_size, true); if (cmd_line_addr != 0) { parse_command_line((char *)cmd_line_addr, cmd_line_size); } } } void config_menu(bool initial) { save_screen_region(POP_REGION, popup_save_buffer); set_background_colour(BLACK); clear_screen_region(POP_REGION); cpu_mode_t old_cpu_mode = cpu_mode; bool tty_update = enable_tty; bool exit_menu = false; while (!exit_menu) { prints(POP_R+1, POP_LM, "Settings:"); prints(POP_R+3, POP_LI, " Test selection"); prints(POP_R+4, POP_LI, " Address range"); prints(POP_R+5, POP_LI, " CPU sequencing mode"); prints(POP_R+6, POP_LI, " Error reporting mode"); if (initial) { if (!smp_enabled) set_foreground_colour(BOLD+BLACK); prints(POP_R+7, POP_LI, " CPU selection"); if (!smp_enabled) set_foreground_colour(WHITE); //if (no_temperature) set_foreground_colour(BOLD+BLACK); printf(POP_R+8, POP_LI, " Temperature %s", enable_temperature ? "disable" : "enable "); //if (no_temperature) set_foreground_colour(WHITE); printf(POP_R+9, POP_LI, " Boot trace %s", enable_trace ? "disable" : "enable "); prints(POP_R+10, POP_LI, " Exit menu"); } else { prints(POP_R+7, POP_LI, " Skip current test"); prints(POP_R+8 , POP_LI, " Exit menu"); } if (tty_update) { tty_send_region(POP_REGION); } tty_update = enable_tty; switch (get_key()) { case '1': test_selection_menu(); break; case '2': address_range_menu(); break; case '3': cpu_mode_menu(); break; case '4': error_mode_menu(); break; case '5': if (initial) { if (smp_enabled) { cpu_selection_menu(); } } else { exit_menu = true; bail = true; } break; case '6': if (initial) { enable_temperature = !enable_temperature; } break; case '7': if (initial) { enable_trace = !enable_trace; } break; case '0': exit_menu = true; break; default: usleep(1000); tty_update = false; break; } } restore_screen_region(POP_REGION, popup_save_buffer); set_background_colour(BLUE); if (enable_tty) { tty_send_region(POP_REGION); } if (cpu_mode != old_cpu_mode) { display_cpu_topology(); restart = true; } if (restart) { bail = true; } } void initial_config(void) { display_initial_notice(); if (num_available_cpus < 2) { smp_enabled = false; } if (pause_at_start) { bool got_key = false; for (int i = 0; i < 3000 && !got_key; i++) { usleep(1000); switch (get_key()) { case ESC: clear_message_area(); display_notice("Rebooting..."); reboot(); break; case '1': config_menu(true); got_key = true; break; case '2': smp_enabled = !smp_enabled; display_initial_notice(); i = 0; break; case ' ': toggle_scroll_lock(); break; case '\n': got_key = true; break; default: break; } } } } memtest86plus-7.20/app/config.h000066400000000000000000000031011471441223100163720ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef CONFIG_H #define CONFIG_H /** * \file * * Provides the configuration settings and pop-up menu. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ #include #include #include "smp.h" #include "cpuid.h" typedef enum { PAR, SEQ, ONE } cpu_mode_t; typedef enum { ERROR_MODE_NONE, ERROR_MODE_SUMMARY, ERROR_MODE_ADDRESS, ERROR_MODE_BADRAM } error_mode_t; typedef enum { POWER_SAVE_OFF, POWER_SAVE_LOW, POWER_SAVE_HIGH } power_save_t; extern uintptr_t pm_limit_lower; extern uintptr_t pm_limit_upper; extern uintptr_t num_pages_to_test; extern cpu_mode_t cpu_mode; extern error_mode_t error_mode; extern cpu_state_t cpu_state[MAX_CPUS]; extern core_type_t hybrid_core_type[MAX_CPUS]; extern bool exclude_ecores; extern bool smp_enabled; extern bool enable_big_status; extern bool enable_temperature; extern bool enable_trace; extern bool enable_sm; extern bool enable_tty; extern bool enable_bench; extern bool enable_mch_read; extern bool enable_ecc_polling; extern bool enable_numa; extern bool pause_at_start; extern power_save_t power_save; extern uintptr_t tty_address; extern int tty_baud_rate; extern int tty_update_period; extern uint32_t tty_mmio_ref_clk; extern int tty_mmio_stride; extern bool err_banner_redraw; void config_init(void); void config_menu(bool initial); void initial_config(void); #endif // CONFIG_H memtest86plus-7.20/app/display.c000066400000000000000000000442151471441223100166000ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. // Copyright (C) 2004-2023 Sam Demeulemeester. #include #include #include "cpuid.h" #include "cpuinfo.h" #include "hwctrl.h" #include "io.h" #include "keyboard.h" #include "memctrl.h" #include "serial.h" #include "pmem.h" #include "smbios.h" #include "smbus.h" #include "spd.h" #include "temperature.h" #include "tsc.h" #include "barrier.h" #include "spinlock.h" #include "config.h" #include "error.h" #include "build_version.h" #include "tests.h" #include "display.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define POP_STAT_R 12 #define POP_STAT_C 18 #define POP_STAT_W 44 #define POP_STAT_H 11 #define POP_STAT_LAST_R (POP_STAT_R + POP_STAT_H - 1) #define POP_STAT_LAST_C (POP_STAT_C + POP_STAT_W - 1) #define POP_STATUS_REGION POP_STAT_R, POP_STAT_C, POP_STAT_LAST_R, POP_STAT_LAST_C #define SPINNER_PERIOD 100 // milliseconds #define NUM_SPIN_STATES 4 static const char spin_state[NUM_SPIN_STATES] = { '|', '/', '-', '\\' }; static const char cpu_mode_str[3][4] = { "PAR", "SEQ", "RR " }; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static bool scroll_lock = false; static bool scroll_wait = false; static int spin_idx = 0; // current spinner position static int pass_ticks = 0; // current value (ticks_per_pass is final value) static int test_ticks = 0; // current value (ticks_per_test is final value) static int pass_bar_length = 0; // currently displayed length static int test_bar_length = 0; // currently displayed length static uint64_t run_start_time = 0; // TSC time stamp static uint64_t next_spin_time = 0; // TSC time stamp static int prev_sec = -1; // previous second static bool timed_update_done = false; // update cycle status bool big_status_displayed = false; static uint16_t popup_status_save_buffer[POP_STAT_W * POP_STAT_H]; //------------------------------------------------------------------------------ // Variables //------------------------------------------------------------------------------ int scroll_message_row; int max_cpu_temp = 0; display_mode_t display_mode = DISPLAY_MODE_NA; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void display_init(void) { cursor_off(); clear_screen(); /* The commented horizontal lines provide visual cue for where and how * they will appear on the screen. They are drawn down below using * Extended ASCII characters. */ set_foreground_colour(BLACK); set_background_colour(WHITE); clear_screen_region(0, 0, 0, 27); prints(0, 0, " Memtest86+ v" MT_VERSION); set_foreground_colour(RED); printc(0, 15, '+'); set_foreground_colour(WHITE); set_background_colour(BLUE); prints(1, 0, "CLK/Temp: N/A | Pass %"); prints(2, 0, "L1 Cache: N/A | Test %"); prints(3, 0, "L2 Cache: N/A | Test #"); prints(4, 0, "L3 Cache: N/A | Testing:"); prints(5, 0, "Memory : N/A | Pattern:"); // prints(6, 0, "--------------------------------------------------------------------------------"); prints(7, 0, "CPU: SMP: N/A | Time: Status: Init."); prints(8, 0, "Using: | Pass: Errors:"); // prints(9, 0, "--------------------------------------------------------------------------------"); if (ecc_status.ecc_enabled) { prints(8, 57, "Err: ECC:"); } for (int i = 0;i < 80; i++) { print_char(6, i, 0xc4); print_char(9, i, 0xc4); } for (int i = 0; i < 6; i++) { print_char(i, 28, 0xb3); } for (int i = 7; i < 10; i++) { print_char(i, 42, 0xb3); } print_char(6, 28, 0xc1); print_char(6, 42, 0xc2); print_char(9, 42, 0xc1); set_foreground_colour(BLUE); set_background_colour(WHITE); clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1); prints(ROW_FOOTER, 0, " Exit Configuration Scroll Lock"); prints(ROW_FOOTER, 64, MT_VERSION "." GIT_HASH); #if defined (__x86_64__) prints(ROW_FOOTER, 74, ".x64"); #elif defined (__i386__) prints(ROW_FOOTER, 74, ".x32"); #elif defined (__loongarch_lp64) prints(ROW_FOOTER, 74, ".la64"); #endif set_foreground_colour(WHITE); set_background_colour(BLUE); if (cpu_model) { display_cpu_model(cpu_model); } if (clks_per_msec) { display_cpu_clk((int)(clks_per_msec / 1000)); } #if TESTWORD_WIDTH < 64 if (cpuid_info.flags.lm) { display_cpu_addr_mode(" [LM]"); } else if (cpuid_info.flags.pae) { display_cpu_addr_mode("[PAE]"); } #endif if (l1_cache) { display_l1_cache_size(l1_cache); } if (l2_cache) { display_l2_cache_size(l2_cache); } if (l3_cache) { display_l3_cache_size(l3_cache); } if (l1_cache_speed) { display_l1_cache_speed(l1_cache_speed); } if (l2_cache_speed) { display_l2_cache_speed(l2_cache_speed); } if (l3_cache_speed) { display_l3_cache_speed(l3_cache_speed); } if (ram_speed) { display_ram_speed(ram_speed); } if (num_pm_pages) { // Round to nearest MB. display_memory_size(1024 * ((num_pm_pages + 128) / 256)); } scroll_message_row = ROW_SCROLL_T; } void display_cpu_topology(void) { extern int num_enabled_cpus; int num_cpu_sockets = 1; // Display Thread Count and Thread Dispatch Mode if (smp_enabled) { if (cpuid_info.topology.is_hybrid && cpuid_info.topology.ecore_count > 0 && exclude_ecores) { display_threading(num_enabled_cpus - cpuid_info.topology.ecore_count, cpu_mode_str[cpu_mode]); } else { display_threading(num_enabled_cpus, cpu_mode_str[cpu_mode]); } } else { display_threading_disabled(); } // If topology failed, assume topology according to APIC if (cpuid_info.topology.core_count <= 0) { cpuid_info.topology.core_count = num_enabled_cpus; cpuid_info.topology.thread_count = num_enabled_cpus; if(cpuid_info.flags.htt && num_enabled_cpus >= 2 && num_enabled_cpus % 2 == 0) { cpuid_info.topology.core_count /= 2; } } // Compute number of sockets according to individual CPU core count if (num_enabled_cpus > cpuid_info.topology.thread_count && num_enabled_cpus % cpuid_info.topology.thread_count == 0) { num_cpu_sockets = num_enabled_cpus / cpuid_info.topology.thread_count; } // Display P/E-Core count for Hybrid CPUs. if (cpuid_info.topology.is_hybrid) { if (cpuid_info.topology.pcore_count > 1) { if (cpuid_info.flags.htt && (cpuid_info.topology.thread_count - cpuid_info.topology.ecore_count) == cpuid_info.topology.pcore_count) { cpuid_info.topology.pcore_count /= 2; } display_cpu_topo_hybrid(cpuid_info.topology.pcore_count, cpuid_info.topology.ecore_count, cpuid_info.topology.thread_count); } else { display_cpu_topo_hybrid_short(cpuid_info.topology.thread_count); } return; } // Condensed display for multi-socket motherboard if (num_cpu_sockets > 1) { display_cpu_topo_multi_socket(num_cpu_sockets, num_cpu_sockets * cpuid_info.topology.core_count, num_cpu_sockets * cpuid_info.topology.thread_count); return; } if (cpuid_info.topology.thread_count < 100) { display_cpu_topo(cpuid_info.topology.core_count, cpuid_info.topology.thread_count); } else { display_cpu_topo_short(cpuid_info.topology.core_count, cpuid_info.topology.thread_count); } } void post_display_init(void) { print_smbios_startup_info(); print_spd_startup_info(); if (imc.freq) { // Try to get RAM information from IMC display_spec_mode("IMC: "); if (imc.type[3] == '5') { display_spec_ddr5(imc.freq, imc.type, imc.tCL, imc.tCL_dec, imc.tRCD, imc.tRP, imc.tRAS); } else { display_spec_ddr(imc.freq, imc.type, imc.tCL, imc.tCL_dec, imc.tRCD, imc.tRP, imc.tRAS); } display_mode = DISPLAY_MODE_IMC; } else if (ram.freq > 0 && ram.tCL > 0) { // If not available, grab max memory specs from SPD display_spec_mode("RAM: "); if (ram.freq <= 166) { display_spec_sdr(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS); } else { display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tCL_dec, ram.tRCD, ram.tRP, ram.tRAS); } display_mode = DISPLAY_MODE_SPD; } else { // If nothing available, fallback to "Using Core" Display display_mode = DISPLAY_MODE_NA; } } void display_start_run(void) { if (!enable_trace && !enable_sm) { clear_message_area(); } clear_screen_region(7, 49, 7, 57); // run time if (ecc_status.ecc_enabled) { clear_screen_region(8, 49, 8, 53); // pass number clear_screen_region(8, 61, 8, 68); // error count clear_screen_region(8, 74, 8, SCREEN_WIDTH - 1); // ecc error count } else { clear_screen_region(8, 49, 8, 59); // pass number clear_screen_region(8, 68, 8, SCREEN_WIDTH - 1); // error count } display_pass_count(0); error_count = 0; display_error_count(); if (clks_per_msec > 0) { // If we've measured the CPU speed, we know the TSC is available. run_start_time = get_tsc(); next_spin_time = run_start_time + SPINNER_PERIOD * clks_per_msec; } display_spinner('-'); display_status("Testing"); if (enable_tty){ tty_full_redraw(); } } void display_start_pass(void) { clear_screen_region(1, 39, 1, SCREEN_WIDTH - 1); // progress bar display_pass_percentage(0); pass_bar_length = 0; pass_ticks = 0; } void display_start_test(void) { clear_screen_region(2, 39, 3, SCREEN_WIDTH - 1); // progress bar, test details clear_screen_region(4, 39, 4, SCREEN_WIDTH - 6); // Avoid erasing paging mode clear_screen_region(5, 39, 5, SCREEN_WIDTH - 1); clear_screen_region(3, 36, 3, 37); // test number display_test_percentage(0); display_test_number(test_num); display_test_description(test_list[test_num].description); test_bar_length = 0; test_ticks = 0; #if 0 uint64_t current_time = get_tsc(); int secs = (current_time - run_start_time) / (1000 * (uint64_t)clks_per_msec); int mins = secs / 60; secs %= 60; int hours = mins / 60; mins %= 60; do_trace(0, "T %i: %i:%02i:%02i", test_num, hours, mins, secs); #endif } void display_error_count(void) { if (ecc_status.ecc_enabled) { display_err_count_with_ecc(error_count, error_count_cecc); } else { display_err_count_without_ecc(error_count); } } void display_temperature(void) { if (!enable_temperature) { return; } int actual_cpu_temp = get_cpu_temperature(); if (actual_cpu_temp == 0) { if (max_cpu_temp == 0) { enable_temperature = false; } return; } if (max_cpu_temp < actual_cpu_temp ) { max_cpu_temp = actual_cpu_temp; } int offset = actual_cpu_temp / 100 + max_cpu_temp / 100; clear_screen_region(1, 18, 1, 22); printf(1, 20-offset, "%2i/%2i%cC", actual_cpu_temp, max_cpu_temp, 0xF8); } void display_big_status(bool pass) { if (!enable_big_status || big_status_displayed) { return; } save_screen_region(POP_STATUS_REGION, popup_status_save_buffer); set_background_colour(BLACK); set_foreground_colour(pass ? GREEN : RED); clear_screen_region(POP_STATUS_REGION); if (pass) { prints(POP_STAT_R+1, POP_STAT_C+5, "###### ## ##### ##### "); prints(POP_STAT_R+2, POP_STAT_C+5, "## ## #### ## ## ## ## "); prints(POP_STAT_R+3, POP_STAT_C+5, "## ## ## ## ## ## "); prints(POP_STAT_R+4, POP_STAT_C+5, "###### ## ## ##### ##### "); prints(POP_STAT_R+5, POP_STAT_C+5, "## ######## ## ## "); prints(POP_STAT_R+6, POP_STAT_C+5, "## ## ## ## ## ## ## "); prints(POP_STAT_R+7, POP_STAT_C+5, "## ## ## ##### ##### "); } else { prints(POP_STAT_R+1, POP_STAT_C+5, "####### ## ###### ## "); prints(POP_STAT_R+2, POP_STAT_C+5, "## #### ## ## "); prints(POP_STAT_R+3, POP_STAT_C+5, "## ## ## ## ## "); prints(POP_STAT_R+4, POP_STAT_C+5, "##### ## ## ## ## "); prints(POP_STAT_R+5, POP_STAT_C+5, "## ######## ## ## "); prints(POP_STAT_R+6, POP_STAT_C+5, "## ## ## ## ## "); prints(POP_STAT_R+7, POP_STAT_C+5, "## ## ## ###### ###### "); } prints(POP_STAT_R+8, POP_STAT_C+5, " "); prints(POP_STAT_R+9, POP_STAT_C+5, "Press any key to remove this banner "); set_background_colour(BLUE); set_foreground_colour(WHITE); big_status_displayed = true; } void restore_big_status(void) { if (!big_status_displayed) { return; } restore_screen_region(POP_STATUS_REGION, popup_status_save_buffer); big_status_displayed = false; } void check_input(void) { char input_key = get_key(); if (input_key == '\0') { return; } else if (big_status_displayed) { restore_big_status(); enable_big_status = false; } switch (input_key) { case ESC: clear_message_area(); display_notice("Rebooting..."); reboot(); break; case '1': config_menu(false); break; case ' ': set_scroll_lock(!scroll_lock); break; case '\n': scroll_wait = false; break; default: break; } } void set_scroll_lock(bool enabled) { scroll_lock = enabled; set_foreground_colour(BLUE); prints(ROW_FOOTER, 48, scroll_lock ? "unlock" : "lock "); set_foreground_colour(WHITE); } void toggle_scroll_lock(void) { set_scroll_lock(!scroll_lock); } void scroll(void) { if (scroll_message_row < ROW_SCROLL_B) { scroll_message_row++; } else { if (scroll_lock) { display_footer_message(" Single step "); } scroll_wait = true; do { check_input(); } while (scroll_wait && scroll_lock); scroll_wait = false; clear_footer_message(); scroll_screen_region(ROW_SCROLL_T, 0, ROW_SCROLL_B, SCREEN_WIDTH - 1); } } void do_tick(int my_cpu) { int act_sec = 0; bool use_spin_wait = (power_save < POWER_SAVE_HIGH); if (use_spin_wait) { barrier_spin_wait(run_barrier); } else { barrier_halt_wait(run_barrier); } if (master_cpu == my_cpu) { check_input(); error_update(); } if (use_spin_wait) { barrier_spin_wait(run_barrier); } else { barrier_halt_wait(run_barrier); } // Only the master CPU does the update. if (master_cpu != my_cpu) { return; } test_ticks++; pass_ticks++; pass_type_t pass_type = (pass_num == 0) ? FAST_PASS : FULL_PASS; int pct = 0; if (ticks_per_test[pass_type][test_num] > 0) { pct = 100 * test_ticks / ticks_per_test[pass_type][test_num]; if (pct > 100) { pct = 100; } } display_test_percentage(pct); display_test_bar((BAR_LENGTH * pct) / 100); pct = 0; if (ticks_per_pass[pass_type] > 0) { pct = 100 * pass_ticks / ticks_per_pass[pass_type]; if (pct > 100) { pct = 100; } } display_pass_percentage(pct); display_pass_bar((BAR_LENGTH * pct) / 100); bool update_spinner = true; if (clks_per_msec > 0) { uint64_t current_time = get_tsc(); int secs = (current_time - run_start_time) / (1000 * (uint64_t)clks_per_msec); int mins = secs / 60; secs %= 60; act_sec = secs; int hours = mins / 60; mins %= 60; display_run_time(hours, mins, secs); if (current_time >= next_spin_time) { next_spin_time = current_time + SPINNER_PERIOD * clks_per_msec; } else { update_spinner = false; } } /* --------------- * Timed functions * --------------- */ // update spinner every SPINNER_PERIOD ms if (update_spinner) { spin_idx = (spin_idx + 1) % NUM_SPIN_STATES; display_spinner(spin_state[spin_idx]); } // This only tick one time per second if (!timed_update_done) { // Display FAIL banner if (new) errors detected if (err_banner_redraw && !big_status_displayed && error_count > 1) { display_big_status(false); } // Check ECC Errors memctrl_poll_ecc(); // Update temperature display_temperature(); // Update TTY one time every TTY_UPDATE_PERIOD second(s) if (enable_tty) { if (act_sec % tty_update_period == 0) { tty_partial_redraw(); } } timed_update_done = true; } if (act_sec != prev_sec) { prev_sec = act_sec; timed_update_done = false; } } void do_trace(int my_cpu, const char *fmt, ...) { va_list args; va_start(args, fmt); spin_lock(error_mutex); scroll(); printi(scroll_message_row, 0, my_cpu, 2, false, false); vprintf(scroll_message_row, 4, fmt, args); spin_unlock(error_mutex); va_end(args); } memtest86plus-7.20/app/display.h000066400000000000000000000162301471441223100166010ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef DISPLAY_H #define DISPLAY_H /** * \file * * Provides (macro) functions that implement the UI display. * All knowledge about the display layout is encapsulated here. * *//* * Copyright (C) 2020-2022 Martin Whitaker. * Copyright (C) 2004-2023 Sam Demeulemeester. */ #include #include "screen.h" #include "print.h" #include "string.h" #include "test.h" #define ROW_SPD 13 #define ROW_MESSAGE_T 10 #define ROW_MESSAGE_B (SCREEN_HEIGHT - 2) #define ROW_SCROLL_T (ROW_MESSAGE_T + 2) #define ROW_SCROLL_B (SCREEN_HEIGHT - 2) #define ROW_FOOTER (SCREEN_HEIGHT - 1) #define BAR_LENGTH 40 #define ERROR_LIMIT UINT64_C(999999999999) typedef enum { DISPLAY_MODE_NA, DISPLAY_MODE_SPD, DISPLAY_MODE_IMC } display_mode_t; #define display_cpu_model(str) \ prints(0, 30, str) #define display_cpu_clk(freq) \ printf(1, 10, "%iMHz", freq) #define display_cpu_addr_mode(str) \ prints(4, 75, str) #define display_l1_cache_size(size) \ printf(2, 9, "%6kB", (uintptr_t)(size)) #define display_l2_cache_size(size) \ printf(3, 9, "%6kB", (uintptr_t)(size)) #define display_l3_cache_size(size) \ printf(4, 9, "%6kB", (uintptr_t)(size)) #define display_memory_size(size) \ printf(5, 9, "%6kB", (uintptr_t)(size)) #define display_l1_cache_speed(speed) \ printf(2, 18, "%S6kB/s", (uintptr_t)(speed)) #define display_l2_cache_speed(speed) \ printf(3, 18, "%S6kB/s", (uintptr_t)(speed)) #define display_l3_cache_speed(speed) \ printf(4, 18, "%S6kB/s", (uintptr_t)(speed)) #define display_ram_speed(speed) \ printf(5, 18, "%S6kB/s", (uintptr_t)(speed)) #define display_status(status) \ prints(7, 68, status) #define display_threading(nb, mode) \ printf(7,31, "%uT (%s)", nb, mode) #define display_threading_disabled() \ prints(7,31, "Disabled") #define display_cpu_topo_hybrid(num_pcores, num_ecores, num_threads) \ { \ clear_screen_region(7, 5, 7, 25); \ printf(7, 5, "%uP+%uE-Cores (%uT)", num_pcores, num_ecores, num_threads); \ } #define display_cpu_topo_hybrid_short(num_threads) \ printf(7, 5, "%u Threads (Hybrid)", num_threads) #define display_cpu_topo_multi_socket(num_sockets, num_cores, num_threads) \ printf(7, 5, "%uS / %uC / %uT", num_sockets, num_cores, num_threads) #define display_cpu_topo( num_cores, num_threads) \ printf(7, 5, "%u Cores %u Threads", num_cores, num_threads) #define display_cpu_topo_short( num_cores, num_threads) \ printf(7, 5, "%u Cores (%uT)", num_cores, num_threads) #define display_spec_mode(mode) \ prints(8,0, mode); #define display_spec_ddr5(freq, type, cl, cl_dec, rcd, rp, ras) \ printf(8,5, "%s-%u / CAS %u%s-%u-%u-%u", \ type, freq, cl, cl_dec?".5":"", rcd, rp, ras); #define display_spec_ddr(freq, type, cl, cl_dec, rcd, rp, ras) \ printf(8,5, "%uMHz (%s-%u) CAS %u%s-%u-%u-%u", \ freq / 2, type, freq, cl, cl_dec?".5":"", rcd, rp, ras); #define display_spec_sdr(freq, type, cl, rcd, rp, ras) \ printf(8,5, "%uMHz (%s PC%u) CAS %u-%u-%u-%u", \ freq, type, freq, cl, rcd, rp, ras); #define display_dmi_mb(sys_ma, sys_sku) \ dmicol = prints(23, dmicol, sys_man); \ prints(23, dmicol + 1, sys_sku); #define display_active_cpu(cpu_num) \ prints(8, 7, "Core #"); \ printi(8, 13, cpu_num, 3, false, true) #define display_all_active() \ prints(8, 7, "All Cores") #define display_spinner(spin_state) \ printc(7, 77, spin_state) #define display_pass_percentage(pct) \ printi(1, 34, pct, 3, false, false) #define display_pass_bar(length) \ while (length > pass_bar_length) { \ printc(1, 39 + pass_bar_length, '#'); \ pass_bar_length++; \ } #define display_test_percentage(pct) \ printi(2, 34, pct, 3, false, false) #define display_test_bar(length) \ while (length > test_bar_length) { \ printc(2, 39 + test_bar_length, '#'); \ test_bar_length++; \ } #define display_test_number(number) \ printi(3, 36, number, 2, false, true) #define display_test_description(str) \ prints(3, 39, str) #define display_test_addresses(pb, pe, total) \ { \ clear_screen_region(4, 39, 4, SCREEN_WIDTH - 6); \ printf(4, 39, "%kB - %kB [%kB of %kB]", pb, pe, (pe) - (pb), total); \ } #define display_test_stage_description(...) \ { \ clear_screen_region(4, 39, 4, SCREEN_WIDTH - 6); \ printf(4, 39, __VA_ARGS__); \ } #define display_test_pattern_name(str) \ { \ clear_screen_region(5, 39, 5, SCREEN_WIDTH - 1); \ prints(5, 39, str); \ } #define display_test_pattern_value(pattern) \ { \ clear_screen_region(5, 39, 5, SCREEN_WIDTH - 1); \ printf(5, 39, "0x%0*x", TESTWORD_DIGITS, pattern); \ } #define display_test_pattern_values(pattern, offset) \ { \ clear_screen_region(5, 39, 5, SCREEN_WIDTH - 1); \ printf(5, 39, "0x%0*x - %i", TESTWORD_DIGITS, pattern, offset); \ } #define display_run_time(hours, mins, secs) \ printf(7, 51, "%i:%02i:%02i", hours, mins, secs) #define display_pass_count(count) \ printi(8, 51, count, 0, false, true) #define display_err_count_without_ecc(count) \ printi(8, 68, count, 0, false, true) #define display_err_count_with_ecc(count_err, count_ecc) \ { \ printi(8, 62, count_err, 0, false, true); \ printi(8, 74, count_ecc, 0, false, true); \ } #define clear_message_area() \ { \ clear_screen_region(ROW_MESSAGE_T, 0, ROW_MESSAGE_B, SCREEN_WIDTH - 1); \ scroll_message_row = ROW_SCROLL_T - 1; \ } #define display_pinned_message(row, col, ...) \ printf(ROW_MESSAGE_T + row, col, __VA_ARGS__) #define display_scrolled_message(col, ...) \ printf(scroll_message_row, col, __VA_ARGS__) #define display_notice(str) \ prints(ROW_MESSAGE_T + 8, (SCREEN_WIDTH - strlen(str)) / 2, str) #define display_notice_with_args(length, ...) \ printf(ROW_MESSAGE_T + 8, (SCREEN_WIDTH - length) / 2, __VA_ARGS__) #define clear_footer_message() \ { \ set_background_colour(WHITE); \ clear_screen_region(ROW_FOOTER, 56, ROW_FOOTER, SCREEN_WIDTH - 1); \ set_background_colour(BLUE); \ } #define display_footer_message(str) \ { \ set_foreground_colour(BLUE); \ prints(ROW_FOOTER, 56, str); \ set_foreground_colour(WHITE); \ } #define trace(my_cpu, ...) \ if (enable_trace) do_trace(my_cpu, __VA_ARGS__) #define display_msr_failed_flag() \ printc(0, SCREEN_WIDTH - 1, '*'); extern int scroll_message_row; extern display_mode_t display_mode; void display_init(void); void display_cpu_topology(void); void post_display_init(void); void display_start_run(void); void display_start_pass(void); void display_start_test(void); void display_error_count(void); void display_temperature(void); void display_big_status(bool pass); void restore_big_status(void); void check_input(void); void set_scroll_lock(bool enabled); void toggle_scroll_lock(void); void scroll(void); void do_tick(int my_cpu); void do_trace(int my_cpu, const char *fmt, ...); #endif // DISPLAY_H memtest86plus-7.20/app/error.c000066400000000000000000000316711471441223100162660ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. // // Derived from memtest86+ error.c // // error.c - MemTest-86 Version 4.1 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include #include "smp.h" #include "vmem.h" #include "badram.h" #include "config.h" #include "display.h" #include "test.h" #include "tests.h" #include "serial.h" #include "memctrl.h" #include "error.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #ifndef USB_WORKAROUND #define USB_WORKAROUND 1 #endif //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef enum { ADDR_ERROR, DATA_ERROR, PARITY_ERROR, UECC_ERROR, CECC_ERROR, NEW_MODE } error_type_t; typedef struct { uintptr_t page; uintptr_t offset; } page_offs_t; typedef struct { page_offs_t min_addr; page_offs_t max_addr; testword_t bad_bits; int min_bits; int max_bits; uint64_t total_bits; uintptr_t run_length; uintptr_t max_run; uintptr_t last_addr; testword_t last_xor; } error_info_t; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static error_mode_t last_error_mode = ERROR_MODE_NONE; static error_info_t error_info; //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ uint64_t error_count = 0; uint64_t error_count_cecc = 0; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static bool update_error_info(testword_t page, testword_t offset, uintptr_t addr, testword_t xor) { bool update_stats = false; // Update address range. if (error_info.min_addr.page > page) { error_info.min_addr.page = page; error_info.min_addr.offset = offset; update_stats = true; } else if (error_info.min_addr.page == page && error_info.min_addr.offset > offset) { error_info.min_addr.offset = offset; update_stats = true; } if (error_info.max_addr.page < page) { error_info.max_addr.page = page; error_info.max_addr.offset = offset; update_stats = true; } else if (error_info.max_addr.page == page && error_info.max_addr.offset < offset) { error_info.max_addr.offset = offset; update_stats = true; } // Update bits in error. int bits = 0; for (int i = 0; i < TESTWORD_WIDTH; i++) { if ((xor >> i) & 1) { bits++; } } if (bits > 0 && error_count < ERROR_LIMIT) { error_info.total_bits += bits; } if (bits > error_info.max_bits) { error_info.max_bits = bits; update_stats = true; } if (bits < error_info.min_bits) { error_info.min_bits = bits; update_stats = true; } if (error_info.bad_bits ^ xor) { update_stats = true; } error_info.bad_bits |= xor; // Update max contiguous range. if (error_info.max_run > 0) { if (addr == error_info.last_addr + sizeof(testword_t) || addr == error_info.last_addr - sizeof(testword_t)) { error_info.run_length++; } else { error_info.run_length = 1; } } else { error_info.run_length = 1; } if (error_info.run_length > error_info.max_run) { error_info.max_run = error_info.run_length; update_stats = true; } return update_stats; } static void common_err(error_type_t type, uintptr_t addr, testword_t good, testword_t bad, bool use_for_badram) { spin_lock(error_mutex); restore_big_status(); bool new_header = (error_count == 0 && error_count_cecc == 0) || (error_mode != last_error_mode); if (new_header) { clear_message_area(); badram_init(); } last_error_mode = error_mode; testword_t xor = good ^ bad; bool new_stats = false; testword_t page = page_of((void *)addr); testword_t offset = addr & (PAGE_SIZE - 1); switch (type) { case ADDR_ERROR: new_stats = update_error_info(page, offset, addr, 0); break; case DATA_ERROR: new_stats = update_error_info(page, offset, addr, xor); break; case NEW_MODE: new_stats = (error_count > 0); default: break; } bool new_address = (type != NEW_MODE); bool new_badram = false; if (error_mode == ERROR_MODE_BADRAM && use_for_badram) { new_badram = badram_insert(page, offset); } if (new_address) { if (type == CECC_ERROR) { if ((error_count_cecc + ecc_status.count) < 999999) { error_count_cecc += ecc_status.count; } } else { if (error_count < ERROR_LIMIT) { error_count++; } if (test_list[test_num].errors < INT_MAX) { test_list[test_num].errors++; } } } switch (error_mode) { case ERROR_MODE_SUMMARY: if (type == PARITY_ERROR) { break; } if (new_header) { display_pinned_message(0, 1, " Lowest Error Address:"); display_pinned_message(1, 1, " Highest Error Address:"); display_pinned_message(2, 1, " Bits in Error Mask:"); display_pinned_message(3, 1, " Bits in Error - Total:"); display_pinned_message(4, 1, " Max Contiguous Errors:"); display_pinned_message(0, 64, "Test Errors"); for (int i = 0; i < NUM_TEST_PATTERNS; i++) { display_pinned_message(1 + i, 65, "%2i:", i); } } if (new_stats) { int bits = 0; for (int i = 0; i < TESTWORD_WIDTH; i++) { if (error_info.bad_bits >> i & 1) { bits++; } } display_pinned_message(0, 25, "%09x%03x (%kB)", error_info.min_addr.page, error_info.min_addr.offset, error_info.min_addr.page << 2); display_pinned_message(1, 25, "%09x%03x (%kB)", error_info.max_addr.page, error_info.max_addr.offset, error_info.max_addr.page << 2); display_pinned_message(2, 25, "%0*x", TESTWORD_DIGITS, error_info.bad_bits); display_pinned_message(3, 25, " %2i Min: %2i Max: %2i Avg: %2i", bits, error_info.min_bits, error_info.max_bits, (int)(error_info.total_bits / error_count)); display_pinned_message(4, 25, "%u", error_info.max_run); for (int i = 0; i < NUM_TEST_PATTERNS; i++) { display_pinned_message(1 + i, 69, "%c%i", test_list[i].errors == INT_MAX ? '>' : ' ', test_list[i].errors); } display_error_count(); } break; case ERROR_MODE_ADDRESS: // Skip duplicates. if (!new_header && addr == error_info.last_addr && xor == error_info.last_xor) { break; } if (new_header) { #if TESTWORD_WIDTH > 32 // columns: 0---------1---------2---------3---------4---------5---------6---------7--------- display_pinned_message(0, 0, "pCPU Pass Test Failing Address Expected Found "); display_pinned_message(1, 0, "---- ---- ---- --------------------- ---------------- ----------------"); // fields: NN NNNN NN PPPPPPPPPOOO (N.NN?B) XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX #else // columns: 0---------1---------2---------3---------4---------5---------6---------7--------- display_pinned_message(0, 0, "pCPU Pass Test Failing Address Expected Found Err Bits"); display_pinned_message(1, 0, "---- ---- ---- --------------------- -------- -------- --------"); // fields: NN NNNN NN PPPPPPPPPOOO (N.NN?B) XXXXXXXX XXXXXXXX XXXXXXXX #endif } if (new_address) { check_input(); scroll(); set_foreground_colour(YELLOW); display_scrolled_message(0, " %2i %4i %2i %09x%03x (%kB)", type != CECC_ERROR ? smp_my_cpu_num() : ecc_status.core, pass_num, test_num, page, offset, page << 2); if (type == PARITY_ERROR) { display_scrolled_message(41, "%s", "Parity error detected near this address"); } else if (type == CECC_ERROR) { display_scrolled_message(41, "%s%2i", "Correctable ECC Error - CH#", ecc_status.channel); } else { #if TESTWORD_WIDTH > 32 display_scrolled_message(41, "%016x %016x", good, bad); #else display_scrolled_message(41, "%08x %08x %08x %i", good, bad, xor, error_count); #endif } set_foreground_colour(WHITE); display_error_count(); } break; case ERROR_MODE_BADRAM: if (new_badram) { badram_display(); } break; default: break; } if (type != PARITY_ERROR && type != CECC_ERROR) { error_info.last_addr = addr; error_info.last_xor = xor; } spin_unlock(error_mutex); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void error_init(void) { error_info.min_addr.page = UINTPTR_MAX; error_info.min_addr.offset = PAGE_SIZE - 1; error_info.max_addr.page = 0; error_info.max_addr.offset = 0; error_info.bad_bits = 0; error_info.min_bits = 255; error_info.max_bits = 0; error_info.total_bits = 0; error_info.run_length = 0; error_info.max_run = 0; error_info.last_addr = 0; error_info.last_xor = 0; error_count = 0; } void addr_error(testword_t *addr1, testword_t *addr2, testword_t good, testword_t bad) { common_err(ADDR_ERROR, (uintptr_t)addr1, good, bad, false); (void)addr2; } void data_error(testword_t *addr, testword_t good, testword_t bad, bool use_for_badram) { #if USB_WORKAROUND /* Skip any errors that appear to be due to the BIOS using location * 0x4e0 for USB keyboard support. This often happens with Intel * 810, 815 and 820 chipsets. It is possible that we will skip * a real error but the odds are very low. */ if ((uintptr_t)addr == 0x4e0 || (uintptr_t)addr == 0x410) { return; } #endif common_err(DATA_ERROR, (uintptr_t)addr, good, bad, use_for_badram); } void ecc_error() { common_err(CECC_ERROR, ecc_status.addr, 0, 0, false); error_update(); } #if REPORT_PARITY_ERRORS void parity_error(void) { // We don't know the real address that caused the parity error, // so use the last recorded test address. common_err(PARITY_ERROR, test_addr[my_cpu_num()], 0, 0, false); } #endif void error_update(void) { if (error_count > 0 || error_count_cecc > 0) { if (error_mode != last_error_mode) { common_err(NEW_MODE, 0, 0, 0, false); } if (error_mode == ERROR_MODE_SUMMARY && test_list[test_num].errors > 0) { display_pinned_message(1 + test_num, 69, "%c%i", test_list[test_num].errors == INT_MAX ? '>' : ' ', test_list[test_num].errors); } display_error_count(); // Only fail if error is uncorrected if (error_count > 0) { display_status("Failed!"); // Display FAIL banner on first uncorrectable error if (error_count == 1) { display_big_status(false); } } if (enable_tty) { tty_error_redraw(); } } } memtest86plus-7.20/app/error.h000066400000000000000000000022431471441223100162640ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef ERROR_H #define ERROR_H /** * \file * * Provides functions that can be called by the memory tests to report errors. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ #include #include #include "test.h" /** * The number of errors recorded during the current run. */ extern uint64_t error_count; /** * The number of correctable ECC errors recorded during the current run. */ extern uint64_t error_count_cecc; /** * Initialises the error records. */ void error_init(void); /** * Adds an address error to the error reports. */ void addr_error(testword_t *addr1, testword_t *addr2, testword_t good, testword_t bad); /** * Adds a data error to the error reports. */ void data_error(testword_t *addr, testword_t good, testword_t bad, bool use_for_badram); /** * Adds an ECC error to the error reports. * ECC Error details are stored in ecc_status */ void ecc_error(); #if REPORT_PARITY_ERRORS /** * Adds a parity error to the error reports. */ void parity_error(void); #endif /** * Refreshes the error display after the error mode is changed. */ void error_update(void); #endif // ERROR_H memtest86plus-7.20/app/interrupt.c000066400000000000000000000170561471441223100171720ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2024 Martin Whitaker. // // Derived from extract of memtest86+ lib.c: // // lib.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include "cpuid.h" #include "hwctrl.h" #include "keyboard.h" #include "screen.h" #include "smp.h" #include "error.h" #include "display.h" #include "interrupt.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define INT_DIVBY0 0 #define INT_RSV 1 #define INT_NMI 2 #define INT_BRKPOINT 3 #define INT_OVERFLOW 4 #define INT_BOUND 5 #define INT_UNDEFOP 6 #define INT_DEVNA 7 #define INT_DOUBLEFLT 8 #define INT_FPUSEGOVR 9 #define INT_INVDTSS 10 #define INT_SEGFLT 11 #define INT_STKSEGFLT 12 #define INT_GPF 13 #define INT_PAGEFLT 14 #define OPCODE_HLT 0xF4 #define OPCODE_JE 0x74 #define OPCODE_RDMSR 0x320F #define OPCODE_WRMSR 0x300F #ifdef __x86_64__ #define REG_PREFIX "r" #define REG_DIGITS "16" #define ADR_DIGITS "12" #else #define REG_PREFIX "e" #define REG_DIGITS "8" #define ADR_DIGITS "8" #endif static const char codes[][13] = { "Divide by 0", "Debug", "NMI", "Breakpoint", "Overflow", "Bounds", "Invalid Op", "No FPU", "Double fault", "Seg overrun", "Invalid TSS", "Seg fault", "Stack fault", "Gen prot.", "Page fault", "Reserved", "FPU error", "Alignment", "Machine chk", "SIMD FPE" }; //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ #ifdef __x86_64__ typedef uint64_t reg_t; #else typedef uint32_t reg_t; #endif struct trap_regs { reg_t ds; reg_t es; reg_t ss; reg_t ax; reg_t bx; reg_t cx; reg_t dx; reg_t di; reg_t si; #ifdef __x86_64__ reg_t r8; reg_t r9; reg_t r10; reg_t r11; #else reg_t reserved1; reg_t reserved2; reg_t sp; #endif reg_t bp; reg_t vect; reg_t code; reg_t ip; reg_t cs; reg_t flags; #ifdef __x86_64__ reg_t sp; #endif }; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void interrupt(struct trap_regs *trap_regs) { // Get the page fault address. uintptr_t address = 0; if (trap_regs->vect == INT_PAGEFLT) { #ifdef __x86_64__ __asm__( "movq %%cr2, %0" :"=r" (address) ); #else __asm__( "movl %%cr2, %0" :"=r" (address) ); #endif } if (trap_regs->vect == INT_NMI) { uint8_t *pc = (uint8_t *)trap_regs->ip; if (pc[-1] == OPCODE_HLT) { // Assume this is a barrier wakeup signal sent via IPI. return; } // Catch the rare case that a core will fail to reach the HLT instruction before // its wakeup signal arrives. The barrier code contains an atomic decrement, a JE // instruction (two bytes), and a HLT instruction (one byte). The atomic decrement // must have completed if another core has reached the point of sending the wakeup // signals, so we should find the HLT opcode either at pc[0] or at pc[2]. If we find // it, adjust the interrupt return address to point to the following instruction. if (pc[0] == OPCODE_HLT || (pc[0] == OPCODE_JE && pc[2] == OPCODE_HLT)) { uintptr_t *return_addr; if (cpuid_info.flags.lm == 1) { return_addr = (uintptr_t *)(trap_regs->sp - 40); } else { return_addr = (uintptr_t *)(trap_regs->sp - 12); } if (pc[2] == OPCODE_HLT) { *return_addr += 3; } else { *return_addr += 1; } return; } #if REPORT_PARITY_ERRORS parity_error(); return; #endif } // Catch GPF following a RDMSR instruction (usually from a non-existent msr) // and allow the program to continue. A cleaner way to do this would be to // use an exception table similar to the linux kernel, but it's probably // overkill for Memtest86+. Set a return value of 0 and leave a small mark // on top-right corner to indicate something went wrong at some point. if (trap_regs->vect == INT_GPF) { uint16_t *pc = (uint16_t *)trap_regs->ip; if (pc[0] == OPCODE_RDMSR) { uintptr_t *return_addr; if (cpuid_info.flags.lm == 1) { return_addr = (uintptr_t *)(trap_regs->sp - 40); } else { return_addr = (uintptr_t *)(trap_regs->sp - 12); } *return_addr += 2; trap_regs->ax = 0; trap_regs->dx = 0; display_msr_failed_flag(); return; } } spin_lock(error_mutex); clear_message_area(); display_pinned_message(0, 0, "Unexpected interrupt on CPU %i", smp_my_cpu_num()); if (trap_regs->vect <= 19) { display_pinned_message(2, 0, "Type: %s", codes[trap_regs->vect]); } else { display_pinned_message(2, 0, "Type: %i", trap_regs->vect); } display_pinned_message(3, 0, " IP: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ip); display_pinned_message(4, 0, " CS: %0" REG_DIGITS "x", (uintptr_t)trap_regs->cs); display_pinned_message(5, 0, "Flag: %0" REG_DIGITS "x", (uintptr_t)trap_regs->flags); display_pinned_message(6, 0, "Code: %0" REG_DIGITS "x", (uintptr_t)trap_regs->code); display_pinned_message(7, 0, " DS: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ds); display_pinned_message(8, 0, " ES: %0" REG_DIGITS "x", (uintptr_t)trap_regs->es); display_pinned_message(9, 0, " SS: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ss); if (trap_regs->vect == 14) { display_pinned_message(9, 0, " Addr: %0" REG_DIGITS "x", address); } display_pinned_message(2, 25, REG_PREFIX "ax: %0" REG_DIGITS "x", (uintptr_t)trap_regs->ax); display_pinned_message(3, 25, REG_PREFIX "bx: %0" REG_DIGITS "x", (uintptr_t)trap_regs->bx); display_pinned_message(4, 25, REG_PREFIX "cx: %0" REG_DIGITS "x", (uintptr_t)trap_regs->cx); display_pinned_message(5, 25, REG_PREFIX "dx: %0" REG_DIGITS "x", (uintptr_t)trap_regs->dx); display_pinned_message(6, 25, REG_PREFIX "di: %0" REG_DIGITS "x", (uintptr_t)trap_regs->di); display_pinned_message(7, 25, REG_PREFIX "si: %0" REG_DIGITS "x", (uintptr_t)trap_regs->si); display_pinned_message(8, 25, REG_PREFIX "bp: %0" REG_DIGITS "x", (uintptr_t)trap_regs->bp); display_pinned_message(9, 25, REG_PREFIX "sp: %0" REG_DIGITS "x", (uintptr_t)trap_regs->sp); display_pinned_message(0, 50, "Stack:"); for (int i = 0; i < 12; i++) { uintptr_t addr = trap_regs->sp + sizeof(reg_t)*(11 - i); reg_t data = *(reg_t *)addr; display_pinned_message(1 + i, 50, "%0" ADR_DIGITS "x %0" REG_DIGITS "x", addr, (uintptr_t)data); } display_pinned_message(11, 0, "CS:IP:"); uint8_t *pp = (uint8_t *)((uintptr_t)trap_regs->ip); for (int i = 0; i < 12; i++) { display_pinned_message(11, 7 + 3*i, "%02x", (uintptr_t)pp[i]); } clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1); prints(ROW_FOOTER, 0, "Press any key to reboot..."); while (get_key() == 0) { } reboot(); } memtest86plus-7.20/app/interrupt.h000066400000000000000000000004611471441223100171670ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef INTERRUPT_H #define INTERRUPT_H /** * \file * * Provides the interrupt handler. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ struct trap_regs; /** * Handles an interrupt. */ void interrupt(struct trap_regs *trap_regs); #endif // INTERRUPT_H memtest86plus-7.20/app/loongarch/000077500000000000000000000000001471441223100167355ustar00rootroot00000000000000memtest86plus-7.20/app/loongarch/interrupt.c000066400000000000000000000160021471441223100211340ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. // Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved. // //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ #include #include "hwctrl.h" #include "screen.h" #include "keyboard.h" #include "smp.h" #include "display.h" #include #define INT_SIP0 0 #define INT_SIP1 1 #define INT_IP0 2 #define INT_IP1 3 #define INT_IP2 4 #define INT_IP3 5 #define INT_IP4 6 #define INT_IP5 7 #define INT_IP6 8 #define INT_IP7 9 #define INT_PMC 10 #define INT_TIMER 11 #define INT_IPI 12 #define EXC_INT 0 #define EXC_PIL 1 #define EXC_PIS 2 #define EXC_PIF 3 #define EXC_PME 4 #define EXC_PNR 5 #define EXC_PNX 6 #define EXC_PPI 7 #define EXC_ADE 8 #define EXC_ALE 9 #define EXC_BCE 10 #define EXC_SYS 11 #define EXC_BRK 12 #define EXC_INE 13 #define EXC_IPE 14 #define EXC_FPD 15 #define EXC_SXD 16 #define EXC_ASXD 17 #define EXC_FPE 18 #define OP_IDLE 0x06488000 #define OP_BGE 0x64000000 #ifdef __loongarch_lp64 typedef uint64_t reg_t; #define CSR_REG_DIGITS "16" #define GP_REG_DIGITS "16" #define ADR_DIGITS "12" #else typedef uint32_t reg_t; #define CSR_REG_DIGITS "8" #define GP_REG_DIGITS "8" #define ADR_DIGITS "8" #endif static const char *exception_code[] = { "#INT - Interrupt(CSR.ECFG.VS=0)", "#PIL - Page invalid exception for Load option", "#PIS - Page invalid exception for Store operation", "#PIF - Page invalid exception for Fetch operation", "#PME - Page modification exception", "#PNR - Page non-readable exception", "#PNX - Page non-executable exception", "#PPI - Page privilege level illegal exception", "#ADE - Address error exception", "#ALE - Address alignment fault exception", "#BCE - Bound check exception", "#SYS - System call exception", "#BRK - Breakpoint exception", "#INE - Instruction non-defined exception", "#IPE - Instruction privilege error exception", "#FPD - Floating-point instruction disable exception", "#SXD - 128-bit vector (SIMD instructions) expansion instruction disable exception", "#ASXD - 256-bit vector (Advanced SIMD instructions) expansion instruction disable exception", "#FPE - Floating-Point error exception", "#TBR - TLB refill exception" }; struct system_context { // // GP // reg_t r0; reg_t r1; reg_t r2; reg_t r3; reg_t r4; reg_t r5; reg_t r6; reg_t r7; reg_t r8; reg_t r9; reg_t r10; reg_t r11; reg_t r12; reg_t r13; reg_t r14; reg_t r15; reg_t r16; reg_t r17; reg_t r18; reg_t r19; reg_t r20; reg_t r21; reg_t r22; reg_t r23; reg_t r24; reg_t r25; reg_t r26; reg_t r27; reg_t r28; reg_t r29; reg_t r30; reg_t r31; // // CSR // reg_t crmd; reg_t prmd; reg_t euen; reg_t misc; reg_t ecfg; reg_t estat; reg_t era; reg_t badv; reg_t badi; }; void interrupt(struct system_context *system_context) { uint8_t ecode; if (system_context->estat & (1 << INT_IPI)) { uint32_t *pc = (uint32_t *)system_context->era; // // Clean the mailbox 0 and 3 // __iocsrwr_d(0x0, 0x1020); __iocsrwr_d(0x0, 0x1038); // // Clean IPI // __iocsrwr_w(__iocsrrd_w(0x1000), 0x100c); __dbar(0); if ((pc[-1] & ~0x7FFF) == OP_IDLE) { // Assume this is a barrier wakeup signal sent via IPI. system_context->era += 4; return; } // Catch the rare case that a core will fail to reach the IDLE instruction before // its wakeup signal arrives. The barrier code contains an atomic decrement, a BLT // instruction, and a IDLE instruction. The atomic decrement must have completed if // another core has reached the point of sending the wakeup signals, so we should // find the IDLE opcode either at pc[0] or at pc[1]. If we find it, adjust the ERA // to point to the following instruction. if ((pc[0] & ~0x7FFF) == OP_IDLE) { system_context->era += 8; return; } if ((pc[0] & ~0x3FFFFFF) == OP_BGE && (pc[1] & ~0x7FFF) == OP_IDLE) { system_context->era += 12; return; } return; } ecode = (system_context->estat >> 16) & 0x3F; spin_lock(error_mutex); clear_message_area(); display_pinned_message(0, 0, "Unexpected interrupt on CPU %i", smp_my_cpu_num()); if (__csrrd_w(0x8A) & 0x1) { display_pinned_message(2, 0, "Type: %s", exception_code[19]); } else if (ecode < 19) { display_pinned_message(2, 0, "Type: %s", exception_code[ecode]); } else { display_pinned_message(2, 0, "Type: %i", ecode); } display_pinned_message(3, 0, " BADV: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->badv); display_pinned_message(4, 0, " BADI: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->badi); display_pinned_message(5, 0, " ERA: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->era); display_pinned_message(6, 0, " EUEN: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->euen); display_pinned_message(7, 0, " ECFG: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->ecfg); display_pinned_message(8, 0, "ESTAT: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->estat); display_pinned_message(3, 25, "RA: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r1); display_pinned_message(4, 25, "SP: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r3); display_pinned_message(5, 25, "A0: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r4); display_pinned_message(6, 25, "A1: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r5); display_pinned_message(7, 25, "A2: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r6); display_pinned_message(8, 25, "A3: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r7); display_pinned_message(9, 25, "A4: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r8); display_pinned_message(10, 25, "A5: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r9); display_pinned_message(11, 25, "T0: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r12); display_pinned_message(12, 25, "T1: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r13); display_pinned_message(13, 25, "T2: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r14); display_pinned_message(0, 50, "Stack:"); for (int i = 0; i < 12; i++) { uintptr_t addr = system_context->r3 + sizeof(reg_t)*(11 - i); reg_t data = *(reg_t *)addr; display_pinned_message(1 + i, 50, "%0" ADR_DIGITS "x %0" GP_REG_DIGITS "x", addr, (uintptr_t)data); } clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1); prints(ROW_FOOTER, 0, "Press any key to reboot..."); while (get_key() == 0) { } reboot(); } memtest86plus-7.20/app/main.c000066400000000000000000000534361471441223100160640ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. // // Derived from memtest86+ main.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, memtest@memtest.org // https://www.memtest.org // ------------------------------------------------ // main.c - MemTest-86 Version 3.5 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "boot.h" #include "bootparams.h" #include "acpi.h" #include "cache.h" #include "cpuid.h" #include "cpuinfo.h" #include "heap.h" #include "hwctrl.h" #include "hwquirks.h" #include "io.h" #include "keyboard.h" #include "pmem.h" #include "memctrl.h" #include "memsize.h" #include "pci.h" #include "screen.h" #include "serial.h" #include "smbios.h" #include "smp.h" #include "temperature.h" #include "timers.h" #include "vmem.h" #include "unistd.h" #include "badram.h" #include "config.h" #include "display.h" #include "error.h" #include "test.h" #include "tests.h" #include "tsc.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #ifndef TRACE_BARRIERS #define TRACE_BARRIERS 0 #endif #ifndef TEST_INTERRUPT #define TEST_INTERRUPT 0 #endif #define LOW_LOAD_LIMIT SIZE_C(4,MB) // must be a multiple of the page size #define HIGH_LOAD_LIMIT (VM_PINNED_SIZE << PAGE_SHIFT) //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ // The following variables are written by the current "master" CPU, but may // be read by all active CPUs. static volatile int init_state = 0; static uintptr_t low_load_addr; static uintptr_t high_load_addr; static barrier_t *start_barrier = NULL; static bool start_run = false; static bool start_pass = false; static bool start_test = false; static bool rerun_test = false; static bool dummy_run = false; static uintptr_t window_start = 0; static uintptr_t window_end = 0; static size_t num_mapped_pages = 0; static int test_stage = 0; //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ // These are exposed in test.h. uint8_t chunk_index[MAX_CPUS]; int num_active_cpus = 0; int num_enabled_cpus = 1; int master_cpu = 0; barrier_t *run_barrier = NULL; spinlock_t *error_mutex = NULL; vm_map_t vm_map[MAX_MEM_SEGMENTS]; int vm_map_size = 0; uint32_t proximity_domains[MAX_CPUS]; int pass_num = 0; int test_num = 0; int window_num = 0; bool restart = false; bool bail = false; uintptr_t test_addr[MAX_CPUS]; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ #define SHORT_BARRIER \ if (TRACE_BARRIERS) { \ trace(my_cpu, "Start barrier wait at %s line %i", __FILE__, __LINE__); \ } \ if (power_save < POWER_SAVE_HIGH) { \ barrier_spin_wait(start_barrier); \ } else { \ barrier_halt_wait(start_barrier); \ } #define LONG_BARRIER \ if (TRACE_BARRIERS) { \ trace(my_cpu, "Start barrier wait at %s line %i", __FILE__, __LINE__); \ } \ if (power_save > POWER_SAVE_OFF) { \ barrier_halt_wait(start_barrier); \ } else { \ barrier_spin_wait(start_barrier); \ } static void run_at(uintptr_t addr, int my_cpu) { uintptr_t *new_start_addr = (uintptr_t *)(addr + startup - _start); if (my_cpu == 0) { // Copy the program code and all data except the stacks. memmove((void *)addr, (void *)_start, _stacks - _start); // Copy the thread-local storage. size_t locals_offset = _stacks - _start + BSP_STACK_SIZE - LOCALS_SIZE; for (int cpu_num = 0; cpu_num < num_available_cpus; cpu_num++) { memcpy((void *)(addr + locals_offset), (void *)(_start + locals_offset), LOCALS_SIZE); locals_offset += AP_STACK_SIZE; } } LONG_BARRIER; #ifdef __i386__ // The 32-bit startup code needs to know where it is located. __asm__ __volatile__("movl %0, %%edi" : : "r" (new_start_addr)); #endif goto *new_start_addr; } static bool set_load_addr(uintptr_t *load_addr, size_t program_size, uintptr_t lower_limit, uintptr_t upper_limit) { uintptr_t current_start = (uintptr_t)_start; if (current_start >= lower_limit && (current_start + program_size) <= upper_limit) { *load_addr = current_start; return true; } for (int i = 0; i < pm_map_size; i++) { uintptr_t try_start = pm_map[i].start << PAGE_SHIFT; uintptr_t try_limit = pm_map[i].end << PAGE_SHIFT; if (try_start < lower_limit) try_start = lower_limit; uintptr_t try_end = try_start + program_size; if (try_end > try_limit) continue; if (try_start >= upper_limit) break; if (try_end < lower_limit) continue; *load_addr = try_start; return true; } enable_trace = true; trace(0, "Insufficient free space in range 0x%x to 0x%x", lower_limit, upper_limit - 1); return false; } static void global_init(void) { floppy_off(); cpuid_init(); // Nothing before this should access the boot parameters, in case they are located above 4GB. // This is the first region we map, so it is guaranteed not to fail. boot_params_addr = map_region(boot_params_addr, sizeof(boot_params_t), true); hwctrl_init(); screen_init(); cpuinfo_init(); pmem_init(); heap_init(); pci_init(); quirks_init(); acpi_init(); timers_init(); membw_init(); smbios_init(); badram_init(); config_init(); memctrl_init(); tty_init(); smp_init(smp_enabled); // Force disable the NUMA code paths when no proximity domain was found. if (num_proximity_domains == 0) { enable_numa = false; } // At this point we have started reserving physical pages in the memory // map for data structures that need to be permanently pinned in place. // This may overwrite any data structures passed to us by the BIOS and/or // boot loader, e.g. the boot parameters, boot command line, and ACPI // tables. So do not access those data structures after this point. keyboard_init(); display_init(); error_init(); temperature_init(); initial_config(); clear_message_area(); if (!smp_enabled) { num_available_cpus = 1; } num_enabled_cpus = 0; for (int i = 0; i < num_available_cpus; i++) { if (cpu_state[i] == CPU_STATE_ENABLED) { if (enable_numa) { uint32_t proximity_domain_idx = smp_get_proximity_domain_idx(i); chunk_index[i] = smp_alloc_cpu_in_proximity_domain(proximity_domain_idx); } else { chunk_index[i] = num_enabled_cpus; } num_enabled_cpus++; } } display_cpu_topology(); master_cpu = 0; display_temperature(); if (enable_trace) { display_pinned_message(0, 0,"CPU Trace"); display_pinned_message(1, 0,"--- ----------------------------------------------------------------------------"); set_scroll_lock(true); } else if (enable_sm) { post_display_init(); } size_t program_size = (_stacks - _start) + BSP_STACK_SIZE + (num_enabled_cpus - 1) * AP_STACK_SIZE; bool load_addr_ok = set_load_addr(& low_load_addr, program_size, 0x1000, LOW_LOAD_LIMIT) && set_load_addr(&high_load_addr, program_size, LOW_LOAD_LIMIT, HIGH_LOAD_LIMIT); trace(0, "program size %ikB", (int)(program_size / 1024)); trace(0, " low_load_addr %0*x", 2*sizeof(uintptr_t), low_load_addr); trace(0, "high_load_addr %0*x", 2*sizeof(uintptr_t), high_load_addr); for (int i = 0; i < pm_map_size; i++) { trace(0, "pm %0*x - %0*x", 2*sizeof(uintptr_t), pm_map[i].start, 2*sizeof(uintptr_t), pm_map[i].end); } if (acpi_config.rsdp_addr != 0) { trace(0, "ACPI RSDP (v%u.%u) found in %s at %0*x", acpi_config.ver_maj, acpi_config.ver_min, rsdp_source, 2*sizeof(uintptr_t), acpi_config.rsdp_addr); trace(0, "ACPI FADT found at %0*x", 2*sizeof(uintptr_t), acpi_config.fadt_addr); trace(0, "ACPI SRAT found at %0*x", 2*sizeof(uintptr_t), acpi_config.srat_addr); //trace(0, "ACPI SLIT found at %0*x", 2*sizeof(uintptr_t), acpi_config.slit_addr); } if (!load_addr_ok) { trace(0, "Cannot relocate program. Press any key to reboot..."); while (get_key() == 0) { } reboot(); } start_barrier = smp_alloc_barrier(1); run_barrier = smp_alloc_barrier(1); error_mutex = smp_alloc_mutex(); start_run = true; dummy_run = true; restart = false; } static void ap_enumerate(int my_cpu) { if (!cpuid_info.topology.is_hybrid) { return; } hybrid_core_type[my_cpu] = get_ap_hybrid_type(); if (hybrid_core_type[my_cpu] == CORE_PCORE) { cpuid_info.topology.pcore_count++; } else if (hybrid_core_type[my_cpu] == CORE_ECORE) { cpuid_info.topology.ecore_count++; } if (hybrid_core_type[my_cpu] == CORE_ECORE && exclude_ecores) { cpu_state[my_cpu] = CPU_STATE_DISABLED; //TODO : hlt AP? } if (my_cpu == num_enabled_cpus - 1) { display_cpu_topology(); } } static void setup_vm_map(uintptr_t win_start, uintptr_t win_end) { vm_map_size = 0; num_mapped_pages = 0; // Reduce the window to fit in the user-specified limits. if (win_start < pm_limit_lower) { win_start = pm_limit_lower; } if (win_end > pm_limit_upper) { win_end = pm_limit_upper; } if (win_start >= win_end) { return; } // Now initialise the virtual memory map with the intersection // of the window and the physical memory segments. for (int i = 0; i < pm_map_size; i++) { // These are page numbers. uintptr_t seg_start = pm_map[i].start; uintptr_t seg_end = pm_map[i].end; if (seg_start <= win_start) { seg_start = win_start; } if (seg_end >= win_end) { seg_end = win_end; } if (seg_start < seg_end && seg_start < win_end && seg_end > win_start) { // We need to test part of that physical memory segment. if (enable_numa) { // Now also pay attention to proximity domains, which are based on physical addresses. uint64_t orig_start = (uint64_t)seg_start << PAGE_SHIFT; uint64_t orig_end = (uint64_t)seg_end << PAGE_SHIFT; uint32_t proximity_domain_idx; uint64_t new_start; uint64_t new_end; while (1) { if (smp_narrow_to_proximity_domain(orig_start, orig_end, &proximity_domain_idx, &new_start, &new_end)) { // Create a new entry in the virtual memory map. num_mapped_pages += (new_end - new_start) >> PAGE_SHIFT; vm_map[vm_map_size].pm_base_addr = new_start >> PAGE_SHIFT; vm_map[vm_map_size].start = first_word_mapping(new_start >> PAGE_SHIFT); vm_map[vm_map_size].end = last_word_mapping((new_end >> PAGE_SHIFT) - 1, sizeof(testword_t)); vm_map[vm_map_size].proximity_domain_idx = proximity_domain_idx; vm_map_size++; if (new_start != orig_start || new_end != orig_end) { // Proceed to the next part of the range. orig_start = new_end; // No shift here, we already have a physical address. orig_end = (uint64_t)seg_end << PAGE_SHIFT; } else { // We're done with this range. break; } } else { // Could not match with proximity domain, fall back to default behaviour. This shouldn't happen ! vm_map[vm_map_size].proximity_domain_idx = 0; goto non_numa_vm_map_entry; } } } else { non_numa_vm_map_entry: num_mapped_pages += seg_end - seg_start; vm_map[vm_map_size].pm_base_addr = seg_start; vm_map[vm_map_size].start = first_word_mapping(seg_start); vm_map[vm_map_size].end = last_word_mapping(seg_end - 1, sizeof(testword_t)); vm_map_size++; } } } #if 0 for (int i = 0; i < vm_map_size; i++) { do_trace(0, "vm %0*x - %0*x", 2*sizeof(uintptr_t), vm_map[i].start, 2*sizeof(uintptr_t), vm_map[i].end); } #endif } static void test_all_windows(int my_cpu) { bool parallel_test = false; bool i_am_master = (my_cpu == master_cpu); bool i_am_active = i_am_master; if (!dummy_run) { if (cpu_mode == PAR && test_list[test_num].cpu_mode == PAR) { parallel_test = true; i_am_active = true; } } if (i_am_master) { num_active_cpus = 1; if (!dummy_run) { if (parallel_test) { num_active_cpus = num_enabled_cpus; if(display_mode == DISPLAY_MODE_NA) { display_all_active(); } } else { if (display_mode == 0) { display_active_cpu(my_cpu); } } } barrier_reset(run_barrier, num_active_cpus); } int iterations = test_list[test_num].iterations; if (pass_num == 0) { // Reduce iterations for a faster first pass. iterations /= 3; } // Loop through all possible windows. do { LONG_BARRIER; if (bail) { break; } if (i_am_master) { if (window_num == 0 && test_list[test_num].stages > 1) { // A multi-stage test runs through all the windows at each stage. // Relocation may disrupt the test. window_num = 1; } if (window_num == 0 && pm_limit_lower >= LOW_LOAD_LIMIT) { // Avoid unnecessary relocation. window_num = 1; } } SHORT_BARRIER; // Relocate if necessary. if (window_num > 0) { if (!dummy_run && (uintptr_t)&_start != low_load_addr) { run_at(low_load_addr, my_cpu); } } else { if (!dummy_run && (uintptr_t)&_start != high_load_addr) { run_at(high_load_addr, my_cpu); } } if (i_am_master) { //trace(my_cpu, "start window %i", window_num); switch (window_num) { case 0: window_start = 0; window_end = (LOW_LOAD_LIMIT >> PAGE_SHIFT); break; case 1: window_start = (LOW_LOAD_LIMIT >> PAGE_SHIFT); window_end = VM_WINDOW_SIZE; break; default: window_start = window_end; window_end += VM_WINDOW_SIZE; } setup_vm_map(window_start, window_end); } SHORT_BARRIER; if (!i_am_active) { continue; } if (num_mapped_pages == 0) { // No memory to test in this window. if (i_am_master) { window_num++; } continue; } if (dummy_run) { if (i_am_master) { ticks_per_test[pass_num][test_num] += run_test(-1, test_num, test_stage, iterations); } } else { if (!map_window(vm_map[0].pm_base_addr)) { // Either there is no PAE or we are at the PAE limit. break; } run_test(my_cpu, test_num, test_stage, iterations); } if (i_am_master) { window_num++; } } while (window_end < pm_map[pm_map_size - 1].end); } static void select_next_master(void) { do { master_cpu = (master_cpu + 1) % num_available_cpus; } while (cpu_state[master_cpu] == CPU_STATE_DISABLED); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ // The main entry point called from the startup code. void main(void) { int my_cpu; if (init_state == 0) { // If this is the first time here, we must be CPU 0, as the APs haven't been started yet. my_cpu = 0; } else { my_cpu = smp_my_cpu_num(); } if (init_state < 2) { cache_on(); if (my_cpu == 0) { global_init(); init_state = 1; if (enable_trace && num_enabled_cpus > 1) { set_scroll_lock(false); trace(0, "starting other CPUs"); } barrier_reset(start_barrier, num_enabled_cpus); int failed = smp_start(cpu_state); if (failed) { const char *message = "Failed to start CPU core %i. Press any key to reboot..."; display_notice_with_args(strlen(message), message, failed); while (get_key() == 0) { } reboot(); } if (enable_trace && num_enabled_cpus > 1) { trace(0, "all other CPUs started"); set_scroll_lock(true); } init_state = 2; } else { trace(my_cpu, "AP started"); cpu_state[my_cpu] = CPU_STATE_RUNNING; ap_enumerate(my_cpu); while (init_state < 2) { usleep(100); } } } #if TEST_INTERRUPT if (my_cpu == 0) { __asm__ __volatile__ ("int $1"); } #endif // Due to the need to relocate ourselves in the middle of tests, the following // code cannot be written in the natural way as a set of nested loops. So we // have a single loop and use global state variables to allow us to restart // where we left off after each relocation. while (1) { SHORT_BARRIER; if (my_cpu == 0) { if (start_run) { pass_num = 0; start_pass = true; if (!dummy_run) { display_start_run(); badram_init(); error_init(); } } if (start_pass) { test_num = 0; start_test = true; if (dummy_run) { ticks_per_pass[pass_num] = 0; } else { display_start_pass(); } } if (start_test) { trace(my_cpu, "start test %i", test_num); test_stage = 0; rerun_test = true; if (dummy_run) { ticks_per_test[pass_num][test_num] = 0; } else if (test_list[test_num].enabled) { display_start_test(); } bail = false; } if (rerun_test) { window_num = 0; window_start = 0; window_end = 0; } start_run = false; start_pass = false; start_test = false; rerun_test = false; } SHORT_BARRIER; if (test_list[test_num].enabled) { test_all_windows(my_cpu); } SHORT_BARRIER; if (my_cpu != 0) { continue; } check_input(); if (restart) { // The configuration has been changed. master_cpu = 0; start_run = true; dummy_run = true; restart = false; continue; } error_update(); if (test_list[test_num].enabled) { if (++test_stage < test_list[test_num].stages) { rerun_test = true; continue; } test_stage = 0; switch (cpu_mode) { case PAR: if (test_list[test_num].cpu_mode == SEQ) { select_next_master(); if (master_cpu != 0) { rerun_test = true; continue; } } break; case ONE: select_next_master(); break; case SEQ: select_next_master(); if (master_cpu != 0) { rerun_test = true; continue; } break; default: break; } } if (dummy_run) { ticks_per_pass[pass_num] += ticks_per_test[pass_num][test_num]; } start_test = true; test_num++; if (test_num < NUM_TEST_PATTERNS) { continue; } pass_num++; if (dummy_run && pass_num == NUM_PASS_TYPES) { start_run = true; dummy_run = false; continue; } start_pass = true; if (!dummy_run) { display_pass_count(pass_num); if (error_count == 0) { display_status("Pass "); display_big_status(true); } else { display_big_status(false); } } } } memtest86plus-7.20/app/test.h000066400000000000000000000054051471441223100161150ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TEST_H #define TEST_H /** * \file * * Provides types and variables used when performing the memory tests. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ #include #include #include "pmem.h" #include "smp.h" #include "barrier.h" #include "spinlock.h" /** * A mapping from a CPU core number to the index number of the memory chunk * it operates on when performing a memory test in parallel across all the * enabled cores (in the current proximity domain, when NUMA awareness is * enabled). */ extern uint8_t chunk_index[MAX_CPUS]; /** * An array where the count of used CPUs in the current proximity domain. */ extern uint8_t used_cpus_in_proximity_domain[MAX_PROXIMITY_DOMAINS]; /* * The number of CPU cores being used for the current test. This is always * either 1 or the full number of enabled CPU cores. */ extern int num_active_cpus; /** * The current master CPU core. */ extern int master_cpu; /** * A barrier used when running tests. */ extern barrier_t *run_barrier; /** * A mutex used when reporting errors or printing trace information. */ extern spinlock_t *error_mutex; #if (ARCH_BITS == 64) /** * The word width (in bits) used for memory testing. */ #define TESTWORD_WIDTH 64 /** * The number of hex digits needed to display a memory test word. */ #define TESTWORD_DIGITS 16 /** * The string representation of TESTWORDS_DIGITS */ #define TESTWORD_DIGITS_STR "16" #else /** * The word width (in bits) used for memory testing. */ #define TESTWORD_WIDTH 32 /** * The number of hex digits needed to display a memory test word. */ #define TESTWORD_DIGITS 8 /** * The string representation of TESTWORDS_DIGITS */ #define TESTWORD_DIGITS_STR "8" #endif /** * The word type used for memory testing. */ typedef uintptr_t testword_t; /** * A virtual memory segment descriptor. */ typedef struct { uintptr_t pm_base_addr; testword_t *start; testword_t *end; uint32_t proximity_domain_idx; } vm_map_t; /** * The list of memory segments currently mapped into virtual memory. */ extern vm_map_t vm_map[MAX_MEM_SEGMENTS]; /** * The number of memory segments currently mapped into virtual memory. */ extern int vm_map_size; /** * The number of completed test passes. */ extern int pass_num; /** * The current test number. */ extern int test_num; /** * The current window number. */ extern int window_num; /** * A flag indicating that testing should be restarted due to a configuration * change. */ extern bool restart; /** * A flag indicating that the current test should be aborted. */ extern bool bail; /** * The base address of the block of memory currently being tested. */ extern uintptr_t test_addr[MAX_CPUS]; #endif // TEST_H memtest86plus-7.20/app/version.h000066400000000000000000000000651471441223100166200ustar00rootroot00000000000000#define MT_VERSION "7.20" #define GIT_HASH "unknown" memtest86plus-7.20/boot/000077500000000000000000000000001471441223100151445ustar00rootroot00000000000000memtest86plus-7.20/boot/boot.h000066400000000000000000000053561471441223100162710ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BOOT_H #define BOOT_H /** * \file * * Provides definitions used in the boot code. Also defines exported symbols * needed in the main code. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ /* * NOTE: Increasing the value of MAX_APS would require: * - relocating the stacks when the program is loaded in low memory * - modifying smp.c to support the x2APIC architecture * - adjusting the display if more than 3 digits are needed for CPU IDs */ #define MAX_APS 255 /* Maximum number of active APs */ #define BSP_STACK_SIZE 16384 /* Stack size for the BSP */ #ifdef __loongarch_lp64 #define AP_STACK_SIZE 2048 /* Stack size for each AP */ #else #define AP_STACK_SIZE 1024 /* Stack size for each AP */ #endif #define STACKS_SIZE (BSP_STACK_SIZE + MAX_APS * AP_STACK_SIZE) #define LOCALS_SIZE 16 /* Stack region reserved for thread-local storage */ #define LOW_LOAD_ADDR 0x00010000 /* The low load address for the main program */ #define HIGH_LOAD_ADDR 0x00100000 /* The high load address for the main program */ #define SETUP_SECS 2 /* Size of the 16-bit setup code in sectors */ #define BOOT_SEG 0x07c0 /* Segment address for the 16-bit boot code */ #define SETUP_SEG 0x07e0 /* Segment address for the 16-bit setup code */ #define MAIN_SEG 0x1000 /* Segment address for the main program code when loaded by the 16-bit bootloader */ #define KERNEL_CS 0x10 /* 32-bit segment address for code */ #define KERNEL_DS 0x18 /* 32-bit segment address for data */ /* The following addresses are offsets from BOOT_SEG. */ #define BOOT_STACK ((1 + SETUP_SECS) * 512) #define BOOT_STACK_TOP ((MAIN_SEG - BOOT_SEG) << 4) /* The following definitions must match the Linux boot_params struct. */ #define E820_ENTRIES 0x1e8 /* offsetof(boot_params.e820_entries) */ #define E820_MAP 0x2d0 /* offsetof(boot_params.e820_table) */ #define E820_MAP_SIZE 128 /* max. number of entries in E820_MAP */ /* The following definitions must match the Linux e820_entry struct. */ #define E820_ADDR 0 /* offsetof(e820_entry.addr) */ #define E820_SIZE 8 /* offsetof(e820_entry.size) */ #define E820_TYPE 16 /* offsetof(e820_entry.type) */ #define E820_ENTRY_SIZE 20 /* sizeof(e820_entry) */ #ifndef __ASSEMBLY__ #include extern uint8_t _start[]; extern uint8_t startup32[]; extern uint8_t startup64[]; extern uint8_t startup[]; extern uint64_t pml4[]; extern uint64_t pdp[]; extern uint64_t pd0[]; extern uint64_t pd1[]; extern uint64_t pd2[]; extern uint64_t pd3[]; extern uintptr_t boot_params_addr; extern uint8_t ap_trampoline[]; extern uint32_t ap_startup_addr; extern uint8_t ap_trampoline_end[]; extern uint8_t _stacks[]; extern uint8_t _end[]; #endif /* ! __ASSEMBLY__ */ #endif /* BOOT_H */ memtest86plus-7.20/boot/bootparams.h000066400000000000000000000062041471441223100174660ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BOOTPARAMS_H #define BOOTPARAMS_H /** * \file * * Provides definitions for the boot params structure passed to us by * intermediate bootloaders when using the Linux boot protocol. This matches * the Linux boot_params struct, although we only define the fields we are * interested in. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ #include #include typedef struct { uint8_t orig_x; uint8_t orig_y; uint16_t ext_mem_k; uint16_t orig_video_page; uint8_t orig_video_mode; uint8_t orig_video_cols; uint8_t flags; uint8_t unused2; uint16_t orig_video_ega_bx; uint16_t unused3; uint8_t orig_video_lines; uint8_t orig_video_isVGA; uint16_t orig_video_points; uint16_t lfb_width; uint16_t lfb_height; uint16_t lfb_depth; uint32_t lfb_base; uint32_t lfb_size; uint16_t cl_magic, cl_offset; uint16_t lfb_linelength; uint8_t red_size; uint8_t red_pos; uint8_t green_size; uint8_t green_pos; uint8_t blue_size; uint8_t blue_pos; uint8_t rsvd_size; uint8_t rsvd_pos; uint16_t vesapm_seg; uint16_t vesapm_off; uint16_t pages; uint16_t vesa_attributes; uint32_t capabilities; uint32_t ext_lfb_base; uint8_t _reserved[2]; } __attribute__((packed)) screen_info_t; #define VIDEO_TYPE_VLFB 0x23 // VESA VGA in graphic mode #define VIDEO_TYPE_EFI 0x70 // EFI graphic mode #define VIDEO_TYPE_NONE 0xff // no video display (added for Memtest86+) #define LFB_CAPABILITY_64BIT_BASE (1 << 1) typedef struct { uint32_t loader_signature; uint32_t sys_tab; uint32_t mem_desc_size; uint32_t mem_desc_version; uint32_t mem_map; uint32_t mem_map_size; uint32_t sys_tab_hi; uint32_t mem_map_hi; } __attribute__((packed)) efi_info_t; #define EFI32_LOADER_SIGNATURE ('E' | ('L' << 8) | ('3' << 16) | ('2' << 24)) #define EFI64_LOADER_SIGNATURE ('E' | ('L' << 8) | ('6' << 16) | ('4' << 24)) typedef enum { E820_NONE = 0, E820_RAM = 1, E820_RESERVED = 2, E820_ACPI = 3, // usable as RAM once ACPI tables have been read E820_NVS = 4 } e820_type_t; typedef struct { uint64_t addr; uint64_t size; uint32_t type; } __attribute__((packed)) e820_entry_t; typedef struct { screen_info_t screen_info; uint8_t unused1[0x070 - 0x040]; uint64_t acpi_rsdp_addr; uint8_t unused2[0x1c0 - 0x078]; efi_info_t efi_info; uint8_t unused3[0x1e8 - 0x1e0]; uint8_t e820_entries; uint8_t unused4[0x214 - 0x1e9]; uint32_t code32_start; uint8_t unused5[0x228 - 0x218]; uint32_t cmd_line_ptr; uint8_t unused6[0x238 - 0x22c]; uint32_t cmd_line_size; uint8_t unused7[0x2d0 - 0x23c]; e820_entry_t e820_map[E820_MAP_SIZE]; uint8_t unused8[0xeec - 0xd00]; } __attribute__((packed)) boot_params_t; #endif /* BOOTPARAMS_H */ memtest86plus-7.20/boot/bootsect.S000066400000000000000000000167001471441223100171160ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // bootsect.S supports booting directly from the BIOS or via an intermediate // bootloader that supports the Linux boot protocol. If booted directly from // the BIOS, it is loaded at address 0x7c00. It then loads setup.S immediately // after itself (address 0x7e00) and the main program code at segment MAIN_SEG, // using BIOS interrupts to read the data from disk. When using an intermediate // bootloader, it provides the first few bytes of the Linux boot header (at the // end of the boot sector), with the remainder of the header being provided by // setup.S. // // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ bootsect.S: // // bootsect.s Copyright (C) 1991, 1992 Linus Torvalds // // 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86. #define __ASSEMBLY__ #include "boot.h" .section ".bootsect", "ax", @progbits .code16 # The BIOS boot entry point. This will be located at 0x7c00. .globl boot boot: # Initialise the segment registers and the stack. ljmp $BOOT_SEG, $init init: movw %cs, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw $BOOT_STACK_TOP, %ax movw %ax, %sp # Many BIOS's default disk parameter tables will not recognize # multi-sector reads beyond the maximum sector number specified # in the default diskette parameter tables - this may mean 7 # sectors in some cases. # # Since single sector reads are slow and out of the question, # we must take care of this by creating new parameter tables # (for the first disk) in RAM. We will set the maximum sector # count to 18 - the most we will encounter on an HD 1.44. # # High doesn't hurt. Low does. # # Segments are as follows: # ds=es=ss=cs = BOOT_SEG, # fs = 0, gs = parameter table segment pushw $0 popw %fs movw $0x78, %bx # fs:bx is parameter table address lgs %fs:(%bx),%si # gs:si is source movw %dx, %di # es:di is destination movw $6, %cx # copy 12 bytes cld rep movsw %gs:(%si), (%di) movw %dx, %di movb $18, 4(%di) # patch sector count movw %di, %fs:(%bx) movw %es, %fs:2(%bx) movw %cs, %ax movw %ax, %fs movw %ax, %gs xorb %ah, %ah # reset FDC xorb %dl, %dl int $0x13 # Load the setup sectors directly after the boot block. # Note that 'es' is already set up. load_setup: xorw %dx, %dx # drive 0, head 0 movw $0x0002, %cx # sector 2, track 0 movw $0x0200, %bx # address = 512, in BOOT_SEG movw $(0x0200 + SETUP_SECS), %ax # service 2, nr of sectors # (assume all on head 0, track 0) int $0x13 # read it jnc load_setup_done # ok - continue pushw %ax # dump error code call print_nl movw %sp, %bp call print_hex popw %ax xorb %dl, %dl # reset FDC xorb %ah, %ah int $0x13 jmp load_setup load_setup_done: # Get disk drive parameters, specifically number of sectors/track. # It seems that there is no BIOS call to get the number of sectors. # Guess 18 sectors if sector 18 can be read, 15 if sector 15 can be # read. Otherwise guess 9. xorw %dx, %dx # drive 0, head 0 movw $0x0012, %cx # sector 18, track 0 movw $BOOT_STACK, %bx # use the bottom of the stack (es = cs) movw $0x0201, %ax # service 2, 1 sector int $0x13 jnc got_sectors movb $0x0f, %cl # sector 15 movw $0x0201, %ax # service 2, 1 sector int $0x13 jnc got_sectors movb $0x09, %cl got_sectors: movw %cx, %cs:sectors movw $BOOT_SEG, %ax movw %ax, %es # Print a message. movb $0x03, %ah # read cursor pos xorb %bh, %bh int $0x10 leaw boot_msg, %bp movw $(boot_msg_end - boot_msg), %cx movw $0x0007, %bx # page 0, attribute 7 (normal) movw $0x1301, %ax # write string, move cursor int $0x10 # Load the main test program. movw $MAIN_SEG, %ax movw %ax, %es call read_it call kill_motor call turn_off_cursor call print_nl # Fix up the Linux boot header to indicate we've loaded into low memory. movl $LOW_LOAD_ADDR, code32_start # After that (everything loaded), we jump to the setup code loaded # directly after the boot block. ljmp $SETUP_SEG, $0 # This subroutine loads the system at address 0x10000, making sure no 64KB # boundaries are crossed. We try to load it as fast as possible, loading # whole tracks whenever we can. # # in: es - starting address segment (normally 0x1000) # sread: .word 1 + SETUP_SECS # sectors read of current track head: .word 0 # current head track: .word 0 # current track read_it: movw %es, %ax testw $0x0fff, %ax die: jne die # es must be at 64kB boundary xorw %bx,%bx # bx is starting address within segment rp_read: movw %es, %ax subw $MAIN_SEG, %ax # have we loaded all yet? cmpw sys_size, %ax jbe ok1_read ret ok1_read: movw %cs:sectors, %ax subw sread, %ax movw %ax, %cx shlw $9, %cx addw %bx, %cx jnc ok2_read je ok2_read xorw %ax, %ax subw %bx, %ax shrw $9, %ax ok2_read: call read_track movw %ax, %cx add sread, %ax cmpw %cs:sectors, %ax jne ok3_read movw $1, %ax subw head, %ax jne ok4_read incw track ok4_read: movw %ax, head xorw %ax, %ax ok3_read: movw %ax, sread shlw $9, %cx addw %cx, %bx jnc rp_read movw %es, %ax addb $0x10, %ah movw %ax, %es xorw %bx, %bx jmp rp_read read_track: pusha pusha movw $0xe2e, %ax # loading... message 2e = . movw $7, %bx int $0x10 popa movw track, %dx movw sread, %cx incw %cx movb %dl, %ch movw head, %dx movb %dl, %dh andw $0x0100, %dx movb $2, %ah pushw %dx # save for error dump pushw %cx pushw %bx pushw %ax int $0x13 jc bad_rt addw $8, %sp popa ret bad_rt: pushw %ax # save error code call print_all # ah = error, al = read xorb %ah, %ah xorb %dl, %dl int $0x13 addw $10, %sp popa jmp read_track # This subroutine is for debugging purposes. It will print out all of the # registers. The assumption is that this is called from a routine, with a # stack frame like: # dx # cx # bx # ax # err # ret <- sp print_all: movw $5, %cx # error code + 4 registers movw %sp, %bp print_loop: pushw %cx # save count left call print_nl # nl for readability cmpb 5, %cl # see if register name is needed jae no_reg movw $(0xe05 + 'A' - 1), %ax subb %cl, %al int $0x10 movb $'X', %al int $0x10 movb $':', %al int $0x10 no_reg: addw $2, %bp # next register call print_hex # print it popw %cx loop print_loop ret print_nl: movw $0xe0d, %ax # CR int $0x10 movb $0x0a, %al # LF int $0x10 ret # This subroutine is for debugging purposes, and prints the word pointed to # by ss:bp in hexadecimal. print_hex: movw $4, %cx # 4 hex digits movw (%bp), %dx # load word into dx print_digit: rolw $4, %dx # rotate so that lowest 4 bits are used movb $0xe, %ah movb %dl, %al # mask off so we have only next nibble andb $0xf, %al addb $'0', %al # convert to 0-based digit cmpb $'9', %al # check for overflow jbe good_digit addb $('A' - '0' - 10), %al good_digit: int $0x10 loop print_digit ret # This subroutine turns off the floppy drive motor, so that we enter the # kernel in a known state, and don't have to worry about it later. kill_motor: pushw %dx movw $0x3f2, %dx xorb %al, %al outb %al, %dx popw %dx ret # This subroutine turns off the text display cursor. turn_off_cursor: movb $0x01, %ah movb $0x00, %bh movw $0x2000, %cx int $0x10 ret # Local variables. sectors: .word 0 boot_msg: .ascii "Loading Memtest86+" boot_msg_end: # Emulate the Linux boot header, to allow loading by intermediate boot loaders. .org 497 setup_sects: .byte SETUP_SECS root_flags: .word 0 sys_size: .long _sys_size ram_size: .word 0 vid_mode: .word 0 root_dev: .word 0 boot_flag: .word 0xAA55 memtest86plus-7.20/boot/efi.h000066400000000000000000000232611471441223100160640ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef EFI_H #define EFI_H /** * \file * * Provides definitions for accessing the UEFI boot services and configuration * tables. * *//* * Copyright (C) 2020-2022 Martin Whitaker. */ #include #if (ARCH_BITS == 64) #define NATIVE_MSB UINT64_C(0x8000000000000000) #else #define NATIVE_MSB 0x80000000 #endif /** * EFI_STATUS values. */ #define EFI_SUCCESS 0 #define EFI_INVALID_PARAMETER (NATIVE_MSB | 2) #define EFI_UNSUPPORTED (NATIVE_MSB | 3) #define EFI_BUFFER_TOO_SMALL (NATIVE_MSB | 5) #define EFI_NOT_READY (NATIVE_MSB | 6) #define EFI_NOT_FOUND (NATIVE_MSB | 14) #define EFI_ABORTED (NATIVE_MSB | 21) /** * EFI_LOCATE_SEARCH_TYPE values. */ #define EFI_LOCATE_BY_PROTOCOL 2 /** * EFI_ALLOCATE_TYPE values. */ #define EFI_ALLOCATE_MAX_ADDRESS 1 #define EFI_ALLOCATE_ADDRESS 2 /** * EFI_MEMORY_TYPE values. */ #define EFI_LOADER_CODE 1 #define EFI_LOADER_DATA 2 #define EFI_BOOT_SERVICES_CODE 3 #define EFI_BOOT_SERVICES_DATA 4 #define EFI_CONVENTIONAL_MEMORY 7 #define EFI_ACPI_RECLAIM_MEMORY 9 /** * EFI_RESET_TYPE values. */ #define EFI_RESET_COLD 0 #define EFI_RESET_WARM 1 #define EFI_RESET_SHUTDOWN 2 /** * EFI_GRAPHICS_PIXEL_FORMAT values. */ #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 #define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 #define PIXEL_BIT_MASK 2 #define PIXEL_BLT_ONLY 3 #define EFI_SYSTEM_TABLE_SIGNATURE UINT64_C(0x5453595320494249) #define EFI_RUNTIME_SERVICES_SIGNATURE UINT64_C(0x5652453544e5552) #if defined(__x86_64__) || defined(__i386__) #define efiapi __attribute__((ms_abi)) #else #define efiapi #endif #if (ARCH_BITS == 64) typedef uint64_t uintn_t; #else typedef uint32_t uintn_t; #endif typedef void * efi_handle_t; typedef uintn_t efi_status_t; typedef uint64_t efi_phys_addr_t; typedef uint64_t efi_virt_addr_t; typedef uint16_t efi_char16_t; typedef struct { uint32_t a; uint16_t b; uint16_t c; uint8_t d[8]; } efi_guid_t; typedef struct { uint32_t type; uint32_t pad; efi_phys_addr_t phys_addr; efi_virt_addr_t virt_addr; uint64_t num_pages; uint64_t attribute; } efi_memory_desc_t; typedef struct { uint32_t red_mask; uint32_t green_mask; uint32_t blue_mask; uint32_t rsvd_mask; } efi_pixel_bitmask_t; typedef struct { uint32_t version; uint32_t h_resolution; uint32_t v_resolution; int pixel_format; efi_pixel_bitmask_t pixel_info; uint32_t pixels_per_scan_line; } efi_gop_mode_info_t; typedef struct { uint32_t max_mode; uint32_t mode; efi_gop_mode_info_t *info; uintn_t info_size; efi_phys_addr_t frame_buffer_base; uintn_t frame_buffer_size; } efi_gop_mode_t; typedef struct efi_graphics_output_s { efi_status_t (efiapi *query_mode)(struct efi_graphics_output_s *, uint32_t, uintn_t *, efi_gop_mode_info_t **); efi_status_t (efiapi *set_mode)(struct efi_graphics_output_s *, uint32_t); void *blt; efi_gop_mode_t *mode; } efi_graphics_output_t; typedef struct { uint64_t signature; uint32_t revision; uint32_t header_size; uint32_t crc32; uint32_t reserved; } efi_table_header_t; typedef struct { uint16_t scan_code; efi_char16_t ch; } efi_input_key_t; typedef struct efi_simple_text_in_s { void *reset; efi_status_t (efiapi *read_key_stroke)(struct efi_simple_text_in_s *, efi_input_key_t *); void *test_string; } efi_simple_text_in_t; typedef struct efi_simple_text_out_s { void *reset; efi_status_t (efiapi *output_string)(struct efi_simple_text_out_s *, efi_char16_t *); void *test_string; } efi_simple_text_out_t; typedef struct { efi_table_header_t header; void *raise_tpl; void *restore_tpl; efi_status_t (efiapi *allocate_pages)(int, int, uintn_t, efi_phys_addr_t *); efi_status_t (efiapi *free_pages)(efi_phys_addr_t, uintn_t); efi_status_t (efiapi *get_memory_map)(uintn_t *, void *, uintn_t *, uintn_t *, uint32_t *); efi_status_t (efiapi *allocate_pool)(int, uintn_t, void **); efi_status_t (efiapi *free_pool)(void *); void *create_event; void *set_timer; void *wait_for_event; void *signal_event; void *close_event; void *check_event; void *install_protocol_interface; void *reinstall_protocol_interface; void *uninstall_protocol_interface; efi_status_t (efiapi *handle_protocol)(efi_handle_t, efi_guid_t *, void **); void *reserved; void *register_protocol_notify; efi_status_t (efiapi *locate_handle)(int, efi_guid_t *, void *, uintn_t *, efi_handle_t *); void *locate_device_path; efi_status_t (efiapi *install_configuration_table)(efi_guid_t *, void *); void *load_image; void *start_image; void *exit; void *unload_image; efi_status_t (efiapi *exit_boot_services)(efi_handle_t, uintn_t); void *get_next_monotonic_count; void *stall; void *set_watchdog_timer; void *connect_controller; efi_status_t (efiapi *disconnect_controller)(efi_handle_t, efi_handle_t, efi_handle_t); void *open_protocol; void *close_protocol; void *open_protocol_information; void *protocols_per_handle; void *locate_handle_buffer; efi_status_t (efiapi *locate_protocol)(efi_guid_t *, void *, void **); void *install_multiple_protocol_interfaces; void *uninstall_multiple_protocol_interfaces; void *calculate_crc32; void *copy_mem; void *set_mem; void *create_event_ex; } efi_boot_services_t; typedef struct { efi_table_header_t header; unsigned long get_time; unsigned long set_time; unsigned long get_wakeup_time; unsigned long set_wakeup_time; unsigned long set_virtual_address_map; unsigned long convert_pointer; unsigned long get_variable; unsigned long get_next_variable; unsigned long set_variable; unsigned long get_next_high_mono_count; efi_status_t (efiapi *reset_system)(int, int, int); unsigned long update_capsule; unsigned long query_capsule_caps; unsigned long query_variable_info; } efi_runtime_services_t; typedef struct { efi_guid_t guid; uint32_t table; } efi32_config_table_t; typedef struct { efi_guid_t guid; uint64_t table; } efi64_config_table_t; typedef struct { efi_guid_t guid; void *table; } efi_config_table_t; typedef struct { efi_table_header_t header; uint32_t fw_vendor; uint32_t fw_revision; uint32_t con_in_handle; uint32_t con_in; uint32_t con_out_handle; uint32_t con_out; uint32_t std_err_handle; uint32_t std_err; uint32_t runtime_services; uint32_t boot_services; uint32_t num_config_tables; uint32_t config_tables; } efi32_system_table_t; typedef struct { efi_table_header_t header; uint64_t fw_vendor; uint32_t fw_revision; uint32_t pad; uint64_t con_in_handle; uint64_t con_in; uint64_t con_out_handle; uint64_t con_out; uint64_t std_err_handle; uint64_t std_err; uint64_t runtime_services; uint64_t boot_services; uint64_t num_config_tables; uint64_t config_tables; } efi64_system_table_t; typedef struct { efi_table_header_t header; efi_char16_t *fw_vendor; uint32_t fw_revision; efi_handle_t con_in_handle; efi_simple_text_in_t *con_in; efi_handle_t con_out_handle; efi_simple_text_out_t *con_out; efi_handle_t std_err_handle; efi_simple_text_out_t *std_err; efi_runtime_services_t *runtime_services; efi_boot_services_t *boot_services; uintn_t num_config_tables; efi_config_table_t *config_tables; } efi_system_table_t; typedef struct { uint32_t revision; efi_handle_t parent_handle; efi_system_table_t *system_table; efi_handle_t device_handle; void *file_path; void *reserved; uint32_t load_options_size; void *load_options; void *image_base; uint64_t image_size; int image_code_type; int image_data_type; void *unload; } efi_loaded_image_t; #endif /* EFI_H */ memtest86plus-7.20/boot/efisetup.c000066400000000000000000000632041471441223100171410ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2020-2024 Martin Whitaker. // // Derived from Linux 5.6 arch/x86/boot/compressed/eboot.c and extracts // from drivers/firmware/efi/libstub: // // Copyright 2011 Intel Corporation; author Matt Fleming #include #include "boot.h" #include "bootparams.h" #include "efi.h" #include "memsize.h" #include "string.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define MAP_BUFFER_HEADROOM 8 // number of descriptors #define MIN_H_RESOLUTION 640 // as required by our main display #define MIN_V_RESOLUTION 400 //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static efi_guid_t EFI_CONSOLE_OUT_DEVICE_GUID = { 0xd3b36f2c, 0xd551, 0x11d4, {0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }; static efi_guid_t EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = { 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a} }; static efi_guid_t EFI_LOADED_IMAGE_PROTOCOL_GUID = { 0x5b1b31a1, 0x9562, 0x11d2, {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }; static efi_system_table_t *sys_table = NULL; static uint32_t pref_h_resolution; static uint32_t pref_v_resolution; static bool rotate; static bool debug; //------------------------------------------------------------------------------ // Macro Functions //------------------------------------------------------------------------------ #define round_up(value, align) \ (((value) + (align) - 1) & ~((align) - 1)) // The following macros are used in Linux to hide differences in mixed mode. // For now, just support native mode. #define efi_table_attr(table, attr) \ table->attr #define efi_call_proto(proto, func, ...) \ proto->func(proto, ##__VA_ARGS__) #define efi_call_bs(func, ...) \ sys_table->boot_services->func(__VA_ARGS__) #define efi_call_rs(func, ...) \ sys_table->runtime_services->func(__VA_ARGS__) #define efi_get_num_handles(size) \ (int)((size) / sizeof(efi_handle_t)) #define efi_get_handle_at(array, index) \ (array)[index] //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static void print_unicode_string(efi_char16_t *str) { efi_call_proto(efi_table_attr(sys_table, con_out), output_string, str); } static void print_string(char *str) { char *s8; for (s8 = str; *s8; s8++) { efi_char16_t ch[2] = { 0 }; ch[0] = *s8; if (*s8 == '\n') { efi_char16_t cr[2] = { '\r', 0 }; print_unicode_string(cr); } print_unicode_string(ch); } } static void print_dec(unsigned value) { char buffer[16]; char *str = &buffer[15]; *str = '\0'; do { str--; *str = '0' + value % 10; value /= 10; } while (value > 0); print_string(str); } static void print_hex(uintptr_t value) { char buffer[32]; char *str = &buffer[31]; *str = '\0'; do { str--; *str = '0' + value % 16; if (*str > '9') *str += 'a' - '0' - 10; value /= 16; } while (value > 0); print_string(str); } static void wait_for_key(void) { efi_input_key_t input_key; while (efi_call_proto(efi_table_attr(sys_table, con_in), read_key_stroke, &input_key) == EFI_NOT_READY) {} } static void test_frame_buffer(screen_info_t *si) { uint32_t r_value = 0xffffffff >> (32 - si->red_size); uint32_t g_value = 0; uint32_t b_value = 0; int pixel_size = (si->lfb_depth / 8); union { uint8_t byte[4]; uint32_t word; } pixel_value; pixel_value.word = (r_value << si->red_pos) | (g_value << si->green_pos) | (b_value << si->blue_pos); uintptr_t lfb_base = si->lfb_base; #if (ARCH_BITS == 64) if (LFB_CAPABILITY_64BIT_BASE & si->capabilities) { lfb_base |= (uintptr_t)si->ext_lfb_base << 32; } #endif uint8_t *lfb_row = (uint8_t *)lfb_base; for (int y = 0; y < 4; y++) { for (int x = 0; x < si->lfb_width; x++) { for (int b = 0; b < pixel_size; b++) { lfb_row[x * pixel_size + b] = pixel_value.byte[b]; } } lfb_row += si->lfb_linelength * 2; } lfb_row += (si->lfb_height - 16) * si->lfb_linelength; for (int y = 0; y < 4; y++) { for (int x = 0; x < si->lfb_width; x++) { for (int b = 0; b < pixel_size; b++) { lfb_row[x * pixel_size + b] = pixel_value.byte[b]; } } lfb_row += si->lfb_linelength * 2; } } static int get_cmd_line_length(efi_loaded_image_t *image) { // We only use ASCII characters in our command line options, so for simplicity // just truncate the command line if we find a non-ASCII character. efi_char16_t *cmd_line = (efi_char16_t *)image->load_options; int max_length = image->load_options_size / sizeof(efi_char16_t); int length = 0; // Skip Unicode byte order mark if present if (cmd_line[0] == u'\uFEFF') { cmd_line = &cmd_line[1]; max_length--; } while (length < max_length && cmd_line[length] > 0x00 && cmd_line[length] < 0x80) { length++; } return length; } static void get_cmd_line(efi_loaded_image_t *image, int num_chars, char *buffer) { efi_char16_t *cmd_line = (efi_char16_t *)image->load_options; if (cmd_line[0] == u'\uFEFF') { cmd_line = &cmd_line[1]; } for (int i = 0; i < num_chars; i++) { buffer[i] = cmd_line[i]; } buffer[num_chars] = '\0'; } static void parse_option(const char *option, int option_length) { if ((option_length == 8) && (strncmp(option, "efidebug", 8) == 0)) { debug = true; return; } if ((option_length < 8) || (strncmp(option, "screen.", 7) != 0)) return; option_length -= 7; option += 7; if ((option_length == 6) && (strncmp(option, "rhs-up", 6) == 0)) { rotate = true; return; } if ((option_length == 6) && (strncmp(option, "lhs-up", 6) == 0)) { rotate = true; return; } if ((option_length >= 6) && (strncmp(option, "mode=", 5) == 0)) { option_length -= 5; option += 5; if ((option_length == 4) && (strncmp(option, "bios", 4) == 0)) { pref_h_resolution = 0; pref_v_resolution = 0; return; } int h_value = 0; while ((option_length > 0) && (*option >= '0') && (*option <= '9')) { h_value = h_value * 10 + (*option - '0'); option_length--; option++; } if ((option_length < 2) || (*option != 'x')) return; option_length--; option++; int v_value = 0; while ((option_length > 0) && (*option >= '0') && (*option <= '9')) { v_value = v_value * 10 + (*option - '0'); option_length--; option++; } if (option_length != 0) return; pref_h_resolution = h_value; pref_v_resolution = v_value; return; } } static void parse_cmd_line(uintptr_t cmd_line_addr, int cmd_line_size) { pref_h_resolution = UINT32_MAX; pref_v_resolution = UINT32_MAX; rotate = false; if (cmd_line_addr != 0) { const char *cmd_line = (const char *)cmd_line_addr; const char *option = cmd_line; int option_length = 0; for (int i = 0; i < cmd_line_size; i++) { switch (cmd_line[i]) { case '\0': parse_option(option, option_length); return; case ' ': parse_option(option, option_length); option = &cmd_line[i+1]; option_length = 0; break; default: option_length++; break; } } } } static efi_status_t alloc_memory(void **ptr, size_t size, efi_phys_addr_t max_addr) { efi_status_t status; size_t num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; efi_phys_addr_t addr = max_addr; status = efi_call_bs(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA, num_pages, &addr); if (status == EFI_SUCCESS) { *ptr = (void *)(uintptr_t)addr; } return status; } static efi_memory_desc_t *get_memory_desc(uintptr_t map_addr, size_t desc_size, size_t n) { return (efi_memory_desc_t *)(map_addr + n * desc_size); } static bool map_buffer_has_headroom(size_t buffer_size, size_t map_size, size_t desc_size) { size_t slack = buffer_size - map_size; return slack / desc_size >= MAP_BUFFER_HEADROOM; } static efi_status_t get_memory_map( efi_memory_desc_t **mem_map, uintn_t *mem_map_size, uintn_t *mem_desc_size, uint32_t *mem_desc_version, uintn_t *mem_map_key, uintn_t *map_buffer_size ) { efi_status_t status; *mem_map = NULL; *map_buffer_size = *mem_map_size = 32 * sizeof(efi_memory_desc_t); // for first try again: status = efi_call_bs(allocate_pool, EFI_LOADER_DATA, *map_buffer_size, (void **)mem_map); if (status != EFI_SUCCESS) { goto fail; } status = efi_call_bs(get_memory_map, mem_map_size, *mem_map, mem_map_key, mem_desc_size, mem_desc_version); if (status == EFI_BUFFER_TOO_SMALL || !map_buffer_has_headroom(*map_buffer_size, *mem_map_size, *mem_desc_size)) { efi_call_bs(free_pool, *mem_map); // Make sure there is some headroom so that the buffer can be reused // for a new map after allocations are no longer permitted. It's // unlikely that the map will grow to exceed this headroom once we // are ready to trigger ExitBootServices(). *mem_map_size += *mem_desc_size * MAP_BUFFER_HEADROOM; *map_buffer_size = *mem_map_size; goto again; } if (status != EFI_SUCCESS) { efi_call_bs(free_pool, *mem_map); goto fail; } fail: return status; } static void get_bit_range(uint32_t mask, uint8_t *pos, uint8_t *size) { int first = 0; int length = 0; if (mask) { while (!(mask & 0x1)) { mask >>= 1; first++; } while (mask & 0x1) { mask >>= 1; length++; } } *pos = first; *size = length; } static efi_graphics_output_t *find_gop(efi_handle_t *handles, size_t handles_size) { efi_status_t status; efi_graphics_output_t *first_gop = NULL; for (int i = 0; i < efi_get_num_handles(handles_size); i++) { efi_handle_t handle = efi_get_handle_at(handles, i); efi_graphics_output_t *gop = NULL; status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&gop); if (status != EFI_SUCCESS) { continue; } efi_gop_mode_t *mode = efi_table_attr(gop, mode); efi_gop_mode_info_t *info = efi_table_attr(mode, info); // BLT is not available after we call ExitBootServices(). if (info->pixel_format == PIXEL_BLT_ONLY) { continue; } if (debug) { print_string("Found GOP with "); print_dec(mode->max_mode); print_string(" modes\n"); } // Systems that use the UEFI Console Splitter may provide multiple GOP // devices, not all of which are backed by real hardware. The workaround // is to search for a GOP implementing the ConOut protocol, and if one // isn't found, to just fall back to the first GOP. void *con_out = NULL; status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out); if (status == EFI_SUCCESS) { if (debug) { print_string("This GOP implements the ConOut protocol\n"); } return gop; } if (first_gop == NULL) { first_gop = gop; } } return first_gop; } static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *handles, size_t handles_size) { efi_status_t status; efi_graphics_output_t *gop = find_gop(handles, handles_size); if (!gop) { print_string("No graphics display found\n"); return EFI_NOT_FOUND; } efi_gop_mode_t *mode = efi_table_attr(gop, mode); bool use_current_mode = (pref_h_resolution == 0) && (pref_v_resolution == 0); if (debug) { print_string("Requested size : "); if ((pref_h_resolution == UINT32_MAX) && (pref_v_resolution == UINT32_MAX)) { print_string("auto"); } else { print_dec(pref_h_resolution); print_string(" x "); print_dec(pref_v_resolution); } if (rotate) { print_string(" rotated"); } print_string("\n"); } efi_gop_mode_info_t best_info; best_info.h_resolution = UINT32_MAX; best_info.v_resolution = UINT32_MAX; uint32_t best_mode = UINT32_MAX; if (use_current_mode) { best_mode = mode->mode; best_info = *mode->info; } else { for (uint32_t mode_num = 0; mode_num < mode->max_mode; mode_num++) { efi_gop_mode_info_t *info; uintn_t info_size; status = efi_call_proto(gop, query_mode, mode_num, &info_size, &info); if (status != EFI_SUCCESS) { continue; } if ((info->h_resolution == pref_h_resolution) && (info->v_resolution == pref_v_resolution)) { best_mode = mode_num; best_info = *info; break; } if (rotate) { if (info->v_resolution >= MIN_H_RESOLUTION && info->h_resolution >= MIN_V_RESOLUTION && info->v_resolution < best_info.v_resolution) { best_mode = mode_num; best_info = *info; } } else { if (info->h_resolution >= MIN_H_RESOLUTION && info->v_resolution >= MIN_V_RESOLUTION && info->h_resolution < best_info.h_resolution) { best_mode = mode_num; best_info = *info; } } efi_call_bs(free_pool, info); } } if (best_mode == UINT32_MAX) { print_string("No suitable screen resolution found\n"); return EFI_NOT_FOUND; } efi_phys_addr_t lfb_base = efi_table_attr(mode, frame_buffer_base); si->orig_video_isVGA = VIDEO_TYPE_EFI; si->lfb_width = best_info.h_resolution; si->lfb_height = best_info.v_resolution; si->lfb_base = lfb_base; #if (ARCH_BITS == 64) if (lfb_base >> 32) { si->capabilities |= LFB_CAPABILITY_64BIT_BASE; si->ext_lfb_base = lfb_base >> 32; } #endif switch (best_info.pixel_format) { case PIXEL_RGB_RESERVED_8BIT_PER_COLOR: if (debug) { print_string("RGB32 mode\n"); } si->lfb_depth = 32; si->lfb_linelength = best_info.pixels_per_scan_line * 4; si->red_size = 8; si->red_pos = 0; si->green_size = 8; si->green_pos = 8; si->blue_size = 8; si->blue_pos = 16; si->rsvd_size = 8; si->rsvd_pos = 24; break; case PIXEL_BGR_RESERVED_8BIT_PER_COLOR: if (debug) { print_string("BGR32 mode\n"); } si->lfb_depth = 32; si->lfb_linelength = best_info.pixels_per_scan_line * 4; si->red_size = 8; si->red_pos = 16; si->green_size = 8; si->green_pos = 8; si->blue_size = 8; si->blue_pos = 0; si->rsvd_size = 8; si->rsvd_pos = 24; break; case PIXEL_BIT_MASK: if (debug) { print_string("Bit mask mode\n"); } get_bit_range(best_info.pixel_info.red_mask, &si->red_pos, &si->red_size); get_bit_range(best_info.pixel_info.green_mask, &si->green_pos, &si->green_size); get_bit_range(best_info.pixel_info.blue_mask, &si->blue_pos, &si->blue_size); get_bit_range(best_info.pixel_info.rsvd_mask, &si->rsvd_pos, &si->rsvd_size); si->lfb_depth = si->red_size + si->green_size + si->blue_size + si->rsvd_size; si->lfb_linelength = (best_info.pixels_per_scan_line * si->lfb_depth) / 8; break; default: if (debug) { print_string("Unsupported mode\n"); } si->lfb_depth = 4; si->lfb_linelength = si->lfb_width / 2; si->red_size = 0; si->red_pos = 0; si->green_size = 0; si->green_pos = 0; si->blue_size = 0; si->blue_pos = 0; si->rsvd_size = 0; si->rsvd_pos = 0; break; } si->lfb_size = si->lfb_linelength * si->lfb_height; if (debug) { print_string("FB base : "); print_hex((uintptr_t)lfb_base); print_string("\n"); print_string("FB size : "); print_dec(si->lfb_width); print_string(" x "); print_dec(si->lfb_height); print_string("\n"); print_string("FB format :"); print_string(" R"); print_dec(si->red_size); print_string(" G"); print_dec(si->green_size); print_string(" B"); print_dec(si->blue_size); print_string(" A"); print_dec(si->rsvd_size); print_string("\n"); print_string("FB stride : "); print_dec(si->lfb_linelength); print_string("\n"); print_string("Press any key to continue...\n"); wait_for_key(); } if (!use_current_mode) { status = efi_call_proto(gop, set_mode, best_mode); if (status != EFI_SUCCESS) { print_string("Set GOP mode failed\n"); return status; } } if (debug) { test_frame_buffer(si); print_string("Press any key to continue...\n"); wait_for_key(); } return EFI_SUCCESS; } static efi_status_t set_screen_info(boot_params_t *boot_params) { efi_status_t status; uintn_t handles_size = 0; efi_handle_t *handles = NULL; status = efi_call_bs(locate_handle, EFI_LOCATE_BY_PROTOCOL, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, &handles_size, handles); if (status == EFI_BUFFER_TOO_SMALL) { status = efi_call_bs(allocate_pool, EFI_LOADER_DATA, handles_size, (void **)&handles); if (status != EFI_SUCCESS) { return status; } status = efi_call_bs(locate_handle, EFI_LOCATE_BY_PROTOCOL, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, &handles_size, handles); if (status == EFI_SUCCESS) { status = set_screen_info_from_gop(&boot_params->screen_info, handles, handles_size); } if (status == EFI_NOT_FOUND) { // This may be a headless system. We can still output to a serial console. boot_params->screen_info.orig_video_isVGA = VIDEO_TYPE_NONE; status = EFI_SUCCESS; } efi_call_bs(free_pool, handles); } else if (status == EFI_NOT_FOUND) { // This may be a headless system. We can still output to a serial console. boot_params->screen_info.orig_video_isVGA = VIDEO_TYPE_NONE; status = EFI_SUCCESS; } return status; } static efi_status_t set_efi_info_and_exit_boot_services(efi_handle_t handle, boot_params_t *boot_params) { efi_status_t status; efi_memory_desc_t *mem_map = NULL; uintn_t mem_map_size = 0; uintn_t mem_desc_size = 0; uint32_t mem_desc_version = 0; uintn_t mem_map_key = 0; uintn_t map_buffer_size = 0; status = get_memory_map(&mem_map, &mem_map_size, &mem_desc_size, &mem_desc_version, &mem_map_key, &map_buffer_size); if (status != EFI_SUCCESS) { goto fail; } status = efi_call_bs(exit_boot_services, handle, mem_map_key); if (status == EFI_INVALID_PARAMETER) { // The memory map changed between efi_get_memory_map() and // exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: // EFI_BOOT_SERVICES.ExitBootServices, we need to get the // updated map, and try again. The spec implies one retry // should be sufficent, which is confirmed against the EDK2 // implementation. Per the spec, we can only invoke // get_memory_map() and exit_boot_services() - we cannot alloc // so efi_get_memory_map() cannot be used, and we must reuse // the buffer. For all practical purposes, the headroom in the // buffer should account for any changes in the map so the call // to get_memory_map() is expected to succeed here. mem_map_size = map_buffer_size; status = efi_call_bs(get_memory_map, &mem_map_size, mem_map, &mem_map_key, &mem_desc_size, &mem_desc_version); if (status != EFI_SUCCESS) { goto fail; } status = efi_call_bs(exit_boot_services, handle, mem_map_key); } if (status != EFI_SUCCESS) { goto fail; } #if (ARCH_BITS == 64) boot_params->efi_info.loader_signature = EFI64_LOADER_SIGNATURE; #else boot_params->efi_info.loader_signature = EFI32_LOADER_SIGNATURE; #endif boot_params->efi_info.sys_tab = (uintptr_t)sys_table; boot_params->efi_info.mem_desc_size = mem_desc_size; boot_params->efi_info.mem_desc_version = mem_desc_version; boot_params->efi_info.mem_map = (uintptr_t)mem_map; boot_params->efi_info.mem_map_size = mem_map_size; #if (ARCH_BITS == 64) boot_params->efi_info.sys_tab_hi = (uintptr_t)sys_table >> 32; boot_params->efi_info.mem_map_hi = (uintptr_t)mem_map >> 32; #endif fail: return status; } static void set_e820_map(boot_params_t *params) { uintptr_t mem_map_addr = params->efi_info.mem_map; #if (ARCH_BITS == 64) mem_map_addr |= (uintptr_t)params->efi_info.mem_map_hi << 32; #endif size_t mem_map_size = params->efi_info.mem_map_size; size_t mem_desc_size = params->efi_info.mem_desc_size; size_t num_descs = mem_map_size / mem_desc_size; e820_entry_t *prev = NULL; e820_entry_t *next = params->e820_map; int num_entries = 0; for (size_t i = 0; i < num_descs && num_entries < E820_MAP_SIZE; i++) { efi_memory_desc_t *mem_desc = get_memory_desc(mem_map_addr, mem_desc_size, i); e820_type_t e820_type = E820_RESERVED; switch (mem_desc->type) { case EFI_ACPI_RECLAIM_MEMORY: e820_type = E820_ACPI; break; case EFI_LOADER_CODE: case EFI_LOADER_DATA: case EFI_BOOT_SERVICES_CODE: case EFI_BOOT_SERVICES_DATA: case EFI_CONVENTIONAL_MEMORY: e820_type = E820_RAM; break; default: continue; } // Merge adjacent mappings. if (prev && prev->type == e820_type && (prev->addr + prev->size) == mem_desc->phys_addr) { prev->size += mem_desc->num_pages << PAGE_SHIFT; continue; } next->addr = mem_desc->phys_addr; next->size = mem_desc->num_pages << PAGE_SHIFT; next->type = e820_type; prev = next++; num_entries++; } params->e820_entries = num_entries; } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ boot_params_t *efi_setup(efi_handle_t handle, efi_system_table_t *sys_table_arg, boot_params_t *boot_params) { efi_status_t status; sys_table = sys_table_arg; if (sys_table->header.signature != EFI_SYSTEM_TABLE_SIGNATURE) { print_string("bad system table signature\n"); goto fail; } if (boot_params == NULL) { efi_loaded_image_t *image; status = efi_call_bs(handle_protocol, handle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, (void **)&image); if (status != EFI_SUCCESS) { print_string("failed to get handle for loaded image protocol\n"); goto fail; } int cmd_line_length = get_cmd_line_length(image); // Allocate below 3GB to avoid having to remap. status = alloc_memory((void **)&boot_params, sizeof(boot_params_t) + cmd_line_length + 1, 0xbfffffff); if (status != EFI_SUCCESS) { print_string("failed to allocate low memory for boot params\n"); goto fail; } memset(boot_params, 0, sizeof(boot_params_t)); uintptr_t cmd_line_addr = (uintptr_t)boot_params + sizeof(boot_params_t); get_cmd_line(image, cmd_line_length, (char *)cmd_line_addr); boot_params->cmd_line_ptr = cmd_line_addr; boot_params->cmd_line_size = cmd_line_length + 1; } boot_params->code32_start = (uintptr_t)startup32; parse_cmd_line(boot_params->cmd_line_ptr, boot_params->cmd_line_size); status = set_screen_info(boot_params); if (status != EFI_SUCCESS) { print_string("set_screen_info() failed\n"); goto fail; } status = set_efi_info_and_exit_boot_services(handle, boot_params); if (status != EFI_SUCCESS) { print_string("set_efi_info_and_exit_boot_services() failed\n"); goto fail; } set_e820_map(boot_params); return boot_params; fail: print_string("efi_setup() failed\n"); while (1) { #if defined(__x86_64) || defined(__i386__) __asm__("hlt"); #elif defined(__loongarch_lp64) __asm__("idle 0"); #endif } } memtest86plus-7.20/boot/header.S000066400000000000000000000151161471441223100165240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // header.S supports booting directly from a UEFI BIOS or via an intermediate // bootloader that supports the Linux boot protocol. When booted directly from // the BIOS, it provides the MS-DOS & PE/COFF headers. When using an intermediate // bootloader, it provides the first few bytes of the Linux boot header (at the // end of the boot sector), with the remainder of the header being provided by // setup.S. // // Copyright (C) 2020-2024 Martin Whitaker. // // Derived from Linux 5.6 arch/x86/boot/header.S: // // Copyright (C) 1991, 1992 Linus Torvalds // // Based on bootsect.S and setup.S // modified by more people than can be counted // // Rewritten as a common file by H. Peter Anvin (Apr 2007) #define __ASSEMBLY__ #include "boot.h" #include "peimage.h" # The EFI loader loads the header at ImageBase, so we have to locate the main program # after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load # address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code. #define IMAGE_BASE 0x200000 .section ".header", "ax", @progbits .code16 .globl boot boot: # "MZ", the MS-DOS header signature. .byte 0x4d .byte 0x5a # In case we are booted by a legacy BIOS, print an error message. # Fortunately the MS-DOS header translates to harmless instructions. ljmp $BOOT_SEG, $(error - boot) error: movw %cs, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss xorw %sp, %sp sti cld movw $error_msg, %si 0: lodsb andb %al, %al jz wait movb $0xe, %ah movw $7, %bx int $0x10 jmp 0b wait: # Allow the user to press a key, then reboot. xorw %ax, %ax int $0x16 int $0x19 # int 0x19 should never return. In case it does, invoke the BIOS. # reset code. ljmp $0xf000,$0xfff0 # The PE header pointer. .org 0x3c .long pe_header error_msg: .ascii "This is a UEFI bootable image\r\n" .ascii "\n" .asciz "Press any key to reboot\r\n" pe_header: .ascii "PE" .word 0 coff_header: #ifdef __x86_64__ .word IMAGE_FILE_MACHINE_X64 # Machine (x86-64) #else .word IMAGE_FILE_MACHINE_I386 # Machine (i386) #endif .word 3 # NumberOfSections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 0 # NumberOfSymbols .word section_table - optional_header # SizeOfOptionalHeader #ifdef __x86_64__ .word IMAGE_FILE_DEBUG_STRIPPED \ | IMAGE_FILE_LOCAL_SYMS_STRIPPED \ | IMAGE_FILE_LINE_NUMS_STRIPPED \ | IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics #else .word IMAGE_FILE_32BIT_MACHINE \ | IMAGE_FILE_DEBUG_STRIPPED \ | IMAGE_FILE_LOCAL_SYMS_STRIPPED \ | IMAGE_FILE_LINE_NUMS_STRIPPED \ | IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics. #endif optional_header: #ifdef __x86_64__ .word IMAGE_NT_OPTIONAL_HDR64_MAGIC # PE32+ format #else .word IMAGE_NT_OPTIONAL_HDR32_MAGIC # PE32 format #endif .byte 0x02 # MajorLinkerVersion .byte 0x14 # MinorLinkerVersion .long _virt_text_size # SizeOfCode .long _virt_sbat_size # SizeOfInitializedData .long 0 # SizeOfUninitializedData .long _virt_text_start + 0x1e0 # AddressOfEntryPoint .long _virt_text_start # BaseOfCode #ifndef __x86_64__ .long _virt_sbat_start # BaseOfData #endif extra_header_fields: #ifdef __x86_64__ .quad IMAGE_BASE # ImageBase #else .long IMAGE_BASE # ImageBase #endif .long 4096 # SectionAlignment .long 512 # FileAlignment .word 0 # MajorOperatingSystemVersion .word 0 # MinorOperatingSystemVersion .word 0 # MajorImageVersion .word 0 # MinorImageVersion .word 0 # MajorSubsystemVersion .word 0 # MinorSubsystemVersion .long 0 # Win32VersionValue .long _virt_img_size # SizeOfImage .long _file_head_size # SizeOfHeaders .long 0 # CheckSum .word 10 # Subsystem (EFI application) .word 0 # DllCharacteristics #ifdef __x86_64__ .quad 0 # SizeOfStackReserve .quad 0 # SizeOfStackCommit .quad 0 # SizeOfHeapReserve .quad 0 # SizeOfHeapCommit #else .long 0 # SizeOfStackReserve .long 0 # SizeOfStackCommit .long 0 # SizeOfHeapReserve .long 0 # SizeOfHeapCommit #endif .long 0 # LoaderFlags .long IMAGE_DIRECTORY_ENTRY_DEBUG # NumberOfRvaAndSizes .long 0 # DataDirectory.Export.VirtualAddress .long 0 # DataDirectory.Export.Size .long 0 # DataDirectory.Import.VirtualAddress .long 0 # DataDirectory.Import.Size .long 0 # DataDirectory.Resource.VirtualAddress .long 0 # DataDirectory.Resource.Size .long 0 # DataDirectory.Exception.VirtualAddress .long 0 # DataDirectory.Exception.Size .long 0 # DataDirectory.Certs.VirtualAddress .long 0 # DataDirectory.Certs.Size .long _virt_reloc_start # DataDirectory.BaseReloc.VirtualAddress .long _real_reloc_size # DataDirectory.BaseReloc.Size # Section table section_table: .ascii ".text" .byte 0 .byte 0 .byte 0 .long _virt_text_size # VirtualSize .long _virt_text_start # VirtualAddress .long _file_text_size # SizeOfRawData .long _file_text_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers .long IMAGE_SCN_MEM_READ \ | IMAGE_SCN_MEM_WRITE \ | IMAGE_SCN_MEM_EXECUTE \ | IMAGE_SCN_CNT_CODE # Characteristics (section flags) .ascii ".reloc" .byte 0 .byte 0 .long _virt_reloc_size # VirtualSize .long _virt_reloc_start # VirtualAddress .long _file_reloc_size # SizeOfRawData .long _file_reloc_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers .long IMAGE_SCN_MEM_READ \ | IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags) .ascii ".sbat" .byte 0 .byte 0 .byte 0 .long _virt_sbat_size # VirtualSize .long _virt_sbat_start # VirtualAddress .long _file_sbat_size # SizeOfRawData .long _file_sbat_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers .long IMAGE_SCN_MEM_READ \ | IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags) # Emulate the Linux boot header, to allow loading by intermediate boot loaders. .org 497 setup_sects: .byte SETUP_SECS root_flags: .word 0 sys_size: .long _sys_size ram_size: .word 0 vid_mode: .word 0 root_dev: .word 0 boot_flag: .word 0xAA55 .org 512 .section ".reloc" .long 0 // Page RVA .long 10 // Block Size (2*4+2) .word (IMAGE_REL_BASED_ABSOLUTE << 12) + 0 // reloc 0 -> 0 .section ".sbat", "a", @progbits .incbin "../boot/sbat.csv" memtest86plus-7.20/boot/loongarch/000077500000000000000000000000001471441223100171205ustar00rootroot00000000000000memtest86plus-7.20/boot/loongarch/header.S000066400000000000000000000111631471441223100204760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // header64.S supports booting directly from a LoongArcht64 UEFI BIOS or via // an intermediate bootloader that supports the Linux boot protocol. When booted // directly from the BIOS, it provides the MS-DOS & PE/COFF headers. // // Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved. // #define __ASSEMBLY__ #include "boot.h" #include "peimage.h" # The EFI loader loads the header at ImageBase, so we have to locate the main program # after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load # address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code. #define IMAGE_BASE 0x200000 .section ".header", "ax", @progbits .globl head head: # "MZ", the MS-DOS header signature. .byte 0x4d .byte 0x5a # The PE header pointer. .org 0x3c .long pe_header pe_header: .ascii "PE" .short 0 coff_header: .short IMAGE_FILE_MACHINE_LOONGARCH64 # Machine (LoongArch64) .short 3 # NumberOfSections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 0 # NumberOfSymbols .short section_table - optional_header # SizeOfOptionalHeader .short IMAGE_FILE_DEBUG_STRIPPED \ | IMAGE_FILE_LOCAL_SYMS_STRIPPED \ | IMAGE_FILE_LINE_NUMS_STRIPPED \ | IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics optional_header: .short IMAGE_NT_OPTIONAL_HDR64_MAGIC # PE32+ format .byte 0x02 # MajorLinkerVersion .byte 0x14 # MinorLinkerVersion .long _virt_text_size # SizeOfCode .long _virt_sbat_size # SizeOfInitializedData .long 0 # SizeOfUninitializedData .long _virt_text_start + 0x400 # AddressOfEntryPoint .long _virt_text_start # BaseOfCode extra_header_fields: .quad IMAGE_BASE # ImageBase .long 4096 # SectionAlignment .long 512 # FileAlignment .short 0 # MajorOperatingSystemVersion .short 0 # MinorOperatingSystemVersion .short 0 # MajorImageVersion .short 0 # MinorImageVersion .short 0 # MajorSubsystemVersion .short 0 # MinorSubsystemVersion .long 0 # Win32VersionValue .long _virt_img_size # SizeOfImage .long _file_head_size # SizeOfHeaders .long 0 # CheckSum .short 10 # Subsystem (EFI application) .short 0 # DllCharacteristics .quad 0 # SizeOfStackReserve .quad 0 # SizeOfStackCommit .quad 0 # SizeOfHeapReserve .quad 0 # SizeOfHeapCommit .long 0 # LoaderFlags .long IMAGE_DIRECTORY_ENTRY_DEBUG # NumberOfRvaAndSizes .long 0 # DataDirectory.Export.VirtualAddress .long 0 # DataDirectory.Export.Size .long 0 # DataDirectory.Import.VirtualAddress .long 0 # DataDirectory.Import.Size .long 0 # DataDirectory.Resource.VirtualAddress .long 0 # DataDirectory.Resource.Size .long 0 # DataDirectory.Exception.VirtualAddress .long 0 # DataDirectory.Exception.Size .long 0 # DataDirectory.Certs.VirtualAddress .long 0 # DataDirectory.Certs.Size .long _virt_reloc_start # DataDirectory.BaseReloc.VirtualAddress .long _real_reloc_size # DataDirectory.BaseReloc.Size # Section table section_table: .ascii ".text" .byte 0 .byte 0 .byte 0 .long _virt_text_size # VirtualSize .long _virt_text_start # VirtualAddress .long _file_text_size # SizeOfRawData .long _file_text_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .short 0 # NumberOfRelocations .short 0 # NumberOfLineNumbers .long IMAGE_SCN_MEM_READ \ | IMAGE_SCN_MEM_WRITE \ | IMAGE_SCN_MEM_EXECUTE \ | IMAGE_SCN_CNT_CODE # Characteristics (section flags) .ascii ".reloc" .byte 0 .byte 0 .long _virt_reloc_size # VirtualSize .long _virt_reloc_start # VirtualAddress .long _file_reloc_size # SizeOfRawData .long _file_reloc_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .short 0 # NumberOfRelocations .short 0 # NumberOfLineNumbers .long IMAGE_SCN_MEM_READ \ | IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags) .ascii ".sbat" .byte 0 .byte 0 .byte 0 .long _virt_sbat_size # VirtualSize .long _virt_sbat_start # VirtualAddress .long _file_sbat_size # SizeOfRawData .long _file_sbat_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .short 0 # NumberOfRelocations .short 0 # NumberOfLineNumbers .long IMAGE_SCN_MEM_READ \ | IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags) .org 512 .section ".reloc" .long 0 // Page RVA .long 10 // Block Size (2*4+2) .short (IMAGE_REL_BASED_ABSOLUTE << 12) + 0 // reloc 0 -> 0 .section ".sbat", "a", @progbits .incbin "../boot/sbat.csv" memtest86plus-7.20/boot/loongarch/startup64.S000066400000000000000000000263511471441223100211270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // startup64.S contains the 64-bit startup code for both the BSP and APs. // It initialises stacks, memory management, and exception handling, clears // the BSS, completes relocation, and finally calls the main application. // // Copyright (C) 2020-2022 Martin Whitaker. // Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved. // #define __ASSEMBLY__ #include "boot.h" #inlucde "interrupt.h" #include "registers.h" .text exception_entry: csrwr $sp, LOONGARCH_CSR_KS0 csrrd $sp, LOONGARCH_CSR_KS0 // // Push GP registers // addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + FP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE) st.d $zero, $sp, 0 * RSIZE st.d $ra, $sp, 1 * RSIZE st.d $tp, $sp, 2 * RSIZE st.d $a0, $sp, 4 * RSIZE st.d $a1, $sp, 5 * RSIZE st.d $a2, $sp, 6 * RSIZE st.d $a3, $sp, 7 * RSIZE st.d $a4, $sp, 8 * RSIZE st.d $a5, $sp, 9 * RSIZE st.d $a6, $sp, 10 * RSIZE st.d $a7, $sp, 11 * RSIZE st.d $t0, $sp, 12 * RSIZE st.d $t1, $sp, 13 * RSIZE st.d $t2, $sp, 14 * RSIZE st.d $t3, $sp, 15 * RSIZE st.d $t4, $sp, 16 * RSIZE st.d $t5, $sp, 17 * RSIZE st.d $t6, $sp, 18 * RSIZE st.d $t7, $sp, 19 * RSIZE st.d $t8, $sp, 20 * RSIZE st.d $r21, $sp, 21 * RSIZE st.d $fp, $sp, 22 * RSIZE st.d $s0, $sp, 23 * RSIZE st.d $s1, $sp, 24 * RSIZE st.d $s2, $sp, 25 * RSIZE st.d $s3, $sp, 26 * RSIZE st.d $s4, $sp, 27 * RSIZE st.d $s5, $sp, 28 * RSIZE st.d $s6, $sp, 29 * RSIZE st.d $s7, $sp, 30 * RSIZE st.d $s8, $sp, 31 * RSIZE csrrd $t0, LOONGARCH_CSR_KS0 // Read the old stack pointer. st.d $t0, $sp, 3 * RSIZE // // Push CSR registers // addi.d $sp, $sp, GP_REG_CONTEXT_SIZE csrrd $t0, LOONGARCH_CSR_CRMD st.d $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE csrrd $t0, LOONGARCH_CSR_PRMD st.d $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE csrrd $t0, LOONGARCH_CSR_EUEN st.d $t0, $sp, LOONGARCH_CSR_EUEN * RSIZE csrrd $t0, LOONGARCH_CSR_MISC st.d $t0, $sp, LOONGARCH_CSR_MISC * RSIZE csrrd $t0, LOONGARCH_CSR_ECFG st.d $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE csrrd $t0, LOONGARCH_CSR_ESTAT st.d $t0, $sp, LOONGARCH_CSR_ESTAT * RSIZE csrrd $t0, LOONGARCH_CSR_ERA st.d $t0, $sp, LOONGARCH_CSR_ERA * RSIZE csrrd $t0, LOONGARCH_CSR_BADV st.d $t0, $sp, LOONGARCH_CSR_BADV * RSIZE csrrd $t0, LOONGARCH_CSR_BADI st.d $t0, $sp, LOONGARCH_CSR_BADI * RSIZE // // Push FP registers // addi.d $sp, $sp, CSR_REG_CONTEXT_SIZE csrrd $t0, LOONGARCH_CSR_EUEN andi $t0, $t0, 0x1 beqz $t0, PushRegDone fst.d $fa0, $sp, 0 * RSIZE fst.d $fa1, $sp, 1 * RSIZE fst.d $fa2, $sp, 2 * RSIZE fst.d $fa3, $sp, 3 * RSIZE fst.d $fa4, $sp, 4 * RSIZE fst.d $fa5, $sp, 5 * RSIZE fst.d $fa6, $sp, 6 * RSIZE fst.d $fa7, $sp, 7 * RSIZE fst.d $ft0, $sp, 8 * RSIZE fst.d $ft1, $sp, 9 * RSIZE fst.d $ft2, $sp, 10 * RSIZE fst.d $ft3, $sp, 11 * RSIZE fst.d $ft4, $sp, 12 * RSIZE fst.d $ft5, $sp, 13 * RSIZE fst.d $ft6, $sp, 14 * RSIZE fst.d $ft7, $sp, 15 * RSIZE fst.d $ft8, $sp, 16 * RSIZE fst.d $ft9, $sp, 17 * RSIZE fst.d $ft10, $sp, 18 * RSIZE fst.d $ft11, $sp, 19 * RSIZE fst.d $ft12, $sp, 20 * RSIZE fst.d $ft13, $sp, 21 * RSIZE fst.d $ft14, $sp, 22 * RSIZE fst.d $ft15, $sp, 23 * RSIZE fst.d $fs0, $sp, 24 * RSIZE fst.d $fs1, $sp, 25 * RSIZE fst.d $fs2, $sp, 26 * RSIZE fst.d $fs3, $sp, 27 * RSIZE fst.d $fs4, $sp, 28 * RSIZE fst.d $fs5, $sp, 29 * RSIZE fst.d $fs6, $sp, 30 * RSIZE fst.d $fs7, $sp, 31 * RSIZE movfcsr2gr $t3, $fcsr0 st.d $t3, $sp, 32 * RSIZE // Push the FCSR0 register. // // Push the fcc0-fcc7 registers. // movcf2gr $t3, $fcc0 move $t2, $t3 movcf2gr $t3, $fcc1 bstrins.d $t2, $t3, 0xf, 0x8 movcf2gr $t3, $fcc2 bstrins.d $t2, $t3, 0x17, 0x10 movcf2gr $t3, $fcc3 bstrins.d $t2, $t3, 0x1f, 0x18 movcf2gr $t3, $fcc4 bstrins.d $t2, $t3, 0x27, 0x20 movcf2gr $t3, $fcc5 bstrins.d $t2, $t3, 0x2f, 0x28 movcf2gr $t3, $fcc6 bstrins.d $t2, $t3, 0x37, 0x30 movcf2gr $t3, $fcc7 bstrins.d $t2, $t3, 0x3f, 0x38 st.d $t2, $sp, 33 * RSIZE // // Push exception context down // PushRegDone: addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE) move $a0, $sp bl interrupt # If returned, POP the REG // // Pop CSR reigsters // addi.d $sp, $sp, GP_REG_CONTEXT_SIZE ld.d $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE csrwr $t0, LOONGARCH_CSR_CRMD ld.d $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE csrwr $t0, LOONGARCH_CSR_PRMD ld.d $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE csrwr $t0, LOONGARCH_CSR_ECFG ld.d $t0, $sp, LOONGARCH_CSR_ERA * RSIZE csrwr $t0, LOONGARCH_CSR_ERA addi.d $sp, $sp, CSR_REG_CONTEXT_SIZE // Fource change the stack pointer befor pop the FP registers. csrrd $t1, LOONGARCH_CSR_EUEN andi $t1, $t1, 0x1 beqz $t1, PopGP // If the FPE not set, only pop the GP registers. // // Pop FP registers // fld.d $fa0, $sp, 0 * RSIZE fld.d $fa1, $sp, 1 * RSIZE fld.d $fa2, $sp, 2 * RSIZE fld.d $fa3, $sp, 3 * RSIZE fld.d $fa4, $sp, 4 * RSIZE fld.d $fa5, $sp, 5 * RSIZE fld.d $fa6, $sp, 6 * RSIZE fld.d $fa7, $sp, 7 * RSIZE fld.d $ft0, $sp, 8 * RSIZE fld.d $ft1, $sp, 9 * RSIZE fld.d $ft2, $sp, 10 * RSIZE fld.d $ft3, $sp, 11 * RSIZE fld.d $ft4, $sp, 12 * RSIZE fld.d $ft5, $sp, 13 * RSIZE fld.d $ft6, $sp, 14 * RSIZE fld.d $ft7, $sp, 15 * RSIZE fld.d $ft8, $sp, 16 * RSIZE fld.d $ft9, $sp, 17 * RSIZE fld.d $ft10, $sp, 18 * RSIZE fld.d $ft11, $sp, 19 * RSIZE fld.d $ft12, $sp, 20 * RSIZE fld.d $ft13, $sp, 21 * RSIZE fld.d $ft14, $sp, 22 * RSIZE fld.d $ft15, $sp, 23 * RSIZE fld.d $fs0, $sp, 24 * RSIZE fld.d $fs1, $sp, 25 * RSIZE fld.d $fs2, $sp, 26 * RSIZE fld.d $fs3, $sp, 27 * RSIZE fld.d $fs4, $sp, 28 * RSIZE fld.d $fs5, $sp, 29 * RSIZE fld.d $fs6, $sp, 30 * RSIZE fld.d $fs7, $sp, 31 * RSIZE ld.d $t0, $sp, 32 * RSIZE movgr2fcsr $fcsr0, $t0 // Pop the fcsr0 register. // // Pop the fcc0-fcc7 registers. // ld.d $t0, $sp, 33 * RSIZE bstrpick.d $t1, $t0, 7, 0 movgr2cf $fcc0, $t1 bstrpick.d $t1, $t0, 15, 8 movgr2cf $fcc1, $t1 bstrpick.d $t1, $t0, 23, 16 movgr2cf $fcc2, $t1 bstrpick.d $t1, $t0, 31, 24 movgr2cf $fcc3, $t1 bstrpick.d $t1, $t0, 39, 32 movgr2cf $fcc4, $t1 bstrpick.d $t1, $t0, 47, 40 movgr2cf $fcc5, $t1 bstrpick.d $t1, $t0, 55, 48 movgr2cf $fcc6, $t1 bstrpick.d $t1, $t0, 63, 56 movgr2cf $fcc7, $t1 PopGP: // // Pop GP registers // addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE) ld.d $ra, $sp, 1 * RSIZE ld.d $tp, $sp, 2 * RSIZE ld.d $a0, $sp, 4 * RSIZE ld.d $a1, $sp, 5 * RSIZE ld.d $a2, $sp, 6 * RSIZE ld.d $a3, $sp, 7 * RSIZE ld.d $a4, $sp, 8 * RSIZE ld.d $a5, $sp, 9 * RSIZE ld.d $a6, $sp, 10 * RSIZE ld.d $a7, $sp, 11 * RSIZE ld.d $t0, $sp, 12 * RSIZE ld.d $t1, $sp, 13 * RSIZE ld.d $t2, $sp, 14 * RSIZE ld.d $t3, $sp, 15 * RSIZE ld.d $t4, $sp, 16 * RSIZE ld.d $t5, $sp, 17 * RSIZE ld.d $t6, $sp, 18 * RSIZE ld.d $t7, $sp, 19 * RSIZE ld.d $t8, $sp, 20 * RSIZE ld.d $r21, $sp, 21 * RSIZE ld.d $fp, $sp, 22 * RSIZE ld.d $s0, $sp, 23 * RSIZE ld.d $s1, $sp, 24 * RSIZE ld.d $s2, $sp, 25 * RSIZE ld.d $s3, $sp, 26 * RSIZE ld.d $s4, $sp, 27 * RSIZE ld.d $s5, $sp, 28 * RSIZE ld.d $s6, $sp, 29 * RSIZE ld.d $s7, $sp, 30 * RSIZE ld.d $s8, $sp, 31 * RSIZE ld.d $sp, $sp, 3 * RSIZE ertn // Return from exception. .globl startup32 startup32: break 0 # Should be unreachable. # The EFI PE32+ boot entry point. The entry point for MP boot. .org 0x400 .globl efi_boot efi_boot: move $a2, $zero # the boot params pointer (0 = not yet allocated) bl efi_setup # Save the boot params pointer. la.pcrel $t0, boot_params_addr st.d $a0, $t0, 0x0 b startup64 # The 64-bit boot entry point and the entry point for AP boot. .org 0x440 .globl startup64 startup64: # Disable intrrupts globally li.w $t0, (0x1 << 2) csrxchg $zero, $t0, LOONGARCH_CSR_CRMD # Init Core li.w $t0, 0xA8 # DA mode, load/store cacheable, instructions cacheable, interrupts disabled, DMWn has no effect. csrwr $t0, LOONGARCH_CSR_CRMD // // Make sure the legacy boot mode works properly. // la.pcrel $t0, Jmp bstrins.d $t0, $zero, 63, 32 jr $t0 Jmp: invtlb 0x0, $zero, $zero # Set the PG and DMWn li.d $t0, 0x8000000000000001 csrwr $t0, LOONGARCH_CSR_DMWIN0 # Uncache able address window, MMIO base: 0x8000000000000000 li.d $t0, 0x11 # Cache able address window, PA == VA csrwr $t0, LOONGARCH_CSR_DMWIN1 li.w $t0, 0xB0 # PG mode, load/store cacheable, instructions cacheable, disable intrrupts, DMWn effected. csrwr $t0, LOONGARCH_CSR_CRMD # Enable all IPIs li.w $t0, 0xFFFFFFFF li.w $t1, 0x1004 iocsrwr.w $t0, $t1 # Turn on FPE to enable FPU. li.w $t0, (0x1 << 0) csrxchg $t0, $t0, LOONGARCH_CSR_EUEN b startup # The 64-bit main entry point and for restart after relocation. .org 0x500 .globl startup startup: # Disable intrrupts globally li.w $t0, (0x1 << 2) csrxchg $zero, $t0, LOONGARCH_CSR_CRMD # Some of the startup actions are not thread safe. Use a mutex # to protect this section of code. la.pcrel $t0, startup_mutex li.w $t1, 0x1 Locked: amswap_db.w $t2, $t1, $t0 bnez $t2, Locked Critical: # Set cores stack csrrd $t0, LOONGARCH_CSR_CPUID li.d $t1, AP_STACK_SIZE mul.d $t0, $t0, $t1 li.d $t1, BSP_STACK_SIZE - LOCALS_SIZE add.d $t0, $t0, $t1 la.pcrel $sp, _stacks add.d $sp, $sp, $t0 SetException: # Only enable the IPI li.w $t0, 0x1FFF csrxchg $zero, $t0, LOONGARCH_CSR_ECFG li.w $t0, (0x1 << 12) csrxchg $t0, $t0, LOONGARCH_CSR_ECFG # Set exception base csrrd $t0, LOONGARCH_CSR_ECFG bstrins.w $t0, $zero, 18, 16 csrwr $t0, LOONGARCH_CSR_ECFG la.pcrel $t0, exception_entry csrwr $t0, LOONGARCH_CSR_EBASE # Exception and interrupts la.pcrel $t0, exception_entry csrwr $t0, LOONGARCH_CSR_TLBREBASE # TLB refill # Enable intrrupts globally li.w $t0, (0x1 << 2) csrxchg $t0, $t0, LOONGARCH_CSR_CRMD bl reloc # Release the startup mutex. la.pcrel $t0, startup_mutex li.w $t1, 0x0 amswap_db.w $zero, $t1, $t0 # Run the application. bl main .previous # Variables. .data .align 4 .globl ap_startup_addr ap_startup_addr: .quad 0 # filled in at run time .globl boot_params_addr boot_params_addr: .quad 0 startup_mutex: .long 0 first_boot: .long 1 .previous # Startup stack. .bss .align 16 startup_stack_base: . = . + 64 startup_stack_top: .previous # Main stack area. .section ".stacks", "aw", @nobits .align 16 . = . + STACKS_SIZE .previous memtest86plus-7.20/boot/macros.h000066400000000000000000000014401471441223100166000ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef MACROS_H #define MACROS_H /** * \file * * Provides miscellaneous useful definitions. * *//* * Copyright (C) 2024 Lionel Debroux. */ #ifndef __ASSEMBLY__ #ifdef __GNUC__ // Enhanced definitions under GCC and compatible, e.g. Clang. // These are from GPLv2 Linux 6.7, for erroring out when the argument isn't an array type. #define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); }))) #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) #else // Fallback definitions. #define ARRAY_SIZE(var_) (sizeof(var_) / sizeof((var_)[0])) #endif #endif #endif memtest86plus-7.20/boot/mbr.S000066400000000000000000000067661471441223100160670ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // mbr.S supports booting directly from the BIOS when a memtest binary image is // stored on a hard disk or USB stick. When booted by the BIOS, it is loaded // at address 0x7c00. It then loads the setup code immediately after itself // (address 0x7e00) and the main program code at segment MAIN_SEG, using BIOS // interrupts to read the data from disk. It locates the start of the memtest // image containing the setup and main program code from the LBA stored at // offset 0x1b0. The LBA value is assumed to be offset by 4, for compatibility // with the xorrisofs --grub2-mbr option. // // The first 512B of the memtest binary image is not used, so either of the // memtest.bin or memtest.efi images may be used. // // Copyright (C) 2020 Martin Whitaker. #define __ASSEMBLY__ #include "boot.h" .section ".mbr", "ax", @progbits .code16 # The BIOS boot entry point. This will be located at 0x7c00. .globl boot boot: # Initialise the segment registers and the stack. ljmp $BOOT_SEG, $init init: movw %cs, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw $BOOT_STACK_TOP, %ax movw %ax, %sp # Check we have a valid drive number. If not, assume we are # booting from the first hard drive. testb $0x80, %dl jz 0f testb $0x70, %dl jz 1f 0: movb $0x80, %dl 1: # Load the setup code. movw $SETUP_SECS, dap_length movw $SETUP_SEG, dap_segment movl image_base, %eax subl $3, %eax movl %eax, dap_lba_start movw $dap, %si movb $0x42, %ah int $0x13 jc load_error # Print a message. pushw %dx movb $0x03, %ah # read cursor pos xorb %bh, %bh int $0x10 leaw boot_msg, %bp movw $(boot_msg_end - boot_msg), %cx movw $0x0007, %bx # page 0, attribute 7 (normal) movw $0x1301, %ax # write string, move cursor int $0x10 popw %dx # Load the main test program. movw $MAIN_SEG, dap_segment addl $SETUP_SECS, dap_lba_start movw $_sys_size, %cx # length in 16B chunks addw $31, %cx # convert to sectors (rounding up) shrw $5, %cx 0: movw $64, %bx # load either 64 sectors or remaining cmpw %bx, %cx # length if less than 64 sectors ja 1f movw %cx, %bx 1: movw %bx, dap_length movw $dap, %si movb $0x42, %ah int $0x13 jc load_error addw $0x800, dap_segment addl $0x40, dap_lba_start subw %bx, %cx jg 0b # Turn off the floppy drive motor. movw $0x3f2, %dx xorb %al, %al outb %al, %dx # Turn off the text display cursor. movb $0x01, %ah movb $0x00, %bh movw $0x2000, %cx int $0x10 # Fix up the Linux boot header to indicate we've loaded into low memory. movl $LOW_LOAD_ADDR, 0x214 # After that (everything loaded), we jump to the setup code loaded # directly after the boot block. ljmp $SETUP_SEG, $0 load_error: movb %ah, %al call hex_to_ascii movb %al, error_msg+1 movb %ah, %al shrb $4, %al call hex_to_ascii movb %al, error_msg+0 movb $0x03, %ah # read cursor pos xorb %bh, %bh int $0x10 leaw error_msg, %bp movw $(error_msg_end - error_msg), %cx movw $0x0007, %bx # page 0, attribute 7 (normal) movw $0x1301, %ax # write string, move cursor int $0x10 0: hlt jmp 0b hex_to_ascii: andb $0xf, %al addb $'0', %al cmpb $'9', %al jle 1f addb $('A' - '0' - 10), %al 1: ret # Local variables. dap: .byte 0x10 .byte 0 dap_length: .word 0 dap_offset: .word 0 dap_segment: .word 0 dap_lba_start: .quad 0 boot_msg: .ascii "Loading Memtest86+\r\n" boot_msg_end: error_msg: .ascii " Disk read failed\r\n" error_msg_end: .org 0x1b0 image_base: .quad 5 # default to sector 1, offset by 4 .org 0x1fe boot_flag: .word 0xAA55 memtest86plus-7.20/boot/peimage.h000066400000000000000000000267501471441223100167360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // This is include/coff/pe.h from binutils-2.10.0.18 // Copyright The Free Software Foundation /* PE COFF header information */ #ifndef _PE_H #define _PE_H /* NT specific file attributes. */ #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 #define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 #define IMAGE_FILE_16BIT_MACHINE 0x0040 #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 #define IMAGE_FILE_32BIT_MACHINE 0x0100 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 #define IMAGE_FILE_SYSTEM 0x1000 #define IMAGE_FILE_DLL 0x2000 #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 /* Additional flags to be set for section headers to allow the NT loader to read and write to the section data (to replace the addresses of data in dlls for one thing); also to execute the section in .text's case. */ #define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 #define IMAGE_SCN_MEM_READ 0x40000000 #define IMAGE_SCN_MEM_WRITE 0x80000000 /* Section characteristics added for ppc-nt. */ #define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */ #define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */ #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */ #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */ #define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */ #define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */ #define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */ #define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */ #define IMAGE_SCN_MEM_FARDATA 0x00008000 #define IMAGE_SCN_MEM_PURGEABLE 0x00020000 #define IMAGE_SCN_MEM_16BIT 0x00020000 #define IMAGE_SCN_MEM_LOCKED 0x00040000 #define IMAGE_SCN_MEM_PRELOAD 0x00080000 #define IMAGE_SCN_ALIGN_1BYTES 0x00100000 #define IMAGE_SCN_ALIGN_2BYTES 0x00200000 #define IMAGE_SCN_ALIGN_4BYTES 0x00300000 #define IMAGE_SCN_ALIGN_8BYTES 0x00400000 #define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */ #define IMAGE_SCN_ALIGN_32BYTES 0x00600000 #define IMAGE_SCN_ALIGN_64BYTES 0x00700000 #define IMAGE_SCN_ALIGN_128BYTES 0x00800000 #define IMAGE_SCN_ALIGN_256BYTES 0x00900000 #define IMAGE_SCN_ALIGN_512BYTES 0x00a00000 #define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 #define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 #define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 #define IMAGE_SCN_ALIGN_8129BYTES 0x00e00000 #define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 #define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 #define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 #define IMAGE_SCN_MEM_SHARED 0x10000000 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 #define IMAGE_SCN_MEM_READ 0x40000000 #define IMAGE_SCN_MEM_WRITE 0x80000000 #define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */ #define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */ #define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */ #define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */ #define IMAGE_REL_BASED_ABSOLUTE 0x0000 /* COMDAT selection codes. */ #define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */ #define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */ #define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */ #define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */ #define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */ /* Machine numbers. */ #define IMAGE_FILE_MACHINE_UNKNOWN 0x0 #define IMAGE_FILE_MACHINE_ALPHA 0x184 #define IMAGE_FILE_MACHINE_ARM 0x1c0 #define IMAGE_FILE_MACHINE_ALPHA64 0x284 #define IMAGE_FILE_MACHINE_I386 0x14c #define IMAGE_FILE_MACHINE_IA64 0x200 #define IMAGE_FILE_MACHINE_M68K 0x268 #define IMAGE_FILE_MACHINE_MIPS16 0x266 #define IMAGE_FILE_MACHINE_MIPSFPU 0x366 #define IMAGE_FILE_MACHINE_MIPSFPU16 0x466 #define IMAGE_FILE_MACHINE_POWERPC 0x1f0 #define IMAGE_FILE_MACHINE_R3000 0x162 #define IMAGE_FILE_MACHINE_R4000 0x166 #define IMAGE_FILE_MACHINE_R10000 0x168 #define IMAGE_FILE_MACHINE_SH3 0x1a2 #define IMAGE_FILE_MACHINE_SH4 0x1a6 #define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x1c2 #define IMAGE_FILE_MACHINE_X64 0x8664 #define IMAGE_FILE_MACHINE_ARM64 0xaa64 #define IMAGE_FILE_MACHINE_RISCV32 0x5032 #define IMAGE_FILE_MACHINE_RISCV64 0x5064 #define IMAGE_FILE_MACHINE_RISCV128 0x5128 #define IMAGE_FILE_MACHINE_LOONGARCH32 0x6232 #define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264 #define IMAGE_SUBSYSTEM_UNKNOWN 0 #define IMAGE_SUBSYSTEM_NATIVE 1 #define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 #define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 #define IMAGE_SUBSYSTEM_POSIX_CUI 7 #define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 #define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 #define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 #define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 /* Magic values that are true for all dos/nt implementations */ #define DOSMAGIC 0x5a4d #define NT_SIGNATURE 0x00004550 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x010b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x020b #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define IMAGE_DIRECTORY_ENTRY_TLS 9 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 /* NT allows long filenames, we want to accommodate this. This may break some of the bfd functions */ #undef FILNMLEN #define FILNMLEN 18 /* # characters in a file name */ #ifndef __ASSEMBLY__ struct external_PEI_filehdr { /* DOS header fields - always at offset zero in the EXE file */ char e_magic[2]; /* Magic number, 0x5a4d */ char e_cblp[2]; /* Bytes on last page of file, 0x90 */ char e_cp[2]; /* Pages in file, 0x3 */ char e_crlc[2]; /* Relocations, 0x0 */ char e_cparhdr[2]; /* Size of header in paragraphs, 0x4 */ char e_minalloc[2]; /* Minimum extra paragraphs needed, 0x0 */ char e_maxalloc[2]; /* Maximum extra paragraphs needed, 0xFFFF */ char e_ss[2]; /* Initial (relative) SS value, 0x0 */ char e_sp[2]; /* Initial SP value, 0xb8 */ char e_csum[2]; /* Checksum, 0x0 */ char e_ip[2]; /* Initial IP value, 0x0 */ char e_cs[2]; /* Initial (relative) CS value, 0x0 */ char e_lfarlc[2]; /* File address of relocation table, 0x40 */ char e_ovno[2]; /* Overlay number, 0x0 */ char e_res[4][2]; /* Reserved words, all 0x0 */ char e_oemid[2]; /* OEM identifier (for e_oeminfo), 0x0 */ char e_oeminfo[2]; /* OEM information; e_oemid specific, 0x0 */ char e_res2[10][2]; /* Reserved words, all 0x0 */ char e_lfanew[4]; /* File address of new exe header, usually 0x80 */ char dos_message[16][4]; /* other stuff, always follow DOS header */ /* Note: additional bytes may be inserted before the signature. Use the e_lfanew field to find the actual location of the NT signature */ char nt_signature[4]; /* required NT signature, 0x4550 */ /* From standard header */ char f_magic[2]; /* magic number */ char f_nscns[2]; /* number of sections */ char f_timdat[4]; /* time & date stamp */ char f_symptr[4]; /* file pointer to symtab */ char f_nsyms[4]; /* number of symtab entries */ char f_opthdr[2]; /* sizeof(optional hdr) */ char f_flags[2]; /* flags */ }; #endif #ifdef COFF_IMAGE_WITH_PE /* The filehdr is only weird in images. */ #undef FILHDR #define FILHDR struct external_PEI_filehdr #undef FILHSZ #define FILHSZ 152 #endif /* COFF_IMAGE_WITH_PE */ /* 32-bit PE a.out header: */ #ifndef __ASSEMBLY__ typedef struct { AOUTHDR standard; /* NT extra fields; see internal.h for descriptions */ char ImageBase[4]; char SectionAlignment[4]; char FileAlignment[4]; char MajorOperatingSystemVersion[2]; char MinorOperatingSystemVersion[2]; char MajorImageVersion[2]; char MinorImageVersion[2]; char MajorSubsystemVersion[2]; char MinorSubsystemVersion[2]; char Reserved1[4]; char SizeOfImage[4]; char SizeOfHeaders[4]; char CheckSum[4]; char Subsystem[2]; char DllCharacteristics[2]; char SizeOfStackReserve[4]; char SizeOfStackCommit[4]; char SizeOfHeapReserve[4]; char SizeOfHeapCommit[4]; char LoaderFlags[4]; char NumberOfRvaAndSizes[4]; /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */ char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */ } PEAOUTHDR; #endif #undef AOUTSZ #define AOUTSZ (AOUTHDRSZ + 196) /* Like PEAOUTHDR, except that the "standard" member has no BaseOfData (aka data_start) member and that some of the members are 8 instead of just 4 bytes long. */ #ifndef __ASSEMBLY__ typedef struct { AOUTHDR standard; /* NT extra fields; see internal.h for descriptions */ char ImageBase[8]; char SectionAlignment[4]; char FileAlignment[4]; char MajorOperatingSystemVersion[2]; char MinorOperatingSystemVersion[2]; char MajorImageVersion[2]; char MinorImageVersion[2]; char MajorSubsystemVersion[2]; char MinorSubsystemVersion[2]; char Reserved1[4]; char SizeOfImage[4]; char SizeOfHeaders[4]; char CheckSum[4]; char Subsystem[2]; char DllCharacteristics[2]; char SizeOfStackReserve[8]; char SizeOfStackCommit[8]; char SizeOfHeapReserve[8]; char SizeOfHeapCommit[8]; char LoaderFlags[4]; char NumberOfRvaAndSizes[4]; /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */ char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */ } PEP64AOUTHDR; #endif #define PEP64AOUTSZ 240 #undef E_FILNMLEN #define E_FILNMLEN 18 /* # characters in a file name */ /* Import Tyoes fot ILF format object files.. */ #define IMPORT_CODE 0 #define IMPORT_DATA 1 #define IMPORT_CONST 2 /* Import Name Tyoes for ILF format object files. */ #define IMPORT_ORDINAL 0 #define IMPORT_NAME 1 #define IMPORT_NAME_NOPREFIX 2 #define IMPORT_NAME_UNDECORATE 3 #endif /* _PE_H */ memtest86plus-7.20/boot/sbat.csv000066400000000000000000000002111471441223100166040ustar00rootroot00000000000000sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md memtest86+,1,Memtest86+,6.0,https://github.com/memtest86plus memtest86plus-7.20/boot/setup.S000066400000000000000000000203371471441223100164350ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // setup.S collects the memory map information from the BIOS, disables APM, // enables A20, and performs the switch from real mode to protected mode // before jumping to the main program entry point. // // The memory map information is stored in the 4KB block of memory immediately // following the setup code. The layout of the information matches the Linux // boot_params struct. A pointer to this block is passed to the main program, // for compatiblity with the Linux 32-bit boot protocol. // // Copyright (C) 2020-2021 Martin Whitaker. // // Derived from memtest86+ setup.S and head.S: // // 1-Jan-96 Modified by Chris Brady for use as a boot/loader for memtest-86. #define __ASSEMBLY__ #include "boot.h" #include "build_version.h" #define BOOT_PARAMS_START (SETUP_SECS * 512) #define BOOT_PARAMS_END (BOOT_PARAMS_START + 4096) .section ".setup", "ax", @progbits .code16 # Emulate the Linux boot header, to allow loading by other boot loaders. # Indicate that the main program code should be loaded in high memory. # bootsect.S will fix up the values if we are booted directly from the BIOS. .globl setup setup: jmp do_setup header: .ascii "HdrS" version: .word 0x020c realmode_swtch: .long 0 start_sys_seg: .word 0x1000 kernel_version: .word mt86plus_version-512 type_of_loader: .byte 0 loadflags: .byte 0x1 # LOADED_HIGH setup_move_size: .word 0 .globl code32_start code32_start: .long HIGH_LOAD_ADDR ramdisk_image: .long 0 ramdisk_size: .long 0 bootsect_kludge: .long 0 heap_end_ptr: .word 0 ext_loader_ver: .byte 0 ext_loader_type: .byte 0 cmd_line_ptr: .long 0 initrd_addr_max: .long 0xffffffff kernel_alignment: .long 4096 relocatable_kernel: .byte 0 min_alignment: .byte 12 xload_flags: #ifdef __x86_64__ .word 0x9 # XLF_KERNEL_64,XLF_EFI_HANDOVER_64 #else .word 0x4 # XLF_EFI_HANDOVER_32 #endif cmd_line_size: .long 255 hardware_subarch: .long 0 hardware_subarch_data: .quad 0 payload_offset: .long 0 payload_length: .long 0 setup_data: .quad 0 pref_address: .quad HIGH_LOAD_ADDR init_size: .long _init_size handover_offset: .long 0x10 do_setup: # Reload the segment registers, except for the stack. movw %cs, %ax movw %ax, %ds movw %ax, %es # Get the memory map and disable APM. call get_mem_info call disable_apm # Disable interrupts. cli movb $0x80, %al # disable NMI outb %al, $0x70 # Enable A20. # Try to switch using the fast A20 gate. movw $0x92, %dx inb %dx, %al # Skip if it's unimplemented (read returns 0xff). cmpb $0xff, %al jz 0f orb $0x02, %al # set the ALT_A20_GATE bit andb $0xfe, %al # clear the INIT_NOW bit outb %al, %dx 0: # Use the keyboard controller method anyway. call empty_8042 movb $0xd1, %al # send write command outb %al, $0x64 call empty_8042 movb $0xdf, %al # A20 on outb %al, $0x60 call empty_8042 # Set up a minimal GDT and IDT. xorl %eax, %eax movw %cs, %ax shll $4, %eax addl %eax, gdt_descr - setup + 2 lgdt gdt_descr - setup lidt idt_descr - setup # Load a pointer to the boot_params block into ESI. xorl %esi, %esi movw %cs, %si shll $4, %esi addl $BOOT_PARAMS_START, %esi # Fix up the jump address. movl (code32_start - setup), %eax movl %eax, (jump - setup + 2) # Copy code32_start to the boot_params struct. movl %eax, (BOOT_PARAMS_START + 0x214) # Copy cmd_line_ptr and cmd_line_size to the boot_params struct. movl (cmd_line_ptr - setup), %eax movl %eax, (BOOT_PARAMS_START + 0x228) movl (cmd_line_size - setup), %eax movl %eax, (BOOT_PARAMS_START + 0x238) # Switch to protected mode. movl %cr0, %eax orl $1, %eax movl %eax, %cr0 jmp flush flush: # Reload the segment registers and jump to the main test program. movw $KERNEL_DS, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw %ax, %fs movw %ax, %gs jump: data32 ljmp $KERNEL_CS, $0 # This subroutine queries the BIOS to determine the system memory map # and stores the results in the boot_params structure that we pass to # the startup code. #define SMAP 0x534d4150 get_mem_info: push %ds push %es # Set DS and ES to point to the start of the boot_params structure. movw %ds, %ax addw $(BOOT_PARAMS_START >> 4), %ax movw %ax, %ds movw %ax, %es # Zero the entire boot_params structure. movw $0x0000, %di movw $0x0400, %cx xorl %eax, %eax cld rep stosl # First try method E820. E820 returns memory classified into a whole # bunch of different types, and allows memory holes and everything. mem_e820: movw $E820_MAP, %di # destination pointer xorl %ebx, %ebx # continuation counter loop_e820: movl $0x0000e820, %eax # e820, upper word zeroed movl $SMAP, %edx # ASCII 'SMAP' movl $20, %ecx # size of the e820 record int $0x15 # make the call jc done_e820 # bail out if it fails cmpl $SMAP, %eax # check the return is 'SMAP' jne done_e820 # bail out if it fails incb (E820_ENTRIES) addw $E820_ENTRY_SIZE, %di movb (E820_ENTRIES), %al # check for table full cmpb $E820_MAP_SIZE, %al je done_e820 cmpl $0, %ebx # any more entries? jne loop_e820 done_e820: cmpb $0, (E820_ENTRIES) jnz get_mem_done # Next try method E801. mem_e801: stc # Fix to work around buggy BIOSs xorw %cx,%cx # which don't clear/set carry on xorw %dx,%dx # pass/error of e801h memory size # call or merely pass cx,dx through # without changing them. movw $0xe801, %ax int $0x15 jc mem_88 cmpw $0x0, %cx # Kludge to handle BIOSes which jne 0f # report their extended memory in cmpw $0x0, %dx # AX/BX rather than CX/DX. The spec jne 0f # I have read seems to indicate that movw %ax, %cx # AX/BX are more reasonable anyway. movw %bx, %dx 0: jmp fake_e820 # Finally try method 88. mem_88: movb $0x88, %ah int $0x15 movw %ax, %cx movw $0, %dx fake_e820: # Write entry for memory below 1MB. movl $0x0, E820_ADDR(%di) movl $0xa0000, E820_SIZE(%di) movl $1, E820_TYPE(%di) incb (E820_ENTRIES) addw $E820_ENTRY_SIZE, %di # Write entry for memory between 1MB and 16MB. andl $0xffff, %ecx # convert to 32-bits jz 0f shll $10, %ecx # convert to bytes movl $0x100000, E820_ADDR(%di) movl %ecx, E820_SIZE(%di) movl $1, E820_TYPE(%di) incb (E820_ENTRIES) addw $E820_ENTRY_SIZE, %di 0: # Write entry for memory above 16MB. andl $0xffff, %edx # convert to 32-bits jz 1f shll $16, %edx # convert to bytes movl $0x1000000, E820_ADDR(%di) movl %edx, E820_SIZE(%di) movl $1, E820_TYPE(%di) incb (E820_ENTRIES) addw $E820_ENTRY_SIZE, %di 1: get_mem_done: pop %es pop %ds ret # This subroutine disables APM if it is present. disable_apm: movw $0x5300, %ax # APM BIOS installation check xorw %bx, %bx int $0x15 jc disable_apm_done # error -> no APM BIOS cmpw $0x504d, %bx # check for "PM" signature jne disable_apm_done # no signature -> no APM BIOS movw $0x5304, %ax # Disconnect first just in case xorw %bx, %bx int $0x15 # ignore return code movw $0x5301, %ax # Real Mode connect xorw %bx, %bx int $0x15 jc disable_apm_done # error movw $0x5308, %ax # Disable APM mov $0xffff, %bx xorw %cx, %cx int $0x15 disable_apm_done: ret # This subroutine checks that the keyboard command queue is empty (after # emptying the output buffers). No timeout is used - if this hangs there # is something wrong with the machine, and we probably couldn't proceed # anyway. empty_8042: call delay inb $0x64, %al # 8042 status port cmpb $0xff, %al # skip if not implemented jz empty_8042_ret testb $1, %al # anything in the output buffer? jz no_output call delay inb $0x60, %al # read it jmp empty_8042 no_output: testb $2, %al # is input buffer full? jnz empty_8042 # yes - loop empty_8042_ret: ret # This subroutine provides a short delay. delay: .word 0x00eb # jmp $+2 ret # A minimal GDT and IDT. .align 4 gdt: .quad 0x0000000000000000 # NULL descriptor .quad 0x0000000000000000 # not used .quad 0x00c09a0000007fff # 128MB 32-bit code at 0x000000 .quad 0x00c0920000007fff # 128MB 32-bit code at 0x000000 gdt_end: .word 0 # for alignment gdt_descr: .word gdt_end - gdt - 1 # gdt limit .long gdt - setup # gdt base - relocated at run time .word 0 # for alignment idt_descr: .word 0 # idt limit=0 .long 0 # idt base=0 mt86plus_version: .ascii "Memtest86+ v" , MT_VERSION .byte 0 # Pad to the declared size. .org (SETUP_SECS*512) memtest86plus-7.20/boot/startup32.S000066400000000000000000000402551471441223100171450ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // startup32.S contains the 32-bit startup code for both the BSP and APs. // It initialises stacks, memory management, and exception handling, clears // the BSS, completes relocation, and finally calls the main application. // It supports the 32-bit Linux boot protocol and EFI boot for the first // boot of the BSP. // // Copyright (C) 2020-2022 Martin Whitaker. // // Derived from memtest86+ head.S: // // linux/boot/head.S // Copyright (C) 1991, 1992 Linus Torvalds // 1-Jan-96 Modified by Chris Brady for use as a boot/loader for MemTest-86. // Set up the memory management for flat non-paged linear addressing. // 17 May 2004 : Added X86_PWRCAP for AMD64 (Memtest86+ - Samuel D.) #define __ASSEMBLY__ #include "boot.h" #define INT64_CS 0x08 #define NUM_INT_VEC 20 .text .code32 # The Linux 32-bit boot entry point. .globl startup32 startup32: cld cli # Jump to the shared 32-bit entry point with the boot params pointer # in %esi and the startup address in %edi. movl 0x214(%esi), %ebx # bootparams.code32_start leal (startup - startup32)(%ebx), %edi jmp startup # The Linux 32-bit EFI handover point. .org 0x10 .globl efi_handover efi_handover: popl %eax # the return address (discard) popl %ecx # the EFI image handle popl %edx # the EFI system table pointer popl %esi # the boot params pointer # Load the GOT pointer. call 0f 0: popl %ebx addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx # Fill out the boot params structure. subl $12, %esp # align the stack andl $~0xf, %esp addl $12, %esp pushl %esi # the boot params pointer pushl %edx # the EFI system table pointer pushl %ecx # the EFI image handle call efi_setup # Fall through to the shared 32-bit entry point with the boot params # pointer in %esi and the startup address in %edi. movl %eax, %esi movl 0x214(%esi), %ebx # bootparams.code32_start leal (startup - startup32)(%ebx), %edi # The 32-bit entry point for AP boot and for restart after relocation. .globl startup startup: # Some of the startup actions are not thread safe. Use a mutex # to protect this section of code. leal (startup_mutex - startup)(%edi), %eax 0: lock btsl $0, (%eax) jc 0b # Use the startup stack until we pick the correct one. leal (startup_stack_top - startup)(%edi), %esp # Load the GOT pointer. call 0f 0: popl %ebx addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx # If first boot, save the boot params pointer... cmpl $1, first_boot@GOTOFF(%ebx) jne 1f movl %esi, boot_params_addr@GOTOFF(%ebx) # ...and check if the processor supports long mode. movl $0x80000000, %eax # check if function 0x80000001 is available pushl %ebx # ebx is overwritten by cpuid cpuid popl %ebx # restore ebx cmpl $0x80000001, %eax jb 1f movl $0x80000001, %eax # test the LM flag pushl %ebx # ebx is overwritten by cpuid cpuid popl %ebx # restore ebx andl $0x20000000, %edx jz 1f movl $1, use_long_mode@GOTOFF(%ebx) 1: # Pick the correct stack. call smp_my_cpu_num movl $AP_STACK_SIZE, %edx mul %edx addl $(BSP_STACK_SIZE - LOCALS_SIZE), %eax leal _stacks@GOTOFF(%ebx), %esp addl %eax, %esp # Initialise the GDT descriptor. leal gdt@GOTOFF(%ebx), %eax movl %eax, 2 + gdt_descr@GOTOFF(%ebx) # Load the GDT and the segment registers. lgdt gdt_descr@GOTOFF(%ebx) leal flush@GOTOFF(%ebx), %eax movw $KERNEL_CS, -2(%esp) movl %eax, -6(%esp) ljmp *-6(%esp) flush: movw $KERNEL_DS, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss # Initialise the IDT. If we are going to operate in long mode, we need # a 64-bit IDT, otherwise we need a 32-bit IDT. leal idt@GOTOFF(%ebx), %edi cmpl $1, use_long_mode@GOTOFF(%ebx) je init_idt64 jmp init_idt32 # Initialise the IDT descriptor. init_idt_descr: movw %ax, idt_descr@GOTOFF(%ebx) leal idt@GOTOFF(%ebx), %eax movl %eax, 2 + idt_descr@GOTOFF(%ebx) # Load the IDT. lidt idt_descr@GOTOFF(%ebx) # Zero the BSS (if first boot). cmpl $1, first_boot@GOTOFF(%ebx) jne 1f xorl %eax, %eax leal _bss@GOTOFF(%ebx), %edi leal _end@GOTOFF(%ebx), %ecx subl %edi, %ecx 0: movl %eax, (%edi) addl $4, %edi subl $4, %ecx jnz 0b movl $0, first_boot@GOTOFF(%ebx) 1: # Initialise the FPU. finit # Call the dynamic linker to fix up the addresses in the GOT. call reloc # Disable paging (needed during restart). Also disable write protect # (in case set by EFI boot). movl %cr0, %eax andl $0x7ffeffff, %eax movl %eax, %cr0 # Enable PAE if supported. pushl %ebx # ebx is overwritten by cpuid movl $0x00000001, %eax # test the PAE flag cpuid andl $0x00000040, %edx popl %ebx # restore ebx jz 1f # bail if not supported movl %cr4, %eax # enable PAE orl $0x00000020, %eax movl %eax, %cr4 leal pdp@GOTOFF(%ebx), %eax # set the page directory base address movl %eax, %cr3 # Enable long mode if supported. cmpl $1, use_long_mode@GOTOFF(%ebx) jne 0f movl $0xc0000080, %ecx # enable long mode rdmsr orl $0x00000100, %eax wrmsr leal pml4@GOTOFF(%ebx), %eax # set the page directory base address movl %eax, %cr3 # Enable paging and protection. 0: movl %cr0, %eax orl $0x80000001, %eax movl %eax, %cr0 1: # Release the startup mutex. movl $0, startup_mutex@GOTOFF(%ebx) # Run the application. call main # In case we return, simulate an exception. pushfl pushl %cs call 0f 0: pushl $0 # error code pushl $257 # vector jmp int_handler32 # The EFI PE32 boot entry point. .org 0x1e0 .globl efi_boot efi_boot: popl %eax # the return address (discard) popl %ecx # the EFI image handle popl %edx # the EFI system table pointer pushl $0 # the boot params pointer (0 = not yet allocated) pushl %edx # the EFI system table pointer pushl %ecx # the EFI image handle call efi_handover # never returns # Initialise the 64-bit IDT. init_idt64: leal vec64_0@GOTOFF(%ebx), %esi movw $NUM_INT_VEC, %cx 0: movl %esi, %edx movl $(INT64_CS << 16), %eax movw %dx, %ax # selector = 0x0008 = long mode cs movw $0x8E00, %dx # interrupt gate - dpl=0, present movl %eax, (%edi) movl %edx, 4(%edi) addl $(vec64_1-vec64_0), %esi addl $16, %edi dec %cx jnz 0b movw $(NUM_INT_VEC*16 - 1), %ax jmp init_idt_descr # Initialise the 32-bit IDT. init_idt32: leal vec32_0@GOTOFF(%ebx), %esi movw $NUM_INT_VEC, %cx 0: movl %esi, %edx movl $(KERNEL_CS << 16), %eax movw %dx, %ax # selector = 0x0010 = cs movw $0x8E00, %dx # interrupt gate - dpl=0, present movl %eax, (%edi) movl %edx, 4(%edi) addl $(vec32_1-vec32_0), %esi addl $8, %edi dec %cx jnz 0b movw $(NUM_INT_VEC*8 - 1), %ax jmp init_idt_descr # Individual interrupt vector handlers for long mode. These need to be # spaced equally, to allow the IDT initialisation loop above to work, # so we use noops to pad out where required. .code64 vec64_0: pushq $0 # error code pushq $0 # vector jmp int_handler64 vec64_1: pushq $0 # error code pushq $1 # vector jmp int_handler64 vec64_2: pushq $0 # error code pushq $2 # vector jmp int_handler64 vec64_3: pushq $0 # error code pushq $3 # vector jmp int_handler64 vec64_4: pushq $0 # error code pushq $4 # vector jmp int_handler64 vec64_5: pushq $0 # error code pushq $5 # vector jmp int_handler64 vec64_6: pushq $0 # error code pushq $6 # vector jmp int_handler64 vec64_7: pushq $0 # error code pushq $7 # vector jmp int_handler64 vec64_8: nop;nop # error code already provided pushq $8 # vector jmp int_handler64 vec64_9: pushq $0 # error code pushq $9 # vector jmp int_handler64 vec64_10: nop;nop # error code already provided pushq $10 # vector jmp int_handler64 vec64_11: nop;nop # error code already provided pushq $11 # vector jmp int_handler64 vec64_12: nop;nop # error code already provided pushq $12 # vector jmp int_handler64 vec64_13: nop;nop # error code already provided pushq $13 # vector jmp int_handler64 vec64_14: nop;nop # error code already provided pushq $14 # vector jmp int_handler64 vec64_15: pushq $0 # error code pushq $15 # vector jmp int_handler64 vec64_16: pushq $0 # error code pushq $16 # vector jmp int_handler64 vec64_17: nop;nop # error code pushq $17 # vector jmp int_handler64 vec64_18: pushq $0 # error code pushq $18 # vector jmp int_handler64 vec64_19: pushq $0 # error code pushq $19 # vector jmp int_handler64 # The interrupt handler code for long mode. Pass the register state to the # common interrupt handler. On entry this expects the stack to contain: # # rsp+30 ss # rsp+28 rsp # rsp+20 rflags # rsp+18 cs # rsp+10 rip # rsp+08 error code # rsp+00 vector number # # We create a new stack frame in the format expected by int_handler. We can # reuse the space currently occupied by the vector number and the error code, # as they are not needed on return. int_handler64: subq $16, %rsp movl %ebp, 0x04(%rsp) # save the state of ebp leal 0x48(%rsp), %ebp # save the state of esp before the interrupt movl %ebp, 0x00(%rsp) movl 0x10(%rsp), %ebp # save the vector number movl %ebp, 0x08(%rsp) movl 0x18(%rsp), %ebp # save the error code movl %ebp, 0x0c(%rsp) movl 0x20(%rsp), %ebp # save the state of eip movl %ebp, 0x10(%rsp) movl 0x28(%rsp), %ebp # save the state of cs movl %ebp, 0x14(%rsp) movl 0x30(%rsp), %ebp # save the state of eflags movl %ebp, 0x18(%rsp) leal int_handler(%rip), %ebp movw $KERNEL_CS, -2(%rsp) movl %ebp, -6(%rsp) lcall *-6(%rsp) movl 0x04(%rsp), %ebp # restore the saved state of ebp addq $32, %rsp # discard the stack frame we created iretq # Individual interrupt vector handlers for protected mode. These need to be # spaced equally, to allow the IDT initialisation loop above to work, so we # use noops to pad out where required. .code32 vec32_0: pushl $0 # error code pushl $0 # vector jmp int_handler32 vec32_1: pushl $0 # error code pushl $1 # vector jmp int_handler32 vec32_2: pushl $0 # error code pushl $2 # vector jmp int_handler32 vec32_3: pushl $0 # error code pushl $3 # vector jmp int_handler32 vec32_4: pushl $0 # error code pushl $4 # vector jmp int_handler32 vec32_5: pushl $0 # error code pushl $5 # vector jmp int_handler32 vec32_6: pushl $0 # error code pushl $6 # vector jmp int_handler32 vec32_7: pushl $0 # error code pushl $7 # vector jmp int_handler32 vec32_8: nop;nop # error code already provided pushl $8 # vector jmp int_handler32 vec32_9: pushl $0 # error code pushl $9 # vector jmp int_handler32 vec32_10: nop;nop # error code already provided pushl $10 # vector jmp int_handler32 vec32_11: nop;nop # error code already provided pushl $11 # vector jmp int_handler32 vec32_12: nop;nop # error code already provided pushl $12 # vector jmp int_handler32 vec32_13: nop;nop # error code already provided pushl $13 # vector jmp int_handler32 vec32_14: nop;nop # error code already provided pushl $14 # vector jmp int_handler32 vec32_15: pushl $0 # error code pushl $15 # vector jmp int_handler32 vec32_16: pushl $0 # error code pushl $16 # vector jmp int_handler32 vec32_17: nop;nop # error code pushl $17 # vector jmp int_handler32 vec32_18: pushl $0 # error code pushl $18 # vector jmp int_handler32 vec32_19: pushl $0 # error code pushl $19 # vector jmp int_handler32 # The interrupt handler code for protected mode. Pass the register state to # the common interrupt handler. On entry this expects the stack to contain: # # esp+10 eflags # esp+0c cs # esp+08 eip # esp+04 error code # esp+00 vector number # # It adds the additional state expected by int_handler to the bottom of the # stack frame. int_handler32: pushl %ebp # save the state of ebp leal 24(%esp), %ebp # save the state of esp before the interrupt pushl %ebp leal int_handler@GOTOFF(%ebx), %ebp movw $KERNEL_CS, -2(%esp) movl %ebp, -6(%esp) lcall *-6(%esp) popl %ebp # discard the saved state of esp popl %ebp # restore the saved state of ebp addl $8, %esp # discard the vector number and error code iret # The common interrupt handler code. Pass the register state to the application # interrupt handler. On entry this expects the stack to contain: # # esp+18 eflags # esp+14 cs # esp+10 eip # esp+0c error code # esp+08 vector number # esp+04 ebp # esp+00 esp # # It adds the additional state expected by the application to the bottom of the # stack frame. int_handler: pushl %esi pushl %edi pushl %edx pushl %ecx pushl %ebx pushl %eax pushl %ss pushl %es pushl %ds pushl %esp # pointer to trap regs struct on the stack cld call interrupt addl $16, %esp popl %eax popl %ebx popl %ecx popl %edx popl %edi popl %esi lret # The interrupt descriptor table, used for both long mode and protected mode. .align 4 .word 0 # for alignment .globl idt_descr idt_descr: .word 0 # size: filled in at run time .quad 0 # addr: filled in at run time .align 8 .globl idt idt: .fill 2*NUM_INT_VEC, 8, 0 # filled in at run time idt_end: # The global descriptor table. .align 4 .word 0 # for alignment gdt_descr: .word gdt_end - gdt - 1 # size .long 0 # addr: filled in at run time .align 4 .globl gdt gdt: .quad 0x0000000000000000 # NULL descriptor .quad 0x00209a0000000000 # 0x08 64-bit code at 0x000000 .quad 0x00cf9a000000ffff # 0x10 main 4gb code at 0x000000 .quad 0x00cf92000000ffff # 0x18 main 4gb data at 0x000000 .globl gdt_end gdt_end: .data .macro ptes64 start, count=64 .quad \start + 0x0000000 + 0x83 .quad \start + 0x0200000 + 0x83 .quad \start + 0x0400000 + 0x83 .quad \start + 0x0600000 + 0x83 .quad \start + 0x0800000 + 0x83 .quad \start + 0x0A00000 + 0x83 .quad \start + 0x0C00000 + 0x83 .quad \start + 0x0E00000 + 0x83 .if \count-1 ptes64 "(\start+0x01000000)",\count-1 .endif .endm .macro maxdepth depth=1 .if \depth-1 maxdepth \depth-1 .endif .endm maxdepth # The long mode level 4 page map table. .align 4096 .globl pml4 pml4: .long pdp + 0x3 # relocated at run time .long 0 # Page Directory Pointer Table: # 4 Entries, pointing to the Page Directory Tables. .align 4096 .globl pdp pdp: .long pd0 + 0x1 # relocated at run time .long 0 .long pd1 + 0x1 # relocated at run time .long 0 .long pd2 + 0x1 # relocated at run time .long 0 .long pd3 + 0x1 # relocated at run time .long 0 # Page Directory Tables: # There are 4 tables. The first two map the first 2 GB of memory. The third # is used with PAE to map the rest of memory in 1 GB segments. The fourth is # reserved for mapping the video frame buffer. We use 2 MB pages so only the # Page Directory Table is used (no page tables). .align 4096 .globl pd0 pd0: ptes64 0x0000000000000000 .align 4096 .globl pd1 pd1: ptes64 0x0000000040000000 .align 4096 .globl pd2 pd2: ptes64 0x0000000080000000 .align 4096 .globl pd3 pd3: ptes64 0x00000000C0000000 .previous # ap_trampoline is the entry point for CPUs other than the bootstrap # CPU (BSP). It gets copied to a page in low memory, to enable the APs # to boot when the main program has been loaded in high memory. .code16 .align 4 .globl ap_trampoline ap_trampoline: movw %cs, %ax movw %ax, %ds # Load the startup address and use it to patch the jump address. movl (ap_startup_addr - ap_trampoline), %edi movl %edi, (ap_jump - ap_trampoline + 2) # Patch and load the GDT descriptor. It should point to the main # GDT descriptor, which has already been initialised by the BSP. movl %edi, %eax addl $(gdt - startup), %eax movl %eax, (ap_gdt_descr - ap_trampoline + 2) data32 lgdt ap_gdt_descr - ap_trampoline # Switch to protected mode and reload the segment registers. movl %cr0, %eax orl $1, %eax movl %eax, %cr0 jmp ap_flush ap_flush: movw $KERNEL_DS, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss # Jump to the main entry point with the startup address in %edi. ap_jump: data32 ljmp $KERNEL_CS, $0 .align 4 .word 0 # for alignment ap_gdt_descr: .word gdt_end - gdt - 1 # gdt limit .long 0 # gdt base - filled in at run time .globl ap_startup_addr ap_startup_addr: .long 0 # filled in at run time .globl ap_trampoline_end ap_trampoline_end: .previous # Variables. .data .align 4 .globl boot_params_addr boot_params_addr: .long 0 startup_mutex: .long 0 first_boot: .long 1 use_long_mode: .long 0 .previous # Startup stack. .bss .align 16 startup_stack_base: . = . + 64 startup_stack_top: .previous # Main stack area. .section ".stacks", "aw", @nobits .align 16 . = . + STACKS_SIZE .previous memtest86plus-7.20/boot/startup64.S000066400000000000000000000267571471441223100171650ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // // startup64.S contains the 64-bit startup code for both the BSP and APs. // It initialises stacks, memory management, and exception handling, clears // the BSS, completes relocation, and finally calls the main application. // It supports both the 32-bit and 64-bit Linux boot protocols and EFI boot // for the first boot of the BSP. // // Copyright (C) 2020-2024 Martin Whitaker. // // Derived from memtest86+ head.S: // // linux/boot/head.S // Copyright (C) 1991, 1992 Linus Torvalds // 1-Jan-96 Modified by Chris Brady for use as a boot/loader for MemTest-86. // Set up the memory management for flat non-paged linear addressing. // 17 May 2004 : Added X86_PWRCAP for AMD64 (Memtest86+ - Samuel D.) #define __ASSEMBLY__ #include "boot.h" #define NUM_INT_VEC 20 .text .code32 # The Linux 32-bit boot entry point. .globl startup32 startup32: cld cli # Get the load address. movl 0x214(%esi), %ebx # bootparams.code32_start # Save the boot params pointer. movl %esi, (boot_params_addr - startup32)(%ebx) # Use the startup stack until we pick the correct one. leal (startup_stack_top - startup32)(%ebx), %esp # Initialise the pml4 and pdp tables. leal (pml4 - startup32)(%ebx), %ecx leal (pdp - startup32)(%ebx), %edx movl %edx, %eax addl $0x3, %eax movl %eax, 0(%ecx) leal (pd0 - startup32)(%ebx), %eax addl $0x3, %eax movl %eax, 0(%edx) leal (pd1 - startup32)(%ebx), %eax addl $0x3, %eax movl %eax, 8(%edx) leal (pd2 - startup32)(%ebx), %eax addl $0x3, %eax movl %eax, 16(%edx) leal (pd3 - startup32)(%ebx), %eax addl $0x3, %eax movl %eax, 24(%edx) # Set the page directory base address. movl %ecx, %cr3 # Enable PAE. movl %cr4, %eax orl $0x20, %eax movl %eax, %cr4 # Enable long mode. movl $0xc0000080, %ecx rdmsr orl $0x00000100, %eax wrmsr # Enable paging and protection. movl %cr0, %eax orl $0x80000001, %eax movl %eax, %cr0 # Initialise the 64-bit GDT descriptor. leal (gdt - startup32)(%ebx), %eax movl %eax, 2 + (gdt_descr - startup32)(%ebx) # Load the GDT and enter long mode. lgdt (gdt_descr - startup32)(%ebx) leal (startup - startup32)(%ebx), %eax movw $KERNEL_CS, -2(%esp) movl %eax, -6(%esp) ljmp *-6(%esp) .code64 # The EFI PE32+ boot entry point. .org 0x1e0 .globl efi_boot efi_boot: movq %rcx, %rdi # the EFI image handle movq %rdx, %rsi # the EFI system table pointer movq $0, %rdx # the boot params pointer (0 = not yet allocated) jmp efi_handover # The Linux 64-bit boot entry point. .org 0x200 .globl startup64 startup64: cld cli # Save the boot params pointer. movq %rsi, boot_params_addr(%rip) jmp startup # The Linux 64-bit EFI handover point. .org 0x210 .globl efi_handover efi_handover: andq $~0xf, %rsp call efi_setup # Save the boot params pointer. movq %rax, boot_params_addr(%rip) # The 64-bit entry point for AP boot and for restart after relocation. .globl startup startup: # Some of the startup actions are not thread safe. Use a mutex # to protect this section of code. 0: lock btsl $0, startup_mutex(%rip) jc 0b # Use the startup stack until we pick the correct one. leaq startup_stack_top(%rip), %rsp # Pick the correct stack. xorq %rax, %rax call smp_my_cpu_num movl $AP_STACK_SIZE, %edx mul %edx addq $(BSP_STACK_SIZE - LOCALS_SIZE), %rax leaq _stacks(%rip), %rsp addq %rax, %rsp # Initialise the pml4 and pdp tables. leaq pml4(%rip), %rcx leaq pdp(%rip), %rdx movq %rdx, %rax addq $0x3, %rax movq %rax, 0(%rcx) leaq pd0(%rip), %rax addq $0x3, %rax movq %rax, 0(%rdx) leaq pd1(%rip), %rax addq $0x3, %rax movq %rax, 8(%rdx) leaq pd2(%rip), %rax addq $0x3, %rax movq %rax, 16(%rdx) leaq pd3(%rip), %rax addq $0x3, %rax movq %rax, 24(%rdx) # Set the page directory base address. movq %rcx, %cr3 # Initialise the GDT descriptor. leaq gdt(%rip), %rax movq %rax, 2 + gdt_descr(%rip) # Load the GDT and the segment registers. lgdt gdt_descr(%rip) leaq flush(%rip), %rax movw $KERNEL_CS, -2(%rsp) movl %eax, -6(%rsp) ljmp *-6(%rsp) flush: movw $KERNEL_DS, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss # Initialise the IDT. leaq idt(%rip), %rdi leaq vec0(%rip), %rsi movw $NUM_INT_VEC, %cx 0: movq %rsi, %rdx movl $(KERNEL_CS << 16), %eax movw %dx, %ax # selector = 0x0010 = cs movw $0x8E00, %dx # interrupt gate - dpl=0, present movl %eax, (%rdi) movl %edx, 4(%rdi) shrq $32, %rdx movl %edx, 8(%rdi) movl $0, 12(%rdi) addq $(vec1-vec0), %rsi addq $16, %rdi dec %cx jnz 0b # Initialise the IDT descriptor. leaq idt(%rip), %rax movq %rax, 2 + idt_descr(%rip) # Load the IDT. lidt idt_descr(%rip) # Zero the BSS (if first boot). cmpl $1, first_boot(%rip) jne 1f xorq %rax, %rax leaq _bss(%rip), %rdi leaq _end(%rip), %rcx subq %rdi, %rcx 0: movq %rax, (%rdi) addq $8, %rdi subq $8, %rcx jnz 0b movl $0, first_boot(%rip) 1: # Initialise the FPU. finit #if 0 # Enable SSE. movq %cr0, %rax andw $0xfffb, %ax # clear coprocessor emulation bit orw $0x0002, %ax # set coprocessor monitoring bit mov %rax, %cr0 movq %cr4, %rax orw $0x0600, %ax # set OSFXSR and OSXMMEXCPT movq %rax, %cr4 #endif # Call the dynamic linker to fix up the addresses in the GOT. call reloc # Release the startup mutex. movl $0, startup_mutex(%rip) # Run the application. call main # In case we return, simulate an exception. pushfq xorq %rax, %rax movw %cs, %ax pushq %rax call 0f 0: pushq $0 # error code pushq $257 # vector jmp int_handler # Individual interrupt vector handlers. These need to be spaced equally, to # allow the IDT initialisation loop above to work, so we use noops to pad out # where required. vec0: pushq $0 # error code pushq $0 # vector jmp int_handler vec1: pushq $0 # error code pushq $1 # vector jmp int_handler vec2: pushq $0 # error code pushq $2 # vector jmp int_handler vec3: pushq $0 # error code pushq $3 # vector jmp int_handler vec4: pushq $0 # error code pushq $4 # vector jmp int_handler vec5: pushq $0 # error code pushq $5 # vector jmp int_handler vec6: pushq $0 # error code pushq $6 # vector jmp int_handler vec7: pushq $0 # error code pushq $7 # vector jmp int_handler vec8: nop;nop # error code already provided pushq $8 # vector jmp int_handler vec9: pushq $0 # error code pushq $9 # vector jmp int_handler vec10: nop;nop # error code already provided pushq $10 # vector jmp int_handler vec11: nop;nop # error code already provided pushq $11 # vector jmp int_handler vec12: nop;nop # error code already provided pushq $12 # vector jmp int_handler vec13: nop;nop # error code already provided pushq $13 # vector jmp int_handler vec14: nop;nop # error code already provided pushq $14 # vector jmp int_handler vec15: pushq $0 # error code pushq $15 # vector jmp int_handler vec16: pushq $0 # error code pushq $16 # vector jmp int_handler vec17: nop;nop # error code pushq $17 # vector jmp int_handler vec18: pushq $0 # error code pushq $18 # vector jmp int_handler vec19: pushq $0 # error code pushq $19 # vector jmp int_handler # The common interrupt handler code. Pass the register state to the application # interrupt handler. On entry this expects the stack to contain: # # rsp+30 ss # rsp+28 rsp # rsp+20 rflags # rsp+18 cs # rsp+10 rip # rsp+08 error code # rsp+00 vector number # # It adds the additional state expected by the application to the bottom of the # stack frame. int_handler: pushq %rbp pushq %r11 pushq %r10 pushq %r9 pushq %r8 pushq %rsi pushq %rdi pushq %rdx pushq %rcx pushq %rbx pushq %rax xorq %rax, %rax movw %ss, %ax pushq %rax movw %es, %ax pushq %rax movw %ds, %ax pushq %rax movq %rsp, %rdi # pointer to trap regs struct on the stack cld call interrupt addq $24, %rsp popq %rax popq %rbx popq %rcx popq %rdx popq %rdi popq %rsi popq %r8 popq %r9 popq %r10 popq %r11 popq %rbp addq $16, %rsp # discard the vector number and error code iretq # The interrupt descriptor table. .align 4 .word 0 # for alignment idt_descr: .word idt_end - idt - 1 # size .quad 0 # addr: filled in at run time idt: .fill NUM_INT_VEC*2, 8, 0 # filled in at run time idt_end: # The global descriptor table. .word 0 # for alignment gdt_descr: .word gdt_end - gdt - 1 # size .quad 0 # addr: filled in at run time .align 4 .globl gdt gdt: .quad 0x0000000000000000 # NULL descriptor .quad 0x0000000000000000 # not used .quad 0x00209a0000000000 # 0x10 64-bit code at 0x000000 .quad 0x0000920000000000 # 0x18 64-bit data at 0x000000 .globl gdt_end gdt_end: .data .macro ptes64 start, count=64 .quad \start + 0x0000000 + 0x83 .quad \start + 0x0200000 + 0x83 .quad \start + 0x0400000 + 0x83 .quad \start + 0x0600000 + 0x83 .quad \start + 0x0800000 + 0x83 .quad \start + 0x0A00000 + 0x83 .quad \start + 0x0C00000 + 0x83 .quad \start + 0x0E00000 + 0x83 .if \count-1 ptes64 "(\start+0x01000000)",\count-1 .endif .endm .macro maxdepth depth=1 .if \depth-1 maxdepth \depth-1 .endif .endm maxdepth # The level 4 page map table. .align 4096 .globl pml4 pml4: .quad 0 # filled in at run time # Page Directory Pointer Table: # 4 Entries, pointing to the Page Directory Tables. .align 4096 .globl pdp pdp: .quad 0 # filled in at run time .quad 0 # filled in at run time .quad 0 # filled in at run time .quad 0 # filled in at run time # Page Directory Tables: # There are 4 tables. The first two map the first 2 GB of memory. The third # is used with PAE to map the rest of memory in 1 GB segments. The fourth is # reserved for mapping the video frame buffer. We use 2 MB pages so only the # Page Directory Table is used (no page tables). .align 4096 .globl pd0 pd0: ptes64 0x0000000000000000 .align 4096 .globl pd1 pd1: ptes64 0x0000000040000000 .align 4096 .globl pd2 pd2: ptes64 0x0000000080000000 .align 4096 .globl pd3 pd3: ptes64 0x00000000C0000000 .previous # ap_trampoline is the entry point for CPUs other than the bootstrap # CPU (BSP). It gets copied to a page in low memory, to enable the APs # to boot when the main program has been loaded in high memory. .code16 .align 4 .globl ap_trampoline ap_trampoline: movw %cs, %ax movw %ax, %ds # Patch the jump address. movl (ap_startup_addr - ap_trampoline), %ebx movl %ebx, (ap_jump - ap_trampoline + 2) # Patch and load the GDT descriptor. It should point to the main # GDT descriptor, which has already been initialised by the BSP. movl %ebx, %eax addl $(gdt - startup), %eax movl %eax, (ap_gdt_descr - ap_trampoline + 2) data32 lgdt ap_gdt_descr - ap_trampoline # Set the page directory base address. movl %ebx, %eax addl $(pml4 - startup), %eax movl %eax, %cr3 # Enable PAE. movl %cr4, %eax orl $0x20, %eax movl %eax, %cr4 # Enable long mode. movl $0xc0000080, %ecx rdmsr orl $0x00000100, %eax wrmsr # Enable paging and protection. movl %cr0, %eax orl $0x80000001, %eax movl %eax, %cr0 # Jump to the 64-bit entry point. ap_jump: data32 ljmp $KERNEL_CS, $0 .align 4 .word 0 # for alignment ap_gdt_descr: .word gdt_end - gdt - 1 # gdt limit .long 0 # gdt base - filled in at run time .globl ap_startup_addr ap_startup_addr: .long 0 # filled in at run time .globl ap_trampoline_end ap_trampoline_end: .previous # Variables. .data .align 4 .globl boot_params_addr boot_params_addr: .quad 0 startup_mutex: .long 0 first_boot: .long 1 .previous # Startup stack. .bss .align 16 startup_stack_base: . = . + 64 startup_stack_top: .previous # Main stack area. .section ".stacks", "aw", @nobits .align 16 . = . + STACKS_SIZE .previous memtest86plus-7.20/build32/000077500000000000000000000000001471441223100154455ustar00rootroot00000000000000memtest86plus-7.20/build32/Makefile000066400000000000000000000221711471441223100171100ustar00rootroot00000000000000AS = as -32 CC = gcc OBJCOPY = objcopy GIT = git ifeq ($(GIT),none) GIT_AVAILABLE = false else GIT_AVAILABLE = true endif CFLAGS = -std=gnu11 -Wall -Wextra -Wshadow -m32 -march=i586 -fpic -fno-builtin \ -ffreestanding -fomit-frame-pointer -fno-stack-protector \ -fexcess-precision=standard -DARCH_BITS=32 ifeq ($(DEBUG), 1) CFLAGS+=-ggdb3 -DDEBUG_GDB OPT_SMALL=-Og OPT_FAST=-Og MS_LDS=memtest_shared_debug.lds else OPT_SMALL=-Os OPT_FAST=-O3 MS_LDS=ldscripts/memtest_shared.lds endif INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app -Iapp SYS_OBJS = system/acpi.o \ system/cpuid.o \ system/cpuinfo.o \ system/cpulocal.o \ system/ehci.o \ system/font.o \ system/heap.o \ system/hwctrl.o \ system/hwquirks.o \ system/keyboard.o \ system/ohci.o \ system/memctrl.o \ system/pci.o \ system/pmem.o \ system/reloc.o \ system/screen.o \ system/serial.o \ system/smbios.o \ system/i2c_x86.o \ system/spd.o \ system/smp.o \ system/temperature.o \ system/timers.o \ system/uhci.o \ system/usbhcd.o \ system/vmem.o \ system/xhci.o IMC_SRCS = $(wildcard ../system/imc/*.c) IMC_OBJS = $(subst ../,,$(IMC_SRCS:.c=.o)) LIB_OBJS = lib/barrier.o \ lib/div64.o \ lib/print.o \ lib/read.o \ lib/string.o \ lib/unistd.o TST_OBJS = tests/addr_walk1.o \ tests/bit_fade.o \ tests/block_move.o \ tests/modulo_n.o \ tests/mov_inv_fixed.o \ tests/mov_inv_random.o \ tests/mov_inv_walk1.o \ tests/own_addr.o \ tests/test_helper.o \ tests/tests.o APP_OBJS = app/badram.o \ app/config.o \ app/display.o \ app/error.o \ app/interrupt.o \ app/main.o OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin memtest.efi check: @if [ -z ${DEBUG} ]; then\ echo "Macro DEBUG is not defined. Run debug_memtest.sh to invoke debug target";\ exit 1;\ fi debug: check memtest.debug memtest.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(IMC_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) boot/header.o : | ../boot/sbat.csv boot/startup.o: ../boot/startup32.S ../boot/boot.h @mkdir -p boot $(CC) -m32 -x assembler-with-cpp -c -I../boot -o $@ $< boot/%.o: ../boot/%.S ../boot/boot.h app/build_version.h @mkdir -p boot $(CC) -m32 -x assembler-with-cpp -c -I../boot -Iapp -o $@ $< boot/efisetup.o: ../boot/efisetup.c @mkdir -p boot $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/reloc.o: ../system/reloc32.c @mkdir -p system $(CC) -c $(CFLAGS) -fno-strict-aliasing $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/%.o: ../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/imc/%.o: ../system/imc/%.c @mkdir -p system/imc $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) lib/%.o: ../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) tests/%.o: ../tests/%.c @mkdir -p tests $(CC) -c $(CFLAGS) $(OPT_FAST) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/%.o: ../app/%.c app/build_version.h @mkdir -p app $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/build_version.h: FORCE @mkdir -p app @( \ cp -f ../app/version.h $@.tmp; \ if $(GIT_AVAILABLE) && test -d ../.git ; then \ hash=`git rev-parse HEAD | cut -c1-7`; \ sed -i 's/GIT_HASH\s\".*"/GIT_HASH "'$$hash'"/' $@.tmp; \ else \ sed -i 's/GIT_HASH\s\".*"/GIT_HASH "unknown"/' $@.tmp; \ fi; \ cmp $@ $@.tmp 2>/dev/null || cp -f $@.tmp $@; \ rm -f $@.tmp; \ ) FORCE: # Link it statically once so I know I don't have undefined symbols and # then link it dynamically so I have full relocation information. memtest_shared: $(OBJS) $(MS_LDS) Makefile $(LD) --warn-constructors --warn-common -static -T $(MS_LDS) -o $@ $(OBJS) && \ $(LD) -shared -Bsymbolic -T $(MS_LDS) -o $@ $(OBJS) memtest_shared.bin: memtest_shared $(OBJCOPY) -O binary $< memtest_shared.bin memtest.debug: memtest_shared objcopy --only-keep-debug memtest_shared memtest.debug strip -R .eh_frame memtest_shared strip -R .comment memtest_shared memtest.bin: memtest_shared.bin boot/bootsect.o boot/setup.o ldscripts/memtest_bin.lds $(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_bin.lds boot/bootsect.o boot/setup.o -b binary memtest_shared.bin -o memtest.bin memtest.efi: memtest_shared.bin boot/header.o boot/setup.o ldscripts/memtest_efi.lds $(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_efi.lds boot/header.o boot/setup.o -b binary memtest_shared.bin -o memtest.efi memtest.mbr: memtest_shared.bin boot/mbr.o ldscripts/memtest_mbr.lds $(LD) -T ldscripts/memtest_mbr.lds boot/mbr.o -b binary memtest_shared.bin -o memtest.mbr floppy.img: memtest.bin dd if=/dev/zero of=floppy.img bs=1474560 count=1 dd if=memtest.bin of=floppy.img bs=1474560 conv=notrunc esp.img: memtest.efi @mkdir -p iso/EFI/BOOT cp memtest.efi iso/EFI/BOOT/bootia32.efi @rm -f esp.img /sbin/mkdosfs -n MEMTEST-ESP -F12 -C esp.img 4096 mcopy -s -i esp.img iso/EFI :: memtest.iso: memtest.mbr floppy.img esp.img @mkdir -p iso/boot cp floppy.img iso/boot/floppy.img xorrisofs -pad -R -J -volid MT86PLUS_32 -graft-points -hide-rr-moved --grub2-mbr memtest.mbr \ -b /boot/floppy.img --efi-boot --interval:appended_partition_2:all:: \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./esp.img \ -o ./memtest.iso /boot=./iso/boot /EFI=./iso/EFI iso: memtest.iso clean: rm -rf boot system lib tests app *.img *.iso memtest* iso grub-* # grub-memtest.iso uses GRUB as an intermediate bootloader to allow Memtest86+ # to be started with the native USB keyboard drivers either enabled or disabled, # or to be started in a fail-safe mode with SMP and memory identification & # speed testing disabled. # # By setting GRUB_CFG to "grub-test", grub-memtest.iso can instead be used for # testing the various different boot modes. Upstream GRUB only supports the # 16-bit and 32-bit boot protocols, via the "linux16" and "linux" commands. # Fedora add support for the EFI handover boot protocols, via the "linuxefi" # command, and, since 2019, remove support for the 32-bit protocol, aliasing # the "linux" command to either "linux16" or "linuxefi", depending on whether # you boot in legacy or EFI mode. Mageia follows Fedora, but restores support # for the 32-bit protocol, via the "linux32" command. Other distributions no # doubt do their own thing. The boot menu on grub-memtest.iso attempts to # support all these options. GRUB_CFG ?= grub GRUB_FONT_DIR ?= /usr/share/grub GRUB_LIB_DIR ?= /usr/lib/grub GRUB_MKIMAGE := $(shell command -v grub2-mkimage || command -v grub-mkimage) GRUB_MODULES = iso9660 fat part_msdos part_gpt all_video font gfxterm gfxmenu \ boot chain configfile echo ls grub-eltorito.img: $(GRUB_MKIMAGE) --output $@ --prefix /boot/grub --format i386-pc-eltorito biosdisk $(GRUB_MODULES) grub-bootia32.efi: $(GRUB_MKIMAGE) --output $@ --prefix /EFI/BOOT/grub --format i386-efi $(GRUB_MODULES) grub-esp.img: memtest.efi grub-bootia32.efi ../grub/${GRUB_CFG}-efi.cfg @mkdir -p grub-iso/EFI/BOOT/grub/i386-efi grub-iso/EFI/BOOT/grub/fonts cp memtest.efi grub-iso/EFI/BOOT/memtest cp grub-bootia32.efi grub-iso/EFI/BOOT/bootia32.efi cp ../grub/${GRUB_CFG}-efi.cfg grub-iso/EFI/BOOT/grub/grub.cfg cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/EFI/BOOT/grub/fonts/ cp $(GRUB_LIB_DIR)/i386-efi/*.mod grub-iso/EFI/BOOT/grub/i386-efi/ @rm -f grub-esp.img /sbin/mkdosfs -n MT86P_ESP -F12 -C grub-esp.img 8192 mcopy -s -i grub-esp.img grub-iso/EFI :: grub-memtest.iso: memtest.bin grub-eltorito.img ../grub/${GRUB_CFG}-legacy.cfg grub-esp.img @mkdir -p grub-iso/boot/grub/i386-pc grub-iso/boot/grub/fonts cp memtest.bin grub-iso/boot/memtest cp grub-eltorito.img grub-iso/boot/eltorito.img cp ../grub/${GRUB_CFG}-legacy.cfg grub-iso/boot/grub/grub.cfg cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/boot/grub/fonts/ cp $(GRUB_LIB_DIR)/i386-pc/*.mod grub-iso/boot/grub/i386-pc/ xorrisofs -pad -R -J -volid MT86PLUS_32 -graft-points -hide-rr-moved \ --grub2-mbr $(GRUB_LIB_DIR)/i386-pc/boot_hybrid.img \ -b /boot/eltorito.img -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \ --efi-boot --interval:appended_partition_2:all:: \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./grub-esp.img \ -o ./grub-memtest.iso /boot=./grub-iso/boot /EFI=./grub-iso/EFI grub-iso: grub-memtest.iso memtest86plus-7.20/build32/debug_memtest.sh000077500000000000000000000152451471441223100206370ustar00rootroot00000000000000#! /bin/bash ############################################################################### # # Description : `debug_memtest.sh` is a script that allows memtest86plus # developers to set up a debugging environment with GDB in QEMU. It calls # the `make debug` target of a modified Makefile to create an additional # debug-symbol file called `memtest.debug`. The symbols of this file are # loaded with an offset (in accordance with the loading location of the # efi-image) into GDB to match the exact addresses of the symbols in memory. # # For more detailed information, please read 'HOW_TO_DEBUG_WITH_GDB' # # Author : Regina König # Year : 2022 # Email : koenig_regina@arcor.de # ############################################################################## Help() { echo "Syntax: $0 [-h|-c|-t ]" echo "options:" echo " -h Print this help" echo " -c Delete all debugging related files" echo " -t Define an alternative terminal" echo " You can define your own terminal inclusive its execution command via:" echo " ./debug_script.sh -t \" \"" echo " See following examples:" echo " ./debug_script.sh -t \"x-terminal-emulator -e \"" echo " ./debug_script.sh -t \"gnome-terminal -- \"" echo " ./debug_script.sh -t \"xterm -e \"" } Clear() { echo "Deleting files..." rm -rf hda-contents rm -f debug.log rm -f gdbscript rm -f QemuKill make clean rm -f OVMF32_VARS.fd rm -f OVMF32_CODE.fd rm -f memtest_shared_debug.lds } while getopts ":hct:" option; do case $option in h) # display Help Help exit;; c) # clear directory Clear exit;; t) # define own terminal TERMINAL="$OPTARG" if ! $TERMINAL ls; then echo "Your entered command is not valid. Please check it again" echo "Or type \"./debug_memtest.sh -h\" for help" exit 1 fi exit;; \?) # invalid option echo "Error: Invalid option" echo "Type $0 -h for more information" exit;; esac done Check() { # Check if QEMU and OVMF are installed if ! command -v qemu-system-i386 > /dev/null 2>&1; then echo "Qemu not installed" exit 1 fi # Check for presence of OVMF32_VARS.fd and OVMF32_CODE.fd if [ ! -f OVMF32_CODE.fd ] && [ ! -f /usr/share/OVMF/OVMF32_CODE.fd ]; then echo "Package ovmf-ia32 not installed. Type 'sudo apt install ovmf-ia32' and create some symlinks." echo "Or copy your own versions of OVMF32_VARS.fd and OVMF32_CODE.fd into this directory" exit 1 fi # Check if gdb is installed if ! command -v gdb > /dev/null 2>&1; then echo "GDB not installed" exit 1 fi # Check for various terminals. Do not define TERMINAL if already defined by commandline if [ -z $TERMINAL ]; then if command -v x-terminal-emulator &> /dev/null; then echo "x-terminal-emulator found" TERMINAL="x-terminal-emulator -e " elif command -v gnome-terminal &> /dev/null; then echo "gnome-terminal found" TERMINAL="gnome-terminal -- " elif command -v xterm &> /dev/null; then echo "xterm found" TERMINAL="xterm -e " else echo "No terminal recognized. Please install x-terminal-emulator or gnome-terminal or xterm." echo "Or define your own terminal alternatively." echo "Type ./debug_memtest.sh -h for more information" exit 1 fi fi } Make() { make debug DEBUG=1 ret_val=$? if [[ $ret_val -ne 0 ]] ; then echo "Make failed with return value: $ret_val" exit 1 fi } # Retrieve addresses from code (not used in this version) # Get_Offsets() { # IMAGEBASE=$(grep -P '#define\tIMAGE_BASE' header.S | cut -f3) # BASEOFCODE=$(grep -P '#define\tBASE_OF_CODE' header.S | cut -f3) # TODO: get RELOCADDR and DATA # } Init() { QEMU="qemu-system-i386" QEMU_FLAGS+=" -hda fat:rw:hda-contents -net none" QEMU_FLAGS+=" -drive if=pflash,format=raw,readonly=on,file=OVMF32_CODE.fd" QEMU_FLAGS+=" -drive if=pflash,format=raw,file=OVMF32_VARS.fd" # Define offsets for loading of symbol-table IMAGEBASE=0x200000 BASEOFCODE=0x1000 DATA=0x23000 RELOCADDR=0x400000 printf -v OFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE)) printf -v DATAOFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE + $DATA)) printf -v RELOCDATA "0x%X" $(($RELOCADDR + $DATA)) GDB_FILE="gdbscript" # Check if gdbscript exists. If not, create one. if [ ! -f $GDB_FILE ] then echo "Creating gdbscript.." echo "set pagination off" > $GDB_FILE if [ -z "$OFFSET" ] || [ -z "$RELOCADDR" ]; then echo "OFFSET or RELOCADDR is not set." exit 1 fi echo "add-symbol-file memtest.debug $OFFSET -s .data $DATAOFFSET" >> $GDB_FILE echo "add-symbol-file memtest.debug $RELOCADDR -s .data $RELOCDATA" >> $GDB_FILE echo "b main" >> $GDB_FILE echo "commands" >> $GDB_FILE echo "layout src" >> $GDB_FILE echo "delete 1" >> $GDB_FILE echo "end" >> $GDB_FILE echo "b run_at" >> $GDB_FILE echo "shell sleep 0.5" >> $GDB_FILE echo "target remote localhost:1234" >> $GDB_FILE echo "info b" >> $GDB_FILE echo "c" >> $GDB_FILE fi if [ ! -f ldscripts/memtest_shared.lds ]; then echo "'memtest_shared.lds' does not exist." exit 1 fi sed '/DISCARD/d' ldscripts/memtest_shared.lds > memtest_shared_debug.lds if [ ! -f memtest_shared_debug.lds ]; then echo "Creation of 'memtest_shared_debug.lds' failed." exit 1 fi } Prepare_Directory() { # Create dir hda-contents and a boot directory mkdir -p hda-contents/EFI/boot if [ ! -d hda-contents ]; then echo "Creation of directory hda-contents failed." exit 1 fi # Copy memtest.efi to hda-contents cp memtest.efi hda-contents/ cp memtest.efi hda-contents/EFI/boot/BOOT_IA32.efi # Copy OVMF* files from /usr/share if [ ! -f OVMF32_VARS.fd ] || [ ! -f OVMF32_CODE.fd ]; then cp /usr/share/OVMF/OVMF32_CODE.fd . cp /usr/share/OVMF/OVMF32_VARS.fd . fi } # Global checks Check # Initialize Init # Build Make # Create needed directories and move efi binary to appropriate location Prepare_Directory # Run QEMU and launch second terminal, # wait for connection via gdb $TERMINAL gdb -x $GDB_FILE & $QEMU $QEMU_FLAGS -s -S memtest86plus-7.20/build32/ldscripts/000077500000000000000000000000001471441223100174545ustar00rootroot00000000000000memtest86plus-7.20/build32/ldscripts/memtest_bin.lds000066400000000000000000000004561471441223100224730ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH("i386") ENTRY(boot); SECTIONS { . = 0; .bootsect : { *(.bootsect) } .setup : { *(.setup) } .memtest : { _start = . ; *(.data) _end = . ; } /DISCARD/ : { *(*) } _sys_size = (_end - _start + 15) >> 4; _init_size = (_end - _start) + _bss_size; } memtest86plus-7.20/build32/ldscripts/memtest_efi.lds000066400000000000000000000026401471441223100224630ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(i386) ENTRY(boot); SECTIONS { . = 0; .header : { *(.header) } .setup : { *(.setup) } . = ALIGN(512); .text : { _file_text_start = . ; *(.data) _real_text_end = . ; . = ALIGN(512); _file_text_end = . ; } .reloc : { _file_reloc_start = . ; *(.reloc) _real_reloc_end = . ; . = ALIGN(512); _file_reloc_end = . ; } .sbat : { _file_sbat_start = . ; *(.sbat) _real_sbat_end = . ; . = ALIGN(512); _file_sbat_end = . ; } /DISCARD/ : { *(*) } _real_text_size = _real_text_end - _file_text_start; _real_reloc_size = _real_reloc_end - _file_reloc_start; _real_sbat_size = _real_sbat_end - _file_sbat_start; _file_head_size = _file_text_start; _file_text_size = _file_text_end - _file_text_start; _file_reloc_size = _file_reloc_end - _file_reloc_start; _file_sbat_size = _file_sbat_end - _file_sbat_start; _sys_size = (_real_text_size + 15) >> 4; _init_size = _real_text_size + _bss_size; _virt_head_size = ((_file_head_size + 4095) >> 12) << 12; _virt_text_size = ((_init_size + 4095) >> 12) << 12; _virt_reloc_size = ((_file_reloc_size + 4095) >> 12) << 12; _virt_sbat_size = ((_file_sbat_size + 4095) >> 12) << 12; _virt_text_start = _virt_head_size; _virt_reloc_start = _virt_text_start + _virt_text_size; _virt_sbat_start = _virt_reloc_start + _virt_reloc_size; _virt_img_size = _virt_sbat_start + _virt_sbat_size; } memtest86plus-7.20/build32/ldscripts/memtest_mbr.lds000066400000000000000000000003461471441223100225010ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH("i386") ENTRY(boot); SECTIONS { . = 0; .mbr : { *(.mbr) } .memtest (NOLOAD) : { _start = . ; *(.data) _end = . ; } /DISCARD/ : { *(*) } _sys_size = (_end - _start + 15) >> 4; } memtest86plus-7.20/build32/ldscripts/memtest_shared.lds000066400000000000000000000016741471441223100231740ustar00rootroot00000000000000OUTPUT_FORMAT("elf32-i386"); OUTPUT_ARCH(i386); ENTRY(startup32); SECTIONS { . = 0; .text : { _start = .; *(.text) *(.text.*) *(.plt) _etext = . ; } = 0x9090 .rodata : { *(.rodata) *(.rodata.*) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } .dynamic : { *(.dynamic) } .rel.text : { *(.rel.text .rel.text.*) } .rel.rodata : { *(.rel.rodata .rel.rodata.*) } .rel.data : { *(.rel.data .rel.data.*) } .rel.got : { *(.rel.got .rel.got.*) } .rel.plt : { *(.rel.plt .rel.plt.*) } . = ALIGN(4); .data : { _data = .; *(.data) *(.data.*) } .got : { *(.got.plt) *(.got) _edata = . ; } . = ALIGN(4); .bss : { _bss = .; *(.dynbss) *(.bss) *(.bss.*) *(COMMON) . = ALIGN(16); _stacks = .; *(.stacks) /* _end must be at least 256 byte aligned */ . = ALIGN(256); _end = .; } /DISCARD/ : { *(*) } } memtest86plus-7.20/build64/000077500000000000000000000000001471441223100154525ustar00rootroot00000000000000memtest86plus-7.20/build64/Makefile000066400000000000000000000222101471441223100171070ustar00rootroot00000000000000AS = as -64 CC = gcc OBJCOPY = objcopy GIT = git ifeq ($(GIT),none) GIT_AVAILABLE = false else GIT_AVAILABLE = true endif CFLAGS = -std=gnu11 -Wall -Wextra -Wshadow -m64 -march=x86-64 -mno-mmx -mno-sse -mno-sse2 \ -fpic -fno-builtin -ffreestanding -fomit-frame-pointer -fno-stack-protector \ -fexcess-precision=standard -DARCH_BITS=64 ifeq ($(DEBUG), 1) CFLAGS+=-ggdb3 -DDEBUG_GDB OPT_SMALL=-Og OPT_FAST=-Og MS_LDS=memtest_shared_debug.lds else OPT_SMALL=-Os OPT_FAST=-O3 MS_LDS=ldscripts/memtest_shared.lds endif INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app -Iapp SYS_OBJS = system/acpi.o \ system/cpuid.o \ system/cpuinfo.o \ system/cpulocal.o \ system/ehci.o \ system/font.o \ system/hwctrl.o \ system/heap.o \ system/hwquirks.o \ system/keyboard.o \ system/ohci.o \ system/memctrl.o \ system/pci.o \ system/pmem.o \ system/reloc.o \ system/screen.o \ system/serial.o \ system/smbios.o \ system/i2c_x86.o \ system/spd.o \ system/smp.o \ system/temperature.o \ system/timers.o \ system/uhci.o \ system/usbhcd.o \ system/vmem.o \ system/xhci.o IMC_SRCS = $(wildcard ../system/imc/*.c) IMC_OBJS = $(subst ../,,$(IMC_SRCS:.c=.o)) LIB_OBJS = lib/barrier.o \ lib/print.o \ lib/read.o \ lib/string.o \ lib/unistd.o TST_OBJS = tests/addr_walk1.o \ tests/bit_fade.o \ tests/block_move.o \ tests/modulo_n.o \ tests/mov_inv_fixed.o \ tests/mov_inv_random.o \ tests/mov_inv_walk1.o \ tests/own_addr.o \ tests/test_helper.o \ tests/tests.o APP_OBJS = app/badram.o \ app/config.o \ app/display.o \ app/error.o \ app/interrupt.o \ app/main.o OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin memtest.efi check: @if [ -z ${DEBUG} ]; then\ echo "Macro DEBUG is not defined. Run debug_memtest.sh to invoke debug target";\ exit 1;\ fi debug: check memtest.debug memtest.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(IMC_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) boot/header.o : | ../boot/sbat.csv boot/startup.o: ../boot/startup64.S ../boot/boot.h @mkdir -p boot $(CC) -m64 -x assembler-with-cpp -c -I../boot -o $@ $< boot/%.o: ../boot/%.S ../boot/boot.h app/build_version.h @mkdir -p boot $(CC) -m64 -x assembler-with-cpp -c -I../boot -Iapp -o $@ $< boot/efisetup.o: ../boot/efisetup.c @mkdir -p boot $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/reloc.o: ../system/reloc64.c @mkdir -p system $(CC) -c $(CFLAGS) -fno-strict-aliasing $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/%.o: ../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/imc/%.o: ../system/imc/%.c @mkdir -p system/imc $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) lib/%.o: ../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) tests/%.o: ../tests/%.c @mkdir -p tests $(CC) -c $(CFLAGS) $(OPT_FAST) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/%.o: ../app/%.c app/build_version.h @mkdir -p app $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/build_version.h: FORCE @mkdir -p app @( \ cp -f ../app/version.h $@.tmp; \ if $(GIT_AVAILABLE) && test -d ../.git ; then \ hash=`git rev-parse HEAD | cut -c1-7`; \ sed -i 's/GIT_HASH\s\".*"/GIT_HASH "'$$hash'"/' $@.tmp; \ else \ sed -i 's/GIT_HASH\s\".*"/GIT_HASH "unknown"/' $@.tmp; \ fi; \ cmp $@ $@.tmp 2>/dev/null || cp -f $@.tmp $@; \ rm -f $@.tmp; \ ) FORCE: # Link it statically once so I know I don't have undefined symbols and # then link it dynamically so I have full relocation information. memtest_shared: $(OBJS) $(MS_LDS) Makefile $(LD) --warn-constructors --warn-common -static -T $(MS_LDS) -o $@ $(OBJS) && \ $(LD) -shared -Bsymbolic -T $(MS_LDS) -o $@ $(OBJS) memtest_shared.bin: memtest_shared $(OBJCOPY) -O binary $< memtest_shared.bin memtest.debug: memtest_shared objcopy --only-keep-debug memtest_shared memtest.debug strip -R .eh_frame memtest_shared strip -R .comment memtest_shared memtest.bin: memtest_shared.bin boot/bootsect.o boot/setup.o ldscripts/memtest_bin.lds $(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_bin.lds boot/bootsect.o boot/setup.o -b binary memtest_shared.bin -o memtest.bin memtest.efi: memtest_shared.bin boot/header.o boot/setup.o ldscripts/memtest_efi.lds $(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_efi.lds boot/header.o boot/setup.o -b binary memtest_shared.bin -o memtest.efi memtest.mbr: memtest_shared.bin boot/mbr.o ldscripts/memtest_mbr.lds $(LD) -T ldscripts/memtest_mbr.lds boot/mbr.o -b binary memtest_shared.bin -o memtest.mbr floppy.img: memtest.bin dd if=/dev/zero of=floppy.img bs=1474560 count=1 dd if=memtest.bin of=floppy.img bs=1474560 conv=notrunc esp.img: memtest.efi @mkdir -p iso/EFI/BOOT cp memtest.efi iso/EFI/BOOT/bootx64.efi @rm -f esp.img /sbin/mkdosfs -n MEMTEST-ESP -F12 -C esp.img 4096 mcopy -s -i esp.img iso/EFI :: memtest.iso: memtest.mbr floppy.img esp.img @mkdir -p iso/boot cp floppy.img iso/boot/floppy.img xorrisofs -pad -R -J -volid MT86PLUS_64 -graft-points -hide-rr-moved --grub2-mbr memtest.mbr \ -b /boot/floppy.img --efi-boot --interval:appended_partition_2:all:: \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./esp.img \ -o ./memtest.iso /boot=./iso/boot /EFI=./iso/EFI iso: memtest.iso clean: rm -rf boot system lib tests app *.img *.iso memtest* iso grub-* # grub-memtest.iso uses GRUB as an intermediate bootloader to allow Memtest86+ # to be started with the native USB keyboard drivers either enabled or disabled, # or to be started in a fail-safe mode with SMP and memory identification & # speed testing disabled. # # By setting GRUB_CFG to "grub-test", grub-memtest.iso can instead be used for # testing the various different boot modes. Upstream GRUB only supports the # 16-bit and 32-bit boot protocols, via the "linux16" and "linux" commands. # Fedora add support for the EFI handover boot protocols, via the "linuxefi" # command, and, since 2019, remove support for the 32-bit protocol, aliasing # the "linux" command to either "linux16" or "linuxefi", depending on whether # you boot in legacy or EFI mode. Mageia follows Fedora, but restores support # for the 32-bit protocol, via the "linux32" command. Other distributions no # doubt do their own thing. The boot menu on grub-memtest.iso attempts to # support all these options. GRUB_CFG ?= grub GRUB_FONT_DIR ?= /usr/share/grub GRUB_LIB_DIR ?= /usr/lib/grub GRUB_MKIMAGE := $(shell command -v grub2-mkimage || command -v grub-mkimage) GRUB_MODULES = iso9660 fat part_msdos part_gpt all_video font gfxterm gfxmenu \ boot chain configfile echo ls grub-eltorito.img: $(GRUB_MKIMAGE) --output $@ --prefix /boot/grub --format i386-pc-eltorito biosdisk $(GRUB_MODULES) grub-bootx64.efi: $(GRUB_MKIMAGE) --output $@ --prefix /EFI/BOOT/grub --format x86_64-efi $(GRUB_MODULES) grub-esp.img: memtest.efi grub-bootx64.efi ../grub/${GRUB_CFG}-efi.cfg @mkdir -p grub-iso/EFI/BOOT/grub/x86_64-efi grub-iso/EFI/BOOT/grub/fonts cp memtest.efi grub-iso/EFI/BOOT/memtest cp grub-bootx64.efi grub-iso/EFI/BOOT/bootx64.efi cp ../grub/${GRUB_CFG}-efi.cfg grub-iso/EFI/BOOT/grub/grub.cfg cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/EFI/BOOT/grub/fonts/ cp $(GRUB_LIB_DIR)/x86_64-efi/*.mod grub-iso/EFI/BOOT/grub/x86_64-efi/ @rm -f grub-esp.img /sbin/mkdosfs -n MT86P_ESP -F12 -C grub-esp.img 8192 mcopy -s -i grub-esp.img grub-iso/EFI :: grub-memtest.iso: memtest.bin grub-eltorito.img ../grub/${GRUB_CFG}-legacy.cfg grub-esp.img @mkdir -p grub-iso/boot/grub/i386-pc grub-iso/boot/grub/fonts cp memtest.bin grub-iso/boot/memtest cp grub-eltorito.img grub-iso/boot/eltorito.img cp ../grub/${GRUB_CFG}-legacy.cfg grub-iso/boot/grub/grub.cfg cp $(GRUB_FONT_DIR)/unicode.pf2 grub-iso/boot/grub/fonts/ cp $(GRUB_LIB_DIR)/i386-pc/*.mod grub-iso/boot/grub/i386-pc/ xorrisofs -pad -R -J -volid MT86PLUS_64 -graft-points -hide-rr-moved \ --grub2-mbr $(GRUB_LIB_DIR)/i386-pc/boot_hybrid.img \ -b /boot/eltorito.img -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \ --efi-boot --interval:appended_partition_2:all:: \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./grub-esp.img \ -o ./grub-memtest.iso /boot=./grub-iso/boot /EFI=./grub-iso/EFI grub-iso: grub-memtest.iso memtest86plus-7.20/build64/debug_memtest.sh000077500000000000000000000153341471441223100206430ustar00rootroot00000000000000#! /bin/bash ############################################################################### # # Description : `debug_memtest.sh` is a script that allows memtest86plus # developers to set up a debugging environment with GDB in QEMU. It calls # the `make debug` target of a modified Makefile to create an additional # debug-symbol file called `memtest.debug`. The symbols of this file are # loaded with an offset (in accordance with the loading location of the # efi-image) into GDB to match the exact addresses of the symbols in memory. # # For more detailed information, please read 'HOW_TO_DEBUG_WITH_GDB' # # Author : Regina König # Year : 2022 # Email : koenig_regina@arcor.de # ############################################################################## Help() { echo "Syntax: $0 [-h|-c|-t ]" echo "options:" echo " -h Print this help" echo " -c Delete all debugging related files" echo " -t Define an alternative terminal" echo " You can define your own terminal inclusive its execution command via:" echo " ./debug_script.sh -t \" \"" echo " See following examples:" echo " ./debug_script.sh -t \"x-terminal-emulator -e \"" echo " ./debug_script.sh -t \"gnome-terminal -- \"" echo " ./debug_script.sh -t \"xterm -e \"" } Clear() { echo "Deleting files..." rm -rf hda-contents rm -f debug.log rm -f gdbscript rm -f QemuKill make clean rm -f OVMF.fd rm -f OVMF_VARS.fd rm -f OVMF_CODE.fd rm -f memtest_shared_debug.lds } while getopts ":hct:" option; do case $option in h) # display Help Help exit;; c) # clear directory Clear exit;; t) # define own terminal TERMINAL="$OPTARG" if ! $TERMINAL ls; then echo "Your entered command is not valid. Please check it again" echo "Or type \"./debug_memtest.sh -h\" for help" exit 1 fi exit;; \?) # invalid option echo "Error: Invalid option" echo "Type $0 -h for more information" exit;; esac done Check() { # Check if QEMU and OVMF are installed if ! command -v qemu-system-x86_64 > /dev/null 2>&1; then echo "Qemu not installed" exit 1 fi # Check for presence of OVMF.fd, OVMF_VARS.fd and OVMF_CODE.fd if [ ! -f OVMF.fd ] && [ ! -f /usr/share/ovmf/OVMF.fd ]; then echo "Package ovmf not installed. Type 'sudo apt install ovmf'." echo "Or copy your own versions of OVMF.fd, OVMF_VARS.fd and OVMF_CODE.fd into this directory" exit 1 fi # Check if gdb is installed if ! command -v gdb > /dev/null 2>&1; then echo "GDB not installed" exit 1 fi # Check for various terminals. Do not define TERMINAL if already defined by commandline if [ -z $TERMINAL ]; then if command -v x-terminal-emulator &> /dev/null; then echo "x-terminal-emulator found" TERMINAL="x-terminal-emulator -e " elif command -v gnome-terminal &> /dev/null; then echo "gnome-terminal found" TERMINAL="gnome-terminal -- " elif command -v xterm &> /dev/null; then echo "xterm found" TERMINAL="xterm -e " else echo "No terminal recognized. Please install x-terminal-emulator or gnome-terminal or xterm." echo "Or define your own terminal alternatively." echo "Type ./debug_memtest.sh -h for more information" exit 1 fi fi } Make() { make debug DEBUG=1 ret_val=$? if [[ $ret_val -ne 0 ]] ; then echo "Make failed with return value: $ret_val" exit 1 fi } # Retrieve addresses from code (not used in this version) # Get_Offsets() { # IMAGEBASE=$(grep -P '#define\tIMAGE_BASE' header.S | cut -f3) # BASEOFCODE=$(grep -P '#define\tBASE_OF_CODE' header.S | cut -f3) # TODO: get RELOCADDR and DATA # } Init() { QEMU="qemu-system-x86_64" QEMU_FLAGS=" -bios OVMF.fd" QEMU_FLAGS+=" -hda fat:rw:hda-contents -net none" QEMU_FLAGS+=" -drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.fd" QEMU_FLAGS+=" -drive if=pflash,format=raw,file=OVMF_VARS.fd" # Define offsets for loading of symbol-table IMAGEBASE=0x200000 BASEOFCODE=0x1000 DATA=0x23000 RELOCADDR=0x400000 printf -v OFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE)) printf -v DATAOFFSET "0x%X" $(($IMAGEBASE + $BASEOFCODE + $DATA)) printf -v RELOCDATA "0x%X" $(($RELOCADDR + $DATA)) GDB_FILE="gdbscript" # Check if gdbscript exists. If not, create one. if [ ! -f $GDB_FILE ] then echo "Creating gdbscript.." echo "set pagination off" > $GDB_FILE if [ -z "$OFFSET" ] || [ -z "$RELOCADDR" ]; then echo "OFFSET or RELOCADDR is not set." exit 1 fi echo "add-symbol-file memtest.debug $OFFSET -s .data $DATAOFFSET" >> $GDB_FILE echo "add-symbol-file memtest.debug $RELOCADDR -s .data $RELOCDATA" >> $GDB_FILE echo "b main" >> $GDB_FILE echo "commands" >> $GDB_FILE echo "layout src" >> $GDB_FILE echo "delete 1" >> $GDB_FILE echo "end" >> $GDB_FILE echo "b run_at" >> $GDB_FILE echo "shell sleep 0.5" >> $GDB_FILE echo "target remote localhost:1234" >> $GDB_FILE echo "info b" >> $GDB_FILE echo "c" >> $GDB_FILE fi if [ ! -f ldscripts/memtest_shared.lds ]; then echo "'memtest_shared.lds' does not exist." exit 1 fi sed '/DISCARD/d' ldscripts/memtest_shared.lds > memtest_shared_debug.lds if [ ! -f memtest_shared_debug.lds ]; then echo "Creation of 'memtest_shared_debug.lds' failed." exit 1 fi } Prepare_Directory() { # Create dir hda-contents and a boot directory mkdir -p hda-contents/EFI/boot if [ ! -d hda-contents ]; then echo "Creation of directory hda-contents failed." exit 1 fi # Copy memtest.efi to hda-contents cp memtest.efi hda-contents/ cp memtest.efi hda-contents/EFI/boot/BOOT_X64.efi # Copy OVMF* files from /usr/share if [ ! -f OVMF.fd ] || [ ! -f OVMF_VARS.fd ] || [ ! -f OVMF_CODE.fd ]; then cp /usr/share/ovmf/OVMF.fd . cp /usr/share/OVMF/OVMF_CODE.fd . cp /usr/share/OVMF/OVMF_VARS.fd . fi } # Global checks Check # Initialize Init # Build Make # Create needed directories and move efi binary to appropriate location Prepare_Directory # Run QEMU and launch second terminal, # wait for connection via gdb $TERMINAL gdb -x $GDB_FILE & $QEMU $QEMU_FLAGS -s -S memtest86plus-7.20/build64/la64/000077500000000000000000000000001471441223100162205ustar00rootroot00000000000000memtest86plus-7.20/build64/la64/Makefile000066400000000000000000000142641471441223100176670ustar00rootroot00000000000000CC ?= gcc LD ?= ld OBJCOPY ?= objcopy GIT ?= git ifeq ($(GIT),none) GIT_AVAILABLE = false else GIT_AVAILABLE = true endif CFLAGS = -std=gnu11 -Wall -Wextra -Wshadow -march=loongarch64 -fpic -fno-builtin \ -ffreestanding -fomit-frame-pointer -fno-stack-protector -DARCH_BITS=64 ifeq ($(DEBUG), 1) CFLAGS+=-ggdb3 -DDEBUG_GDB OPT_SMALL=-Og OPT_FAST=-Og MS_LDS=memtest_shared_debug.lds else OPT_SMALL=-Os OPT_FAST=-O3 MS_LDS=ldscripts/memtest_shared.lds endif INC_DIRS = -I../../boot -I../../system -I../../system/loongarch -I../../lib -I../../tests -I../../app -Iapp SYS_OBJS = system/acpi.o \ system/cpulocal.o \ system/ehci.o \ system/font.o \ system/heap.o \ system/hwquirks.o \ system/keyboard.o \ system/ohci.o \ system/pci_mmio.o \ system/pmem.o \ system/reloc.o \ system/screen.o \ system/serial.o \ system/smbios.o \ system/spd.o \ system/smp.o \ system/timers.o \ system/uhci.o \ system/usbhcd.o \ system/xhci.o \ system/loongarch/i2c.o \ system/loongarch/cpuid.o \ system/loongarch/cpuinfo.o \ system/loongarch/hwctrl.o \ system/loongarch/memctrl.o \ system/loongarch/temperature.o \ system/loongarch/vmem.o IMC_SRCS = $(wildcard ../../system/imc/loongson/*.c) IMC_OBJS = $(subst ../../,,$(IMC_SRCS:.c=.o)) LIB_OBJS = lib/barrier.o \ lib/print.o \ lib/read.o \ lib/string.o \ lib/unistd.o TST_OBJS = tests/addr_walk1.o \ tests/bit_fade.o \ tests/block_move.o \ tests/modulo_n.o \ tests/mov_inv_fixed.o \ tests/mov_inv_random.o \ tests/mov_inv_walk1.o \ tests/own_addr.o \ tests/test_helper.o \ tests/tests.o APP_OBJS = app/badram.o \ app/config.o \ app/display.o \ app/error.o \ app/main.o \ app/loongarch/interrupt.o OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.efi check: @if [ -z ${DEBUG} ]; then\ echo "Macro DEBUG is not defined. Run debug_memtest.sh to invoke debug target";\ exit 1;\ fi debug: check memtest.debug memtestloongarch.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(IMC_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) boot/header.o : | ../../boot/sbat.csv boot/startup.o: ../../boot/loongarch/startup64.S ../../boot/boot.h @mkdir -p boot $(CC) -x assembler-with-cpp -c -I../../boot -I../../system/loongarch -o $@ $< boot/%.o: ../../boot/%.S ../../boot/boot.h app/build_version.h @mkdir -p boot $(CC) -x assembler-with-cpp -c -I../../boot -Iapp -o $@ $< boot/loongarch/%.o: ../../boot/loongarch/%.S ../../boot/boot.h app/build_version.h @mkdir -p boot/loongarch $(CC) -x assembler-with-cpp -c -I../../boot -Iapp -o $@ $< boot/efisetup.o: ../../boot/efisetup.c @mkdir -p boot $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/reloc.o: ../../system/reloc64.c @mkdir -p system $(CC) -c $(CFLAGS) -fno-strict-aliasing $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/%.o: ../../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/loongarch/%.o: ../../system/loongarch/%.c @mkdir -p system/loongarch/ $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/imc/loongson/%.o: ../../system/imc/loongson/%.c @mkdir -p system/imc/loongson/ $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) lib/%.o: ../../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) tests/%.o: ../../tests/%.c @mkdir -p tests $(CC) -c $(CFLAGS) $(OPT_FAST) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/%.o: ../../app/%.c app/build_version.h @mkdir -p app $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/loongarch/%.o: ../../app/loongarch/%.c app/build_version.h @mkdir -p app/loongarch $(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/build_version.h: FORCE @mkdir -p app @( \ cp -f ../../app/version.h $@.tmp; \ if $(GIT_AVAILABLE) && test -d ../../.git ; then \ hash=`git rev-parse HEAD | cut -c1-7`; \ sed -i 's/GIT_HASH\s\".*"/GIT_HASH "'$$hash'"/' $@.tmp; \ else \ sed -i 's/GIT_HASH\s\".*"/GIT_HASH "unknown"/' $@.tmp; \ fi; \ cmp $@ $@.tmp 2>/dev/null || cp -f $@.tmp $@; \ rm -f $@.tmp; \ ) FORCE: # Link it statically once so I know I don't have undefined symbols and # then link it dynamically so I have full relocation information. memtest_shared: $(OBJS) $(MS_LDS) Makefile $(LD) --warn-constructors --warn-common -static -T $(MS_LDS) -o $@ $(OBJS) && \ $(LD) -shared -Bsymbolic -T $(MS_LDS) -o $@ $(OBJS) memtest_shared.bin: memtest_shared $(OBJCOPY) -O binary $< memtest_shared.bin memtest.debug: memtest_shared objcopy --only-keep-debug memtest_shared memtest.debug strip -R .eh_frame memtest_shared strip -R .comment memtest_shared memtest.efi: memtest_shared.bin boot/loongarch/header.o ldscripts/memtest_efi.lds $(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared)) $(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_efi.lds boot/loongarch/header.o -b binary memtest_shared.bin -o memtest.efi esp.img: memtest.efi @mkdir -p iso/EFI/BOOT cp memtest.efi iso/EFI/BOOT/BOOTLOONGARCH64.EFI @rm -f esp.img /sbin/mkdosfs -n MEMTEST-ESP -F12 -C esp.img 4096 mcopy -s -i esp.img iso/EFI :: memtest.iso: esp.img xorrisofs -pad -R -J -volid MT86PLUS_64 -graft-points \ -part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./esp.img \ -o ./memtest.iso /EFI=./iso/EFI iso: memtest.iso clean: rm -rf boot system lib tests app *.img *.iso memtest* iso grub-* # grub-memtest.iso will be added in future. TODO memtest86plus-7.20/build64/la64/ldscripts/000077500000000000000000000000001471441223100202275ustar00rootroot00000000000000memtest86plus-7.20/build64/la64/ldscripts/memtest_efi.lds000066400000000000000000000024471471441223100232430ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(loongarch) ENTRY(head); SECTIONS { . = 0; .header : { *(.header) } . = ALIGN(4096); .text : { _file_text_start = . ; *(.data) _real_text_end = . ; . = ALIGN(512); _file_text_end = . ; } .reloc : { _file_reloc_start = . ; *(.reloc) _real_reloc_end = . ; . = ALIGN(512); _file_reloc_end = . ; } .sbat : { _file_sbat_start = . ; *(.sbat) _real_sbat_end = . ; . = ALIGN(512); _file_sbat_end = . ; } /DISCARD/ : { *(*) } _real_text_size = _real_text_end - _file_text_start; _real_reloc_size = _real_reloc_end - _file_reloc_start; _real_sbat_size = _real_sbat_end - _file_sbat_start; _file_head_size = _file_text_start; _file_text_size = _file_text_end - _file_text_start; _file_reloc_size = _file_reloc_end - _file_reloc_start; _file_sbat_size = _file_sbat_end - _file_sbat_start; _sys_size = (_real_text_size + 15) >> 4; _init_size = _real_text_size + _bss_size; _virt_head_size = _file_head_size; _virt_text_size = _init_size; _virt_reloc_size = _file_reloc_size; _virt_sbat_size = _file_sbat_size; _virt_text_start = _virt_head_size; _virt_reloc_start = _virt_text_start + _virt_text_size; _virt_sbat_start = _virt_reloc_start + _virt_reloc_size; _virt_img_size = _virt_sbat_start + _virt_sbat_size; } memtest86plus-7.20/build64/la64/ldscripts/memtest_shared.lds000066400000000000000000000017301471441223100237400ustar00rootroot00000000000000OUTPUT_FORMAT("elf64-loongarch") OUTPUT_ARCH(loongarch); ENTRY(startup64); SECTIONS { . = 0; .text : { _start = .; *(.text) *(.text.*) *(.plt) _etext = . ; } = 0x00004003 .rodata : { *(.rodata) *(.rodata.*) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } .dynamic : { *(.dynamic) } .rela.text : { *(.rela.text .rela.text.*) } .rela.rodata : { *(.rela.rodata .rela.rodata.*) } .rela.data : { *(.rela.data .rela.data.*) } .rela.got : { *(.rela.got .rela.got.*) } .rela.plt : { *(.rela.plt .rela.plt.*) } . = ALIGN(4); .data : { _data = .; *(.data) *(.data.*) } .got : { *(.got.plt) *(.got) _edata = . ; } . = ALIGN(4); .bss : { _bss = .; *(.dynbss) *(.bss) *(.bss.*) *(COMMON) . = ALIGN(16); _stacks = .; *(.stacks) /* _end must be at least 256 byte aligned */ . = ALIGN(256); _end = .; } /DISCARD/ : { *(*) } } memtest86plus-7.20/build64/ldscripts/000077500000000000000000000000001471441223100174615ustar00rootroot00000000000000memtest86plus-7.20/build64/ldscripts/memtest_bin.lds000066400000000000000000000004631471441223100224760ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(i386:x86-64) ENTRY(boot); SECTIONS { . = 0; .bootsect : { *(.bootsect) } .setup : { *(.setup) } .memtest : { _start = . ; *(.data) _end = . ; } /DISCARD/ : { *(*) } _sys_size = (_end - _start + 15) >> 4; _init_size = (_end - _start) + _bss_size; } memtest86plus-7.20/build64/ldscripts/memtest_efi.lds000066400000000000000000000026471471441223100224770ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(i386:x86-64) ENTRY(boot); SECTIONS { . = 0; .header : { *(.header) } .setup : { *(.setup) } . = ALIGN(512); .text : { _file_text_start = . ; *(.data) _real_text_end = . ; . = ALIGN(512); _file_text_end = . ; } .reloc : { _file_reloc_start = . ; *(.reloc) _real_reloc_end = . ; . = ALIGN(512); _file_reloc_end = . ; } .sbat : { _file_sbat_start = . ; *(.sbat) _real_sbat_end = . ; . = ALIGN(512); _file_sbat_end = . ; } /DISCARD/ : { *(*) } _real_text_size = _real_text_end - _file_text_start; _real_reloc_size = _real_reloc_end - _file_reloc_start; _real_sbat_size = _real_sbat_end - _file_sbat_start; _file_head_size = _file_text_start; _file_text_size = _file_text_end - _file_text_start; _file_reloc_size = _file_reloc_end - _file_reloc_start; _file_sbat_size = _file_sbat_end - _file_sbat_start; _sys_size = (_real_text_size + 15) >> 4; _init_size = _real_text_size + _bss_size; _virt_head_size = ((_file_head_size + 4095) >> 12) << 12; _virt_text_size = ((_init_size + 4095) >> 12) << 12; _virt_reloc_size = ((_file_reloc_size + 4095) >> 12) << 12; _virt_sbat_size = ((_file_sbat_size + 4095) >> 12) << 12; _virt_text_start = _virt_head_size; _virt_reloc_start = _virt_text_start + _virt_text_size; _virt_sbat_start = _virt_reloc_start + _virt_reloc_size; _virt_img_size = _virt_sbat_start + _virt_sbat_size; } memtest86plus-7.20/build64/ldscripts/memtest_mbr.lds000066400000000000000000000003531471441223100225040ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(i386:x86-64) ENTRY(boot); SECTIONS { . = 0; .mbr : { *(.mbr) } .memtest (NOLOAD) : { _start = . ; *(.data) _end = . ; } /DISCARD/ : { *(*) } _sys_size = (_end - _start + 15) >> 4; } memtest86plus-7.20/build64/ldscripts/memtest_shared.lds000066400000000000000000000017241471441223100231750ustar00rootroot00000000000000OUTPUT_FORMAT("elf64-x86-64"); OUTPUT_ARCH(i386:x86-64); ENTRY(startup64); SECTIONS { . = 0; .text : { _start = .; *(.text) *(.text.*) *(.plt) _etext = . ; } = 0x9090 .rodata : { *(.rodata) *(.rodata.*) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } .dynamic : { *(.dynamic) } .rela.text : { *(.rela.text .rela.text.*) } .rela.rodata : { *(.rela.rodata .rela.rodata.*) } .rela.data : { *(.rela.data .rela.data.*) } .rela.got : { *(.rela.got .rela.got.*) } .rela.plt : { *(.rela.plt .rela.plt.*) } . = ALIGN(4); .data : { _data = .; *(.data) *(.data.*) } .got : { *(.got.plt) *(.got) _edata = . ; } . = ALIGN(4); .bss : { _bss = .; *(.dynbss) *(.bss) *(.bss.*) *(COMMON) . = ALIGN(16); _stacks = .; *(.stacks) /* _end must be at least 256 byte aligned */ . = ALIGN(256); _end = .; } /DISCARD/ : { *(*) } } memtest86plus-7.20/doc/000077500000000000000000000000001471441223100147465ustar00rootroot00000000000000memtest86plus-7.20/doc/Doxyfile000066400000000000000000003350101471441223100164560ustar00rootroot00000000000000# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "Memtest86+" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = YES # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = YES # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which efficively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = README_DEVEL.md ../app ../boot ../lib ../system ../tests # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = __MEMRW_* __MMIORW_* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = README_DEVEL.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: # https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /