0

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?

Woodrow Barlow
  • 8,477
  • 3
  • 48
  • 86
  • *"I've found I need to add an offset to get past the PCI/serial/etc. portions of memory and reach into RAM itself."* -- This needs clarification. *"Is there a more portable way"* -- Seems like you're overlooking [this answer](http://stackoverflow.com/questions/11580285/pass-large-amount-of-binary-data-from-u-boot-to-linux-kernel/12137511#12137511) that I mentioned before in your other question. – sawdust Apr 04 '17 at 01:58
  • @sawdust maybe i added to much noise to this post. in this question, i'm simply asking "is there a function or constant i can use from within my device driver which returns the address of the first byte of system RAM?". – Woodrow Barlow Apr 04 '17 at 17:06
  • @sawdust this question isn't about how to reserve the memory or what ioremap does. maybe i should edit that noise out of the question. – Woodrow Barlow Apr 04 '17 at 17:10

3 Answers3

3

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".

Community
  • 1
  • 1
sawdust
  • 16,103
  • 3
  • 40
  • 50
  • i'm sorry, i'm still not seeing how this is an xy question. if someone loads my driver in their kernel, their kernel already has the correct board files, and has already called `platform_device_register`. 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? – Woodrow Barlow Apr 04 '17 at 20:48
  • 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. – Woodrow Barlow Apr 04 '17 at 20:51
  • my driver isn't meant to be a generic interface for registers, it's an interface for accessing a specific part of RAM. so it makes sense, from a design standpoint, to give it an address within RAM as a hard-coded value in the code but it does not make sense to hard-code the position of RAM in relation to the rest of memory. – Woodrow Barlow Apr 04 '17 at 21:35
  • i'm open to the possibility that i'm thinking about this in a flawed way, i just don't understand what is flawed about it. – Woodrow Barlow Apr 04 '17 at 21:37
  • *"i just don't understand what is flawed about it"* -- Since you've described what you want to do in such vague terms, It's hard to guess. See my update. BTW once your driver is started by the kernel, there is absolutely no way it can disregard *"assumptions of how RAM is arranged"*. – sawdust Apr 04 '17 at 22:01
  • @WoodrowBarlow Are you trying to do DMA from your device driver? There are kernel APIs for this kind of stuff. I agree it's a bit of an X-Y problem since you're fairly vague on why you need to access specific parts of RAM. What kind of device are you trying to implement a driver for? – tangrs Apr 05 '17 at 01:10
  • i'm reserving a chunk of ram, and the device driver just treats it as a circular buffer and exposes it as a "file" (/dev/foo). needs to always be at the same RAM address so that if you do a warm reboot the file will keep its contents. we use this for logging, since the only persistent storage media we have can't handle heavy i/o. – Woodrow Barlow Apr 05 '17 at 15:46
  • so the device has an nvram variable set that tells the driver what address range it wants to use. i've been using addresses within RAM for this, e.g., the "startaddr" variable would contain `0xFF00000` to use the last megabyte of 256M RAM; however, after the good points you've raised, i think i'll change that to include the `0x80000000` offset right in the nvram variable. – Woodrow Barlow Apr 05 '17 at 15:49
0

RAM memory mapping is based identical to each SOC or processor. Vendor usually provide their memory mapping related documents to the user. Probably you need to refer your processor datasheet related document. As you said memory is hard-coded. In most cases memory mapping is hard-coded over device-tree or in SDRAM driver itself.

  • i know it's different for every SoC; that's the problem, since my code might run on various chips. i want to be able to write code that refers to, e.g., "the zeroeth byte of RAM" without hard-coding the starting address of RAM. the kernel obviously knows what the starting address of RAM is, so is there a function or constant i can use to get this information from the kernel? – Woodrow Barlow Apr 04 '17 at 17:04
  • One such easy way to solve your requirement is to use parameters to your driver module as below 'insmod mymodule.ko Address=0x80000000` – Arulpandiyan Vadivel Apr 04 '17 at 17:13
  • i realize that, but that's just hard-coding it in a different place. the kernel has the information, i'm just looking for a pre-existing way of requesting it at runtime. if such a thing doesn't exist, *then* i'll look into things like driver parameters. – Woodrow Barlow Apr 04 '17 at 17:21
  • Not sure how early in boot you are: https://en.wikipedia.org/wiki/E820 Normally this is used by OSes in early boot. Note that I've never done driver development, so using this might be verboten. – ruthafjord Apr 04 '17 at 17:59
0

enter image description here

Maybe you could look into memblock struct and memblock_start_of_DRAM().

/sys/kernel/debug/memblock/memory represent memory banks.

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

1: 0x0000000980000000..0x00000009ffffffff 
RAM bank 1: 0x0000000980000000 0x0000000080000000
cosinus0
  • 601
  • 1
  • 4
  • 15