退出 DRAM 中的 Linux 到 SRAM 中的 运行 裸机代码

Exit Linux in DRAM to run bare-metal code in SRAM

没有细节的问题:

是否可以从 Linux、运行ning 从 DDR 复制裸机可执行文件到处理器内部 SRAM 并 运行 它?此应用程序将暂停 DDR 并禁用电源轨以修复硬件问题。

详情:

我正在开发使用 ARM 处理器和 运行ning 嵌入式 Linux 的定制嵌入式产品。处理器是 Atmel ATSAMA5D36。我们正在使用以下辅助引导加载程序和内核:

目前的开机流程大概是这样:

设备以两种模式运行:连接到主电源或由电池备份。当连接到主​​电源时,可以发出暂停 linux 的命令,这会将我们的 LPDDR 内存置于自刷新模式。一旦 LPDDR 处于自刷新状态,就可以移除主电源,并且 LPDDR 将持续到下一个上电周期。板上有一个小型辅助 ARM Cortex-M0 微控制器,运行 一直在执行 IO 处理和其他一些与时间相关的任务。

问题:

当主电源从电路板上移除时,电池切换为 LPDDR、辅助 ARM 微控制器供电,并且主 SAMA5 电源关闭。当 SAMA5 断电且电源电容放电时,处理器会短暂 (100uS) 掉电并强制 IO 进入复位状态,然后再永久关闭。不幸的是,电源中的 "blip" 将 LPDDR CKE 线拉高到足以使 LPDDR 退出自刷新模式。这会导致 LPDDR 上的内存损坏。

显示主电源电压和 DDR_CKE 信号的示波器图:

要解决此问题,我们需要暂停 LPDDR,然后告诉 PMIC 禁用处理器上的 DDR_IO 电源(DDR 的 IO 电源和主电源是独立的稳压器)。这将防止使 LPDDR 退出自刷新的电源故障。不幸的是,这是先有鸡还是先有蛋的问题。如果我们暂停 LPDDR,我们将无法 运行 任何代码来与 PMIC 通信并禁用特定电源。如果我们禁用电源,我们将无法再与 LPDDR 通信以使其进入自刷新状态。

当前解决方法:

当发出暂停主处理器的命令时,它会将其转发给辅助微控制器,然后暂停 LPDDR 以进行自我刷新。辅助微控制器然后重置主处理器并等待辅助引导加载程序启动。当引导加载程序启动时,它会检查微控制器以查看是否发出了暂停。如果是,它将 LPDDR 置于自刷新状态,告诉 PMIC(通过 I2C)禁用 DDR_IO 电源,并等待一段时间 (1) 等待电源被移除。

问题在于启动时间——从重置到 DDR 初始化和挂起需要 120 毫秒。 LPDDR 的刷新周期为 16-64 毫秒,因此我们至少缺少一个 DDR 的刷新周期。到目前为止,在测试中我们还没有看到由于这种延迟而导致的内存损坏,但这显然不是一个理想的解决方案(但比硬件修订版要好)。

Is it possible to copy a bare-metal executable from Linux, running from DDR, into the processor internal SRAM and run it?

这从非常困难到不可能,具体取决于您的平台。 Linux 内核本身从 DRAM 运行,所以当 DRAM 关闭时,您将没有实时 linux 内核,也没有任何 OS 服务(没有系统调用,没有调度程序,没有 irqs/irq处理程序;如果它在 dram 中,则没有 irq 向量?)。

您的芯片只有 128 KB 的 SRAM:http://www.atmel.com/devices/ATsama5d36.aspx?tab=parameters - 包含完整的 Linux 内核太小了。

因此,您可以尝试创建一些可执行文件,将其优先级更改为 RT(实时)以便能够忽略 scheduler/irqs...您应该将此可执行文件完全放入 SRAM(仅当 SRAM可以用作直接寻址内存)...您还应该检查它是否会在需要时加载(或者您是否应该使可执行文件始终处于 运行 核心?它是多核芯片吗?)

将它写成不是可执行文件,而是作为内核的一部分可能会更容易一些……任务看起来仍然是不可能的……

DRAM 和 CPU 运行 在 DRAM 关闭之前的时间是多少?

DRAM 可能会在多个刷新周期内保存数据;它将保存大部分数据几秒钟(冷却时更长 - https://en.wikipedia.org/wiki/Cold_boot_attack;热时更短)。您可能想要添加一些数据校验和...(您的芯片没有用于 DRAM 的 ECC?)

刚转回来回答这个问题。实际上,可以将代码从 DRAM 加载到 SRAM 中并执行。经过进一步调查,我们发现许多嵌入式平台使用这种方法来处理电源管理功能,尤其是 suspend/resume -- 与我在这里试图解决的问题完全一样。

以下是 linux 在 at91 平台上处理进入挂起的方式:http://lxr.free-electrons.com/source/arch/arm/mach-at91/pm.c#L416

我们需要对此进行自定义实现,并最终创建了一个使用相同方法的内核模块。然后我们从用户空间插入内核模块,它为我们提供了一种从 SRAM 运行 代码执行我们的电源管理功能的方法。

示例内核模块代码:

#include <linux/io.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#define VIRT_BASE 0xFEF78000 //for AT91

static void (*sram_fn)(void) = NULL;

unsigned char buf[] = {
    //your complied ARM byte code
};

static void __init init_sram_fn (void)
{
    sram_fn = (void *) (VIRT_BASE – sizeof(buf));
    memcpy(sram_fn, &buf, sizeof(buf));
    sram_fn();
}

static void __exit cleanup_sram_fn (void){}

module_init(init_sram_fn);
module_exit(cleanup_sram_fn);

来自用户空间的调用

system("insmod custom_pm_module.ko");