User:Knuxfanwin8/Enabling pstore and ramoops

From postmarketOS
🚧 This page is a work-in-progress. Some information contained within may be inaccurate or incomplete.

If you don't have a serial cable, and your device's downstream kernel does not support the android ramconsole driver, you might need to set up pstore and ramoops instead.

Information

pstore

pstore is a persistent storage driver. It saves data in a reserved part of memory, which can then be read from a working kernel. Its primary purpose is to save kernel crash logs to memory.

ramoops

From the documentation:

Ramoops is an oops/panic logger that writes its logs to RAM before the system crashes. It works by logging oopses and panics in a circular buffer. Ramoops needs a system with persistent RAM so that the content of that area can survive after a restart.

pstore/ramoops setup

Note This setup has to be done on both the downstream and mainline kernel.


Kernel configuration

To enable pstore and use the ramoops backend, make sure the following kernel options are set:

CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_RAM=y

They can be found under File systems -> Miscellaneous filesystems -> Persistent store support.

On older kernels without CONFIG_PSTORE_CONSOLE and CONFIG_PSTORE_RAM, you'll need to enable

CONFIG_RAMOOPS=y

(found under Device drivers -> Character devices -> Log panic/oops to a RAM buffer) instead of CONFIG_PSTORE_RAM. Note that this will only allow you to preview kernel oopses/panics. If you need to get full kernel logs, you can either set up the ram_console driver (see "Android ram_console driver setup" section below) or backport CONFIG_PSTORE_RAM (see patches here).

Configuring ramoops

Follow the guide in the ramoops documentation (you only need to do one of the steps). Make sure that the memory address you reserve and its size is the same in your downstream and mainline configuration, or else you won't be able to read the information.

Note Note: the documentation for the cmdline settings doesn't mention the fact that you have to set ramoops.mem_size as well. This value contains the size, so it should be set to something like 0x00004000 (that's 16kb).


Reading from pstore

Now you can mount the pstore partition:

mkdir /tmp/pstore
mount -t pstore -o kmsg_bytes=16000 - /tmp/pstore

Android ram_console driver setup

For devices with older downstream kernels without PSTORE_CONSOLE (kernels below v3.6(-rc1)) it may be easier to set up the ram_console driver.

Note This setup has to be done on both the downstream and mainline kernel.


Backporting the driver

Copy the driver from the p4wifi tree.

Kernel configuration

Make sure CONFIG_ANDROID_RAM_CONSOLE is set to =y.

Configuring ram_console

This process will differ depending on your downstream kernel version.

If you backported the driver from p4wifi or your downstream kernel had the same driver

If you didn't have the ram_console driver because the kernel was too new (>3.6) and you copied it from the p4wifi tree, or if your downstream kernel was old enough to contain that version of the driver, follow the steps below.

In downstream, define the ram_console platform device:

static struct resource ram_console_resources[] = {
	{
		.flags = IORESOURCE_MEM,
	},
};

static struct platform_device ram_console_device = {
	.name 		= "ram_console",
	.id 		= -1,
	.num_resources	= ARRAY_SIZE(ram_console_resources),
	.resource	= ram_console_resources,
};
void __init ram_console_debug_init_mem(unsigned long start, unsigned long size)
{
	struct resource *res;

	res = platform_get_resource(&ram_console_device, IORESOURCE_MEM, 0);
	if (!res)
		goto fail;
	res->start = start;
	res->end = res->start + size - 1;

	return;

fail:
	ram_console_device.resource = NULL;
	ram_console_device.num_resources = 0;
	pr_err("Failed to reserve memory block for ram console\n");
}

void __init ram_console_debug_init(void)
{
	int err;

	err = platform_device_register(&ram_console_device);
	if (err) {
		pr_err("%s: ram console registration failed (%d)!\n", __func__, err);
	}
}

Then, find the init function of your device (usually near the end of the file) and add the following lines:

