内核 API 获取物理 RAM 偏移量

Kernel API to get Physical RAM Offset

我正在编写一个设备驱动程序(用于 Linux 内核 2.6.x),它使用物理地址直接与物理 RAM 交互。对于我设备的内存布局(根据 cat /proc/iomem 的输出),系统 RAM 从物理地址 0x80000000 开始;但是,此代码可能 运行 在具有不同内存布局的其他设备上,因此我不想对该偏移量进行硬编码。

我的设备驱动程序中是否有函数、宏或常量可以为我提供系统 RAM 第一个字节的物理地址?

RAM 内存映射基于与每个 SOC 或处理器相同的基础。 Vendor usually provide their memory mapping related documents to the user. 可能您需要参考您的处理器数据表相关文档。 正如您所说,内存是硬编码的。在大多数情况下,内存映射是通过设备树或 SDRAM 驱动程序本身进行硬编码的。

Is there a function, macro, or constant which I can use from within my device driver that gives me the physical address of the first byte of System RAM?

没关系,因为你问的是XY question
您不应该在设备 driver.
中寻找或尝试使用 "first byte of System RAM" driver 只需要知道其寄存器块的地址(和长度)(这就是 "memory" 的用途,不是吗?)。

在 2.6 内核中(即在设备树之前),此信息通常通过 board[=] 中的 struct resourcestruct platform_device 定义传递给 drivers 83=]_devices.c 文件。

struct resource中的IORESOURCE_MEM属性是将设备的内存块起始地址和结束地址传递给设备driver的机制。
起始地址通常是硬编码的,直接取自 SoC 数据表或电路板的内存映射。
如果您更改 SoC,则需要新的开发板文件。

例如,下面是来自 arch/arm/mach-at91/at91rm9200_devices.c 的代码,用于为评估板配置和设置 MMC 设备(AT91RM9200_BASE_MCI 是该设备寄存器块的物理内存地址):

#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
static u64 mmc_dmamask = DMA_BIT_MASK(32);
static struct at91_mmc_data mmc_data;

static struct resource mmc_resources[] = {
    [0] = {
        .start  = AT91RM9200_BASE_MCI,
        .end    = AT91RM9200_BASE_MCI + SZ_16K - 1,
        .flags  = IORESOURCE_MEM,
    },
    [1] = {
        .start  = AT91RM9200_ID_MCI,
        .end    = AT91RM9200_ID_MCI,
        .flags  = IORESOURCE_IRQ,
    },
};

static struct platform_device at91rm9200_mmc_device = {
    .name       = "at91_mci",
    .id     = -1,
    .dev        = {
                .dma_mask       = &mmc_dmamask,
                .coherent_dma_mask  = DMA_BIT_MASK(32),
                .platform_data      = &mmc_data,
    },
    .resource   = mmc_resources,
    .num_resources  = ARRAY_SIZE(mmc_resources),
};

void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
{
    if (!data)
        return;

    /* input/irq */
    if (data->det_pin) {
        at91_set_gpio_input(data->det_pin, 1);
        at91_set_deglitch(data->det_pin, 1);
    }
    if (data->wp_pin)
        at91_set_gpio_input(data->wp_pin, 1);
    if (data->vcc_pin)
        at91_set_gpio_output(data->vcc_pin, 0);

    /* CLK */
    at91_set_A_periph(AT91_PIN_PA27, 0);

    if (data->slot_b) {
        /* CMD */
        at91_set_B_periph(AT91_PIN_PA8, 1);

        /* DAT0, maybe DAT1..DAT3 */
        at91_set_B_periph(AT91_PIN_PA9, 1);
        if (data->wire4) {
            at91_set_B_periph(AT91_PIN_PA10, 1);
            at91_set_B_periph(AT91_PIN_PA11, 1);
            at91_set_B_periph(AT91_PIN_PA12, 1);
        }
    } else {
        /* CMD */
        at91_set_A_periph(AT91_PIN_PA28, 1);

        /* DAT0, maybe DAT1..DAT3 */
        at91_set_A_periph(AT91_PIN_PA29, 1);
        if (data->wire4) {
            at91_set_B_periph(AT91_PIN_PB3, 1);
            at91_set_B_periph(AT91_PIN_PB4, 1);
            at91_set_B_periph(AT91_PIN_PB5, 1);
        }
    }

    mmc_data = *data;
    platform_device_register(&at91rm9200_mmc_device);
}
#else
void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
#endif

附录

i'm still not seeing how this is an xy question.

我认为这是一个 XY 问题,因为:

  • 你把"System RAM"和物理内存地址space.
    混为一谈 "RAM" 将是存在于地址 space.
    中的实际 (readable/writable) 内存 "System memory"是Linux内核管理的RAM(参考your previous question)。
    外围设备可以在(物理)内存地址space中有寄存器and/or设备内存,但这不应该被称为"System RAM"

  • 您没有提供任何背景说明您的 driver "interacts directly with physical RAM using physical addresses." 与其他 Linux drivers.

  • 您假设某个函数是您的 driver 的解决方案,但您不知道该函数的名称。这是 XY 问题的原型。


can't i call a function like get_platform_device (which i just made up) to get the struct platform_device and then find the struct resource that represents System RAM?

设备 driver 将调用 platform_get_resource()(在其探测函数中)以检索其在电路板文件中定义的 struct resource
继续上面开始的示例,driver 的探测例程具有:

static int __init at91_mci_probe(struct platform_device *pdev)
{
    struct mmc_host *mmc;
    struct at91mci_host *host;
    struct resource *res;
    int ret;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res)
        return -ENXIO;

    if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME))
        return -EBUSY;

...  

    /*
     * Map I/O region
     */
    host->baseaddr = ioremap(res->start, resource_size(res));
    if (!host->baseaddr) {
        ret = -ENOMEM;
        goto fail1;
    }

that would allow me to write code that can always access the nth byte of RAM, without assumptions of how RAM is arranged in relation to other parts of memory.

这看起来像是一个安全漏洞或潜在的错误。
我挑战你在使用 "physical address of the first byte of System RAM".

的主线 Linux 内核中找到一个 driver

您的标题是"Kernel API to get Physical RAM Offset".
您正在查找的 API 似乎是 struct resource.

你想做的事情似乎与 Linux 内核约定背道而驰。为了系统的完整性和安全性,drivers 不要尝试访问 any/every 部分内存。
driver 将请求并可以独占访问其寄存器 and/or 设备内存的地址 space。
内核管理下的所有 RAM 只能通过 well-defined 约定访问,例如 copy_to_user() 或 DMA API.
的缓冲区地址 设备 driver 根本无法自由访问它选择的任何内存部分。
一旦 driver 被内核启动,它就绝对无法忽视 "assumptions of how RAM is arranged".

也许您可以查看 memblock 结构和 memblock_start_of_DRAM()。

/sys/kernel/debug/memblock/memory表示内存库。

0: 0x0000000940000000..0x0000000957bb5fff 
RAM bank 0: 0x0000000940000000 0x0000000017bb6000

1: 0x0000000980000000..0x00000009ffffffff 
RAM bank 1: 0x0000000980000000 0x0000000080000000