13

I'm sorry if this is poorly worded, but I've done a memory test on one of my computers, and certain memory addresses have errors. This is the first time I've searched. If you need more information, then I can provide it.

4 Answers 4

12

If you look in /etc/default/grub, you'll find a GRUB_BADRAM= parameter where you can identify what bad memory locations there are.

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"

After setting the addresses, run sudo update-grub to apply.

Note: That GRUB_BADRAM doesn't work with Kernel Lockdown (which would be enabled if you have Secure Boot). You can check if you have Secure Boot enabled by running mokutil --sb-state.


Taken from https://help.ubuntu.com/community/BadRAM#BADRAM_setting_in_Grub2 ...

BADRAM setting in Grub2

The GRUB2 config file in Natty has a line for configuring kernel bad ram exclusions. So, I will assume that is the preferred way of mapping out a section of memory that is showing errors. The line I set was

GRUB_BADRAM="0x7DDF0000,0xffffc000" 

The suggested way on every web site I could find was to set this was to run memtest86 and let it show you the BadRAM settings. memtest86 gave me a page of stuff I would have had to enter. I could see that all the addresses were in one 16K block, so I just wanted to map that 16K block out of action. Here is how I generated the correct entry.

The first parameter is easy. That is the base address of the bad memory. In my case, I could see that all the bad addresses were greater than 0x7DDF0000 and less than 0x7DDF4000. So, I took the beginning of the 16K block as my starting address.

The second parameter is a mask. You put 1s where the address range you want shares the same values and 0s where it will vary. This means you need to pick your address range such that only the low order bits vary. Looking at my address, the first part of the mask is easy. You want to start with 0xffff. For the next nibble, I will explain with bit maps. I want to range from 0000 to 0011. So, the mask for badram would be 1100 or a hex c. The last 3 nibbles need to be all 0s in the mask, since we want the entire range mapped out. So, we get a total result of 0xffffc000.

After setting this line in /etc/default/grub, I ran sudo update-grub and rebooted and my bad memory was no longer being used. No kernel patches are needed to map out bad memory using this method.

You can check whether the ranges provided are correct by running dmesg and looking for BIOS-provided physical RAM map or extended physical RAM map:

[    0.000000] BIOS-e820: [mem 0x000000022a898000-0x000000022a89bfff] unusable

You can also check /proc/iomem as root to see the unusable ranges.


Of course, the best action plan would be to replace the defective RAM.

1
  • Thank you, this worked very nicely. It fixed the problem.
    – j3d1H
    May 25, 2017 at 22:34
10

I found the easiest and most robust way for me is to add a kernel parameter memtest=4 to my GRUB config. This adds a couple of seconds to boot-up where the kernel checks your ram and then marks them as bad for you.

  1. sudo nano /etc/default/grub

  2. update this line:

    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash memtest=4"
    
  3. sudo update-grub

  4. reboot

  5. Optionally, check that it is working by running dmesg and see logs like this:

    [    5.043917]   aaaaaaaaaaaaaaaa bad mem addr 0x0000000581a90000 - 0x0000000581a90010 reserved
    

Source.

2
  • thank the badmem authors instead ;)
    – allanlaal
    Jul 16, 2021 at 10:59
  • I still say thank God that I could figure this out since all the walk-in-shops were closed because of covid..
    – AmanicA
    Sep 15, 2022 at 7:42
1

heynnema's answer doesn't work if you have kernel lockdown enabled (which is enabled by default if you are using Secure Boot).

AmanicA's answer doesn't really scale: it will run the test on each boot, slowing down your boot times.

You can use the kernel cmdline parameter memmap to reserve certain regions of physical memory so they are not used.

See kernel parameters for details.

For example, memtest86+ and the linux memtest found that address 0x22a89a128 was faulty on my machine.

So I added the following line to my kernel cmdline to disable 8KiB of ram around the faulty ram page:

memmap=16K$0x22a898000

Which reserves (this is what the $ is for) 16 KiB of memory starting at physical address 0x22a898000.

I did this by editing /etc/default/grub and adding memmap=16K\\\$0x22a898000 at the end of the GRUB_CMDLINE_LINUX_DEFAULT variable in that file, and then running sudo update-grub.

The \\\ is because grub does some weird character escaping.

You can verify that this works by looking for the reserved regions in either dmesg or by opening /proc/iomem as root:

