Skip to content
Advertisement

Kernel API to get Physical RAM Offset

I’m writing a device driver (for Linux kernel 2.6.x) that interacts directly with physical RAM using physical addresses. For my device’s memory layout (according to the output of cat /proc/iomem), System RAM begins at physical address 0x80000000; however, this code may run on other devices with different memory layouts so I don’t want to hard-code that offset.

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?

Advertisement

Answer

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?

It doesn’t matter, because you’re asking an XY question.
You should not be looking for or trying to use the “first byte of System RAM” in a device driver.
The driver only needs knowledge of the address (and length) of its register block (that is what this “memory” is for, isn’t it?).

In 2.6 kernels (i.e. before Device Tree), this information was typically passed to drivers through struct resource and struct platform_device definitions in a board_devices.c file.

The IORESOURCE_MEM property in the struct resource is the mechanism to pass the device’s memory block start and end addresses to the device driver.
The start address is typically hardcoded, and taken straight from the SoC datasheet or the board’s memory map.
If you change the SoC, then you need new board file(s).

As an example, here’s code from arch/arm/mach-at91/at91rm9200_devices.c to configure and setup the MMC devices for a eval board (AT91RM9200_BASE_MCI is the physical memory address of this device’s register block):

#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

ADDENDUM

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

I consider it an XY question because:

  • You conflate “System RAM” with physical memory address space.
    “RAM” would be actual (readable/writable) memory that exists in the address space.
    “System memory” is the RAM that the Linux kernel manages (refer to your previous question).
    Peripherals can have registers and/or device memory in (physical) memory address space, but this should not be called “System RAM”.

  • You have not provided any background on how or why your driver “interacts directly with physical RAM using physical addresses.” in a manner that is different from other Linux drivers.

  • You presume that a certain function is the solution for your driver, but you don’t know the name of that function. That’s a prototype for an XY question.


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?

The device driver would call platform_get_resource() (in its probe function) to retrieve its struct resource that was defined in the board file.
To continue the example started above, the driver’s probe routune has:

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.

That reads like a security hole or a potential bug.
I challenge you to to find a driver in the mainline Linux kernel that uses the “physical address of the first byte of System RAM”.

Your title is “Kernel API to get Physical RAM Offset”.
The API you are looking would seem to be the struct resource.

What you want to do seems to fly in the face of Linux kernel conventions. For the integrity and security of the system, drivers do not try to access any/every part of memory.
The driver will request and can be given exclusive access to the address space of its registers and/or device memory.
All RAM under kernel management is only accessed through well-defined conventions, such as buffer addresses for copy_to_user() or the DMA API.
A device driver simply does not have free reign to access any part of memory it chooses.
Once a driver is started by the kernel, there is absolutely no way it can disregard “assumptions of how RAM is arranged”.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement