├── FAQ ├── Makefile ├── README ├── README.background ├── README.build-process ├── bootsect.S ├── build-number.txt ├── build.number ├── build_number.h ├── buildnumber.mak ├── changelog ├── config.c ├── config.h ├── controller.c ├── controller.h ├── cpuid.c ├── cpuid.h ├── defs.h ├── dmi.c ├── dmi.h ├── elf.h ├── error.c ├── extra.c ├── extra.h ├── head.S ├── init.c ├── io.h ├── jedec_id.h ├── lib.c ├── linuxbios.c ├── linuxbios_tables.h ├── main.c ├── major_version ├── make_buildnum.sh ├── makedos.sh ├── makeiso.sh ├── memsize.c ├── memtest.bin.lds ├── memtest.lds ├── memtest_shared.lds ├── msr.h ├── mt86+_loader ├── mt86+_loader.asm ├── patn.c ├── pci.c ├── pci.h ├── precomp.bin ├── random.c ├── reloc.c ├── screen_buffer.c ├── screen_buffer.h ├── serial.h ├── setup.S ├── smp.c ├── smp.h ├── spd.c ├── spd.h ├── stddef.h ├── stdin.h ├── stdint.h ├── test.c ├── test.h ├── version.number └── vmem.c /FAQ: -------------------------------------------------------------------------------- 1 | - What is memtest86+, what do I use it for? 2 | 3 | Memtest86+ is a utility designed to test whether your memory is in working 4 | order. It repeatedly writes an enormous amount of different patterns to all 5 | memory locations and reads them back again and verifies whether the result 6 | of the read is the same as what was written to memory. 7 | 8 | There can be a multitude of reasons for running memtest, but foremost of all 9 | is of course to test whether your memory modules might be bad. Whenever you 10 | suspect your modules to be bad because of system crashes, lockups or reboots 11 | it would be nice to know whether the modules are in working order. 12 | Memtest86+ is a utility which tries to answer that question for you. 13 | 14 | Another common use exists in the overclocking scene. When overclocking a 15 | system you are essentially pushing your system to the limits and at some 16 | point it will simply give way and break. Unfortunately there isn't a clear 17 | cut way of deciding whether a system is still working correctly. Because of 18 | the complexity of a computer a system which is pushed to the limits doesn't 19 | just break completely when it starts to fail, instead little errors start 20 | showing up in many different places in the system growing more frequent and 21 | widespread the more the system is pushed. Each one of these little errors 22 | can lead to a crash of your system but can also go unnoticed for days or 23 | weeks in a running system. The art so to speak of overclocking is thus to 24 | push the system as far as it can go without introducing any such errors. As 25 | memory is usually one of the first places these such errors start coming up 26 | a memory test is very useful. 27 | 28 | - How do I get it to run? 29 | 30 | There are several ways to use memtest, which are described below: 31 | 32 | + Run from floppydisk 33 | 34 | Memtest86+ is directly executable by any modern x86 compatible machine, by 35 | writing the bootable binary to a floppy disk one can boot from the disk to 36 | run memtest. 37 | 38 | Simply download the appropriate package, the Pre-Compiled Bootable Binary 39 | (.gz) package for Linux users and the Pre-Compiled package for Floppy (DOS 40 | - Win) for Windows users. 41 | 42 | For Windows, unzip the package into a directory like C:\memtest, insert a 43 | blank floppy into your a: disk drive and run the install.bat file. As the 44 | install prompts you, to use memtest directly, leave the disk in the drive 45 | and reboot your machine. 46 | 47 | For Linux, unpack the package into your home directory, insert a blank 48 | floppy into your floppy drive and execute 'dd if=~/memtest+-1.xx.bin.gz 49 | of=/dev/fd0 conv=osync' replacing 1.xx with the correct version number of 50 | the memtest86+ you downloaded. To run memtest immediately reboot your 51 | machine. 52 | 53 | Your machine should now boot from the disk, display the word Loading 54 | folowed by a series of periods and then show a screen much like the 55 | screenshots on the memtest86+ web page. The test is automatically started. 56 | 57 | If your machine simply boots back into Windows/Linux you will most likely 58 | have to configure your BIOS to attempt to boot from floppy disk on 59 | startup, refer to your computer's/mainboard's manual how to do this. 60 | 61 | When you are done testing simply remove the floppy and reset your 62 | computer, if ever you want to execure the test again simply reinsert the 63 | disk and reboot/start your computer. 64 | 65 | + Run from CD 66 | 67 | Memtest86+ is directly executable by any modern x86 compatible machine, by 68 | writing the iso to a CD one can boot from the CD to run memtest. 69 | 70 | Simply download the appropriate package, the Download - Pre-Compiled 71 | Bootable ISO (.gz) for Linux users and the Pre-Compiled Bootable ISO 72 | (.zip) for Windows users. 73 | 74 | For Windows, unzip the package into a directory like C:\memtest. You will 75 | now see a file called memtest86+-1.xx.iso in this directory. You will need 76 | to burn this file to a CD with a CD recording program. Do note however 77 | that you should not make a regular data CD on which you for instance write 78 | your text documents and holiday photographs. Instead the iso file is a so 79 | called image of a CD, it is a direct copy of a CD. Your CD recording 80 | program will most likely have a feature called burn image or something to 81 | that effect which you should use to burn the CD. 82 | 83 | For linux, unzip the package into your home directory. and execute 84 | 'cdrecord dev= ~/memtest86+-1.xx.iso' where you replace with the scsi address of your CD burner and replace 1.xx with the 86 | correct version number of the memtest86+ your downloaded. 87 | 88 | When the burning completed your drive will most likely have ejected the CD 89 | and you should have a bootable memtest86+ CD. To run the test directly 90 | reinsert the CD and reboot your machine. 91 | 92 | Your machine should now boot from the CD, display the word Loading folowed 93 | by a series of periods and then show a screen much like the screenshots on 94 | the memtest86+ web page. The test is automatically started. 95 | 96 | If your machine simply boots back into Windows/Linux you will most likely 97 | have to configure your BIOS to attempt to boot from CD-ROM drive on 98 | startup, refer to your computer's/mainboard's manual how to do this. 99 | 100 | When you are done testing simply remove the CD and reset your computer, if 101 | ever you want to execure the test again simply reinsert the CD and 102 | reboot/start your computer. 103 | 104 | + Run from USB Flash drive 105 | 106 | FIXME 107 | 108 | + Run from boot manager 109 | 110 | FIXME 111 | 112 | - How long does memtest86+ run? How do I stop it? 113 | 114 | Memtest86+ runs indefinately unless you stop it. It does however repeat the 115 | same tests over and over again. Memtest86+ contains a number of different 116 | tests which each take different approaches in trying to expose any errors in 117 | your memory. In the top right of your screen you can see the progress of 118 | each test in the lower of the two progress bars. The topmost progress bar 119 | shows the progress of a pass, each pass consists of all the tests in the 120 | memtest suite. 121 | 122 | Thus all tests are executed in one pass, so does that mean that no errors 123 | will show after the first pass if that pass didn't reveal any errors? Well 124 | no, there are several reasons why errors might only show up after a number 125 | of passes. Firstly as of this writing, the latest version of memtest also 126 | includes a test which uses random test patterns, each pass these patterns 127 | will of course be different. Secondly some types of errors simply don't show 128 | up until the system has been running for a while or are very critical on a 129 | certain timing condition, or other such conditions. 130 | 131 | To conclude, one successful pass of memtest will give you a pretty good idea 132 | that your memory is ok, only in rare cases will there be errors showing 133 | after the first pass. To be sure though simply have the test run overnight 134 | or even for a couple of days depending on the level of importance of the 135 | system. 136 | 137 | - How many errors are acceptable? 138 | 139 | No errors are acceptable. Even if there is just one error, something is 140 | amiss which can cause your system to crash. Of course what the cause of the 141 | errors is you will still have to determine. 142 | 143 | - What do I do when I get errors? 144 | 145 | Firstly, don't start drawing any conclusions. You only know that memtest86+ 146 | is giving your errors, not what the cause is. Unfortunately it is not a 147 | straightforward exercise to decisively test the memory in an actual system. 148 | This is because a computer is not just built up of some memory, but also 149 | includes many other elements such as a memory controller, cache, a cache 150 | controller, algorithmic and logic units, etc, all of which contribute to the 151 | machine. If there are faults in any of these other parts of the computer you 152 | will likely also see errors showing up in memtest. 153 | 154 | So what to do? First verify that the BIOS settings of your machine are 155 | correctly configured. Look up the memory timing settings applicable to the 156 | brand and type of memory modules you have and check they match your BIOS 157 | settings, correct them if they don't and run memtest again 158 | 159 | Ok, you have all the settings correctly set and you're still getting errors. 160 | Well of course a very likely cause are the memory modules and the logical 161 | course of action is to look into them further. 162 | 163 | If you are well stocked, have a few other machines at your disposal, or just 164 | want to spend the cash for some new modules the best way to test if the 165 | cause are your memory modules is just to replace them and test again. If you 166 | are less fortunate though there is still something you can do. 167 | 168 | If you have more then one module in your system, test them one by one, if 169 | one is consistently giving errors and another is consistently showing no 170 | errors it's a pretty good bet that the module giving the errors is simply 171 | defective. To exclude the possibility that a defective slot is throwing your 172 | results, use the same slot to test each different module. 173 | 174 | If each module by itself shows no errors, but when you place two or more 175 | modules into the machine at the same time you do get errors, you are most 176 | likely stuck with a compatibility issue and unfortunately there isn't a 177 | whole lot you can do about it. Be sure to check your computer/motherboard 178 | manual to see if the setup you are trying is allowed, some boards require 179 | special restrictions in the sizes of modules, the order of modules, the 180 | placement of double sided and single sides modules and more of such things. 181 | 182 | If you have only one module in your system, or all modules are giving 183 | errors, there are only very few options left. The only thing you can do 184 | really is to try the module(s) in another slot. Finally simply try out 185 | different orders of the memory modules, although your manual might not 186 | mention anything on the matter sometimes there simply exist timing or other 187 | issues which can be resolved by changing the order of your modules. And of 188 | course test each slot by putting a single module into that slot and running 189 | memtest on it. 190 | 191 | In the end if you still have not been able to localize the problem you will 192 | have to find a replacement module to establish whether the problem lies in 193 | your modules. See if you can borrow a module from someone else. 194 | 195 | When you have replaced the memory by new memory and the errors still 196 | persist, first check if you can rule out any compatibility issues or timing 197 | issues. If you are sure the memory should work in the system the cause of 198 | the errors must obviously lie someplace else in the system. 199 | 200 | The only way to find out where, is by trial and error really. Simply start 201 | replacing and/or removing parts of your computer one by one, running memtest 202 | each time you changed anything, until the errors are resolved. 203 | 204 | - I'm getting errors in test #x, what doest that mean? 205 | 206 | Interpreting memtest results is as scientific an endeavour as testing 207 | whether a person is a witch by the methods used in Monty Python's Holy 208 | Grail. In short, don't even start, it's not going to get you anywhere. Just 209 | interpret any error as you should any other and use the methods descibed in 210 | the previous question to determine the cause. 211 | 212 | - I'm getting errors in test #5 and/or #8 and have read a lot about it. 213 | 214 | Yes there are just about enough discussions on the topic to fill a book, but 215 | it all boils down to the answer given above. The only thing that can be said 216 | is that many a times, when memory latencies are incorrectly set in the BIOS 217 | you will experience errors in test #5 and #8. (Though #8 does not exist 218 | anymore as of version 1.40 and might be reinstated as a different test in a 219 | later version.) This does however NOT mean that errors in these tests are 220 | always the cause of incorrect settings, your memory might just as well be 221 | defective. 222 | 223 | - I'm getting errors in memtest on one machine, but not when I put the same 224 | memory in another, what does that mean? 225 | 226 | It can mean one of two things: 227 | - The machine that is giving the errors is defective. Errors don't just 228 | orginate from the memory module itself, but can also be caused by 229 | defects in the cpu, chipset, motherboard, PSU and even by timing issues 230 | introduced by any other component in the machine. 231 | - The machine giving the errors is imposing stricter timing than the other 232 | which the memory module simply can't cope with. If the module should 233 | work with the machine according to its specifications then it most 234 | likely is defective. 235 | 236 | - Which memory is tested? 237 | 238 | As much as possible of the system memory is tested. Unfortunately memtest86+ 239 | can usually not test all of the memory. The reason for this is that todays 240 | processors have become so complex that they require a small amount of memory 241 | to keep accounting data of the processor state. If memtest were to write 242 | over these areas the state of the processor becomes invalid and it's 243 | behaviour unpredictable. Alas it is also impossible to relocate these areas 244 | in the memory. 245 | 246 | This means that a small area of your memory can not be tested by memtest. If 247 | this part of the memory is defective you will know soon enough though as the 248 | processor, or parts of the processor simply won't work correctly if this 249 | part of your memory is defective. Do realise though that in very rare cases 250 | memtest will show no errors even though the module is defective, not because 251 | memtest can't detect the error, but because memtest can't test the area the 252 | error is located in. 253 | 254 | - When I select BIOS-ALL I get many errors / my machine crashes. 255 | 256 | This is normal. With todays computers this option should never be selected. 257 | See the previous question about the reason for the errors. 258 | 259 | - I want to use memtest on a multiboot CD, how do I do this? 260 | 261 | This is of course very dependent on which boot loader you use for your CD. 262 | Below is a description of how to set up a multiboot CD including memtest+ 263 | with isolinux, if you have experience with any other bootloader(s) please 264 | consider writing a small description of using memtest with that bootloader 265 | for the FAQ. 266 | 267 | -isolinux 268 | 269 | For general instructions on how to make a bootable CD with isolinux see 270 | the syslinux website and the manual. What you need to do to get memtest 271 | working is as follows. 272 | 273 | Download the Pre-Compiled Bootable Binary, the .gz if you are working 274 | under linux, the .zip if you are working under windows. Unpack the file 275 | from the package and rename it to an 8.3 filename with an extension other 276 | than .bin, renaming to memtest. (without an extension) is a good choice. 277 | 278 | Put the file somewhere in your CD directory structure, for example in 279 | images/memtest and edit your config file to include the following: 280 | 281 | label memtest 282 | kernel /images/memtest 283 | 284 | If you want to boot memtest automatically insert or change a line at the 285 | top to: 286 | 287 | default memtest 288 | 289 | If you want to display a prompt from which you can start memtest add or 290 | change the lines at the top to: (Change the timeout to suit your needs) 291 | 292 | prompt 1 293 | timeout 200 294 | 295 | - If memtest86+ shows no errors does that mean my memory is not defective? 296 | 297 | Of course no answers are definitive, no matter how good memtest86+ will 298 | eventually become there is always the possibility that a particular type of 299 | error will go unnoticed. As long as you are having no problems with the 300 | system it will be pretty safe to say that the modules are good. If you are 301 | having problems with the system however you will just have to check by trial 302 | and error, ie swapping the modules for new ones and/or testing with modules 303 | of a different brand/type. 304 | 305 | - When I run install.bat it doesn't write anything to floppy. 306 | 307 | You most likely have unpacked the memtest+-2.xx.floppy.zip file into a 308 | folder with a long pathname and/or containing + and - signs. It seems 309 | rawrite doesn't like that. Just move the files you unpacked to a directory 310 | like c:\memtest and execure it from there. 311 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for MemTest86+ 2 | # 3 | # Author: Chris Brady 4 | # Created: January 1, 1996 5 | 6 | 7 | # 8 | # Path for the floppy disk device 9 | # 10 | FDISK=/dev/fd0 11 | 12 | AS=as -32 13 | CC=gcc 14 | 15 | CFLAGS= -Wall -march=i486 -m32 -O1 -fomit-frame-pointer -fno-builtin \ 16 | -ffreestanding -fPIC $(SMP_FL) -fno-stack-protector 17 | 18 | LDFLAGS= -m elf_i386 19 | 20 | OBJS= head.o reloc.o main.o test.o init.o lib.o patn.o screen_buffer.o \ 21 | config.o cpuid.o linuxbios.o pci.o memsize.o spd.o error.o dmi.o controller.o \ 22 | smp.o vmem.o random.o 23 | 24 | 25 | all: clean memtest.bin memtest 26 | 27 | # Link it statically once so I know I don't have undefined 28 | # symbols and then link it dynamically so I have full 29 | # relocation information 30 | memtest_shared: $(OBJS) memtest_shared.lds Makefile 31 | $(LD) --warn-constructors --warn-common -static -T memtest_shared.lds $(LDFLAGS) \ 32 | -o $@ $(OBJS) && \ 33 | $(LD) -shared -Bsymbolic -T memtest_shared.lds $(LDFLAGS) -o $@ $(OBJS) 34 | 35 | memtest_shared.bin: memtest_shared 36 | objcopy -O binary $< memtest_shared.bin 37 | 38 | memtest: memtest_shared.bin memtest.lds 39 | $(LD) -s -T memtest.lds -b binary memtest_shared.bin -o $@ 40 | 41 | head.s: head.S config.h defs.h test.h 42 | $(CC) -E -traditional $< -o $@ 43 | 44 | bootsect.s: bootsect.S config.h defs.h 45 | $(CC) -E -traditional $< -o $@ 46 | 47 | setup.s: setup.S config.h defs.h 48 | $(CC) -E -traditional $< -o $@ 49 | 50 | memtest.bin: memtest_shared.bin bootsect.o setup.o memtest.bin.lds 51 | $(LD) -T memtest.bin.lds bootsect.o setup.o -b binary \ 52 | memtest_shared.bin -o memtest.bin 53 | 54 | reloc.o: reloc.c 55 | $(CC) -c $(CFLAGS) -fno-strict-aliasing reloc.c 56 | 57 | test.o: test.c 58 | $(CC) -c -Wall -march=i486 -m32 -O0 -fomit-frame-pointer -fno-builtin -ffreestanding test.c 59 | 60 | random.o: random.c 61 | $(CC) -c -Wall -march=i486 -m32 -O3 -fomit-frame-pointer -fno-builtin -ffreestanding random.c 62 | 63 | # rule for build number generation 64 | build_number: 65 | sh make_buildnum.sh 66 | 67 | clean: 68 | rm -f *.o *.s *.iso memtest.bin memtest memtest_shared \ 69 | memtest_shared.bin memtest.iso 70 | 71 | iso: 72 | make all 73 | ./makeiso.sh 74 | 75 | install: all 76 | install -D memtest.bin $(DESTDIR)/boot/memtest.bin 77 | 78 | install-precomp: 79 | dd $(FDISK) bs=8192 80 | 81 | dos: all 82 | cat mt86+_loader memtest.bin > memtest.exe 83 | -------------------------------------------------------------------------------- /README.background: -------------------------------------------------------------------------------- 1 | The Anatomy & Physiology of Memtest86-SMP 2 | ----------------------------------------- 3 | 4 | 1. Binary layout 5 | 6 | --------------------------------------------------------------- 7 | | bootsect.o | setup.o | head.o memtest_shared | 8 | --------------------------------------------------------------- 9 | Labels _start<-------memtest---------->_end 10 | ----------------------------------------------------------- 11 | addr 0 512 512+4*512 | 12 | ----------------------------------------------------------- 13 | 14 | 2. The following steps occur after we power on. 15 | a. The bootsect.o code gets loaded at 0x7c00 16 | and copies 17 | i. itself to 0x90000 18 | ii. setup.o to 0x90200 19 | iii. everything between _start and _end i.e memtest 20 | to 0x10000 21 | b. jumps somewhere into the copied bootsect.o code at 0x90000 22 | ,does some trivial stuff and jumps to setup.o 23 | c. setup.o puts the processor in protected mode, with a basic 24 | gdt and idt and does a long jump to the start of the 25 | memtest code (startup_32, see 4 below). The code and data 26 | segment base address are all set to 0x0. So a linear 27 | address range and no paging is enabled. 28 | d. From now on we no longer required the bootsect.o and setup.o 29 | code. 30 | 3. The code in memtest is compiled as position independent 31 | code. Which implies that the code can be moved dynamically in 32 | the address space and can still work. Since we are now in head.o, 33 | which is compiled with PIC , we no longer should use absolute 34 | addresses references while accessing functions or globals. 35 | All symbols are stored in a table called Global Offset Table(GOT) 36 | and %ebx is set to point to the base of that table. So to get/set 37 | the value of a symbol we need to read (%ebx + symbolOffsetIntoGOT) to 38 | get the symbol value. For eg. if foo is global varible the assembly 39 | code to store %eax value into foo will be changed from 40 | mov %eax, foo 41 | to 42 | mov %eax, foo@GOTOFF(%ebx) 43 | 4. (startup_32) The first step done in head.o is to change 44 | the gdtr and idtr register values to point to the final(!) 45 | gdt and ldt tables in head.o, since we can no longer use the 46 | gdt and ldt tables in setup.o, and call the dynamic linker 47 | stub in memtest_shared (see call _dl_start in head.S). This 48 | dynamic linker stub relocates all the code in memtest w.r.t 49 | the new base location i.e 0x1000. Finally we call the test_start() 50 | 'C' routine. 51 | 5. The test_start() C routine is the main routine which lets the BSP 52 | bring up the APs from their halt state, relocate the code 53 | (if necessary) to new address, move the APs to the newly 54 | relocated address and execute the tests. The BSP is the 55 | master which controls the execution of the APs, and mostly 56 | it is the one which manupulates the global variables. 57 | i. we change the stack to a private per cpu stack. 58 | (this step happens every time we move to a new location) 59 | ii. We kick start the APs in the system by 60 | a. Putting a temporary real mode code 61 | (_ap_trampoline_start - _ap_trampoline_protmode) 62 | at 0x9000, which puts the AP in protected mode and jumps 63 | to _ap_trampoline_protmode in head.o. The code in 64 | _ap_trampoline_protmode calls start_32 in head.o which 65 | reinitialises the AP's gdt and idt to point to the 66 | final(!) gdt and idt. (see step 4 above) 67 | b. Since the APs also traverse through the same initialisation 68 | code(startup_32 in head.o), the APs also call test_start(). 69 | The APs just spin wait (see AP_SpinWaitStart) till the 70 | are instructed by the BSP to jump to a new location, 71 | which can either be a test execution or spin wait at a 72 | new location. 73 | iii. The base address at which memtest tries to execute as far 74 | as possible is 0x2000. This is the lowest possible address 75 | memtest can put itself at. So the next step is to 76 | move to 0x2000, which it cannot directly, since copying 77 | code to 0x2000 will override the existing code at 0x1000. 78 | 0x2000 +sizeof(memtest) will usually be greater than 0x1000. 79 | so we temporarily relocated to 0x200000 and then relocate 80 | back to 0x2000. Every time the BSP relocates the code to the 81 | new location, it pulls up the APs spin waiting at the old 82 | location to spin wait at the corresponding relocated 83 | spin wait location, by making them jump to the new 84 | statup_32 relocated location(see 4 above). 85 | Hence forth during the tests 0x200000 is the only place 86 | we relocate to if we need to test a memory window 87 | (see v. below to get a description of what a window is) 88 | which includes address range 0x2000. 89 | 90 | Address map during normal execution. 91 | -------------------------------------------------------------------- 92 | | head.o memtest_shared | |RAM_END 93 | -------------------------------------------------------------------- 94 | Labels _start<-------memtest---------->_end 95 | -------------------------------------------------------------------- 96 | addr 0x0 0x2000 | Memory that is being tested.. |RAM_END 97 | -------------------------------------------------------------------- 98 | 99 | Address map during relocated state. 100 | -------------------------------------------------------------------- 101 | | head.o memtest_shared | |RAM_END 102 | -------------------------------------------------------------------- 103 | Labels _start<-------memtest---------->_end 104 | -------------------------------------------------------------------- 105 | addr memory that is being tested... |0x200000 | |RAM_END 106 | -------------------------------------------------------------------- 107 | 108 | iv. Once we are at 0x2000 we initialise the system, and 109 | determine the memory map ,usually via the bios e820 map. 110 | The sorted, and non-overlapping RAM page ranges are 111 | placed in v->pmap[] array. This array is the reference 112 | of the RAM memory map on the system. 113 | v. The memory range(in page numbers) which the 114 | memtest86 can test is partitioned into windows. 115 | the current version of memtest86-smp has the capability 116 | to test the memory from 0x0 - 0xFFFFFFFFF (max address 117 | when pae mode is enabled). 118 | We then compute the linear memory address ranges(called 119 | segments) for the window we are currently about to 120 | test. The windows are 121 | a. 0 - 640K 122 | b. (0x2000 + (_end - _start)) - 4G (since the code is at 0x2000). 123 | c. >4G to test pae address range, each window with size 124 | of 0x80000(2G), large enough to be mapped in one page directory 125 | entry. So a window size of 0x80000 means we can map 1024 page 126 | table entries, with page size of 2M(pae mode), with one 127 | page directory entry. Something similar to kseg entry 128 | in linux. The upper bound page number is 0x1000000 which 129 | corresponds to linear address 0xFFFFFFFFF + 1 which uses 130 | all the 36 address bits. 131 | Each window is compared against the sorted & non-overlapping 132 | e820 map which we have stored in v->pmap[] array, since all 133 | memory in the selected window address range may correspond to 134 | RAM or can be usable. A list of segments within the window is 135 | created , which contain the usable portions of the window. 136 | This is stored in v->mmap[] array. 137 | vi. Once the v->mmap[] array populated, we have the list of 138 | non-overlapping segments in the current window which are the 139 | final address ranges that can be tested. The BSP executes the 140 | test first and lets each AP execute the test one by one. Once 141 | all the APs finish execting the same test, the BSP moves to the 142 | next window follows the same procedure till all the windows 143 | are done. Once all the windows are done, the BSP moves to the 144 | next test. Before executing in any window the BSP checks if 145 | the window overlaps with the code/data of memtest86, if so 146 | tries to relocate to 0x200000. If the window includes both 147 | 0x2000 as well as 0x200000 the BSP skips that window. 148 | Looking at the window values the only time the memtest 149 | relocates is when testing the 0 - 640K window. 150 | 151 | Known Issues: 152 | * Memtest86-smp does not work on IBM-NUMA machines, x440 and friends. 153 | 154 | email comments to: 155 | Kalyan Rajasekharuni 156 | Sub: Memtest86-SMP 157 | -------------------------------------------------------------------------------- /README.build-process: -------------------------------------------------------------------------------- 1 | During memory testing memtest86 relocates itself in memory so it can test the 2 | memory it was previously running from. memtest86 is compiled as position mostly 3 | independent code. Some relocations records must be processed to achieve the 4 | affect of position independent code. A 16 bit loader is prepended to memtest86 5 | so it can be loaded from a floppy, or from lilo. 6 | 7 | In restructuring the build process I had several goals. Maintainability and 8 | comprehsibility of the build process. Simplicity of the toolset. And the 9 | ability to build images bootable by both the legacy x86 bootloader, 10 | and images bootable by bootloaders that directly load static ELF images. 11 | 12 | With the ability to proecess relocation records, memtest.bin has been 13 | reduced in size from 84480 bytes to 49308 bytes. And now only requires one copy 14 | of memtest86. A reduction in size of 35K. And the build process can now ignore 15 | the size of memtest86. 16 | 17 | BIOS calls have been moved from setup.S to head.S making bootsect.S and 18 | setup.S exclusively for booting. 19 | 20 | memtest86 is built in three stages. In the first stage the relocatable object 21 | files are built as with any program. In the second stage the relocatable object 22 | files are linked together into memtest_shared, a shared library version 23 | of memtest86. In the third stage a raw memory image of memtest_shared is formed 24 | and linked into memtest.bin, and memtest. 25 | 26 | memtest.bin is the floppy/lilo bootable target. 27 | 28 | memtest is the ELF bootable target. 29 | 30 | Another major change is now data in the bss segment is also preserved 31 | when memtest86 is relocated, and memtest86 can be relocated to any address. 32 | 33 | The one thing to watch out for is pointers to data inside of memtest86. Except 34 | for constant pointers to static data there is not enough information to generate 35 | relocation records for pointers so they will not change when memtest86 is 36 | relocated, which might lead to nasty surpises. 37 | 38 | Eric Biederman 39 | 40 | -------------------------------------------------------------------------------- /bootsect.S: -------------------------------------------------------------------------------- 1 | /* 2 | * bootsect.s Copyright (C) 1991, 1992 Linus Torvalds 3 | * 4 | * bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves 5 | * itself out of the way to address 0x90000, and jumps there. 6 | * 7 | * It then loads 'setup' directly after itself (0x90200), and the system 8 | * at 0x10000, using BIOS interrupts. 9 | * 10 | * The loader has been made as simple as possible, and continuos 11 | * read errors will result in a unbreakable loop. Reboot by hand. It 12 | * loads pretty fast by getting whole tracks at a time whenever possible. 13 | * 14 | * 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86. 15 | */ 16 | 17 | #include "defs.h" 18 | 19 | ROOT_DEV = 0 20 | 21 | .code16 22 | .section ".bootsect", "ax", @progbits 23 | _boot: 24 | 25 | 26 | # ld86 requires an entry symbol. This may as well be the usual one. 27 | .globl _main 28 | _main: 29 | movw $BOOTSEG, %ax 30 | movw %ax, %ds 31 | movw $INITSEG, %ax 32 | movw %ax, %es 33 | movw $256, %cx 34 | subw %si, %si 35 | subw %di, %di 36 | cld 37 | rep 38 | movsw 39 | ljmp $INITSEG, $go - _boot 40 | 41 | go: 42 | movw %cs, %ax 43 | movw $(0x4000-12), %dx # 0x4000 is arbitrary value >= length of 44 | # bootsect + length of setup + room for stack 45 | # 12 is disk parm size 46 | 47 | # bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We 48 | # wouldn't have to worry about this if we checked the top of memory. Also 49 | # my BIOS can be configured to put the wini drive tables in high memory 50 | # instead of in the vector table. The old stack might have clobbered the 51 | # drive table. 52 | 53 | movw %ax, %ds 54 | movw %ax, %es 55 | movw %ax, %ss # put stack at INITSEG:0x4000-12. 56 | movw %dx, %sp 57 | 58 | /* 59 | * Many BIOS's default disk parameter tables will not 60 | * recognize multi-sector reads beyond the maximum sector number 61 | * specified in the default diskette parameter tables - this may 62 | * mean 7 sectors in some cases. 63 | * 64 | * Since single sector reads are slow and out of the question, 65 | * we must take care of this by creating new parameter tables 66 | * (for the first disk) in RAM. We will set the maximum sector 67 | * count to 18 - the most we will encounter on an HD 1.44. 68 | * 69 | * High doesn't hurt. Low does. 70 | * 71 | * Segments are as follows: ds=es=ss=cs - INITSEG, 72 | * fs = 0, gs = parameter table segment 73 | */ 74 | pushw $0 75 | popw %fs 76 | movw $0x78, %bx # fs:bx is parameter table address 77 | lgs %fs:(%bx),%si # gs:si is source 78 | 79 | movw %dx, %di # es:di is destination 80 | movw $6, %cx # copy 12 bytes 81 | cld 82 | 83 | rep movsw %gs:(%si), (%di) 84 | 85 | movw %dx, %di 86 | movb $18, 4(%di) # patch sector count 87 | 88 | movw %di, %fs:(%bx) 89 | movw %es, %fs:2(%bx) 90 | 91 | movw %cs, %ax 92 | movw %ax, %fs 93 | movw %ax, %gs 94 | 95 | xorb %ah, %ah # reset FDC 96 | xorb %dl, %dl 97 | int $0x13 98 | 99 | # load the setup-sectors directly after the bootblock. 100 | # Note that 'es' is already set up. 101 | 102 | load_setup: 103 | xorw %dx, %dx # drive 0, head 0 104 | movw $0x0002, %cx # sector 2, track 0 105 | movw $0x0200, %bx # address = 512, in INITSEG 106 | movw $(0x0200 + SETUPSECS), %ax # service 2, nr of sectors 107 | # (assume all on head 0, track 0) 108 | int $0x13 # read it 109 | jnc ok_load_setup # ok - continue 110 | 111 | pushw %ax # dump error code 112 | call print_nl 113 | movw %sp, %bp 114 | call print_hex 115 | popw %ax 116 | 117 | xorb %dl, %dl # reset FDC 118 | xorb %ah, %ah 119 | int $0x13 120 | jmp load_setup 121 | 122 | ok_load_setup: 123 | 124 | # Get disk drive parameters, specifically nr of sectors/track 125 | 126 | 127 | /* It seems that there is no BIOS call to get the number of sectors. Guess 128 | * 18 sectors if sector 18 can be read, 15 if sector 15 can be read. 129 | * Otherwise guess 9 130 | */ 131 | 132 | xorw %dx, %dx # drive 0, head 0 133 | movw $0x0012, %cx # sector 18, track 0 134 | movw $(0x200+(SETUPSECS*0x200)), %bx # address after setup (es = cs) 135 | movw $0x0201, %ax # service 2, 1 sector 136 | int $0x13 137 | jnc got_sectors 138 | movb $0x0f, %cl # sector 15 139 | movw $0x0201, %ax # service 2, 1 sector 140 | int $0x13 141 | jnc got_sectors 142 | movb $0x09, %cl 143 | 144 | got_sectors: 145 | movw %cx, %cs:sectors - _boot 146 | movw $INITSEG, %ax 147 | movw %ax, %es 148 | 149 | # Print some inane message 150 | 151 | movb $0x03, %ah # read cursor pos 152 | xorb %bh, %bh 153 | int $0x10 154 | 155 | movw $9, %cx 156 | movw $0x0007, %bx # page 0, attribute 7 (normal) 157 | movw $msg1 - _boot, %bp 158 | movw $0x1301, %ax # write string, move cursor 159 | int $0x10 160 | 161 | # ok, we've written the message, now 162 | # we want to load the system (at 0x10000) 163 | 164 | movw $TSTLOAD, %ax 165 | movw %ax, %es # segment of 0x010000 166 | call read_it 167 | call kill_motor 168 | call turnoffcursor 169 | call print_nl 170 | 171 | # after that (everyting loaded), we jump to 172 | # the setup-routine loaded directly after 173 | # the bootblock: 174 | 175 | ljmp $SETUPSEG,$0 176 | 177 | # This routine loads the system at address 0x10000, making sure 178 | # no 64kB boundaries are crossed. We try to load it as fast as 179 | # possible, loading whole tracks whenever we can. 180 | # 181 | # in: es - starting address segment (normally 0x1000) 182 | # 183 | sread: .word 1+SETUPSECS # sectors read of current track 184 | head: .word 0 # current head 185 | track: .word 0 # current track 186 | 187 | read_it: 188 | movw %es, %ax 189 | testw $0x0fff, %ax 190 | die: 191 | jne die # es must be at 64kB boundary 192 | xorw %bx,%bx # bx is starting address within segment 193 | rp_read: 194 | movw %es, %ax 195 | subw $TSTLOAD, %ax # have we loaded all yet? 196 | cmpw syssize - _boot, %ax 197 | jbe ok1_read 198 | ret 199 | ok1_read: 200 | movw %cs:sectors - _boot, %ax 201 | subw sread - _boot, %ax 202 | movw %ax, %cx 203 | shlw $9, %cx 204 | addw %bx, %cx 205 | jnc ok2_read 206 | je ok2_read 207 | xorw %ax, %ax 208 | subw %bx, %ax 209 | shrw $9, %ax 210 | ok2_read: 211 | call read_track 212 | movw %ax, %cx 213 | add sread - _boot, %ax 214 | cmpw %cs:sectors - _boot, %ax 215 | jne ok3_read 216 | movw $1, %ax 217 | subw head - _boot, %ax 218 | jne ok4_read 219 | incw track - _boot 220 | ok4_read: 221 | movw %ax, head - _boot 222 | xorw %ax, %ax 223 | ok3_read: 224 | movw %ax, sread - _boot 225 | shlw $9, %cx 226 | addw %cx, %bx 227 | jnc rp_read 228 | movw %es, %ax 229 | addb $0x10, %ah 230 | movw %ax, %es 231 | xorw %bx, %bx 232 | jmp rp_read 233 | 234 | read_track: 235 | pusha 236 | pusha 237 | movw $0xe2e, %ax # loading... message 2e = . 238 | movw $7, %bx 239 | int $0x10 240 | popa 241 | 242 | movw track - _boot, %dx 243 | movw sread - _boot, %cx 244 | incw %cx 245 | movb %dl, %ch 246 | movw head - _boot, %dx 247 | movb %dl, %dh 248 | andw $0x0100, %dx 249 | movb $2, %ah 250 | 251 | pushw %dx # save for error dump 252 | pushw %cx 253 | pushw %bx 254 | pushw %ax 255 | 256 | int $0x13 257 | jc bad_rt 258 | addw $8, %sp 259 | popa 260 | ret 261 | 262 | bad_rt: 263 | pushw %ax # save error code 264 | call print_all # ah = error, al = read 265 | 266 | xorb %ah, %ah 267 | xorb %dl, %dl 268 | int $0x13 269 | 270 | addw $10, %sp 271 | popa 272 | jmp read_track 273 | 274 | /* 275 | * print_all is for debugging purposes. 276 | * It will print out all of the registers. The assumption is that this is 277 | * called from a routine, with a stack frame like 278 | * dx 279 | * cx 280 | * bx 281 | * ax 282 | * error 283 | * ret <- sp 284 | * 285 | */ 286 | 287 | print_all: 288 | movw $5, %cx # error code + 4 registers 289 | movw %sp, %bp 290 | 291 | print_loop: 292 | pushw %cx # save count left 293 | call print_nl # nl for readability 294 | 295 | cmpb 5, %cl # see if register name is needed 296 | jae no_reg 297 | 298 | movw $(0xe05 + 'A' - 1), %ax 299 | subb %cl, %al 300 | int $0x10 301 | movb $'X', %al 302 | int $0x10 303 | movb $':', %al 304 | int $0x10 305 | 306 | no_reg: 307 | addw $2, %bp # next register 308 | call print_hex # print it 309 | popw %cx 310 | loop print_loop 311 | ret 312 | 313 | print_nl: 314 | movw $0xe0d, %ax # CR 315 | int $0x10 316 | movb $0x0a, %al # LF 317 | int $0x10 318 | ret 319 | 320 | /* 321 | * print_hex is for debugging purposes, and prints the word 322 | * pointed to by ss:bp in hexadecmial. 323 | */ 324 | 325 | print_hex: 326 | movw $4, %cx # 4 hex digits 327 | movw (%bp), %dx # load word into dx 328 | 329 | print_digit: 330 | rolw $4, %dx # rotate so that lowest 4 bits are used 331 | movb $0xe, %ah 332 | movb %dl, %al # mask off so we have only next nibble 333 | andb $0xf, %al 334 | addb $'0', %al # convert to 0-based digit 335 | cmpb $'9', %al # check for overflow 336 | jbe good_digit 337 | addb $('A' - '0' - 10), %al 338 | 339 | good_digit: 340 | int $0x10 341 | loop print_digit 342 | ret 343 | 344 | 345 | /* 346 | * This procedure turns off the floppy drive motor, so 347 | * that we enter the kernel in a known state, and 348 | * don't have to worry about it later. 349 | */ 350 | kill_motor: 351 | pushw %dx 352 | movw $0x3f2, %dx 353 | xorb %al, %al 354 | outb %al, %dx 355 | popw %dx 356 | ret 357 | 358 | turnoffcursor: 359 | movb $0x01, %ah # turn off the cursor 360 | movb $0x00, %bh 361 | movw $0x2000, %cx 362 | int $0x10 363 | ret 364 | 365 | sectors: 366 | .word 0 367 | 368 | msg1: 369 | .byte 13,10 370 | .ascii "Loading" 371 | 372 | .org 497 373 | setup_sects: 374 | .byte SETUPSECS 375 | .org 500 376 | syssize: 377 | .word _syssize 378 | .org 508 379 | root_dev: 380 | .word ROOT_DEV 381 | boot_flag: 382 | .word 0xAA55 383 | _eboot: 384 | -------------------------------------------------------------------------------- /build-number.txt: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /build.number: -------------------------------------------------------------------------------- 1 | 617 2 | -------------------------------------------------------------------------------- /build_number.h: -------------------------------------------------------------------------------- 1 | #ifndef BUILD_NUMBER_STR 2 | #define BUILD_NUMBER_STR "617" 3 | #endif 4 | #ifndef VERSION_STR 5 | #define VERSION_STR "4.99617 - Thu May 3 11:45:57 CEST 2012" 6 | #endif 7 | #ifndef VERSION_STR_SHORT 8 | #define VERSION_STR_SHORT "4.99617" 9 | #endif 10 | -------------------------------------------------------------------------------- /buildnumber.mak: -------------------------------------------------------------------------------- 1 | # Create an auto-incrementing build number. 2 | 3 | BUILD_NUMBER_LDFLAGS = -Xlinker --defsym -Xlinker __BUILD_DATE=$$(date +'%Y%m%d') 4 | BUILD_NUMBER_LDFLAGS += -Xlinker --defsym -Xlinker __BUILD_NUMBER=$$(cat $(BUILD_NUMBER_FILE)) 5 | 6 | # Build number file. Increment if any object file changes. 7 | $(BUILD_NUMBER_FILE): $(OBJS) 8 | @if ! test -f $(BUILD_NUMBER_FILE); then echo 0 > $(BUILD_NUMBER_FILE); fi 9 | @echo $$(($$(cat $(BUILD_NUMBER_FILE)) + 1)) > $(BUILD_NUMBER_FILE) 10 | 11 | -------------------------------------------------------------------------------- /changelog: -------------------------------------------------------------------------------- 1 | Memtest86+ V5.01 changelog 2 | ---------------------------- 3 | 4 | - Added support for up to 2 TB of RAM on X64 CPUs 5 | - Added experimental SMT support up to 32 cores 6 | - Added complete detection for memory controllers. 7 | - Added Motherboard Manufacturer & Model reporting 8 | - Added CPU temperature reporting 9 | - Added enhanced Fail Safe Mode (Press F1 at startup) 10 | - Added support for Intel "Sandy Bridge-E" CPUs 11 | - Added support for Intel "Ivy Bridge" CPUs 12 | - Added preliminary support for Intel "Haswell" CPUs 13 | - Added preliminary support for Intel "Haswell-ULT" CPUs 14 | - Added support for AMD "Kabini" (K16) CPUs 15 | - Added support for AMD "Bulldozer" CPUs 16 | - Added support for AMD "Trinity" CPUs 17 | - Added support for AMD E-/C-/G-/Z- "Bobcat" CPUs 18 | - Added support for Intel Atom "Pineview" CPUs 19 | - Added support for Intel Atom "Cedar Trail" CPUs 20 | - Added SPD detection on most AMD Chipsets 21 | - Enforced Coreboot support 22 | - Optimized run time for faster memory error detection 23 | - Rewriten lots of memory timings detection code 24 | - Corrected bugs, bugs and more bugs 25 | 26 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | /* config.c - MemTest-86 Version 3.4 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Chris Brady 5 | * ---------------------------------------------------- 6 | * MemTest86+ V5.00 Specific code (GPL V2.0) 7 | * By Samuel DEMEULEMEESTER, sdemeule@memtest.org 8 | * http://www.x86-secret.com - http://www.memtest.org 9 | */ 10 | #include "test.h" 11 | #include "screen_buffer.h" 12 | #include "dmi.h" 13 | 14 | extern int bail, beepmode; 15 | extern struct tseq tseq[]; 16 | extern short e820_nr; 17 | void performance(); 18 | extern volatile short cpu_mode; 19 | extern volatile int test; 20 | extern void find_chunks(); 21 | extern volatile short start_seq; 22 | extern short restart_flag; 23 | extern short onepass; 24 | extern short btflag; 25 | 26 | extern void get_list(int x, int y, int len, char *buf); 27 | 28 | char save[2][POP_H][POP_W]; 29 | char save2[2][POP2_H][POP2_W]; 30 | 31 | void get_config() 32 | { 33 | int flag = 0, sflag = 0, i, j, k, n, m, prt = 0; 34 | int reprint_screen = 0; 35 | char cp[64]; 36 | ulong page; 37 | 38 | popup(); 39 | wait_keyup(); 40 | while(!flag) { 41 | cprint(POP_Y+1, POP_X+2, "Settings:"); 42 | cprint(POP_Y+3, POP_X+6, "(1) Test Selection"); 43 | cprint(POP_Y+4, POP_X+6, "(2) Address Range"); 44 | cprint(POP_Y+5, POP_X+6, "(3) Error Report Mode"); 45 | cprint(POP_Y+6, POP_X+6, "(4) Core Selection"); 46 | cprint(POP_Y+7, POP_X+6, "(5) Refresh Screen"); 47 | cprint(POP_Y+8, POP_X+6, "(6) Display DMI Data"); 48 | cprint(POP_Y+9, POP_X+6, "(7) Display SPD Data"); 49 | cprint(POP_Y+11, POP_X+6, "(0) Continue"); 50 | 51 | /* Wait for key release */ 52 | /* Fooey! This nuts'es up the serial input. */ 53 | sflag = 0; 54 | switch(get_key()) { 55 | case 2: 56 | /* 1 - Test Selection */ 57 | popclear(); 58 | cprint(POP_Y+1, POP_X+2, "Test Selection:"); 59 | cprint(POP_Y+3, POP_X+6, "(1) Default Tests"); 60 | cprint(POP_Y+4, POP_X+6, "(2) Skip Current Test"); 61 | cprint(POP_Y+5, POP_X+6, "(3) Select Test"); 62 | cprint(POP_Y+6, POP_X+6, "(4) Enter Test List"); 63 | cprint(POP_Y+7, POP_X+6, "(0) Cancel"); 64 | if (v->testsel < 0) { 65 | cprint(POP_Y+3, POP_X+5, ">"); 66 | } else { 67 | cprint(POP_Y+5, POP_X+5, ">"); 68 | } 69 | wait_keyup(); 70 | while (!sflag) { 71 | switch(get_key()) { 72 | case 2: 73 | /* Default - All tests */ 74 | i = 0; 75 | while (tseq[i].cpu_sel) { 76 | tseq[i].sel = 1; 77 | i++; 78 | } 79 | find_ticks_for_pass(); 80 | sflag++; 81 | break; 82 | case 3: 83 | /* Skip test */ 84 | bail++; 85 | sflag++; 86 | break; 87 | case 4: 88 | /* Select test */ 89 | popclear(); 90 | cprint(POP_Y+1, POP_X+3, 91 | "Test Selection:"); 92 | cprint(POP_Y+4, POP_X+5, 93 | "Test Number [1-11]: "); 94 | n = getval(POP_Y+4, POP_X+24, 0) - 1; 95 | if (n <= 11) 96 | { 97 | /* Deselect all tests */ 98 | i = 0; 99 | while (tseq[i].cpu_sel) { 100 | tseq[i].sel = 0; 101 | i++; 102 | } 103 | /* Now set the selection */ 104 | tseq[n].sel = 1; 105 | v->pass = -1; 106 | test = n; 107 | find_ticks_for_pass(); 108 | sflag++; 109 | bail++; 110 | } 111 | break; 112 | case 5: 113 | /* Enter a test list */ 114 | popclear(); 115 | cprint(POP_Y+1, POP_X+3, 116 | "Enter a comma separated list"); 117 | cprint(POP_Y+2, POP_X+3, 118 | "of tests to execute:"); 119 | cprint(POP_Y+5, POP_X+5, "List: "); 120 | /* Deselect all tests */ 121 | k = 0; 122 | while (tseq[k].cpu_sel) { 123 | tseq[k].sel = 0; 124 | k++; 125 | } 126 | 127 | /* Get the list */ 128 | for (i=0; i<64; i++) cp[i] = 0; 129 | get_list(POP_Y+5, POP_X+10, 64, cp); 130 | 131 | /* Now enable all of the tests in the 132 | * list */ 133 | i = j = m = 0; 134 | while (1) { 135 | if (isdigit(cp[i])) { 136 | n = cp[i]-'0'; 137 | j = j*10 + n; 138 | i++; 139 | if (cp[i] == ',' || cp[i] == 0){ 140 | if (j < k) { 141 | tseq[j].sel = 1; 142 | m++; 143 | } 144 | if (cp[i] == 0) break; 145 | j = 0; 146 | i++; 147 | } 148 | } 149 | } 150 | 151 | /* If we didn't select at least one 152 | * test turn them all back on */ 153 | if (m == 0) { 154 | k = 0; 155 | while (tseq[k].cpu_sel) { 156 | tseq[k].sel = 1; 157 | k++; 158 | } 159 | } 160 | v->pass = -1; 161 | test = n; 162 | find_ticks_for_pass(); 163 | sflag++; 164 | bail++; 165 | break; 166 | case 11: 167 | case 57: 168 | sflag++; 169 | break; 170 | } 171 | } 172 | popclear(); 173 | break; 174 | case 3: 175 | /* 2 - Address Range */ 176 | popclear(); 177 | cprint(POP_Y+1, POP_X+2, "Test Address Range:"); 178 | cprint(POP_Y+3, POP_X+6, "(1) Set Lower Limit"); 179 | cprint(POP_Y+4, POP_X+6, "(2) Set Upper Limit"); 180 | cprint(POP_Y+5, POP_X+6, "(3) Test All Memory"); 181 | cprint(POP_Y+6, POP_X+6, "(0) Cancel"); 182 | wait_keyup(); 183 | while (!sflag) { 184 | switch(get_key()) { 185 | case 2: 186 | /* Lower Limit */ 187 | popclear(); 188 | cprint(POP_Y+2, POP_X+4, 189 | "Lower Limit: "); 190 | cprint(POP_Y+4, POP_X+4, 191 | "Current: "); 192 | aprint(POP_Y+4, POP_X+13, v->plim_lower); 193 | cprint(POP_Y+6, POP_X+4, 194 | "New: "); 195 | page = getval(POP_Y+6, POP_X+9, 12); 196 | if (page + 1 <= v->plim_upper) { 197 | v->plim_lower = page; 198 | test--; 199 | bail++; 200 | } 201 | adj_mem(); 202 | find_chunks(); 203 | find_ticks_for_pass(); 204 | sflag++; 205 | break; 206 | case 3: 207 | /* Upper Limit */ 208 | popclear(); 209 | cprint(POP_Y+2, POP_X+4, 210 | "Upper Limit: "); 211 | cprint(POP_Y+4, POP_X+4, 212 | "Current: "); 213 | aprint(POP_Y+4, POP_X+13, v->plim_upper); 214 | cprint(POP_Y+6, POP_X+4, 215 | "New: "); 216 | page = getval(POP_Y+6, POP_X+9, 12); 217 | if (page - 1 >= v->plim_lower) { 218 | v->plim_upper = page; 219 | bail++; 220 | test--; 221 | } 222 | adj_mem(); 223 | find_chunks(); 224 | find_ticks_for_pass(); 225 | sflag++; 226 | break; 227 | case 4: 228 | /* All of memory */ 229 | v->plim_lower = 0; 230 | v->plim_upper = 231 | v->pmap[v->msegs - 1].end; 232 | test--; 233 | bail++; 234 | adj_mem(); 235 | find_chunks(); 236 | find_ticks_for_pass(); 237 | sflag++; 238 | break; 239 | case 11: 240 | case 57: 241 | /* 0/CR - Continue */ 242 | sflag++; 243 | break; 244 | } 245 | } 246 | popclear(); 247 | break; 248 | case 4: 249 | /* Error Mode */ 250 | popclear(); 251 | cprint(POP_Y+1, POP_X+2, "Printing Mode:"); 252 | cprint(POP_Y+3, POP_X+6, "(1) Error Summary"); 253 | cprint(POP_Y+4, POP_X+6, "(2) Individual Errors"); 254 | cprint(POP_Y+5, POP_X+6, "(3) BadRAM Patterns"); 255 | cprint(POP_Y+6, POP_X+6, "(4) Error Counts Only"); 256 | cprint(POP_Y+7, POP_X+6, "(5) Beep on Error"); 257 | cprint(POP_Y+8, POP_X+6, "(0) Cancel"); 258 | cprint(POP_Y+3+v->printmode, POP_X+5, ">"); 259 | if (beepmode) { cprint(POP_Y+7, POP_X+5, ">"); } 260 | wait_keyup(); 261 | while (!sflag) { 262 | switch(get_key()) { 263 | case 2: 264 | /* Error Summary */ 265 | v->printmode=PRINTMODE_SUMMARY; 266 | v->erri.eadr = 0; 267 | v->erri.hdr_flag = 0; 268 | sflag++; 269 | break; 270 | case 3: 271 | /* Separate Addresses */ 272 | v->printmode=PRINTMODE_ADDRESSES; 273 | v->erri.eadr = 0; 274 | v->erri.hdr_flag = 0; 275 | v->msg_line = LINE_SCROLL-1; 276 | sflag++; 277 | break; 278 | case 4: 279 | /* BadRAM Patterns */ 280 | v->printmode=PRINTMODE_PATTERNS; 281 | v->erri.hdr_flag = 0; 282 | sflag++; 283 | prt++; 284 | break; 285 | case 5: 286 | /* Error Counts Only */ 287 | v->printmode=PRINTMODE_NONE; 288 | v->erri.hdr_flag = 0; 289 | sflag++; 290 | break; 291 | case 6: 292 | /* Set Beep On Error mode */ 293 | beepmode = !beepmode; 294 | sflag++; 295 | break; 296 | case 11: 297 | case 57: 298 | /* 0/CR - Continue */ 299 | sflag++; 300 | break; 301 | } 302 | } 303 | popclear(); 304 | break; 305 | case 5: 306 | /* CPU Mode */ 307 | reprint_screen = 1; 308 | popclear(); 309 | cprint(POP_Y+1, POP_X+2, "CPU Selection Mode:"); 310 | cprint(POP_Y+3, POP_X+6, "(1) Parallel (All)"); 311 | cprint(POP_Y+4, POP_X+6, "(2) Round Robin (RRb)"); 312 | cprint(POP_Y+5, POP_X+6, "(3) Sequential (Seq)"); 313 | cprint(POP_Y+6, POP_X+6, "(0) Cancel"); 314 | cprint(POP_Y+2+cpu_mode, POP_X+5, ">"); 315 | wait_keyup(); 316 | while(!sflag) { 317 | switch(get_key()) { 318 | case 2: 319 | if (cpu_mode != CPM_ALL) bail++; 320 | cpu_mode = CPM_ALL; 321 | sflag++; 322 | popdown(); 323 | cprint(9,34,"All"); 324 | popup(); 325 | break; 326 | case 3: 327 | if (cpu_mode != CPM_RROBIN) bail++; 328 | cpu_mode = CPM_RROBIN; 329 | sflag++; 330 | popdown(); 331 | cprint(9,34,"RRb"); 332 | popup(); 333 | break; 334 | case 4: 335 | if (cpu_mode != CPM_SEQ) bail++; 336 | cpu_mode = CPM_SEQ; 337 | sflag++; 338 | popdown(); 339 | cprint(9,34,"Seq"); 340 | popup(); 341 | break; 342 | case 11: 343 | case 57: 344 | /* 0/CR - Continue */ 345 | sflag++; 346 | break; 347 | } 348 | } 349 | popclear(); 350 | break; 351 | case 6: 352 | reprint_screen = 1; 353 | flag++; 354 | break; 355 | case 7: 356 | /* Display DMI Memory Info */ 357 | pop2up(); 358 | print_dmi_info(); 359 | pop2down(); 360 | break; 361 | case 8: 362 | /* Display SPD Data */ 363 | popdown(); 364 | show_spd(); 365 | popup(); 366 | sflag++; 367 | break; 368 | case 11: 369 | case 57: 370 | case 28: 371 | /* 0/CR/SP - Continue */ 372 | flag++; 373 | break; 374 | } 375 | } 376 | popdown(); 377 | if (prt) { 378 | printpatn(); 379 | } 380 | if (reprint_screen){ 381 | tty_print_screen(); 382 | } 383 | } 384 | 385 | void popup() 386 | { 387 | int i, j; 388 | char *pp; 389 | 390 | for (i=POP_Y; iselected_pages = 0; 495 | for (i=0; i< v->msegs; i++) { 496 | /* Segment inside limits ? */ 497 | if (v->pmap[i].start >= v->plim_lower && 498 | v->pmap[i].end <= v->plim_upper) { 499 | v->selected_pages += (v->pmap[i].end - v->pmap[i].start); 500 | continue; 501 | } 502 | /* Segment starts below limit? */ 503 | if (v->pmap[i].start < v->plim_lower) { 504 | /* Also ends below limit? */ 505 | if (v->pmap[i].end < v->plim_lower) { 506 | continue; 507 | } 508 | 509 | /* Ends past upper limit? */ 510 | if (v->pmap[i].end > v->plim_upper) { 511 | v->selected_pages += 512 | v->plim_upper - v->plim_lower; 513 | } else { 514 | /* Straddles lower limit */ 515 | v->selected_pages += 516 | (v->pmap[i].end - v->plim_lower); 517 | } 518 | continue; 519 | } 520 | /* Segment ends above limit? */ 521 | if (v->pmap[i].end > v->plim_upper) { 522 | /* Also starts above limit? */ 523 | if (v->pmap[i].start > v->plim_upper) { 524 | continue; 525 | } 526 | /* Straddles upper limit */ 527 | v->selected_pages += 528 | (v->plim_upper - v->pmap[i].start); 529 | } 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MemTest86+ V5 Specific code (GPL V2.0) 3 | * By Samuel DEMEULEMEESTER, sdemeule@memtest.org 4 | * http://www.canardpc.com - http://www.memtest.org 5 | * ------------------------------------------------ 6 | * config.h - MemTest-86 Version 3.3 7 | * 8 | * Compile time configuration options 9 | * 10 | * Released under version 2 of the Gnu Public License. 11 | * By Chris Brady 12 | */ 13 | 14 | /* CONSERVATIVE_SMP - If set to 0, SMP will be enabled by default */ 15 | /* Might be enabled in future revision after extensive testing */ 16 | /* In all cases, SMP is disabled by defaut on server platform */ 17 | #define CONSERVATIVE_SMP 1 18 | 19 | /* BEEP_MODE - Beep on error. Default off, Change to 1 to enable */ 20 | #define BEEP_MODE 0 21 | 22 | /* BEEP_END_NO_ERROR - Beep at end of each pass without error. Default off, Change to 1 to enable */ 23 | #define BEEP_END_NO_ERROR 0 24 | 25 | /* PARITY_MEM - Enables support for reporting memory parity errors */ 26 | /* Experimental, normally enabled */ 27 | #define PARITY_MEM 28 | 29 | /* SERIAL_CONSOLE_DEFAULT - The default state of the serial console. */ 30 | /* This is normally off since it slows down testing. Change to a 1 */ 31 | /* to enable. */ 32 | #define SERIAL_CONSOLE_DEFAULT 0 33 | 34 | /* SERIAL_TTY - The default serial port to use. 0=ttyS0, 1=ttyS1 */ 35 | #define SERIAL_TTY 0 36 | 37 | /* SERIAL_BAUD_RATE - Baud rate for the serial console */ 38 | #define SERIAL_BAUD_RATE 9600 39 | 40 | /* SCRN_DEBUG - extra check for SCREEN_BUFFER 41 | */ 42 | /* #define SCRN_DEBUG */ 43 | 44 | /* APM - Turns off APM at boot time to avoid blanking the screen */ 45 | /* Normally enabled */ 46 | #define APM_OFF 47 | 48 | /* USB_WAR - Enables a workaround for errors caused by BIOS USB keyboard */ 49 | /* and mouse support*/ 50 | /* Normally enabled */ 51 | #define USB_WAR 52 | 53 | -------------------------------------------------------------------------------- /controller.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Distrotech/memtest86/a0af2ad356379c1dace15b0dc1677f81e240dd54/controller.c -------------------------------------------------------------------------------- /controller.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMTEST_CONTROLLER_H 2 | #define MEMTEST_CONTROLLER_H 3 | 4 | struct pci_memory_controller { 5 | unsigned vendor; 6 | unsigned device; 7 | char *name; 8 | char *ram_type; 9 | int tested; 10 | void (*poll_fsb)(void); 11 | void (*poll_timings)(void); 12 | void (*setup_ecc)(void); 13 | void (*poll_errors)(void); 14 | }; 15 | 16 | void find_controller(void); 17 | void poll_errors(void); 18 | void set_ecc_polling(int val); 19 | void coretemp(void); 20 | extern struct pci_memory_controller controllers[]; 21 | 22 | 23 | #endif /* MEMTEST_CONTROLLER_H */ 24 | -------------------------------------------------------------------------------- /cpuid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cpuid.c -- 3 | * 4 | * Implements CPUID querying functions 5 | * 6 | */ 7 | #include "stdin.h" 8 | #include "cpuid.h" 9 | 10 | struct cpu_ident cpu_id; 11 | 12 | void get_cpuid() 13 | { 14 | unsigned int *v, dummy[3]; 15 | char *p, *q; 16 | 17 | /* Get max std cpuid & vendor ID */ 18 | cpuid(0x0, &cpu_id.max_cpuid, &cpu_id.vend_id.uint32_array[0], 19 | &cpu_id.vend_id.uint32_array[2], &cpu_id.vend_id.uint32_array[1]); 20 | cpu_id.vend_id.char_array[11] = 0; 21 | 22 | /* Get processor family information & feature flags */ 23 | if (cpu_id.max_cpuid >= 1) { 24 | cpuid(0x00000001, &cpu_id.vers.flat, &cpu_id.info.flat, 25 | &cpu_id.fid.uint32_array[1], &cpu_id.fid.uint32_array[0]); 26 | } 27 | 28 | /* Get the digital thermal sensor & power management status bits */ 29 | if(cpu_id.max_cpuid >= 6) { 30 | cpuid(0x00000006, &cpu_id.dts_pmp, &dummy[0], &dummy[1], &dummy[2]); 31 | } 32 | 33 | /* Get the max extended cpuid */ 34 | cpuid(0x80000000, &cpu_id.max_xcpuid, &dummy[0], &dummy[1], &dummy[2]); 35 | 36 | /* Get extended feature flags, only save EDX */ 37 | if (cpu_id.max_xcpuid >= 0x80000001) { 38 | cpuid(0x80000001, &dummy[0], &dummy[1], 39 | &dummy[2], &cpu_id.fid.uint32_array[2]); 40 | } 41 | 42 | /* Get the brand ID */ 43 | if (cpu_id.max_xcpuid >= 0x80000004) { 44 | v = (unsigned int *)&cpu_id.brand_id; 45 | cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); 46 | cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); 47 | cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); 48 | cpu_id.brand_id.char_array[47] = 0; 49 | } 50 | /* 51 | * Intel chips right-justify this string for some dumb reason; 52 | * undo that brain damage: 53 | */ 54 | p = q = &cpu_id.brand_id.char_array[0]; 55 | while (*p == ' ') 56 | p++; 57 | if (p != q) { 58 | while (*p) 59 | *q++ = *p++; 60 | while (q <= &cpu_id.brand_id.char_array[48]) 61 | *q++ = '\0'; /* Zero-pad the rest */ 62 | } 63 | 64 | /* Get cache information */ 65 | switch(cpu_id.vend_id.char_array[0]) { 66 | case 'A': 67 | /* AMD Processors */ 68 | /* The cache information is only in ecx and edx so only save 69 | * those registers */ 70 | if (cpu_id.max_xcpuid >= 0x80000005) { 71 | cpuid(0x80000005, &dummy[0], &dummy[1], 72 | &cpu_id.cache_info.uint[0], &cpu_id.cache_info.uint[1]); 73 | } 74 | if (cpu_id.max_xcpuid >= 0x80000006) { 75 | cpuid(0x80000006, &dummy[0], &dummy[1], 76 | &cpu_id.cache_info.uint[2], &cpu_id.cache_info.uint[3]); 77 | } 78 | break; 79 | case 'G': 80 | /* Intel Processors, Need to do this in init.c */ 81 | break; 82 | } 83 | 84 | /* Turn off mon bit since monitor based spin wait may not be reliable */ 85 | cpu_id.fid.bits.mon = 0; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /cpuid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cpuid.h -- 3 | * contains the data structures required for CPUID 4 | * implementation. 5 | */ 6 | 7 | #define CPUID_VENDOR_LENGTH 3 /* 3 GPRs hold vendor ID */ 8 | #define CPUID_VENDOR_STR_LENGTH (CPUID_VENDOR_LENGTH * sizeof(uint32_t) + 1) 9 | #define CPUID_BRAND_LENGTH 12 /* 12 GPRs hold vendor ID */ 10 | #define CPUID_BRAND_STR_LENGTH (CPUID_BRAND_LENGTH * sizeof(uint32_t) + 1) 11 | 12 | extern struct cpu_ident cpu_id; 13 | 14 | static inline void __cpuid(unsigned int *eax, unsigned int *ebx, 15 | unsigned int *ecx, unsigned int *edx) 16 | { 17 | /* ecx is often an input as well as an output. */ 18 | asm volatile("\t" 19 | "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" 20 | : "=a" (*eax), 21 | "=D" (*ebx), 22 | "=c" (*ecx), 23 | "=d" (*edx) 24 | : "0" (*eax), "2" (*ecx)); 25 | } 26 | 27 | static inline void cpuid(unsigned int op, 28 | unsigned int *eax, unsigned int *ebx, 29 | unsigned int *ecx, unsigned int *edx) 30 | { 31 | *eax = op; 32 | *ecx = 0; 33 | __cpuid(eax, ebx, ecx, edx); 34 | } 35 | 36 | /* Some CPUID calls want 'count' to be placed in ecx */ 37 | static inline void cpuid_count(unsigned int op, int count, 38 | unsigned int *eax, unsigned int *ebx, 39 | unsigned int *ecx, unsigned int *edx) 40 | { 41 | *eax = op; 42 | *ecx = count; 43 | __cpuid(eax, ebx, ecx, edx); 44 | } 45 | 46 | /* Typedef for storing the Cache Information */ 47 | typedef union { 48 | unsigned char ch[48]; 49 | uint32_t uint[12]; 50 | struct { 51 | uint32_t fill1:24; /* Bit 0 */ 52 | uint32_t l1_i_sz:8; 53 | uint32_t fill2:24; 54 | uint32_t l1_d_sz:8; 55 | uint32_t fill3:16; 56 | uint32_t l2_sz:16; 57 | uint32_t fill4:18; 58 | uint32_t l3_sz:14; 59 | uint32_t fill5[8]; 60 | } amd; 61 | } cpuid_cache_info_t; 62 | 63 | /* Typedef for storing the CPUID Vendor String */ 64 | typedef union { 65 | /* Note: the extra byte in the char array is for '\0'. */ 66 | char char_array[CPUID_VENDOR_STR_LENGTH]; 67 | uint32_t uint32_array[CPUID_VENDOR_LENGTH]; 68 | } cpuid_vendor_string_t; 69 | 70 | /* Typedef for storing the CPUID Brand String */ 71 | typedef union { 72 | /* Note: the extra byte in the char array is for '\0'. */ 73 | char char_array[CPUID_BRAND_STR_LENGTH]; 74 | uint32_t uint32_array[CPUID_BRAND_LENGTH]; 75 | } cpuid_brand_string_t; 76 | 77 | /* Typedef for storing CPUID Version */ 78 | typedef union { 79 | uint32_t flat; 80 | struct { 81 | uint32_t stepping:4; /* Bit 0 */ 82 | uint32_t model:4; 83 | uint32_t family:4; 84 | uint32_t processorType:2; 85 | uint32_t reserved1514:2; 86 | uint32_t extendedModel:4; 87 | uint32_t extendedFamily:8; 88 | uint32_t reserved3128:4; /* Bit 31 */ 89 | } bits; 90 | } cpuid_version_t; 91 | 92 | /* Typedef for storing CPUID Processor Information */ 93 | typedef union { 94 | uint32_t flat; 95 | struct { 96 | uint32_t brandIndex:8; /* Bit 0 */ 97 | uint32_t cflushLineSize:8; 98 | uint32_t logicalProcessorCount:8; 99 | uint32_t apicID:8; /* Bit 31 */ 100 | } bits; 101 | } cpuid_proc_info_t; 102 | 103 | /* Typedef for storing CPUID Feature flags */ 104 | typedef union { 105 | uint32_t flat; 106 | struct { 107 | uint32_t :1; 108 | } bits; 109 | } cpuid_custom_features; 110 | 111 | /* Typedef for storing CPUID Feature flags */ 112 | typedef union { 113 | uint32_t uint32_array[3]; 114 | struct { 115 | uint32_t fpu:1; /* EDX feature flags, bit 0 */ 116 | uint32_t vme:1; 117 | uint32_t de:1; 118 | uint32_t pse:1; 119 | uint32_t rdtsc:1; 120 | uint32_t msr:1; 121 | uint32_t pae:1; 122 | uint32_t mce:1; 123 | uint32_t cx8:1; 124 | uint32_t apic:1; 125 | uint32_t bit10:1; 126 | uint32_t sep:1; 127 | uint32_t mtrr:1; 128 | uint32_t pge:1; 129 | uint32_t mca:1; 130 | uint32_t cmov:1; 131 | uint32_t pat:1; 132 | uint32_t pse36:1; 133 | uint32_t psn:1; 134 | uint32_t cflush:1; 135 | uint32_t bit20:1; 136 | uint32_t ds:1; 137 | uint32_t acpi:1; 138 | uint32_t mmx:1; 139 | uint32_t fxsr:1; 140 | uint32_t sse:1; 141 | uint32_t sse2:1; 142 | uint32_t ss:1; 143 | uint32_t htt:1; 144 | uint32_t tm:1; 145 | uint32_t bit30:1; 146 | uint32_t pbe:1; /* EDX feature flags, bit 31 */ 147 | uint32_t sse3:1; /* ECX feature flags, bit 0 */ 148 | uint32_t mulq:1; 149 | uint32_t bit2:1; 150 | uint32_t mon:1; 151 | uint32_t dscpl:1; 152 | uint32_t vmx:1; 153 | uint32_t smx:1; 154 | uint32_t eist:1; 155 | uint32_t tm2:1; 156 | uint32_t bits_9_31:23; 157 | uint32_t bits0_28:29; /* EDX extended feature flags, bit 0 */ 158 | uint32_t lm:1; /* Long Mode */ 159 | uint32_t bits_30_31:2; /* EDX extended feature flags, bit 32 */ 160 | } bits; 161 | } cpuid_feature_flags_t; 162 | 163 | /* An overall structure to cache all of the CPUID information */ 164 | struct cpu_ident { 165 | uint32_t max_cpuid; 166 | uint32_t max_xcpuid; 167 | uint32_t dts_pmp; 168 | cpuid_version_t vers; 169 | cpuid_proc_info_t info; 170 | cpuid_feature_flags_t fid; 171 | cpuid_vendor_string_t vend_id; 172 | cpuid_brand_string_t brand_id; 173 | cpuid_cache_info_t cache_info; 174 | cpuid_custom_features custom; 175 | }; 176 | 177 | struct cpuid4_eax { 178 | uint32_t ctype:5; 179 | uint32_t level:3; 180 | uint32_t is_self_initializing:1; 181 | uint32_t is_fully_associative:1; 182 | uint32_t reserved:4; 183 | uint32_t num_threads_sharing:12; 184 | uint32_t num_cores_on_die:6; 185 | }; 186 | 187 | struct cpuid4_ebx { 188 | uint32_t coherency_line_size:12; 189 | uint32_t physical_line_partition:10; 190 | uint32_t ways_of_associativity:10; 191 | }; 192 | 193 | struct cpuid4_ecx { 194 | uint32_t number_of_sets:32; 195 | }; 196 | 197 | -------------------------------------------------------------------------------- /defs.h: -------------------------------------------------------------------------------- 1 | /* defs.h - MemTest-86 Version 3.3 2 | * assembler/compiler definitions 3 | * 4 | * Released under version 2 of the Gnu Public License. 5 | * By Chris Brady 6 | */ 7 | 8 | #define SETUPSECS 4 /* Number of setup sectors */ 9 | 10 | /* 11 | * Caution!! There is magic in the build process. Read 12 | * README.build-process before you change anything. 13 | * Unlike earlier versions all of the settings are in defs.h 14 | * so the build process should be more robust. 15 | */ 16 | #define LOW_TEST_ADR 0x00010000 /* Final adrs for test code */ 17 | 18 | #define BOOTSEG 0x07c0 /* Segment adrs for inital boot */ 19 | #define INITSEG 0x9000 /* Segment adrs for relocated boot */ 20 | #define SETUPSEG (INITSEG+0x20) /* Segment adrs for relocated setup */ 21 | #define TSTLOAD 0x1000 /* Segment adrs for load of test */ 22 | 23 | #define KERNEL_CS 0x10 /* 32 bit segment adrs for code */ 24 | #define KERNEL_DS 0x18 /* 32 bit segment adrs for data */ 25 | #define REAL_CS 0x20 /* 16 bit segment adrs for code */ 26 | #define REAL_DS 0x28 /* 16 bit segment adrs for data */ 27 | -------------------------------------------------------------------------------- /dmi.c: -------------------------------------------------------------------------------- 1 | /* dmi.c using the DMI from SMBIOS to read information about the hardware's 2 | * memory devices capabilities and where they are mapped into the address space 3 | * 4 | * Copyright (c) Joachim Deguara, AMD 2006 5 | * 6 | * Release under the GPL version 2 7 | * ---------------------------------------------------- 8 | * Memtest86+ V4.00 - Added compliance with SMBIOS Spec V2.6.1 9 | */ 10 | 11 | 12 | #include "test.h" 13 | #include 14 | 15 | 16 | #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) 17 | #define round_down(x,y) ((x) & ~((y)-1)) 18 | 19 | 20 | struct dmi_eps { 21 | uint8_t anchor[4]; 22 | int8_t checksum; 23 | uint8_t length; 24 | uint8_t majorversion; 25 | uint8_t minorversion; 26 | uint16_t maxstructsize; 27 | uint8_t revision; 28 | uint8_t pad[5]; 29 | uint8_t intanchor[5]; 30 | int8_t intchecksum; 31 | uint16_t tablelength; 32 | uint32_t tableaddress; 33 | uint16_t numstructs; 34 | uint8_t SMBIOSrev; 35 | } __attribute__((packed)); 36 | 37 | struct tstruct_header{ 38 | uint8_t type; 39 | uint8_t length; 40 | uint16_t handle; 41 | } __attribute__((packed)); 42 | 43 | struct system_map { 44 | struct tstruct_header header; 45 | uint8_t manufacturer; 46 | uint8_t productname; 47 | uint8_t version; 48 | uint8_t serialnumber; 49 | uint8_t uuidbytes[16]; 50 | uint8_t wut; 51 | } __attribute__((packed)); 52 | 53 | struct cpu_map { 54 | struct tstruct_header header; 55 | uint8_t cpu_socket; 56 | uint8_t cpu_type; 57 | uint8_t cpu_family; 58 | uint8_t cpu_manufacturer; 59 | uint32_t cpu_id; 60 | uint8_t cpu_version; 61 | uint8_t cpu_voltage; 62 | uint16_t ext_clock; 63 | uint16_t max_speed; 64 | uint16_t cur_speed; 65 | uint8_t cpu_status; 66 | uint8_t cpu_upgrade; 67 | uint16_t l1_handle; 68 | uint16_t l2_handle; 69 | uint16_t l3_handle; 70 | uint8_t cpu_serial; 71 | uint8_t cpu_asset_tag; 72 | uint8_t cpu_part_number; 73 | uint8_t core_count; 74 | uint8_t core_enabled; 75 | uint8_t thread_count; 76 | uint16_t cpu_specs; 77 | uint16_t cpu_family_2; 78 | } __attribute__((packed)); 79 | 80 | struct mem_dev { 81 | struct tstruct_header header; 82 | uint16_t pma_handle; 83 | uint16_t err_handle; 84 | uint16_t tot_width; 85 | uint16_t dat_width; 86 | uint16_t size; 87 | uint8_t form; 88 | uint8_t set; 89 | uint8_t dev_locator; 90 | uint8_t bank_locator; 91 | uint8_t type; 92 | uint16_t typedetail; 93 | uint16_t speed; 94 | uint8_t manufacturer; 95 | uint8_t serialnum; 96 | uint8_t asset; 97 | uint8_t partnum; 98 | } __attribute__((packed)); 99 | 100 | struct md_map{ 101 | struct tstruct_header header; 102 | uint32_t start; 103 | uint32_t end; 104 | uint16_t md_handle; 105 | uint16_t mama_handle; 106 | uint8_t row_pos; 107 | uint8_t interl_pos; 108 | uint8_t interl_depth; 109 | } __attribute__((packed)); 110 | 111 | struct pma{ 112 | struct tstruct_header header; 113 | uint8_t location; 114 | uint8_t use; 115 | uint8_t ecc; 116 | uint32_t capacity; 117 | uint16_t errhandle; 118 | uint16_t numdevs; 119 | } __attribute__((packed)); 120 | 121 | static char *form_factors[] = { 122 | "?", 123 | "Other", "Unknown", "SIMM", "SIP", "Chip", "DIP", "ZIP", 124 | "Proprietary Card", "DIMM", "TSOP", "Row of chips", "RIMM", 125 | "SODIMM", "SRIMM", "FB-DIMM" 126 | }; 127 | 128 | 129 | static char *memory_types[] = { 130 | "?", 131 | "Other", "????", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM", 132 | "ROM", "FLASH", "EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", 133 | "SDRAM", "SGRAM", "RDRAM", "DDR", "DDR2", "DDR2 FB", "RSVD", 134 | "RSVD","RSVD","DDR3","FBD2" 135 | }; 136 | 137 | 138 | struct mem_dev * mem_devs[MAX_DMI_MEMDEVS]; 139 | int mem_devs_count=0; 140 | struct md_map * md_maps[MAX_DMI_MEMDEVS]; 141 | struct system_map * dmi_system_info; 142 | struct cpu_map * dmi_cpu_info; 143 | int md_maps_count=0; 144 | int dmi_err_cnts[MAX_DMI_MEMDEVS]; 145 | short dmi_initialized=0; 146 | 147 | char * get_tstruct_string(struct tstruct_header *header, int n){ 148 | if(n<1) 149 | return 0; 150 | char * a = (char *)header + header->length; 151 | n--; 152 | do{ 153 | if (!*a) 154 | n--; 155 | if (!n && *a) 156 | return a; 157 | a++; 158 | }while (!(*a==0 && *(a-1)==0)); 159 | return 0; 160 | } 161 | 162 | 163 | int open_dmi(void){ 164 | char *dmi, *dmi_search_start, *dmi_start; 165 | int found=0; 166 | struct dmi_eps *eps; 167 | char *table_start; 168 | int tstruct_count=0; 169 | dmi_search_start = (char *)DMI_SEARCH_START; 170 | 171 | //find anchor 172 | for(dmi = dmi_search_start; dmi < dmi_search_start + 0xf0000; dmi +=16){ 173 | if( *dmi == '_' && 174 | *(dmi+1) == 'S' && 175 | *(dmi+2) == 'M' && 176 | *(dmi+3) == '_'){ 177 | found =1; 178 | break; 179 | } 180 | } 181 | if (!found) { 182 | return -1; 183 | } 184 | dmi_start=dmi; 185 | eps=(struct dmi_eps *)dmi; 186 | 187 | //check checksum 188 | int8_t checksum=0; 189 | for (; dmi < dmi_start + eps->length; dmi++) 190 | checksum += *dmi; 191 | if (checksum){ 192 | return -1; 193 | } 194 | 195 | //we need at least revision 2.1 of SMBIOS 196 | if ( eps->majorversion < 2 && 197 | eps->minorversion < 1){ 198 | return -1; 199 | } 200 | 201 | 202 | table_start=(char *)eps->tableaddress; 203 | dmi=table_start; 204 | //look at all structs 205 | while(dmi < table_start + eps->tablelength){ 206 | struct tstruct_header *header = (struct tstruct_header *)dmi; 207 | 208 | if (header->type == 17) 209 | mem_devs[mem_devs_count++] = (struct mem_dev *)dmi; 210 | 211 | // Need fix (SMBIOS/DDR3) 212 | if (header->type == 20 || header->type == 1) 213 | md_maps[md_maps_count++] = (struct md_map *)dmi; 214 | 215 | // MB_SPEC 216 | if (header->type == 2) 217 | { 218 | dmi_system_info = (struct system_map *)dmi; 219 | } 220 | 221 | // CPU_SPEC 222 | if (header->type == 4) 223 | { 224 | dmi_cpu_info = (struct cpu_map *)dmi; 225 | } 226 | 227 | dmi+=header->length; 228 | 229 | while( ! (*dmi == 0 && *(dmi+1) == 0 ) ) 230 | dmi++; 231 | dmi+=2; 232 | 233 | if (++tstruct_count > eps->numstructs) 234 | return -1; 235 | } 236 | return 0; 237 | } 238 | 239 | void init_dmi(void){ 240 | int i; 241 | for(i=0; i < MAX_DMI_MEMDEVS; i++) 242 | dmi_err_cnts[i]=0; 243 | open_dmi(); 244 | dmi_initialized=1; 245 | } 246 | 247 | void print_dmi_startup_info(void) 248 | { 249 | char *string1; 250 | char *string2; 251 | char *string3; 252 | int dmicol = 78; 253 | int slenght; 254 | int sl1, sl2, sl3; 255 | 256 | if(!dmi_initialized) { init_dmi(); } 257 | 258 | string1 = get_tstruct_string(&dmi_system_info->header,dmi_system_info->manufacturer); 259 | sl1 = strlen(string1); 260 | string2 = get_tstruct_string(&dmi_system_info->header,dmi_system_info->productname); 261 | sl2 = strlen(string2); 262 | string3 = get_tstruct_string(&dmi_cpu_info->header,dmi_cpu_info->cpu_socket); 263 | sl3 = strlen(string3); 264 | 265 | slenght = sl1 + sl2; 266 | if(sl3 > 2) { slenght += sl3 + 4; } else { slenght++; } 267 | 268 | if(sl1 && sl2) 269 | { 270 | //dmicol -= slenght; // right align 271 | dmicol = 39 - slenght/2; // center align 272 | cprint(LINE_DMI, dmicol, string1); 273 | dmicol += sl1 + 1; 274 | cprint(LINE_DMI, dmicol, string2); 275 | dmicol += sl2 + 1; 276 | 277 | if(sl3 > 2){ 278 | cprint(LINE_DMI, dmicol, "("); 279 | dmicol++; 280 | cprint(LINE_DMI, dmicol, string3); 281 | dmicol += sl3; 282 | cprint(LINE_DMI, dmicol, ")"); 283 | } 284 | } 285 | } 286 | 287 | void print_dmi_info(void){ 288 | int i,j,page; 289 | char * string=0; 290 | 291 | if(!dmi_initialized) 292 | init_dmi(); 293 | 294 | if (mem_devs_count == 0){ 295 | cprint(POP2_Y+1, POP2_X+2, "No valid DMI Memory Devices info found"); 296 | while (get_key() == 0); 297 | return; 298 | } 299 | 300 | for(page=1; page <= 1 + (mem_devs_count-1)/8; page++){ 301 | pop2clear(); 302 | cprint(POP2_Y+1, POP2_X+2, "DMI Memory Device Info (page "); 303 | itoa(string,page); 304 | cprint(POP2_Y+1, POP2_X+32, string); 305 | cprint(POP2_Y+1, POP2_X+33, "/"); 306 | itoa(string,1 + (mem_devs_count-1)/8); 307 | cprint(POP2_Y+1, POP2_X+34, string); 308 | cprint(POP2_Y+1, POP2_X+35, ")"); 309 | 310 | cprint(POP2_Y+3, POP2_X+4, "Location Size(MB) Speed(MHz) Type Form"); 311 | cprint(POP2_Y+4, POP2_X+4, "--------------------------------------------------------------"); 312 | 313 | for(i=8*(page-1); iheader), mem_devs[i]->dev_locator)); 319 | 320 | if (mem_devs[i]->size == 0){ 321 | cprint(yof, POP2_X+4+18, "Empty"); 322 | }else if (mem_devs[i]->size == 0xFFFF){ 323 | cprint(yof, POP2_X+4+18, "Unknown"); 324 | }else{ 325 | size_in_mb = 0xEFFF & mem_devs[i]->size; 326 | if (mem_devs[i]->size & 0x8000) 327 | size_in_mb = size_in_mb<<10; 328 | itoa(string, size_in_mb); 329 | cprint(yof, POP2_X+4+18, string); 330 | } 331 | 332 | //this is the only field that needs to be SMBIOS 2.3+ 333 | if ( mem_devs[i]->speed && 334 | mem_devs[i]->header.length > 21){ 335 | itoa(string, mem_devs[i]->speed); 336 | cprint(yof, POP2_X+4+27, string); 337 | }else{ 338 | cprint(yof, POP2_X+4+27, "Unknown"); 339 | } 340 | cprint(yof, POP2_X+4+37, memory_types[mem_devs[i]->type]); 341 | cprint(yof, POP2_X+4+44, form_factors[mem_devs[i]->form]); 342 | 343 | //print mappings 344 | int mapped=0,of=0; 345 | cprint(yof+1, POP2_X+6,"mapped to: "); 346 | for(j=0; jheader.handle != md_maps[j]->md_handle) 349 | continue; 350 | if (mapped++){ 351 | cprint(yof+1, POP2_X+17+of, ","); 352 | of++; 353 | } 354 | hprint3(yof+1, POP2_X+17+of, md_maps[j]->start>>22, 4); 355 | of += 4; 356 | hprint3(yof+1, POP2_X+17+of, md_maps[j]->start<<10, 8); 357 | of += 8; 358 | cprint(yof+1, POP2_X+17+of, "-"); 359 | of++; 360 | hprint3(yof+1, POP2_X+17+of, md_maps[j]->end>>22, 4); 361 | of += 4; 362 | hprint3(yof+1, POP2_X+17+of, ((md_maps[j]->end+1)<<10) - 1, 8); 363 | of += 8; 364 | if(md_maps[j]->end == 0) { hprint3(yof+1, POP2_X+17+of-8,0,8); } 365 | } 366 | if (!mapped) 367 | { 368 | cprint(yof+1, POP2_X+17, "No mapping (Interleaved Device)"); 369 | } 370 | 371 | } 372 | 373 | wait_keyup(); 374 | while (get_key() == 0); 375 | } 376 | } 377 | 378 | //return 1 if the list of bad memory devices changes, 0 otherwise, -1 if no mapped 379 | int add_dmi_err(ulong adr){ 380 | int i,j,found=-1; 381 | 382 | if(!dmi_initialized) 383 | init_dmi(); 384 | 385 | for(i=0; i < md_maps_count; i++){ 386 | if ( adr < (md_maps[i]->start<<10) || 387 | adr > (md_maps[i]->end<<10) ) 388 | continue; 389 | 390 | //matching map found, now check find corresponding dev 391 | for(j=0; j < mem_devs_count; j++){ 392 | if (mem_devs[j]->header.handle != md_maps[i]->md_handle) 393 | continue; 394 | if (dmi_err_cnts[j]){ 395 | found=0; 396 | }else{ 397 | found = dmi_err_cnts[j] = 1; 398 | } 399 | } 400 | } 401 | 402 | return found; 403 | } 404 | 405 | void print_dmi_err(void){ 406 | int i,count,of; 407 | char *string; 408 | 409 | scroll(); 410 | 411 | cprint(v->msg_line, 0,"Bad Memory Devices: "); 412 | of=20; 413 | for ( i=count=0; i < MAX_DMI_MEMDEVS; i++){ 414 | if (!dmi_err_cnts[i]) 415 | continue; 416 | struct mem_dev *md = mem_devs[i]; 417 | if(count++){ 418 | cprint(v->msg_line, of, ", "); 419 | of+=2; 420 | } 421 | string=get_tstruct_string((struct tstruct_header *)md,md->dev_locator); 422 | if (strlen(string) + of > 80){ 423 | scroll(); 424 | of=7; 425 | } 426 | cprint(v->msg_line, of, string); 427 | of += strlen(string); 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /dmi.h: -------------------------------------------------------------------------------- 1 | #ifndef __DMI_H__ 2 | #define __DMI_H__ 3 | int add_dmi_err(ulong adr); 4 | void print_dmi_err(void); 5 | void print_dmi_info(void); 6 | void print_dmi_startup_info(void); 7 | #endif 8 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | /* error.c - MemTest-86 Version 4.1 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Chris Brady 5 | */ 6 | #include "stddef.h" 7 | #include "stdint.h" 8 | #include "test.h" 9 | #include "config.h" 10 | #include "cpuid.h" 11 | #include "smp.h" 12 | #include "dmi.h" 13 | #include "controller.h" 14 | 15 | extern int dmi_err_cnts[MAX_DMI_MEMDEVS]; 16 | extern int beepmode; 17 | extern short dmi_initialized; 18 | extern struct cpu_ident cpu_id; 19 | extern struct barrier_s *barr; 20 | extern int test_ticks, nticks; 21 | extern struct tseq tseq[]; 22 | extern volatile int test; 23 | void poll_errors(); 24 | extern int num_cpus; 25 | 26 | static void update_err_counts(void); 27 | static void print_err_counts(void); 28 | static void common_err(); 29 | static int syn, chan, len=1; 30 | 31 | /* 32 | * Display data error message. Don't display duplicate errors. 33 | */ 34 | void error(ulong *adr, ulong good, ulong bad) 35 | { 36 | 37 | ulong xor; 38 | 39 | spin_lock(&barr->mutex); 40 | 41 | xor = good ^ bad; 42 | 43 | #ifdef USB_WAR 44 | /* Skip any errrors that appear to be due to the BIOS using location 45 | * 0x4e0 for USB keyboard support. This often happens with Intel 46 | * 810, 815 and 820 chipsets. It is possible that we will skip 47 | * a real error but the odds are very low. 48 | */ 49 | if ((ulong)adr == 0x4e0 || (ulong)adr == 0x410) { 50 | return; 51 | } 52 | #endif 53 | 54 | /* A sporadic bug exists in test #6, with SMP enabled, that 55 | * reports false positives on < 65K-0.5MB range. I was 56 | * not able to solve this. After investigations, it seems 57 | * related to a BIOS issue similiar to the one solved by 58 | * USB_WAR, but for MP Table. 59 | */ 60 | /* Solved 61 | if (test == 6 && (ulong)adr <= 0x07FFFF && num_cpus > 1) 62 | { 63 | cprint(6,78,"-"); // Debug 64 | return; 65 | } 66 | */ 67 | 68 | common_err(adr, good, bad, xor, 0); 69 | spin_unlock(&barr->mutex); 70 | } 71 | 72 | 73 | 74 | /* 75 | * Display address error message. 76 | * Since this is strictly an address test, trying to create BadRAM 77 | * patterns does not make sense. Just report the error. 78 | */ 79 | void ad_err1(ulong *adr1, ulong *mask, ulong bad, ulong good) 80 | { 81 | spin_lock(&barr->mutex); 82 | common_err(adr1, good, bad, (ulong)mask, 1); 83 | spin_unlock(&barr->mutex); 84 | } 85 | 86 | /* 87 | * Display address error message. 88 | * Since this type of address error can also report data errors go 89 | * ahead and generate BadRAM patterns. 90 | */ 91 | void ad_err2(ulong *adr, ulong bad) 92 | { 93 | spin_lock(&barr->mutex); 94 | common_err(adr, (ulong)adr, bad, ((ulong)adr) ^ bad, 0); 95 | spin_unlock(&barr->mutex); 96 | } 97 | 98 | static void update_err_counts(void) 99 | { 100 | if (beepmode){ 101 | beep(600); 102 | beep(1000); 103 | } 104 | 105 | if (v->pass && v->ecount == 0) { 106 | cprint(LINE_MSG, COL_MSG, 107 | " "); 108 | } 109 | ++(v->ecount); 110 | tseq[test].errors++; 111 | 112 | } 113 | 114 | static void print_err_counts(void) 115 | { 116 | int i; 117 | char *pp; 118 | 119 | if ((v->ecount > 4096) && (v->ecount % 256 != 0)) return; 120 | 121 | dprint(LINE_INFO, 72, v->ecount, 6, 0); 122 | /* 123 | dprint(LINE_INFO, 56, v->ecc_ecount, 6, 0); 124 | */ 125 | 126 | /* Paint the error messages on the screen red to provide a vivid */ 127 | /* indicator that an error has occured */ 128 | if ((v->printmode == PRINTMODE_ADDRESSES || 129 | v->printmode == PRINTMODE_PATTERNS) && 130 | v->msg_line < 24) { 131 | for(i=0, pp=(char *)((SCREEN_ADR+v->msg_line*160+1)); 132 | i<76; i++, pp+=2) { 133 | *pp = 0x47; 134 | } 135 | } 136 | } 137 | 138 | /* 139 | * Print an individual error 140 | */ 141 | void common_err( ulong *adr, ulong good, ulong bad, ulong xor, int type) 142 | { 143 | int i, j, n, x, flag=0; 144 | ulong page, offset; 145 | int patnchg; 146 | ulong mb; 147 | 148 | update_err_counts(); 149 | 150 | switch(v->printmode) { 151 | case PRINTMODE_SUMMARY: 152 | /* Don't do anything for a parity error. */ 153 | if (type == 3) { 154 | return; 155 | } 156 | 157 | /* Address error */ 158 | if (type == 1) { 159 | xor = good ^ bad; 160 | } 161 | 162 | /* Ecc correctable errors */ 163 | if (type == 2) { 164 | /* the bad value is the corrected flag */ 165 | if (bad) { 166 | v->erri.cor_err++; 167 | } 168 | page = (ulong)adr; 169 | offset = good; 170 | } else { 171 | page = page_of(adr); 172 | offset = (ulong)adr & 0xFFF; 173 | } 174 | 175 | /* Calc upper and lower error addresses */ 176 | if (v->erri.low_addr.page > page) { 177 | v->erri.low_addr.page = page; 178 | v->erri.low_addr.offset = offset; 179 | flag++; 180 | } else if (v->erri.low_addr.page == page && 181 | v->erri.low_addr.offset > offset) { 182 | v->erri.low_addr.offset = offset; 183 | v->erri.high_addr.offset = offset; 184 | flag++; 185 | } else if (v->erri.high_addr.page < page) { 186 | v->erri.high_addr.page = page; 187 | flag++; 188 | } 189 | if (v->erri.high_addr.page == page && 190 | v->erri.high_addr.offset < offset) { 191 | v->erri.high_addr.offset = offset; 192 | flag++; 193 | } 194 | 195 | /* Calc bits in error */ 196 | for (i=0, n=0; i<32; i++) { 197 | if (xor>>i & 1) { 198 | n++; 199 | } 200 | } 201 | v->erri.tbits += n; 202 | if (n > v->erri.max_bits) { 203 | v->erri.max_bits = n; 204 | flag++; 205 | } 206 | if (n < v->erri.min_bits) { 207 | v->erri.min_bits = n; 208 | flag++; 209 | } 210 | if (v->erri.ebits ^ xor) { 211 | flag++; 212 | } 213 | v->erri.ebits |= xor; 214 | 215 | /* Calc max contig errors */ 216 | len = 1; 217 | if ((ulong)adr == (ulong)v->erri.eadr+4 || 218 | (ulong)adr == (ulong)v->erri.eadr-4 ) { 219 | len++; 220 | } 221 | if (len > v->erri.maxl) { 222 | v->erri.maxl = len; 223 | flag++; 224 | } 225 | v->erri.eadr = (ulong)adr; 226 | 227 | if (v->erri.hdr_flag == 0) { 228 | clear_scroll(); 229 | cprint(LINE_HEADER+0, 1, "Error Confidence Value:"); 230 | cprint(LINE_HEADER+1, 1, " Lowest Error Address:"); 231 | cprint(LINE_HEADER+2, 1, " Highest Error Address:"); 232 | cprint(LINE_HEADER+3, 1, " Bits in Error Mask:"); 233 | cprint(LINE_HEADER+4, 1, " Bits in Error - Total:"); 234 | cprint(LINE_HEADER+4, 29, "Min: Max: Avg:"); 235 | cprint(LINE_HEADER+5, 1, " Max Contiguous Errors:"); 236 | x = 24; 237 | if (dmi_initialized) { 238 | for ( i=0; i < MAX_DMI_MEMDEVS;){ 239 | n = LINE_HEADER+7; 240 | for (j=0; j<4; j++) { 241 | if (dmi_err_cnts[i] >= 0) { 242 | dprint(n, x, i, 2, 0); 243 | cprint(n, x+2, ": 0"); 244 | } 245 | i++; 246 | n++; 247 | } 248 | x += 10; 249 | } 250 | } 251 | 252 | cprint(LINE_HEADER+0, 64, "Test Errors"); 253 | v->erri.hdr_flag++; 254 | } 255 | if (flag) { 256 | /* Calc bits in error */ 257 | for (i=0, n=0; i<32; i++) { 258 | if (v->erri.ebits>>i & 1) { 259 | n++; 260 | } 261 | } 262 | page = v->erri.low_addr.page; 263 | offset = v->erri.low_addr.offset; 264 | mb = page >> 8; 265 | hprint(LINE_HEADER+1, 25, page); 266 | hprint2(LINE_HEADER+1, 33, offset, 3); 267 | cprint(LINE_HEADER+1, 36, " - . MB"); 268 | dprint(LINE_HEADER+1, 39, mb, 5, 0); 269 | dprint(LINE_HEADER+1, 45, ((page & 0xF)*10)/16, 1, 0); 270 | page = v->erri.high_addr.page; 271 | offset = v->erri.high_addr.offset; 272 | mb = page >> 8; 273 | hprint(LINE_HEADER+2, 25, page); 274 | hprint2(LINE_HEADER+2, 33, offset, 3); 275 | cprint(LINE_HEADER+2, 36, " - . MB"); 276 | dprint(LINE_HEADER+2, 39, mb, 5, 0); 277 | dprint(LINE_HEADER+2, 45, ((page & 0xF)*10)/16, 1, 0); 278 | hprint(LINE_HEADER+3, 25, v->erri.ebits); 279 | dprint(LINE_HEADER+4, 25, n, 2, 1); 280 | dprint(LINE_HEADER+4, 34, v->erri.min_bits, 2, 1); 281 | dprint(LINE_HEADER+4, 42, v->erri.max_bits, 2, 1); 282 | dprint(LINE_HEADER+4, 50, v->erri.tbits/v->ecount, 2, 1); 283 | dprint(LINE_HEADER+5, 25, v->erri.maxl, 7, 1); 284 | x = 28; 285 | for ( i=0; i < MAX_DMI_MEMDEVS;){ 286 | n = LINE_HEADER+7; 287 | for (j=0; j<4; j++) { 288 | if (dmi_err_cnts[i] > 0) { 289 | dprint (n, x, dmi_err_cnts[i], 7, 1); 290 | } 291 | i++; 292 | n++; 293 | } 294 | x += 10; 295 | } 296 | 297 | for (i=0; tseq[i].msg != NULL; i++) { 298 | dprint(LINE_HEADER+1+i, 66, i, 2, 0); 299 | dprint(LINE_HEADER+1+i, 68, tseq[i].errors, 8, 0); 300 | } 301 | } 302 | if (v->erri.cor_err) { 303 | dprint(LINE_HEADER+6, 25, v->erri.cor_err, 8, 1); 304 | } 305 | break; 306 | 307 | case PRINTMODE_ADDRESSES: 308 | /* Don't display duplicate errors */ 309 | if ((ulong)adr == (ulong)v->erri.eadr && 310 | xor == v->erri.exor) { 311 | return; 312 | } 313 | if (v->erri.hdr_flag == 0) { 314 | clear_scroll(); 315 | cprint(LINE_HEADER, 0, 316 | "Tst Pass Failing Address Good Bad Err-Bits Count CPU"); 317 | cprint(LINE_HEADER+1, 0, 318 | "--- ---- ----------------------- -------- -------- -------- ----- ----"); 319 | v->erri.hdr_flag++; 320 | } 321 | /* Check for keyboard input */ 322 | check_input(); 323 | scroll(); 324 | 325 | if ( type == 2 || type == 3) { 326 | page = (ulong)adr; 327 | offset = good; 328 | } else { 329 | page = page_of(adr); 330 | offset = ((unsigned long)adr) & 0xFFF; 331 | } 332 | mb = page >> 8; 333 | dprint(v->msg_line, 0, test+1, 3, 0); 334 | dprint(v->msg_line, 4, v->pass, 5, 0); 335 | hprint(v->msg_line, 11, page); 336 | hprint2(v->msg_line, 19, offset, 3); 337 | cprint(v->msg_line, 22, " - . MB"); 338 | dprint(v->msg_line, 25, mb, 5, 0); 339 | dprint(v->msg_line, 31, ((page & 0xF)*10)/16, 1, 0); 340 | 341 | if (type == 3) { 342 | /* ECC error */ 343 | cprint(v->msg_line, 36, 344 | bad?"corrected ": "uncorrected "); 345 | hprint2(v->msg_line, 60, syn, 4); 346 | cprint(v->msg_line, 68, "ECC"); 347 | dprint(v->msg_line, 74, chan, 2, 0); 348 | } else if (type == 2) { 349 | cprint(v->msg_line, 36, "Parity error detected "); 350 | } else { 351 | hprint(v->msg_line, 36, good); 352 | hprint(v->msg_line, 46, bad); 353 | hprint(v->msg_line, 56, xor); 354 | dprint(v->msg_line, 66, v->ecount, 5, 0); 355 | dprint(v->msg_line, 74, smp_my_cpu_num(), 2,1); 356 | v->erri.exor = xor; 357 | } 358 | v->erri.eadr = (ulong)adr; 359 | print_err_counts(); 360 | break; 361 | 362 | case PRINTMODE_PATTERNS: 363 | if (v->erri.hdr_flag == 0) { 364 | clear_scroll(); 365 | v->erri.hdr_flag++; 366 | } 367 | /* Do not do badram patterns from test 0 or 5 */ 368 | if (test == 0 || test == 5) { 369 | return; 370 | } 371 | /* Only do patterns for data errors */ 372 | if ( type != 0) { 373 | return; 374 | } 375 | /* Process the address in the pattern administration */ 376 | patnchg=insertaddress ((ulong) adr); 377 | if (patnchg) { 378 | printpatn(); 379 | } 380 | break; 381 | 382 | case PRINTMODE_NONE: 383 | if (v->erri.hdr_flag == 0) { 384 | clear_scroll(); 385 | v->erri.hdr_flag++; 386 | } 387 | break; 388 | } 389 | } 390 | 391 | /* 392 | * Print an ecc error 393 | */ 394 | void print_ecc_err(unsigned long page, unsigned long offset, 395 | int corrected, unsigned short syndrome, int channel) 396 | { 397 | ++(v->ecc_ecount); 398 | syn = syndrome; 399 | chan = channel; 400 | common_err((ulong *)page, offset, corrected, 0, 2); 401 | } 402 | 403 | #ifdef PARITY_MEM 404 | /* 405 | * Print a parity error message 406 | */ 407 | void parity_err( unsigned long edi, unsigned long esi) 408 | { 409 | unsigned long addr; 410 | 411 | if (test == 5) { 412 | addr = esi; 413 | } else { 414 | addr = edi; 415 | } 416 | common_err((ulong *)addr, addr & 0xFFF, 0, 0, 3); 417 | } 418 | #endif 419 | 420 | /* 421 | * Print the pattern array as a LILO boot option addressing BadRAM support. 422 | */ 423 | void printpatn (void) 424 | { 425 | int idx=0; 426 | int x; 427 | 428 | /* Check for keyboard input */ 429 | check_input(); 430 | 431 | if (v->numpatn == 0) 432 | return; 433 | 434 | scroll(); 435 | 436 | cprint (v->msg_line, 0, "badram="); 437 | x=7; 438 | 439 | for (idx = 0; idx < v->numpatn; idx++) { 440 | 441 | if (x > 80-22) { 442 | scroll(); 443 | x=7; 444 | } 445 | cprint (v->msg_line, x, "0x"); 446 | hprint (v->msg_line, x+2, v->patn[idx].adr ); 447 | cprint (v->msg_line, x+10, ",0x"); 448 | hprint (v->msg_line, x+13, v->patn[idx].mask); 449 | if (idx+1 < v->numpatn) 450 | cprint (v->msg_line, x+21, ","); 451 | x+=22; 452 | } 453 | } 454 | 455 | /* 456 | * Show progress by displaying elapsed time and update bar graphs 457 | */ 458 | short spin_idx[MAX_CPUS]; 459 | char spin[4] = {'|','/','-','\\'}; 460 | 461 | void do_tick(int me) 462 | { 463 | int i, j, pct; 464 | ulong h, l, n, t; 465 | extern int mstr_cpu; 466 | 467 | if (++spin_idx[me] > 3) { 468 | spin_idx[me] = 0; 469 | } 470 | cplace(8, me+7, spin[spin_idx[me]]); 471 | 472 | 473 | /* Check for keyboard input */ 474 | if (me == mstr_cpu) { 475 | check_input(); 476 | } 477 | /* A barrier here holds the other CPUs until the configuration 478 | * changes are done */ 479 | s_barrier(); 480 | 481 | /* Only the first selected CPU does the update */ 482 | if (me != mstr_cpu) { 483 | return; 484 | } 485 | 486 | /* FIXME only print serial error messages from the tick handler */ 487 | if (v->ecount) { 488 | print_err_counts(); 489 | } 490 | 491 | nticks++; 492 | v->total_ticks++; 493 | 494 | if (test_ticks) { 495 | pct = 100*nticks/test_ticks; 496 | if (pct > 100) { 497 | pct = 100; 498 | } 499 | } else { 500 | pct = 0; 501 | } 502 | dprint(2, COL_MID+4, pct, 3, 0); 503 | i = (BAR_SIZE * pct) / 100; 504 | while (i > v->tptr) { 505 | if (v->tptr >= BAR_SIZE) { 506 | break; 507 | } 508 | cprint(2, COL_MID+9+v->tptr, "#"); 509 | v->tptr++; 510 | } 511 | 512 | if (v->pass_ticks) { 513 | pct = 100*v->total_ticks/v->pass_ticks; 514 | if (pct > 100) { 515 | pct = 100; 516 | } 517 | } else { 518 | pct = 0; 519 | } 520 | dprint(1, COL_MID+4, pct, 3, 0); 521 | i = (BAR_SIZE * pct) / 100; 522 | while (i > v->pptr) { 523 | if (v->pptr >= BAR_SIZE) { 524 | break; 525 | } 526 | cprint(1, COL_MID+9+v->pptr, "#"); 527 | v->pptr++; 528 | } 529 | 530 | if (v->ecount && v->printmode == PRINTMODE_SUMMARY) { 531 | /* Compute confidence score */ 532 | pct = 0; 533 | 534 | /* If there are no errors within 1mb of start - end addresses */ 535 | h = v->pmap[v->msegs - 1].end - 0x100; 536 | if (v->erri.low_addr.page > 0x100 && 537 | v->erri.high_addr.page < h) { 538 | pct += 8; 539 | } 540 | 541 | /* Errors for only some tests */ 542 | if (v->pass) { 543 | for (i=0, n=0; tseq[i].msg != NULL; i++) { 544 | if (tseq[i].errors == 0) { 545 | n++; 546 | } 547 | } 548 | pct += n*3; 549 | } else { 550 | for (i=0, n=0; ierri.ebits & 0xf) n++; 562 | if (v->erri.ebits & 0xf0) n++; 563 | if (v->erri.ebits & 0xf00) n++; 564 | if (v->erri.ebits & 0xf000) n++; 565 | if (v->erri.ebits & 0xf0000) n++; 566 | if (v->erri.ebits & 0xf00000) n++; 567 | if (v->erri.ebits & 0xf000000) n++; 568 | if (v->erri.ebits & 0xf0000000) n++; 569 | pct += (8-n)*2; 570 | 571 | /* Adjust the score */ 572 | pct = pct*100/22; 573 | /* 574 | if (pct > 100) { 575 | pct = 100; 576 | } 577 | */ 578 | dprint(LINE_HEADER+0, 25, pct, 3, 1); 579 | } 580 | 581 | 582 | /* We can't do the elapsed time unless the rdtsc instruction 583 | * is supported 584 | */ 585 | if (cpu_id.fid.bits.rdtsc) { 586 | asm __volatile__( 587 | "rdtsc":"=a" (l),"=d" (h)); 588 | asm __volatile__ ( 589 | "subl %2,%0\n\t" 590 | "sbbl %3,%1" 591 | :"=a" (l), "=d" (h) 592 | :"g" (v->startl), "g" (v->starth), 593 | "0" (l), "1" (h)); 594 | t = h * ((unsigned)0xffffffff / v->clks_msec) / 1000; 595 | t += (l / v->clks_msec) / 1000; 596 | i = t % 60; 597 | j = i % 10; 598 | 599 | if(j != v->each_sec) 600 | { 601 | 602 | dprint(LINE_TIME, COL_TIME+9, i % 10, 1, 0); 603 | dprint(LINE_TIME, COL_TIME+8, i / 10, 1, 0); 604 | t /= 60; 605 | i = t % 60; 606 | dprint(LINE_TIME, COL_TIME+6, i % 10, 1, 0); 607 | dprint(LINE_TIME, COL_TIME+5, i / 10, 1, 0); 608 | t /= 60; 609 | dprint(LINE_TIME, COL_TIME, t, 4, 0); 610 | 611 | if(v->check_temp > 0 && !(v->fail_safe & 4)) 612 | { 613 | coretemp(); 614 | } 615 | v->each_sec = j; 616 | } 617 | 618 | } 619 | 620 | 621 | 622 | /* Poll for ECC errors */ 623 | /* 624 | poll_errors(); 625 | */ 626 | } 627 | 628 | -------------------------------------------------------------------------------- /extra.h: -------------------------------------------------------------------------------- 1 | // This is the extra stuff added to the memtest+ from memtest.org 2 | // Code from Eric Nelson and Wee 3 | /* extra.c 4 | * 5 | * Released under version 2 of the Gnu Public License. 6 | * 7 | */ 8 | 9 | #ifndef MEMTEST_EXTRA_H 10 | #define MEMTEST_EXTRA_H 11 | 12 | void change_timing(int cas, int rcd, int rp, int ras); 13 | void find_memctr(void); 14 | void disclaimer(void); 15 | void get_option(void); 16 | void get_menu(void); 17 | void a64_parameter(void); 18 | int get_cas(void); 19 | void change_timing_i852(int cas, int rcd, int rp, int ras); 20 | void change_timing_i925(int cas, int rcd, int rp, int ras); 21 | void change_timing_i875(int cas, int rcd, int rp, int ras); 22 | void change_timing_nf2(int cas, int rcd, int rp, int ras); 23 | void change_timing_amd64(int cas, int rcd, int rp, int ras); 24 | void amd64_tweak(int rwt, int wrt, int ref, int en2t, int rct, int rrd, int rwqb, int wr); 25 | void __delay(ulong loops); 26 | 27 | #endif /* MEMTEST_EXTRA_H */ 28 | -------------------------------------------------------------------------------- /init.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Distrotech/memtest86/a0af2ad356379c1dace15b0dc1677f81e240dd54/init.c -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASM_IO_H 2 | #define _ASM_IO_H 3 | 4 | /* 5 | * This file contains the definitions for the x86 IO instructions 6 | * inb/inw/inl/outb/outw/outl and the "string versions" of the same 7 | * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" 8 | * versions of the single-IO instructions (inb_p/inw_p/..). 9 | * 10 | * This file is not meant to be obfuscating: it's just complicated 11 | * to (a) handle it all in a way that makes gcc able to optimize it 12 | * as well as possible and (b) trying to avoid writing the same thing 13 | * over and over again with slight variations and possibly making a 14 | * mistake somewhere. 15 | */ 16 | 17 | #ifdef SLOW_IO_BY_JUMPING 18 | #define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") 19 | #else 20 | #define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") 21 | #endif 22 | 23 | #ifdef REALLY_SLOW_IO 24 | #define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } 25 | #else 26 | #define SLOW_DOWN_IO __SLOW_DOWN_IO 27 | #endif 28 | 29 | /* 30 | * Talk about misusing macros.. 31 | */ 32 | 33 | #define __OUT1(s,x) \ 34 | extern inline void __out##s(unsigned x value, unsigned short port) { 35 | 36 | #define __OUT2(s,s1,s2) \ 37 | __asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" 38 | 39 | #define __OUT(s,s1,x) \ 40 | __OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \ 41 | __OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \ 42 | __OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \ 43 | __OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; } 44 | 45 | #define __IN1(s) \ 46 | extern inline RETURN_TYPE __in##s(unsigned short port) { RETURN_TYPE _v; 47 | 48 | #define __IN2(s,s1,s2) \ 49 | __asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" 50 | 51 | #define __IN(s,s1,i...) \ 52 | __IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \ 53 | __IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \ 54 | __IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \ 55 | __IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; } 56 | 57 | #define __OUTS(s) \ 58 | extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \ 59 | { __asm__ __volatile__ ("cld ; rep ; outs" #s \ 60 | : "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } 61 | 62 | #define RETURN_TYPE unsigned char 63 | /* __IN(b,"b","0" (0)) */ 64 | __IN(b,"") 65 | #undef RETURN_TYPE 66 | #define RETURN_TYPE unsigned short 67 | /* __IN(w,"w","0" (0)) */ 68 | __IN(w,"") 69 | #undef RETURN_TYPE 70 | #define RETURN_TYPE unsigned int 71 | __IN(l,"") 72 | #undef RETURN_TYPE 73 | 74 | __OUT(b,"b",char) 75 | __OUT(w,"w",short) 76 | __OUT(l,,int) 77 | 78 | __OUTS(b) 79 | __OUTS(w) 80 | __OUTS(l) 81 | 82 | /* 83 | * Note that due to the way __builtin_constant_p() works, you 84 | * - can't use it inside a inline function (it will never be true) 85 | * - you don't have to worry about side effects within the __builtin.. 86 | */ 87 | #define outb(val,port) \ 88 | ((__builtin_constant_p((port)) && (port) < 256) ? \ 89 | __outbc((val),(port)) : \ 90 | __outb((val),(port))) 91 | 92 | #define inb(port) \ 93 | ((__builtin_constant_p((port)) && (port) < 256) ? \ 94 | __inbc(port) : \ 95 | __inb(port)) 96 | 97 | 98 | #define outw(val,port) \ 99 | ((__builtin_constant_p((port)) && (port) < 256) ? \ 100 | __outwc((val),(port)) : \ 101 | __outw((val),(port))) 102 | 103 | #define inw(port) \ 104 | ((__builtin_constant_p((port)) && (port) < 256) ? \ 105 | __inwc(port) : \ 106 | __inw(port)) 107 | 108 | 109 | #define outl(val,port) \ 110 | ((__builtin_constant_p((port)) && (port) < 256) ? \ 111 | __outlc((val),(port)) : \ 112 | __outl((val),(port))) 113 | 114 | #define inl(port) \ 115 | ((__builtin_constant_p((port)) && (port) < 256) ? \ 116 | __inlc(port) : \ 117 | __inl(port)) 118 | #endif 119 | -------------------------------------------------------------------------------- /jedec_id.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Distrotech/memtest86/a0af2ad356379c1dace15b0dc1677f81e240dd54/jedec_id.h -------------------------------------------------------------------------------- /linuxbios.c: -------------------------------------------------------------------------------- 1 | #include "linuxbios_tables.h" 2 | #include "test.h" 3 | 4 | static unsigned long ip_compute_csum(void *addr, unsigned long length) 5 | { 6 | uint16_t *ptr; 7 | unsigned long sum; 8 | unsigned long len; 9 | unsigned long laddr; 10 | /* compute an ip style checksum */ 11 | laddr = (unsigned long )addr; 12 | sum = 0; 13 | if (laddr & 1) { 14 | uint16_t buffer; 15 | unsigned char *ptr; 16 | /* copy the first byte into a 2 byte buffer. 17 | * This way automatically handles the endian question 18 | * of which byte (low or high) the last byte goes in. 19 | */ 20 | buffer = 0; 21 | ptr = addr; 22 | memmove(&buffer, ptr, 1); 23 | sum += buffer; 24 | if (sum > 0xFFFF) 25 | sum -= 0xFFFF; 26 | length -= 1; 27 | addr = ptr +1; 28 | 29 | } 30 | len = length >> 1; 31 | ptr = addr; 32 | while (len--) { 33 | sum += *(ptr++); 34 | if (sum > 0xFFFF) 35 | sum -= 0xFFFF; 36 | } 37 | addr = ptr; 38 | if (length & 1) { 39 | uint16_t buffer; 40 | unsigned char *ptr; 41 | /* copy the last byte into a 2 byte buffer. 42 | * This way automatically handles the endian question 43 | * of which byte (low or high) the last byte goes in. 44 | */ 45 | buffer = 0; 46 | ptr = addr; 47 | memmove(&buffer, ptr, 1); 48 | sum += buffer; 49 | if (sum > 0xFFFF) 50 | sum -= 0xFFFF; 51 | } 52 | return (~sum) & 0xFFFF; 53 | 54 | } 55 | 56 | #define for_each_lbrec(head, rec) \ 57 | for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \ 58 | (((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \ 59 | (rec->size >= 1) && \ 60 | ((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \ 61 | rec = (struct lb_record *)(((char *)rec) + rec->size)) 62 | 63 | 64 | static int count_lb_records(struct lb_header *head) 65 | { 66 | struct lb_record *rec; 67 | int count; 68 | count = 0; 69 | for_each_lbrec(head, rec) { 70 | count++; 71 | } 72 | return count; 73 | } 74 | 75 | static struct lb_header * __find_lb_table(unsigned long start, unsigned long end) 76 | { 77 | unsigned long addr; 78 | /* For now be stupid.... */ 79 | for(addr = start; addr < end; addr += 16) { 80 | struct lb_header *head = (struct lb_header *)addr; 81 | struct lb_record *recs = (struct lb_record *)(addr + sizeof(*head)); 82 | if (memcmp(head->signature, "LBIO", 4) != 0) 83 | continue; 84 | if (head->header_bytes != sizeof(*head)) 85 | continue; 86 | if (ip_compute_csum((unsigned char *)head, sizeof(*head)) != 0) 87 | continue; 88 | if (ip_compute_csum((unsigned char *)recs, head->table_bytes) 89 | != head->table_checksum) 90 | continue; 91 | if (count_lb_records(head) != head->table_entries) 92 | continue; 93 | return head; 94 | }; 95 | return 0; 96 | } 97 | 98 | static struct lb_header * find_lb_table(void) 99 | { 100 | struct lb_header *head; 101 | head = 0; 102 | if (!head) { 103 | /* First try at address 0 */ 104 | head = __find_lb_table(0x00000, 0x1000); 105 | } 106 | if (!head) { 107 | /* Then try at address 0xf0000 */ 108 | head = __find_lb_table(0xf0000, 0x100000); 109 | } 110 | return head; 111 | } 112 | 113 | int query_linuxbios(void) 114 | { 115 | struct lb_header *head; 116 | struct lb_record *rec; 117 | struct lb_memory *mem; 118 | struct lb_forward *forward; 119 | int i, entries; 120 | 121 | head = find_lb_table(); 122 | if (!head) { 123 | return 0; 124 | } 125 | 126 | /* coreboot also can forward the table to the high tables area. */ 127 | rec = (struct lb_record *)(((char *)head) + sizeof(*head)); 128 | if (rec->tag == LB_TAG_FORWARD) { 129 | forward = (struct lb_forward *)rec; 130 | head = (struct lb_header *)(unsigned long)(forward->forward); 131 | if (!head) { return 0; } 132 | } 133 | 134 | mem = 0; 135 | for_each_lbrec(head, rec) { 136 | if (rec->tag == LB_TAG_MEMORY) { 137 | mem = (struct lb_memory *)rec; 138 | break; 139 | } 140 | } 141 | if (!mem) { 142 | return 1; 143 | } 144 | entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); 145 | if (entries == 0) 146 | return 1; 147 | mem_info.e820_nr = 0; 148 | for(i = 0; i < entries; i++) { 149 | unsigned long long start; 150 | unsigned long long size; 151 | unsigned long type; 152 | if (i >= E820MAX) { 153 | break; 154 | } 155 | start = mem->map[i].start; 156 | size = mem->map[i].size; 157 | type = (mem->map[i].type == LB_MEM_RAM)?E820_RAM: E820_RESERVED; 158 | mem_info.e820[mem_info.e820_nr].addr = start; 159 | mem_info.e820[mem_info.e820_nr].size = size; 160 | mem_info.e820[mem_info.e820_nr].type = type; 161 | mem_info.e820_nr++; 162 | } 163 | return 1; 164 | } 165 | 166 | -------------------------------------------------------------------------------- /linuxbios_tables.h: -------------------------------------------------------------------------------- 1 | #ifndef LINUXBIOS_TABLES_H 2 | #define LINUXBIOS_TABLES_H 3 | 4 | #include "stdint.h" 5 | 6 | /* The linuxbios table information is for conveying information 7 | * from the firmware to the loaded OS image. Primarily this 8 | * is expected to be information that cannot be discovered by 9 | * other means, such as quering the hardware directly. 10 | * 11 | * All of the information should be Position Independent Data. 12 | * That is it should be safe to relocated any of the information 13 | * without it's meaning/correctnes changing. For table that 14 | * can reasonably be used on multiple architectures the data 15 | * size should be fixed. This should ease the transition between 16 | * 32 bit and 64 bit architectures etc. 17 | * 18 | * The completeness test for the information in this table is: 19 | * - Can all of the hardware be detected? 20 | * - Are the per motherboard constants available? 21 | * - Is there enough to allow a kernel to run that was written before 22 | * a particular motherboard is constructed? (Assuming the kernel 23 | * has drivers for all of the hardware but it does not have 24 | * assumptions on how the hardware is connected together). 25 | * 26 | * With this test it should be straight forward to determine if a 27 | * table entry is required or not. This should remove much of the 28 | * long term compatibility burden as table entries which are 29 | * irrelevant or have been replaced by better alternatives may be 30 | * dropped. Of course it is polite and expidite to include extra 31 | * table entries and be backwards compatible, but it is not required. 32 | */ 33 | 34 | 35 | struct lb_header 36 | { 37 | uint8_t signature[4]; /* LBIO */ 38 | uint32_t header_bytes; 39 | uint32_t header_checksum; 40 | uint32_t table_bytes; 41 | uint32_t table_checksum; 42 | uint32_t table_entries; 43 | }; 44 | 45 | /* Every entry in the boot enviroment list will correspond to a boot 46 | * info record. Encoding both type and size. The type is obviously 47 | * so you can tell what it is. The size allows you to skip that 48 | * boot enviroment record if you don't know what it easy. This allows 49 | * forward compatibility with records not yet defined. 50 | */ 51 | struct lb_record { 52 | uint32_t tag; /* tag ID */ 53 | uint32_t size; /* size of record (in bytes) */ 54 | }; 55 | 56 | #define LB_TAG_UNUSED 0x0000 57 | 58 | #define LB_TAG_MEMORY 0x0001 59 | #define LB_TAG_FORWARD 0x0011 60 | 61 | struct lb_memory_range { 62 | uint64_t start; 63 | uint64_t size; 64 | uint32_t type; 65 | #define LB_MEM_RAM 1 66 | #define LB_MEM_RESERVED 2 67 | 68 | }; 69 | 70 | struct lb_memory { 71 | uint32_t tag; 72 | uint32_t size; 73 | struct lb_memory_range map[0]; 74 | }; 75 | 76 | #define LB_TAG_HWRPB 0x0002 77 | struct lb_hwrpb { 78 | uint32_t tag; 79 | uint32_t size; 80 | uint64_t hwrpb; 81 | }; 82 | 83 | struct lb_forward { 84 | uint32_t tag; 85 | uint32_t size; 86 | uint64_t forward; 87 | }; 88 | 89 | #endif /* LINUXBIOS_TABLES_H */ 90 | -------------------------------------------------------------------------------- /major_version: -------------------------------------------------------------------------------- 1 | 4.99 2 | -------------------------------------------------------------------------------- /make_buildnum.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Distrotech/memtest86/a0af2ad356379c1dace15b0dc1677f81e240dd54/make_buildnum.sh -------------------------------------------------------------------------------- /makedos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -f memtest.bin ] 4 | then 5 | CSIZE="$(awk 'NR==16' mt86+_loader.asm | awk '{print $2}')"; 6 | NSIZE="$(ls -l memtest.bin | awk '{print $5}')"; 7 | sed "s/$CSIZE/$NSIZE/" mt86+_loader.asm > mt86+_loader.asm.new; 8 | mv mt86+_loader.asm.new mt86+_loader.asm; 9 | nasm mt86+_loader.asm; 10 | fi 11 | -------------------------------------------------------------------------------- /makeiso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # check to see if the correct tools are installed 4 | for X in wc genisoimage 5 | do 6 | if [ "$(which $X)" = "" ]; then 7 | echo "makeiso.sh error: $X is not in your path." >&2 8 | exit 1 9 | elif [ ! -x $(which $X) ]; then 10 | echo "makeiso.sh error: $X is not executable." >&2 11 | exit 1 12 | fi 13 | done 14 | 15 | #check to see if memtest.bin is present 16 | if [ ! -w memtest.bin ]; then 17 | echo "makeiso.sh error: cannot find memtest.bin, did you compile it?" >&2 18 | exit 1 19 | fi 20 | 21 | 22 | # enlarge the size of memtest.bin 23 | SIZE=$(wc -c memtest.bin | awk '{print $1}') 24 | FILL=$((1474560 - $SIZE)) 25 | dd if=/dev/zero of=fill.tmp bs=$FILL count=1 26 | cat memtest.bin fill.tmp > memtest.img 27 | rm -f fill.tmp 28 | 29 | echo "Generating iso image ..." 30 | 31 | mkdir "cd" 32 | mkdir "cd/boot" 33 | mv memtest.img cd/boot 34 | cd cd 35 | 36 | # Create the cd.README 37 | echo -e "There is nothing to do here\r\r\nMemtest86+ is located on the bootsector of this CD\r\r\n" > README.TXT 38 | echo -e "Just boot from this CD and Memtest86+ will launch" >> README.TXT 39 | 40 | genisoimage -A "MKISOFS 1.1.2" -p "Memtest86+ 5.01" -publisher "Samuel D. " -b boot/memtest.img -c boot/boot.catalog -V "MT501" -o memtest.iso . 41 | mv memtest.iso ../mt501.iso 42 | cd .. 43 | rm -rf cd 44 | 45 | echo "Done! Memtest86+ 5.01 ISO is mt501.iso" 46 | -------------------------------------------------------------------------------- /memsize.c: -------------------------------------------------------------------------------- 1 | /* memsize.c - MemTest-86 Version 3.3 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Chris Brady 5 | */ 6 | 7 | #include "test.h" 8 | #include "defs.h" 9 | #include "config.h" 10 | 11 | short e820_nr; 12 | short memsz_mode = SZ_MODE_BIOS; 13 | 14 | static ulong alt_mem_k; 15 | static ulong ext_mem_k; 16 | static struct e820entry e820[E820MAX]; 17 | 18 | ulong p1, p2; 19 | ulong *p; 20 | 21 | static void sort_pmap(void); 22 | //static void memsize_bios(void); 23 | static void memsize_820(void); 24 | static void memsize_801(void); 25 | static int sanitize_e820_map(struct e820entry *orig_map, 26 | struct e820entry *new_bios, short old_nr); 27 | static void memsize_linuxbios(); 28 | 29 | /* 30 | * Find out how much memory there is. 31 | */ 32 | void mem_size(void) 33 | { 34 | int i, flag=0; 35 | v->test_pages = 0; 36 | 37 | /* Get the memory size from the BIOS */ 38 | /* Determine the memory map */ 39 | if (query_linuxbios()) { 40 | flag = 1; 41 | } else if (query_pcbios()) { 42 | flag = 2; 43 | } 44 | 45 | /* On the first time thru only */ 46 | /* Make a copy of the memory info table so that we can re-evaluate */ 47 | /* The memory map later */ 48 | if (e820_nr == 0 && alt_mem_k == 0 && ext_mem_k == 0) { 49 | ext_mem_k = mem_info.e88_mem_k; 50 | alt_mem_k = mem_info.e801_mem_k; 51 | e820_nr = mem_info.e820_nr; 52 | for (i=0; i< mem_info.e820_nr; i++) { 53 | e820[i].addr = mem_info.e820[i].addr; 54 | e820[i].size = mem_info.e820[i].size; 55 | e820[i].type = mem_info.e820[i].type; 56 | } 57 | } 58 | if (flag == 1) { 59 | memsize_linuxbios(); 60 | } else if (flag == 2) { 61 | memsize_820(); 62 | } 63 | 64 | /* Guarantee that pmap entries are in ascending order */ 65 | sort_pmap(); 66 | v->plim_lower = 0; 67 | v->plim_upper = v->pmap[v->msegs-1].end; 68 | 69 | adj_mem(); 70 | } 71 | 72 | static void sort_pmap(void) 73 | { 74 | int i, j; 75 | /* Do an insertion sort on the pmap, on an already sorted 76 | * list this should be a O(1) algorithm. 77 | */ 78 | for(i = 0; i < v->msegs; i++) { 79 | /* Find where to insert the current element */ 80 | for(j = i -1; j >= 0; j--) { 81 | if (v->pmap[i].start > v->pmap[j].start) { 82 | j++; 83 | break; 84 | } 85 | } 86 | /* Insert the current element */ 87 | if (i != j) { 88 | struct pmap temp; 89 | temp = v->pmap[i]; 90 | memmove(&v->pmap[j], &v->pmap[j+1], 91 | (i -j)* sizeof(temp)); 92 | v->pmap[j] = temp; 93 | } 94 | } 95 | } 96 | static void memsize_linuxbios(void) 97 | { 98 | int i, n; 99 | /* Build the memory map for testing */ 100 | n = 0; 101 | for (i=0; i < e820_nr; i++) { 102 | unsigned long long end; 103 | 104 | if (e820[i].type != E820_RAM) { 105 | continue; 106 | } 107 | end = e820[i].addr; 108 | end += e820[i].size; 109 | v->pmap[n].start = (e820[i].addr + 4095) >> 12; 110 | v->pmap[n].end = end >> 12; 111 | v->test_pages += v->pmap[n].end - v->pmap[n].start; 112 | n++; 113 | } 114 | v->msegs = n; 115 | } 116 | static void memsize_820() 117 | { 118 | int i, n, nr; 119 | struct e820entry nm[E820MAX]; 120 | unsigned long long start; 121 | unsigned long long end; 122 | 123 | /* Clean up, adjust and copy the BIOS-supplied E820-map. */ 124 | nr = sanitize_e820_map(e820, nm, e820_nr); 125 | 126 | /* If there is not a good 820 map use the BIOS 801/88 info */ 127 | if (nr < 1 || nr > E820MAX) { 128 | memsize_801(); 129 | return; 130 | } 131 | 132 | /* Build the memory map for testing */ 133 | n = 0; 134 | for (i=0; i RES_START && start < RES_END) { 141 | if (end < RES_END) { 142 | continue; 143 | } 144 | start = RES_END; 145 | } 146 | if (end > RES_START && end < RES_END) { 147 | end = RES_START; 148 | } 149 | v->pmap[n].start = (start + 4095) >> 12; 150 | v->pmap[n].end = end >> 12; 151 | v->test_pages += v->pmap[n].end - v->pmap[n].start; 152 | n++; 153 | #if 0 154 | int epmap = 0; 155 | int lpmap = 0; 156 | if(n > 12) { epmap = 34; lpmap = -12; } 157 | hprint (11+n+lpmap,0+epmap,v->pmap[n-1].start); 158 | hprint (11+n+lpmap,10+epmap,v->pmap[n-1].end); 159 | hprint (11+n+lpmap,20+epmap,v->pmap[n-1].end - v->pmap[n-1].start); 160 | dprint (11+n+lpmap,30+epmap,nm[i].type,0,0); 161 | #endif 162 | } 163 | } 164 | v->msegs = n; 165 | } 166 | 167 | static void memsize_801(void) 168 | { 169 | ulong mem_size; 170 | 171 | /* compare results from 88 and 801 methods and take the greater */ 172 | /* These sizes are for extended memory in 1k units. */ 173 | 174 | if (alt_mem_k < ext_mem_k) { 175 | mem_size = ext_mem_k; 176 | } else { 177 | mem_size = alt_mem_k; 178 | } 179 | /* First we map in the first 640k */ 180 | v->pmap[0].start = 0; 181 | v->pmap[0].end = RES_START >> 12; 182 | v->test_pages = RES_START >> 12; 183 | 184 | /* Now the extended memory */ 185 | v->pmap[1].start = (RES_END + 4095) >> 12; 186 | v->pmap[1].end = (mem_size + 1024) >> 2; 187 | v->test_pages += mem_size >> 2; 188 | v->msegs = 2; 189 | } 190 | 191 | /* 192 | * Sanitize the BIOS e820 map. 193 | * 194 | * Some e820 responses include overlapping entries. The following 195 | * replaces the original e820 map with a new one, removing overlaps. 196 | * 197 | */ 198 | static int sanitize_e820_map(struct e820entry *orig_map, struct e820entry *new_bios, 199 | short old_nr) 200 | { 201 | struct change_member { 202 | struct e820entry *pbios; /* pointer to original bios entry */ 203 | unsigned long long addr; /* address for this change point */ 204 | }; 205 | struct change_member change_point_list[2*E820MAX]; 206 | struct change_member *change_point[2*E820MAX]; 207 | struct e820entry *overlap_list[E820MAX]; 208 | struct e820entry biosmap[E820MAX]; 209 | struct change_member *change_tmp; 210 | ulong current_type, last_type; 211 | unsigned long long last_addr; 212 | int chgidx, still_changing; 213 | int overlap_entries; 214 | int new_bios_entry; 215 | int i; 216 | 217 | /* 218 | Visually we're performing the following (1,2,3,4 = memory types)... 219 | Sample memory map (w/overlaps): 220 | ____22__________________ 221 | ______________________4_ 222 | ____1111________________ 223 | _44_____________________ 224 | 11111111________________ 225 | ____________________33__ 226 | ___________44___________ 227 | __________33333_________ 228 | ______________22________ 229 | ___________________2222_ 230 | _________111111111______ 231 | _____________________11_ 232 | _________________4______ 233 | 234 | Sanitized equivalent (no overlap): 235 | 1_______________________ 236 | _44_____________________ 237 | ___1____________________ 238 | ____22__________________ 239 | ______11________________ 240 | _________1______________ 241 | __________3_____________ 242 | ___________44___________ 243 | _____________33_________ 244 | _______________2________ 245 | ________________1_______ 246 | _________________4______ 247 | ___________________2____ 248 | ____________________33__ 249 | ______________________4_ 250 | */ 251 | /* First make a copy of the map */ 252 | for (i=0; iaddr = biosmap[i].addr; 272 | change_point[chgidx++]->pbios = &biosmap[i]; 273 | change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; 274 | change_point[chgidx++]->pbios = &biosmap[i]; 275 | } 276 | 277 | /* sort change-point list by memory addresses (low -> high) */ 278 | still_changing = 1; 279 | while (still_changing) { 280 | still_changing = 0; 281 | for (i=1; i < 2*old_nr; i++) { 282 | /* if > , swap */ 283 | /* or, if current= & last=, swap */ 284 | if ((change_point[i]->addr < change_point[i-1]->addr) || 285 | ((change_point[i]->addr == change_point[i-1]->addr) && 286 | (change_point[i]->addr == change_point[i]->pbios->addr) && 287 | (change_point[i-1]->addr != change_point[i-1]->pbios->addr)) 288 | ) 289 | { 290 | change_tmp = change_point[i]; 291 | change_point[i] = change_point[i-1]; 292 | change_point[i-1] = change_tmp; 293 | still_changing=1; 294 | } 295 | } 296 | } 297 | 298 | /* create a new bios memory map, removing overlaps */ 299 | overlap_entries=0; /* number of entries in the overlap table */ 300 | new_bios_entry=0; /* index for creating new bios map entries */ 301 | last_type = 0; /* start with undefined memory type */ 302 | last_addr = 0; /* start with 0 as last starting address */ 303 | /* loop through change-points, determining affect on the new bios map */ 304 | for (chgidx=0; chgidx < 2*old_nr; chgidx++) 305 | { 306 | /* keep track of all overlapping bios entries */ 307 | if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr) 308 | { 309 | /* add map entry to overlap list (> 1 entry implies an overlap) */ 310 | overlap_list[overlap_entries++]=change_point[chgidx]->pbios; 311 | } 312 | else 313 | { 314 | /* remove entry from list (order independent, so swap with last) */ 315 | for (i=0; ipbios) 318 | overlap_list[i] = overlap_list[overlap_entries-1]; 319 | } 320 | overlap_entries--; 321 | } 322 | /* if there are overlapping entries, decide which "type" to use */ 323 | /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */ 324 | current_type = 0; 325 | for (i=0; itype > current_type) 327 | current_type = overlap_list[i]->type; 328 | /* continue building up new bios map based on this information */ 329 | if (current_type != last_type) { 330 | if (last_type != 0) { 331 | new_bios[new_bios_entry].size = 332 | change_point[chgidx]->addr - last_addr; 333 | /* move forward only if the new size was non-zero */ 334 | if (new_bios[new_bios_entry].size != 0) 335 | if (++new_bios_entry >= E820MAX) 336 | break; /* no more space left for new bios entries */ 337 | } 338 | if (current_type != 0) { 339 | new_bios[new_bios_entry].addr = change_point[chgidx]->addr; 340 | new_bios[new_bios_entry].type = current_type; 341 | last_addr=change_point[chgidx]->addr; 342 | } 343 | last_type = current_type; 344 | } 345 | } 346 | return(new_bios_entry); 347 | } 348 | -------------------------------------------------------------------------------- /memtest.bin.lds: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("binary") 2 | OUTPUT_ARCH("i386") 3 | 4 | ENTRY(_main); 5 | SECTIONS { 6 | . = 0; 7 | .bootsect : { *(.bootsect) } 8 | .setup : { *(.setup) } 9 | .memtest : { 10 | _start = . ; 11 | *(.data) 12 | _end = . ; 13 | } 14 | _syssize = (_end - _start + 15) >> 4; 15 | } 16 | -------------------------------------------------------------------------------- /memtest.lds: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-i386"); 2 | OUTPUT_ARCH(i386); 3 | 4 | ENTRY(_start); 5 | SECTIONS { 6 | . = 0x10000; 7 | _start = . ; 8 | .data : { 9 | *(.data) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /memtest_shared.lds: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-i386"); 2 | OUTPUT_ARCH(i386); 3 | 4 | ENTRY(startup_32); 5 | SECTIONS { 6 | . = 0; 7 | .text : { 8 | _start = .; 9 | *(.text) 10 | *(.text.*) 11 | *(.plt) 12 | _etext = . ; 13 | } = 0x9090 14 | .rodata : { 15 | *(.rodata) 16 | *(.rodata.*) 17 | } 18 | .dynsym : { *(.dynsym) } 19 | .dynstr : { *(.dynstr) } 20 | .hash : { *(.hash) } 21 | .gnu.hash : { *(.gnu.hash) } 22 | .dynamic : { *(.dynamic) } 23 | 24 | .rel.text : { *(.rel.text .rel.text.*) } 25 | .rel.rodata : { *(.rel.rodata .rel.rodata.*) } 26 | .rel.data : { *(.rel.data .rel.data.*) } 27 | .rel.got : { *(.rel.got .rel.got.*) } 28 | .rel.plt : { *(.rel.plt .rel.plt.*) } 29 | 30 | . = ALIGN(4); 31 | .data : { 32 | _data = .; 33 | *(.data) 34 | *(.data.*) 35 | } 36 | .got : { 37 | *(.got.plt) 38 | *(.got) 39 | _edata = . ; 40 | } 41 | . = ALIGN(4); 42 | .bss : { 43 | _bss = .; 44 | *(.dynbss) 45 | *(.bss) 46 | *(.bss.*) 47 | *(COMMON) 48 | /* _end must be at least 256 byte aligned */ 49 | . = ALIGN(256); 50 | _end = .; 51 | } 52 | /DISCARD/ : { *(*) } 53 | } 54 | -------------------------------------------------------------------------------- /msr.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_MSR_H 2 | #define __ASM_MSR_H 3 | 4 | /* 5 | * Access to machine-specific registers (available on 586 and better only) 6 | * Note: the rd* operations modify the parameters directly (without using 7 | * pointer indirection), this allows gcc to optimize better 8 | */ 9 | 10 | #define __FIXUP_ALIGN ".align 8" 11 | #define __FIXUP_WORD ".quad" 12 | #define EFAULT 14 /* Bad address */ 13 | 14 | #define rdmsr(msr,val1,val2) \ 15 | __asm__ __volatile__("rdmsr" \ 16 | : "=a" (val1), "=d" (val2) \ 17 | : "c" (msr) : "edi") 18 | 19 | /* 20 | #define rdmsr_safe(msr,val1,val2) ({\ 21 | int _rc; \ 22 | __asm__ __volatile__( \ 23 | "1: rdmsr\n2:\n" \ 24 | ".section .fixup,\"ax\"\n" \ 25 | "3: movl %5,%2\n; jmp 2b\n" \ 26 | ".previous\n" \ 27 | ".section __ex_table,\"a\"\n" \ 28 | " "__FIXUP_ALIGN"\n" \ 29 | ".previous\n" \ 30 | : "=a" (val1), "=d" (val2), "=&r" (_rc) \ 31 | : "c" (msr), "2" (0), "i" (-EFAULT)); \ 32 | _rc; }) 33 | */ 34 | 35 | #define wrmsr(msr,val1,val2) \ 36 | __asm__ __volatile__("wrmsr" \ 37 | : /* no outputs */ \ 38 | : "c" (msr), "a" (val1), "d" (val2)) 39 | 40 | #define rdtsc(low,high) \ 41 | __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) 42 | 43 | #define rdtscl(low) \ 44 | __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx") 45 | 46 | #define rdtscll(val) \ 47 | __asm__ __volatile__("rdtsc" : "=A" (val)) 48 | 49 | #define write_tsc(val1,val2) wrmsr(0x10, val1, val2) 50 | 51 | #define rdpmc(counter,low,high) \ 52 | __asm__ __volatile__("rdpmc" \ 53 | : "=a" (low), "=d" (high) \ 54 | : "c" (counter)) 55 | 56 | /* symbolic names for some interesting MSRs */ 57 | /* Intel defined MSRs. */ 58 | #define MSR_IA32_P5_MC_ADDR 0 59 | #define MSR_IA32_P5_MC_TYPE 1 60 | #define MSR_IA32_PLATFORM_ID 0x17 61 | #define MSR_IA32_EBL_CR_POWERON 0x2a 62 | 63 | #define MSR_IA32_APICBASE 0x1b 64 | #define MSR_IA32_APICBASE_BSP (1<<8) 65 | #define MSR_IA32_APICBASE_ENABLE (1<<11) 66 | #define MSR_IA32_APICBASE_BASE (0xfffff<<12) 67 | 68 | #define MSR_IA32_UCODE_WRITE 0x79 69 | #define MSR_IA32_UCODE_REV 0x8b 70 | 71 | #define MSR_IA32_BBL_CR_CTL 0x119 72 | 73 | #define MSR_IA32_MCG_CAP 0x179 74 | #define MSR_IA32_MCG_STATUS 0x17a 75 | #define MSR_IA32_MCG_CTL 0x17b 76 | 77 | #define MSR_IA32_THERM_CONTROL 0x19a 78 | #define MSR_IA32_THERM_INTERRUPT 0x19b 79 | #define MSR_IA32_THERM_STATUS 0x19c 80 | #define MSR_IA32_MISC_ENABLE 0x1a0 81 | #define MSR_IA32_TEMPERATURE_TARGET 0x1a2 82 | 83 | #define MSR_IA32_DEBUGCTLMSR 0x1d9 84 | #define MSR_IA32_LASTBRANCHFROMIP 0x1db 85 | #define MSR_IA32_LASTBRANCHTOIP 0x1dc 86 | #define MSR_IA32_LASTINTFROMIP 0x1dd 87 | #define MSR_IA32_LASTINTTOIP 0x1de 88 | 89 | #define MSR_IA32_MC0_CTL 0x400 90 | #define MSR_IA32_MC0_STATUS 0x401 91 | #define MSR_IA32_MC0_ADDR 0x402 92 | #define MSR_IA32_MC0_MISC 0x403 93 | 94 | #define MSR_P6_PERFCTR0 0xc1 95 | #define MSR_P6_PERFCTR1 0xc2 96 | #define MSR_P6_EVNTSEL0 0x186 97 | #define MSR_P6_EVNTSEL1 0x187 98 | 99 | #define MSR_IA32_PERF_STATUS 0x198 100 | #define MSR_IA32_PERF_CTL 0x199 101 | 102 | /* AMD Defined MSRs */ 103 | #define MSR_K6_EFER 0xC0000080 104 | #define MSR_K6_STAR 0xC0000081 105 | #define MSR_K6_WHCR 0xC0000082 106 | #define MSR_K6_UWCCR 0xC0000085 107 | #define MSR_K6_EPMR 0xC0000086 108 | #define MSR_K6_PSOR 0xC0000087 109 | #define MSR_K6_PFIR 0xC0000088 110 | 111 | #define MSR_K7_EVNTSEL0 0xC0010000 112 | #define MSR_K7_PERFCTR0 0xC0010004 113 | #define MSR_K7_HWCR 0xC0010015 114 | #define MSR_K7_CLK_CTL 0xC001001b 115 | #define MSR_K7_FID_VID_CTL 0xC0010041 116 | #define MSR_K7_VID_STATUS 0xC0010042 117 | 118 | /* Centaur-Hauls/IDT defined MSRs. */ 119 | #define MSR_IDT_FCR1 0x107 120 | #define MSR_IDT_FCR2 0x108 121 | #define MSR_IDT_FCR3 0x109 122 | #define MSR_IDT_FCR4 0x10a 123 | 124 | #define MSR_IDT_MCR0 0x110 125 | #define MSR_IDT_MCR1 0x111 126 | #define MSR_IDT_MCR2 0x112 127 | #define MSR_IDT_MCR3 0x113 128 | #define MSR_IDT_MCR4 0x114 129 | #define MSR_IDT_MCR5 0x115 130 | #define MSR_IDT_MCR6 0x116 131 | #define MSR_IDT_MCR7 0x117 132 | #define MSR_IDT_MCR_CTRL 0x120 133 | 134 | /* VIA Cyrix defined MSRs*/ 135 | #define MSR_VIA_FCR 0x1107 136 | #define MSR_VIA_LONGHAUL 0x110a 137 | #define MSR_VIA_BCR2 0x1147 138 | 139 | /* Transmeta defined MSRs */ 140 | #define MSR_TMTA_LONGRUN_CTRL 0x80868010 141 | #define MSR_TMTA_LONGRUN_FLAGS 0x80868011 142 | #define MSR_TMTA_LRTI_READOUT 0x80868018 143 | #define MSR_TMTA_LRTI_VOLT_MHZ 0x8086801a 144 | 145 | #endif /* __ASM_MSR_H */ 146 | -------------------------------------------------------------------------------- /mt86+_loader: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Distrotech/memtest86/a0af2ad356379c1dace15b0dc1677f81e240dd54/mt86+_loader -------------------------------------------------------------------------------- /mt86+_loader.asm: -------------------------------------------------------------------------------- 1 | ; A loader for www.memtest.org images, by Eric Auer 2003. 2 | ; This assumes that the image starts with the boot sector, 3 | ; which has the size of setup.S in sectors in a byte at offset 4 | ; 1f1h (497). Further, I assume setup.S directly after the boot 5 | ; sector and the actual memtest head.S after setup.S ... 6 | 7 | ; This version is derived from memtestL loader, which loads 8 | ; memtest.bin from a separate file. This version is meant to 9 | ; be used like (DOS / Unix variants): 10 | ; copy /b memteste.bin + memtest.bin memtest.exe 11 | ; cat memteste.bin memtest.bin > memtest.exe 12 | ; The good thing is that you get a single file which can be 13 | ; compressed, for example with http://upx.sf.net/ (UPX). 14 | 15 | %define fullsize (150024 + buffer - exeh) 16 | ; 150024 is the size of memtest86+ V5.01, adjust as needed! 17 | 18 | %define stacksize 2048 19 | %define stackpara ((stacksize + 15) / 16) 20 | 21 | ; the trick is that NASM believes the header would be part 22 | ; of the loaded image, so we "org 20h bytes too early" to fix: 23 | org 0e0h ; NASM thinks after header we have 100h 24 | ; which is what we want it to think. 25 | 26 | exeh: db "MZ" 27 | dw fullsize % 512 ; how much to load from 28 | dw (fullsize + 511) / 512 ; .exe to RAM 29 | dw 0 ; no relocations used 30 | dw 2 ; header size is 2 * 16 bytes 31 | dw stackpara ; minimum heap is 128 * 16 bytes, for stack 32 | dw stackpara ; we do not need more heap either 33 | dw (fullsize + 15) / 16 ; SS is after file 34 | ; segment offsets are relative to PSPseg+10h 35 | ; initial DS and ES point to PSPseg, and file 36 | ; except headers is loaded to PSPseg+10h. 37 | 38 | dw stacksize-4 ; initial SP value 39 | dw 0 ; no checksum 40 | dw 100h ; initial IP 41 | dw -10h ; initial CS relative to PSPseg+10h 42 | dw 0 ; no relocation table, "offset 0 in file" 43 | dw 0 ; this is not an overlay 44 | db "MEMT" ; padding to a multiple of 16 bytes 45 | 46 | ; loaded part begins here (set CS so that IP is 100h here) 47 | 48 | start: ; entry point ; if you use obj + linker, use "..start:" 49 | mov ah, 01h 50 | mov bh, 00h 51 | mov cx, 2000h 52 | int 10h 53 | 54 | mov ax,cs ; *** 55 | mov ds,ax ; *** 56 | mov es,ax ; *** 57 | 58 | ; test if we have 386 or better: 59 | pushf ; save flags 60 | xor ax,ax 61 | push ax 62 | popf ; try to clear all bits 63 | pushf 64 | pop ax 65 | and ax,0f000h 66 | cmp ax,0f000h 67 | jz noinst1 ; 4 msb stuck to 1: 808x or 80186 68 | mov ax,0f000h 69 | push ax 70 | popf ; try to set 4 msb 71 | pushf 72 | pop ax 73 | test ax,0f000h 74 | jz noinst1 ; 4 msb stuck to 0: 80286 75 | popf ; restore flags 76 | jmp short found386 77 | 78 | noinst1: 79 | popf ; restore flags 80 | mov dx,need386 81 | jmp generror 82 | 83 | 84 | found386: ; now test if the system is in real mode: 85 | smsw ax ; MSW is the low half of CR0 86 | ; (smsw is not priv'd, unlike mov eax,cr0) 87 | test al,1 ; if the PE (protected mode) flag on? 88 | %ifndef DEBUG ; ignore findings in debug mode 89 | jnz foundprotected 90 | %endif 91 | jmp foundreal 92 | 93 | foundprotected: 94 | mov dx,noreal 95 | jmp generror 96 | 97 | ; ------------ 98 | 99 | need386 db "Sorry, you need at least a 386 CPU to use Memtest86+." 100 | db 13,10,"$" 101 | noreal db "You cannot run Memtest86+ if the system already is in" 102 | db " protected mode.",13,10,"$" 103 | 104 | ; ------------ 105 | 106 | generror: ; generic error exit 107 | push cs 108 | pop ds 109 | push cs 110 | pop es 111 | mov ah,9 112 | int 21h 113 | mov ax,4c01h 114 | int 21h 115 | 116 | ; ------------ 117 | 118 | foundreal: 119 | mov cx,buffer+15 120 | shr cx,4 ; buffer offset in paragraphs 121 | mov ax,cs 122 | add ax,cx ; buffer offset in paragraphs 123 | ; now AX is the buffer segment 124 | mov [cs:bufsetup+2],ax ; far pointer to boot sector now 125 | mov cx,20h ; size of boot sector in paragraphs 126 | add [cs:bufsetup+2],cx ; far pointer to setup now 127 | movzx eax,ax 128 | shl eax,4 ; linear buffer offset 129 | mov [cs:buflinear],eax 130 | 131 | findpoint: ; now patch the loader! 132 | mov al,[buffer+1f1h] ; size of setup.S in sectors 133 | ; should be 4 ... 134 | inc al ; the boot sector itself 135 | movzx eax,al 136 | shl eax,9 ; log 2 of sector size 137 | add [cs:buflinear],eax ; linear address of head.S now 138 | mov ax,[buffer+251h] ; should be jmp far dword (ofs, seg) 139 | cmp ax,0ea66h 140 | jz foundpatch 141 | patchbug: ; could not patch the jump 142 | mov dx,nopatch 143 | jmp generror 144 | 145 | gdtbug: 146 | mov dx,nogdt 147 | jmp generror 148 | 149 | foundpatch: 150 | mov eax,[cs:buflinear] 151 | mov [buffer+253h],eax ; patch the protected mode entry jump 152 | ; (offset only - segment selector unchanged: flat linear CS) 153 | 154 | findgdt: 155 | mov eax,[cs:buffer+20ch] ; should be lgdt offset 156 | and eax,00ffffffh 157 | cmp eax,0016010fh ; lgdt ... 158 | jnz gdtbug 159 | 160 | mov ax,[cs:buffer+20fh] ; GDTR contents pointer 161 | mov bx,ax 162 | mov eax,[cs:buffer+200h+bx+2] ; GDT linear offset 163 | and eax,1ffh ; assume GDT in first sector of setup.S 164 | ; *** WARNING: this is needed because setup.S contains 165 | ; *** HARDCODED offset of setup.S on linear 90200h, which 166 | ; *** is 90000h + bootsect.S ... flaw in Memtest86! 167 | 168 | mov cx,[cs:bufsetup+2] ; setup.S segment 169 | movzx ecx,cx 170 | shl ecx,4 ; linear setup.S address 171 | add eax,ecx ; fixed GDT linear offset 172 | mov [cs:buffer+200h+bx+2],eax ; patch it 173 | 174 | ;mov dx,trying 175 | ;mov ah,9 176 | ;int 21h 177 | 178 | ;xor ax,ax 179 | ;int 16h ; wait for a keypress from the user 180 | 181 | mov ax,[cs:bufsetup+2] ; setup segment 182 | mov ds,ax ; set nice data segments for setup.S ... 183 | mov es,ax 184 | xor ax,ax 185 | mov fs,ax 186 | mov gs,ax 187 | 188 | cli 189 | lss sp,[cs:newstack] ; stack in first 64k now! 190 | movzx esp,sp ; ensure 16bit stack pointer 191 | ; Memtest86 head.S assumes that it can just turn SS to 192 | ; linear. This would put the stack at 0:200h or so for us 193 | ; if we fail to move the stack around ... 194 | 195 | %ifdef DEBUG 196 | mov ebp,[cs:buflinear] ; will show up in debugging logs 197 | mov esi,[cs:bufsetup] ; will show up in debugging logs 198 | %endif 199 | 200 | jmp far [cs:bufsetup] 201 | ; setup.S will enable the A20 (ignoring HIMEM, just using 202 | ; the classic 8042 programming trick) and turn on protected 203 | ; mode. Then it will jump to head.S, which luckily can run 204 | ; from any offset inside the linear 4 GB CS ... 205 | 206 | ; ------------ 207 | 208 | buflinear dd 0 ; linear address of head.S entry point 209 | bufsetup dw 0,0 ; far pointer to setup.S entry point 210 | 211 | newstack dw 03fch,0 ; beware, stack will overwrite IDT. 212 | 213 | ; ------------ 214 | 215 | nopatch db "jmp far dword not found at setup.S offset 37h,",13,10 216 | db "(file offset 237h is not 66h, 0eah)",13,10 217 | db "please adjust and recompile memtestl...",13,10,"$" 218 | 219 | nogdt db "lgdt [...] not found at setup.S offset 0ch,",13,10 220 | db "(file offset 20ch is not 0fh, 01h, 16h)",13,10 221 | db "please adjust and recompile memtestl...",13,10,"$" 222 | 223 | trying db "Now trying to start Memtest86...",13,10 224 | db "You have to reboot to leave Memtest86 again.",13,10 225 | db "Press a key to go on.",13,10,"$" 226 | 227 | ; ------------ 228 | 229 | align 16 230 | buffer: ; a label pointing to where in the file memtest.bin will be. 231 | 232 | -------------------------------------------------------------------------------- /patn.c: -------------------------------------------------------------------------------- 1 | /* Pattern extension for memtest86 2 | * 3 | * Generates patterns for the Linux kernel's BadRAM extension that avoids 4 | * allocation of faulty pages. 5 | * 6 | * Released under version 2 of the Gnu Public License. 7 | * 8 | * By Rick van Rein, vanrein@zonnet.nl 9 | * ---------------------------------------------------- 10 | * MemTest86+ V1.60 Specific code (GPL V2.0) 11 | * By Samuel DEMEULEMEESTER, sdemeule@memtest.org 12 | * http://www.x86-secret.com - http://www.memtest.org 13 | */ 14 | 15 | 16 | #include "test.h" 17 | 18 | 19 | /* 20 | * DEFAULT_MASK covers a longword, since that is the testing granularity. 21 | */ 22 | #define DEFAULT_MASK ((~0L) << 2) 23 | 24 | 25 | /* extern struct vars *v; */ 26 | 27 | 28 | /* What it does: 29 | * - Keep track of a number of BadRAM patterns in an array; 30 | * - Combine new faulty addresses with it whenever possible; 31 | * - Keep masks as selective as possible by minimising resulting faults; 32 | * - Print a new pattern only when the pattern array is changed. 33 | */ 34 | 35 | #define COMBINE_MASK(a,b,c,d) ((a & b & c & d) | (~a & b & ~c & d)) 36 | 37 | /* Combine two adr/mask pairs to one adr/mask pair. 38 | */ 39 | void combine (ulong adr1, ulong mask1, ulong adr2, ulong mask2, 40 | ulong *adr, ulong *mask) { 41 | 42 | *mask = COMBINE_MASK (adr1, mask1, adr2, mask2); 43 | 44 | *adr = adr1 | adr2; 45 | *adr &= *mask; // Normalise, no fundamental need for this 46 | } 47 | 48 | /* Count the number of addresses covered with a mask. 49 | */ 50 | ulong addresses (ulong mask) { 51 | ulong ctr=1; 52 | int i=32; 53 | while (i-- > 0) { 54 | if (! (mask & 1)) { 55 | ctr += ctr; 56 | } 57 | mask >>= 1; 58 | } 59 | return ctr; 60 | } 61 | 62 | /* Count how much more addresses would be covered by adr1/mask1 when combined 63 | * with adr2/mask2. 64 | */ 65 | ulong combicost (ulong adr1, ulong mask1, ulong adr2, ulong mask2) { 66 | ulong cost1=addresses (mask1); 67 | ulong tmp, mask; 68 | combine (adr1, mask1, adr2, mask2, &tmp, &mask); 69 | return addresses (mask) - cost1; 70 | } 71 | 72 | /* Find the cheapest array index to extend with the given adr/mask pair. 73 | * Return -1 if nothing below the given minimum cost can be found. 74 | */ 75 | int cheapindex (ulong adr1, ulong mask1, ulong mincost) { 76 | int i=v->numpatn; 77 | int idx=-1; 78 | while (i-- > 0) { 79 | ulong tmpcost=combicost(v->patn[i].adr, v->patn[i].mask, adr1, mask1); 80 | if (tmpcost < mincost) { 81 | mincost=tmpcost; 82 | idx=i; 83 | } 84 | } 85 | return idx; 86 | } 87 | 88 | /* Try to find a relocation index for idx if it costs nothing. 89 | * Return -1 if no such index exists. 90 | */ 91 | int relocateidx (int idx) { 92 | ulong adr =v->patn[idx].adr; 93 | ulong mask=v->patn[idx].mask; 94 | int new; 95 | v->patn[idx].adr ^= ~0L; // Never select idx 96 | new=cheapindex (adr, mask, 1+addresses (mask)); 97 | v->patn[idx].adr = adr; 98 | return new; 99 | } 100 | 101 | /* Relocate the given index idx only if free of charge. 102 | * This is useful to combine to `neighbouring' sections to integrate. 103 | * Inspired on the Buddy memalloc principle in the Linux kernel. 104 | */ 105 | void relocateiffree (int idx) { 106 | int newidx=relocateidx (idx); 107 | if (newidx>=0) { 108 | ulong cadr, cmask; 109 | combine (v->patn [newidx].adr, v->patn[newidx].mask, 110 | v->patn [ idx].adr, v->patn[ idx].mask, 111 | &cadr, &cmask); 112 | v->patn[newidx].adr =cadr; 113 | v->patn[newidx].mask=cmask; 114 | if (idx < --v->numpatn) { 115 | v->patn[idx].adr =v->patn[v->numpatn].adr; 116 | v->patn[idx].mask=v->patn[v->numpatn].mask; 117 | } 118 | relocateiffree (newidx); 119 | } 120 | } 121 | 122 | /* Insert a single faulty address in the pattern array. 123 | * Return 1 only if the array was changed. 124 | */ 125 | int insertaddress (ulong adr) { 126 | if (cheapindex (adr, DEFAULT_MASK, 1L) != -1) 127 | return 0; 128 | 129 | if (v->numpatn < BADRAM_MAXPATNS) { 130 | v->patn[v->numpatn].adr =adr; 131 | v->patn[v->numpatn].mask=DEFAULT_MASK; 132 | v->numpatn++; 133 | relocateiffree (v->numpatn-1); 134 | } else { 135 | int idx=cheapindex (adr, DEFAULT_MASK, ~0L); 136 | ulong cadr, cmask; 137 | combine (v->patn [idx].adr, v->patn[idx].mask, 138 | adr, DEFAULT_MASK, &cadr, &cmask); 139 | v->patn[idx].adr =cadr; 140 | v->patn[idx].mask=cmask; 141 | relocateiffree (idx); 142 | } 143 | return 1; 144 | } 145 | -------------------------------------------------------------------------------- /pci.c: -------------------------------------------------------------------------------- 1 | /* pci.c - MemTest-86 Version 3.2 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Chris Brady 5 | * ---------------------------------------------------- 6 | * MemTest86+ V5.00 Specific code (GPL V2.0) 7 | * By Samuel DEMEULEMEESTER, sdemeule@memtest.org 8 | * http://www.x86-secret.com - http://www.memtest.org 9 | */ 10 | 11 | #include "io.h" 12 | #include "pci.h" 13 | #include "test.h" 14 | #include "stdint.h" 15 | #include "cpuid.h" 16 | 17 | #define PCI_CONF_TYPE_NONE 0 18 | #define PCI_CONF_TYPE_1 1 19 | #define PCI_CONF_TYPE_2 2 20 | 21 | extern struct cpu_ident cpu_id; 22 | 23 | static unsigned char pci_conf_type = PCI_CONF_TYPE_NONE; 24 | 25 | #define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ 26 | (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) 27 | 28 | #define PCI_CONF2_ADDRESS(dev, reg) (unsigned short)(0xC000 | (dev << 8) | reg) 29 | 30 | #define PCI_CONF3_ADDRESS(bus, dev, fn, reg) \ 31 | (0x80000000 | (((reg >> 8) & 0xF) << 24) | (bus << 16) | ((dev & 0x1F) << 11) | (fn << 8) | (reg & 0xFF)) 32 | 33 | int pci_conf_read(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, unsigned long *value) 34 | { 35 | int result; 36 | 37 | if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255 && pci_conf_type != PCI_CONF_TYPE_1)) 38 | return -1; 39 | 40 | result = -1; 41 | switch(pci_conf_type) { 42 | case PCI_CONF_TYPE_1: 43 | if(reg < 256){ 44 | outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); 45 | }else{ 46 | outl(PCI_CONF3_ADDRESS(bus, dev, fn, reg), 0xCF8); 47 | } 48 | switch(len) { 49 | case 1: *value = inb(0xCFC + (reg & 3)); result = 0; break; 50 | case 2: *value = inw(0xCFC + (reg & 2)); result = 0; break; 51 | case 4: *value = inl(0xCFC); result = 0; break; 52 | } 53 | break; 54 | case PCI_CONF_TYPE_2: 55 | outb(0xF0 | (fn << 1), 0xCF8); 56 | outb(bus, 0xCFA); 57 | 58 | switch(len) { 59 | case 1: *value = inb(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; 60 | case 2: *value = inw(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; 61 | case 4: *value = inl(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; 62 | } 63 | outb(0, 0xCF8); 64 | break; 65 | } 66 | return result; 67 | } 68 | 69 | int pci_conf_write(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, unsigned long value) 70 | { 71 | int result; 72 | 73 | if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255 && pci_conf_type != PCI_CONF_TYPE_1)) 74 | return -1; 75 | 76 | result = -1; 77 | 78 | switch(pci_conf_type) 79 | { 80 | case PCI_CONF_TYPE_1: 81 | if(reg < 256){ 82 | outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); 83 | }else{ 84 | outl(PCI_CONF3_ADDRESS(bus, dev, fn, reg), 0xCF8); 85 | } 86 | switch(len) { 87 | case 1: outb(value, 0xCFC + (reg & 3)); result = 0; break; 88 | case 2: outw(value, 0xCFC + (reg & 2)); result = 0; break; 89 | case 4: outl(value, 0xCFC); result = 0; break; 90 | } 91 | break; 92 | case PCI_CONF_TYPE_2: 93 | outb(0xF0 | (fn << 1), 0xCF8); 94 | outb(bus, 0xCFA); 95 | 96 | switch(len) { 97 | case 1: outb(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; 98 | case 2: outw(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; 99 | case 4: outl(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break; 100 | } 101 | outb(0, 0xCF8); 102 | break; 103 | } 104 | return result; 105 | } 106 | 107 | static int pci_sanity_check(void) 108 | { 109 | unsigned long value; 110 | int result; 111 | /* Do a trivial check to make certain we can see a host bridge. 112 | * There are reportedly some buggy chipsets from intel and 113 | * compaq where this test does not work, I will worry about 114 | * that when we support them. 115 | */ 116 | result = pci_conf_read(0, 0, 0, PCI_CLASS_DEVICE, 2, &value); 117 | if (result == 0) { 118 | result = -1; 119 | if (value == PCI_CLASS_BRIDGE_HOST) { 120 | result = 0; 121 | } 122 | } 123 | return result; 124 | } 125 | 126 | static int pci_check_direct(void) 127 | { 128 | unsigned char tmpCFB; 129 | unsigned int tmpCF8; 130 | 131 | if (cpu_id.vend_id.char_array[0] == 'A' && cpu_id.vers.bits.family == 0xF) { 132 | pci_conf_type = PCI_CONF_TYPE_1; 133 | return 0; 134 | } else { 135 | /* Check if configuration type 1 works. */ 136 | pci_conf_type = PCI_CONF_TYPE_1; 137 | tmpCFB = inb(0xCFB); 138 | outb(0x01, 0xCFB); 139 | tmpCF8 = inl(0xCF8); 140 | outl(0x80000000, 0xCF8); 141 | if ((inl(0xCF8) == 0x80000000) && (pci_sanity_check() == 0)) { 142 | outl(tmpCF8, 0xCF8); 143 | outb(tmpCFB, 0xCFB); 144 | return 0; 145 | } 146 | outl(tmpCF8, 0xCF8); 147 | 148 | /* Check if configuration type 2 works. */ 149 | 150 | pci_conf_type = PCI_CONF_TYPE_2; 151 | outb(0x00, 0xCFB); 152 | outb(0x00, 0xCF8); 153 | outb(0x00, 0xCFA); 154 | if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 && (pci_sanity_check() == 0)) { 155 | outb(tmpCFB, 0xCFB); 156 | return 0; 157 | 158 | } 159 | 160 | outb(tmpCFB, 0xCFB); 161 | 162 | /* Nothing worked return an error */ 163 | pci_conf_type = PCI_CONF_TYPE_NONE; 164 | return -1; 165 | 166 | } 167 | } 168 | 169 | int pci_init(void) 170 | { 171 | int result; 172 | /* For now just make certain we can directly 173 | * use the pci functions. 174 | */ 175 | result = pci_check_direct(); 176 | return result; 177 | } 178 | -------------------------------------------------------------------------------- /pci.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMTEST_PCI_H 2 | #define MEMTEST_PCI_H 3 | 4 | int pci_conf_read(unsigned bus, unsigned dev, unsigned fn, unsigned reg, 5 | unsigned len, unsigned long *value); 6 | int pci_conf_write(unsigned bus, unsigned dev, unsigned fn, unsigned reg, 7 | unsigned len, unsigned long value); 8 | int pci_init(void); 9 | 10 | #define MAKE_PCIE_ADDRESS(bus, device, function) (((bus) & 0xFF)<<20) | (((device) & 0x1F)<<15) | (((function) & 0x7)<<12) 11 | 12 | /* 13 | * Under PCI, each device has 256 bytes of configuration address space, 14 | * of which the first 64 bytes are standardized as follows: 15 | */ 16 | #define PCI_VENDOR_ID 0x00 /* 16 bits */ 17 | #define PCI_DEVICE_ID 0x02 /* 16 bits */ 18 | #define PCI_COMMAND 0x04 /* 16 bits */ 19 | #define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ 20 | #define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ 21 | #define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ 22 | #define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ 23 | #define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ 24 | #define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ 25 | #define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ 26 | #define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ 27 | #define PCI_COMMAND_SERR 0x100 /* Enable SERR */ 28 | #define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ 29 | 30 | #define PCI_STATUS 0x06 /* 16 bits */ 31 | #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ 32 | #define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ 33 | #define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ 34 | #define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ 35 | #define PCI_STATUS_PARITY 0x100 /* Detected parity error */ 36 | #define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ 37 | #define PCI_STATUS_DEVSEL_FAST 0x000 38 | #define PCI_STATUS_DEVSEL_MEDIUM 0x200 39 | #define PCI_STATUS_DEVSEL_SLOW 0x400 40 | #define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ 41 | #define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ 42 | #define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ 43 | #define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ 44 | #define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ 45 | 46 | #define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 47 | revision */ 48 | #define PCI_REVISION_ID 0x08 /* Revision ID */ 49 | #define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ 50 | #define PCI_CLASS_DEVICE 0x0a /* Device class */ 51 | 52 | #define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ 53 | #define PCI_LATENCY_TIMER 0x0d /* 8 bits */ 54 | #define PCI_HEADER_TYPE 0x0e /* 8 bits */ 55 | #define PCI_HEADER_TYPE_NORMAL 0 56 | #define PCI_HEADER_TYPE_BRIDGE 1 57 | #define PCI_HEADER_TYPE_CARDBUS 2 58 | 59 | #define PCI_BIST 0x0f /* 8 bits */ 60 | #define PCI_BIST_CODE_MASK 0x0f /* Return result */ 61 | #define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ 62 | #define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ 63 | 64 | /* 65 | * Base addresses specify locations in memory or I/O space. 66 | * Decoded size can be determined by writing a value of 67 | * 0xffffffff to the register, and reading it back. Only 68 | * 1 bits are decoded. 69 | */ 70 | #define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ 71 | #define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ 72 | #define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ 73 | #define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ 74 | #define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ 75 | #define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ 76 | #define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ 77 | #define PCI_BASE_ADDRESS_SPACE_IO 0x01 78 | #define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 79 | #define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 80 | #define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ 81 | #define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ 82 | #define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ 83 | #define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ 84 | #define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) 85 | #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) 86 | /* bit 1 is reserved if address_space = 1 */ 87 | 88 | 89 | /* Device classes and subclasses */ 90 | #define PCI_CLASS_NOT_DEFINED 0x0000 91 | #define PCI_CLASS_NOT_DEFINED_VGA 0x0001 92 | 93 | #define PCI_BASE_CLASS_BRIDGE 0x06 94 | #define PCI_CLASS_BRIDGE_HOST 0x0600 95 | 96 | #endif /* MEMTEST_PCI_H */ 97 | -------------------------------------------------------------------------------- /precomp.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Distrotech/memtest86/a0af2ad356379c1dace15b0dc1677f81e240dd54/precomp.bin -------------------------------------------------------------------------------- /random.c: -------------------------------------------------------------------------------- 1 | /******************************************************************/ 2 | /* Random number generator */ 3 | /* concatenation of following two 16-bit multiply with carry generators */ 4 | /* x(n)=a*x(n-1)+carry mod 2^16 and y(n)=b*y(n-1)+carry mod 2^16, */ 5 | /* number and carry packed within the same 32 bit integer. */ 6 | /******************************************************************/ 7 | #include "stdint.h" 8 | #include "cpuid.h" 9 | #include "smp.h" 10 | 11 | /* Keep a separate seed for each CPU */ 12 | /* Space the seeds by at least a cache line or performance suffers big time! */ 13 | static unsigned int SEED_X[MAX_CPUS*16]; 14 | static unsigned int SEED_Y[MAX_CPUS*16]; 15 | 16 | unsigned long rand (int cpu) 17 | { 18 | static unsigned int a = 18000, b = 30903; 19 | int me; 20 | 21 | me = cpu*16; 22 | 23 | SEED_X[me] = a*(SEED_X[me]&65535) + (SEED_X[me]>>16); 24 | SEED_Y[me] = b*(SEED_Y[me]&65535) + (SEED_Y[me]>>16); 25 | 26 | return ((SEED_X[me]<<16) + (SEED_Y[me]&65535)); 27 | } 28 | 29 | 30 | void rand_seed( unsigned int seed1, unsigned int seed2, int cpu) 31 | { 32 | int me; 33 | 34 | me = cpu*16; 35 | SEED_X[me] = seed1; 36 | SEED_Y[me] = seed2; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /reloc.c: -------------------------------------------------------------------------------- 1 | /* reloc.c - MemTest-86 Version 3.3 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Eric Biederman 5 | */ 6 | 7 | #include "stddef.h" 8 | #include "stdint.h" 9 | #include "elf.h" 10 | 11 | #define __ELF_NATIVE_CLASS 32 12 | #define ELF_MACHINE_NO_RELA 1 13 | 14 | /* We use this macro to refer to ELF types independent of the native wordsize. 15 | `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ 16 | 17 | #define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) 18 | #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) 19 | #define _ElfW_1(e,w,t) e##w##t 20 | /* We use this macro to refer to ELF types independent of the native wordsize. 21 | `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ 22 | #define ELFW(type) _ElfW (ELF, __ELF_NATIVE_CLASS, type) 23 | 24 | #define assert(expr) ((void) 0) 25 | 26 | /* This #define produces dynamic linking inline functions for 27 | bootstrap relocation instead of general-purpose relocation. */ 28 | #define RTLD_BOOTSTRAP 29 | 30 | struct link_map 31 | { 32 | ElfW(Addr) l_addr; /* Current load address */ 33 | ElfW(Addr) ll_addr; /* Last load address */ 34 | ElfW(Dyn) *l_ld; 35 | /* Indexed pointers to dynamic section. 36 | [0,DT_NUM) are indexed by the processor-independent tags. 37 | [DT_NUM,DT_NUM+DT_PROCNUM) are indexed by the tag minus DT_LOPROC. 38 | [DT_NUM+DT_PROCNUM,DT_NUM+DT_PROCNUM+DT_EXTRANUM) are indexed 39 | by DT_EXTRATAGIDX(tagvalue) and 40 | [DT_NUM+DT_PROCNUM, 41 | DT_NUM+DT_PROCNUM+DT_EXTRANUM) 42 | are indexed by DT_EXTRATAGIDX(tagvalue) (see ). */ 43 | 44 | ElfW(Dyn) *l_info[DT_NUM + DT_PROCNUM + DT_EXTRANUM]; 45 | }; 46 | 47 | 48 | /* Return the link-time address of _DYNAMIC. Conveniently, this is the 49 | first element of the GOT. This must be inlined in a function which 50 | uses global data. */ 51 | static inline Elf32_Addr __attribute__ ((unused)) 52 | elf_machine_dynamic (void) 53 | { 54 | register Elf32_Addr *got asm ("%ebx"); 55 | return *got; 56 | } 57 | 58 | /* Return the run-time load address of the shared object. */ 59 | static inline Elf32_Addr __attribute__ ((unused)) 60 | elf_machine_load_address (void) 61 | { 62 | Elf32_Addr addr; 63 | asm volatile ("leal _start@GOTOFF(%%ebx), %0\n" 64 | : "=r" (addr) : : "cc"); 65 | return addr; 66 | } 67 | 68 | /* Perform the relocation specified by RELOC and SYM (which is fully resolved). 69 | MAP is the object containing the reloc. */ 70 | static inline void 71 | elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc, 72 | const Elf32_Sym *sym, Elf32_Addr *const reloc_addr) 73 | { 74 | Elf32_Addr ls_addr, s_addr; 75 | Elf32_Addr value; 76 | if (ELF32_R_TYPE (reloc->r_info) == R_386_RELATIVE) 77 | { 78 | *reloc_addr += map->l_addr - map->ll_addr; 79 | return; 80 | } 81 | if (ELF32_R_TYPE(reloc->r_info) == R_386_NONE) { 82 | return; 83 | } 84 | value = sym->st_value; 85 | /* Every section except the undefined section has a base of map->l_addr */ 86 | ls_addr = sym->st_shndx == SHN_UNDEF ? 0 : map->ll_addr; 87 | s_addr = sym->st_shndx == SHN_UNDEF ? 0 : map->l_addr; 88 | 89 | switch (ELF32_R_TYPE (reloc->r_info)) 90 | { 91 | case R_386_COPY: 92 | { 93 | /* Roll memcpy by hand as we don't have function calls yet. */ 94 | unsigned char *dest, *src; 95 | long i; 96 | dest = (unsigned char *)reloc_addr; 97 | src = (unsigned char *)(value + s_addr); 98 | for(i = 0; i < sym->st_size; i++) { 99 | dest[i] = src[i]; 100 | } 101 | } 102 | break; 103 | case R_386_GLOB_DAT: 104 | *reloc_addr = s_addr + value; 105 | break; 106 | case R_386_JMP_SLOT: 107 | *reloc_addr = s_addr + value; 108 | break; 109 | case R_386_32: 110 | if (map->ll_addr == 0) { 111 | *reloc_addr += value; 112 | } 113 | *reloc_addr += s_addr - ls_addr; 114 | break; 115 | case R_386_PC32: 116 | if (map->ll_addr == 0) { 117 | *reloc_addr += value - reloc->r_offset; 118 | } 119 | *reloc_addr += (s_addr - map->l_addr) - (ls_addr - map->ll_addr); 120 | break; 121 | default: 122 | assert (! "unexpected dynamic reloc type"); 123 | break; 124 | } 125 | } 126 | 127 | /* Read the dynamic section at DYN and fill in INFO with indices DT_*. */ 128 | 129 | static inline void __attribute__ ((unused)) 130 | elf_get_dynamic_info(ElfW(Dyn) *dyn, ElfW(Addr) l_addr, 131 | ElfW(Dyn) *info[DT_NUM + DT_PROCNUM + DT_EXTRANUM]) 132 | { 133 | if (! dyn) 134 | return; 135 | 136 | while (dyn->d_tag != DT_NULL) 137 | { 138 | if (dyn->d_tag < DT_NUM) 139 | info[dyn->d_tag] = dyn; 140 | else if (dyn->d_tag >= DT_LOPROC && 141 | dyn->d_tag < DT_LOPROC + DT_PROCNUM) 142 | info[dyn->d_tag - DT_LOPROC + DT_NUM] = dyn; 143 | else if ((Elf32_Word) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM) 144 | info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_PROCNUM 145 | ] = dyn; 146 | else 147 | assert (! "bad dynamic tag"); 148 | ++dyn; 149 | } 150 | 151 | if (info[DT_PLTGOT] != NULL) 152 | info[DT_PLTGOT]->d_un.d_ptr += l_addr; 153 | if (info[DT_STRTAB] != NULL) 154 | info[DT_STRTAB]->d_un.d_ptr += l_addr; 155 | if (info[DT_SYMTAB] != NULL) 156 | info[DT_SYMTAB]->d_un.d_ptr += l_addr; 157 | #if ! ELF_MACHINE_NO_RELA 158 | if (info[DT_RELA] != NULL) 159 | { 160 | assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela))); 161 | info[DT_RELA]->d_un.d_ptr += l_addr; 162 | } 163 | #endif 164 | #if ! ELF_MACHINE_NO_REL 165 | if (info[DT_REL] != NULL) 166 | { 167 | assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel))); 168 | info[DT_REL]->d_un.d_ptr += l_addr; 169 | } 170 | #endif 171 | if (info[DT_PLTREL] != NULL) 172 | { 173 | #if ELF_MACHINE_NO_RELA 174 | assert (info[DT_PLTREL]->d_un.d_val == DT_REL); 175 | #elif ELF_MACHINE_NO_REL 176 | assert (info[DT_PLTREL]->d_un.d_val == DT_RELA); 177 | #else 178 | assert (info[DT_PLTREL]->d_un.d_val == DT_REL 179 | || info[DT_PLTREL]->d_un.d_val == DT_RELA); 180 | #endif 181 | } 182 | if (info[DT_JMPREL] != NULL) 183 | info[DT_JMPREL]->d_un.d_ptr += l_addr; 184 | } 185 | 186 | 187 | 188 | /* Perform the relocations in MAP on the running program image as specified 189 | by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT 190 | relocations; they should be set up to call _dl_runtime_resolve, rather 191 | than fully resolved now. */ 192 | 193 | static inline void 194 | elf_dynamic_do_rel (struct link_map *map, 195 | ElfW(Addr) reladdr, ElfW(Addr) relsize) 196 | { 197 | const ElfW(Rel) *r = (const void *) reladdr; 198 | const ElfW(Rel) *end = (const void *) (reladdr + relsize); 199 | 200 | const ElfW(Sym) *const symtab = 201 | (const void *) map->l_info[DT_SYMTAB]->d_un.d_ptr; 202 | 203 | for (; r < end; ++r) { 204 | elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], 205 | (void *) (map->l_addr + r->r_offset)); 206 | } 207 | } 208 | 209 | 210 | void _dl_start(void) 211 | { 212 | static Elf32_Addr last_load_address = 0; 213 | struct link_map map; 214 | size_t cnt; 215 | 216 | 217 | /* Partly clean the `map' structure up. Don't use `memset' 218 | since it might nor be built in or inlined and we cannot make function 219 | calls at this point. */ 220 | for (cnt = 0; cnt < sizeof(map.l_info) / sizeof(map.l_info[0]); ++cnt) { 221 | map.l_info[cnt] = 0; 222 | } 223 | 224 | /* Get the last load address */ 225 | map.ll_addr = last_load_address; 226 | 227 | /* Figure out the run-time load address of the dynamic linker itself. */ 228 | last_load_address = map.l_addr = elf_machine_load_address(); 229 | 230 | /* Read our own dynamic section and fill in the info array. */ 231 | map.l_ld = (void *)map.l_addr + elf_machine_dynamic(); 232 | 233 | elf_get_dynamic_info (map.l_ld, map.l_addr - map.ll_addr, map.l_info); 234 | 235 | /* Relocate ourselves so we can do normal function calls and 236 | * data access using the global offset table. 237 | */ 238 | #if !ELF_MACHINE_NO_REL 239 | elf_dynamic_do_rel(&map, 240 | map.l_info[DT_REL]->d_un.d_ptr, 241 | map.l_info[DT_RELSZ]->d_un.d_val); 242 | if (map.l_info[DT_PLTREL]->d_un.d_val == DT_REL) { 243 | elf_dynamic_do_rel(&map, 244 | map.l_info[DT_JMPREL]->d_un.d_ptr, 245 | map.l_info[DT_PLTRELSZ]->d_un.d_val); 246 | } 247 | #endif 248 | 249 | #if !ELF_MACHINE_NO_RELA 250 | elf_dynamic_do_rela(&map, 251 | map.l_info[DT_RELA]->d_un.d_ptr, 252 | map.l_info[DT_RELASZ]->d_un.d_val); 253 | if (map.l_info[DT_PLTREL]->d_un.d_val == DT_RELA) { 254 | elf_dynamic_do_rela(&map, 255 | map.l_info[DT_JMPREL]->d_un.d_ptr, 256 | map.l_info[DT_PLTRELSZ]->d_un.d_val); 257 | } 258 | #endif 259 | 260 | /* Now life is sane; we can call functions and access global data. 261 | Set up to use the operating system facilities, and find out from 262 | the operating system's program loader where to find the program 263 | header table in core. Put the rest of _dl_start into a separate 264 | function, that way the compiler cannot put accesses to the GOT 265 | before ELF_DYNAMIC_RELOCATE. */ 266 | return; 267 | } 268 | -------------------------------------------------------------------------------- /screen_buffer.c: -------------------------------------------------------------------------------- 1 | /* screen_buffer.c - MemTest-86 Version 3.3 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Jani Averbach, Jaa@iki.fi, 2001 5 | */ 6 | 7 | #include "test.h" 8 | #include "screen_buffer.h" 9 | 10 | #define SCREEN_X 80 11 | #define SCREEN_Y 25 12 | #define Y_SIZE SCREEN_Y 13 | /* 14 | * X-size should by one of by screen size, 15 | * so that there is room for ending '\0' 16 | */ 17 | #define X_SIZE SCREEN_X+1 18 | 19 | static char screen_buf[Y_SIZE][X_SIZE]; 20 | 21 | #ifdef SCRN_DEBUG 22 | 23 | char *padding = "12345678901234567890123456789012345678901234567890123456789012345678901234567890"; 24 | 25 | #define CHECK_BOUNDS(y,x) do {if (y < 0 || Y_SIZE <= y || x < 0 || X_SIZE <= x) print_error("out of index");}while(0) 26 | 27 | #else /* ! SCRN_DEBUG */ 28 | 29 | #define CHECK_BOUNDS(y,x) 30 | 31 | #endif /* SCRN_DEBUG */ 32 | 33 | char 34 | get_scrn_buf(const int y, 35 | const int x) 36 | { 37 | CHECK_BOUNDS(y,x); 38 | return screen_buf[y][x]; 39 | } 40 | 41 | 42 | void 43 | set_scrn_buf(const int y, 44 | const int x, 45 | const char val) 46 | { 47 | CHECK_BOUNDS(y,x); 48 | screen_buf[y][x] = val; 49 | } 50 | 51 | void clear_screen_buf() 52 | { 53 | int y, x; 54 | 55 | for (y=0; y < SCREEN_Y; ++y){ 56 | for (x=0; x < SCREEN_X; ++x){ 57 | CHECK_BOUNDS(y,x); 58 | screen_buf[y][x] = ' '; 59 | } 60 | CHECK_BOUNDS(y,SCREEN_X); 61 | screen_buf[y][SCREEN_X] = '\0'; 62 | } 63 | } 64 | 65 | void tty_print_region(const int pi_top, 66 | const int pi_left, 67 | const int pi_bottom, 68 | const int pi_right) 69 | { 70 | int y; 71 | char tmp; 72 | 73 | for (y=pi_top; y < pi_bottom; ++y){ 74 | CHECK_BOUNDS(y, pi_right); 75 | 76 | tmp = screen_buf[y][pi_right]; 77 | screen_buf[y][pi_right] = '\0'; 78 | 79 | CHECK_BOUNDS(y, pi_left); 80 | ttyprint(y, pi_left, &(screen_buf[y][pi_left])); 81 | 82 | screen_buf[y][pi_right] = tmp; 83 | } 84 | } 85 | 86 | void tty_print_line( 87 | int y, int x, const char *text) 88 | { 89 | for(; *text && (x < SCREEN_X); x++, text++) { 90 | if (*text != screen_buf[y][x]) { 91 | break; 92 | } 93 | } 94 | /* If there is nothing to do return */ 95 | if (*text == '\0') { 96 | return; 97 | } 98 | ttyprint(y, x, text); 99 | for(; *text && (x < SCREEN_X); x++, text++) { 100 | screen_buf[y][x] = *text; 101 | } 102 | } 103 | 104 | 105 | void tty_print_screen(void) 106 | { 107 | #ifdef SCRN_DEBUG 108 | int i; 109 | 110 | for (i=0; i < SCREEN_Y; ++i) 111 | ttyprint(i,0, padding); 112 | #endif /* SCRN_DEBUG */ 113 | 114 | tty_print_region(0, 0, SCREEN_Y, SCREEN_X); 115 | } 116 | 117 | void print_error(char *pstr) 118 | { 119 | 120 | #ifdef SCRN_DEBUG 121 | ttyprint(0,0, padding); 122 | #endif /* SCRN_DEBUG */ 123 | 124 | ttyprint(0,35, pstr); 125 | 126 | while(1); 127 | } 128 | -------------------------------------------------------------------------------- /screen_buffer.h: -------------------------------------------------------------------------------- 1 | /* --*- C -*-- 2 | * 3 | * By Jani Averbach, Jaa@iki.fi, 2001 4 | * 5 | * Released under version 2 of the Gnu Public License. 6 | * 7 | */ 8 | #ifndef SCREEN_BUFFER_H_1D10F83B_INCLUDED 9 | #define SCREEN_BUFFER_H_1D10F83B_INCLUDED 10 | 11 | #include "config.h" 12 | 13 | char get_scrn_buf(const int y, const int x); 14 | void set_scrn_buf(const int y, const int x, const char val); 15 | void clear_screen_buf(void); 16 | void tty_print_region(const int pi_top,const int pi_left, const int pi_bottom,const int pi_right); 17 | void tty_print_line(int y, int x, const char *text); 18 | void tty_print_screen(void); 19 | void print_error(char *pstr); 20 | #endif /* SCREEN_BUFFER_H_1D10F83B_INCLUDED */ 21 | -------------------------------------------------------------------------------- /serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * include/linux/serial.h 3 | * 4 | * Copyright (C) 1992, 1994 by Theodore Ts'o. 5 | * 6 | * Redistribution of this file is permitted under the terms of the GNU 7 | * Public License (GPL) 8 | * 9 | * These are the UART port assignments, expressed as offsets from the base 10 | * register. These assignments should hold for any serial port based on 11 | * a 8250, 16450, or 16550(A). 12 | */ 13 | 14 | #ifndef _LINUX_SERIAL_REG_H 15 | #define _LINUX_SERIAL_REG_H 16 | 17 | #define UART_RX 0 /* In: Receive buffer (DLAB=0) */ 18 | #define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */ 19 | #define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ 20 | #define UART_DLM 1 /* Out: Divisor Latch High (DLAB=1) */ 21 | #define UART_IER 1 /* Out: Interrupt Enable Register */ 22 | #define UART_IIR 2 /* In: Interrupt ID Register */ 23 | #define UART_FCR 2 /* Out: FIFO Control Register */ 24 | #define UART_EFR 2 /* I/O: Extended Features Register */ 25 | /* (DLAB=1, 16C660 only) */ 26 | #define UART_LCR 3 /* Out: Line Control Register */ 27 | #define UART_MCR 4 /* Out: Modem Control Register */ 28 | #define UART_LSR 5 /* In: Line Status Register */ 29 | #define UART_MSR 6 /* In: Modem Status Register */ 30 | #define UART_SCR 7 /* I/O: Scratch Register */ 31 | 32 | 33 | 34 | /* 35 | * These are the definitions for the FIFO Control Register 36 | * (16650 only) 37 | */ 38 | #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ 39 | #define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ 40 | #define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ 41 | #define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */ 42 | #define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */ 43 | #define UART_FCR_TRIGGER_1 0x00 /* Mask for trigger set at 1 */ 44 | #define UART_FCR_TRIGGER_4 0x40 /* Mask for trigger set at 4 */ 45 | #define UART_FCR_TRIGGER_8 0x80 /* Mask for trigger set at 8 */ 46 | #define UART_FCR_TRIGGER_14 0xC0 /* Mask for trigger set at 14 */ 47 | /* 16650 redefinitions */ 48 | #define UART_FCR6_R_TRIGGER_8 0x00 /* Mask for receive trigger set at 1 */ 49 | #define UART_FCR6_R_TRIGGER_16 0x40 /* Mask for receive trigger set at 4 */ 50 | #define UART_FCR6_R_TRIGGER_24 0x80 /* Mask for receive trigger set at 8 */ 51 | #define UART_FCR6_R_TRIGGER_28 0xC0 /* Mask for receive trigger set at 14 */ 52 | #define UART_FCR6_T_TRIGGER_16 0x00 /* Mask for transmit trigger set at 16 */ 53 | #define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */ 54 | #define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */ 55 | #define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */ 56 | 57 | /* 58 | * These are the definitions for the Line Control Register 59 | * 60 | * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting 61 | * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. 62 | */ 63 | #define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ 64 | #define UART_LCR_SBC 0x40 /* Set break control */ 65 | #define UART_LCR_SPAR 0x20 /* Stick parity (?) */ 66 | #define UART_LCR_EPAR 0x10 /* Even parity select */ 67 | #define UART_LCR_PARITY 0x08 /* Parity Enable */ 68 | #define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ 69 | #define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ 70 | #define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ 71 | #define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ 72 | #define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ 73 | 74 | /* 75 | * These are the definitions for the Line Status Register 76 | */ 77 | #define UART_LSR_TEMT 0x40 /* Transmitter empty */ 78 | #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ 79 | #define UART_LSR_BI 0x10 /* Break interrupt indicator */ 80 | #define UART_LSR_FE 0x08 /* Frame error indicator */ 81 | #define UART_LSR_PE 0x04 /* Parity error indicator */ 82 | #define UART_LSR_OE 0x02 /* Overrun error indicator */ 83 | #define UART_LSR_DR 0x01 /* Receiver data ready */ 84 | 85 | /* 86 | * These are the definitions for the Interrupt Identification Register 87 | */ 88 | #define UART_IIR_NO_INT 0x01 /* No interrupts pending */ 89 | #define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ 90 | 91 | #define UART_IIR_MSI 0x00 /* Modem status interrupt */ 92 | #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ 93 | #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ 94 | #define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ 95 | 96 | /* 97 | * These are the definitions for the Interrupt Enable Register 98 | */ 99 | #define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ 100 | #define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ 101 | #define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ 102 | #define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ 103 | 104 | /* 105 | * These are the definitions for the Modem Control Register 106 | */ 107 | #define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ 108 | #define UART_MCR_OUT2 0x08 /* Out2 complement */ 109 | #define UART_MCR_OUT1 0x04 /* Out1 complement */ 110 | #define UART_MCR_RTS 0x02 /* RTS complement */ 111 | #define UART_MCR_DTR 0x01 /* DTR complement */ 112 | 113 | /* 114 | * These are the definitions for the Modem Status Register 115 | */ 116 | #define UART_MSR_DCD 0x80 /* Data Carrier Detect */ 117 | #define UART_MSR_RI 0x40 /* Ring Indicator */ 118 | #define UART_MSR_DSR 0x20 /* Data Set Ready */ 119 | #define UART_MSR_CTS 0x10 /* Clear to Send */ 120 | #define UART_MSR_DDCD 0x08 /* Delta DCD */ 121 | #define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ 122 | #define UART_MSR_DDSR 0x02 /* Delta DSR */ 123 | #define UART_MSR_DCTS 0x01 /* Delta CTS */ 124 | #define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ 125 | 126 | /* 127 | * These are the definitions for the Extended Features Register 128 | * (StarTech 16C660 only, when DLAB=1) 129 | */ 130 | #define UART_EFR_CTS 0x80 /* CTS flow control */ 131 | #define UART_EFR_RTS 0x40 /* RTS flow control */ 132 | #define UART_EFR_SCD 0x20 /* Special character detect */ 133 | #define UART_EFR_ENI 0x10 /* Enhanced Interrupt */ 134 | /* 135 | * the low four bits control software flow control 136 | */ 137 | 138 | #include "io.h" 139 | #define serial_echo_outb(v,a) outb((v),(a)+serial_base_ports[serial_tty]) 140 | #define serial_echo_inb(a) inb((a)+serial_base_ports[serial_tty]) 141 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) 142 | /* Wait for transmitter & holding register to empty */ 143 | #define WAIT_FOR_XMITR \ 144 | do { \ 145 | lsr = serial_echo_inb(UART_LSR); \ 146 | } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY) 147 | 148 | #if 0 149 | static inline void serial_echo(int ch) 150 | { 151 | int lsr; 152 | WAIT_FOR_XMITR; 153 | serial_echo_outb(ch, UART_TX); 154 | } 155 | static inline void serial_debug(int ch) 156 | { 157 | serial_echo(ch); 158 | serial_echo('\r'); 159 | serial_echo('\n'); 160 | } 161 | #endif 162 | #endif /* _LINUX_SERIAL_REG_H */ 163 | 164 | -------------------------------------------------------------------------------- /setup.S: -------------------------------------------------------------------------------- 1 | /* 2 | * setup.s is responsible for getting the system data from the BIOS, 3 | * and putting them into the appropriate places in system memory. 4 | * both setup.s and system has been loaded by the bootblock. 5 | * 6 | * 1-Jan-96 Modified by Chris Brady for use as a boot/loader for memtest-86. 7 | */ 8 | 9 | #define __ASSEMBLY__ 10 | #include "defs.h" 11 | 12 | .code16 13 | .section ".setup", "ax", @progbits 14 | .globl start 15 | start: 16 | # ok, the read went well 17 | # now we want to move to protected mode ... 18 | 19 | 20 | cli # no interrupts allowed # 21 | movb $0x80, %al # disable NMI for the bootup sequence 22 | outb %al, $0x70 23 | 24 | # The system will move itself to its rightful place. 25 | # reload the segment registers and the stack since the 26 | # APs also execute this code 27 | #ljmp $INITSEG, $(reload - start + 0x200) 28 | reload: 29 | movw $INITSEG, %ax 30 | movw %ax, %ds 31 | movw %ax, %es 32 | movw %ax, %fs 33 | movw %ax, %ss # reset the stack to INITSEG:0x4000-12. 34 | movw %dx, %sp 35 | push %cs 36 | pop %ds 37 | lidt idt_48 - start # load idt with 0,0 38 | lgdt gdt_48 - start # load gdt with whatever appropriate 39 | 40 | # that was painless, now we enable A20 41 | # start from grub-a20.patch 42 | /* 43 | * try to switch gateA20 using PORT92, the "Fast A20 and Init" 44 | * register 45 | */ 46 | mov $0x92, %dx 47 | inb %dx, %al 48 | /* skip the port92 code if it's unimplemented (read returns 0xff) */ 49 | cmpb $0xff, %al 50 | jz alt_a20_done 51 | 52 | /* set or clear bit1, the ALT_A20_GATE bit */ 53 | movb 4(%esp), %ah 54 | testb %ah, %ah 55 | jz alt_a20_cont1 56 | orb $2, %al 57 | jmp alt_a20_cont2 58 | alt_a20_cont1: 59 | and $0xfd, %al 60 | 61 | /* clear the INIT_NOW bit; don't accidently reset the machine */ 62 | alt_a20_cont2: 63 | and $0xfe, %al 64 | outb %al, %dx 65 | 66 | alt_a20_done: 67 | # end from grub-a20.patch 68 | 69 | call empty_8042 70 | 71 | movb $0xD1, %al # command write 72 | outb %al, $0x64 73 | call empty_8042 74 | 75 | movb $0xDF, %al # A20 on 76 | outb %al, $0x60 77 | call empty_8042 78 | 79 | /* 80 | * Note that the short jump isn't strictly needed, althought there are 81 | * reasons why it might be a good idea. It won't hurt in any case. 82 | */ 83 | movw $0x0001, %ax # protected mode (PE) bit 84 | lmsw %ax # This is it# 85 | jmp flush_instr 86 | flush_instr: 87 | movw $KERNEL_DS, %ax 88 | movw %ax, %ds 89 | movw %ax, %es 90 | movw %ax, %ss 91 | movw %ax, %fs 92 | movw %ax, %gs 93 | 94 | data32 ljmp $KERNEL_CS, $(TSTLOAD <<4) # jmp offset 2000 of segment 0x10 (cs) 95 | 96 | /* 97 | * This routine checks that the keyboard command queue is empty 98 | * (after emptying the output buffers) 99 | * 100 | * No timeout is used - if this hangs there is something wrong with 101 | * the machine, and we probably couldn't proceed anyway. 102 | */ 103 | empty_8042: 104 | call delay 105 | inb $0x64, %al # 8042 status port 106 | cmpb $0xff, %al # from grub-a20-patch, skip if not impl 107 | jz empty_8042_ret 108 | testb $1, %al # output buffer? 109 | jz no_output 110 | call delay 111 | inb $0x60, %al # read it 112 | jmp empty_8042 113 | 114 | no_output: 115 | testb $2, %al # is input buffer full? 116 | jnz empty_8042 # yes - loop 117 | empty_8042_ret: 118 | ret 119 | # 120 | # Delay is needed after doing i/o 121 | # 122 | delay: 123 | .word 0x00eb # jmp $+2 124 | ret 125 | 126 | gdt: 127 | .word 0,0,0,0 # dummy 128 | 129 | .word 0,0,0,0 # unused 130 | 131 | .word 0x7FFF # limit 128mb 132 | .word 0x0000 # base address=0 133 | .word 0x9A00 # code read/exec 134 | .word 0x00C0 # granularity=4096, 386 135 | 136 | .word 0x7FFF # limit 128mb 137 | .word 0x0000 # base address=0 138 | .word 0x9200 # data read/write 139 | .word 0x00C0 # granularity=4096, 386 140 | 141 | idt_48: 142 | .word 0 # idt limit=0 143 | .long 0 # idt base=0L 144 | 145 | gdt_48: 146 | .word 0x800 # gdt limit=2048, 256 GDT entries 147 | .word 512+gdt - start,0x9 # gdt base = 0X9xxxx 148 | 149 | msg1: 150 | .asciz "Setup.S\r\n" 151 | 152 | /* Pad setup to the proper size */ 153 | .org (SETUPSECS*512) 154 | 155 | -------------------------------------------------------------------------------- /smp.h: -------------------------------------------------------------------------------- 1 | /* ********************************************************** 2 | * Copyright 2002 VMware, Inc. All rights reserved. -- VMware Confidential 3 | * **********************************************************/ 4 | 5 | 6 | #ifndef _SMP_H_ 7 | #define _SMP_H_ 8 | #include "stdint.h" 9 | #include "defs.h" 10 | #define MAX_CPUS 32 11 | 12 | #define FPSignature ('_' | ('M' << 8) | ('P' << 16) | ('_' << 24)) 13 | 14 | typedef struct { 15 | uint32_t signature; // "_MP_" 16 | uint32_t phys_addr; 17 | uint8_t length; 18 | uint8_t spec_rev; 19 | uint8_t checksum; 20 | uint8_t feature[5]; 21 | } floating_pointer_struct_t; 22 | 23 | #define MPCSignature ('P' | ('C' << 8) | ('M' << 16) | ('P' << 24)) 24 | typedef struct { 25 | uint32_t signature; // "PCMP" 26 | uint16_t length; 27 | uint8_t spec_rev; 28 | uint8_t checksum; 29 | char oem[8]; 30 | char productid[12]; 31 | uint32_t oem_ptr; 32 | uint16_t oem_size; 33 | uint16_t oem_count; 34 | uint32_t lapic_addr; 35 | uint32_t reserved; 36 | } mp_config_table_header_t; 37 | 38 | /* Followed by entries */ 39 | 40 | #define MP_PROCESSOR 0 41 | #define MP_BUS 1 42 | #define MP_IOAPIC 2 43 | #define MP_INTSRC 3 44 | #define MP_LINTSRC 4 45 | 46 | typedef struct { 47 | uint8_t type; /* MP_PROCESSOR */ 48 | uint8_t apic_id; /* Local APIC number */ 49 | uint8_t apic_ver; /* Its versions */ 50 | uint8_t cpu_flag; 51 | #define CPU_ENABLED 1 /* Processor is available */ 52 | #define CPU_BOOTPROCESSOR 2 /* Processor is the BP */ 53 | uint32_t cpu_signature; 54 | #define CPU_STEPPING_MASK 0x0F 55 | #define CPU_MODEL_MASK 0xF0 56 | #define CPU_FAMILY_MASK 0xF00 57 | uint32_t featureflag; /* CPUID feature value */ 58 | uint32_t reserved[2]; 59 | } mp_processor_entry_t; 60 | 61 | typedef struct { 62 | uint8_t type; // has value MP_BUS 63 | uint8_t busid; 64 | char bustype[6]; 65 | } mp_bus_entry_t; 66 | 67 | /* We don't understand the others */ 68 | 69 | typedef struct { 70 | uint8_t type; // set to MP_IOAPIC 71 | uint8_t apicid; 72 | uint8_t apicver; 73 | uint8_t flags; 74 | #define MPC_APIC_USABLE 0x01 75 | uint32_t apicaddr; 76 | } mp_io_apic_entry_t; 77 | 78 | 79 | typedef struct { 80 | uint8_t type; 81 | uint8_t irqtype; 82 | uint16_t irqflag; 83 | uint8_t srcbus; 84 | uint8_t srcbusirq; 85 | uint8_t dstapic; 86 | uint8_t dstirq; 87 | } mp_interrupt_entry_t; 88 | 89 | #define MP_INT_VECTORED 0 90 | #define MP_INT_NMI 1 91 | #define MP_INT_SMI 2 92 | #define MP_INT_EXTINT 3 93 | 94 | #define MP_IRQDIR_DEFAULT 0 95 | #define MP_IRQDIR_HIGH 1 96 | #define MP_IRQDIR_LOW 3 97 | 98 | 99 | typedef struct { 100 | uint8_t type; 101 | uint8_t irqtype; 102 | uint16_t irqflag; 103 | uint8_t srcbusid; 104 | uint8_t srcbusirq; 105 | uint8_t destapic; 106 | #define MP_APIC_ALL 0xFF 107 | uint8_t destapiclint; 108 | } mp_local_interrupt_entry_t; 109 | 110 | #define RSDPSignature ('R' | ('S' << 8) | ('D' << 16) | (' ' << 24)) 111 | typedef struct { 112 | char signature[8]; // "RSD " 113 | uint8_t checksum; 114 | char oemid[6]; 115 | uint8_t revision; 116 | uint32_t rsdt; 117 | uint32_t length; 118 | uint32_t xrsdt[2]; 119 | uint8_t xsum; 120 | } rsdp_t; 121 | 122 | #define RSDTSignature ('R' | ('S' << 8) | ('D' << 16) | ('T' << 24)) 123 | #define XSDTSignature ('X' | ('S' << 8) | ('D' << 16) | ('T' << 24)) 124 | typedef struct { 125 | char signature[4]; // "RSDT" 126 | uint32_t length; 127 | uint8_t revision; 128 | uint8_t checksum; 129 | char oemid[18]; 130 | char cid[4]; 131 | char cver[4]; 132 | } rsdt_t; 133 | 134 | #define MADTSignature ('A' | ('P' << 8) | ('I' << 16) | ('C' << 24)) 135 | typedef struct { 136 | uint8_t type; 137 | uint8_t length; 138 | uint8_t acpi_id; 139 | uint8_t apic_id; /* Local APIC number */ 140 | uint32_t enabled; 141 | } madt_processor_entry_t; 142 | 143 | /* APIC definitions */ 144 | /* 145 | * APIC registers 146 | */ 147 | #define APICR_ID 0x02 148 | #define APICR_ESR 0x28 149 | #define APICR_ICRLO 0x30 150 | #define APICR_ICRHI 0x31 151 | 152 | /* APIC destination shorthands */ 153 | #define APIC_DEST_DEST 0 154 | #define APIC_DEST_LOCAL 1 155 | #define APIC_DEST_ALL_INC 2 156 | #define APIC_DEST_ALL_EXC 3 157 | 158 | /* APIC IPI Command Register format */ 159 | #define APIC_ICRHI_RESERVED 0x00ffffff 160 | #define APIC_ICRHI_DEST_MASK 0xff000000 161 | #define APIC_ICRHI_DEST_OFFSET 24 162 | 163 | #define APIC_ICRLO_RESERVED 0xfff32000 164 | #define APIC_ICRLO_DEST_MASK 0x000c0000 165 | #define APIC_ICRLO_DEST_OFFSET 18 166 | #define APIC_ICRLO_TRIGGER_MASK 0x00008000 167 | #define APIC_ICRLO_TRIGGER_OFFSET 15 168 | #define APIC_ICRLO_LEVEL_MASK 0x00004000 169 | #define APIC_ICRLO_LEVEL_OFFSET 14 170 | #define APIC_ICRLO_STATUS_MASK 0x00001000 171 | #define APIC_ICRLO_STATUS_OFFSET 12 172 | #define APIC_ICRLO_DESTMODE_MASK 0x00000800 173 | #define APIC_ICRLO_DESTMODE_OFFSET 11 174 | #define APIC_ICRLO_DELMODE_MASK 0x00000700 175 | #define APIC_ICRLO_DELMODE_OFFSET 8 176 | #define APIC_ICRLO_VECTOR_MASK 0x000000ff 177 | #define APIC_ICRLO_VECTOR_OFFSET 0 178 | 179 | /* APIC trigger types (edge/level) */ 180 | #define APIC_TRIGGER_EDGE 0 181 | #define APIC_TRIGGER_LEVEL 1 182 | 183 | /* APIC delivery modes */ 184 | #define APIC_DELMODE_FIXED 0 185 | #define APIC_DELMODE_LOWEST 1 186 | #define APIC_DELMODE_SMI 2 187 | #define APIC_DELMODE_NMI 4 188 | #define APIC_DELMODE_INIT 5 189 | #define APIC_DELMODE_STARTUP 6 190 | #define APIC_DELMODE_EXTINT 7 191 | typedef uint32_t apic_register_t[4]; 192 | 193 | extern volatile apic_register_t *APIC; 194 | 195 | unsigned smp_my_cpu_num(); 196 | 197 | void smp_init_bsp(void); 198 | void smp_init_aps(void); 199 | 200 | void smp_boot_ap(unsigned cpu_num); 201 | void smp_ap_booted(unsigned cpu_num); 202 | 203 | typedef struct { 204 | unsigned int slock; 205 | } spinlock_t; 206 | 207 | struct barrier_s 208 | { 209 | spinlock_t mutex; 210 | spinlock_t lck; 211 | int maxproc; 212 | volatile int count; 213 | spinlock_t st1; 214 | spinlock_t st2; 215 | spinlock_t s_lck; 216 | int s_maxproc; 217 | volatile int s_count; 218 | spinlock_t s_st1; 219 | spinlock_t s_st2; 220 | }; 221 | 222 | void barrier(); 223 | void s_barrier(); 224 | void barrier_init(int max); 225 | void s_barrier_init(int max); 226 | 227 | static inline void 228 | __GET_CPUID(int ax, uint32_t *regs) 229 | { 230 | __asm__ __volatile__("\t" 231 | /* save ebx in case -fPIC is being used */ 232 | "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx" 233 | : "=a" (regs[0]), "=D" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) 234 | : "a" (ax) 235 | : "memory" 236 | ); 237 | } 238 | 239 | #define GET_CPUID(_ax,_bx,_cx,_dx) { \ 240 | uint32_t regs[4]; \ 241 | __GET_CPUID(_ax,regs); \ 242 | _ax = regs[0]; \ 243 | _bx = regs[1]; \ 244 | _cx = regs[2]; \ 245 | _dx = regs[3]; \ 246 | } 247 | 248 | /* 249 | * Checked against the Intel manual and GCC --hpreg 250 | * 251 | * volatile because the tsc always changes without the compiler knowing it. 252 | */ 253 | static inline uint64_t 254 | RDTSC(void) 255 | { 256 | uint64_t tim; 257 | 258 | __asm__ __volatile__( 259 | "rdtsc" 260 | : "=A" (tim) 261 | ); 262 | 263 | return tim; 264 | } 265 | 266 | static inline uint64_t __GET_MSR(int cx) 267 | { 268 | uint64_t msr; 269 | 270 | __asm__ __volatile__( 271 | "rdmsr" 272 | : "=A" (msr) 273 | : "c" (cx) 274 | ); 275 | 276 | return msr; 277 | } 278 | 279 | #define __GCC_OUT(s, s2, port, val) do { \ 280 | __asm__( \ 281 | "out" #s " %" #s2 "1, %w0" \ 282 | : \ 283 | : "Nd" (port), "a" (val) \ 284 | ); \ 285 | } while (0) 286 | #define OUTB(port, val) __GCC_OUT(b, b, port, val) 287 | 288 | static inline void spin_wait(spinlock_t *lck) 289 | { 290 | if (cpu_id.fid.bits.mon) { 291 | /* Use monitor/mwait for a low power, low contention spin */ 292 | asm volatile( 293 | "movl $0,%%ecx\n\t" 294 | "movl %%ecx, %%edx\n\t" 295 | "1:\n\t" 296 | "movl %%edi,%%eax\n\t" 297 | "monitor\n\t" 298 | "cmpb $0,(%%edi)\n\t" 299 | "jne 2f\n\t" 300 | "movl %%ecx, %%eax\n\t" 301 | "mwait\n\t" 302 | "jmp 1b\n" 303 | "2:" 304 | : : "D" (lck): "%eax", "%ecx", "%edx" 305 | ); 306 | } else { 307 | /* No monitor/mwait so just spin with a lot of nop's */ 308 | int inc = 0x400; 309 | asm volatile( 310 | "1:\t" 311 | "cmpb $0,%1\n\t" 312 | "jne 2f\n\t" 313 | "rep ; nop\n\t" 314 | "jmp 1b\n" 315 | "2:" 316 | : : "c" (inc), "m" (lck->slock): "memory" 317 | ); 318 | } 319 | } 320 | 321 | static inline void spin_lock(spinlock_t *lck) 322 | { 323 | if (cpu_id.fid.bits.mon) { 324 | /* Use monitor/mwait for a low power, low contention spin */ 325 | asm volatile( 326 | "\n1:\t" 327 | " ; lock;decb (%%edi)\n\t" 328 | "jns 3f\n" 329 | "movl $0,%%ecx\n\t" 330 | "movl %%ecx, %%edx\n\t" 331 | "2:\t" 332 | "movl %%edi,%%eax\n\t" 333 | "monitor\n\t" 334 | "movl %%ecx, %%eax\n\t" 335 | "mwait\n\t" 336 | "cmpb $0,(%%edi)\n\t" 337 | "jle 2b\n\t" 338 | "jmp 1b\n" 339 | "3:\n\t" 340 | : : "D" (lck): "%eax", "%ecx", "%edx" 341 | ); 342 | } else { 343 | /* No monitor/mwait so just spin with a lot of nop's */ 344 | int inc = 0x400; 345 | asm volatile( 346 | "\n1:\t" 347 | " ; lock;decb %0\n\t" 348 | "jns 3f\n" 349 | "2:\t" 350 | "rep;nop\n\t" 351 | "cmpb $0,%0\n\t" 352 | "jle 2b\n\t" 353 | "jmp 1b\n" 354 | "3:\n\t" 355 | : "+m" (lck->slock) : "c" (inc) : "memory" 356 | ); 357 | } 358 | } 359 | 360 | static inline void spin_unlock(spinlock_t *lock) 361 | { 362 | asm volatile("movb $1,%0" : "+m" (lock->slock) :: "memory"); 363 | } 364 | 365 | 366 | #endif /* _SMP_H_ */ 367 | -------------------------------------------------------------------------------- /spd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MemTest86+ V5 Specific code (GPL V2.0) 3 | * By Samuel DEMEULEMEESTER, sdemeule@memtest.org 4 | * http://www.canardpc.com - http://www.memtest.org 5 | */ 6 | 7 | 8 | #include "test.h" 9 | #include "io.h" 10 | #include "pci.h" 11 | #include "msr.h" 12 | #include "spd.h" 13 | #include "screen_buffer.h" 14 | #include "jedec_id.h" 15 | 16 | #define NULL 0 17 | 18 | #define SMBHSTSTS smbusbase 19 | #define SMBHSTCNT smbusbase + 2 20 | #define SMBHSTCMD smbusbase + 3 21 | #define SMBHSTADD smbusbase + 4 22 | #define SMBHSTDAT smbusbase + 5 23 | 24 | extern void wait_keyup(); 25 | 26 | int smbdev, smbfun; 27 | unsigned short smbusbase; 28 | unsigned char spd_raw[256]; 29 | char s[] = {'/', 0, '-', 0, '\\', 0, '|', 0}; 30 | 31 | static void ich5_get_smb(void) 32 | { 33 | unsigned long x; 34 | int result; 35 | result = pci_conf_read(0, smbdev, smbfun, 0x20, 2, &x); 36 | if (result == 0) smbusbase = (unsigned short) x & 0xFFFE; 37 | } 38 | 39 | static void piix4_get_smb(void) 40 | { 41 | unsigned long x; 42 | int result; 43 | 44 | result = pci_conf_read(0, smbdev, smbfun, 0x08, 1, &x); 45 | 46 | if(x < 0x40){ 47 | // SB600/700 48 | result = pci_conf_read(0, smbdev, smbfun, 0x90, 2, &x); 49 | if (result == 0) smbusbase = (unsigned short) x & 0xFFFE; 50 | } else { 51 | // SB800 52 | sb800_get_smb(); 53 | } 54 | } 55 | 56 | void sb800_get_smb(void) 57 | { 58 | int lbyte, hbyte; 59 | 60 | __outb(AMD_SMBUS_BASE_REG + 1, AMD_INDEX_IO_PORT); 61 | lbyte = __inb(AMD_DATA_IO_PORT); 62 | __outb(AMD_SMBUS_BASE_REG, AMD_INDEX_IO_PORT); 63 | hbyte = __inb(AMD_DATA_IO_PORT); 64 | 65 | smbusbase = lbyte; 66 | smbusbase <<= 8; 67 | smbusbase += hbyte; 68 | smbusbase &= 0xFFE0; 69 | 70 | if (smbusbase == 0xFFE0) { smbusbase = 0; } 71 | } 72 | 73 | unsigned char ich5_smb_read_byte(unsigned char adr, unsigned char cmd) 74 | { 75 | int l1, h1, l2, h2; 76 | unsigned long long t; 77 | __outb(0x1f, SMBHSTSTS); // reset SMBus Controller 78 | __outb(0xff, SMBHSTDAT); 79 | while(__inb(SMBHSTSTS) & 0x01); // wait until ready 80 | __outb(cmd, SMBHSTCMD); 81 | __outb((adr << 1) | 0x01, SMBHSTADD); 82 | __outb(0x48, SMBHSTCNT); 83 | rdtsc(l1, h1); 84 | //cprint(POP2_Y, POP2_X + 16, s + cmd % 8); // progress bar 85 | while (!(__inb(SMBHSTSTS) & 0x02)) { // wait til command finished 86 | rdtsc(l2, h2); 87 | t = ((h2 - h1) * 0xffffffff + (l2 - l1)) / v->clks_msec; 88 | if (t > 10) break; // break after 10ms 89 | } 90 | return __inb(SMBHSTDAT); 91 | } 92 | 93 | static int ich5_read_spd(int dimmadr) 94 | { 95 | int x; 96 | spd_raw[0] = ich5_smb_read_byte(0x50 + dimmadr, 0); 97 | if (spd_raw[0] == 0xff) return -1; // no spd here 98 | for (x = 1; x < 256; x++) { 99 | spd_raw[x] = ich5_smb_read_byte(0x50 + dimmadr, (unsigned char) x); 100 | } 101 | return 0; 102 | } 103 | 104 | static void us15w_get_smb(void) 105 | { 106 | unsigned long x; 107 | int result; 108 | result = pci_conf_read(0, 0x1f, 0, 0x40, 2, &x); 109 | if (result == 0) smbusbase = (unsigned short) x & 0xFFC0; 110 | } 111 | 112 | unsigned char us15w_smb_read_byte(unsigned char adr, unsigned char cmd) 113 | { 114 | int l1, h1, l2, h2; 115 | unsigned long long t; 116 | //__outb(0x00, smbusbase + 1); // reset SMBus Controller 117 | //__outb(0x00, smbusbase + 6); 118 | //while((__inb(smbusbase + 1) & 0x08) != 0); // wait until ready 119 | __outb(0x02, smbusbase + 0); // Byte read 120 | __outb(cmd, smbusbase + 5); // Command 121 | __outb(0x07, smbusbase + 1); // Clear status 122 | __outb((adr << 1) | 0x01, smbusbase + 4); // DIMM address 123 | __outb(0x12, smbusbase + 0); // Start 124 | //while (((__inb(smbusbase + 1) & 0x08) == 0)) {} // wait til busy 125 | rdtsc(l1, h1); 126 | cprint(POP2_Y, POP2_X + 16, s + cmd % 8); // progress bar 127 | while (((__inb(smbusbase + 1) & 0x01) == 0) || 128 | ((__inb(smbusbase + 1) & 0x08) != 0)) { // wait til command finished 129 | rdtsc(l2, h2); 130 | t = ((h2 - h1) * 0xffffffff + (l2 - l1)) / v->clks_msec; 131 | if (t > 10) break; // break after 10ms 132 | } 133 | return __inb(smbusbase + 6); 134 | } 135 | 136 | static int us15w_read_spd(int dimmadr) 137 | { 138 | int x; 139 | spd_raw[0] = us15w_smb_read_byte(0x50 + dimmadr, 0); 140 | if (spd_raw[0] == 0xff) return -1; // no spd here 141 | for (x = 1; x < 256; x++) { 142 | spd_raw[x] = us15w_smb_read_byte(0x50 + dimmadr, (unsigned char) x); 143 | } 144 | return 0; 145 | } 146 | 147 | struct pci_smbus_controller { 148 | unsigned vendor; 149 | unsigned device; 150 | char *name; 151 | void (*get_adr)(void); 152 | int (*read_spd)(int dimmadr); 153 | }; 154 | 155 | static struct pci_smbus_controller smbcontrollers[] = { 156 | // Intel SMBUS 157 | {0x8086, 0x9C22, "Intel HSW-ULT", ich5_get_smb, ich5_read_spd}, 158 | {0x8086, 0x8C22, "Intel HSW", ich5_get_smb, ich5_read_spd}, 159 | {0x8086, 0x1E22, "Intel Z77", ich5_get_smb, ich5_read_spd}, 160 | {0x8086, 0x1C22, "Intel P67", ich5_get_smb, ich5_read_spd}, 161 | {0x8086, 0x3B30, "Intel P55", ich5_get_smb, ich5_read_spd}, 162 | {0x8086, 0x3A60, "Intel ICH10B", ich5_get_smb, ich5_read_spd}, 163 | {0x8086, 0x3A30, "Intel ICH10R", ich5_get_smb, ich5_read_spd}, 164 | {0x8086, 0x2930, "Intel ICH9", ich5_get_smb, ich5_read_spd}, 165 | {0x8086, 0x283E, "Intel ICH8", ich5_get_smb, ich5_read_spd}, 166 | {0x8086, 0x27DA, "Intel ICH7", ich5_get_smb, ich5_read_spd}, 167 | {0x8086, 0x266A, "Intel ICH6", ich5_get_smb, ich5_read_spd}, 168 | {0x8086, 0x24D3, "Intel ICH5", ich5_get_smb, ich5_read_spd}, 169 | {0x8086, 0x24C3, "Intel ICH4", ich5_get_smb, ich5_read_spd}, 170 | {0x8086, 0x25A4, "Intel 6300ESB", ich5_get_smb, ich5_read_spd}, 171 | {0x8086, 0x269B, "Intel ESB2", ich5_get_smb, ich5_read_spd}, 172 | {0x8086, 0x8119, "Intel US15W", us15w_get_smb, us15w_read_spd}, 173 | {0x8086, 0x5032, "Intel EP80579", ich5_get_smb, ich5_read_spd}, 174 | 175 | // AMD SMBUS 176 | {0x1002, 0x4385, "AMD SB600/700", piix4_get_smb, ich5_read_spd}, 177 | {0x1022, 0x780B, "AMD SB800/900", sb800_get_smb, ich5_read_spd}, 178 | {0, 0, "", NULL, NULL} 179 | }; 180 | 181 | 182 | int find_smb_controller(void) 183 | { 184 | int i = 0; 185 | unsigned long valuev, valued; 186 | 187 | for (smbdev = 0; smbdev < 32; smbdev++) { 188 | for (smbfun = 0; smbfun < 8; smbfun++) { 189 | pci_conf_read(0, smbdev, smbfun, 0, 2, &valuev); 190 | if (valuev != 0xFFFF) { // if there is something look what's it.. 191 | for (i = 0; smbcontrollers[i].vendor > 0; i++) { // check if this is a known smbus controller 192 | if (valuev == smbcontrollers[i].vendor) { 193 | pci_conf_read(0, smbdev, smbfun, 2, 2, &valued); // read the device id 194 | if (valued == smbcontrollers[i].device) { 195 | return i; 196 | } 197 | } 198 | } 199 | } 200 | } 201 | } 202 | return -1; 203 | } 204 | 205 | 206 | 207 | void get_spd_spec(void) 208 | { 209 | int index; 210 | int h, i, j, z; 211 | int k = 0; 212 | int module_size; 213 | int curcol; 214 | int temp_nbd; 215 | int tck; 216 | 217 | index = find_smb_controller(); 218 | 219 | if (index == -1) 220 | { 221 | // Unknown SMBUS Controller, exit 222 | return; 223 | } 224 | 225 | smbcontrollers[index].get_adr(); 226 | cprint(LINE_SPD-2, 0, "Memory SPD Informations"); 227 | cprint(LINE_SPD-1, 0, "--------------------------"); 228 | 229 | for (j = 0; j < 8; j++) { 230 | if (smbcontrollers[index].read_spd(j) == 0) { 231 | curcol = 1; 232 | if(spd_raw[2] == 0x0b){ 233 | // We are here if DDR3 present 234 | 235 | // First print slot#, module capacity 236 | cprint(LINE_SPD+k, curcol, " - Slot :"); 237 | dprint(LINE_SPD+k, curcol+8, k, 1, 0); 238 | 239 | module_size = get_ddr3_module_size(spd_raw[4] & 0xF, spd_raw[8] & 0x7, spd_raw[7] & 0x7, spd_raw[7] >> 3); 240 | temp_nbd = getnum(module_size); curcol += 12; 241 | dprint(LINE_SPD+k, curcol, module_size, temp_nbd, 0); curcol += temp_nbd; 242 | cprint(LINE_SPD+k, curcol, " MB"); curcol += 4; 243 | 244 | // If XMP is supported, check Tck in XMP reg 245 | if(spd_raw[176] == 0x0C && spd_raw[177] == 0x4A && spd_raw[12]) 246 | { 247 | tck = spd_raw[186]; 248 | } else { 249 | tck = spd_raw[12]; 250 | } 251 | 252 | // Then module jedec speed 253 | switch(tck) 254 | { 255 | default: 256 | cprint(LINE_SPD+k, curcol, "DDR3-????"); 257 | break; 258 | case 20: 259 | cprint(LINE_SPD+k, curcol, "DDR3-800"); 260 | curcol--; 261 | break; 262 | case 15: 263 | cprint(LINE_SPD+k, curcol, "DDR3-1066"); 264 | break; 265 | case 12: 266 | cprint(LINE_SPD+k, curcol, "DDR3-1333"); 267 | break; 268 | case 10: 269 | cprint(LINE_SPD+k, curcol, "DDR3-1600"); 270 | break; 271 | case 9: 272 | cprint(LINE_SPD+k, curcol, "DDR3-1866"); 273 | break; 274 | case 8: 275 | cprint(LINE_SPD+k, curcol, "DDR3-2133"); 276 | break; 277 | case 7: 278 | cprint(LINE_SPD+k, curcol, "DDR3-2400"); 279 | break; 280 | case 6: 281 | cprint(LINE_SPD+k, curcol, "DDR3-2533"); 282 | break; 283 | case 5: 284 | cprint(LINE_SPD+k, curcol, "DDR3-2666"); 285 | break; 286 | } 287 | 288 | curcol += 10; 289 | 290 | if((spd_raw[8] >> 3) == 1) { cprint(LINE_SPD+k, curcol, "ECC"); curcol += 4; } 291 | 292 | // Then print module infos (manufacturer & part number) 293 | spd_raw[117] &= 0x0F; // Parity odd or even 294 | for (i = 0; jep106[i].cont_code < 9; i++) { 295 | if (spd_raw[117] == jep106[i].cont_code && spd_raw[118] == jep106[i].hex_byte) { 296 | // We are here if a Jedec manufacturer is detected 297 | cprint(LINE_SPD+k, curcol, "-"); curcol += 2; 298 | cprint(LINE_SPD+k, curcol, jep106[i].name); 299 | for(z = 0; jep106[i].name[z] != '\0'; z++) { curcol++; } 300 | curcol++; 301 | // Display module serial number 302 | for (h = 128; h < 146; h++) { 303 | cprint(LINE_SPD+k, curcol, convert_hex_to_char(spd_raw[h])); 304 | curcol++; 305 | } 306 | 307 | // Detect Week and Year of Manufacturing (Think to upgrade after 2015 !!!) 308 | if(curcol <= 72 && spd_raw[120] > 3 && spd_raw[120] < 16 && spd_raw[121] < 55) 309 | { 310 | cprint(LINE_SPD+k, curcol, "(W"); 311 | dprint(LINE_SPD+k, curcol+2, spd_raw[121], 2, 0); 312 | if(spd_raw[121] < 10) { cprint(LINE_SPD+k, curcol+2, "0"); } 313 | cprint(LINE_SPD+k, curcol+4, "'"); 314 | dprint(LINE_SPD+k, curcol+5, spd_raw[120], 2, 0); 315 | if(spd_raw[120] < 10) { cprint(LINE_SPD+k, curcol+5, "0"); } 316 | cprint(LINE_SPD+k, curcol+7, ")"); 317 | curcol += 9; 318 | } 319 | 320 | // Detect XMP Memory 321 | if(spd_raw[176] == 0x0C && spd_raw[177] == 0x4A) 322 | { 323 | cprint(LINE_SPD+k, curcol, "*XMP*"); 324 | } 325 | } 326 | } 327 | } 328 | // We enter this function if DDR2 is detected 329 | if(spd_raw[2] == 0x08){ 330 | // First print slot#, module capacity 331 | cprint(LINE_SPD+k, curcol, " - Slot :"); 332 | dprint(LINE_SPD+k, curcol+8, k, 1, 0); 333 | 334 | module_size = get_ddr2_module_size(spd_raw[31], spd_raw[5]); 335 | temp_nbd = getnum(module_size); curcol += 12; 336 | dprint(LINE_SPD+k, curcol, module_size, temp_nbd, 0); curcol += temp_nbd; 337 | cprint(LINE_SPD+k, curcol, " MB"); curcol += 4; 338 | 339 | // Then module jedec speed 340 | float ddr2_speed, byte1, byte2; 341 | 342 | byte1 = (spd_raw[9] >> 4) * 10; 343 | byte2 = spd_raw[9] & 0xF; 344 | 345 | ddr2_speed = 1 / (byte1 + byte2) * 10000 * 2; 346 | 347 | temp_nbd = getnum(ddr2_speed); 348 | cprint(LINE_SPD+k, curcol, "DDR2-"); curcol += 5; 349 | dprint(LINE_SPD+k, curcol, ddr2_speed, temp_nbd, 0); curcol += temp_nbd; 350 | 351 | if((spd_raw[11] >> 1) == 1) { cprint(LINE_SPD+k, curcol+1, "ECC"); curcol += 4; } 352 | 353 | // Then print module infos (manufacturer & part number) 354 | int ccode = 0; 355 | 356 | for(i = 64; i < 72; i++) 357 | { 358 | if(spd_raw[i] == 0x7F) { ccode++; } 359 | } 360 | 361 | curcol++; 362 | 363 | for (i = 0; jep106[i].cont_code < 9; i++) { 364 | if (ccode == jep106[i].cont_code && spd_raw[64+ccode] == jep106[i].hex_byte) { 365 | // We are here if a Jedec manufacturer is detected 366 | cprint(LINE_SPD+k, curcol, "-"); curcol += 2; 367 | cprint(LINE_SPD+k, curcol, jep106[i].name); 368 | for(z = 0; jep106[i].name[z] != '\0'; z++) { curcol++; } 369 | curcol++; 370 | // Display module serial number 371 | for (h = 73; h < 91; h++) { 372 | cprint(LINE_SPD+k, curcol, convert_hex_to_char(spd_raw[h])); 373 | curcol++; 374 | } 375 | 376 | } 377 | } 378 | 379 | } 380 | k++; 381 | } 382 | } 383 | } 384 | 385 | 386 | void show_spd(void) 387 | { 388 | int index; 389 | int i, j; 390 | int flag = 0; 391 | pop2up(); 392 | wait_keyup(); 393 | index = find_smb_controller(); 394 | if (index == -1) { 395 | cprint(POP2_Y, POP2_X+1, "SMBus Controller not known"); 396 | while (!get_key()); 397 | wait_keyup(); 398 | pop2down(); 399 | return; 400 | } 401 | else cprint(POP2_Y, POP2_X+1, "SPD Data: Slot"); 402 | smbcontrollers[index].get_adr(); 403 | for (j = 0; j < 16; j++) { 404 | if (smbcontrollers[index].read_spd(j) == 0) { 405 | dprint(POP2_Y, POP2_X + 15, j, 2, 0); 406 | for (i = 0; i < 256; i++) { 407 | hprint2(2 + POP2_Y + i / 16, 3 + POP2_X + (i % 16) * 3, spd_raw[i], 2); 408 | } 409 | flag = 0; 410 | while(!flag) { 411 | if (get_key()) flag++; 412 | } 413 | wait_keyup(); 414 | } 415 | } 416 | pop2down(); 417 | } 418 | 419 | int get_ddr3_module_size(int sdram_capacity, int prim_bus_width, int sdram_width, int ranks) 420 | { 421 | int module_size; 422 | 423 | switch(sdram_capacity) 424 | { 425 | case 0: 426 | module_size = 256; 427 | break; 428 | case 1: 429 | module_size = 512; 430 | break; 431 | default: 432 | case 2: 433 | module_size = 1024; 434 | break; 435 | case 3: 436 | module_size = 2048; 437 | break; 438 | case 4: 439 | module_size = 4096; 440 | break; 441 | case 5: 442 | module_size = 8192; 443 | break; 444 | case 6: 445 | module_size = 16384; 446 | break; 447 | } 448 | 449 | module_size /= 8; 450 | 451 | switch(prim_bus_width) 452 | { 453 | case 0: 454 | module_size *= 8; 455 | break; 456 | case 1: 457 | module_size *= 16; 458 | break; 459 | case 2: 460 | module_size *= 32; 461 | break; 462 | case 3: 463 | module_size *= 64; 464 | break; 465 | } 466 | 467 | switch(sdram_width) 468 | { 469 | case 0: 470 | module_size /= 4; 471 | break; 472 | case 1: 473 | module_size /= 8; 474 | break; 475 | case 2: 476 | module_size /= 16; 477 | break; 478 | case 3: 479 | module_size /= 32; 480 | break; 481 | 482 | } 483 | 484 | module_size *= (ranks + 1); 485 | 486 | return module_size; 487 | } 488 | 489 | 490 | int get_ddr2_module_size(int rank_density_byte, int rank_num_byte) 491 | { 492 | int module_size; 493 | 494 | switch(rank_density_byte) 495 | { 496 | case 1: 497 | module_size = 1024; 498 | break; 499 | case 2: 500 | module_size = 2048; 501 | break; 502 | case 4: 503 | module_size = 4096; 504 | break; 505 | case 8: 506 | module_size = 8192; 507 | break; 508 | case 16: 509 | module_size = 16384; 510 | break; 511 | case 32: 512 | module_size = 128; 513 | break; 514 | case 64: 515 | module_size = 256; 516 | break; 517 | default: 518 | case 128: 519 | module_size = 512; 520 | break; 521 | } 522 | 523 | module_size *= (rank_num_byte & 7) + 1; 524 | 525 | return module_size; 526 | 527 | } 528 | 529 | 530 | struct ascii_map { 531 | unsigned hex_code; 532 | char *name; 533 | }; 534 | 535 | 536 | char* convert_hex_to_char(unsigned hex_org) { 537 | static char buf[2] = " "; 538 | if (hex_org >= 0x20 && hex_org < 0x80) { 539 | buf[0] = hex_org; 540 | } else { 541 | //buf[0] = '\0'; 542 | buf[0] = ' '; 543 | } 544 | 545 | return buf; 546 | } -------------------------------------------------------------------------------- /spd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MemTest86+ V5.00 Specific code (GPL V2.0) 3 | * By Samuel DEMEULEMEESTER, sdemeule@memtest.org 4 | * http://www.canardpc.com - http://www.memtest.org 5 | */ 6 | 7 | #define AMD_INDEX_IO_PORT 0xCD6 8 | #define AMD_DATA_IO_PORT 0xCD7 9 | #define AMD_SMBUS_BASE_REG 0x2C 10 | 11 | void get_spd_spec(void); 12 | int get_ddr2_module_size(int rank_density_byte, int rank_num_byte); 13 | int get_ddr3_module_size(int sdram_capacity, int prim_bus_width, int sdram_width, int ranks); 14 | char* convert_hex_to_char(unsigned hex_org); 15 | void sb800_get_smb(void); 16 | 17 | 18 | -------------------------------------------------------------------------------- /stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef I386_STDDEF_H 2 | #define I386_STDDEF_H 3 | 4 | #define NULL ((void *)0) 5 | 6 | typedef unsigned long size_t; 7 | 8 | #endif /* I386_STDDEF_H */ 9 | -------------------------------------------------------------------------------- /stdin.h: -------------------------------------------------------------------------------- 1 | #ifndef I386_STDINT_H 2 | #define I386_STDINT_H 3 | 4 | /* Exact integral types */ 5 | typedef unsigned char uint8_t; 6 | typedef signed char int8_t; 7 | 8 | typedef unsigned short uint16_t; 9 | typedef signed short int16_t; 10 | 11 | typedef unsigned int uint32_t; 12 | typedef signed int int32_t; 13 | 14 | typedef unsigned long long uint64_t; 15 | typedef signed long long int64_t; 16 | 17 | /* Small types */ 18 | typedef unsigned char uint_least8_t; 19 | typedef signed char int_least8_t; 20 | 21 | typedef unsigned short uint_least16_t; 22 | typedef signed short int_least16_t; 23 | 24 | typedef unsigned int uint_least32_t; 25 | typedef signed int int_least32_t; 26 | 27 | typedef unsigned long long uint_least64_t; 28 | typedef signed long long int_least64_t; 29 | 30 | /* Fast Types */ 31 | typedef unsigned char uint_fast8_t; 32 | typedef signed char int_fast8_t; 33 | 34 | typedef unsigned int uint_fast16_t; 35 | typedef signed int int_fast16_t; 36 | 37 | typedef unsigned int uint_fast32_t; 38 | typedef signed int int_fast32_t; 39 | 40 | typedef unsigned long long uint_fast64_t; 41 | typedef signed long long int_fast64_t; 42 | 43 | /* Types for `void *' pointers. */ 44 | typedef int intptr_t; 45 | typedef unsigned int uintptr_t; 46 | 47 | /* Largest integral types */ 48 | typedef long long int intmax_t; 49 | typedef unsigned long long uintmax_t; 50 | 51 | 52 | #endif /* I386_STDINT_H */ -------------------------------------------------------------------------------- /stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef I386_STDINT_H 2 | #define I386_STDINT_H 3 | 4 | /* Exact integral types */ 5 | typedef unsigned char uint8_t; 6 | typedef signed char int8_t; 7 | 8 | typedef unsigned short uint16_t; 9 | typedef signed short int16_t; 10 | 11 | typedef unsigned int uint32_t; 12 | typedef signed int int32_t; 13 | 14 | typedef unsigned long long uint64_t; 15 | typedef signed long long int64_t; 16 | 17 | /* Small types */ 18 | typedef unsigned char uint_least8_t; 19 | typedef signed char int_least8_t; 20 | 21 | typedef unsigned short uint_least16_t; 22 | typedef signed short int_least16_t; 23 | 24 | typedef unsigned int uint_least32_t; 25 | typedef signed int int_least32_t; 26 | 27 | typedef unsigned long long uint_least64_t; 28 | typedef signed long long int_least64_t; 29 | 30 | /* Fast Types */ 31 | typedef unsigned char uint_fast8_t; 32 | typedef signed char int_fast8_t; 33 | 34 | typedef unsigned int uint_fast16_t; 35 | typedef signed int int_fast16_t; 36 | 37 | typedef unsigned int uint_fast32_t; 38 | typedef signed int int_fast32_t; 39 | 40 | typedef unsigned long long uint_fast64_t; 41 | typedef signed long long int_fast64_t; 42 | 43 | /* Types for `void *' pointers. */ 44 | typedef int intptr_t; 45 | typedef unsigned int uintptr_t; 46 | 47 | /* Largest integral types */ 48 | typedef long long int intmax_t; 49 | typedef unsigned long long uintmax_t; 50 | 51 | typedef char bool; 52 | #ifndef FALSE 53 | #define FALSE 0 54 | #endif 55 | 56 | #ifndef TRUE 57 | #define TRUE 1 58 | #endif 59 | 60 | #endif /* I386_STDINT_H */ 61 | -------------------------------------------------------------------------------- /test.h: -------------------------------------------------------------------------------- 1 | /* test.h - MemTest-86 Version 3.4 2 | * 3 | * Released under version 2 of the Gnu Public License. 4 | * By Chris Brady 5 | */ 6 | 7 | #ifndef _TEST_H_ 8 | #define _TEST_H_ 9 | #define E88 0x00 10 | #define E801 0x04 11 | #define E820NR 0x08 /* # entries in E820MAP */ 12 | #define E820MAP 0x0c /* our map */ 13 | #define E820MAX 127 /* number of entries in E820MAP */ 14 | #define E820ENTRY_SIZE 20 15 | #define MEMINFO_SIZE (E820MAP + E820MAX * E820ENTRY_SIZE) 16 | 17 | #ifndef __ASSEMBLY__ 18 | 19 | #define E820_RAM 1 20 | #define E820_RESERVED 2 21 | #define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ 22 | #define E820_NVS 4 23 | 24 | struct e820entry { 25 | unsigned long long addr; /* start of memory segment */ 26 | unsigned long long size; /* size of memory segment */ 27 | unsigned long type; /* type of memory segment */ 28 | }; 29 | 30 | struct mem_info_t { 31 | unsigned long e88_mem_k; /* 0x00 */ 32 | unsigned long e801_mem_k; /* 0x04 */ 33 | unsigned long e820_nr; /* 0x08 */ 34 | struct e820entry e820[E820MAX]; /* 0x0c */ 35 | /* 0x28c */ 36 | }; 37 | 38 | typedef unsigned long ulong; 39 | #define STACKSIZE (8*1024) 40 | #define MAX_MEM 0x7FF00000 /* 8 TB */ 41 | #define WIN_SZ 0x80000 /* 2 GB */ 42 | #define UNMAP_SZ (0x100000-WIN_SZ) /* Size of umappped first segment */ 43 | 44 | #define SPINSZ 0x4000000 /* 256 MB */ 45 | #define MOD_SZ 20 46 | #define BAILOUT if (bail) return(1); 47 | #define BAILR if (bail) return; 48 | 49 | #define RES_START 0xa0000 50 | #define RES_END 0x100000 51 | #define SCREEN_ADR 0xb8000 52 | #define SCREEN_END_ADR (SCREEN_ADR + 80*25*2) 53 | 54 | #define DMI_SEARCH_START 0x0000F000 55 | #define DMI_SEARCH_LENGTH 0x000F0FFF 56 | #define MAX_DMI_MEMDEVS 16 57 | 58 | #define TITLE_WIDTH 28 59 | #define LINE_TITLE 0 60 | #define LINE_TST 3 61 | #define LINE_RANGE 4 62 | #define LINE_PAT 5 63 | #define LINE_TIME 5 64 | #define LINE_STATUS 8 65 | #define LINE_INFO 9 66 | #define LINE_HEADER 12 67 | #define LINE_SCROLL 14 68 | #define LINE_SPD 14 69 | #define LINE_MSG 22 70 | #define LINE_CPU 7 71 | #define LINE_RAM 8 72 | #define LINE_DMI 23 73 | #define COL_INF1 15 74 | #define COL_INF2 32 75 | #define COL_INF3 51 76 | #define COL_INF4 70 77 | #define COL_MODE 15 78 | #define COL_MID 30 79 | #define COL_PAT 41 80 | #define BAR_SIZE (78-COL_MID-9) 81 | #define COL_MSG 23 82 | #define COL_TIME 67 83 | #define COL_SPEC 41 84 | 85 | #define POP_W 34 86 | #define POP_H 15 87 | #define POP_X 11 88 | #define POP_Y 8 89 | #define POP2_W 74 90 | #define POP2_H 21 91 | #define POP2_X 3 92 | #define POP2_Y 2 93 | 94 | /* CPU mode types */ 95 | #define CPM_ALL 1 96 | #define CPM_RROBIN 2 97 | #define CPM_SEQ 3 98 | 99 | /* memspeed operations */ 100 | #define MS_COPY 1 101 | #define MS_WRITE 2 102 | #define MS_READ 3 103 | 104 | #define SZ_MODE_BIOS 1 105 | #define SZ_MODE_PROBE 2 106 | 107 | #define getCx86(reg) ({ outb((reg), 0x22); inb(0x23); }) 108 | 109 | int memcmp(const void *s1, const void *s2, ulong count); 110 | void *memmove(void *dest, const void *src, ulong n); 111 | int strncmp(const char *s1, const char *s2, ulong n); 112 | int strstr(char *str1, char *str2); 113 | int strlen(char *string); 114 | int query_linuxbios(void); 115 | int query_pcbios(void); 116 | int insertaddress(ulong); 117 | void printpatn(void); 118 | void printpatn(void); 119 | void itoa(char s[], int n); 120 | void reverse(char *p); 121 | void serial_console_setup(char *param); 122 | void serial_echo_init(void); 123 | void serial_echo_print(const char *s); 124 | void ttyprint(int y, int x, const char *s); 125 | void ttyprintc(int y, int x, char c); 126 | void cprint(int y,int x, const char *s); 127 | void cplace(int y,int x, const char s); 128 | void hprint(int y,int x, ulong val); 129 | void hprint2(int y,int x, ulong val, int len); 130 | void hprint3(int y,int x, ulong val, int len); 131 | void xprint(int y,int x,ulong val); 132 | void aprint(int y,int x,ulong page); 133 | void dprint(int y,int x,ulong val,int len, int right); 134 | void movinv1(int iter, ulong p1, ulong p2, int cpu); 135 | void movinvr(int cpu); 136 | void movinv32(int iter, ulong p1, ulong lb, ulong mb, int sval, int off, 137 | int cpu); 138 | void modtst(int off, int iter, ulong p1, ulong p2, int cpu); 139 | void error(ulong* adr, ulong good, ulong bad); 140 | void ad_err1(ulong *adr1, ulong *adr2, ulong good, ulong bad); 141 | void ad_err2(ulong *adr, ulong bad); 142 | void do_tick(); 143 | void init(void); 144 | struct eregs; 145 | void inter(struct eregs *trap_regs); 146 | void set_cache(int val); 147 | void check_input(void); 148 | void footer(void); 149 | void scroll(void); 150 | void clear_scroll(void); 151 | void popup(void); 152 | void popdown(void); 153 | void popclear(void); 154 | void pop2up(void); 155 | void pop2down(void); 156 | void pop2clear(void); 157 | void get_config(void); 158 | void get_menu(void); 159 | void get_printmode(void); 160 | void addr_tst1(int cpu); 161 | void addr_tst2(int cpu); 162 | int getnum(ulong val); 163 | void sleep(long sec, int flag, int cpu, int sms); 164 | void block_move(int iter, int cpu); 165 | void find_ticks(void); 166 | void print_err(ulong *adr, ulong good, ulong bad, ulong xor); 167 | void print_ecc_err(ulong page, ulong offset, int corrected, 168 | unsigned short syndrome, int channel); 169 | void mem_size(void); 170 | void adj_mem(void); 171 | ulong getval(int x, int y, int result_shift); 172 | int get_key(void); 173 | int ascii_to_keycode(int in); 174 | void wait_keyup(void); 175 | void print_hdr(void); 176 | void restart(void); 177 | void parity_err(ulong edi, ulong esi); 178 | void start_config(void); 179 | void clear_screen(void); 180 | void paging_off(void); 181 | void show_spd(void); 182 | int map_page(unsigned long page); 183 | void *mapping(unsigned long page_address); 184 | void *emapping(unsigned long page_address); 185 | int isdigit(char c); 186 | ulong memspeed(ulong src, ulong len, int iter); 187 | unsigned long page_of(void *ptr); 188 | ulong correct_tsc(ulong el_org); 189 | void bit_fade_fill(unsigned long n, int cpu); 190 | void bit_fade_chk(unsigned long n, int cpu); 191 | void find_ticks_for_pass(void); 192 | void beep(unsigned int frequency); 193 | 194 | #define PRINTMODE_SUMMARY 0 195 | #define PRINTMODE_ADDRESSES 1 196 | #define PRINTMODE_PATTERNS 2 197 | #define PRINTMODE_NONE 3 198 | 199 | #define BADRAM_MAXPATNS 10 200 | 201 | struct pair { 202 | ulong adr; 203 | ulong mask; 204 | }; 205 | 206 | static inline void cache_off(void) 207 | { 208 | asm( 209 | "push %eax\n\t" 210 | "movl %cr0,%eax\n\t" 211 | "orl $0x40000000,%eax\n\t" /* Set CD */ 212 | "movl %eax,%cr0\n\t" 213 | "wbinvd\n\t" 214 | "pop %eax\n\t"); 215 | } 216 | 217 | static inline void cache_on(void) 218 | { 219 | asm( 220 | "push %eax\n\t" 221 | "movl %cr0,%eax\n\t" 222 | "andl $0x9fffffff,%eax\n\t" /* Clear CD and NW */ 223 | "movl %eax,%cr0\n\t" 224 | "pop %eax\n\t"); 225 | } 226 | 227 | struct mmap { 228 | ulong pbase_addr; 229 | ulong *start; 230 | ulong *end; 231 | }; 232 | 233 | struct pmap { 234 | ulong start; 235 | ulong end; 236 | }; 237 | 238 | struct tseq { 239 | short sel; 240 | short cpu_sel; 241 | short pat; 242 | short iter; 243 | short errors; 244 | char *msg; 245 | }; 246 | 247 | struct xadr { 248 | ulong page; 249 | ulong offset; 250 | }; 251 | 252 | struct err_info { 253 | struct xadr low_addr; 254 | struct xadr high_addr; 255 | unsigned long ebits; 256 | long tbits; 257 | short min_bits; 258 | short max_bits; 259 | unsigned long maxl; 260 | unsigned long eadr; 261 | unsigned long exor; 262 | unsigned long cor_err; 263 | short hdr_flag; 264 | }; 265 | 266 | 267 | 268 | #define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */ 269 | 270 | #define MAX_MEM_SEGMENTS E820MAX 271 | 272 | /* Define common variables accross relocations of memtest86 */ 273 | struct vars { 274 | int pass; 275 | int msg_line; 276 | int ecount; 277 | int ecc_ecount; 278 | int msegs; 279 | int testsel; 280 | int scroll_start; 281 | int pass_ticks; 282 | int total_ticks; 283 | int pptr; 284 | int tptr; 285 | struct err_info erri; 286 | struct pmap pmap[MAX_MEM_SEGMENTS]; 287 | volatile struct mmap map[MAX_MEM_SEGMENTS]; 288 | ulong plim_lower; 289 | ulong plim_upper; 290 | ulong clks_msec; 291 | ulong starth; 292 | ulong startl; 293 | ulong snaph; 294 | ulong snapl; 295 | int printmode; 296 | int numpatn; 297 | struct pair patn [BADRAM_MAXPATNS]; 298 | ulong test_pages; 299 | ulong selected_pages; 300 | ulong reserved_pages; 301 | int check_temp; 302 | int fail_safe; 303 | int each_sec; 304 | int beepmode; 305 | }; 306 | 307 | #define FIRMWARE_UNKNOWN 0 308 | #define FIRMWARE_PCBIOS 1 309 | #define FIRMWARE_LINUXBIOS 2 310 | 311 | extern struct vars * const v; 312 | extern unsigned char _start[], _end[], startup_32[]; 313 | extern unsigned char _size, _pages; 314 | 315 | extern struct mem_info_t mem_info; 316 | 317 | #endif /* __ASSEMBLY__ */ 318 | #endif /* _TEST_H_ */ 319 | -------------------------------------------------------------------------------- /version.number: -------------------------------------------------------------------------------- 1 | 4.99617 - Thu May 3 11:45:57 CEST 2012 2 | -------------------------------------------------------------------------------- /vmem.c: -------------------------------------------------------------------------------- 1 | /* vmem.c - MemTest-86 2 | * 3 | * Virtual memory handling (PAE) 4 | * 5 | * Released under version 2 of the Gnu Public License. 6 | * By Chris Brady 7 | */ 8 | #include "stdint.h" 9 | #include "test.h" 10 | #include "cpuid.h" 11 | 12 | extern struct cpu_ident cpu_id; 13 | 14 | static unsigned long mapped_win = 1; 15 | void paging_off(void) 16 | { 17 | if (!cpu_id.fid.bits.pae) 18 | return; 19 | __asm__ __volatile__ ( 20 | /* Disable paging */ 21 | "movl %%cr0, %%eax\n\t" 22 | "andl $0x7FFFFFFF, %%eax\n\t" 23 | "movl %%eax, %%cr0\n\t" 24 | : : 25 | : "ax" 26 | ); 27 | } 28 | 29 | static void paging_on(void *pdp) 30 | { 31 | if (!cpu_id.fid.bits.pae) 32 | return; 33 | __asm__ __volatile__( 34 | /* Load the page table address */ 35 | "movl %0, %%cr3\n\t" 36 | /* Enable paging */ 37 | "movl %%cr0, %%eax\n\t" 38 | "orl $0x80000000, %%eax\n\t" 39 | "movl %%eax, %%cr0\n\t" 40 | : 41 | : "r" (pdp) 42 | : "ax" 43 | ); 44 | } 45 | 46 | static void paging_on_lm(void *pml) 47 | { 48 | if (!cpu_id.fid.bits.pae) 49 | return; 50 | __asm__ __volatile__( 51 | /* Load the page table address */ 52 | "movl %0, %%cr3\n\t" 53 | /* Enable paging */ 54 | "movl %%cr0, %%eax\n\t" 55 | "orl $0x80000000, %%eax\n\t" 56 | "movl %%eax, %%cr0\n\t" 57 | : 58 | : "r" (pml) 59 | : "ax" 60 | ); 61 | } 62 | 63 | int map_page(unsigned long page) 64 | { 65 | unsigned long i; 66 | struct pde { 67 | unsigned long addr_lo; 68 | unsigned long addr_hi; 69 | }; 70 | extern unsigned char pdp[]; 71 | extern unsigned char pml4[]; 72 | extern struct pde pd2[]; 73 | unsigned long win = page >> 19; 74 | 75 | /* Less than 2 GB so no mapping is required */ 76 | if (win == 0) { 77 | return 0; 78 | } 79 | if (cpu_id.fid.bits.pae == 0) { 80 | /* Fail, we don't have PAE */ 81 | return -1; 82 | } 83 | if (cpu_id.fid.bits.lm == 0 && (page > 0x1000000)) { 84 | /* Fail, we want an address that is out of bounds (> 64GB) 85 | * for PAE and no long mode (ie. 32 bit CPU). 86 | */ 87 | return -1; 88 | } 89 | /* Compute the page table entries... */ 90 | for(i = 0; i < 1024; i++) { 91 | /*-----------------10/30/2004 12:37PM--------------- 92 | * 0xE3 -- 93 | * Bit 0 = Present bit. 1 = PDE is present 94 | * Bit 1 = Read/Write. 1 = memory is writable 95 | * Bit 2 = Supervisor/User. 0 = Supervisor only (CPL 0-2) 96 | * Bit 3 = Writethrough. 0 = writeback cache policy 97 | * Bit 4 = Cache Disable. 0 = page level cache enabled 98 | * Bit 5 = Accessed. 1 = memory has been accessed. 99 | * Bit 6 = Dirty. 1 = memory has been written to. 100 | * Bit 7 = Page Size. 1 = page size is 2 MBytes 101 | * --------------------------------------------------*/ 102 | pd2[i].addr_lo = ((win & 1) << 31) + ((i & 0x3ff) << 21) + 0xE3; 103 | pd2[i].addr_hi = (win >> 1); 104 | } 105 | paging_off(); 106 | if (cpu_id.fid.bits.lm == 1) { 107 | paging_on_lm(pml4); 108 | } else { 109 | paging_on(pdp); 110 | } 111 | mapped_win = win; 112 | return 0; 113 | } 114 | 115 | void *mapping(unsigned long page_addr) 116 | { 117 | void *result; 118 | if (page_addr < 0x80000) { 119 | /* If the address is less than 1GB directly use the address */ 120 | result = (void *)(page_addr << 12); 121 | } 122 | else { 123 | unsigned long alias; 124 | alias = page_addr & 0x7FFFF; 125 | alias += 0x80000; 126 | result = (void *)(alias << 12); 127 | } 128 | return result; 129 | } 130 | 131 | void *emapping(unsigned long page_addr) 132 | { 133 | void *result; 134 | result = mapping(page_addr -1); 135 | /* Fill in the low address bits */ 136 | result = ((unsigned char *)result) + 0xffc; 137 | return result; 138 | } 139 | 140 | unsigned long page_of(void *addr) 141 | { 142 | unsigned long page; 143 | page = ((unsigned long)addr) >> 12; 144 | if (page >= 0x80000) { 145 | page &= 0x7FFFF; 146 | page += mapped_win << 19; 147 | } 148 | #if 0 149 | cprint(LINE_SCROLL -2, 0, "page_of( )-> "); 150 | hprint(LINE_SCROLL -2, 8, ((unsigned long)addr)); 151 | hprint(LINE_SCROLL -2, 20, page); 152 | #endif 153 | return page; 154 | } 155 | --------------------------------------------------------------------------------