# grep 22a89 /proc/iomem
100000000 - 22a897fff : System RAM
22a898000 - 22a89bfff : Reserved
22a89c000 - 4807fffff : System RAM
0

To follow on to @heynnema's answer, I wrote a small python script to compute what the mask should be. I wasn't sure of what the proper way to find what the position of the leftmost significant 0 was with bin operations, so I hacked it with a string-based function. But it did work for finding what the mask should be programatically:

def find_grub_badram_format(start, stop):
    """
    References:
        https://askubuntu.com/questions/908925/how-do-i-tell-ubuntu-not-to-use-certain-memory-addresses

    Example:
        start = 0x7DDF0000
        stop  = 0x7DDF4000 - 1
    """
    import numpy as np
    nbytes = 64
    dtype = np.uint64
    # Starting bad mem address range
    start = dtype(start)
    # Subtract 1 because to make the stop range inclusive instead of exclusive
    stop  = dtype(stop)

    def xnor(a, b):
        return ~(a ^ b)

    def find_pos_of_leftmost_sig_zero(a, nbytes=64):
        """
        Given a bit string like, we find the position of the first zero bit we
        see coming from the left, such that we can right-bitshift to it to the

            011111110110110
             ^      ^

        Example:
            >>> nbytes = 32
            >>> vals = [0b0, 0b1, 0b1111011, 0b1001]
            >>> for a in vals:
            >>>     print('-----')
            >>>     pos0 = find_pos_of_leftmost_sig_one_and_zero(a, nbytes)
            >>>     print(f'a = {a:032b}')
            >>>     print('    ' + ' ' * (nbytes - pos0 - 1) + '^')
            -----
            a = 00000000000000000000000000000000
                                               ^
            -----
            a = 00000000000000000000000000000001
                                               ^
            -----
            a = 00000000000000000000000001111011
                                             ^
            -----
            a = 00000000000000000000000000001001
                                             ^
        """
        # not really a fast op, but it works well enough There is a semi-corner
        # case at 1 and 0, but it doesnt change how we use it
        binstr = ('{:0' + str(nbytes) + 'b}').format(a)
        try:
            leftmost_one = binstr.index('1')
        except ValueError:
            return 0
        try:
            leftmost_sig_zero = binstr[leftmost_one:].index('0') + leftmost_one
        except Exception:
            return 0
        # Flip string indexes to bit indexes
        sig_zero = nbytes - (leftmost_sig_zero + 1)
        return sig_zero

    def find_first_sig_one(a, nbytes=64):
        binstr = ('{:0' + str(nbytes) + 'b}').format(a)
        leftmost_one = binstr.index('1')
        sig_one = nbytes - (leftmost_one + 1)
        return sig_one


    # Find all the bits in common
    common = xnor(start, stop)

    # Find the position of the first zero (non-common bit) we see from the left
    shift0 = find_pos_of_leftmost_sig_zero(common, nbytes) + 1

    # Find the number of significant bits in the stop position
    shift1 = find_first_sig_one(stop, nbytes) + 1


    shift0 = dtype(shift0)
    shift1 = dtype(shift1)

    head_mask = (dtype(1) << shift1) - dtype(1)
    tail_mask = ~((dtype(1) << shift0) - dtype(1))
    mask = head_mask & tail_mask

    print(f'start  = 0b{start:064b} = 0x{start:016x}')
    print(f'stop   = 0b{stop:064b} = 0x{stop:016x}')
    print(f'common = 0b{common:064b} = 0x{common:016x}')
    print(f'mask   = 0b{mask:064b} = 0x{mask:016x}')

    print('--')
    print(f'mask = ' + hex(mask))

    badram_format = f'{start:#0{18}x},{mask:#0{18}x}'
    print(badram_format)
    badram_format = f'{start:#x},{mask:#x}'
    print(badram_format)

This results in

start  = 0b0000000000000000000000000000000001111101110111110000000000000000 = 0x000000007ddf0000
stop   = 0b0000000000000000000000000000000001111101110111110011111111111111 = 0x000000007ddf3fff
common = 0b1111111111111111111111111111111111111111111111111100000000000000 = 0xffffffffffffc000
mask   = 0b0000000000000000000000000000000001111111111111111100000000000000 = 0x000000007fffc000
--
mask = 0x7fffc000
0x000000007ddf0000,0x000000007fffc000
0x7ddf0000,0x7fffc000

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .