1 /* Copyright 2013 Google Inc. All Rights Reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 * This "tool" can be used to brute force the XOR bitmask that a memory
19 * controller uses to interleave addresses onto its two channels. To use it,
20 * you need to have a bunch of addresses that are known to go to only one
21 * of the memory channels... easiest way to get these is to run stressapptest on
22 * a machine while holding a soldering iron close to the chips of one channel.
23 * Generate about a thousand failures and extract their physical addresses
24 * from the output. Write them to findmask.inc in a way that forms a valid
25 * definition for the addrs array. Make and run on a big machine.
27 * The program iterates over all possible bitmasks within the first NUM_BITS,
28 * parallelizing execution over NUM_THREADS. Every integer is masked
29 * onto all supplied addresses, counting the amount of times this results in
30 * an odd or even amount of bits. If all but NOISE addresses fall on one side,
31 * it will print that mask to stdout. Note that the script will always "find"
32 * the mask 0x0, and may also report masks such as 0x100000000 depending on
33 * your test machines memory size... you will need to use your own judgement to
34 * interpret the results.
36 * As the program might run for a long time, you can send SIGUSR1 to it to
37 * output the last mask that was processed and get a rough idea of the
49 #define NUM_THREADS 128 // keep this a power of two
51 static uint64_t addrs[] = {
52 #include "findmask.inc"
54 static uint64_t lastmask;
56 __attribute__((optimize(3, "unroll-loops")))
57 void* thread_func(void* arg) {
58 register uint64_t mask;
59 register uintptr_t num = (uintptr_t)arg;
61 for (mask = num; mask < (1ULL << (NUM_BITS + 1)); mask += NUM_THREADS) {
62 register const uint64_t* cur;
66 for (cur = addrs; (char*)cur < (char*)addrs + sizeof(addrs); cur++) {
68 register uint64_t addr asm("rdx") = *cur & mask;
69 register uint32_t tmp asm("ebx");
71 // Behold: the dark bit counting magic!
73 // Fold high and low 32 bits onto each other
74 "MOVl %%edx, %%ebx\n\t"
76 "XORl %%ebx, %%edx\n\t"
77 // Fold high and low 16 bits onto each other
78 "MOVl %%edx, %%ebx\n\t"
81 // Fold high and low 8 bits onto each other
83 // Invoke ancient 8086 parity flag (only counts lowest byte)
86 // Stupid SET instruction can only affect the lowest byte...
89 // Increment either 'a' or 'b' without needing another branch
92 : "=b" (tmp), "+r"(a), "+r"(b) : "d"(addr) : "cc");
94 #else // generic processor
95 register uint64_t addr = *cur & mask;
96 register uint32_t low = (uint32_t)addr;
97 register uint32_t high = (uint32_t)(addr >> 32);
99 // Takes about twice as long as the version above... take that GCC!
100 __builtin_parity(low) ^ __builtin_parity(high) ? a++ : b++;
103 // Early abort: probably still the most valuable optimization in here
104 if (a >= NOISE && b >= NOISE) break;
107 if (a < NOISE) b = a;
109 printf("Found mask with just %d deviations: 0x%llx\n", b, mask);
113 // I'm a little paranoid about performance: don't write to memory too often
114 if (!(mask & 0x7ff)) lastmask = mask;
120 void signal_handler(int signum) {
121 printf("Received signal... currently evaluating mask 0x%llx!\n", lastmask);
125 int main(int argc, char** argv) {
127 pthread_t threads[NUM_THREADS];
129 signal(SIGUSR1, signal_handler);
131 for (i = 0; i < NUM_THREADS; i++)
132 pthread_create(&threads[i], 0, thread_func, (void*)i);
134 for (i = 0; i < NUM_THREADS; i++)
135 pthread_join(threads[i], 0);