pax_global_header00006660000000000000000000000064141325166670014525gustar00rootroot0000000000000052 comment=21e7c6fb497289abe59d76f51739f78ba0745bd8 pcmemtest-1.5/000077500000000000000000000000001413251666700133735ustar00rootroot00000000000000pcmemtest-1.5/.gitignore000066400000000000000000000002221413251666700153570ustar00rootroot00000000000000# Prerequisites *.d # Preprocessed assembler *.s # Object files *.o # Binaries memtest_shared memtest_shared.bin *.bin *.efi *.img *.iso *.mbr pcmemtest-1.5/LICENSE000066400000000000000000000432541413251666700144100ustar00rootroot00000000000000 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. pcmemtest-1.5/README.md000066400000000000000000000535401413251666700146610ustar00rootroot00000000000000# PCMemTest PCMemTest is a stand-alone memory tester for x86 and x86-64 architecture computers. It provides a more thorough memory check than that provided by BIOS memory tests. PCMemTest 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. ## Table of Contents * [Origins](#origins) * [Licensing](#licensing) * [Build and Installation](#build-and-installation) * [Operation](#operation) * [Error Display](#error-display) * [Trouble-shooting Memory Errors](#trouble-shooting-memory-errors) * [Execution Time](#execution-time) * [Memory Testing Philosophy](#memory-testing-philosophy) * [PCMemTest Test Algorithms](#pcmemtest-test-algorithms) * [Individual Test Descriptions](#individual-test-descriptions) * [Known Limitations and Bugs](#known-limitations-and-bugs) * [Acknowledgments](#acknowledgments) ## Origins PCMemTest is a fork and rewrite of Memtest86+, which in turn was a fork of Memtest86. The purpose of the 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, a number of features of Memtest86+ that are not required for the main purpose of PCMemTest (testing the system memory) have been dropped. In particular, no attempt is made to measure the cache and main memory speed, or to identify and report the DRAM type. This should allow PCMemTest to work without modification on future hardware. PCMemTest is based on the last public release of Memtest86+, v5.01. ## Licensing PCMemTest 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, the memtest.bin 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. Any boot command line options are ignored. If using the 16-bit boot protocol, PCMemTest will use the display in text mode (640x400). If using the 32-bit or 64-bit boot protocols, PCMemTest 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. ## Operation Once booted, PCMemTest 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, PCMemTest responds to the following keys: * F1 * enters the configuration menu * F2 * toggles detection and 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 32 CPU cores can be selected, due to 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) and F10 (or 0) can be used as an alternative to the escape key. ## 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. ## Trouble-shooting Memory Errors Please be aware that not all errors reported by PCMemTest 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 PCMemTest are valid. There are some systems that cause PCMemTest 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. PCMemTest can not diagnose many types of PC failures. For example a faulty CPU that causes your OS to crash will most likely just cause PCMemTest to crash in the same way. ## Execution Time The time required for a complete pass of PCMemTest will vary greatly depending on CPU speed, memory speed, and memory size. PCMemTest 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. ## PCMemTest Test Algorithms PCMemTest 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.i to 2.iii 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.i - 3.iii 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.ii 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.i, 1.ii) and the read pass (1.iv) 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 PCMemTest 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 address plus the window number 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 * When booted on a UEFI system, keyboard input will only be seen if the CSM is enabled in the BIOS. Without this, the test will run, but you will be unable to alter the configuration. * Temperature reporting is currently only supported for Intel CPUs. ## Acknowledgments PCMemTest was based on Memtest86+, 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 used by PCMemTest) * 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 PCMemTest) * 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). pcmemtest-1.5/app/000077500000000000000000000000001413251666700141535ustar00rootroot00000000000000pcmemtest-1.5/app/badram.c000066400000000000000000000145031413251666700155500ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 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" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define MAX_PATTERNS 10 // DEFAULT_MASK covers a uintptr_t, since that is the testing granularity. #ifdef __x86_64__ #define DEFAULT_MASK (UINTPTR_MAX << 3) #else #define DEFAULT_MASK (UINTPTR_MAX << 2) #endif //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef struct { uintptr_t addr; uintptr_t mask; } pattern_t; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static pattern_t pattern[MAX_PATTERNS]; static int num_patterns = 0; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ #define COMBINE_MASK(a,b,c,d) ((a & b & c & d) | (~a & b & ~c & d)) /* * Combine two addr/mask pairs to one addr/mask pair. */ static void combine(uintptr_t addr1, uintptr_t mask1, uintptr_t addr2, uintptr_t mask2, uintptr_t *addr, uintptr_t *mask) { *mask = COMBINE_MASK(addr1, mask1, addr2, mask2); *addr = addr1 | addr2; *addr &= *mask; // Normalise, no fundamental need for this } /* * Count the number of addresses covered with a mask. */ static uintptr_t addresses(uintptr_t mask) { uintptr_t ctr = 1; int i = 8*sizeof(uintptr_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 uintptr_t combi_cost(uintptr_t addr1, uintptr_t mask1, uintptr_t addr2, uintptr_t mask2) { uintptr_t cost1 = addresses(mask1); uintptr_t tmp, mask; combine(addr1, mask1, addr2, mask2, &tmp, &mask); return addresses(mask) - cost1; } /* * Find the cheapest array index to extend with the given addr/mask pair. * Return -1 if nothing below the given minimum cost can be found. */ static int cheap_index(uintptr_t addr1, uintptr_t mask1, uintptr_t min_cost) { int i = num_patterns; int idx = -1; while (i-- > 0) { uintptr_t tmp_cost = combi_cost(pattern[i].addr, pattern[i].mask, addr1, mask1); if (tmp_cost < min_cost) { min_cost = tmp_cost; idx = i; } } return idx; } /* * Try to find a relocation index for idx if it costs nothing. * Return -1 if no such index exists. */ static int relocate_index(int idx) { uintptr_t addr = pattern[idx].addr; uintptr_t mask = pattern[idx].mask; pattern[idx].addr = ~pattern[idx].addr; // Never select idx int new = cheap_index(addr, mask, 1 + addresses(mask)); pattern[idx].addr = addr; return new; } /* * Relocate the given index idx only if free of charge. * This is useful to combine to `neighbouring' sections to integrate. * Inspired on the Buddy memalloc principle in the Linux kernel. */ static void relocate_if_free(int idx) { int newidx = relocate_index(idx); if (newidx >= 0) { uintptr_t caddr, cmask; combine(pattern[newidx].addr, pattern[newidx].mask, pattern[ idx].addr, pattern[ idx].mask, &caddr, &cmask); pattern[newidx].addr = caddr; pattern[newidx].mask = cmask; if (idx < --num_patterns) { pattern[idx].addr = pattern[num_patterns].addr; pattern[idx].mask = pattern[num_patterns].mask; } relocate_if_free (newidx); } } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void badram_init(void) { num_patterns = 0; } bool badram_insert(uintptr_t addr) { if (cheap_index(addr, DEFAULT_MASK, 1) != -1) { return false; } if (num_patterns < MAX_PATTERNS) { pattern[num_patterns].addr = addr; pattern[num_patterns].mask = DEFAULT_MASK; num_patterns++; relocate_if_free(num_patterns - 1); } else { int idx = cheap_index(addr, DEFAULT_MASK, UINTPTR_MAX); uintptr_t caddr, cmask; combine(pattern[idx].addr, pattern[idx].mask, addr, DEFAULT_MASK, &caddr, &cmask); pattern[idx].addr = caddr; pattern[idx].mask = cmask; relocate_if_free(idx); } return true; } void badram_display(void) { if (num_patterns == 0) { return; } check_input(); clear_message_area(); display_pinned_message(0, 0, "BadRAM Patterns"); 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 * (TESTWORD_DIGITS + 2) + 1; if (col > (SCREEN_WIDTH - text_width)) { scroll(); col = 7; } display_scrolled_message(col, "0x%0*x,0x%0*x", TESTWORD_DIGITS, pattern[i].addr, TESTWORD_DIGITS, pattern[i].mask); col += text_width; } } pcmemtest-1.5/app/badram.h000066400000000000000000000011401413251666700155460ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BADRAM_H #define BADRAM_H /* * Support for generating patterns for the Linux kernel BadRAM extension. * * Copyright (C) 2020 Martin Whitaker. */ #include #include /* * 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(uintptr_t addr); /* * Displays the pattern array in the scrollable display region in the * format used by the Linux kernel. */ void badram_display(void); #endif // BADRAM_H pcmemtest-1.5/app/config.c000066400000000000000000000442171413251666700155740ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2021 Martin Whitaker. // // Derived from memtest86+ config.c: // // MemTest86+ V5.00 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.x86-secret.com - http://www.memtest.org // ---------------------------------------------------- // config.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "cpuinfo.h" #include "hwctrl.h" #include "keyboard.h" #include "memsize.h" #include "pmem.h" #include "screen.h" #include "smp.h" #include "read.h" #include "print.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 4 #define POP_C 22 #define POP_W 36 #define POP_H 16 #define POP_REGION POP_R, POP_C, POP_R + POP_H - 1, POP_C + POP_W - 1 static const char *cpu_mode_str[] = { "PAR", "SEQ", "RR " }; //------------------------------------------------------------------------------ // 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; bool enable_pcpu[MAX_PCPUS]; bool enable_temperature = false; bool enable_trace = false; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ 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_C + POP_W - 1); } static void display_input_message(int row, const char *message) { clear_popup_row(row); prints(row, POP_C+2, message); } static void display_error_message(int row, const char *message) { clear_popup_row(row); set_foreground_colour(YELLOW); prints(row, POP_C+2, message); set_foreground_colour(WHITE); } static void display_selection_header(int row, int max_num) { prints(row+0, POP_C+2, "Current selection:"); printc(row+1, POP_C+2, '0'); for (int i = 1; i < max_num; i++) { printc(row+1, POP_C+2+i, i % 10 ? 0xc4 : 0xc3); } printi(row+1, POP_C+2+max_num, max_num, 2, false, true); } static void display_enabled(int row, int n, bool enabled) { printc(row, POP_C+2+n, 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_C+2+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_C+2+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_C+2+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_C+2, "Test Selection:"); prints(POP_R+3, POP_C+4, " Clear selection"); prints(POP_R+4, POP_C+4, " Remove one test"); prints(POP_R+5, POP_C+4, " Add one test"); prints(POP_R+6, POP_C+4, " Add test range"); prints(POP_R+7, POP_C+4, " Add all tests"); prints(POP_R+8, POP_C+4, " Exit menu"); display_selection_header(POP_R+10, NUM_TEST_PATTERNS - 1); for (int i = 0; i < NUM_TEST_PATTERNS; i++) { display_enabled(POP_R+12, i, test_list[i].enabled); } bool exit_menu = false; while (!exit_menu) { bool changed = false; 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': // fall through case ESC: { 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); 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_C+2, "Address Range:"); prints(POP_R+3, POP_C+4, " Set lower limit"); prints(POP_R+4, POP_C+4, " Set upper limit"); prints(POP_R+5, POP_C+4, " Test all memory"); prints(POP_R+6, POP_C+4, " Exit menu"); printf(POP_R+8, POP_C+2, "Current range: %kB - %kB", pm_limit_lower << 2, pm_limit_upper << 2); bool exit_menu = false; while (!exit_menu) { bool changed = false; switch (get_key()) { case '1': { display_input_message(POP_R+10, "Enter lower limit: "); uintptr_t page = read_value(POP_R+10, POP_C+2+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_C+2+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': // fall through case ESC: exit_menu = true; break; default: usleep(1000); break; } if (changed) { clear_popup_row(POP_R+8); printf(POP_R+8, POP_C+2, "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 cpu_mode_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_C+2, "CPU Sequencing Mode:"); prints(POP_R+3, POP_C+4, " Parallel (All)"); prints(POP_R+4, POP_C+4, " Sequential (Seq)"); prints(POP_R+5, POP_C+4, " Round robin (RR)"); prints(POP_R+6, POP_C+4, " Exit menu"); printc(POP_R+3+cpu_mode, POP_C+2, '*'); bool exit_menu = false; while (!exit_menu) { int ch = get_key(); switch (ch) { case '1': case '2': case '3': printc(POP_R+3+cpu_mode, POP_C+2, ' '); cpu_mode = ch - '1'; printc(POP_R+3+cpu_mode, POP_C+2, '*'); break; case '0': // fall through case ESC: exit_menu = true; break; default: usleep(1000); break; } } clear_screen_region(POP_REGION); } static void error_mode_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_C+2, "Error Reporting Mode:"); prints(POP_R+3, POP_C+4, " Error counts only"); prints(POP_R+4, POP_C+4, " Error summary"); prints(POP_R+5, POP_C+4, " Individual errors"); prints(POP_R+6, POP_C+4, " BadRAM patterns"); prints(POP_R+7, POP_C+4, " Exit menu"); printc(POP_R+3+error_mode, POP_C+2, '*'); bool exit_menu = false; while (!exit_menu) { int ch = get_key(); switch (ch) { case '1': case '2': case '3': case '4': printc(POP_R+3+error_mode, POP_C+2, ' '); error_mode = ch - '1'; printc(POP_R+3+error_mode, POP_C+2, '*'); break; case '0': // fall through case ESC: exit_menu = true; break; default: usleep(1000); break; } } clear_screen_region(POP_REGION); } static bool set_all_cpus(bool enabled) { clear_popup_row(POP_R+14); for (int i = 1; i < num_pcpus; i++) { enable_pcpu[i] = enabled; display_enabled(POP_R+12, i, enabled); } return true; } static bool add_or_remove_cpu(bool add) { display_input_message(POP_R+14, "Enter CPU #"); int n = read_value(POP_R+14, POP_C+2+11, 2, 0); if (n < 1 || n >= num_pcpus) { display_error_message(POP_R+14, "Invalid CPU number"); return false; } enable_pcpu[n] = add; display_enabled(POP_R+12, n, add); clear_popup_row(POP_R+14); return true; } static bool add_cpu_range() { display_input_message(POP_R+14, "Enter first CPU #"); int n1 = read_value(POP_R+14, POP_C+2+17, 2, 0); if (n1 < 1 || n1 >= num_pcpus) { display_error_message(POP_R+14, "Invalid CPU number"); return false; } display_input_message(POP_R+14, "Enter last CPU #"); int n2 = read_value(POP_R+14, POP_C+2+16, 2, 0); if (n2 < n1 || n2 >= num_pcpus) { display_error_message(POP_R+14, "Invalid CPU range"); return false; } for (int i = n1; i <= n2; i++) { enable_pcpu[i] = true; display_enabled(POP_R+12, i, true); } clear_popup_row(POP_R+14); return true; } static void cpu_selection_menu(void) { clear_screen_region(POP_REGION); prints(POP_R+1, POP_C+2, "CPU Selection:"); prints(POP_R+3, POP_C+4, " Clear selection"); prints(POP_R+4, POP_C+4, " Remove one CPU"); prints(POP_R+5, POP_C+4, " Add one CPU"); prints(POP_R+6, POP_C+4, " Add CPU range"); prints(POP_R+7, POP_C+4, " Add all CPUs"); prints(POP_R+8, POP_C+4, " Exit menu"); display_selection_header(POP_R+10, num_pcpus - 1); printc(POP_R+12, POP_C+2, 'B'); for (int i = 1; i < num_pcpus; i++) { display_enabled(POP_R+12, i, enable_pcpu[i]); } bool exit_menu = false; while (!exit_menu) { bool changed = false; switch (get_key()) { case '1': changed = set_all_cpus(false); break; case '2': changed = add_or_remove_cpu(false); break; case '3': changed = add_or_remove_cpu(true); break; case '4': changed = add_cpu_range(); break; case '5': changed = set_all_cpus(true); break; case '0': // fall through case ESC: 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_PCPUS; i++) { enable_pcpu[i] = true; } enable_temperature = !no_temperature; enable_trace = false; } 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 exit_menu = false; while (!exit_menu) { prints(POP_R+1, POP_C+2, "Settings:"); prints(POP_R+3, POP_C+4, " Test selection"); prints(POP_R+4, POP_C+4, " Address range"); prints(POP_R+5, POP_C+4, " CPU sequencing mode"); prints(POP_R+6, POP_C+4, " Error reporting mode"); if (initial) { if (num_pcpus < 2) set_foreground_colour(BOLD+BLACK); prints(POP_R+7, POP_C+4, " CPU selection"); if (num_pcpus < 2) set_foreground_colour(WHITE); if (no_temperature) set_foreground_colour(BOLD+BLACK); printf(POP_R+8, POP_C+4, " Temperature %s", enable_temperature ? "disable" : "enable "); if (no_temperature) set_foreground_colour(WHITE); printf(POP_R+9, POP_C+4, " Boot trace %s", enable_trace ? "disable" : "enable "); prints(POP_R+10, POP_C+4, " Exit menu"); } else { prints(POP_R+7, POP_C+4, " Skip current test"); prints(POP_R+8 , POP_C+4, " Exit menu"); } 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 (num_pcpus > 1) { cpu_selection_menu(); } } else { bail = true; } break; case '6': if (initial) { if (!no_temperature) { enable_temperature = !enable_temperature; } } break; case '7': if (initial) { enable_trace = !enable_trace; } break; case '0': // fall through case ESC: exit_menu = true; break; default: usleep(1000); break; } } restore_screen_region(POP_REGION, popup_save_buffer); set_background_colour(BLUE); if (cpu_mode != old_cpu_mode) { display_cpu_mode(cpu_mode_str[cpu_mode]); restart = true; } if (restart) { bail = true; } } void initial_config(void) { display_notice("Press to configure, to enable SMP, to start testing "); bool got_key = false; bool smp_enabled = false; bool smp_init_done = false; for (int i = 0; i < 5000 && !got_key; i++) { usleep(1000); switch (get_key()) { case '0': // fall through case ESC: clear_message_area(); display_notice("Rebooting..."); reboot(); break; case '1': smp_init(smp_enabled); smp_init_done = true; config_menu(true); got_key = true; break; case '2': smp_enabled = !smp_enabled; 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 "); } i = 0; break; case ' ': toggle_scroll_lock(); break; case '\n': got_key = true; break; default: break; } } if (!smp_init_done) { smp_init(smp_enabled); } } pcmemtest-1.5/app/config.h000066400000000000000000000014611413251666700155730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef CONFIG_H #define CONFIG_H /* * Provides the configuration settings and pop-up menu. * * Copyright (C) 2020 Martin Whitaker. */ #include #include #include "smp.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; 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 bool enable_pcpu[MAX_PCPUS]; extern bool enable_temperature; extern bool enable_trace; void config_init(void); void config_menu(bool initial); void initial_config(void); #endif // CONFIG_H pcmemtest-1.5/app/display.c000066400000000000000000000176261413251666700160000ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2021 Martin Whitaker. #include #include #include "cpuid.h" #include "cpuinfo.h" #include "hwctrl.h" #include "io.h" #include "keyboard.h" #include "pmem.h" #include "temperature.h" #include "tsc.h" #include "barrier.h" #include "spinlock.h" #include "config.h" #include "error.h" #include "tests.h" #include "display.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define NUM_SPIN_STATES 4 static const char spin_state[NUM_SPIN_STATES] = { '|', '/', '-', '\\' }; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static bool scroll_lock = false; static bool scroll_wait = false; static int spin_idx[MAX_VCPUS]; // 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 //------------------------------------------------------------------------------ // Variables //------------------------------------------------------------------------------ int scroll_message_row; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void display_init(void) { cursor_off(); clear_screen(); set_foreground_colour(RED); set_background_colour(WHITE); clear_screen_region(0, 0, 0, 27); #if TESTWORD_WIDTH > 32 prints( 0, 0, " PCMemTest-64 v1.5 "); #else prints( 0, 0, " PCMemTest-32 v1.5 "); #endif set_foreground_colour(WHITE); set_background_colour(BLUE); prints( 0,28, "| "); prints( 1, 0, "CPU : 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, "vCPU#: | Time: Temperature: N/A "); prints( 8, 0, "State: | "); prints( 9, 0, "Cores: Active / Total (Run: All) | Pass: Errors: "); prints(10, 0, "-------------------------------------------------------------------------------"); // Redraw lines using box drawing characters. for (int i = 0; i < 80; i++) { print_char( 6, i, 0xc4); print_char(10, i, 0xc4); } for (int i = 0; i < 6; i++) { print_char(i, 28, 0xb3); } for (int i = 7; i < 10; i++) { print_char(i, 39, 0xb3); } print_char( 6, 28, 0xc1); print_char( 6, 39, 0xc2); print_char(10, 39, 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"); 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 (cpuid_info.flags.lm) { display_cpu_addr_mode("(x64)"); } else if (cpuid_info.flags.pae) { display_cpu_addr_mode("(PAE)"); } 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 (num_pm_pages) { // Round to nearest MB. display_memory_size(1024 * ((num_pm_pages + 128) / 256)); } scroll_message_row = ROW_SCROLL_T; } void display_start_run(void) { if (!enable_trace) { clear_message_area(); } clear_screen_region(7, 47, 9, 57); // run time clear_screen_region(9, 47, 9, 57); // pass number clear_screen_region(9, 66, 9, SCREEN_WIDTH - 1); // error count display_pass_count(0); display_error_count(0); run_start_time = get_tsc(); } 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, 5, SCREEN_WIDTH - 1); // progress bar, test details 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; } void check_input(void) { switch (get_key()) { case '0': // fall through 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_vcpu) { spin_idx[my_vcpu] = (spin_idx[my_vcpu] + 1) % NUM_SPIN_STATES; display_spinner(my_vcpu, spin_state[spin_idx[my_vcpu]]); barrier_wait(run_barrier); if (master_vcpu == my_vcpu) { check_input(); error_update(); } barrier_wait(run_barrier); // Only the master CPU does the update. if (master_vcpu != my_vcpu) { 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); if (cpuid_info.flags.rdtsc) { int secs = (get_tsc() - run_start_time) / (1000 * clks_per_msec); int mins = secs / 60; secs %= 60; int hours = mins / 60; mins %= 60; display_run_time(hours, mins, secs); } if (enable_temperature) { display_temperature(get_cpu_temperature()); } } 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); } pcmemtest-1.5/app/display.h000066400000000000000000000111361413251666700157730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef DISPLAY_H #define DISPLAY_H /* * Provides (macro) functions that implement the UI display. * All knowledge about the display layout is encapsulated here. * * Copyright (C) 2020 Martin Whitaker. */ #include "screen.h" #include "print.h" #include "string.h" #include "test.h" #define ROW_MESSAGE_T 11 #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) #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(1, 20, str) #define display_l1_cache_size(size) \ printf(2, 10, "%kB", (uintptr_t)(size)) #define display_l2_cache_size(size) \ printf(3, 10, "%kB", (uintptr_t)(size)) #define display_l3_cache_size(size) \ printf(4, 10, "%kB", (uintptr_t)(size)) #define display_memory_size(size) \ printf(5, 10, "%kB", (uintptr_t)(size)) #define display_cpu_num(me) \ printc(7, 7 + (me), '0' + ((me) % 10)) #define display_spinner(me, spin_state) \ printc(8, 7 + (me), spin_state) #define display_active_cpus(count) \ printi(9, 7, count, 2, false, false) #define display_total_cpus(count) \ printi(9, 19, count, 2, false, false) #define display_cpu_mode(str) \ prints(9, 34, str) #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 - 1); \ 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 - 1); \ 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, 47, "%i:%02i:%02i", hours, mins, secs) #define display_temperature(temp) \ printf(7, 71, "%2i%cC ", temp, 0xf8) #define display_pass_count(count) \ printi(9, 47, count, 0, false, true) #define display_error_count(count) \ printi(9, 66, count, 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 + 6, (SCREEN_WIDTH - strlen(str)) / 2, str) #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__) extern int scroll_message_row; void display_init(void); void display_start_run(void); void display_start_pass(void); void display_start_test(void); void check_input(void); void set_scroll_lock(bool enabled); void toggle_scroll_lock(void); void scroll(void); void do_tick(int my_vcpu); void do_trace(int my_vcpu, const char *fmt, ...); #endif // DISPLAY_H pcmemtest-1.5/app/error.c000066400000000000000000000275311413251666700154600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 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 "error.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #ifndef USB_WORKAROUND #define USB_WORKAROUND 1 #endif //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef enum { ADDR_ERROR, DATA_ERROR, PARITY_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; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static bool update_error_info(uintptr_t addr, testword_t xor) { bool update_stats = false; // Update address range. testword_t page = page_of((void *)addr); testword_t offset = addr & 0xFFF; 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); bool new_header = (error_count == 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; switch (type) { case ADDR_ERROR: new_stats = update_error_info(addr, 0); break; case DATA_ERROR: new_stats = update_error_info(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(addr); } if (new_address) { 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(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(); uintptr_t page = page_of((void *)addr); uintptr_t offset = addr & 0xFFF; set_foreground_colour(YELLOW); display_scrolled_message(0, " %2i %4i %2i %09x%03x (%kB)", smp_my_pcpu_num(), 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 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(error_count); } break; case ERROR_MODE_BADRAM: if (new_badram) { badram_display(); } break; default: break; } if (type != PARITY_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 = 0xfff; 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(volatile testword_t *addr1, volatile testword_t *addr2, testword_t good, testword_t bad) { common_err(ADDR_ERROR, (uintptr_t)addr1, good, bad, false); (void)addr2; } void data_error(volatile testword_t *addr, testword_t good, testword_t bad, bool use_for_badram) { #if USB_WORKAROUND /* Skip any errrors 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); } #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_vcpu_num()], 0, 0, false); } #endif void error_update(void) { if (error_count > 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(error_count); } } pcmemtest-1.5/app/error.h000066400000000000000000000016431413251666700154610ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef ERROR_H #define ERROR_H /* * Provides functions that can be called by the memory tests to report errors. * * Copyright (C) 2020 Martin Whitaker. */ #include #include "test.h" /* * The number of errors recorded during the current run. */ extern uint64_t error_count; /* * Initialises the error records. */ void error_init(void); /* * Adds an address error to the error reports. */ void addr_error(volatile testword_t *addr1, volatile testword_t *addr2, testword_t good, testword_t bad); /* * Adds a data error to the error reports. */ void data_error(volatile testword_t *addr, testword_t good, testword_t bad, bool use_for_badram); #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 pcmemtest-1.5/app/interrupt.c000066400000000000000000000113531413251666700163560ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 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 "hwctrl.h" #include "keyboard.h" #include "screen.h" #include "smp.h" #include "error.h" #include "display.h" #include "interrupt.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #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[] = { "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 ss; reg_t es; reg_t ds; reg_t sp; reg_t bp; reg_t si; reg_t di; reg_t dx; reg_t cx; reg_t bx; reg_t ax; reg_t vect; reg_t code; reg_t ip; reg_t cs; reg_t flags; }; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void interrupt(struct trap_regs *trap_regs) { // Get the page fault address. uintptr_t address = 0; if (trap_regs->vect == 14) { #ifdef __x86_64__ __asm__( "movq %%cr2, %0" :"=r" (address) ); #else __asm__( "movl %%cr2, %0" :"=r" (address) ); #endif } #if REPORT_PARITY_ERRORS if (trap_regs->vect == 2) { parity_error(); return; } #endif spin_lock(error_mutex); clear_message_area(); display_pinned_message(0, 0, "Unexpected interrupt on CPU %i", smp_my_pcpu_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, " PC: %0*x", REG_DIGITS, (uintptr_t)trap_regs->ip); display_pinned_message(4, 0, " CS: %0*x", REG_DIGITS, (uintptr_t)trap_regs->cs); display_pinned_message(5, 0, "Flag: %0*x", REG_DIGITS, (uintptr_t)trap_regs->flags); display_pinned_message(6, 0, "Code: %0*x", REG_DIGITS, (uintptr_t)trap_regs->code); display_pinned_message(7, 0, " DS: %0*x", REG_DIGITS, (uintptr_t)trap_regs->ds); display_pinned_message(8, 0, " ES: %0*x", REG_DIGITS, (uintptr_t)trap_regs->es); display_pinned_message(9, 0, " SS: %0*x", REG_DIGITS, (uintptr_t)trap_regs->ss); if (trap_regs->vect == 14) { display_pinned_message(9, 0, " Addr: %0*x", REG_DIGITS, address); } display_pinned_message(2, 25, "%cax: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->ax); display_pinned_message(3, 25, "%cbx: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->bx); display_pinned_message(4, 25, "%ccx: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->cx); display_pinned_message(5, 25, "%cdx: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->dx); display_pinned_message(6, 25, "%cdi: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->di); display_pinned_message(7, 25, "%csi: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->si); display_pinned_message(8, 25, "%cbp: %0*x", REG_PREFIX, REG_DIGITS, (uintptr_t)trap_regs->bp); display_pinned_message(9, 25, "%csp: %0*x", REG_PREFIX, REG_DIGITS, (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*x %0*x", ADR_DIGITS, addr, REG_DIGITS, (uintptr_t)data); } display_pinned_message(11, 0, "CS:PC:"); 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(); } pcmemtest-1.5/app/interrupt.h000066400000000000000000000004301413251666700163550ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef INTERRUPT_H #define INTERRUPT_H /* * Provides the interrupt handler. * * Copyright (C) 2020 Martin Whitaker. */ struct trap_regs; /* * Handles an interrupt. */ void interrupt(struct trap_regs *trap_regs); #endif // INTERRUPT_H pcmemtest-1.5/app/main.c000066400000000000000000000340361413251666700152510ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ main.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://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 "cache.h" #include "cpuid.h" #include "cpuinfo.h" #include "hwctrl.h" #include "io.h" #include "keyboard.h" #include "pmem.h" #include "memsize.h" #include "pci.h" #include "screen.h" #include "smp.h" #include "temperature.h" #include "vmem.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 //------------------------------------------------------------------------------ // 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 barrier_t *start_barrier = NULL; static spinlock_t *start_mutex = NULL; static int8_t pcpu_num_to_vcpu_num[MAX_PCPUS]; static volatile bool start_run = false; static volatile bool start_pass = false; static volatile bool start_test = false; static volatile bool rerun_test = false; static volatile bool dummy_run = false; static volatile int window_num = 0; static volatile uintptr_t window_start = 0; static volatile uintptr_t window_end = 0; static volatile int test_stage = 0; //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ // These are exposed in test.h. int num_vcpus = 1; volatile int master_vcpu = 0; barrier_t *run_barrier = NULL; spinlock_t *error_mutex = NULL; volatile vm_map_t vm_map[MAX_MEM_SEGMENTS]; volatile int vm_map_size = 0; volatile int pass_num = 0; volatile int test_num = 0; volatile bool restart = false; volatile bool bail = false; volatile uintptr_t test_addr[MAX_VCPUS]; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ #define BARRIER \ if (TRACE_BARRIERS && !dummy_run) { \ trace(my_pcpu, "Start barrier wait at %s line %i", __FILE__, __LINE__); \ } \ barrier_wait(start_barrier) static void run_at(uintptr_t addr, int my_pcpu) { uintptr_t *new_start_addr = (uintptr_t *)(addr + startup - _start); if (my_pcpu == 0) { memmove((void *)addr, &_start, _end - _start); } BARRIER; // We use a lock to ensure that only one CPU at a time jumps to // the new code. Some of the startup stuff is not thread safe! spin_lock(start_mutex); goto *new_start_addr; } static void global_init(void) { floppy_off(); cache_on(); cpuid_init(); screen_init(); cpuinfo_init(); pmem_init(); pci_init(); badram_init(); config_init(); display_init(); error_init(); initial_config(); clear_message_area(); display_active_cpus(1); display_total_cpus(num_pcpus); num_vcpus = 0; for (int i = 0; i < num_pcpus; i++) { if (enable_pcpu[i]) { pcpu_num_to_vcpu_num[i] = num_vcpus; display_cpu_num(num_vcpus); display_spinner(num_vcpus, 'S'); num_vcpus++; } } master_vcpu = 0; if (enable_temperature) { int temp = get_cpu_temperature(); if (temp > 0) { display_temperature(temp); } else { enable_temperature = false; no_temperature = true; } } if (enable_trace) { display_pinned_message(0, 0,"CPU Trace"); display_pinned_message(1, 0,"--- ----------------------------------------------------------------------------"); set_scroll_lock(true); } 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 (rsdp_addr != 0) { trace(0, "ACPI RSDP found in %s at %0*x", rsdp_source, 2*sizeof(uintptr_t), rsdp_addr); } start_barrier = smp_alloc_barrier(num_vcpus); run_barrier = smp_alloc_barrier(num_vcpus); start_mutex = smp_alloc_mutex(); error_mutex = smp_alloc_mutex(); if (smp_start(enable_pcpu) != SMP_ERR_NONE) { display_notice("Failed to start other CPUs. Press any key to reboot..."); while (get_key() == 0) { } reboot(); } start_run = true; dummy_run = true; restart = false; } static void setup_vm_map(uintptr_t win_start, uintptr_t win_end) { vm_map_size = 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++) { 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) { 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++; } } } static void test_all_windows(int my_pcpu, int my_vcpu) { int active_cpus = 1; bool i_am_active = (my_vcpu == master_vcpu); if (!dummy_run) { if (cpu_mode == PAR && test_list[test_num].cpu_mode == PAR) { active_cpus = num_vcpus; i_am_active = true; } } if (my_vcpu == master_vcpu) { barrier_init(run_barrier, active_cpus); if (!dummy_run) { display_active_cpus(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 { if (!dummy_run) { display_spinner(my_vcpu, 'W'); } BARRIER; if (bail) { break; } if (my_vcpu == master_vcpu) { 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 >= HIGH_LOAD_ADDR) { // Avoid unnecessary relocation. window_num = 1; } } BARRIER; // Relocate if necessary. if (window_num > 0) { if (!dummy_run && (uintptr_t)&_start != LOW_LOAD_ADDR) { run_at(LOW_LOAD_ADDR, my_pcpu); } } else { if (!dummy_run && (uintptr_t)&_start != HIGH_LOAD_ADDR) { run_at(HIGH_LOAD_ADDR, my_pcpu); } } if (my_vcpu == master_vcpu) { switch (window_num) { case 0: window_start = 0; window_end = (HIGH_LOAD_ADDR >> PAGE_SHIFT); break; case 1: window_start = (HIGH_LOAD_ADDR >> 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); } BARRIER; if (!i_am_active) { continue; } if (vm_map_size == 0) { // No memory to test in this window. if (my_vcpu == master_vcpu) { window_num++; } continue; } if (!dummy_run) { display_spinner(my_vcpu, '-'); } if (dummy_run) { if (my_vcpu == master_vcpu) { 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_vcpu, test_num, test_stage, iterations); } if (my_vcpu == master_vcpu) { window_num++; } } while (window_end < pm_map[pm_map_size - 1].end); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ // The main entry point called from the startup code. void main(void) { int my_pcpu; 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_pcpu = 0; } else { my_pcpu = smp_my_pcpu_num(); } if (init_state < 2) { // Global initialisation is done by the boot CPU. if (my_pcpu == 0) { init_state = 1; global_init(); } else { smp_set_ap_booted(my_pcpu); } } else { // Release the lock taken in run_at(). spin_unlock(start_mutex); } BARRIER; init_state = 2; #if TEST_INTERRUPT if (my_pcpu == 0) { __asm__ __volatile__ ("int $1"); } #endif int my_vcpu = pcpu_num_to_vcpu_num[my_pcpu]; // 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) { BARRIER; if (my_vcpu == 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_vcpu, "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; } BARRIER; if (test_list[test_num].enabled) { test_all_windows(my_pcpu, my_vcpu); } BARRIER; if (my_vcpu != 0) { continue; } check_input(); if (restart) { // The configuration has been changed. master_vcpu = 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) { master_vcpu = (master_vcpu + 1) % num_vcpus; if (master_vcpu != 0) { rerun_test = true; continue; } } break; case ONE: master_vcpu = (master_vcpu + 1) % num_vcpus; break; case SEQ: master_vcpu = (master_vcpu + 1) % num_vcpus; if (master_vcpu != 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_notice("** Pass completed, no errors **"); } } } } pcmemtest-1.5/app/test.h000066400000000000000000000040241413251666700153030ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TEST_H #define TEST_H /* * Provides types and variables used when performing the memory tests. * * Copyright (C) 2020 Martin Whitaker. */ #include #include "pmem.h" #include "barrier.h" #include "spinlock.h" /* * The maximum number of virtual CPUs supported. Note that the display can * only show the state of a maximum of 32 vCPUs. */ #define MAX_VCPUS 32 /* * The number of activated virtual CPUs. */ extern int num_vcpus; /* * The current master virtual CPU. */ extern volatile int master_vcpu; /* * 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; /* * The word width (in bits) used for memory testing. */ #ifdef __x86_64__ #define TESTWORD_WIDTH 64 #else #define TESTWORD_WIDTH 32 #endif /* * The number of hex digits needed to display a memory test word. */ #define TESTWORD_DIGITS (TESTWORD_WIDTH / 4) /* * 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; } vm_map_t; /* * The list of memory segments currently mapped into virtual memory. */ extern volatile vm_map_t vm_map[MAX_MEM_SEGMENTS]; /* * The number of memory segments currently mapped into virtual memory. */ extern volatile int vm_map_size; /* * The number of completed test passes. */ extern volatile int pass_num; /* * The current test number. */ extern volatile int test_num; /* * A flag indicating that testing should be restarted due to a configuration * change. */ extern volatile bool restart; /* * A flag indicating that the current test should be aborted. */ extern volatile bool bail; /* * The base address of the block of memory currently being tested. */ extern volatile uintptr_t test_addr[MAX_VCPUS]; #endif // TEST_H pcmemtest-1.5/boot/000077500000000000000000000000001413251666700143365ustar00rootroot00000000000000pcmemtest-1.5/boot/boot.h000066400000000000000000000043271413251666700154600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BOOT_H #define BOOT_H /* * Definitions used in the boot code. Also defines exported symbols needed * in the main code. * * Copyright (C) 2020 Martin Whitaker. */ #define MAX_APS 64 /* Maximum number of active APs. This only affects memory footprint, so can be increased if needed */ #define BSP_STACK_SIZE 4096 /* Stack size for the BSP */ #define AP_STACK_SIZE 2048 /* Stack size for each AP */ #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 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 _end[]; #endif /* ! __ASSEMBLY__ */ #endif /* BOOT_H */ pcmemtest-1.5/boot/bootparams.h000066400000000000000000000055451413251666700166670ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BOOTPARAMS_H #define BOOTPARAMS_H /* * 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 Martin Whitaker. */ #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 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[0x2d0 - 0x218]; e820_entry_t e820_map[E820_MAP_SIZE]; uint8_t unused6[0xeec - 0xd00]; } __attribute__((packed)) boot_params_t; #endif /* BOOTPARAMS_H */ pcmemtest-1.5/boot/bootsect.S000066400000000000000000000166771413251666700163250ustar00rootroot00000000000000// 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 PCMemTest" 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 pcmemtest-1.5/boot/efi.h000066400000000000000000000162171413251666700152610ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef EFI_H #define EFI_H /* * Provides definitions for accessing the UEFI boot services and configuration * tables. * * Copyright (C) 2020 Martin Whitaker. */ #include #ifdef __x86_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_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_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 efiapi __attribute__((ms_abi)) #ifdef __x86_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 { void *query_mode; void *set_mode; void *blt; efi_gop_mode_t *mode; } efi_graphics_output_protocol_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_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; void *runtime_services; efi_boot_services_t *boot_services; uintn_t num_config_tables; efi_config_table_t *config_tables; } efi_system_table_t; #endif /* EFI_H */ pcmemtest-1.5/boot/efisetup.c000066400000000000000000000431021413251666700163260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2020 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" #define DEBUG 0 //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define MAP_BUFFER_HEADROOM 8 // number of descriptors //------------------------------------------------------------------------------ // 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_system_table_t *sys_table = NULL; //------------------------------------------------------------------------------ // 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_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); } } #if DEBUG 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) {} } #endif 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 efi_status_t alloc_low_memory(void **ptr, size_t size, efi_phys_addr_t min_addr) { 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; } size_t num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; size_t num_descs = mem_map_size / mem_desc_size; for (size_t i = 0; i < num_descs; i++) { efi_memory_desc_t *desc = get_memory_desc((uintptr_t)mem_map, mem_desc_size, i); if (desc->type != EFI_CONVENTIONAL_MEMORY) { continue; } if (desc->num_pages < num_pages) { continue; } efi_phys_addr_t start = desc->phys_addr; efi_phys_addr_t end = start + desc->num_pages * PAGE_SIZE; if (start < min_addr) { start = min_addr; } start = round_up(start, PAGE_SIZE); if ((start + size) > end) { continue; } status = efi_call_bs(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, num_pages, &start); if (status == EFI_SUCCESS) { *ptr = (void *)(uintptr_t)start; efi_call_bs(free_pool, mem_map); return EFI_SUCCESS; } } efi_call_bs(free_pool, mem_map); status = EFI_NOT_FOUND; 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_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_protocol_t *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_protocol_t *current_gop = NULL; status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)¤t_gop); if (status != EFI_SUCCESS) { continue; } void *con_out = NULL; status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out); efi_gop_mode_t *current_mode = efi_table_attr(current_gop, mode); efi_gop_mode_info_t *current_info = efi_table_attr(current_mode, info); // 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. if ((!gop || con_out) && current_info->pixel_format != PIXEL_BLT_ONLY) { gop = current_gop; if (con_out) { break; } } } if (!gop) { #if DEBUG print_string("GOP not found\n"); #endif return EFI_NOT_FOUND; } efi_gop_mode_t *mode = efi_table_attr(gop, mode); efi_gop_mode_info_t *info = efi_table_attr(mode, info); efi_phys_addr_t lfb_base = efi_table_attr(mode, frame_buffer_base); si->orig_video_isVGA = VIDEO_TYPE_EFI; si->lfb_width = info->h_resolution; si->lfb_height = info->v_resolution; si->lfb_base = lfb_base; #ifdef __x86_64__ if (lfb_base >> 32) { si->capabilities |= LFB_CAPABILITY_64BIT_BASE; si->ext_lfb_base = lfb_base >> 32; } #endif switch (info->pixel_format) { case PIXEL_RGB_RESERVED_8BIT_PER_COLOR: #if DEBUG print_string("RGB32 mode\n"); #endif si->lfb_depth = 32; si->lfb_linelength = 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"); #endif si->lfb_depth = 32; si->lfb_linelength = 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"); #endif get_bit_range(info->pixel_info.red_mask, &si->red_pos, &si->red_size); get_bit_range(info->pixel_info.green_mask, &si->green_pos, &si->green_size); get_bit_range(info->pixel_info.blue_mask, &si->blue_pos, &si->blue_size); get_bit_range(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 = (info->pixels_per_scan_line * si->lfb_depth) / 8; break; default: #if DEBUG print_string("Unsupported mode\n"); #endif 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(si->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(); #endif 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); } efi_call_bs(free_pool, handles); } 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; } #ifdef __x86_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; #ifdef __X86_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; #ifdef __X86_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_NONE; 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) { status = alloc_low_memory((void **)&boot_params, sizeof(boot_params_t), 0); 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)); } 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) { __asm__("hlt"); } } pcmemtest-1.5/boot/header.S000066400000000000000000000114271413251666700157170ustar00rootroot00000000000000// 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 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" # 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 #define BASE_OF_CODE 0x1000 .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 0x8664 # Machine (x86-64) #else .word 0x14c # Machine (i386) #endif .word 1 # NumberOfSections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 0 # NumberOfSymbols .word section_table - optional_header # SizeOfOptionalHeader #ifdef __x86_64__ .word 0x20f # Characteristics # IMAGE_FILE_DEBUG_STRIPPED | # IMAGE_FILE_LOCAL_SYMS_STRIPPED | # IMAGE_FILE_LINE_NUMS_STRIPPED | # IMAGE_FILE_EXECUTABLE_IMAGE | # IMAGE_FILE_RELOCS_STRIPPED #else .word 0x30f # Characteristics. # IMAGE_FILE_32BIT_MACHINE | # IMAGE_FILE_DEBUG_STRIPPED | # IMAGE_FILE_LOCAL_SYMS_STRIPPED | # IMAGE_FILE_LINE_NUMS_STRIPPED | # IMAGE_FILE_EXECUTABLE_IMAGE | # IMAGE_FILE_RELOCS_STRIPPED #endif optional_header: #ifdef __x86_64__ .word 0x20b # PE32+ format #else .word 0x10b # PE32 format #endif .byte 0x02 # MajorLinkerVersion .byte 0x14 # MinorLinkerVersion .long _text_size # SizeOfCode .long 0 # SizeOfInitializedData .long 0 # SizeOfUninitializedData .long BASE_OF_CODE + 0x1e0 # AddressOfEntryPoint .long BASE_OF_CODE # BaseOfCode #ifndef __x86_64__ .long 0 # data #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 BASE_OF_CODE + _init_size # SizeOfImage .long 512 # 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 0 # NumberOfRvaAndSizes # Section table section_table: .ascii ".text" .byte 0 .byte 0 .byte 0 .long _text_size # VirtualSize .long BASE_OF_CODE # VirtualAddress .long _text_size # SizeOfRawData .long _text_start # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers .long 0x60500020 # 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 pcmemtest-1.5/boot/mbr.S000066400000000000000000000067651413251666700152600ustar00rootroot00000000000000// 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 PCMemTest\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 pcmemtest-1.5/boot/setup.S000066400000000000000000000176301413251666700156310ustar00rootroot00000000000000// 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 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" #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 0 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 0 relocatable_kernel: .byte 0 min_alignment: .byte 0 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) # 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 # Pad to the declared size. .org (SETUP_SECS*512) pcmemtest-1.5/boot/startup32.S000066400000000000000000000251161413251666700163360ustar00rootroot00000000000000// 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 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 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 though to the shared 32-bit entry point with the boot # params pointer in %esi. movl %eax, %esi # The 32-bit entry point for AP boot and for restart after relocation. .globl startup startup: # Use a temporary stack until we pick the correct one. We can # safely use the high address, even if we are loaded low. movl $(HIGH_LOAD_ADDR + startup_stack_top - startup), %esp # Load the GOT pointer. call 0f 0: popl %ebx addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx # Save the boot params pointer (if first boot). cmpl $1, first_boot@GOTOFF(%ebx) jnz 1f movl %esi, boot_params_addr@GOTOFF(%ebx) 1: # Pick the correct stack. call smp_my_pcpu_num movl $AP_STACK_SIZE, %edx mul %edx leal bsp_stack_top@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. leal idt@GOTOFF(%ebx), %edi leal vec0@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 $(vec1-vec0), %esi addl $8, %edi dec %cx jnz 0b # Initialise the IDT descriptor. 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) jnz 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. movl %ebx, %edi # ebx is overwritten by cpuid movl $0x00000001, %eax # test the PAE flag cpuid andl $0x00000040, %edx jz 1f # bail if not supported movl %cr4, %eax # enable PAE orl $0x00000020, %eax movl %eax, %cr4 leal pdp@GOTOFF(%edi), %eax # set the page directory base address movl %eax, %cr3 # Enable long mode if supported. movl $0x80000000, %eax # check if function 0x80000001 is available cpuid cmpl $0x80000001, %eax jb 0f # bail if not supported mov $0x80000001, %eax # test the LM flag cpuid andl $0x20000000, %edx jz 0f # bail if not supported movl $0xc0000080, %ecx # enable long mode rdmsr orl $0x00000100, %eax wrmsr leal pml4@GOTOFF(%edi), %eax # set the page directory base address movl %eax, %cr3 # Enable paging. 0: movl %cr0, %eax orl $0x80000000, %eax movl %eax, %cr0 1: movl %edi, %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_handler # 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 # 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: pushl $0 # error code pushl $0 # vector jmp int_handler vec1: pushl $0 # error code pushl $1 # vector jmp int_handler vec2: pushl $0 # error code pushl $2 # vector jmp int_handler vec3: pushl $0 # error code pushl $3 # vector jmp int_handler vec4: pushl $0 # error code pushl $4 # vector jmp int_handler vec5: pushl $0 # error code pushl $5 # vector jmp int_handler vec6: pushl $0 # error code pushl $6 # vector jmp int_handler vec7: pushl $0 # error code pushl $7 # vector jmp int_handler vec8: nop;nop # error code already provided pushl $8 # vector jmp int_handler vec9: pushl $0 # error code pushl $9 # vector jmp int_handler vec10: nop;nop # error code already provided pushl $10 # vector jmp int_handler vec11: nop;nop # error code already provided pushl $11 # vector jmp int_handler vec12: nop;nop # error code already provided pushl $12 # vector jmp int_handler vec13: nop;nop # error code already provided pushl $13 # vector jmp int_handler vec14: nop;nop # error code already provided pushl $14 # vector jmp int_handler vec15: pushl $0 # error code pushl $15 # vector jmp int_handler vec16: pushl $0 # error code pushl $16 # vector jmp int_handler vec17: nop;nop # error code pushl $17 # vector jmp int_handler vec18: pushl $0 # error code pushl $18 # vector jmp int_handler vec19: pushl $0 # error code pushl $19 # vector jmp int_handler # The common interrupt handler code. Pass the register state to the # application interrupt handler. int_handler: pushl %eax pushl %ebx pushl %ecx pushl %edx pushl %edi pushl %esi pushl %ebp # original stack pointer leal 48(%esp), %eax pushl %eax pushl %ds pushl %es pushl %ss pushl %esp # pointer to trap regs struct on the stack call interrupt addl $20, %esp popl %ebp popl %esi popl %edi popl %edx popl %ecx popl %ebx popl %eax addl $8, %esp iret # The interrupt descriptor table. .align 4 .word 0 # for alignment idt_descr: .word idt_end - idt - 1 # size .long 0 # addr: filled in at run time idt: .fill NUM_INT_VEC, 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 .long 0 # addr: filled in at run time .align 4 .globl gdt gdt: .quad 0x0000000000000000 # NULL descriptor .quad 0x0000000000000000 # not used .quad 0x00cf9b000000ffff # 0x10 main 4gb code at 0x000000 .quad 0x00cf93000000ffff # 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 # 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) 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. 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 .globl boot_params_addr boot_params_addr: .long 0 first_boot: .long 1 .previous # Stacks. .bss .align 16 bsp_stack_base: . = . + BSP_STACK_SIZE bsp_stack_top: ap_stacks_base: . = . + (AP_STACK_SIZE * MAX_APS) ap_stacks_top: startup_stack_base: . = . + 64 startup_stack_top: .previous pcmemtest-1.5/boot/startup64.S000066400000000000000000000256771413251666700163570ustar00rootroot00000000000000// 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 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 # Use a temporary stack until we pick the correct one. We can # safely use the high address, even if we are loaded low. movl $(HIGH_LOAD_ADDR + startup_stack_top - startup), %esp # Get the load address. movl 0x214(%esi), %ebx # Save the boot params pointer. movl %esi, (boot_params_addr - startup32)(%ebx) # 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: # Use a temporary stack until we pick the correct one. leaq startup_stack_top(%rip), %rsp # Pick the correct stack. xorq %rax, %rax call smp_my_pcpu_num movl $AP_STACK_SIZE, %edx mul %edx leaq bsp_stack_top(%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, (%edi) movl %edx, 4(%edi) shrq $32, %rdx movl %edx, 8(%edi) movl $0, 12(%edi) 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) jnz 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 # 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. int_handler: pushq %rax pushq %rbx pushq %rcx pushq %rdx pushq %rdi pushq %rsi pushq %rbp # original stack pointer leaq 96(%rsp), %rax pushq %rax xorq %rax, %rax movw %ds, %ax pushq %rax movw %es, %ax pushq %rax movw %ss, %ax pushq %rax movq %rsp, %rdi # pointer to trap regs struct on the stack call interrupt addq $32, %rsp popq %rbp popq %rsi popq %rdi popq %rdx popq %rcx popq %rbx popq %rax addq $16, %rsp 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) 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 .globl boot_params_addr boot_params_addr: .quad 0 first_boot: .long 1 .previous # Stacks. .bss .align 16 bsp_stack_base: . = . + BSP_STACK_SIZE bsp_stack_top: ap_stacks_base: . = . + (AP_STACK_SIZE * MAX_APS) ap_stacks_top: startup_stack_base: . = . + 64 startup_stack_top: .previous pcmemtest-1.5/build32/000077500000000000000000000000001413251666700146375ustar00rootroot00000000000000pcmemtest-1.5/build32/Makefile000066400000000000000000000106241413251666700163020ustar00rootroot00000000000000AS = as -32 CC = gcc CFLAGS = -std=c11 -Wall -Wextra -Wshadow -m32 -march=i586 -fpic -fno-builtin \ -ffreestanding -fomit-frame-pointer -fno-stack-protector INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app SYS_OBJS = system/cpuid.o \ system/cpuinfo.o \ system/font.o \ system/hwctrl.o \ system/keyboard.o \ system/pci.o \ system/pmem.o \ system/reloc.o \ system/screen.o \ system/smp.o \ system/temperature.o \ system/vmem.o LIB_OBJS = lib/barrier.o \ lib/ctype.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) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin memtest.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) boot/%.o: boot/%.s $(AS) $< -o $@ boot/startup.s: ../boot/startup32.S ../boot/boot.h @mkdir -p boot $(CC) -m32 -E -traditional -I../boot -o $@ $< boot/%.s: ../boot/%.S ../boot/boot.h @mkdir -p boot $(CC) -m32 -E -traditional -I../boot -o $@ $< boot/efisetup.o: ../boot/efisetup.c @mkdir -p boot $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/reloc.o: ../system/reloc32.c @mkdir -p system $(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/%.o: ../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) lib/%.o: ../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) tests/%.o: ../tests/%.c @mkdir -p tests $(CC) -c $(CFLAGS) -O3 $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/%.o: ../app/%.c @mkdir -p app $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) # 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) ldscripts/memtest_shared.lds Makefile $(LD) --warn-constructors --warn-common -static -T ldscripts/memtest_shared.lds -o $@ $(OBJS) && \ $(LD) -shared -Bsymbolic -T ldscripts/memtest_shared.lds -o $@ $(OBJS) memtest_shared.bin: memtest_shared objcopy -O binary $< memtest_shared.bin 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 :: iso: memtest.mbr floppy.img esp.img @mkdir -p iso/boot cp floppy.img iso/boot/floppy.img xorrisofs -pad -R -J -volid PCMemTest32 -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 clean: rm -rf boot system lib tests app *.img *.iso memtest* iso pcmemtest-1.5/build32/ldscripts/000077500000000000000000000000001413251666700166465ustar00rootroot00000000000000pcmemtest-1.5/build32/ldscripts/memtest_bin.lds000066400000000000000000000004561413251666700216650ustar00rootroot00000000000000OUTPUT_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; } pcmemtest-1.5/build32/ldscripts/memtest_efi.lds000066400000000000000000000005561413251666700216610ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(i386) ENTRY(boot); SECTIONS { . = 0; .header : { *(.header) } .setup : { *(.setup) } . = ALIGN(512); .text : { _text_start = . ; *(.data) . = ALIGN(512); _text_end = . ; } /DISCARD/ : { *(*) } _text_size = (_text_end - _text_start); _sys_size = _text_size >> 4; _init_size = _text_size + _bss_size; } pcmemtest-1.5/build32/ldscripts/memtest_mbr.lds000066400000000000000000000003461413251666700216730ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH("i386") ENTRY(boot); SECTIONS { . = 0; .mbr : { *(.mbr) } .memtest (NOLOAD) : { _start = . ; *(.data) _end = . ; } /DISCARD/ : { *(*) } _sys_size = (_end - _start + 15) >> 4; } pcmemtest-1.5/build32/ldscripts/memtest_shared.lds000066400000000000000000000016171413251666700223630ustar00rootroot00000000000000OUTPUT_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) /* _end must be at least 256 byte aligned */ . = ALIGN(256); _end = .; } /DISCARD/ : { *(*) } } pcmemtest-1.5/build64/000077500000000000000000000000001413251666700146445ustar00rootroot00000000000000pcmemtest-1.5/build64/Makefile000066400000000000000000000156771413251666700163240ustar00rootroot00000000000000AS = as -64 CC = gcc CFLAGS = -std=c11 -Wall -Wextra -Wshadow -m64 -march=x86-64 -mno-mmx -mno-sse -mno-sse2 \ -fpic -fno-builtin -ffreestanding -fomit-frame-pointer -fno-stack-protector INC_DIRS = -I../boot -I../system -I../lib -I../tests -I../app SYS_OBJS = system/cpuid.o \ system/cpuinfo.o \ system/font.o \ system/hwctrl.o \ system/keyboard.o \ system/pci.o \ system/pmem.o \ system/reloc.o \ system/screen.o \ system/smp.o \ system/temperature.o \ system/vmem.o LIB_OBJS = lib/barrier.o \ lib/ctype.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) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin memtest.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) boot/%.o: boot/%.s $(AS) $< -o $@ boot/startup.s: ../boot/startup64.S ../boot/boot.h @mkdir -p boot $(CC) -E -traditional -I../boot -o $@ $< boot/%.s: ../boot/%.S ../boot/boot.h @mkdir -p boot $(CC) -E -traditional -I../boot -o $@ $< boot/efisetup.o: ../boot/efisetup.c @mkdir -p boot $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/reloc.o: ../system/reloc64.c @mkdir -p system $(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) system/%.o: ../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) lib/%.o: ../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) tests/%.o: ../tests/%.c @mkdir -p tests $(CC) -c $(CFLAGS) -O3 $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) app/%.o: ../app/%.c @mkdir -p app $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) # 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) ldscripts/memtest_shared.lds Makefile $(LD) --warn-constructors --warn-common -static -T ldscripts/memtest_shared.lds -o $@ $(OBJS) && \ $(LD) -shared -Bsymbolic -T ldscripts/memtest_shared.lds -o $@ $(OBJS) memtest_shared.bin: memtest_shared objcopy -O binary $< memtest_shared.bin 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 PCMemTest64 -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 iso: memtest.iso clean: rm -rf boot system lib tests app *.img *.iso memtest* iso grub-* # grub-memtest.iso can be used for testing the various different boot modes, # using GRUB as an intermediate bootloader. 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_FONT_DIR ?= /usr/share/grub GRUB_LIB_DIR ?= /usr/lib/grub GRUB_MKIMAGE ?= grub2-mkimage GRUB_MODULES = iso9660 fat part_msdos 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-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/pcmemtest cp grub-bootx64.efi grub-iso/EFI/BOOT/bootx64.efi cp grub/grub-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 MEMTEST-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-legacy.cfg grub-esp.img @mkdir -p grub-iso/boot/grub/i386-pc grub-iso/boot/grub/fonts cp memtest.bin grub-iso/boot/pcmemtest cp grub-eltorito.img grub-iso/boot/eltorito.img cp grub/grub-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 PCMemTest64 -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 pcmemtest-1.5/build64/grub/000077500000000000000000000000001413251666700156035ustar00rootroot00000000000000pcmemtest-1.5/build64/grub/grub-efi.cfg000066400000000000000000000007261413251666700177710ustar00rootroot00000000000000if loadfont unicode ; then set gfxmode=1024x768,800x600,auto set gfxpayload=800x600,1024x768 terminal_output gfxterm fi set default=0 set timeout=-1 insmod linux insmod linuxefi insmod linux32 menuentry "Start PCMemTest using 'linux' command" { linux /EFI/BOOT/pcmemtest } menuentry "Start PCMemTest using 'linuxefi' command" { linuxefi /EFI/BOOT/pcmemtest } menuentry "Start PCMemTest using 'linux32' command" { linux32 /EFI/BOOT/pcmemtest } pcmemtest-1.5/build64/grub/grub-legacy.cfg000066400000000000000000000007071413251666700204710ustar00rootroot00000000000000if loadfont unicode ; then set gfxmode=1024x768,800x600,auto set gfxpayload=800x600,1024x768 terminal_output gfxterm fi set default=0 set timeout=-1 insmod linux insmod linux16 insmod linux32 menuentry "Start PCMemTest using 'linux' command" { linux /boot/pcmemtest } menuentry "Start PCMemTest using 'linux16' command" { linux16 /boot/pcmemtest } menuentry "Start PCMemTest using 'linux32' command" { linux32 /boot/pcmemtest } pcmemtest-1.5/build64/ldscripts/000077500000000000000000000000001413251666700166535ustar00rootroot00000000000000pcmemtest-1.5/build64/ldscripts/memtest_bin.lds000066400000000000000000000004631413251666700216700ustar00rootroot00000000000000OUTPUT_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; } pcmemtest-1.5/build64/ldscripts/memtest_efi.lds000066400000000000000000000005651413251666700216660ustar00rootroot00000000000000OUTPUT_FORMAT("binary") OUTPUT_ARCH(i386:x86-64) ENTRY(boot); SECTIONS { . = 0; .header : { *(.header) } .setup : { *(.setup) } . = ALIGN(512); .text : { _text_start = . ; *(.data) . = ALIGN(512); _text_end = . ; } /DISCARD/ : { *(*) } _text_size = (_text_end - _text_start); _sys_size = _text_size >> 4; _init_size = _text_size + _bss_size; } pcmemtest-1.5/build64/ldscripts/memtest_mbr.lds000066400000000000000000000003531413251666700216760ustar00rootroot00000000000000OUTPUT_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; } pcmemtest-1.5/build64/ldscripts/memtest_shared.lds000066400000000000000000000016471413251666700223730ustar00rootroot00000000000000OUTPUT_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) /* _end must be at least 256 byte aligned */ . = ALIGN(256); _end = .; } /DISCARD/ : { *(*) } } pcmemtest-1.5/lib/000077500000000000000000000000001413251666700141415ustar00rootroot00000000000000pcmemtest-1.5/lib/barrier.c000066400000000000000000000034161413251666700157370ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ smp.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // ------------------------------------------------ // smp.c - MemTest-86 Version 3.5 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include "barrier.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void barrier_init(barrier_t *barrier, int num_threads) { barrier->num_threads = num_threads; barrier->count = num_threads; spin_unlock(&barrier->lock); spin_unlock(&barrier->st1); spin_unlock(&barrier->st2); spin_lock(&barrier->st2); } void barrier_wait(barrier_t *barrier) { if (barrier == NULL || barrier->num_threads < 2) { return; } spin_wait(&barrier->st1); // Wait if the barrier is active. spin_lock(&barrier->lock); // Get lock for barrier struct. if (--barrier->count == 0) { // Last process? spin_lock(&barrier->st1); // Hold up any processes re-entering. spin_unlock(&barrier->st2); // Release the other processes. barrier->count++; spin_unlock(&barrier->lock); } else { spin_unlock(&barrier->lock); spin_wait(&barrier->st2); // Wait for peers to arrive. spin_lock(&barrier->lock); if (++barrier->count == barrier->num_threads) { spin_unlock(&barrier->st1); spin_lock(&barrier->st2); } spin_unlock(&barrier->lock); } } pcmemtest-1.5/lib/barrier.h000066400000000000000000000012001413251666700157310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef BARRIER_H #define BARRIER_H /* * Provides a barrier synchronisation primitive. * * Copyright (C) 2020 Martin Whitaker. */ #include "spinlock.h" /* * A barrier object. */ typedef struct { int num_threads; volatile int count; spinlock_t lock; spinlock_t st1; spinlock_t st2; } barrier_t; /* * Initialises the barrier to block the specified number of threads. */ void barrier_init(barrier_t *barrier, int num_threads); /* * Waits for all threads to arrive at the barrier. */ void barrier_wait(barrier_t *barrier); #endif // BARRIER_H pcmemtest-1.5/lib/ctype.c000066400000000000000000000010451413251666700154310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. #include "ctype.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int toupper(int c) { if (c >= 'a' && c <= 'z') { return c + 'A' -'a'; } else { return c; } } int isdigit(int c) { return c >= '0' && c <= '9'; } int isxdigit(int c) { return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F'); } pcmemtest-1.5/lib/ctype.h000066400000000000000000000011411413251666700154330ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef CTYPE_H #define CTYPE_H /* * Provides a subset of the functions normally provided by . * * Copyright (C) 2020 Martin Whitaker. */ /* * If c is a lower-case letter, returns its upper-case equivalent, otherwise * returns c. Assumes c is an ASCII character. */ int toupper(int c); /* * Returns 1 if c is a decimal digit, otherwise returns 0. Assumes c is an * ASCII character. */ int isdigit(int c); /* * Returns 1 if c is a hexadecimal digit, otherwise returns 0. Assumes c is an * ASCII character. */ int isxdigit(int c); #endif // CTYPE_H pcmemtest-1.5/lib/div64.c000066400000000000000000000005731413251666700152460ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. #include // Implement the 64-bit division primitive. This is only needed for 32-bit // builds. We don't use this for anything critical, so a floating-point // approximation is good enough. uint64_t __udivdi3(uint64_t num, uint64_t den) { return (uint64_t)((double)num / (double)den); } pcmemtest-1.5/lib/print.c000066400000000000000000000163671413251666700154560ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. #include #include "screen.h" #include "string.h" #include "print.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define BUFFER_SIZE 64 //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static int int_to_dec_str(char buffer[], int value, int min_length, int max_length) { bool negative = (value < 0); if (negative) { value = -value; if (min_length > 1) { min_length--; } max_length--; } int length = 0; while (length < min_length || (value > 0 && length < max_length)) { buffer[length++] = '0' + (value % 10); value /= 10; } if (negative) { buffer[length++] = '-'; } return length; } static int uint_to_dec_str(char buffer[], uintptr_t value, int min_length, int max_length) { int length = 0; while (length < min_length || (value > 0 && length < max_length)) { buffer[length++] = '0' + (value % 10); value /= 10; } return length; } static int uint_to_hex_str(char buffer[], uintptr_t value, int min_length, int max_length) { int length = 0; while (length < min_length || (value > 0 && length < max_length) ){ int digit = value % 16; if (digit < 10) { buffer[length++] = '0' + digit; } else { buffer[length++] = 'a' + digit - 10; } value /= 16; } return length; } static int min_str_length(int field_length, bool pad) { return (field_length > 0 && pad) ? field_length : 1; } static int print_in_field(int row, int col, const char buffer[], int buffer_length, int field_length, bool left) { bool reversed = false; if (buffer_length < 0) { buffer_length = -buffer_length; reversed = true; } if (!left) { while (field_length > buffer_length) { print_char(row, col++, ' '); field_length--; } } if (reversed) { for (int i = buffer_length - 1; i >= 0; i--) { print_char(row, col++, buffer[i]); } } else { for (int i = 0; i < buffer_length; i++) { print_char(row, col++, buffer[i]); } } if (left) { while (field_length > buffer_length) { print_char(row, col++, ' '); field_length--; } } return col; } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int printc(int row, int col, const char c) { print_char(row, col++, c); return col; } int prints(int row, int col, const char *str) { while (*str) { print_char(row, col++, *str++); } return col; } int printi(int row, int col, int value, int field_length, bool pad, bool left) { char buffer[BUFFER_SIZE]; int length = int_to_dec_str(buffer, value, min_str_length(field_length, pad), BUFFER_SIZE); return print_in_field(row, col, buffer, -length, field_length, left); } int printu(int row, int col, uintptr_t value, int field_length, bool pad, bool left) { char buffer[BUFFER_SIZE]; int length = uint_to_dec_str(buffer, value, min_str_length(field_length, pad), BUFFER_SIZE); return print_in_field(row, col, buffer, -length, field_length, left); } int printx(int row, int col, uintptr_t value, int field_length, bool pad, bool left) { char buffer[BUFFER_SIZE]; int length = uint_to_hex_str(buffer, value, min_str_length(field_length, pad), BUFFER_SIZE); return print_in_field(row, col, buffer, -length, field_length, left); } int printk(int row, int col, uintptr_t value, int field_length, bool pad, bool left) { static const char suffix[4] = { 'K', 'M', 'G', 'T' }; int scale = 0; int fract = 0; while (value >= 1024 && scale < (int)(sizeof(suffix) - 1)) { fract = value % 1024; value /= 1024; scale++; } int whole_length = field_length > 1 ? field_length - 1 : 0; int fract_length = 0; if (fract > 0) { if (value < 10) { whole_length = field_length > 4 ? field_length - 4 : 0; fract = (100 * fract) / 1024; if (fract > 0) { if (fract % 10) { fract_length = 2; } else { fract_length = 1; fract /= 10; } } } else if (value < 100) { whole_length = field_length > 3 ? field_length - 3 : 0; fract = (100 * fract) / (10 * 1024); if (fract > 0) { fract_length = 1; } } } char buffer[BUFFER_SIZE]; int length = 0; buffer[length++] = suffix[scale]; if (fract_length > 0) { length += int_to_dec_str(&buffer[length], fract, fract_length, fract_length); buffer[length++] = '.'; } length += uint_to_dec_str(&buffer[length], value, min_str_length(whole_length, pad), BUFFER_SIZE - length); return print_in_field(row, col, buffer, -length, field_length, left); } int printf(int row, int col, const char *fmt, ...) { va_list args; va_start(args, fmt); int end_col = vprintf(row, col, fmt, args); va_end(args); return end_col; } int vprintf(int row, int col, const char *fmt, va_list args) { while (*fmt) { if (*fmt != '%') { print_char(row, col++, *fmt++); continue; } fmt++; if (*fmt == '%') { print_char(row, col++, *fmt++); continue; } bool pad = false; bool left = false; int length = 0; if (*fmt == '-') { left = true; fmt++; } if (*fmt == '0') { pad = !left; fmt++; } if (*fmt == '*') { length = va_arg(args, int); if (length < 0) { length = -length; left = true; } fmt++; } else { while (*fmt >= '0' && *fmt <= '9') { length = 10 * length + *fmt - '0'; fmt++; } } switch (*fmt) { case 'c': { char buffer[1]; buffer[0] = va_arg(args, int); col = print_in_field(row, col, buffer, 1, length, left); } break; case 's': { const char *str = va_arg(args, char *); col = print_in_field(row, col, str, strlen(str), length, left); } break; case 'i': col = printi(row, col, va_arg(args, int), length, pad, left); break; case 'u': col = printu(row, col, va_arg(args, uintptr_t), length, pad, left); break; case 'x': col = printx(row, col, va_arg(args, uintptr_t), length, pad, left); break; case 'k': col = printk(row, col, va_arg(args, uintptr_t), length, pad, left); break; } fmt++; } return col; } pcmemtest-1.5/lib/print.h000066400000000000000000000057131413251666700154540ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef PRINT_H #define PRINT_H /* * Provides functions to print strings and formatted values to the screen. * * Copyright (C) 2020 Martin Whitaker. */ #include #include #include /* * Prints a single character on screen at location (row,col) and returns col+1. */ int printc(int row, int col, char c); /* * Prints a string on screen starting at location (row,col) and returns the * next column after the string. */ int prints(int row, int col, const char *str); /* * Prints a signed decimal number on screen starting at location (row,col) in * a field of at least length characters, optionally padding the number with * leading zeros, and optionally left-justifying instead of right-justifying * in the field. Returns the next column after the formatted number. */ int printi(int row, int col, int value, int length, bool pad, bool left); /* * Prints an unsigned decimal number on screen starting at location (row,col) * in a field of at least length characters, optionally padding the number with * leading zeros, and optionally left-justifying instead of right-justifying in * the field. Returns the next column after the formatted number. */ int printu(int row, int col, uintptr_t value, int length, bool pad, bool left); /* * Prints an unsigned hexadecimal number on screen starting at location (row,col) * in a field of at least length characters, optionally padding the number with * leading zeros, and optionally left-justifying instead of right-justifying in * the field. Returns the next column after the formatted number. */ int printx(int row, int col, uintptr_t value, int length, bool pad, bool left); /* * Prints a K value on screen starting at location (row,col) in a field of * at least length characters, optionally padding the number with leading zeros, * and optionally left-justifying instead of right-justifying in the field. The * value is shown to 3 significant figures in the nearest K/M/G/T units. Returns * the next column after the formatted value. */ int printk(int row, int col, uintptr_t value, int length, bool pad, bool left); /* * Emulates the standard printf function. Printing starts at location (row,col). * * The conversion flags supported are: * - left justify * 0 pad with leading zeros * * The conversion specifiers supported are: * c character (int type) * s string (char* type) * i signed decimal integer (int type) * u unsigned decimal integer (uintptr_t type) * x unsigned hexadecimal integer (uintptr_t type) * k K value (scaled to K/M/G/T) (uintptr_t type) * * The only other conversion option supported is the minimum field width. This * may be either a literal value or '*'. * * Returns the next column after the formatted string. */ int printf(int row, int col, const char *fmt, ...); /* * The alternate form of printf. */ int vprintf(int row, int col, const char *fmt, va_list args); #endif // PRINT_H pcmemtest-1.5/lib/read.c000066400000000000000000000062151413251666700152240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an 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 #include "ctype.h" #include "keyboard.h" #include "print.h" #include "unistd.h" #include "read.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ uintptr_t read_value(int row, int col, int field_width, int shift) { char buffer[1 + field_width]; for (int i = 0; i < field_width; i++ ) { buffer[i] = ' '; } buffer[field_width] = '\0'; int n = 0; int base = 10; bool done = false; bool got_suffix = false; while (!done) { char c = get_key(); switch (c) { case '\n': if (n > 0) { done = true; } break; case '\b': if (n > 0) { got_suffix = false; buffer[--n] = ' '; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (n < field_width && base >= 10 && !got_suffix) { buffer[n] = c; } break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': if (n < field_width && base >= 16 && !got_suffix) { buffer[n] = c; } break; case 'k': case 'p': case 'm': case 'g': case 't': if (n > 0 && n < field_width && buffer[n-1] != 'x') { got_suffix = true; buffer[n] = toupper(c); } break; case 'x': /* Only allow 'x' after an initial 0 */ if (n == 1 && n < field_width && buffer[0] == '0') { buffer[n] = 'x'; } break; default: usleep(1000); break; } if (n < field_width && buffer[n] != ' ') { n++; } prints(row, col, buffer); if (buffer[0] == '0' && buffer[1] == 'x') { base = 16; } else { base = 10; } } if (got_suffix) { switch (buffer[n-1]) { case 'T': /* tera */ shift += 40; n--; break; case 'G': /* gig */ shift += 30; n--; break; case 'M': /* meg */ shift += 20; n--; break; case 'P': /* page */ shift += 12; n--; break; case 'K': /* kilo */ shift += 10; n--; break; } } uintptr_t value = 0; for (int i = (base == 16) ? 2 : 0; i < n; i++) { value *= base; if (buffer[i] >= 'a') { value += buffer[i] - 'a'; } else { value += buffer[i] - '0'; } } return shift < 0 ? value >> shift : value << shift; } pcmemtest-1.5/lib/read.h000066400000000000000000000013301413251666700152220ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef READ_H #define READ_H /* * Provides a function to read a numeric value. * * Copyright (C) 2020 Martin Whitaker. */ #include /* * Returns an unsigned numeric value entered on the keyboard. Echoes the * input to the display field located at (row,col), limiting it to field_width * characters. If the entered value is prefixed by "0x", assumes base 16, * otherwise assumes base 10. If the value is suffixed by 'K', 'P', 'M', * 'G', or 'T', the returned value will be scaled by 2^10, 2^12, 2^20, * 2^30, or 2^40 accordingly. The returned value will also be scaled by * 2^shift. */ uintptr_t read_value(int x, int y, int field_width, int shift); #endif // READ_H pcmemtest-1.5/lib/spinlock.h000066400000000000000000000022641413251666700161400ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef SPINLOCK_H #define SPINLOCK_H /* * Provides a lightweight mutex synchronisation primitive. * * Copyright (C) 2020 Martin Whitaker. */ #include /* * A mutex object. Use spin_unlock() to initialise prior to first use. */ typedef volatile bool spinlock_t; /* * Spins until the mutex is unlocked. */ static inline void spin_wait(spinlock_t *lock) { if (lock) { while (*lock) { __builtin_ia32_pause(); for (volatile int i = 0; i < 100; i++) { } // this reduces power consumption } } } /* * Spins until the mutex is unlocked, then locks the mutex. */ static inline void spin_lock(spinlock_t *lock) { if (lock) { while (!__sync_bool_compare_and_swap(lock, false, true)) { do { __builtin_ia32_pause(); for (volatile int i = 0; i < 100; i++) { } // this reduces power consumption } while (*lock); } __sync_synchronize(); } } /* * Unlocks the mutex. */ static inline void spin_unlock(spinlock_t *lock) { if (lock) { __sync_synchronize(); *lock = false; } } #endif // SPINLOCK_H pcmemtest-1.5/lib/string.c000066400000000000000000000042721413251666700156200ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an 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 //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *src1 = s1, *src2 = s2; for (size_t i = 0; i < n; i++) { if (src1[i] != src2[i]) { return (int)src1[i] - (int)src2[i]; } } return 0; } void *memcpy(void *dest, const void *src, size_t n) { char *d = (char *)dest, *s = (char *)src; for (size_t i = 0; i < n; i++) { d[i] = s[i]; } return dest; } void *memmove(void *dest, const void *src, size_t n) { char *d = (char *)dest, *s = (char *)src; if (n > 0) { if (dest < src) { for (size_t i = 0; i < n; i++) { d[i] = s[i]; } } if (dest > src) { size_t i = n; do { i--; d[i] = s[i]; } while (i > 0); } } return dest; } void *memset(void *s, int c, size_t n) { char *d = (char *)s; for (size_t i = 0; i < n; i++) { d[i] = c; } return s; } size_t strlen(const char *s) { size_t len = 0; while (*s++) { len++; } return len; } int strncmp(const char *s1, const char *s2, size_t n) { for (size_t i = 0; i < n; i++) { if (s1[i] != s2[i]) { return (int)s1[i] - (int)s2[i]; } if (s1[i] == '\0') { return 0; } } return 0; } char *strstr(const char *haystack, const char *needle) { size_t haystack_len = strlen(haystack); size_t needle_len = strlen(needle); size_t max_idx = haystack_len - needle_len; for (size_t idx = 0; idx <= max_idx; idx++) { if (memcmp(haystack + idx, needle, needle_len) == 0) { return (char *)haystack + idx; } } return NULL; } pcmemtest-1.5/lib/string.h000066400000000000000000000034501413251666700156220ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef STRING_H #define STRING_H /* * Provides a subset of the functions normally provided by . * * Copyright (C) 2020 Martin Whitaker. */ #include /* * Compares the first n bytes of the memory areas pointed to by s1 and s2 * and returns 0 if all bytes are the same or the numerical difference * between the first mismatching byte in s1 (interpreted as an unsigned * value) and the corresponding byte in s2. */ int memcmp(const void *s1, const void *s2, size_t n); /* * Copies n bytes from the memory area pointed to by src to the memory area * pointed to by dest and returns a pointer to dest. The memory areas must * not overlap. */ void *memcpy(void *dst, const void *src, size_t n); /* * Copies n bytes from the memory area pointed to by src to the memory area * pointed to by dest and returns a pointer to dest. The memory areas may * overlap. */ void *memmove(void *dest, const void *src, size_t n); /* * Fills the first n bytes of the memory area pointed to by s with the byte * value c. */ void *memset(void *s, int c, size_t n); /* * Returns the string length, excluding the terminating null character. */ size_t strlen(const char *s); /* * Compares at most the first n characters in the strings s1 and s2 and * returns 0 if all characters are the same or the numerical difference * between the first mismatching character in s1 (interpreted as a signed * value) and the corresponding character in s2. */ int strncmp(const char *s1, const char *s2, size_t n); /* * Finds the first occurrence of the substring needle in the string haystack * and returns a pointer to the beginning of the located substring, or NULL * if the substring is not found. */ char *strstr(const char *haystack, const char *needle); #endif // STRING_H pcmemtest-1.5/lib/unistd.c000066400000000000000000000021001413251666700156040ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. #include #include "cpuinfo.h" #include "tsc.h" #include "unistd.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void usleep(unsigned int usec) { if (clks_per_msec > 0) { // If we've measured the CPU speed, we know the TSC is available. uint64_t cycles = ((uint64_t)usec * clks_per_msec) / 1000; uint64_t t0 = get_tsc(); do { __builtin_ia32_pause(); for (volatile int i = 0; i < 100; i++) { } // this reduces power consumption } while ((get_tsc() - t0) < cycles); } else { // This will be highly inaccurate, but should give at least the requested delay. volatile uint64_t count = (uint64_t)usec * 1000; while (count > 0) { count--; } } } void sleep(unsigned int sec) { while (sec > 0) { usleep(1000000); sec--; } } pcmemtest-1.5/lib/unistd.h000066400000000000000000000005561413251666700156260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef UNISTD_H #define UNISTD_H /* * Provides a subset of the functions normally provided by . * * Copyright (C) 2020 Martin Whitaker. */ /* * Sleeps for at least usec microseconds. */ void usleep(unsigned int usec); /* * Sleeps for at least sec seconds. */ void sleep(unsigned int sec); #endif // UNISTD_H pcmemtest-1.5/system/000077500000000000000000000000001413251666700147175ustar00rootroot00000000000000pcmemtest-1.5/system/cache.h000066400000000000000000000027121413251666700161350ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef CACHE_H #define CACHE_H /* * Provides functions to enable/disable the CPU caches. * * Copyright (C) 2020 Martin Whitaker. */ /* * Disable the CPU caches. */ static inline void cache_off(void) { #ifdef __x86_64__ __asm__ __volatile__ ("\t" "movq %%cr0, %%rax \n\t" "orl $0x40000000, %%eax \n\t" /* Set CD */ "movq %%rax, %%cr0 \n\t" "wbinvd \n" : /* no outputs */ : /* no inputs */ : "rax" ); #else __asm__ __volatile__ ("\t" "movl %%cr0, %%eax \n\t" "orl $0x40000000, %%eax \n\t" /* Set CD */ "movl %%eax, %%cr0 \n\t" "wbinvd \n" : /* no outputs */ : /* no inputs */ : "eax" ); #endif } /* * Enable the CPU caches. */ static inline void cache_on(void) { #ifdef __x86_64__ __asm__ __volatile__ ("\t" "movq %%cr0, %%rax \n\t" "andl $0x9fffffff, %%eax \n\t" /* Clear CD and NW */ "movq %%rax, %%cr0 \n" : /* no outputs */ : /* no inputs */ : "rax" ); #else __asm__ __volatile__ ("\t" "movl %%cr0, %%eax \n\t" "andl $0x9fffffff, %%eax \n\t" /* Clear CD and NW */ "movl %%eax, %%cr0 \n" : /* no outputs */ : /* no inputs */ : "eax" ); #endif } #endif // CACHE_H pcmemtest-1.5/system/cpuid.c000066400000000000000000000072261413251666700161760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ cpuid.h // (original contained no copyright statement) #include #include "cpuid.h" //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ cpuid_info_t cpuid_info; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void cpuid_init(void) { uint32_t dummy[3]; char *p, *q; // Get the max standard cpuid & vendor ID. cpuid(0x0, 0, &cpuid_info.max_vcpuid, &cpuid_info.vendor_id.raw[0], &cpuid_info.vendor_id.raw[2], &cpuid_info.vendor_id.raw[1] ); cpuid_info.vendor_id.str[CPUID_VENDOR_STR_LENGTH - 1] = '\0'; // Get the processor family information & feature flags. if (cpuid_info.max_vcpuid >= 1) { cpuid(0x1, 0, &cpuid_info.version.raw, &cpuid_info.proc_info.raw, &cpuid_info.flags.raw[1], &cpuid_info.flags.raw[0] ); } // Get the digital thermal sensor & power management status bits. if (cpuid_info.max_vcpuid >= 6) { cpuid(0x6, 0, &cpuid_info.dts_pmp, &dummy[0], &dummy[1], &dummy[2] ); } // Get the max extended cpuid. cpuid(0x80000000, 0, &cpuid_info.max_xcpuid, &dummy[0], &dummy[1], &dummy[2] ); // Get extended feature flags, only save EDX. if (cpuid_info.max_xcpuid >= 0x80000001) { cpuid(0x80000001, 0, &dummy[0], &dummy[1], &dummy[2], &cpuid_info.flags.raw[2] ); } // Get the brand ID. if (cpuid_info.max_xcpuid >= 0x80000004) { cpuid(0x80000002, 0, &cpuid_info.brand_id.raw[0], &cpuid_info.brand_id.raw[1], &cpuid_info.brand_id.raw[2], &cpuid_info.brand_id.raw[3] ); cpuid(0x80000003, 0, &cpuid_info.brand_id.raw[4], &cpuid_info.brand_id.raw[5], &cpuid_info.brand_id.raw[6], &cpuid_info.brand_id.raw[7] ); cpuid(0x80000004, 0, &cpuid_info.brand_id.raw[8], &cpuid_info.brand_id.raw[9], &cpuid_info.brand_id.raw[10], &cpuid_info.brand_id.raw[11] ); cpuid_info.brand_id.str[CPUID_BRAND_STR_LENGTH - 1] = '\0'; } // Intel chips right-justify this string for some reason - undo that. p = q = &cpuid_info.brand_id.str[0]; while (*p == ' ') { p++; } if (p != q) { while (*p) { *q++ = *p++; } while (q <= &cpuid_info.brand_id.str[CPUID_BRAND_STR_LENGTH]) { *q++ = '\0'; } } // Get cache information. switch (cpuid_info.vendor_id.str[0]) { case 'A': // AMD Processors if (cpuid_info.max_xcpuid >= 0x80000005) { cpuid(0x80000005, 0, &dummy[0], &dummy[1], &cpuid_info.cache_info.raw[0], &cpuid_info.cache_info.raw[1] ); } if (cpuid_info.max_xcpuid >= 0x80000006) { cpuid(0x80000006, 0, &dummy[0], &dummy[1], &cpuid_info.cache_info.raw[2], &cpuid_info.cache_info.raw[3] ); } break; case 'G': // Intel Processors // No cpuid info to read. break; } } pcmemtest-1.5/system/cpuid.h000066400000000000000000000130261413251666700161760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef CPUID_H #define CPUID_H /* * Provides access to the CPUID information. * * Copyright (C) 2020 Martin Whitaker. * * Derived from memtest86+ cpuid.h * (original contained no copyright statement) */ #include /* * Structures that hold the collected CPUID information. */ typedef union { uint32_t raw; struct { uint32_t stepping : 4; uint32_t model : 4; uint32_t family : 4; uint32_t processorType : 2; uint32_t : 2; uint32_t extendedModel : 4; uint32_t extendedFamily : 8; uint32_t : 4; }; } cpuid_version_t; typedef union { uint32_t raw; struct { uint32_t brandIndex : 8; uint32_t cflushLineSize : 8; uint32_t logicalProcessorCount : 8; uint32_t apicID : 8; }; } cpuid_proc_info_t; typedef union { uint32_t raw[3]; struct { uint32_t fpu : 1; // EDX feature flags, bit 0 */ uint32_t vme : 1; uint32_t de : 1; uint32_t pse : 1; uint32_t rdtsc : 1; uint32_t msr : 1; uint32_t pae : 1; uint32_t mce : 1; uint32_t cx8 : 1; uint32_t apic : 1; uint32_t : 1; uint32_t sep : 1; uint32_t mtrr : 1; uint32_t pge : 1; uint32_t mca : 1; uint32_t cmov : 1; uint32_t pat : 1; uint32_t pse36 : 1; uint32_t psn : 1; uint32_t cflush : 1; uint32_t : 1; uint32_t ds : 1; uint32_t acpi : 1; uint32_t mmx : 1; uint32_t fxsr : 1; uint32_t sse : 1; uint32_t sse2 : 1; uint32_t ss : 1; uint32_t htt : 1; uint32_t tm : 1; uint32_t bit30 : 1; uint32_t pbe : 1; // EDX feature flags, bit 31 uint32_t sse3 : 1; // ECX feature flags, bit 0 uint32_t mulq : 1; uint32_t bit2 : 1; uint32_t mon : 1; uint32_t dscpl : 1; uint32_t vmx : 1; uint32_t smx : 1; uint32_t eist : 1; uint32_t tm2 : 1; uint32_t : 23; // ECX feature flags, bit 31 uint32_t : 29; // EDX extended feature flags, bit 0 uint32_t lm : 1; uint32_t : 2; // EDX extended feature flags, bit 31 }; } cpuid_feature_flags_t; #define CPUID_VENDOR_LENGTH 3 #define CPUID_VENDOR_STR_LENGTH (CPUID_VENDOR_LENGTH * sizeof(uint32_t) + 1) // includes space for null terminator typedef union { uint32_t raw[CPUID_VENDOR_LENGTH]; char str[CPUID_VENDOR_STR_LENGTH]; } cpuid_vendor_string_t; #define CPUID_BRAND_LENGTH 12 #define CPUID_BRAND_STR_LENGTH (CPUID_BRAND_LENGTH * sizeof(uint32_t) + 1) // includes space for null terminator typedef union { uint32_t raw[CPUID_BRAND_LENGTH]; char str[CPUID_BRAND_STR_LENGTH]; } cpuid_brand_string_t; typedef union { uint32_t raw[12]; struct { uint32_t : 24; uint32_t l1_i_size : 8; uint32_t : 24; uint32_t l1_d_size : 8; uint32_t : 16; uint32_t l2_size : 16; uint32_t : 18; uint32_t l3_size : 14; }; } cpuid_cache_info_t; typedef union { uint32_t raw; struct { uint32_t : 1; }; } cpuid_custom_features; typedef struct { uint32_t max_vcpuid; uint32_t max_xcpuid; uint32_t dts_pmp; cpuid_version_t version; cpuid_proc_info_t proc_info; cpuid_feature_flags_t flags; cpuid_vendor_string_t vendor_id; cpuid_brand_string_t brand_id; cpuid_cache_info_t cache_info; cpuid_custom_features custom; } cpuid_info_t; typedef union { uint32_t raw; struct { uint32_t ctype : 5; uint32_t level : 3; uint32_t is_self_initializing : 1; uint32_t is_fully_associative : 1; uint32_t reserved : 4; uint32_t num_threads_sharing : 12; uint32_t num_cores_on_die : 6; }; } cpuid4_eax_t; typedef union { uint32_t raw; struct { uint32_t coherency_line_size : 12; uint32_t physical_line_partition : 10; uint32_t ways_of_associativity : 10; }; } cpuid4_ebx_t; typedef union { uint32_t raw; struct { uint32_t number_of_sets : 32; }; } cpuid4_ecx_t; /* * The CPUID information collected by cpuid_init(); */ extern cpuid_info_t cpuid_info; /* * Reads the CPUID information and stores it in cpuid_info. */ void cpuid_init(void); /* * Executes the cpuid instruction. */ static inline void cpuid(uint32_t op, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { *eax = op; *ecx = count; __asm__ __volatile__ ("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (*eax), "2" (*ecx) ); } #endif // CPUID_H pcmemtest-1.5/system/cpuinfo.c000066400000000000000000000547201413251666700165360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ init.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // ------------------------------------------------ // init.c - MemTest-86 Version 3.6 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include #include "cpuid.h" #include "io.h" #include "tsc.h" #include "cpuinfo.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define PIT_TICKS_50mS 59659 // PIT clock is 1.193182MHz //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ const char *cpu_model = NULL; uint32_t imc_type = 0; int l1_cache = 0; int l2_cache = 0; int l3_cache = 0; bool no_temperature = false; uint32_t clks_per_msec = 0; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static void determine_cache_size() { switch (cpuid_info.vendor_id.str[0]) { case 'A': // AMD Processors (easy!) l1_cache = cpuid_info.cache_info.l1_d_size; l2_cache = cpuid_info.cache_info.l2_size; l3_cache = cpuid_info.cache_info.l3_size; l3_cache *= 512; break; case 'G': // Intel Processors l1_cache = 0; l2_cache = 0; l3_cache = 0; // Use CPUID(4) if it is available. if (cpuid_info.max_vcpuid > 3) { cpuid4_eax_t eax; cpuid4_ebx_t ebx; cpuid4_ecx_t ecx; uint32_t dummy; // Loop through the cache leaves. int i = 0; do { cpuid(4, i, &eax.raw, &ebx.raw, &ecx.raw, &dummy); // Check for a valid cache type... if (eax.ctype == 1 || eax.ctype == 3) { // Compute the cache size int size = (ecx.number_of_sets + 1) * (ebx.coherency_line_size + 1) * (ebx.physical_line_partition + 1) * (ebx.ways_of_associativity + 1); size /= 1024; switch (eax.level) { case 1: l1_cache += size; break; case 2: l2_cache += size; break; case 3: l3_cache += size; break; default: break; } } i++; } while (eax.ctype != 0); return; } // No CPUID(4) so we use the older CPUID(2) method. uint32_t v[4]; uint8_t *dp = (uint8_t *)v; int i = 0; do { cpuid(2, 0, &v[0], &v[1], &v[2], &v[3]); // If bit 31 is set, this is an unknown format. for (int j = 0; j < 4; j++) { if (v[j] & (1 << 31)) { v[j] = 0; } } // Byte 0 is level count, not a descriptor. for (int j = 1; j < 16; j++) { switch (dp[j]) { case 0x6: case 0xa: case 0x66: l1_cache += 8; break; case 0x8: case 0xc: case 0xd: case 0x60: case 0x67: l1_cache += 16; break; case 0xe: l1_cache += 24; break; case 0x9: case 0x2c: case 0x30: case 0x68: l1_cache += 32; break; case 0x39: case 0x3b: case 0x41: case 0x79: l2_cache += 128; break; case 0x3a: l2_cache += 192; break; case 0x21: case 0x3c: case 0x3f: case 0x42: case 0x7a: case 0x82: l2_cache += 256; break; case 0x3d: l2_cache += 384; break; case 0x3e: case 0x43: case 0x7b: case 0x7f: case 0x80: case 0x83: case 0x86: l2_cache += 512; break; case 0x44: case 0x78: case 0x7c: case 0x84: case 0x87: l2_cache += 1024; break; case 0x45: case 0x7d: case 0x85: l2_cache += 2048; break; case 0x48: l2_cache += 3072; break; case 0x4e: l2_cache += 6144; break; case 0x23: case 0xd0: l3_cache += 512; break; case 0xd1: case 0xd6: l3_cache += 1024; break; case 0x25: case 0xd2: case 0xd7: case 0xdc: case 0xe2: l3_cache += 2048; break; case 0x29: case 0x46: case 0x49: case 0xd8: case 0xdd: case 0xe3: l3_cache += 4096; break; case 0x4a: l3_cache += 6144; break; case 0x47: case 0x4b: case 0xde: case 0xe4: l3_cache += 8192; break; case 0x4c: case 0xea: l3_cache += 12288; break; case 0x4d: l3_cache += 16384; break; case 0xeb: l3_cache += 18432; break; case 0xec: l3_cache += 24576; break; default: break; } } } while (++i < dp[0]); break; default: break; } } static void determine_imc(void) { // Check AMD IMC if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF) { switch (cpuid_info.version.extendedFamily) { case 0x0: imc_type = 0x0100; // Old K8 break; case 0x1: case 0x2: imc_type = 0x0101; // K10 (Family 10h & 11h) break; case 0x3: imc_type = 0x0102; // A-Series APU (Family 12h) break; case 0x5: imc_type = 0x0103; // C- / E- / Z- Series APU (Family 14h) break; case 0x6: imc_type = 0x0104; // FX Series (Family 15h) break; case 0x7: imc_type = 0x0105; // Kabini & related (Family 16h) break; default: break; } return; } // Check Intel IMC if (cpuid_info.vendor_id.str[0] == 'G' && cpuid_info.version.family == 6 && cpuid_info.version.extendedModel) { switch (cpuid_info.version.model) { case 0x5: switch (cpuid_info.version.extendedModel) { case 2: imc_type = 0x0003; // Core i3/i5 1st Gen 45 nm (NHM) break; case 3: no_temperature = true; // Atom Clover Trail break; case 4: imc_type = 0x0007; // HSW-ULT break; default: break; } break; case 0x6: if (cpuid_info.version.extendedModel == 3) { imc_type = 0x0009; // Atom Cedar Trail no_temperature = true; } break; case 0x7: if (cpuid_info.version.extendedModel == 3) { imc_type = 0x000A; // Atom Bay Trail } break; case 0xA: switch (cpuid_info.version.extendedModel) { case 0x1: imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHME) break; case 0x2: imc_type = 0x0004; // Core 2nd Gen (SNB) break; case 0x3: imc_type = 0x0006; // Core 3nd Gen (IVB) break; default: break; } break; case 0xC: switch (cpuid_info.version.extendedModel) { case 0x1: if (cpuid_info.version.stepping > 9) { imc_type = 0x0008; // Atom PineView } no_temperature = true; break; case 0x2: imc_type = 0x0002; // Core i7 1st Gen 32 nm (WMR) break; case 0x3: imc_type = 0x0007; // Core 4nd Gen (HSW) break; default: break; } break; case 0xD: imc_type = 0x0005; // SNB-E break; case 0xE: imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHM) break; default: break; } return; } } static void determine_cpu_model(void) { // If we can get a brand string use it, and we are done. if (cpuid_info.max_xcpuid >= 0x80000004) { cpu_model = cpuid_info.brand_id.str; determine_imc(); return; } // The brand string is not available so we need to figure out CPU what we have. switch (cpuid_info.vendor_id.str[0]) { case 'A': // AMD Processors switch (cpuid_info.version.family) { case 4: switch (cpuid_info.version.model) { case 3: cpu_model = "AMD 486DX2"; break; case 7: cpu_model = "AMD 486DX2-WB"; break; case 8: cpu_model = "AMD 486DX4"; break; case 9: cpu_model = "AMD 486DX4-WB"; break; case 14: cpu_model = "AMD 5x86-WT"; break; case 15: cpu_model = "AMD 5x86-WB"; break; default: break; } break; case 5: switch (cpuid_info.version.model) { case 0: case 1: case 2: case 3: cpu_model = "AMD K5"; l1_cache = 8; break; case 6: case 7: cpu_model = "AMD K6"; break; case 8: cpu_model = "AMD K6-2"; break; case 9: cpu_model = "AMD K6-III"; break; case 13: cpu_model = "AMD K6-III+"; break; default: break; } break; case 6: switch (cpuid_info.version.model) { case 1: cpu_model = "AMD Athlon (0.25)"; break; case 2: case 4: cpu_model = "AMD Athlon (0.18)"; break; case 6: if (l2_cache == 64) { cpu_model = "AMD Duron (0.18)"; } else { cpu_model = "Athlon XP (0.18)"; } break; case 8: case 10: if (l2_cache == 64) { cpu_model = "AMD Duron (0.13)"; } else { cpu_model = "Athlon XP (0.13)"; } break; case 3: case 7: cpu_model = "AMD Duron"; // Duron stepping 0 CPUID for L2 is broken (AMD errata T13) if (cpuid_info.version.stepping == 0) { // Hard code the right L2 size. l2_cache = 64; } break; default: break; } break; default: // All AMD family values >= 10 have the Brand ID feature so we don't need to find the CPU type. break; } break; case 'G': // Transmeta Processors - vendor_id starts with "GenuineTMx86" if (cpuid_info.vendor_id.str[7] == 'T' ) { if (cpuid_info.version.family == 5) { cpu_model = "TM 5x00"; } else if (cpuid_info.version.family == 15) { cpu_model = "TM 8x00"; } l1_cache = cpuid_info.cache_info.l1_i_size + cpuid_info.cache_info.l1_d_size; l2_cache = cpuid_info.cache_info.l2_size; break; } // Intel Processors - vendor_id starts with "GenuineIntel" switch (cpuid_info.version.family) { case 4: switch (cpuid_info.version.model) { case 0: case 1: cpu_model = "Intel 486DX"; break; case 2: cpu_model = "Intel 486SX"; break; case 3: cpu_model = "Intel 486DX2"; break; case 4: cpu_model = "Intel 486SL"; break; case 5: cpu_model = "Intel 486SX2"; break; case 7: cpu_model = "Intel 486DX2-WB"; break; case 8: cpu_model = "Intel 486DX4"; break; case 9: cpu_model = "Intel 486DX4-WB"; break; default: break; } break; case 5: switch (cpuid_info.version.model) { case 0: case 1: case 2: case 3: case 7: cpu_model = "Pentium"; if (l1_cache == 0) { l1_cache = 8; } break; case 4: case 8: cpu_model = "Pentium-MMX"; if (l1_cache == 0) { l1_cache = 16; } break; default: break; } break; case 6: switch (cpuid_info.version.model) { case 0: case 1: cpu_model = "Pentium Pro"; break; case 3: case 4: cpu_model = "Pentium II"; break; case 5: if (l2_cache == 0) { cpu_model = "Celeron"; } else { cpu_model = "Pentium II"; } break; case 6: if (l2_cache == 128) { cpu_model = "Celeron"; } else { cpu_model = "Pentium II"; } break; case 7: case 8: case 11: if (l2_cache == 128) { cpu_model = "Celeron"; } else { cpu_model = "Pentium III"; } break; case 9: if (l2_cache == 512) { cpu_model = "Celeron M (0.13)"; } else { cpu_model = "Pentium M (0.13)"; } break; case 10: cpu_model = "Pentium III Xeon"; break; case 12: l1_cache = 24; cpu_model = "Atom (0.045)"; break; case 13: if (l2_cache == 1024) { cpu_model = "Celeron M (0.09)"; } else { cpu_model = "Pentium M (0.09)"; } break; case 14: cpu_model = "Intel Core"; break; case 15: if (l2_cache == 1024) { cpu_model = "Pentium E"; } else { cpu_model = "Intel Core 2"; } break; default: break; } break; case 15: switch (cpuid_info.version.model) { case 0: case 1: case 2: if (l2_cache == 128) { cpu_model = "Celeron"; } else { cpu_model = "Pentium 4"; } break; case 3: case 4: if (l2_cache == 256) { cpu_model = "Celeron (0.09)"; } else { cpu_model = "Pentium 4 (0.09)"; } break; case 6: cpu_model = "Pentium D (65nm)"; break; default: cpu_model = "Unknown Intel"; break; } break; default: break; } break; case 'C': // VIA/Cyrix/Centaur Processors with CPUID if (cpuid_info.vendor_id.str[1] == 'e' ) { // CentaurHauls l1_cache = cpuid_info.cache_info.l1_i_size + cpuid_info.cache_info.l1_d_size; l2_cache = cpuid_info.cache_info.l2_size >> 8; switch (cpuid_info.version.family) { case 5: cpu_model = "Centaur 5x86"; break; case 6: // VIA C3 switch (cpuid_info.version.model) { case 10: cpu_model = "VIA C7 (C5J)"; l1_cache = 64; l2_cache = 128; break; case 13: cpu_model = "VIA C7 (C5R)"; l1_cache = 64; l2_cache = 128; break; case 15: cpu_model = "VIA Isaiah (CN)"; l1_cache = 64; l2_cache = 128; break; default: if (cpuid_info.version.stepping < 8) { cpu_model = "VIA C3 Samuel2"; } else { cpu_model = "VIA C3 Eden"; } break; } default: break; } } else { /* CyrixInstead */ switch (cpuid_info.version.family) { case 5: switch (cpuid_info.version.model) { case 0: cpu_model = "Cyrix 6x86MX/MII"; break; case 4: cpu_model = "Cyrix GXm"; break; default: break; } break; case 6: // VIA C3 switch (cpuid_info.version.model) { case 6: cpu_model = "Cyrix III"; break; case 7: if (cpuid_info.version.stepping < 8) { cpu_model = "VIA C3 Samuel2"; } else { cpu_model = "VIA C3 Ezra-T"; } break; case 8: cpu_model = "VIA C3 Ezra-T"; break; case 9: cpu_model = "VIA C3 Nehemiah"; break; default: break; } // L1 = L2 = 64 KB from Cyrix III to Nehemiah l1_cache = 64; l2_cache = 64; break; default: break; } } break; default: // Unknown processor - make a guess at the family. switch (cpuid_info.version.family) { case 5: cpu_model = "586"; break; case 6: cpu_model = "686"; break; default: cpu_model = "Unidentified Processor"; break; } break; } } static void measure_cpu_speed(void) { if (cpuid_info.flags.rdtsc == 0) { return; } // Set up timer outb((inb(0x61) & ~0x02) | 0x01, 0x61); outb(0xb0, 0x43); outb(PIT_TICKS_50mS & 0xff, 0x42); outb(PIT_TICKS_50mS >> 8, 0x42); uint32_t start_time; rdtscl(start_time); int loops = 0; do { loops++; } while ((inb(0x61) & 0x20) == 0); uint32_t end_time; rdtscl(end_time); uint32_t run_time = end_time - start_time; // Make sure we have a credible result if (loops >= 4 && run_time >= 50000) { clks_per_msec = run_time / 50; } } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void cpuinfo_init(void) { // Get cache sizes for most AMD and Intel CPUs. Exceptions for old // CPUs are handled in determine_cpu_model(). determine_cache_size(); determine_cpu_model(); measure_cpu_speed(); } pcmemtest-1.5/system/cpuinfo.h000066400000000000000000000016741413251666700165430ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef CPUINFO_H #define CPUINFO_H /* * Provides information about the CPU type, clock speed and cache sizes. * * Copyright (C) 2020 Martin Whitaker. */ #include #include /* * A string identifying the CPU make and model. */ extern const char *cpu_model; /* * A number identifying the integrated memory controller type. */ extern uint32_t imc_type; /* * The size of the L1 cache in KB. */ extern int l1_cache; /* * The size of the L2 cache in KB. */ extern int l2_cache; /* * The size of the L3 cache in KB. */ extern int l3_cache; /* * A flag indicating that we can't read the core temperature on this CPU. */ extern bool no_temperature; /* * The TSC clock speed in kHz. Assumed to be the nominal CPU clock speed. */ extern uint32_t clks_per_msec; /* * Determines the CPU info and stores it in the exported variables. */ void cpuinfo_init(void); #endif // CPUINFO_H pcmemtest-1.5/system/font.c000066400000000000000000003014651413251666700160420ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from Linux lib/fonts/font_8x16.c #include "font.h" const uint8_t font_data[FONT_CHARS][FONT_HEIGHT] = { { /* 0 0x00 '^@' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 1 0x01 '^A' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x81, /* 10000001 */ 0xa5, /* 10100101 */ 0x81, /* 10000001 */ 0x81, /* 10000001 */ 0xbd, /* 10111101 */ 0x99, /* 10011001 */ 0x81, /* 10000001 */ 0x81, /* 10000001 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 2 0x02 '^B' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0xff, /* 11111111 */ 0xdb, /* 11011011 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xc3, /* 11000011 */ 0xe7, /* 11100111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 3 0x03 '^C' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x6c, /* 01101100 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0x7c, /* 01111100 */ 0x38, /* 00111000 */ 0x10, /* 00010000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 4 0x04 '^D' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x7c, /* 01111100 */ 0xfe, /* 11111110 */ 0x7c, /* 01111100 */ 0x38, /* 00111000 */ 0x10, /* 00010000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 5 0x05 '^E' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x3c, /* 00111100 */ 0xe7, /* 11100111 */ 0xe7, /* 11100111 */ 0xe7, /* 11100111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 6 0x06 '^F' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x7e, /* 01111110 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 7 0x07 '^G' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 8 0x08 '^H' */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xe7, /* 11100111 */ 0xc3, /* 11000011 */ 0xc3, /* 11000011 */ 0xe7, /* 11100111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ }, { /* 9 0x09 '^I' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0x42, /* 01000010 */ 0x42, /* 01000010 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 10 0x0a '^J' */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xc3, /* 11000011 */ 0x99, /* 10011001 */ 0xbd, /* 10111101 */ 0xbd, /* 10111101 */ 0x99, /* 10011001 */ 0xc3, /* 11000011 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ }, { /* 11 0x0b '^K' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1e, /* 00011110 */ 0x0e, /* 00001110 */ 0x1a, /* 00011010 */ 0x32, /* 00110010 */ 0x78, /* 01111000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x78, /* 01111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 12 0x0c '^L' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 13 0x0d '^M' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3f, /* 00111111 */ 0x33, /* 00110011 */ 0x3f, /* 00111111 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x70, /* 01110000 */ 0xf0, /* 11110000 */ 0xe0, /* 11100000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 14 0x0e '^N' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7f, /* 01111111 */ 0x63, /* 01100011 */ 0x7f, /* 01111111 */ 0x63, /* 01100011 */ 0x63, /* 01100011 */ 0x63, /* 01100011 */ 0x63, /* 01100011 */ 0x67, /* 01100111 */ 0xe7, /* 11100111 */ 0xe6, /* 11100110 */ 0xc0, /* 11000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 15 0x0f '^O' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xdb, /* 11011011 */ 0x3c, /* 00111100 */ 0xe7, /* 11100111 */ 0x3c, /* 00111100 */ 0xdb, /* 11011011 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 16 0x10 '^P' */ 0x00, /* 00000000 */ 0x80, /* 10000000 */ 0xc0, /* 11000000 */ 0xe0, /* 11100000 */ 0xf0, /* 11110000 */ 0xf8, /* 11111000 */ 0xfe, /* 11111110 */ 0xf8, /* 11111000 */ 0xf0, /* 11110000 */ 0xe0, /* 11100000 */ 0xc0, /* 11000000 */ 0x80, /* 10000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 17 0x11 '^Q' */ 0x00, /* 00000000 */ 0x02, /* 00000010 */ 0x06, /* 00000110 */ 0x0e, /* 00001110 */ 0x1e, /* 00011110 */ 0x3e, /* 00111110 */ 0xfe, /* 11111110 */ 0x3e, /* 00111110 */ 0x1e, /* 00011110 */ 0x0e, /* 00001110 */ 0x06, /* 00000110 */ 0x02, /* 00000010 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 18 0x12 '^R' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 19 0x13 '^S' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 20 0x14 '^T' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7f, /* 01111111 */ 0xdb, /* 11011011 */ 0xdb, /* 11011011 */ 0xdb, /* 11011011 */ 0x7b, /* 01111011 */ 0x1b, /* 00011011 */ 0x1b, /* 00011011 */ 0x1b, /* 00011011 */ 0x1b, /* 00011011 */ 0x1b, /* 00011011 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 21 0x15 '^U' */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0x60, /* 01100000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x0c, /* 00001100 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 22 0x16 '^V' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 23 0x17 '^W' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 24 0x18 '^X' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 25 0x19 '^Y' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 26 0x1a '^Z' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0xfe, /* 11111110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 27 0x1b '^[' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xfe, /* 11111110 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 28 0x1c '^\' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 29 0x1d '^]' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x28, /* 00101000 */ 0x6c, /* 01101100 */ 0xfe, /* 11111110 */ 0x6c, /* 01101100 */ 0x28, /* 00101000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 30 0x1e '^^' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x38, /* 00111000 */ 0x7c, /* 01111100 */ 0x7c, /* 01111100 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 31 0x1f '^_' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0x7c, /* 01111100 */ 0x7c, /* 01111100 */ 0x38, /* 00111000 */ 0x38, /* 00111000 */ 0x10, /* 00010000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 32 0x20 ' ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 33 0x21 '!' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x3c, /* 00111100 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 34 0x22 '"' */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x24, /* 00100100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 35 0x23 '#' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0xfe, /* 11111110 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0xfe, /* 11111110 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 36 0x24 '$' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc2, /* 11000010 */ 0xc0, /* 11000000 */ 0x7c, /* 01111100 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x86, /* 10000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 37 0x25 '%' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc2, /* 11000010 */ 0xc6, /* 11000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc6, /* 11000110 */ 0x86, /* 10000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 38 0x26 '&' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 39 0x27 ''' */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 40 0x28 '(' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 41 0x29 ')' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 42 0x2a '*' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0xff, /* 11111111 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 43 0x2b '+' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 44 0x2c ',' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 45 0x2d '-' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 46 0x2e '.' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 47 0x2f '/' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x02, /* 00000010 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc0, /* 11000000 */ 0x80, /* 10000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 48 0x30 '0' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 49 0x31 '1' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x38, /* 00111000 */ 0x78, /* 01111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 50 0x32 '2' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 51 0x33 '3' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x3c, /* 00111100 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 52 0x34 '4' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x0c, /* 00001100 */ 0x1c, /* 00011100 */ 0x3c, /* 00111100 */ 0x6c, /* 01101100 */ 0xcc, /* 11001100 */ 0xfe, /* 11111110 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x1e, /* 00011110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 53 0x35 '5' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xfc, /* 11111100 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 54 0x36 '6' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x60, /* 01100000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xfc, /* 11111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 55 0x37 '7' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 56 0x38 '8' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 57 0x39 '9' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7e, /* 01111110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x78, /* 01111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 58 0x3a ':' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 59 0x3b ';' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 60 0x3c '<' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x06, /* 00000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 61 0x3d '=' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 62 0x3e '>' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 63 0x3f '?' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 64 0x40 '@' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xde, /* 11011110 */ 0xde, /* 11011110 */ 0xde, /* 11011110 */ 0xdc, /* 11011100 */ 0xc0, /* 11000000 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 65 0x41 'A' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 66 0x42 'B' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfc, /* 11111100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x7c, /* 01111100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0xfc, /* 11111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 67 0x43 'C' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0xc2, /* 11000010 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc2, /* 11000010 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 68 0x44 'D' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xf8, /* 11111000 */ 0x6c, /* 01101100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x6c, /* 01101100 */ 0xf8, /* 11111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 69 0x45 'E' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x66, /* 01100110 */ 0x62, /* 01100010 */ 0x68, /* 01101000 */ 0x78, /* 01111000 */ 0x68, /* 01101000 */ 0x60, /* 01100000 */ 0x62, /* 01100010 */ 0x66, /* 01100110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 70 0x46 'F' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x66, /* 01100110 */ 0x62, /* 01100010 */ 0x68, /* 01101000 */ 0x78, /* 01111000 */ 0x68, /* 01101000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0xf0, /* 11110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 71 0x47 'G' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0xc2, /* 11000010 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xde, /* 11011110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x66, /* 01100110 */ 0x3a, /* 00111010 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 72 0x48 'H' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 73 0x49 'I' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 74 0x4a 'J' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1e, /* 00011110 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x78, /* 01111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 75 0x4b 'K' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xe6, /* 11100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x6c, /* 01101100 */ 0x78, /* 01111000 */ 0x78, /* 01111000 */ 0x6c, /* 01101100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0xe6, /* 11100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 76 0x4c 'L' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xf0, /* 11110000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x62, /* 01100010 */ 0x66, /* 01100110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 77 0x4d 'M' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xee, /* 11101110 */ 0xfe, /* 11111110 */ 0xfe, /* 11111110 */ 0xd6, /* 11010110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 78 0x4e 'N' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xe6, /* 11100110 */ 0xf6, /* 11110110 */ 0xfe, /* 11111110 */ 0xde, /* 11011110 */ 0xce, /* 11001110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 79 0x4f 'O' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 80 0x50 'P' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfc, /* 11111100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x7c, /* 01111100 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0xf0, /* 11110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 81 0x51 'Q' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xd6, /* 11010110 */ 0xde, /* 11011110 */ 0x7c, /* 01111100 */ 0x0c, /* 00001100 */ 0x0e, /* 00001110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 82 0x52 'R' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfc, /* 11111100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x7c, /* 01111100 */ 0x6c, /* 01101100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0xe6, /* 11100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 83 0x53 'S' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x60, /* 01100000 */ 0x38, /* 00111000 */ 0x0c, /* 00001100 */ 0x06, /* 00000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 84 0x54 'T' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x5a, /* 01011010 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 85 0x55 'U' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 86 0x56 'V' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x10, /* 00010000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 87 0x57 'W' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xfe, /* 11111110 */ 0xee, /* 11101110 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 88 0x58 'X' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x7c, /* 01111100 */ 0x38, /* 00111000 */ 0x38, /* 00111000 */ 0x7c, /* 01111100 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 89 0x59 'Y' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 90 0x5a 'Z' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0x86, /* 10000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc2, /* 11000010 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 91 0x5b '[' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 92 0x5c '\' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x80, /* 10000000 */ 0xc0, /* 11000000 */ 0xe0, /* 11100000 */ 0x70, /* 01110000 */ 0x38, /* 00111000 */ 0x1c, /* 00011100 */ 0x0e, /* 00001110 */ 0x06, /* 00000110 */ 0x02, /* 00000010 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 93 0x5d ']' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 94 0x5e '^' */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 95 0x5f '_' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 96 0x60 '`' */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 97 0x61 'a' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0x0c, /* 00001100 */ 0x7c, /* 01111100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 98 0x62 'b' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xe0, /* 11100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x78, /* 01111000 */ 0x6c, /* 01101100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 99 0x63 'c' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 100 0x64 'd' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1c, /* 00011100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x3c, /* 00111100 */ 0x6c, /* 01101100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 101 0x65 'e' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 102 0x66 'f' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1c, /* 00011100 */ 0x36, /* 00110110 */ 0x32, /* 00110010 */ 0x30, /* 00110000 */ 0x78, /* 01111000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x78, /* 01111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 103 0x67 'g' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x7c, /* 01111100 */ 0x0c, /* 00001100 */ 0xcc, /* 11001100 */ 0x78, /* 01111000 */ 0x00, /* 00000000 */ }, { /* 104 0x68 'h' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xe0, /* 11100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x6c, /* 01101100 */ 0x76, /* 01110110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0xe6, /* 11100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 105 0x69 'i' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 106 0x6a 'j' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x00, /* 00000000 */ 0x0e, /* 00001110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ }, { /* 107 0x6b 'k' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xe0, /* 11100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x66, /* 01100110 */ 0x6c, /* 01101100 */ 0x78, /* 01111000 */ 0x78, /* 01111000 */ 0x6c, /* 01101100 */ 0x66, /* 01100110 */ 0xe6, /* 11100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 108 0x6c 'l' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 109 0x6d 'm' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xec, /* 11101100 */ 0xfe, /* 11111110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 110 0x6e 'n' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xdc, /* 11011100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 111 0x6f 'o' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 112 0x70 'p' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xdc, /* 11011100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x7c, /* 01111100 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0xf0, /* 11110000 */ 0x00, /* 00000000 */ }, { /* 113 0x71 'q' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x7c, /* 01111100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x1e, /* 00011110 */ 0x00, /* 00000000 */ }, { /* 114 0x72 'r' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xdc, /* 11011100 */ 0x76, /* 01110110 */ 0x66, /* 01100110 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0xf0, /* 11110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 115 0x73 's' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0x60, /* 01100000 */ 0x38, /* 00111000 */ 0x0c, /* 00001100 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 116 0x74 't' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0xfc, /* 11111100 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x36, /* 00110110 */ 0x1c, /* 00011100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 117 0x75 'u' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 118 0x76 'v' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 119 0x77 'w' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xd6, /* 11010110 */ 0xfe, /* 11111110 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 120 0x78 'x' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x38, /* 00111000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 121 0x79 'y' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7e, /* 01111110 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0xf8, /* 11111000 */ 0x00, /* 00000000 */ }, { /* 122 0x7a 'z' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xcc, /* 11001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 123 0x7b '{' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x0e, /* 00001110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x70, /* 01110000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x0e, /* 00001110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 124 0x7c '|' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 125 0x7d '}' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x70, /* 01110000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x0e, /* 00001110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x70, /* 01110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 126 0x7e '~' */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 127 0x7f '' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 128 0x80 'Ç' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0xc2, /* 11000010 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc2, /* 11000010 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x70, /* 01110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 129 0x81 'ü' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 130 0x82 'é' */ 0x00, /* 00000000 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 131 0x83 'â' */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0x0c, /* 00001100 */ 0x7c, /* 01111100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 132 0x84 'ä' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0x0c, /* 00001100 */ 0x7c, /* 01111100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 133 0x85 'à' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0x0c, /* 00001100 */ 0x7c, /* 01111100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 134 0x86 'å' */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0x0c, /* 00001100 */ 0x7c, /* 01111100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 135 0x87 'ç' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x18, /* 00011000 */ 0x70, /* 01110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 136 0x88 'ê' */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 137 0x89 'ë' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 138 0x8a 'è' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 139 0x8b 'ï' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 140 0x8c 'î' */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 141 0x8d 'ì' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 142 0x8e 'Ä' */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 143 0x8f 'Å' */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 144 0x90 'É' */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x66, /* 01100110 */ 0x62, /* 01100010 */ 0x68, /* 01101000 */ 0x78, /* 01111000 */ 0x68, /* 01101000 */ 0x62, /* 01100010 */ 0x66, /* 01100110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 145 0x91 'æ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xec, /* 11101100 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x7e, /* 01111110 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0x6e, /* 01101110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 146 0x92 'Æ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3e, /* 00111110 */ 0x6c, /* 01101100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xfe, /* 11111110 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xce, /* 11001110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 147 0x93 'ô' */ 0x00, /* 00000000 */ 0x10, /* 00010000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 148 0x94 'ö' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 149 0x95 'ò' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 150 0x96 'û' */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x78, /* 01111000 */ 0xcc, /* 11001100 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 151 0x97 'ù' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 152 0x98 'ÿ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7e, /* 01111110 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x78, /* 01111000 */ 0x00, /* 00000000 */ }, { /* 153 0x99 'Ö' */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 154 0x9a 'Ü' */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 155 0x9b '¢' */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 156 0x9c '£' */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x64, /* 01100100 */ 0x60, /* 01100000 */ 0xf0, /* 11110000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0xe6, /* 11100110 */ 0xfc, /* 11111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 157 0x9d '¥' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 158 0x9e '₧' */ 0x00, /* 00000000 */ 0xf8, /* 11111000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xf8, /* 11111000 */ 0xc4, /* 11000100 */ 0xcc, /* 11001100 */ 0xde, /* 11011110 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 159 0x9f 'ƒ' */ 0x00, /* 00000000 */ 0x0e, /* 00001110 */ 0x1b, /* 00011011 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xd8, /* 11011000 */ 0x70, /* 01110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 160 0xa0 'á' */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0x0c, /* 00001100 */ 0x7c, /* 01111100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 161 0xa1 'í' */ 0x00, /* 00000000 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 162 0xa2 'ó' */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 163 0xa3 'ú' */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x00, /* 00000000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 164 0xa4 'ñ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0x00, /* 00000000 */ 0xdc, /* 11011100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 165 0xa5 'Ñ' */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0x00, /* 00000000 */ 0xc6, /* 11000110 */ 0xe6, /* 11100110 */ 0xf6, /* 11110110 */ 0xfe, /* 11111110 */ 0xde, /* 11011110 */ 0xce, /* 11001110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 166 0xa6 'ª' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x3e, /* 00111110 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 167 0xa7 'º' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 168 0xa8 '¿' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc0, /* 11000000 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x7c, /* 01111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 169 0xa9 '⌐' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 170 0xaa '¬' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 171 0xab '½' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0xe0, /* 11100000 */ 0x62, /* 01100010 */ 0x66, /* 01100110 */ 0x6c, /* 01101100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xdc, /* 11011100 */ 0x86, /* 10000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x3e, /* 00111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 172 0xac '¼' */ 0x00, /* 00000000 */ 0x60, /* 01100000 */ 0xe0, /* 11100000 */ 0x62, /* 01100010 */ 0x66, /* 01100110 */ 0x6c, /* 01101100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x66, /* 01100110 */ 0xce, /* 11001110 */ 0x9a, /* 10011010 */ 0x3f, /* 00111111 */ 0x06, /* 00000110 */ 0x06, /* 00000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 173 0xad '¡' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x3c, /* 00111100 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 174 0xae '«' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x36, /* 00110110 */ 0x6c, /* 01101100 */ 0xd8, /* 11011000 */ 0x6c, /* 01101100 */ 0x36, /* 00110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 175 0xaf '»' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xd8, /* 11011000 */ 0x6c, /* 01101100 */ 0x36, /* 00110110 */ 0x6c, /* 01101100 */ 0xd8, /* 11011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 176 0xb0 '░' */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ 0x11, /* 00010001 */ 0x44, /* 01000100 */ }, { /* 177 0xb1 '▒' */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ 0x55, /* 01010101 */ 0xaa, /* 10101010 */ }, { /* 178 0xb2 '▓' */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ 0xdd, /* 11011101 */ 0x77, /* 01110111 */ }, { /* 179 0xb3 '│' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 180 0xb4 '┤' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 181 0xb5 '╡' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 182 0xb6 '╢' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xf6, /* 11110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 183 0xb7 '╖' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 184 0xb8 '╕' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 185 0xb9 '╣' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xf6, /* 11110110 */ 0x06, /* 00000110 */ 0xf6, /* 11110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 186 0xba '║' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 187 0xbb '╗' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x06, /* 00000110 */ 0xf6, /* 11110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 188 0xbc '╝' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xf6, /* 11110110 */ 0x06, /* 00000110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 189 0xbd '╜' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 190 0xbe '╛' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 191 0xbf '┐' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xf8, /* 11111000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 192 0xc0 '└' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 193 0xc1 '┴' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 194 0xc2 '┬' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 195 0xc3 '├' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 196 0xc4 '─' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 197 0xc5 '┼' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xff, /* 11111111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 198 0xc6 '╞' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 199 0xc7 '╟' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x37, /* 00110111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 200 0xc8 '╚' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x37, /* 00110111 */ 0x30, /* 00110000 */ 0x3f, /* 00111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 201 0xc9 '╔' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3f, /* 00111111 */ 0x30, /* 00110000 */ 0x37, /* 00110111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 202 0xca '╩' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xf7, /* 11110111 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 203 0xcb '╦' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0xf7, /* 11110111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 204 0xcc '╠' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x37, /* 00110111 */ 0x30, /* 00110000 */ 0x37, /* 00110111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 205 0xcd '═' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 206 0xce '╬' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xf7, /* 11110111 */ 0x00, /* 00000000 */ 0xf7, /* 11110111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 207 0xcf '╧' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 208 0xd0 '╨' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 209 0xd1 '╤' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 210 0xd2 '╥' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 211 0xd3 '╙' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x3f, /* 00111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 212 0xd4 '╘' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 213 0xd5 '╒' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 214 0xd6 '╓' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3f, /* 00111111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 215 0xd7 '╫' */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0xff, /* 11111111 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ }, { /* 216 0xd8 '╪' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xff, /* 11111111 */ 0x18, /* 00011000 */ 0xff, /* 11111111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 217 0xd9 '┘' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xf8, /* 11111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 218 0xda '┌' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1f, /* 00011111 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 219 0xdb '█' */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ }, { /* 220 0xdc '▄' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ }, { /* 221 0xdd '▌' */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ 0xf0, /* 11110000 */ }, { /* 222 0xde '▐' */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ 0x0f, /* 00001111 */ }, { /* 223 0xdf '▀' */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0xff, /* 11111111 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 224 0xe0 'α' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0xdc, /* 11011100 */ 0x76, /* 01110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 225 0xe1 'ß' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x78, /* 01111000 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xcc, /* 11001100 */ 0xd8, /* 11011000 */ 0xcc, /* 11001100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xcc, /* 11001100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 226 0xe2 'Γ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0xc0, /* 11000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 227 0xe3 'π' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 228 0xe4 'Σ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 229 0xe5 'σ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0x70, /* 01110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 230 0xe6 'µ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x7c, /* 01111100 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0xc0, /* 11000000 */ 0x00, /* 00000000 */ }, { /* 231 0xe7 'τ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 232 0xe8 'Φ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 233 0xe9 'Θ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xfe, /* 11111110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 234 0xea 'Ω' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0xee, /* 11101110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 235 0xeb 'δ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1e, /* 00011110 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x3e, /* 00111110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x66, /* 01100110 */ 0x3c, /* 00111100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 236 0xec '∞' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0xdb, /* 11011011 */ 0xdb, /* 11011011 */ 0xdb, /* 11011011 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 237 0xed 'φ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x03, /* 00000011 */ 0x06, /* 00000110 */ 0x7e, /* 01111110 */ 0xdb, /* 11011011 */ 0xdb, /* 11011011 */ 0xf3, /* 11110011 */ 0x7e, /* 01111110 */ 0x60, /* 01100000 */ 0xc0, /* 11000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 238 0xee 'ε' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x1c, /* 00011100 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x7c, /* 01111100 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x1c, /* 00011100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 239 0xef '∩' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 01111100 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0xc6, /* 11000110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 240 0xf0 '≡' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0xfe, /* 11111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 241 0xf1 '±' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x7e, /* 01111110 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 242 0xf2 '≥' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x06, /* 00000110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 243 0xf3 '≤' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x30, /* 00110000 */ 0x60, /* 01100000 */ 0x30, /* 00110000 */ 0x18, /* 00011000 */ 0x0c, /* 00001100 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 244 0xf4 '⌠' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x0e, /* 00001110 */ 0x1b, /* 00011011 */ 0x1b, /* 00011011 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ }, { /* 245 0xf5 '⌡' */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0xd8, /* 11011000 */ 0x70, /* 01110000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 246 0xf6 '÷' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 247 0xf7 '≈' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0x00, /* 00000000 */ 0x76, /* 01110110 */ 0xdc, /* 11011100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 248 0xf8 '°' */ 0x00, /* 00000000 */ 0x38, /* 00111000 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x38, /* 00111000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 249 0xf9 '·' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 250 0xfa '•' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 00011000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 251 0xfb '√' */ 0x00, /* 00000000 */ 0x0f, /* 00001111 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0x0c, /* 00001100 */ 0xec, /* 11101100 */ 0x6c, /* 01101100 */ 0x6c, /* 01101100 */ 0x3c, /* 00111100 */ 0x1c, /* 00011100 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 252 0xfc 'ⁿ' */ 0x00, /* 00000000 */ 0x6c, /* 01101100 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x36, /* 00110110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 253 0xfd '²' */ 0x00, /* 00000000 */ 0x3c, /* 00111100 */ 0x66, /* 01100110 */ 0x0c, /* 00001100 */ 0x18, /* 00011000 */ 0x32, /* 00110010 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 254 0xfe '■' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x7e, /* 01111110 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }, { /* 255 0xff ' ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ } }; pcmemtest-1.5/system/font.h000066400000000000000000000006071413251666700160410ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef FONT_H #define FONT_H /* * Provides the font used for framebuffer display. * * Copyright (C) 2020 Martin Whitaker. */ #include /* * Font size definitions. */ #define FONT_WIDTH 8 #define FONT_HEIGHT 16 #define FONT_CHARS 256 /* * The font data. */ extern const uint8_t font_data[FONT_CHARS][FONT_HEIGHT]; #endif // FONT_H pcmemtest-1.5/system/hwctrl.c000066400000000000000000000015431413251666700163710ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an 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 "io.h" #include "hwctrl.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void reboot(void) { // Tell the BIOS to do a warm reboot. *((uint16_t *)0x472) = 0x1234; // Pulse the system reset signal. outb(0xfe, 0x64); } void floppy_off() { // Stop the floppy motor. outb(0x8, 0x3f2); } void cursor_off() { // Set HW cursor off screen. outb(0x0f, 0x3d4); outb(0xff, 0x3d5); outb(0x0e, 0x3d4); outb(0xff, 0x3d5); } pcmemtest-1.5/system/hwctrl.h000066400000000000000000000005501413251666700163730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef HWCTRL_H #define HWCTRL_H /* * Provides miscellaneous hardware control functions. * * Copyright (C) 2020 Martin Whitaker. */ /* * Reboots the machine. */ void reboot(void); /* * Turns off the floppy motor. */ void floppy_off(); /* * Disables the screen cursor. */ void cursor_off(); #endif // HWCTRL_H pcmemtest-1.5/system/io.h000066400000000000000000000077651413251666700155160ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef IO_H #define IO_H /* * Provides macro definitions for the x86 IO instructions * inb/inw/inl/outb/outw/outl and the "string versions" of the same * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" * versions of the single-IO instructions (inb_p/inw_p/..). * * This file is not meant to be obfuscating: it's just complicated * to (a) handle it all in a way that makes gcc able to optimize it * as well as possible and (b) trying to avoid writing the same thing * over and over again with slight variations and possibly making a * mistake somewhere. * * Derived from memtest86+ io.h. * (original contained no copyright statement) */ #ifdef SLOW_IO_BY_JUMPING #define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") #else #define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") #endif #ifdef REALLY_SLOW_IO #define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } #else #define SLOW_DOWN_IO __SLOW_DOWN_IO #endif #define __OUT1(s,x) \ static inline void __out##s(unsigned x value, unsigned short port) { #define __OUT2(s,s1,s2) \ __asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" #define __OUT(s,s1,x) \ __OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \ __OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \ __OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \ __OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; } #define __IN1(s) \ static inline RETURN_TYPE __in##s(unsigned short port) { RETURN_TYPE _v; #define __IN2(s,s1,s2) \ __asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" #define __IN(s,s1,i...) \ __IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \ __IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \ __IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \ __IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; } #define __OUTS(s) \ static inline void outs##s(unsigned short port, const void * addr, unsigned long count) \ { __asm__ __volatile__ ("cld ; rep ; outs" #s \ : "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } #define RETURN_TYPE unsigned char /* __IN(b,"b","0" (0)) */ __IN(b,"") #undef RETURN_TYPE #define RETURN_TYPE unsigned short /* __IN(w,"w","0" (0)) */ __IN(w,"") #undef RETURN_TYPE #define RETURN_TYPE unsigned int __IN(l,"") #undef RETURN_TYPE __OUT(b,"b",char) __OUT(w,"w",short) __OUT(l,,int) __OUTS(b) __OUTS(w) __OUTS(l) /* * Note that due to the way __builtin_constant_p() works, you * - can't use it inside a inline function (it will never be true) * - you don't have to worry about side effects within the __builtin.. */ #define outb(val,port) \ ((__builtin_constant_p((port)) && (port) < 256) ? \ __outbc((val),(port)) : \ __outb((val),(port))) #define inb(port) \ ((__builtin_constant_p((port)) && (port) < 256) ? \ __inbc(port) : \ __inb(port)) #define outw(val,port) \ ((__builtin_constant_p((port)) && (port) < 256) ? \ __outwc((val),(port)) : \ __outw((val),(port))) #define inw(port) \ ((__builtin_constant_p((port)) && (port) < 256) ? \ __inwc(port) : \ __inw(port)) #define outl(val,port) \ ((__builtin_constant_p((port)) && (port) < 256) ? \ __outlc((val),(port)) : \ __outl((val),(port))) #define inl(port) \ ((__builtin_constant_p((port)) && (port) < 256) ? \ __inlc(port) : \ __inl(port)) static __inline unsigned char inb_p (unsigned short int __port) { unsigned char _v; __asm__ __volatile__ ("\t" "inb %w1,%0 \n\t" "outb %%al,$0x80 \n" : "=a" (_v) : "Nd" (__port) ); return _v; } static __inline void outb_p (unsigned char __value, unsigned short int __port) { __asm__ __volatile__ ("\t" "outb %b0,%w1 \n\t" "outb %%al,$0x80 \n" : /* no outputs */ : "a" (__value), "Nd" (__port) ); } #endif // IO_H pcmemtest-1.5/system/keyboard.c000066400000000000000000000051551413251666700166710ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. #include #include "io.h" #include "keyboard.h" //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ // Convert set 1 scancodes to characters. static const char keymap[] = { /* 0x00 */ 0, /* 0x01 */ ESC, /* 0x02 */ '1', /* 0x03 */ '2', /* 0x04 */ '3', /* 0x05 */ '4', /* 0x06 */ '5', /* 0x07 */ '6', /* 0x08 */ '7', /* 0x09 */ '8', /* 0x0a */ '9', /* 0x0b */ '0', /* 0x0c */ '-', /* 0x0d */ '+', /* 0x0e */ '\b', /* 0x0f */ '\t', /* 0x10 */ 'q', /* 0x11 */ 'w', /* 0x12 */ 'e', /* 0x13 */ 'r', /* 0x14 */ 't', /* 0x15 */ 'y', /* 0x16 */ 'u', /* 0x17 */ 'i', /* 0x18 */ 'o', /* 0x19 */ 'p', /* 0x1a */ '[', /* 0x1b */ ']', /* 0x1c */ '\n', /* 0x1d */ 0, /* 0x1e */ 'a', /* 0x1f */ 's', /* 0x20 */ 'd', /* 0x21 */ 'f', /* 0x22 */ 'g', /* 0x23 */ 'h', /* 0x24 */ 'j', /* 0x25 */ 'k', /* 0x26 */ 'l', /* 0x27 */ ';', /* 0x28 */ '\'', /* 0x29 */ '`', /* 0x2a */ 0, /* 0x2b */ '\\', /* 0x2c */ 'z', /* 0x2d */ 'x', /* 0x2e */ 'c', /* 0x2f */ 'v', /* 0x30 */ 'b', /* 0x31 */ 'n', /* 0x32 */ 'm', /* 0x33 */ ',', /* 0x34 */ '.', /* 0x35 */ '/', /* 0x36 */ 0, /* 0x37 */ '*', // keypad /* 0x38 */ 0, /* 0x39 */ ' ', /* 0x3a */ 0, /* 0x3b */ '1', /* 0x3c */ '2', /* 0x3d */ '3', /* 0x3e */ '4', /* 0x3f */ '5', /* 0x40 */ '6', /* 0x41 */ '7', /* 0x42 */ '8', /* 0x43 */ '9', /* 0x44 */ '0', /* 0x45 */ 0, /* 0x46 */ 0, /* 0x47 */ '7', // keypad /* 0x48 */ '8', // keypad /* 0x49 */ '9', // keypad /* 0x4a */ '-', // keypad /* 0x4b */ '4', // keypad /* 0x4c */ '5', // keypad /* 0x4d */ '6', // keypad /* 0x4e */ '+', // keypad /* 0x4f */ '1', // keypad /* 0x50 */ '2', // keypad /* 0x51 */ '3', // keypad /* 0x52 */ '0', // keypad /* 0x53 */ '.', // keypad }; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ char get_key(void) { uint8_t c = inb(0x64); if (c & 0x01) { c = inb(0x60); if (c < sizeof(keymap)) { return keymap[c]; } // Ignore keys we don't recognise and key up codes } return '\0'; } pcmemtest-1.5/system/keyboard.h000066400000000000000000000013721413251666700166730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef KEYBOARD_H #define KEYBOARD_H /* * Provides the keyboard interface. It converts incoming key codes to * ASCII characters. * * Copyright (C) 2020 Martin Whitaker. */ /* * The Escape character. */ #define ESC 27 /* * Checks if a key has been pressed and returns the primary ASCII character * corresponding to that key if so, otherwise returns the null character. * F1 to F10 are mapped to the corresponding decimal digit (F10 -> 0). All * other keys that don't have a corresponding ASCII character are ignored. * Characters are only returned for key presses, not key releases. A US * keyboard layout is assumed (because we can't easily do anything else). */ char get_key(void); #endif // KEYBOARD_H pcmemtest-1.5/system/memsize.h000066400000000000000000000015751413251666700165510ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef MEMSIZE_H #define MEMSIZE_H /* * Provides some convenient constants and constant constructors. * * Copyright (C) 2020 Martin Whitaker. */ #include #include #define KB 10 #define MB 20 #define GB 30 #define TB 40 #define VM_PAGE_SHIFT 21 #define VM_PAGE_SIZE (1 << VM_PAGE_SHIFT) #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #define VM_PAGE_C(size, units) \ (units < VM_PAGE_SHIFT ? (uintptr_t)(size) << (VM_PAGE_SHIFT - units) : (uintptr_t)(size) << (units - VM_PAGE_SHIFT)) #define PAGE_C(size, units) \ (units < PAGE_SHIFT ? (uintptr_t)(size) << (PAGE_SHIFT - units) : (uintptr_t)(size) << (units - PAGE_SHIFT)) #define ADDR_C(size, units) \ ((uintptr_t)(size) << units) #define SIZE_C(size, units) \ ((size_t)(size) << units) #endif // MEMSIZE_H pcmemtest-1.5/system/msr.h000066400000000000000000000024031413251666700156700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef MSR_H #define MSR_H /* * Provides access to the CPU machine-specific registers. * * Copyright (C) 2020 Martin Whitaker. */ #define MSR_PLATFORM_INFO 0xce #define MSR_EBC_FREQUENCY_ID 0x2c #define MSR_IA32_PLATFORM_ID 0x17 #define MSR_IA32_EBL_CR_POWERON 0x2a #define MSR_IA32_MCG_CTL 0x17b #define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_THERM_STATUS 0x19c #define MSR_IA32_TEMPERATURE_TARGET 0x1a2 #define MSR_EFER 0xc0000080 #define MSR_K7_HWCR 0xc0010015 #define MSR_K7_VID_STATUS 0xc0010042 #define MSR_AMD64_NB_CFG 0xc001001f #define MSR_AMD64_COFVID_STATUS 0xc0010071 #define rdmsr(msr, value1, value2) \ __asm__ __volatile__("rdmsr" \ : "=a" (value1), \ "=d" (value2) \ : "c" (msr) \ : "edi" \ ) #define wrmsr(msr, value1, value2) \ __asm__ __volatile__("wrmsr" \ : /* no outputs */ \ : "c" (msr), \ "a" (value1), \ "d" (value2) \ ) #endif // MSR_H pcmemtest-1.5/system/pci.c000066400000000000000000000135241413251666700156430ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ pci.c: // // MemTest86+ V5.00 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.x86-secret.com - http://www.memtest.org // ---------------------------------------------------- // pci.c - MemTest-86 Version 3.2 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include "cpuid.h" #include "io.h" #include "pci.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define PCI_CONF_TYPE_NONE 0 #define PCI_CONF_TYPE_1 1 #define PCI_CONF_TYPE_2 2 #define PCI_CLASS_DEVICE 0x0a #define PCI_CLASS_BRIDGE_HOST 0x0600 //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static unsigned char pci_conf_type = PCI_CONF_TYPE_NONE; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ #define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) #define PCI_CONF2_ADDRESS(dev, reg) (unsigned short)(0xC000 | (dev << 8) | reg) #define PCI_CONF3_ADDRESS(bus, dev, fn, reg) \ (0x80000000 | (((reg >> 8) & 0xF) << 24) | (bus << 16) | ((dev & 0x1F) << 11) | (fn << 8) | (reg & 0xFF)) static int pci_sanity_check(void) { /* Do a trivial check to make certain we can see a host bridge. * There are reportedly some buggy chipsets from intel and * compaq where this test does not work, I will worry about * that when we support them. */ uint32_t value; int result = pci_conf_read(0, 0, 0, PCI_CLASS_DEVICE, 2, &value); if (result == 0) { result = -1; if (value == PCI_CLASS_BRIDGE_HOST) { result = 0; } } return result; } static int pci_check_direct(void) { unsigned char tmpCFB; unsigned int tmpCF8; if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF) { pci_conf_type = PCI_CONF_TYPE_1; return 0; } else { /* Check if configuration type 1 works. */ pci_conf_type = PCI_CONF_TYPE_1; tmpCFB = inb(0xCFB); outb(0x01, 0xCFB); tmpCF8 = inl(0xCF8); outl(0x80000000, 0xCF8); if ((inl(0xCF8) == 0x80000000) && (pci_sanity_check() == 0)) { outl(tmpCF8, 0xCF8); outb(tmpCFB, 0xCFB); return 0; } outl(tmpCF8, 0xCF8); /* Check if configuration type 2 works. */ pci_conf_type = PCI_CONF_TYPE_2; outb(0x00, 0xCFB); outb(0x00, 0xCF8); outb(0x00, 0xCFA); if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 && (pci_sanity_check() == 0)) { outb(tmpCFB, 0xCFB); return 0; } outb(tmpCFB, 0xCFB); /* Nothing worked return an error */ pci_conf_type = PCI_CONF_TYPE_NONE; return -1; } } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int pci_init(void) { // For now just make certain we can directly use the pci functions. return pci_check_direct(); } int pci_conf_read(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, uint32_t *value) { int result; if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255 && pci_conf_type != PCI_CONF_TYPE_1)) return -1; result = -1; switch (pci_conf_type) { case PCI_CONF_TYPE_1: if (reg < 256) { outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); } else { outl(PCI_CONF3_ADDRESS(bus, dev, fn, reg), 0xCF8); } switch (len) { case 1: *value = inb(0xCFC + (reg & 3)); result = 0; break; case 2: *value = inw(0xCFC + (reg & 2)); result = 0; break; case 4: *value = inl(0xCFC); result = 0; break; } break; case PCI_CONF_TYPE_2: outb(0xF0 | (fn << 1), 0xCF8); outb(bus, 0xCFA); switch (len) { case 1: *value = inb(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; case 2: *value = inw(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; case 4: *value = inl(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; } outb(0, 0xCF8); break; } return result; } int pci_conf_write(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, uint32_t value) { int result; if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255 && pci_conf_type != PCI_CONF_TYPE_1)) return -1; result = -1; switch (pci_conf_type) { case PCI_CONF_TYPE_1: if (reg < 256) { outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); } else { outl(PCI_CONF3_ADDRESS(bus, dev, fn, reg), 0xCF8); } switch (len) { case 1: outb(value, 0xCFC + (reg & 3)); result = 0; break; case 2: outw(value, 0xCFC + (reg & 2)); result = 0; break; case 4: outl(value, 0xCFC); result = 0; break; } break; case PCI_CONF_TYPE_2: outb(0xF0 | (fn << 1), 0xCF8); outb(bus, 0xCFA); switch (len) { case 1: outb(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; case 2: outw(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; case 4: outl(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; } outb(0, 0xCF8); break; } return result; } pcmemtest-1.5/system/pci.h000066400000000000000000000007041413251666700156440ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef PCI_H #define PCI_H /* * Provides functions to perform PCI config space reads and writes. * * Copyright (C) 2020 Martin Whitaker. */ #include int pci_init(void); int pci_conf_read(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, uint32_t *value); int pci_conf_write(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, uint32_t value); #endif // PCI_H pcmemtest-1.5/system/pmem.c000066400000000000000000000222621413251666700160250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ memsize.c // // memsize.c - MemTest-86 Version 3.3 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "boot.h" #include "bootparams.h" #include "memsize.h" #include "string.h" #include "pmem.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ // The reserved memory starting at 640KB. #define RESERVED_MEM_START 0x0a0000 #define RESERVED_MEM_END 0x100000 //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ pm_map_t pm_map[MAX_MEM_SEGMENTS]; int pm_map_size = 0; size_t num_pm_pages = 0; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ // Some PC BIOS e820 responses include overlapping entries. // Here we create a new map with the overlaps removed. static int sanitize_e820_map(e820_entry_t new_map[], const e820_entry_t orig_map[], int orig_entries) { struct change_member { const e820_entry_t *entry; // pointer to original bios entry bool start; // true = start addr, false = end addr uint64_t addr; // address for this change point }; // Make these arrays static to keep the stack use small. // This function does not need to be reentrant. static struct change_member change_point_list[2*E820_MAP_SIZE]; static struct change_member *change_point[2*E820_MAP_SIZE]; struct change_member *change_tmp; static const e820_entry_t *overlap_list[E820_MAP_SIZE]; /* Visually we're performing the following (1,2,3,4 = memory types)... Sample memory map (w/overlaps): ____22__________________ ______________________4_ ____1111________________ _44_____________________ 11111111________________ ____________________33__ ___________44___________ __________33333_________ ______________22________ ___________________2222_ _________111111111______ _____________________11_ _________________4______ Sanitized equivalent (no overlap): 1_______________________ _44_____________________ ___1____________________ ____22__________________ ______11________________ _________1______________ __________3_____________ ___________44___________ _____________33_________ _______________2________ ________________1_______ _________________4______ ___________________2____ ____________________33__ ______________________4_ */ // Bail out if we find any unreasonable addresses in the original map. for (int i = 0; i < orig_entries; i++) { if (orig_map[i].addr + orig_map[i].size < orig_map[i].addr) { return 0; } } // Create pointers for initial change-point information (for sorting). for (int i = 0; i < 2*orig_entries; i++) { change_point[i] = &change_point_list[i]; } // Record all known change-points (starting and ending addresses). int chg_idx = 0; for (int i = 0; i < orig_entries; i++) { change_point[chg_idx]->addr = orig_map[i].addr; change_point[chg_idx]->start = true; change_point[chg_idx]->entry = &orig_map[i]; chg_idx++; change_point[chg_idx]->addr = orig_map[i].addr + orig_map[i].size; change_point[chg_idx]->start = false; change_point[chg_idx]->entry = &orig_map[i]; chg_idx++; } // Sort change-point list by memory addresses (low -> high). bool still_changing = true; while (still_changing) { still_changing = false; for (int i = 1; i < 2*orig_entries; i++) { // If current_addr > last_addr or if current_addr = last_addr // and current is a start addr and last is an end addr, swap. if ((change_point[i]->addr < change_point[i-1]->addr) || ((change_point[i]->addr == change_point[i-1]->addr) && change_point[i]->start && !change_point[i-1]->start)) { change_tmp = change_point[i]; change_point[i] = change_point[i-1]; change_point[i-1] = change_tmp; still_changing = true; } } } // Create a new bios memory map, removing overlaps. int overlap_entries = 0; int new_map_entries = 0; uint64_t last_addr = 0; uint32_t last_type = E820_NONE; // Loop through change-points, determining effect on the new map. for (chg_idx = 0; chg_idx < 2*orig_entries; chg_idx++) { // Keep track of all overlapping entries. if (change_point[chg_idx]->start) { // Add map entry to overlap list (> 1 entry implies an overlap) overlap_list[overlap_entries++] = change_point[chg_idx]->entry; } else { // Remove entry from list (order independent, so swap with last) for (int i = 0; i < overlap_entries; i++) { if (overlap_list[i] == change_point[chg_idx]->entry) { overlap_list[i] = overlap_list[overlap_entries-1]; } } overlap_entries--; } // If there are overlapping entries, decide which "type" to use // (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) uint32_t current_type = E820_NONE; for (int i = 0; i < overlap_entries; i++) { if (overlap_list[i]->type > current_type) { current_type = overlap_list[i]->type; } } // Continue building up new map based on this information. if (current_type != last_type) { if (last_type != E820_NONE) { new_map[new_map_entries].size = change_point[chg_idx]->addr - last_addr; // Move forward only if the new size was non-zero if (new_map[new_map_entries].size != 0) { if (++new_map_entries >= E820_MAP_SIZE) { break; } } } if (current_type != E820_NONE) { new_map[new_map_entries].addr = change_point[chg_idx]->addr; new_map[new_map_entries].type = current_type; last_addr = change_point[chg_idx]->addr; } last_type = current_type; } } return new_map_entries; } static void init_pm_map(const e820_entry_t e820_map[], int e820_entries) { pm_map_size = 0; for (int i = 0; i < e820_entries; i++) { if (e820_map[i].type == E820_RAM || e820_map[i].type == E820_ACPI) { uint64_t start = e820_map[i].addr; uint64_t end = start + e820_map[i].size; // Don't ever use memory between 640KB and 1024KB if (start > RESERVED_MEM_START && start < RESERVED_MEM_END) { if (end < RESERVED_MEM_END) { continue; } start = RESERVED_MEM_END; } if (end > RESERVED_MEM_START && end < RESERVED_MEM_END) { end = RESERVED_MEM_START; } pm_map[pm_map_size].start = (start + PAGE_SIZE - 1) >> PAGE_SHIFT; pm_map[pm_map_size].end = end >> PAGE_SHIFT; num_pm_pages += pm_map[pm_map_size].end - pm_map[pm_map_size].start; if ((pm_map_size > 0) && (pm_map[pm_map_size].start == pm_map[pm_map_size - 1].end)) { pm_map[pm_map_size - 1].end = pm_map[pm_map_size].end; } else { pm_map_size++; } } } } static void sort_pm_map(void) { // Do an insertion sort on the pm_map. On an already sorted list this should be a O(1) algorithm. for (int i = 0; i < pm_map_size; i++) { // Find where to insert the current element. int j = i - 1; while (j >= 0) { if (pm_map[i].start > pm_map[j].start) { j++; break; } j--; } // Insert the current element. if (i != j) { pm_map_t temp; temp = pm_map[i]; memmove(&pm_map[j], &pm_map[j+1], (i - j) * sizeof(temp)); pm_map[j] = temp; } } } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void pmem_init(void) { e820_entry_t sanitized_map[E820_MAP_SIZE]; num_pm_pages = 0; const boot_params_t *boot_params = (boot_params_t *)boot_params_addr; int sanitized_entries = sanitize_e820_map(sanitized_map, boot_params->e820_map, boot_params->e820_entries); init_pm_map(sanitized_map, sanitized_entries); sort_pm_map(); } pcmemtest-1.5/system/pmem.h000066400000000000000000000007531413251666700160330ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef PMEM_H #define PMEM_H /* * Provides a description of the system physical memory map. * * Copyright (C) 2020 Martin Whitaker. */ #include #include #define MAX_MEM_SEGMENTS 127 typedef struct { uintptr_t start; uintptr_t end; } pm_map_t; extern pm_map_t pm_map[MAX_MEM_SEGMENTS]; extern int pm_map_size; extern size_t num_pm_pages; void pmem_init(void); #endif /* PMEM_H */ pcmemtest-1.5/system/reloc32.c000066400000000000000000000115311413251666700163350ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ reloc.c: // // reloc.c - MemTest-86 Version 3.3 // // Released under version 2 of the Gnu Public License. // By Eric Biederman #include #include //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ // Dynamic section tag values #define DT_NULL 0 // End of dynamic section #define DT_PLTRELSZ 2 // Size in bytes of PLT relocs #define DT_REL 17 // Address of Rel relocs #define DT_RELSZ 18 // Total size of Rel relocs #define DT_RELENT 19 #define DT_PLTREL 20 // Type of reloc in PLT #define DT_JMPREL 23 // Address of PLT relocs #define DT_NUM 34 // Number of tag values // Relocation types #define R_386_NONE 0 #define R_386_RELATIVE 8 //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef uint32_t Elf32_Addr; typedef int32_t Elf32_Sword; typedef uint32_t Elf32_Word; typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn; typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; } Elf32_Rel; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ #define ELF32_R_TYPE(r_info) ((r_info) & 0xff) static inline void assert(int expr) { if (!expr) { __asm__ __volatile__ ("int $3"); } } /* * Return the run-time load address of the shared object. This must be inlined * in a function which uses global data. */ static inline Elf32_Addr __attribute__ ((unused)) get_load_address(void) { Elf32_Addr addr; __asm__ __volatile__ ( "leal _start@GOTOFF(%%ebx), %0" : "=r" (addr) : : "cc" ); return addr; } /* * Return the link-time address of _DYNAMIC. Conveniently, this is the first * element of the GOT. This must be inlined in a function which uses global * data. */ static inline Elf32_Addr __attribute__ ((unused)) get_dynamic_section_offset(void) { register Elf32_Addr *got __asm__ ("%ebx"); return *got; } static void get_dynamic_info(Elf32_Dyn *dyn_section, Elf32_Addr load_offs, Elf32_Dyn *dyn_info[DT_NUM]) { Elf32_Dyn *dyn = dyn_section; while (dyn->d_tag != DT_NULL) { if (dyn->d_tag < DT_NUM) { dyn_info[dyn->d_tag] = dyn; } dyn++; } if (dyn_info[DT_REL] != NULL) { assert(dyn_info[DT_RELENT]->d_un.d_val == sizeof(Elf32_Rel)); dyn_info[DT_REL]->d_un.d_ptr += load_offs; } if (dyn_info[DT_PLTREL] != NULL) { assert(dyn_info[DT_PLTREL]->d_un.d_val == DT_REL); } if (dyn_info[DT_JMPREL] != NULL) { dyn_info[DT_JMPREL]->d_un.d_ptr += load_offs; } } static void do_relocation(Elf32_Addr load_addr, Elf32_Addr load_offs, const Elf32_Rel *rel) { Elf32_Addr *target_addr = (Elf32_Addr *)(load_addr + rel->r_offset); if (ELF32_R_TYPE(rel->r_info) == R_386_RELATIVE) { *target_addr += load_offs; return; } if (ELF32_R_TYPE(rel->r_info) == R_386_NONE) { return; } assert(! "unexpected dynamic reloc type"); } static void do_relocations(Elf32_Addr load_addr, Elf32_Addr load_offs, Elf32_Addr rel_addr, Elf32_Addr rel_size) { const Elf32_Rel *rel_start = (const Elf32_Rel *)(rel_addr); const Elf32_Rel *rel_end = (const Elf32_Rel *)(rel_addr + rel_size); for (const Elf32_Rel *rel = rel_start; rel < rel_end; rel++) { do_relocation(load_addr, load_offs, rel); } } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void reloc(void) { static volatile Elf32_Addr last_load_addr = 0; Elf32_Dyn *dyn_info[DT_NUM]; for (int i = 0; i < DT_NUM; i++) { dyn_info[i] = NULL; } Elf32_Addr load_addr = get_load_address(); Elf32_Addr load_offs = load_addr - last_load_addr; if (load_addr == last_load_addr) { return; } last_load_addr = load_addr; Elf32_Dyn *dyn_section = (Elf32_Dyn *)(load_addr + get_dynamic_section_offset()); get_dynamic_info(dyn_section, load_offs, dyn_info); do_relocations(load_addr, load_offs, dyn_info[DT_REL]->d_un.d_ptr, dyn_info[DT_RELSZ]->d_un.d_val); if (dyn_info[DT_PLTREL]->d_un.d_val == DT_REL) { do_relocations(load_addr, load_offs, dyn_info[DT_JMPREL]->d_un.d_ptr, dyn_info[DT_PLTRELSZ]->d_un.d_val); } } pcmemtest-1.5/system/reloc64.c000066400000000000000000000117711413251666700163500ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ reloc.c: // // reloc.c - MemTest-86 Version 3.3 // // Released under version 2 of the Gnu Public License. // By Eric Biederman #include #include //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ // Dynamic section tag values #define DT_NULL 0 // End of dynamic section #define DT_PLTRELSZ 2 // Size in bytes of PLT relocs #define DT_RELA 7 // Address of Rel relocs #define DT_RELASZ 8 // Total size of Rel relocs #define DT_RELAENT 9 #define DT_PLTREL 20 // Type of reloc in PLT #define DT_JMPREL 23 // Address of PLT relocs #define DT_NUM 34 // Number of tag values // Relocation types #define R_X86_64_NONE 0 #define R_X86_64_RELATIVE 8 //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef uint64_t Elf64_Addr; typedef int64_t Elf64_Sxword; typedef uint64_t Elf64_Xword; typedef struct { Elf64_Sxword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn; typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ #define ELF64_R_TYPE(r_info) ((r_info) & 0xffffffff) static inline void assert(int expr) { if (!expr) { __asm__ __volatile__ ("int $3"); } } /* * Return the run-time load address of the shared object. */ static inline Elf64_Addr __attribute__ ((unused)) get_load_address(void) { Elf64_Addr addr; __asm__ __volatile__ ( "leaq _start(%%rip), %0" : "=r" (addr) : : "cc" ); return addr; } /* * Return the link-time address of _DYNAMIC. Conveniently, this is the first * element of the GOT. */ static inline Elf64_Addr __attribute__ ((unused)) get_dynamic_section_offset(void) { Elf64_Addr offs; __asm__ __volatile__ ( "movq _GLOBAL_OFFSET_TABLE_(%%rip), %0" : "=r" (offs) : : "cc" ); return offs; } static void get_dynamic_info(Elf64_Dyn *dyn_section, Elf64_Addr load_offs, Elf64_Dyn *dyn_info[DT_NUM]) { Elf64_Dyn *dyn = dyn_section; while (dyn->d_tag != DT_NULL) { if (dyn->d_tag < DT_NUM) { dyn_info[dyn->d_tag] = dyn; } dyn++; } if (dyn_info[DT_RELA] != NULL) { assert(dyn_info[DT_RELAENT]->d_un.d_val == sizeof(Elf64_Rela)); dyn_info[DT_RELA]->d_un.d_ptr += load_offs; } if (dyn_info[DT_PLTREL] != NULL) { assert(dyn_info[DT_PLTREL]->d_un.d_val == DT_RELA); } if (dyn_info[DT_JMPREL] != NULL) { dyn_info[DT_JMPREL]->d_un.d_ptr += load_offs; } } static void do_relocation(Elf64_Addr load_addr, Elf64_Addr load_offs, const Elf64_Rela *rel) { Elf64_Addr *target_addr = (Elf64_Addr *)(load_addr + rel->r_offset); if (ELF64_R_TYPE(rel->r_info) == R_X86_64_RELATIVE) { if (load_offs == load_addr) { *target_addr = load_addr + rel->r_addend; } else { *target_addr += load_offs; } return; } if (ELF64_R_TYPE(rel->r_info) == R_X86_64_NONE) { return; } assert(! "unexpected dynamic reloc type"); } static void do_relocations(Elf64_Addr load_addr, Elf64_Addr load_offs, Elf64_Addr rel_addr, Elf64_Addr rel_size) { const Elf64_Rela *rel_start = (const Elf64_Rela *)(rel_addr); const Elf64_Rela *rel_end = (const Elf64_Rela *)(rel_addr + rel_size); for (const Elf64_Rela *rel = rel_start; rel < rel_end; rel++) { do_relocation(load_addr, load_offs, rel); } } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void reloc(void) { static volatile Elf64_Addr last_load_addr = 0; Elf64_Dyn *dyn_info[DT_NUM]; for (int i = 0; i < DT_NUM; i++) { dyn_info[i] = NULL; } Elf64_Addr load_addr = get_load_address(); Elf64_Addr load_offs = load_addr - last_load_addr; if (load_addr == last_load_addr) { return; } last_load_addr = load_addr; Elf64_Dyn *dyn_section = (Elf64_Dyn *)(load_addr + get_dynamic_section_offset()); get_dynamic_info(dyn_section, load_offs, dyn_info); do_relocations(load_addr, load_offs, dyn_info[DT_RELA]->d_un.d_ptr, dyn_info[DT_RELASZ]->d_un.d_val); if (dyn_info[DT_PLTREL]->d_un.d_val == DT_RELA) { do_relocations(load_addr, load_offs, dyn_info[DT_JMPREL]->d_un.d_ptr, dyn_info[DT_PLTRELSZ]->d_un.d_val); } } pcmemtest-1.5/system/screen.c000066400000000000000000000251141413251666700163450ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker #include #include #include "boot.h" #include "bootparams.h" #include "font.h" #include "vmem.h" #include "screen.h" //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef struct { uint8_t r; uint8_t g; uint8_t b; } __attribute__((packed)) rgb_value_t; typedef union { struct { uint8_t ch; uint8_t attr; }; struct { uint16_t value; }; } vga_char_t; typedef vga_char_t vga_buffer_t[SCREEN_HEIGHT][SCREEN_WIDTH]; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static const rgb_value_t vga_pallete[16] = { // R G B { 0, 0, 0 }, // BLACK { 0, 0, 170 }, // BLUE { 0, 170, 0 }, // GREEN { 0, 170, 170 }, // CYAN { 170, 0, 0 }, // RED { 170, 0, 170 }, // MAUVE { 170, 85, 0 }, // YELLOW (brown really) { 170, 170, 170 }, // WHITE { 85, 85, 85 }, // BOLD+BLACK { 85, 85, 255 }, // BOLD+BLUE { 85, 255, 85 }, // BOLD+GREEN { 85, 255, 255 }, // BOLD+CYAN { 255, 85, 85 }, // BOLD+RED { 255, 85, 255 }, // BOLD+MAUVE { 255, 255, 85 }, // BOLD+YELLOW { 255, 255, 255 } // BOLD+WHITE }; static vga_buffer_t *vga_buffer = (vga_buffer_t *)(0xb8000); static vga_buffer_t shadow_buffer; static int lfb_bytes_per_pixel = 0; static uintptr_t lfb_base; static uintptr_t lfb_stride; static uint32_t lfb_pallete[16]; static uint8_t current_attr = WHITE | BLUE << 4; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static void vga_put_char(int row, int col, uint8_t ch, uint8_t attr) { shadow_buffer[row][col].ch = ch; shadow_buffer[row][col].attr = attr; (*vga_buffer)[row][col].value = shadow_buffer[row][col].value; } static void lfb8_put_char(int row, int col, uint8_t ch, uint8_t attr) { shadow_buffer[row][col].ch = ch; shadow_buffer[row][col].attr = attr; uint8_t fg_colour = attr % 16; uint8_t bg_colour = attr / 16; uint8_t *pixel_row = (uint8_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH; for (int y = 0; y < FONT_HEIGHT; y++) { uint8_t font_row = font_data[ch][y]; for (int x = 0; x < FONT_WIDTH; x++) { pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour; font_row <<= 1; } pixel_row += lfb_stride; } } static void lfb16_put_char(int row, int col, uint8_t ch, uint8_t attr) { shadow_buffer[row][col].ch = ch; shadow_buffer[row][col].attr = attr; uint16_t fg_colour = lfb_pallete[attr % 16]; uint16_t bg_colour = lfb_pallete[attr / 16]; uint16_t *pixel_row = (uint16_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH; for (int y = 0; y < FONT_HEIGHT; y++) { uint8_t font_row = font_data[ch][y]; for (int x = 0; x < FONT_WIDTH; x++) { pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour; font_row <<= 1; } pixel_row += lfb_stride; } } static void lfb24_put_char(int row, int col, uint8_t ch, uint8_t attr) { shadow_buffer[row][col].ch = ch; shadow_buffer[row][col].attr = attr; uint32_t fg_colour = lfb_pallete[attr % 16]; uint32_t bg_colour = lfb_pallete[attr / 16]; uint8_t *pixel_row = (uint8_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH * 3; for (int y = 0; y < FONT_HEIGHT; y++) { uint8_t font_row = font_data[ch][y]; for (int x = 0; x < FONT_WIDTH * 3; x += 3) { uint32_t colour = font_row & 0x80 ? fg_colour : bg_colour; pixel_row[x+0] = colour & 0xff; colour >>= 8; pixel_row[x+1] = colour & 0xff; colour >>= 8; pixel_row[x+2] = colour & 0xff; font_row <<= 1; } pixel_row += lfb_stride; } } static void lfb32_put_char(int row, int col, uint8_t ch, uint8_t attr) { shadow_buffer[row][col].ch = ch; shadow_buffer[row][col].attr = attr; uint32_t fg_colour = lfb_pallete[attr % 16]; uint32_t bg_colour = lfb_pallete[attr / 16]; uint32_t *pixel_row = (uint32_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH; for (int y = 0; y < FONT_HEIGHT; y++) { uint8_t font_row = font_data[ch][y]; for (int x = 0; x < FONT_WIDTH; x++) { pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour; font_row <<= 1; } pixel_row += lfb_stride; } } static void (*put_char)(int, int, uint8_t, uint8_t) = vga_put_char; static void put_value(int row, int col, uint16_t value) { put_char(row, col, value % 256, value / 256); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void screen_init(void) { const boot_params_t *boot_params = (boot_params_t *)boot_params_addr; const screen_info_t *screen_info = &boot_params->screen_info; bool use_lfb = screen_info->orig_video_isVGA == VIDEO_TYPE_VLFB || screen_info->orig_video_isVGA == VIDEO_TYPE_EFI; if (use_lfb) { int lfb_width = screen_info->lfb_width; int lfb_height = screen_info->lfb_height; int lfb_depth = screen_info->lfb_depth; if (lfb_depth <= 8) { lfb_bytes_per_pixel = 1; put_char = lfb8_put_char; } else if (lfb_depth <= 16) { lfb_bytes_per_pixel = 2; put_char = lfb16_put_char; } else if (lfb_depth <= 24) { lfb_bytes_per_pixel = 3; put_char = lfb24_put_char; } else { lfb_bytes_per_pixel = 4; put_char = lfb32_put_char; } lfb_base = screen_info->lfb_base; #ifdef __x86_64__ if (LFB_CAPABILITY_64BIT_BASE & screen_info->capabilities) { lfb_base |= (uintptr_t)screen_info->ext_lfb_base << 32; } #endif lfb_stride = screen_info->lfb_linelength; lfb_base = map_framebuffer(lfb_base, lfb_height * lfb_width * lfb_bytes_per_pixel); // Blank the whole framebuffer. int pixels_per_word = sizeof(uint32_t) / lfb_bytes_per_pixel; uint32_t *line = (uint32_t *)lfb_base; for (int y = 0; y < lfb_height; y++) { for (int x = 0; x < (lfb_width / pixels_per_word); x++) { line[x] = 0; } line += lfb_stride / sizeof(uint32_t); } int excess_width = lfb_width - (SCREEN_WIDTH * FONT_WIDTH); if (excess_width > 0) { lfb_base += (excess_width / 2) * lfb_bytes_per_pixel; } int excess_height = lfb_height - (SCREEN_HEIGHT * FONT_HEIGHT); if (excess_height > 0) { lfb_base += (excess_height / 2) * lfb_stride; } if (lfb_bytes_per_pixel != 3) { lfb_stride /= lfb_bytes_per_pixel; } // Initialise the pallete. uint32_t r_max = (1 << screen_info->red_size ) - 1; uint32_t g_max = (1 << screen_info->green_size) - 1; uint32_t b_max = (1 << screen_info->blue_size ) - 1; for (int i = 0; i < 16; i++) { uint32_t r = ((vga_pallete[i].r * r_max) / 255) << screen_info->red_pos; uint32_t g = ((vga_pallete[i].g * g_max) / 255) << screen_info->green_pos; uint32_t b = ((vga_pallete[i].b * b_max) / 255) << screen_info->blue_pos; lfb_pallete[i] = r | g | b; } } } void set_foreground_colour(screen_colour_t colour) { current_attr = (current_attr & 0xf0) | (colour & 0x0f); } void set_background_colour(screen_colour_t colour) { current_attr = (current_attr & 0x8f) | ((colour << 4) & 0x70); } void clear_screen(void) { for (int row = 0; row < SCREEN_HEIGHT; row++) { for (int col = 0; col < SCREEN_WIDTH; col++) { put_char(row, col, ' ', current_attr); } } } void clear_screen_region(int start_row, int start_col, int end_row, int end_col) { if (start_row < 0) start_row = 0; if (start_col < 0) start_col = 0; if (end_row >= SCREEN_HEIGHT) end_row = SCREEN_HEIGHT - 1; if (end_col >= SCREEN_WIDTH) end_col = SCREEN_WIDTH - 1; if (start_row > end_row) return; if (start_col > end_col) return; for (int row = start_row; row <= end_row; row++) { for (int col = start_col; col <= end_col; col++) { put_char(row, col, ' ', current_attr); } } } void scroll_screen_region(int start_row, int start_col, int end_row, int end_col) { if (start_row < 0) start_row = 0; if (start_col < 0) start_col = 0; if (end_row >= SCREEN_HEIGHT) end_row = SCREEN_HEIGHT - 1; if (end_col >= SCREEN_WIDTH) end_col = SCREEN_WIDTH - 1; if (start_row > end_row) return; if (start_col > end_col) return; for (int row = start_row; row <= end_row; row++) { for (int col = start_col; col <= end_col; col++) { if (row < end_row) { put_value(row, col, shadow_buffer[row + 1][col].value); } else { put_char(row, col, ' ', current_attr); } } } } void save_screen_region(int start_row, int start_col, int end_row, int end_col, uint16_t buffer[]) { if (start_row < 0) start_row = 0; if (start_col < 0) start_col = 0; uint16_t *dst = &buffer[0]; for (int row = start_row; row <= end_row; row++) { if (row >= SCREEN_HEIGHT) break; for (int col = start_col; col <= end_col; col++) { if (col >= SCREEN_WIDTH) break; *dst++ = shadow_buffer[row][col].value; } } } void restore_screen_region(int start_row, int start_col, int end_row, int end_col, const uint16_t buffer[]) { if (start_row < 0) start_row = 0; if (start_col < 0) start_col = 0; const uint16_t *src = &buffer[0]; for (int row = start_row; row <= end_row; row++) { if (row >= SCREEN_HEIGHT) break; for (int col = start_col; col <= end_col; col++) { if (col >= SCREEN_WIDTH) break; put_value(row, col, *src++); } } } void print_char(int row, int col, char ch) { if (row < 0 || row >= SCREEN_HEIGHT) return; if (col < 0 || col >= SCREEN_WIDTH) return; put_char(row, col, ch, (current_attr & 0x0f) | (shadow_buffer[row][col].attr & 0xf0)); } pcmemtest-1.5/system/screen.h000066400000000000000000000043321413251666700163510ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef SCREEN_H #define SCREEN_H /* * Provides the display interface. It provides an 80x25 VGA-compatible text * display. * * Copyright (C) 2020 Martin Whitaker. */ #include /* * Screen size definitions. The screen size cannot be changed. */ #define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 25 /* * Colours that can be used for the foreground or background. */ typedef enum { BLACK = 0, BLUE = 1, GREEN = 2, CYAN = 3, RED = 4, MAUVE = 5, YELLOW = 6, WHITE = 7 } screen_colour_t; /* * Modifier that can be added to any foreground colour. * Has no effect on background colours. */ #define BOLD 8 /* * Initialise the display interface. */ void screen_init(void); /* * Set the foreground colour used for subsequent drawing operations. */ void set_foreground_colour(screen_colour_t colour); /* * Set the background colour used for subsequent drawing operations. */ void set_background_colour(screen_colour_t colour); /* * Clear the whole screen, using the current background colour. */ void clear_screen(void); /* * Clear the specified region of the screen, using the current background * colour. */ void clear_screen_region(int start_row, int start_col, int end_row, int end_col); /* * Move the contents of the specified region of the screen up one row, * discarding the top row, and clearing the bottom row, using the current * background colour. */ void scroll_screen_region(int start_row, int start_col, int end_row, int end_col); /* * Copy the contents of the specified region of the screen into the supplied * buffer. */ void save_screen_region(int start_row, int start_col, int end_row, int end_col, uint16_t buffer[]); /* * Restore the specified region of the screen from the supplied buffer. * This restores both text and colours. */ void restore_screen_region(int start_row, int start_col, int end_row, int end_col, const uint16_t buffer[]); /* * Write the supplied character to the specified screen location, using the * current foreground colour. Has no effect if the location is outside the * screen. */ void print_char(int row, int col, char ch); #endif // SCREEN_H pcmemtest-1.5/system/smp.c000066400000000000000000000517321413251666700156720ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ smp.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // ------------------------------------------------ // smp.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 "efi.h" #include "memsize.h" #include "pmem.h" #include "string.h" #include "unistd.h" #include "smp.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #define MAX_APIC_IDS 256 // APIC registers #define APICR_ID 0x02 #define APICR_ESR 0x28 #define APICR_ICRLO 0x30 #define APICR_ICRHI 0x31 // APIC destination shorthands #define APIC_DEST_DEST 0 #define APIC_DEST_LOCAL 1 #define APIC_DEST_ALL_INC 2 #define APIC_DEST_ALL_EXC 3 // APIC IPI Command Register format #define APIC_ICRHI_RESERVED 0x00ffffff #define APIC_ICRHI_DEST_MASK 0xff000000 #define APIC_ICRHI_DEST_OFFSET 24 #define APIC_ICRLO_RESERVED 0xfff32000 #define APIC_ICRLO_DEST_MASK 0x000c0000 #define APIC_ICRLO_DEST_OFFSET 18 #define APIC_ICRLO_TRIGGER_MASK 0x00008000 #define APIC_ICRLO_TRIGGER_OFFSET 15 #define APIC_ICRLO_LEVEL_MASK 0x00004000 #define APIC_ICRLO_LEVEL_OFFSET 14 #define APIC_ICRLO_STATUS_MASK 0x00001000 #define APIC_ICRLO_STATUS_OFFSET 12 #define APIC_ICRLO_DESTMODE_MASK 0x00000800 #define APIC_ICRLO_DESTMODE_OFFSET 11 #define APIC_ICRLO_DELMODE_MASK 0x00000700 #define APIC_ICRLO_DELMODE_OFFSET 8 #define APIC_ICRLO_VECTOR_MASK 0x000000ff #define APIC_ICRLO_VECTOR_OFFSET 0 // APIC trigger types #define APIC_TRIGGER_EDGE 0 #define APIC_TRIGGER_LEVEL 1 // APIC delivery modes #define APIC_DELMODE_FIXED 0 #define APIC_DELMODE_LOWEST 1 #define APIC_DELMODE_SMI 2 #define APIC_DELMODE_NMI 4 #define APIC_DELMODE_INIT 5 #define APIC_DELMODE_STARTUP 6 #define APIC_DELMODE_EXTINT 7 // Table signatures #define FPSignature ('_' | ('M' << 8) | ('P' << 16) | ('_' << 24)) #define MPCSignature ('P' | ('C' << 8) | ('M' << 16) | ('P' << 24)) #define RSDPSignature1 ('R' | ('S' << 8) | ('D' << 16) | (' ' << 24)) #define RSDPSignature2 ('P' | ('T' << 8) | ('R' << 16) | (' ' << 24)) #define RSDTSignature ('R' | ('S' << 8) | ('D' << 16) | ('T' << 24)) #define XSDTSignature ('X' | ('S' << 8) | ('D' << 16) | ('T' << 24)) #define MADTSignature ('A' | ('P' << 8) | ('I' << 16) | ('C' << 24)) // MP config table entry types #define MP_PROCESSOR 0 #define MP_BUS 1 #define MP_IOAPIC 2 #define MP_INTSRC 3 #define MP_LINTSRC 4 // MP processor cpu_flag values #define CPU_ENABLED 1 #define CPU_BOOTPROCESSOR 2 // MADT processor flag values #define MADT_PF_ENABLED 0x1 #define MADT_PF_ONLINE_CAPABLE 0x2 // Private memory heap used for AP trampoline and synchronisation objects #define HEAP_BASE_ADDR (smp_heap_page << PAGE_SHIFT) #define AP_TRAMPOLINE_PAGE (smp_heap_page) //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ typedef uint32_t apic_register_t[4]; typedef struct { uint32_t signature; // "_MP_" uint32_t phys_addr; uint8_t length; uint8_t spec_rev; uint8_t checksum; uint8_t feature[5]; } floating_pointer_struct_t; typedef struct { uint32_t signature; // "PCMP" uint16_t length; uint8_t spec_rev; uint8_t checksum; char oem[8]; char product_id[12]; uint32_t oem_ptr; uint16_t oem_size; uint16_t oem_count; uint32_t lapic_addr; uint32_t reserved; } mp_config_table_header_t; typedef struct { uint8_t type; // MP_PROCESSOR uint8_t apic_id; uint8_t apic_ver; uint8_t cpu_flag; uint32_t cpu_signature; uint32_t feature_flag; uint32_t reserved[2]; } mp_processor_entry_t; typedef struct { uint8_t type; // MP_BUS uint8_t bus_id; char bus_type[6]; } mp_bus_entry_t; typedef struct { uint8_t type; // MP_IOAPIC uint8_t apic_id; uint8_t apic_ver; uint8_t flags; uint32_t apic_addr; } mp_io_apic_entry_t; typedef struct { uint8_t type; uint8_t irq_type; uint16_t irq_flag; uint8_t src_bus_id; uint8_t src_bus_irq; uint8_t dst_apic; uint8_t dst_irq; } mp_interrupt_entry_t; typedef struct { uint8_t type; uint8_t irq_type; uint16_t irq_flag; uint8_t src_bus_id; uint8_t src_bus_irq; uint8_t dst_apic; uint8_t dst_apic_lint; } mp_local_interrupt_entry_t; typedef struct { char signature[8]; // "RSD PTR " uint8_t checksum; char oem_id[6]; uint8_t revision; uint32_t rsdt_addr; uint32_t length; uint64_t xsdt_addr; uint8_t xchecksum; uint8_t reserved[3]; } rsdp_t; typedef struct { char signature[4]; // "RSDT" or "XSDT" uint32_t length; uint8_t revision; uint8_t checksum; char oem_id[6]; char oem_table_id[8]; char oem_revision[4]; char creator_id[4]; char creator_revision[4]; } rsdt_header_t; typedef struct { uint8_t type; uint8_t length; uint8_t acpi_id; uint8_t apic_id; uint32_t flags; } madt_processor_entry_t; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static const efi_guid_t EFI_ACPI_1_RDSP_GUID = { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }; static const efi_guid_t EFI_ACPI_2_RDSP_GUID = { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81} }; static volatile apic_register_t *apic = NULL; static int8_t apic_id_to_pcpu_num[MAX_APIC_IDS]; static uint8_t pcpu_num_to_apic_id[MAX_PCPUS]; static volatile bool cpu_started[MAX_PCPUS]; static uintptr_t smp_heap_page = 0; static uintptr_t alloc_addr = 0; //------------------------------------------------------------------------------ // Variables //------------------------------------------------------------------------------ int num_pcpus = 1; // There is always at least one CPU, the BSP const char *rsdp_source = ""; uintptr_t rsdp_addr = 0; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static int my_apic_id(void) { return (apic[APICR_ID][0]) >> 24; } static void apic_write(unsigned reg, uint32_t val) { apic[reg][0] = val; } static uint32_t apic_read(unsigned reg) { return apic[reg][0]; } static void send_ipi(unsigned apic_id, unsigned trigger, unsigned level, unsigned mode, uint8_t vector) { uint32_t v; v = apic_read(APICR_ICRHI) & 0x00ffffff; apic_write(APICR_ICRHI, v | (apic_id << 24)); v = apic_read(APICR_ICRLO) & ~0xcdfff; v |= APIC_DEST_DEST << APIC_ICRLO_DEST_OFFSET; v |= trigger << APIC_ICRLO_TRIGGER_OFFSET; v |= level << APIC_ICRLO_LEVEL_OFFSET; v |= mode << APIC_ICRLO_DELMODE_OFFSET; v |= vector; apic_write(APICR_ICRLO, v); } static int checksum(const void *data, int length) { uint8_t sum = 0; uint8_t *ptr = (uint8_t *)data; while (length--) { sum += *ptr++; } return sum; } static floating_pointer_struct_t *scan_for_floating_ptr_struct(uintptr_t addr, int length) { uint32_t *ptr = (uint32_t *)addr; uint32_t *end = ptr + length / sizeof(uint32_t); while (ptr < end) { if (*ptr == FPSignature && checksum(ptr, 16) == 0) { floating_pointer_struct_t *fp = (floating_pointer_struct_t *)ptr; if (fp->length == 1 && (fp->spec_rev == 1 || fp->spec_rev == 4)) { return fp; } } ptr++; } return NULL; } static bool read_mp_config_table(uintptr_t addr) { mp_config_table_header_t *mpc = (mp_config_table_header_t *)addr; if (mpc->signature != MPCSignature || checksum(mpc, mpc->length) != 0) { return false; } apic = (volatile apic_register_t *)((uintptr_t)mpc->lapic_addr); uint8_t *tab_entry_ptr = (uint8_t *)mpc + sizeof(mp_config_table_header_t); uint8_t *mpc_table_end = (uint8_t *)mpc + mpc->length; while (tab_entry_ptr < mpc_table_end) { switch (*tab_entry_ptr) { case MP_PROCESSOR: { mp_processor_entry_t *entry = (mp_processor_entry_t *)tab_entry_ptr; if (entry->cpu_flag & CPU_BOOTPROCESSOR) { // BSP is CPU 0 pcpu_num_to_apic_id[0] = entry->apic_id; } else if (num_pcpus < MAX_PCPUS) { pcpu_num_to_apic_id[num_pcpus] = entry->apic_id; num_pcpus++; } // we cannot handle non-local 82489DX apics if ((entry->apic_ver & 0xf0) != 0x10) { num_pcpus = 1; // reset to initial value return false; } tab_entry_ptr += sizeof(mp_processor_entry_t); break; } case MP_BUS: { tab_entry_ptr += sizeof(mp_bus_entry_t); break; } case MP_IOAPIC: { tab_entry_ptr += sizeof(mp_io_apic_entry_t); break; } case MP_INTSRC: tab_entry_ptr += sizeof(mp_interrupt_entry_t); break; case MP_LINTSRC: tab_entry_ptr += sizeof(mp_local_interrupt_entry_t); break; default: num_pcpus = 1; // reset to initial value return false; } } return true; } static bool find_cpus_in_floating_mp_struct(void) { // Search for the Floating MP structure pointer. floating_pointer_struct_t *fp = scan_for_floating_ptr_struct(0x0, 0x400); if (fp == NULL) { fp = scan_for_floating_ptr_struct(639*0x400, 0x400); } if (fp == NULL) { fp = scan_for_floating_ptr_struct(0xf0000, 0x10000); } if (fp == NULL) { // Search the BIOS EBDA area. uintptr_t address = *(uint16_t *)0x40E << 4; if (address) { fp = scan_for_floating_ptr_struct(address, 0x400); } } if (fp == NULL) { // Floating MP structure pointer not found - give up. return false; } if (fp->feature[0] > 0 && fp->feature[0] <= 7) { // This is a default config, so plug in the numbers. apic = (volatile apic_register_t *)0xFEE00000; pcpu_num_to_apic_id[0] = 0; pcpu_num_to_apic_id[1] = 1; num_pcpus = 2; return true; } // Do we have a pointer to a MP configuration table? if (fp->phys_addr != 0) { if (read_mp_config_table(fp->phys_addr)) { // Found a good MP table, done. return true; } } return false; } static rsdp_t *scan_for_rsdp(uintptr_t addr, int length) { uint32_t *ptr = (uint32_t *)addr; uint32_t *end = ptr + length / sizeof(uint32_t); while (ptr < end) { rsdp_t *rp = (rsdp_t *)ptr; if (*ptr == RSDPSignature1 && *(ptr+1) == RSDPSignature2 && checksum(ptr, 20) == 0) { if (rp->revision < 2 || (rp->length < 1024 && checksum(ptr, rp->length) == 0)) { return rp; } } ptr += 4; } return NULL; } static bool parse_madt(void *addr) { mp_config_table_header_t *mpc = (mp_config_table_header_t *)addr; if (checksum(mpc, mpc->length) != 0) { return false; } apic = (volatile apic_register_t *)((uintptr_t)mpc->lapic_addr); int found_cpus = 0; uint8_t *tab_entry_ptr = (uint8_t *)mpc + sizeof(mp_config_table_header_t); uint8_t *mpc_table_end = (uint8_t *)mpc + mpc->length; while (tab_entry_ptr < mpc_table_end) { madt_processor_entry_t *entry = (madt_processor_entry_t *)tab_entry_ptr; if (entry->type == MP_PROCESSOR) { if (entry->flags & (MADT_PF_ENABLED|MADT_PF_ONLINE_CAPABLE)) { if (num_pcpus < MAX_PCPUS) { pcpu_num_to_apic_id[found_cpus] = entry->apic_id; // The first CPU is the BSP, don't increment. if (found_cpus > 0) { num_pcpus++; } } found_cpus++; } } tab_entry_ptr += entry->length; } return true; } static rsdp_t *find_rsdp_in_efi32_system_table(efi32_system_table_t *system_table) { efi32_config_table_t *config_tables = (efi32_config_table_t *)((uintptr_t)system_table->config_tables); uintptr_t table_addr = 0; for (uint32_t i = 0; i < system_table->num_config_tables; i++) { if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) { table_addr = config_tables[i].table; break; } if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) { table_addr = config_tables[i].table; } } return (rsdp_t *)table_addr; } #ifdef __x86_64__ static rsdp_t *find_rsdp_in_efi64_system_table(efi64_system_table_t *system_table) { efi64_config_table_t *config_tables = (efi64_config_table_t *)((uintptr_t)system_table->config_tables); uintptr_t table_addr = 0; for (uint32_t i = 0; i < system_table->num_config_tables; i++) { if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) { table_addr = config_tables[i].table; break; } if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) { table_addr = config_tables[i].table; } } return (rsdp_t *)table_addr; } #endif static bool find_cpus_in_rsdp(void) { const boot_params_t *boot_params = (boot_params_t *)boot_params_addr; const efi_info_t *efi_info = &boot_params->efi_info; // Search for the RSDP rsdp_t *rp = NULL; if (boot_params->acpi_rsdp_addr != 0) { // Validate it rp = scan_for_rsdp(boot_params->acpi_rsdp_addr, 0x8); if (rp) rsdp_source = "boot parameters"; } if (rp == NULL && efi_info->loader_signature == EFI32_LOADER_SIGNATURE) { uintptr_t system_table_addr = (uintptr_t)efi_info->sys_tab; rp = find_rsdp_in_efi32_system_table((efi32_system_table_t *)system_table_addr); if (rp) rsdp_source = "EFI32 system table"; } #ifdef __x86_64__ if (rp == NULL && efi_info->loader_signature == EFI64_LOADER_SIGNATURE) { uintptr_t system_table_addr = (uintptr_t)efi_info->sys_tab_hi << 32 | (uintptr_t)efi_info->sys_tab; rp = find_rsdp_in_efi64_system_table((efi64_system_table_t *)system_table_addr); if (rp) rsdp_source = "EFI64 system table"; } #endif if (rp == NULL) { // Search the BIOS EBDA area. uintptr_t address = *(uint16_t *)0x40E << 4; if (address) { rp = scan_for_rsdp(address, 0x400); if (rp) rsdp_source = "BIOS EBDA"; } } if (rp == NULL) { // Search the BIOS reserved area. rp = scan_for_rsdp(0xE0000, 0x20000); if (rp) rsdp_source = "BIOS reserved area"; } if (rp == NULL) { // RSDP not found, give up. return false; } rsdp_addr = (uintptr_t)rp; // Found the RSDP, now get either the RSDT or XSDT and scan it for a pointer to the MADT. rsdt_header_t *rt; if (rp->revision >= 2) { rt = (rsdt_header_t *)((uintptr_t)rp->xsdt_addr); if (rt == 0) { return false; } // Validate the XSDT. if (*(uint32_t *)rt != XSDTSignature) { return false; } if (checksum(rt, rt->length) != 0) { return false; } // Scan the XSDT for a pointer to the MADT. uint64_t *tab_ptr = (uint64_t *)((uint8_t *)rt + sizeof(rsdt_header_t)); uint64_t *tab_end = (uint64_t *)((uint8_t *)rt + rt->length); while (tab_ptr < tab_end) { uint32_t *ptr = (uint32_t *)((uintptr_t)(*tab_ptr++)); // read the next table entry if (ptr && *ptr == MADTSignature) { if (parse_madt(ptr)) { return true; } } } } else { rt = (rsdt_header_t *)((uintptr_t)rp->rsdt_addr); if (rt == 0) { return false; } // Validate the RSDT. if (*(uint32_t *)rt != RSDTSignature) { return false; } if (checksum(rt, rt->length) != 0) { return false; } // Scan the RSDT for a pointer to the MADT. uint32_t *tab_ptr = (uint32_t *)((uint8_t *)rt + sizeof(rsdt_header_t)); uint32_t *tab_end = (uint32_t *)((uint8_t *)rt + rt->length); while (tab_ptr < tab_end) { uint32_t *ptr = (uint32_t *)((uintptr_t)(*tab_ptr++)); // read the next table entry if (ptr && *ptr == MADTSignature) { if (parse_madt(ptr)) { return true; } } } } return false; } static smp_error_t start_cpu(int pcpu_num) { int apic_id = pcpu_num_to_apic_id[pcpu_num]; // Clear the APIC ESR register. apic_write(APICR_ESR, 0); apic_read(APICR_ESR); // Pulse the INIT IPI. send_ipi(apic_id, APIC_TRIGGER_LEVEL, 1, APIC_DELMODE_INIT, 0); usleep(100000); send_ipi(apic_id, APIC_TRIGGER_LEVEL, 0, APIC_DELMODE_INIT, 0); for (int num_sipi = 0; num_sipi < 2; num_sipi++) { apic_write(APICR_ESR, 0); send_ipi(apic_id, 0, 0, APIC_DELMODE_STARTUP, AP_TRAMPOLINE_PAGE); bool send_pending; int timeout = 0; do { usleep(10); timeout++; send_pending = (apic_read(APICR_ICRLO) & APIC_ICRLO_STATUS_MASK) != 0; } while (send_pending && timeout < 1000); if (send_pending) { return SMP_ERR_STARTUP_IPI_NOT_SENT; } usleep(100000); uint32_t error = apic_read(APICR_ESR) & 0xef; if (error) { return SMP_ERR_STARTUP_IPI_ERROR + error; } } int timeout = 0; do { usleep(10); timeout++; } while (!cpu_started[pcpu_num] && timeout < 100000); if (!cpu_started[pcpu_num]) { return SMP_ERR_BOOT_TIMEOUT; } return SMP_ERR_NONE; } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void smp_init(bool smp_enable) { for (int i = 0; i < MAX_APIC_IDS; i++) { apic_id_to_pcpu_num[i] = 0; } for (int i = 0; i < MAX_PCPUS; i++) { pcpu_num_to_apic_id[i] = 0; cpu_started[i] = false; } num_pcpus = 1; if (smp_enable) { (void)(find_cpus_in_rsdp() || find_cpus_in_floating_mp_struct()); } for (int i = 0; i < num_pcpus; i++) { apic_id_to_pcpu_num[pcpu_num_to_apic_id[i]] = i; } // Reserve last page of first segment for AP trampoline and sync objects. // These need to remain pinned in place during relocation. smp_heap_page = --pm_map[0].end; ap_startup_addr = (uintptr_t)startup; size_t ap_trampoline_size = ap_trampoline_end - ap_trampoline; memcpy((uint8_t *)HEAP_BASE_ADDR, ap_trampoline, ap_trampoline_size); alloc_addr = HEAP_BASE_ADDR + ap_trampoline_size; } smp_error_t smp_start(bool enable_pcpu[MAX_PCPUS]) { enable_pcpu[0] = true; // we don't support disabling the boot CPU for (int i = 1; i < num_pcpus; i++) { if (enable_pcpu[i]) { smp_error_t error = start_cpu(i); if (error != SMP_ERR_NONE) { return error; } } } return SMP_ERR_NONE; } void smp_set_ap_booted(int pcpu_num) { cpu_started[pcpu_num] = true; } int smp_my_pcpu_num(void) { return num_pcpus > 1 ? apic_id_to_pcpu_num[my_apic_id()] : 0; } barrier_t *smp_alloc_barrier(int num_threads) { barrier_t *barrier = (barrier_t *)(alloc_addr); alloc_addr += sizeof(barrier_t); barrier_init(barrier, num_threads); return barrier; } spinlock_t *smp_alloc_mutex() { spinlock_t *mutex = (spinlock_t *)(alloc_addr); alloc_addr += sizeof(spinlock_t); spin_unlock(mutex); return mutex; } pcmemtest-1.5/system/smp.h000066400000000000000000000031241413251666700156670ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef _SMP_H_ #define _SMP_H_ /* * Provides support for multi-threaded operation. * * Copyright (C) 2020 Martin Whitaker. */ #include #include "boot.h" #include "barrier.h" #include "spinlock.h" /* * The maximum number of active physical CPUs. This only affects memory * footprint, so can be increased if needed. */ #define MAX_PCPUS (1 + MAX_APS) /* * An error code returned by smp_start(). */ typedef enum { SMP_ERR_NONE = 0, SMP_ERR_BOOT_TIMEOUT = 1, SMP_ERR_STARTUP_IPI_NOT_SENT = 2, SMP_ERR_STARTUP_IPI_ERROR = 0x100 // error code will be added to this } smp_error_t; /* * The number of available physical CPUs. Initially this is 1, but may * increase after calling smp_init(). */ extern int num_pcpus; /* * The search step that located the ACPI RSDP (for debug). */ extern const char *rsdp_source; /* * The address of the ACPI RSDP (for debug). */ extern uintptr_t rsdp_addr; /* * Initialises the SMP state and detects the number of physical CPUs. */ void smp_init(bool smp_enable); /* * Starts the selected APs. */ smp_error_t smp_start(bool enable_pcpu[MAX_PCPUS]); /* * Signals that an AP has booted. */ void smp_set_ap_booted(int pcpu_num); /* * Returns the ordinal number of the calling PCPU. */ int smp_my_pcpu_num(void); /* * Allocates and initialises a barrier object in pinned memory. */ barrier_t *smp_alloc_barrier(int num_threads); /* * Allocates and initialises a spinlock object in pinned memory. */ spinlock_t *smp_alloc_mutex(); #endif /* _SMP_H_ */ pcmemtest-1.5/system/temperature.c000066400000000000000000000031761413251666700174270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ init.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // ------------------------------------------------ // init.c - MemTest-86 Version 3.6 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include "cpuid.h" #include "cpuinfo.h" #include "msr.h" #include "pci.h" #include "temperature.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int get_cpu_temperature(void) { if (imc_type == 0) { return 0; } // Intel CPU if (cpuid_info.vendor_id.str[0] == 'G' && cpuid_info.max_vcpuid >= 6) { if (cpuid_info.dts_pmp & 1) { uint32_t msrl, msrh; rdmsr(MSR_IA32_THERM_STATUS, msrl, msrh); int Tabs = (msrl >> 16) & 0x7F; rdmsr(MSR_IA32_TEMPERATURE_TARGET, msrl, msrh); int Tjunc = (msrl >> 16) & 0x7F; if (Tjunc < 50 || Tjunc > 125) { Tjunc = 90; } return Tjunc - Tabs; } } #if 0 // TODO: This doesn't give accurate results. // AMD CPU if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.extendedFamily > 0) { uint32_t rtcr; pci_conf_read(0, 24, 3, 0xA4, 4, &rtcr); int raw_temp = (rtcr >> 21) & 0x7FF; return raw_temp / 8; } #endif return 0; } pcmemtest-1.5/system/temperature.h000066400000000000000000000005311413251666700174240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TEMPERATURE_H #define TEMPERATURE_H /* * Provides a function to read the CPU core temperature. * * Copyright (C) 2020 Martin Whitaker. */ /* * Returns the current temperature of the CPU. Returns 0 if * the temperature cannot be read. */ int get_cpu_temperature(void); #endif // TEMPERATURE_H pcmemtest-1.5/system/tsc.h000066400000000000000000000013711413251666700156630ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TSC_H #define TSC_H /* * Provides access to the CPU timestamp counter. * * Copyright (C) 2020 Martin Whitaker. */ #include #define rdtsc(low, high) \ __asm__ __volatile__("rdtsc" \ : "=a" (low), \ "=d" (high) \ ) #define rdtscl(low) \ __asm__ __volatile__("rdtsc" \ : "=a" (low) \ : /* no inputs */ \ : "edx" \ ) /* * Reads and returns the timestamp counter value. */ static inline uint64_t get_tsc(void) { uint32_t tl; uint32_t th; rdtsc(tl, th); return (uint64_t)th << 32 | (uint64_t)tl; } #endif // TSC_H pcmemtest-1.5/system/vmem.c000066400000000000000000000071061413251666700160330ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from memtest86+ vmem.c // // vmem.c - MemTest-86 // // Virtual memory handling (PAE) // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "boot.h" #include "cpuid.h" #include "vmem.h" //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static uintptr_t mapped_window = 2; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static void load_pdbr() { void *page_table; if (cpuid_info.flags.lm == 1) { page_table = pml4; } else { page_table = pdp; } __asm__ __volatile__( #ifdef __x86_64__ "movq %0, %%cr3\n\t" #else "movl %0, %%cr3\n\t" #endif : : "r" (page_table) : "rax" ); } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ uintptr_t map_framebuffer(uintptr_t base_addr, size_t size) { uintptr_t first_page = base_addr >> VM_PAGE_SHIFT; uintptr_t last_page = (base_addr + size - 1) >> VM_PAGE_SHIFT; if (first_page >= VM_PAGE_C(3,GB) && last_page < VM_PAGE_C(4,GB)) { // No mapping required. return base_addr; } // Compute the page table entries. uintptr_t page = first_page; for (int i = 0; i < 512; i++) { pd3[i] = (page << VM_PAGE_SHIFT) + 0x83; if (++page > last_page) break; } // Reload the PDBR to flush any remnants of the old mapping. load_pdbr(); // Return the mapped address. return ADDR_C(3,GB) + base_addr % VM_PAGE_SIZE; } bool map_window(uintptr_t start_page) { uintptr_t window = start_page >> (30 - PAGE_SHIFT); if (window < 2) { // Less than 2 GB so no mapping is required. return true; } if (cpuid_info.flags.pae == 0) { // No PAE, so we can only access 4GB. if (window < 4) { mapped_window = window; return true; } return false; } if (cpuid_info.flags.lm == 0 && (start_page >= PAGE_C(64,GB))) { // Fail, we want an address that is out of bounds // for PAE and no long mode (ie. 32 bit CPU). return false; } // Compute the page table entries. for (uintptr_t i = 0; i < 512; i++) { pd2[i] = ((uint64_t)window << 30) + (i << VM_PAGE_SHIFT) + 0x83; } // Reload the PDBR to flush any remnants of the old mapping. load_pdbr(); mapped_window = window; return true; } void *first_word_mapping(uintptr_t page) { void *result; if (page < PAGE_C(2,GB)) { // If the address is less than 2GB, it is directly mapped. result = (void *)(page << PAGE_SHIFT); } else { // Otherwise it is mapped to the third GB. uintptr_t alias = PAGE_C(2,GB) + page % PAGE_C(1,GB); result = (void *)(alias << PAGE_SHIFT); } return result; } void *last_word_mapping(uintptr_t page, size_t word_size) { return (uint8_t *)first_word_mapping(page) + (PAGE_SIZE - word_size); } uintptr_t page_of(void *addr) { uintptr_t page = (uintptr_t)addr >> PAGE_SHIFT; if (page >= PAGE_C(2,GB)) { page = page % PAGE_C(1,GB); page += mapped_window << (30 - PAGE_SHIFT); } return page; } pcmemtest-1.5/system/vmem.h000066400000000000000000000011041413251666700160300ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef VMEM_H #define VMEM_H /* * Provides functions to handle physical memory page mapping into virtual * memory. * * Copyright (C) 2020 Martin Whitaker. */ #include #include #include #include "memsize.h" #define VM_WINDOW_SIZE PAGE_C(1,GB) uintptr_t map_framebuffer(uintptr_t base_addr, size_t size); bool map_window(uintptr_t start_page); void *first_word_mapping(uintptr_t page); void *last_word_mapping(uintptr_t page, size_t word_size); uintptr_t page_of(void *addr); #endif // VMEM_H pcmemtest-1.5/tests/000077500000000000000000000000001413251666700145355ustar00rootroot00000000000000pcmemtest-1.5/tests/addr_walk1.c000066400000000000000000000051321413251666700167130ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_addr_walk1(int my_vcpu) { int ticks = 0; // There isn't a meaningful address for this test. test_addr[my_vcpu] = 0; testword_t invert = 0; for (int i = 0; i < 2; i++) { if (my_vcpu == master_vcpu) { display_test_pattern_value(invert); } ticks++; if (my_vcpu < 0) { continue; } for (int j = 0; j < vm_map_size; j++) { uintptr_t pb = (uintptr_t)vm_map[j].start; uintptr_t pe = (uintptr_t)vm_map[j].end; // Walking one on our first address. uintptr_t mask1 = sizeof(testword_t); do { volatile testword_t *p1 = (testword_t *)(pb | mask1); mask1 <<= 1; if (p1 > (testword_t *)pe) { break; } testword_t expect = invert ^ (testword_t)p1; *p1 = expect; // Walking one on our second address. uintptr_t mask2 = sizeof(testword_t); do { volatile testword_t *p2 = (testword_t *)(pb | mask2); mask2 <<= 1; if (p2 == p1) { continue; } if (p2 > (testword_t *)pe) { break; } *p2 = ~invert ^ (testword_t)p2; testword_t actual = *p1; if (unlikely(actual != expect)) { addr_error(p1, p2, expect, actual); *p1 = expect; // recover from error } } while (mask2); } while (mask1); } invert = ~invert; do_tick(my_vcpu); BAILOUT; } return ticks; } pcmemtest-1.5/tests/bit_fade.c000066400000000000000000000107061413251666700164420ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "unistd.h" #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static int pattern_fill(int my_vcpu, testword_t pattern) { int ticks = 0; if (my_vcpu == master_vcpu) { display_test_pattern_value(pattern); } for (int i = 0; i < vm_map_size; i++) { testword_t *start = vm_map[i].start; testword_t *end = vm_map[i].end; volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { *p = pattern; } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; } static int pattern_check(int my_vcpu, testword_t pattern) { int ticks = 0; for (int i = 0; i < vm_map_size; i++) { testword_t *start = vm_map[i].start; testword_t *end = vm_map[i].end; volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t actual = *p; if (unlikely(actual != pattern)) { data_error(p, pattern, actual, true); } } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; } static int fade_delay(int my_vcpu, int sleep_secs) { int ticks = 0; if (my_vcpu == master_vcpu) { display_test_stage_description("fade over %i seconds", sleep_secs); } while (sleep_secs > 0) { sleep_secs--; ticks++; if (my_vcpu < 0) { continue; } sleep(1); do_tick(my_vcpu); BAILOUT; } return ticks; } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_bit_fade(int my_vcpu, int stage, int sleep_secs) { const testword_t all_zero = 0; const testword_t all_ones = ~all_zero; static int last_stage = -1; int ticks = 0; switch (stage) { case 0: ticks = pattern_fill(my_vcpu, all_zero); break; case 1: // Only sleep once. if (stage != last_stage) { ticks = fade_delay(my_vcpu, sleep_secs); } break; case 2: ticks = pattern_check(my_vcpu, all_zero); break; case 3: ticks = pattern_fill(my_vcpu, all_ones); break; case 4: // Only sleep once. if (stage != last_stage) { ticks = fade_delay(my_vcpu, sleep_secs); } break; case 5: ticks = pattern_check(my_vcpu, all_ones); break; default: break; } last_stage = stage; return ticks; } pcmemtest-1.5/tests/block_move.c000066400000000000000000000207231413251666700170250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_block_move(int my_vcpu, int iterations) { int ticks = 0; if (my_vcpu == master_vcpu) { display_test_pattern_name("block move"); } // Initialize memory with the initial pattern. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, 16 * sizeof(testword_t)); testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; testword_t pattern1 = 1; do { testword_t pattern2 = ~pattern1; p[ 0] = pattern1; p[ 1] = pattern1; p[ 2] = pattern1; p[ 3] = pattern1; p[ 4] = pattern2; p[ 5] = pattern2; p[ 6] = pattern1; p[ 7] = pattern1; p[ 8] = pattern1; p[ 9] = pattern1; p[10] = pattern2; p[11] = pattern2; p[12] = pattern1; p[13] = pattern1; p[14] = pattern2; p[15] = pattern2; pattern1 = pattern1 << 1 | pattern1 >> (TESTWORD_WIDTH - 1); // rotate left } while (p <= (pe - 16) && (p += 16)); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } barrier_wait(run_barrier); // Now move the data around. First move the data up half of the segment size // we are testing. Then move the data to the original location + 32 bytes. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, 16 * sizeof(testword_t)); testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } size_t half_length = (pe - p + 1) / 2; testword_t *pm = p + half_length; for (int j = 0; j < iterations; j++) { ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; #ifdef __x86_64__ __asm__ __volatile__ ( "cld\n" "jmp L110\n\t" ".p2align 4,,7\n\t" "L110:\n\t" // At the end of all this // - the second half equals the initial value of the first half // - the first half is right shifted 64-bytes (with wrapping) // Move first half to second half "movq %1,%%rdi\n\t" // Destination, pm (mid point) "movq %0,%%rsi\n\t" // Source, p (start point) "movq %2,%%rcx\n\t" // Length, half_length (size of a half in DWORDS) "rep\n\t" "movsq\n\t" // Move the second half, less the last 64 bytes, to the first half, offset plus 64 bytes "movq %0,%%rdi\n\t" "addq $64,%%rdi\n\t" // Destination, p (start-point) plus 32 bytes "movq %1,%%rsi\n\t" // Source, pm (mid-point) "movq %2,%%rcx\n\t" "subq $8,%%rcx\n\t" // Length, half_length (size of a half in QWORDS) minus 8 QWORDS (64 bytes) "rep\n\t" "movsq\n\t" // Move last 8 QWORDS (64 bytes) of the second half to the start of the first half "movq %0,%%rdi\n\t" // Destination, p(start-point) // Source, 8 QWORDS from the end of the second half, left over by the last rep/movsl "movq $8,%%rcx\n\t" // Length, 8 QWORDS (64 bytes) "rep\n\t" "movsq\n\t" :: "g" (p), "g" (pm), "g" (half_length) : "rdi", "rsi", "rcx" ); #else __asm__ __volatile__ ( "cld\n" "jmp L110\n\t" ".p2align 4,,7\n\t" "L110:\n\t" // At the end of all this // - the second half equals the initial value of the first half // - the first half is right shifted 32 bytes (with wrapping) // Move first half to second half "movl %1,%%edi\n\t" // Destination, pm (mid point) "movl %0,%%esi\n\t" // Source, p (start point) "movl %2,%%ecx\n\t" // Length, half_length (size of a half in DWORDS) "rep\n\t" "movsl\n\t" // Move the second half, less the last 32 bytes, to the first half, offset plus 32 bytes "movl %0,%%edi\n\t" "addl $32,%%edi\n\t" // Destination, p (start-point) plus 32 bytes "movl %1,%%esi\n\t" // Source, pm (mid-point) "movl %2,%%ecx\n\t" "subl $8,%%ecx\n\t" // Length, half_length (size of a half in DWORDS) minus 8 DWORDS (32 bytes) "rep\n\t" "movsl\n\t" // Move last 8 DWORDS (32 bytes) of the second half to the start of the first half "movl %0,%%edi\n\t" // Destination, p(start-point) // Source, 8 DWORDS from the end of the second half, left over by the last rep/movsl "movl $8,%%ecx\n\t" // Length, 8 DWORDS (32 bytes) "rep\n\t" "movsl\n\t" :: "g" (p), "g" (pm), "g" (half_length) : "edi", "esi", "ecx" ); #endif do_tick(my_vcpu); BAILOUT; } } while (!at_end && ++pe); // advance pe to next start point } barrier_wait(run_barrier); // Now check the data. The error checking is rather crude. We just check that the // adjacent words are the same. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, 16 * sizeof(testword_t)); testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { if (unlikely(p[0] != p[1])) { data_error(p, p[0], p[1], false); } } while (p <= (pe - 2) && (p += 2)); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; } pcmemtest-1.5/tests/modulo_n.c000066400000000000000000000111231413251666700165130ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_modulo_n(int my_vcpu, int iterations, testword_t pattern1, testword_t pattern2, int n, int offset) { int ticks = 0; if (my_vcpu == master_vcpu) { display_test_pattern_values(pattern1, offset); } // Write every nth location with pattern1. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, sizeof(testword_t)); end -= n; // avoids pointer overflow when incrementing p testword_t *p = start + offset; // we assume each chunk has at least 'n' words, so this won't overflow testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { *p = pattern1; } while (p <= (pe - n) && (p += n)); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } // Write the rest of memory "iteration" times with pattern2. for (int i = 0; i < iterations; i++) { for (int j = 0; j < vm_map_size; j++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, j, sizeof(testword_t)); int k = 0; testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { if (k != offset) { *p = pattern2; } k++; if (k == n) { k = 0; } } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } } // Now check every nth location. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, sizeof(testword_t)); end -= n; // avoids pointer overflow when incrementing p testword_t *p = start + offset; // we assume each chunk has at least 'offset' words, so this won't overflow testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t actual = *p; if (unlikely(actual != pattern1)) { data_error(p, pattern1, actual, true); } } while (p <= (pe - n) && (p += n)); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; } pcmemtest-1.5/tests/mov_inv_fixed.c000066400000000000000000000122421413251666700175360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" #define HAND_OPTIMISED 1 // Use hand-optimised assembler code for performance. //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_mov_inv_fixed(int my_vcpu, int iterations, testword_t pattern1, testword_t pattern2) { int ticks = 0; if (my_vcpu == master_vcpu) { display_test_pattern_value(pattern1); } // Initialize memory with the initial pattern. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, sizeof(testword_t)); volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; #if HAND_OPTIMISED #ifdef __x86_64__ uint64_t length = pe - p + 1; __asm__ __volatile__ ("\t" "rep \n\t" "stosq \n\t" : : "c" (length), "D" (p), "a" (pattern1) : ); p = pe; #else uint32_t length = pe - p + 1; __asm__ __volatile__ ("\t" "rep \n\t" "stosl \n\t" : : "c" (length), "D" (p), "a" (pattern1) : ); p = pe; #endif #else do { *p = pattern1; } while (p++ < pe); // test before increment in case pointer overflows #endif do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } // Check for the current pattern and then write the alternate pattern for // each memory location. Test from the bottom up and then from the top down. for (int i = 0; i < iterations; i++) { for (int j = 0; j < vm_map_size; j++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, j, sizeof(testword_t)); volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t actual = *p; if (unlikely(actual != pattern1)) { data_error(p, pattern1, actual, true); } *p = pattern2; } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } for (int j = vm_map_size - 1; j >= 0; j--) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, j, sizeof(testword_t)); volatile testword_t *p = end; volatile testword_t *ps = end; bool at_start = false; do { // take care to avoid pointer underflow if ((ps - start) >= SPIN_SIZE) { ps -= SPIN_SIZE - 1; } else { at_start = true; ps = start; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t actual = *p; if (unlikely(actual != pattern2)) { data_error(p, pattern2, actual, true); } *p = pattern1; } while (p-- > ps); // test before decrement in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_start && --ps); // advance ps to next start point } } return ticks; } pcmemtest-1.5/tests/mov_inv_random.c000066400000000000000000000071161413251666700177230ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "cpuid.h" #include "tsc.h" #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_mov_inv_random(int my_vcpu) { int ticks = 0; uint64_t seed; if (cpuid_info.flags.rdtsc) { seed = get_tsc(); } else { seed = UINT64_C(0x12345678) * (1 + pass_num); } if (my_vcpu == master_vcpu) { display_test_pattern_value(seed); } // Initialize memory with the initial pattern. random_seed(my_vcpu, seed); for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, sizeof(testword_t)); volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { *p = random(my_vcpu); } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } // Check for initial pattern and then write the inverse pattern for each // memory location. Repeat. testword_t invert = 0; for (int i = 0; i < 2; i++) { random_seed(my_vcpu, seed); for (int j = 0; j < vm_map_size; j++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, j, sizeof(testword_t)); volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t expect = random(my_vcpu) ^ invert; testword_t actual = *p; if (unlikely(actual != expect)) { data_error(p, expect, actual, true); } *p = ~expect; } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } invert = ~invert; } return ticks; } pcmemtest-1.5/tests/mov_inv_walk1.c000066400000000000000000000117701413251666700174630ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_mov_inv_walk1(int my_vcpu, int iterations, int offset, bool inverse) { int ticks = 0; testword_t pattern = (testword_t)1 << offset; if (my_vcpu == master_vcpu) { display_test_pattern_value(inverse ? ~pattern : pattern); } // Initialize memory with the initial pattern. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, i, sizeof(testword_t)); volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { *p = inverse ? ~pattern : pattern; pattern = pattern << 1 | pattern >> (TESTWORD_WIDTH - 1); // rotate left } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } // Check for initial pattern and then write the complement for each memory location. // Test from bottom up and then from the top down. for (int i = 0; i < iterations; i++) { pattern = (testword_t)1 << offset; for (int j = 0; j < vm_map_size; j++) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, j, sizeof(testword_t)); volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t expect = inverse ? ~pattern : pattern; testword_t actual = *p; if (unlikely(actual != expect)) { data_error(p, expect, actual, true); } *p = ~expect; pattern = pattern << 1 | pattern >> (TESTWORD_WIDTH - 1); // rotate left } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } for (int j = vm_map_size - 1; j >= 0; j--) { testword_t *start, *end; calculate_chunk(&start, &end, my_vcpu, j, sizeof(testword_t)); volatile testword_t *p = end; volatile testword_t *ps = end; bool at_start = false; do { // take care to avoid pointer underflow if ((ps - start) >= SPIN_SIZE) { ps -= SPIN_SIZE - 1; } else { at_start = true; ps = start; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)ps; do { pattern = pattern >> 1 | pattern << (TESTWORD_WIDTH - 1); // rotate right testword_t expect = inverse ? pattern : ~pattern; testword_t actual = *p; if (unlikely(actual != expect)) { data_error(p, expect, actual, true); } *p = ~expect; } while (p-- > ps); // test before decrement in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_start && --ps); // advance ps to next start point } } return ticks; } pcmemtest-1.5/tests/own_addr.c000066400000000000000000000076061413251666700165070ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static int pattern_fill(int my_vcpu, testword_t offset) { int ticks = 0; if (my_vcpu == master_vcpu) { display_test_pattern_name("own address"); } // Write each address with it's own address. for (int i = 0; i < vm_map_size; i++) { testword_t *start = vm_map[i].start; testword_t *end = vm_map[i].end; volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { *p = (testword_t)p + offset; } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; } static int pattern_check(int my_vcpu, testword_t offset) { int ticks = 0; // Check each address has its own address. for (int i = 0; i < vm_map_size; i++) { testword_t *start = vm_map[i].start; testword_t *end = vm_map[i].end; volatile testword_t *p = start; volatile testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_vcpu < 0) { continue; } test_addr[my_vcpu] = (uintptr_t)p; do { testword_t expect = (testword_t)p + offset; testword_t actual = *p; if (unlikely(actual != expect)) { data_error(p, expect, actual, true); } } while (p++ < pe); // test before increment in case pointer overflows do_tick(my_vcpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_own_addr1(int my_vcpu) { int ticks = 0; ticks += pattern_fill(my_vcpu, 0); ticks += pattern_check(my_vcpu, 0); return ticks; } int test_own_addr2(int my_vcpu, int stage) { static testword_t offset = 0; static int last_stage = -1; int ticks = 0; offset = (stage == last_stage) ? offset + 1 : 1; switch (stage) { case 0: ticks = pattern_fill(my_vcpu, offset); break; case 1: ticks = pattern_check(my_vcpu, offset); break; default: break; } last_stage = stage; return ticks; } pcmemtest-1.5/tests/test_funcs.h000066400000000000000000000014501413251666700170630ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TEST_FUNCS_H #define TEST_FUNCS_H /* * Provides the prototypes for the basic test functions used to implement * the tests. * * Copyright (C) 2020 Martin Whitaker. */ #include "test.h" int test_addr_walk1(int my_vcpu); int test_own_addr1(int my_vcpu); int test_own_addr2(int my_vcpu, int stage); int test_mov_inv_fixed(int my_vcpu, int iterations, testword_t pattern1, testword_t pattern2); int test_mov_inv_walk1(int my_vcpu, int iterations, int offset, bool inverse); int test_mov_inv_random(int my_vcpu); int test_modulo_n(int my_vcpu, int iterations, testword_t pattern1, testword_t pattern2, int n, int offset); int test_block_move(int my_vcpu, int iterations); int test_bit_fade(int my_vcpu, int stage, int sleep_secs); #endif // TEST_FUNCS_H pcmemtest-1.5/tests/test_helper.c000066400000000000000000000064531413251666700172270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Partly derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include "config.h" #include "display.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Types //------------------------------------------------------------------------------ // We keep a separate LFSR for each CPU. Space them out by at least a cache line, // otherwise performance suffers. typedef struct { uint64_t lfsr; uint64_t pad[7]; } prsg_state_t; //------------------------------------------------------------------------------ // Private Variables //------------------------------------------------------------------------------ static prsg_state_t prsg_state[MAX_VCPUS]; //------------------------------------------------------------------------------ // Private Functions //------------------------------------------------------------------------------ static inline uint32_t prsg(int my_vcpu) { // This implements a 64 bit linear feedback shift register with XNOR // feedback from taps 64, 63, 61, 60. It generates 32 new bits each // time the function is called. Because the feedback taps are all in // the upper 32 bits, we can generate the new bits in parallel. uint64_t lfsr = prsg_state[my_vcpu].lfsr; uint32_t feedback = ~((lfsr >> 32) ^ (lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 28)); prsg_state[my_vcpu].lfsr = (lfsr << 32) | feedback; return feedback; } //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ void random_seed(int my_vcpu, uint64_t seed) { if (my_vcpu < 0) { return; } // Avoid the PRSG illegal state. if (~seed == 0) { seed = 0; } prsg_state[my_vcpu].lfsr = seed; } testword_t random(int my_vcpu) { if (my_vcpu < 0) { return 0; } testword_t value = prsg(my_vcpu); #if TESTWORD_WIDTH > 32 value = value << 32 | prsg(my_vcpu); #endif return value; } void calculate_chunk(testword_t **start, testword_t **end, int my_vcpu, int segment, size_t chunk_align) { if (my_vcpu < 0) { my_vcpu = 0; } // If we are only running 1 CPU then test the whole segment. if (num_vcpus == 1) { *start = vm_map[segment].start; *end = vm_map[segment].end; } else { uintptr_t segment_size = (vm_map[segment].end - vm_map[segment].start + 1) * sizeof(testword_t); uintptr_t chunk_size = round_down(segment_size / num_vcpus, chunk_align); // Calculate chunk boundaries. *start = (testword_t *)((uintptr_t)vm_map[segment].start + chunk_size * my_vcpu); *end = (testword_t *)((uintptr_t)(*start) + chunk_size) - 1; if (*end > vm_map[segment].end) { *end = vm_map[segment].end; } } } pcmemtest-1.5/tests/test_helper.h000066400000000000000000000033331413251666700172260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TEST_HELPER_H #define TEST_HELPER_H /* * Provides some common definitions and helper functions for the memory * tests. * * Copyright (C) 2020 Martin Whitaker. */ #include #include #include "test.h" /* * A wrapper for guiding branch prediction. */ #define unlikely(x) __builtin_expect(!!(x), 0) /* * The block size processed between each update of the progress bars and * spinners. This also affects how quickly the program will respond to the * keyboard. */ #define SPIN_SIZE (1 << 27) // in testwords /* * A macro to perform test bailout when requested. */ #define BAILOUT if (bail) return ticks /* * Returns value rounded down to the nearest multiple of align_size. */ static inline uintptr_t round_down(uintptr_t value, size_t align_size) { return value & ~(align_size - 1); } /* * Returns value rounded up to the nearest multiple of align_size. */ static inline uintptr_t round_up(uintptr_t value, size_t align_size) { return (value + (align_size - 1)) & ~(align_size - 1); } /* * Seeds the psuedo-random number generator for my_vcpu. */ void random_seed(int my_vcpu, uint64_t seed); /* * Returns a psuedo-random number for my_vcpu. The sequence of numbers returned * is repeatable for a given starting seed. The sequence repeats after 2^64 - 1 * numbers. Within that period, no number is repeated. */ testword_t random(int my_vcpu); /* * Calculates the start and end word address for the chunk of segment that is * to be tested by my_vcpu. The chunk start will be aligned to a multiple of * chunk_align. */ void calculate_chunk(testword_t **start, testword_t **end, int my_vcpu, int segment, size_t chunk_align); #endif // TEST_HELPER_H pcmemtest-1.5/tests/tests.c000066400000000000000000000160541413251666700160510ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020 Martin Whitaker. // // Derived from an extract of memtest86+ main.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://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 "cache.h" #include "cpuid.h" #include "tsc.h" #include "vmem.h" #include "barrier.h" #include "config.h" #include "display.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" #include "tests.h" //------------------------------------------------------------------------------ // Constants //------------------------------------------------------------------------------ #ifndef TRACE_BARRIERS #define TRACE_BARRIERS 0 #endif #define MODULO_N 20 //------------------------------------------------------------------------------ // Public Variables //------------------------------------------------------------------------------ test_pattern_t test_list[NUM_TEST_PATTERNS] = { // ena, cpu, stgs, itrs, errs, description { true, SEQ, 1, 6, 0, "[Address test, walking ones, no cache] "}, { true, SEQ, 1, 6, 0, "[Address test, own address in window] "}, { true, SEQ, 2, 6, 0, "[Address test, own address + window] "}, { true, PAR, 1, 6, 0, "[Moving inversions, 1s & 0s] "}, { true, PAR, 1, 3, 0, "[Moving inversions, 8 bit pattern] "}, { true, PAR, 1, 30, 0, "[Moving inversions, random pattern] "}, #if TESTWORD_WIDTH > 32 { true, PAR, 1, 3, 0, "[Moving inversions, 64 bit pattern] "}, #else { true, PAR, 1, 3, 0, "[Moving inversions, 32 bit pattern] "}, #endif { true, PAR, 1, 81, 0, "[Block move] "}, { true, PAR, 1, 48, 0, "[Random number sequence] "}, { true, PAR, 1, 6, 0, "[Modulo 20, random pattern] "}, { true, ONE, 6, 240, 0, "[Bit fade test, 2 patterns] "}, }; int ticks_per_pass[NUM_PASS_TYPES]; int ticks_per_test[NUM_PASS_TYPES][NUM_TEST_PATTERNS]; //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ #define BARRIER \ if (my_vcpu >= 0) { \ if (TRACE_BARRIERS) { \ trace(my_vcpu, "Run barrier wait at %s line %i", __FILE__, __LINE__); \ } \ barrier_wait(run_barrier); \ } int run_test(int my_vcpu, int test, int stage, int iterations) { if (my_vcpu == master_vcpu) { if ((uintptr_t)&_start > LOW_LOAD_ADDR) { // Relocated so we need to test all selected lower memory. vm_map[0].start = first_word_mapping(pm_limit_lower); // For USB_WORKAROUND. if (vm_map[0].start < (uintptr_t *)0x500) { vm_map[0].start = (uintptr_t *)0x500; } } /* Update display of memory segments being tested */ uintptr_t pb = page_of(vm_map[0].start); uintptr_t pe = page_of(vm_map[vm_map_size - 1].end) + 1; display_test_addresses(pb << 2, pe << 2, num_pages_to_test << 2); } BARRIER; int ticks = 0; switch (test) { // Address test, walking ones. case 0: cache_off(); ticks += test_addr_walk1(my_vcpu); cache_on(); BAILOUT; break; // Address test, own address in window. case 1: ticks += test_own_addr1(my_vcpu); BAILOUT; break; // Address test, own address + window. case 2: ticks += test_own_addr2(my_vcpu, stage); BAILOUT; break; // Moving inversions, all ones and zeros. case 3: { testword_t pattern1 = 0; testword_t pattern2 = ~pattern1; BARRIER; ticks += test_mov_inv_fixed(my_vcpu, iterations, pattern1, pattern2); BAILOUT; BARRIER; ticks += test_mov_inv_fixed(my_vcpu, iterations, pattern2, pattern1); BAILOUT; } break; // Moving inversions, 8 bit walking ones and zeros. case 4: { #if TESTWORD_WIDTH > 32 testword_t pattern1 = UINT64_C(0x8080808080808080); #else testword_t pattern1 = 0x80808080; #endif for (int i = 0; i < 8; i++) { testword_t pattern2 = ~pattern1; BARRIER; ticks += test_mov_inv_fixed(my_vcpu, iterations, pattern1, pattern2); BAILOUT; BARRIER; ticks += test_mov_inv_fixed(my_vcpu, iterations, pattern2, pattern1); BAILOUT; pattern1 >>= 1; } } break; // Moving inversions, fixed random pattern. case 5: if (cpuid_info.flags.rdtsc) { random_seed(my_vcpu, get_tsc()); } else { random_seed(my_vcpu, UINT64_C(0x12345678) * (1 + pass_num)); } for (int i = 0; i < iterations; i++) { testword_t pattern1 = random(my_vcpu); testword_t pattern2 = ~pattern1; BARRIER; ticks += test_mov_inv_fixed(my_vcpu, 2, pattern1, pattern2); BAILOUT; } break; // Moving inversions, 32/64 bit shifting pattern. case 6: for (int offset = 0; offset < TESTWORD_WIDTH; offset++) { BARRIER; ticks += test_mov_inv_walk1(my_vcpu, iterations, offset, false); BAILOUT; BARRIER; ticks += test_mov_inv_walk1(my_vcpu, iterations, offset, true); BAILOUT; } break; // Block move. case 7: ticks += test_block_move(my_vcpu, iterations); BAILOUT; break; // Moving inversions, fully random patterns. case 8: for (int i = 0; i < iterations; i++) { BARRIER; ticks += test_mov_inv_random(my_vcpu); BAILOUT; } break; // Modulo 20 check, fixed random pattern. case 9: if (cpuid_info.flags.rdtsc) { random_seed(my_vcpu, get_tsc()); } else { random_seed(my_vcpu, UINT64_C(0x12345678) * (1 + pass_num)); } for (int i = 0; i < iterations; i++) { for (int offset = 0; offset < MODULO_N; offset++) { testword_t pattern1 = random(my_vcpu); testword_t pattern2 = ~pattern1; BARRIER; ticks += test_modulo_n(my_vcpu, 2, pattern1, pattern2, MODULO_N, offset); BAILOUT; BARRIER; ticks += test_modulo_n(my_vcpu, 2, pattern2, pattern1, MODULO_N, offset); BAILOUT; } } break; // Bit fade test. case 10: ticks += test_bit_fade(my_vcpu, stage, iterations); BAILOUT; break; } return ticks; } pcmemtest-1.5/tests/tests.h000066400000000000000000000014221413251666700160470ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 #ifndef TESTS_H #define TESTS_H /* * Provides support for identifying and running the memory tests. * * Copyright (C) 2020 Martin Whitaker. */ #include #include "config.h" #define NUM_TEST_PATTERNS 11 typedef struct { bool enabled; cpu_mode_t cpu_mode; int stages; int iterations; int errors; char *description; } test_pattern_t; extern test_pattern_t test_list[NUM_TEST_PATTERNS]; typedef enum { FAST_PASS, FULL_PASS, NUM_PASS_TYPES } pass_type_t; extern int ticks_per_pass[NUM_PASS_TYPES]; extern int ticks_per_test[NUM_PASS_TYPES][NUM_TEST_PATTERNS]; int run_test(int my_vcpu, int test, int stage, int iterations); #endif // TESTS_H