Skip to content

Commit 39635b6

Browse files
Zbigniew Drozdfacebook-github-bot
Zbigniew Drozd
authored andcommitted
Flashy memtest
Summary: Poor man's memtest. Sometimes BMC ram can be broken and this utility aims to alert on failing devices Test Plan: unit tests, run manually on a confirmed bad device Reviewed By: kawmarco fbshipit-source-id: 7424937251ea769f299e477a1df376d545adc8f5
1 parent cc01da4 commit 39635b6

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Copyright 2020-present Facebook. All Rights Reserved.
3+
*
4+
* This program file is free software; you can redistribute it and/or modify it
5+
* under the terms of the GNU General Public License as published by the
6+
* Free Software Foundation; version 2 of the License.
7+
*
8+
* This program is distributed in the hope that it will be useful, but WITHOUT
9+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11+
* for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program in a file named COPYING; if not, write to the
15+
* Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor,
17+
* Boston, MA 02110-1301 USA
18+
*/
19+
20+
package common
21+
22+
import (
23+
"github.com/facebook/openbmc/tools/flashy/lib/step"
24+
"github.com/facebook/openbmc/tools/flashy/lib/utils"
25+
"github.com/pkg/errors"
26+
"log"
27+
"unsafe"
28+
)
29+
30+
func init() {
31+
step.RegisterStep(runMemtestSuite)
32+
}
33+
34+
// Equivalent of std::memset(scratchpadPointer, pattern, memtestSize)
35+
func populateMemtest(pattern byte, memtestSize uint64, wordSize uintptr, scratchpadPointer unsafe.Pointer) {
36+
for i := uint64(0); i < memtestSize; i++ {
37+
*(*byte)(unsafe.Pointer(uintptr(scratchpadPointer) + (uintptr(i) * wordSize))) = pattern
38+
}
39+
}
40+
41+
// Iterate over scratchpad and check if pattern matches every byte of it. Return error if not.
42+
var checkMemtest = func(pattern byte, memtestSize uint64, wordSize uintptr, scratchpadPointer unsafe.Pointer) step.StepExitError {
43+
for i := uint64(0); i < memtestSize; i++ {
44+
var value = *(*byte)(unsafe.Pointer(uintptr(scratchpadPointer) + (uintptr(i) * wordSize)))
45+
if value != pattern {
46+
errMsg := errors.Errorf("Wrote pattern 0x%x to index %v of test buffer, read value was 0x%x. Possible memory corruption",
47+
pattern, i, value)
48+
return step.ExitUnsafeToReboot{Err: errMsg}
49+
}
50+
}
51+
52+
return nil
53+
}
54+
55+
// How many bytes should be spared in the memtest (20M)
56+
const memtestMemoryKeepout = 20 * 1024 * 1024
57+
58+
// How small can the memtest run be to be considered valid (10M)
59+
const memtestMinimumMemoryCoverage = 10 * 1024 * 1024
60+
61+
// Perform a suite of memtest checks.
62+
func runMemtestSuite(stepParams step.StepParams) step.StepExitError {
63+
memInfo, err := utils.GetMemInfo()
64+
if err != nil {
65+
return step.ExitSafeToReboot{Err: err}
66+
}
67+
68+
freeMem := memInfo.MemFree
69+
// since memtestSize is pesimistically negative, convert temporarily to signed ints
70+
memtestSize := int64(freeMem) - int64(memtestMemoryKeepout)
71+
72+
if memtestSize < memtestMinimumMemoryCoverage {
73+
log.Printf("Only %v B memory left for memtest, which is less than the minimum size of %v B needed. Skipping memtest",
74+
memtestSize, memtestMinimumMemoryCoverage)
75+
return nil
76+
}
77+
78+
// at this point memtestSize must be positive and larger than memtestMinimumMemoryCoverage
79+
// we are free to convert back to uint64
80+
uMemtestSize := uint64(memtestSize)
81+
log.Printf("Running memtest on %v bytes", uMemtestSize)
82+
83+
// since go could probably optimize both those functions out (assuming perfectly working memory)
84+
// we need to create unsafe pointers that make sure that the writes are commited to ram
85+
// more or less C99 volatile
86+
var scratchpad = make([]byte, memtestSize)
87+
var wordSize = unsafe.Sizeof(scratchpad[0])
88+
var scratchpadPointer = unsafe.Pointer(&scratchpad[0])
89+
90+
var patterns = [4]byte{0x00, 0xff, 0x5a, 0xa5}
91+
for _, pattern := range patterns {
92+
populateMemtest(pattern, uMemtestSize, wordSize, scratchpadPointer)
93+
var err = checkMemtest(pattern, uMemtestSize, wordSize, scratchpadPointer)
94+
if err != nil {
95+
return err
96+
}
97+
}
98+
99+
return nil
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Copyright 2020-present Facebook. All Rights Reserved.
3+
*
4+
* This program file is free software; you can redistribute it and/or modify it
5+
* under the terms of the GNU General Public License as published by the
6+
* Free Software Foundation; version 2 of the License.
7+
*
8+
* This program is distributed in the hope that it will be useful, but WITHOUT
9+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11+
* for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License
14+
* along with this program in a file named COPYING; if not, write to the
15+
* Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor,
17+
* Boston, MA 02110-1301 USA
18+
*/
19+
20+
package common
21+
22+
import (
23+
"github.com/facebook/openbmc/tools/flashy/lib/step"
24+
"github.com/facebook/openbmc/tools/flashy/lib/utils"
25+
"github.com/pkg/errors"
26+
"testing"
27+
"unsafe"
28+
)
29+
30+
var mockMemInfo = &utils.MemInfo{
31+
MemTotal: 120 * 1024 * 1024,
32+
MemFree: 50 * 1024 * 1024,
33+
}
34+
35+
func TestMemtestGoodHW(t *testing.T) {
36+
memInfoOrig := utils.GetMemInfo
37+
memchekOrig := checkMemtest
38+
39+
utils.GetMemInfo = func() (*utils.MemInfo, error) {
40+
return mockMemInfo, nil
41+
}
42+
43+
got := runMemtestSuite(step.StepParams{})
44+
step.CompareTestExitErrors(nil, got, t)
45+
46+
utils.GetMemInfo = memInfoOrig
47+
checkMemtest = memchekOrig
48+
}
49+
50+
func TestMemtestBadHW(t *testing.T) {
51+
memInfoOrig := utils.GetMemInfo
52+
memchekOrig := checkMemtest
53+
54+
utils.GetMemInfo = func() (*utils.MemInfo, error) {
55+
return mockMemInfo, nil
56+
}
57+
58+
checkMemtest = func(pattern byte, memtestSize uint64, wordSize uintptr, scratchpadPointer unsafe.Pointer) step.StepExitError {
59+
*(*byte)(unsafe.Pointer(uintptr(scratchpadPointer) + (uintptr(1234) * wordSize))) = 0xbb
60+
return memchekOrig(pattern, memtestSize, wordSize, scratchpadPointer)
61+
}
62+
63+
got := runMemtestSuite(step.StepParams{})
64+
wantMsg := errors.Errorf("Wrote pattern 0x0 to index 1234 of test buffer, read value was 0xbb. Possible memory corruption")
65+
want := step.ExitUnsafeToReboot{Err: wantMsg}
66+
step.CompareTestExitErrors(want, got, t)
67+
68+
utils.GetMemInfo = memInfoOrig
69+
checkMemtest = memchekOrig
70+
}
71+
72+
func TestMemtestBadHWOnLastLoop(t *testing.T) {
73+
memInfoOrig := utils.GetMemInfo
74+
memchekOrig := checkMemtest
75+
76+
utils.GetMemInfo = func() (*utils.MemInfo, error) {
77+
return mockMemInfo, nil
78+
}
79+
80+
checkMemtest = func(pattern byte, memtestSize uint64, wordSize uintptr, scratchpadPointer unsafe.Pointer) step.StepExitError {
81+
if pattern == 0xa5 {
82+
*(*byte)(unsafe.Pointer(uintptr(scratchpadPointer) + (uintptr(1234) * wordSize))) = 0xbb
83+
}
84+
return memchekOrig(pattern, memtestSize, wordSize, scratchpadPointer)
85+
}
86+
87+
got := runMemtestSuite(step.StepParams{})
88+
wantMsg := errors.Errorf("Wrote pattern 0xa5 to index 1234 of test buffer, read value was 0xbb. Possible memory corruption")
89+
want := step.ExitUnsafeToReboot{Err: wantMsg}
90+
step.CompareTestExitErrors(want, got, t)
91+
92+
utils.GetMemInfo = memInfoOrig
93+
checkMemtest = memchekOrig
94+
}
95+
96+
func TestMemtestNoMemory(t *testing.T) {
97+
memInfoOrig := utils.GetMemInfo
98+
memchekOrig := checkMemtest
99+
100+
utils.GetMemInfo = func() (*utils.MemInfo, error) {
101+
return &utils.MemInfo{
102+
MemTotal: 120 * 1024 * 1024,
103+
MemFree: 2 * 1024 * 1024,
104+
}, nil
105+
}
106+
107+
got := runMemtestSuite(step.StepParams{})
108+
step.CompareTestExitErrors(nil, got, t)
109+
110+
utils.GetMemInfo = memInfoOrig
111+
checkMemtest = memchekOrig
112+
}

0 commit comments

Comments
 (0)