退出 DRAM 中的 Linux 到 SRAM 中的 运行 裸机代码
Exit Linux in DRAM to run bare-metal code in SRAM
没有细节的问题:
是否可以从 Linux、运行ning 从 DDR 复制裸机可执行文件到处理器内部 SRAM 并 运行 它?此应用程序将暂停 DDR 并禁用电源轨以修复硬件问题。
详情:
我正在开发使用 ARM 处理器和 运行ning 嵌入式 Linux 的定制嵌入式产品。处理器是 Atmel ATSAMA5D36。我们正在使用以下辅助引导加载程序和内核:
目前的开机流程大概是这样:
- Atmel 有一个 ROM 引导加载程序,可以在 NOR 闪存上找到我们的引导加载程序并将其复制到 SAMA5 的内部 SRAM
- 二级引导加载程序从 LPDDR 中的 NOR 闪存初始化硬件和copies/decompresses我们的内核
- 我们跳入内核,运行linux跳出LPDDR
设备以两种模式运行:连接到主电源或由电池备份。当连接到主电源时,可以发出暂停 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");
没有细节的问题:
是否可以从 Linux、运行ning 从 DDR 复制裸机可执行文件到处理器内部 SRAM 并 运行 它?此应用程序将暂停 DDR 并禁用电源轨以修复硬件问题。
详情:
我正在开发使用 ARM 处理器和 运行ning 嵌入式 Linux 的定制嵌入式产品。处理器是 Atmel ATSAMA5D36。我们正在使用以下辅助引导加载程序和内核:
目前的开机流程大概是这样:
- Atmel 有一个 ROM 引导加载程序,可以在 NOR 闪存上找到我们的引导加载程序并将其复制到 SAMA5 的内部 SRAM
- 二级引导加载程序从 LPDDR 中的 NOR 闪存初始化硬件和copies/decompresses我们的内核
- 我们跳入内核,运行linux跳出LPDDR
设备以两种模式运行:连接到主电源或由电池备份。当连接到主电源时,可以发出暂停 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");