ram_console_debug_init_mem(0x2E600000, 0x00100000);
ram_console_debug_init();

Where 0x2E600000 is the base address and 0x00100000 is the size of the buffer.

In upstream, reserve memory in the device tree:

	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		android_logger: ram-console@2E600000 {
			reg = <0x2E600000 0x00100000>;
			no-map;
		}
	}

Then in your board.c file (could be your chipset's, but only temporarily) define the ram_console platform device as outlined above.

If your board.c file does not have an init function, add one:

void __init chipset_dt_early_init (void)
{
	...
}
DT_MACHINE_START(...)
	.init_early = chipset_dt_early_init
Icon TODO: Is that correct?

If you had separate ram_console and persistent_ram drivers

The setup process is different for kernels with separate persistent_ram and ram_console drivers. This applies to kernel 3.4.

Icon TODO: What other versions does this apply to?

In downstream, add the following to the includes of your device's board.c file (skip the ones you have already):

#include <linux/platform_device.h>
#ifdef CONFIG_ANDROID_PERSISTENT_RAM
#include <linux/persistent_ram.h>
#endif

Then define the initialization functions:

#ifdef CONFIG_ANDROID_RAM_CONSOLE
#define RAM_CONSOLE_SIZE 0x100000
static struct platform_device ram_console_device = {
    .name = "ram_console",
    .id = -1,
};
#endif

#ifdef CONFIG_ANDROID_PERSISTENT_RAM
#define PERSISTENT_RAM_SIZE 0x100000
static struct persistent_ram_descriptor pram_descs[] = {
#ifdef CONFIG_ANDROID_RAM_CONSOLE
    {
        .name = "ram_console",
        .size = RAM_CONSOLE_SIZE,
    },
#endif
};

static struct persistent_ram persist_ram = {
    .size = PERSISTENT_RAM_SIZE,
    .num_descs = ARRAY_SIZE(pram_descs),
    .descs = pram_descs,
};

static void __init add_persist_ram_devices(void)
{
    int ret;
    persist_ram.start = 0x2A600000;

    pr_info("PERSIST RAM CONSOLE START ADDR : 0x%x\n",
            persist_ram.start);

    ret = persistent_ram_early_init(&persist_ram);
    if (ret)
        pr_err("%s: failed to initialize persistent ram\n", __func__);
}
#endif /* CONFIG_ANDROID_PERSISTENT_RAM */

Replace 0x100000 with the ram console size, and 0x2A600000 with the address at which you want to reserve the ram console.

Then, find your reserve function (look for .reserve = ... around the end of the board file). If it doesn't exist, define it and add it.

To your reserve function, add the following:

#if defined(CONFIG_ANDROID_PERSISTENT_RAM)
    add_persist_ram_devices();
#endif

Then add the following to your init function:

#ifdef CONFIG_ANDROID_RAM_CONSOLE
    platform_device_register(&ram_console_device);
#endif

Then follow the upstream setup section from the previous section.

Icon TODO: Does this work?

Reading from the logs

In your downstream kernel, do:

cat /proc/last_kmsg

Troubleshooting

I set my ramoops parameters in my kernel cmdline and it doesn't work!

If you added the ramoops parameters to CONFIG_CMDLINE and they seem to be ignored, and there's a similar message in the dmesg:

~ # dmesg | grep ramoops
<6>[    3.009338] C0 [      swapper/0] ramoops: platform device not found, using module parameters
<3>[    3.009735] C0 [      swapper/0] ramoops: The memory size and the record size must be non-zero
<4>[    3.009765] C0 [      swapper/0] ramoops: probe of ramoops failed with error -22

check if you don't have CONFIG_CMDLINE_FROM_BOOTLOADER set. If you do, set CONFIG_CMDLINE_EXTEND to =y.

You're usually able to check the used cmdline at the beggining of the dmesg.

~ # dmesg | grep "Kernel command line"
<5>[    0.000000] C0 [        swapper] Kernel command line: ...

See also