How to reserve Linux's memory for a single driver - linux

I am running a secure OS in the ARM Trustzone, along side Linux which is running in the "normal" world. Since some part of the RAM is protected by hardware from access by the normal world (using a TZASC), I want to prevent Linux to try to access it otherwise it will crash. I also need a shared buffer between the two world, allocated by Linux. To this purpose, I want to reserve two memory regions, using this document.
I have two kind of areas:
the first one is a static one and I don't want to ever touch it
the second one is a dynamic one and I only want a single driver to be able to modify its content.
Here is an extract of my device tree:
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* global autoconfigured region for contiguous allocations */
linux,cma {
compatible = "shared-dma-pool";
reusable;
reg = <0 0xa0000000 0 0x14000000>;
linux,cma-default;
};
/* First range, static, that I don't want anyone to touch */
reserved_static: mymem#0xc0000000 {
compatible = "mymem,reserved-memory";
reg = <0 0xc0000000 0 0x08000000>;
no-map;
};
/* Second range, limited to a single driver */
reserved_dynamic: shared {
compatible = "mymem,memory-shared";
size = <0 0x08000000>;
alignment = <0 0x200000>;
alloc-ranges = <0 0xc8000000 0 0x38000000>;
};
};
mydev {
compatible = "mydev,mydev";
memory-region = <&reserved_dynamic>;
};
I can access the device tree information from my driver code, and I would like to prevent any access to the static memory range (I think I got that one covered with the no-map attribute), and also that only my driver is able to access the dynamic one.
Is this possible, and how can I achieve it ?

Related

How to pass efficiently the hugepages-backed buffer to the BM DMA device in Linux?

I need to provide a huge circular buffer (a few GB) for the bus-mastering DMA PCIe device implemented in FPGA.
The buffers should not be reserved at the boot time. Therefore, the buffer may be not contiguous.
The device supports scatter-gather (SG) operation, but for performance reasons, the addresses and lengths of consecutive contiguous segments of the buffer are stored inside the FPGA.
Therefore, usage of standard 4KB pages is not acceptable (there would be up to 262144 segments for each 1GB of the buffer).
The right solution should allocate the buffer consisting of 2MB hugepages in the user space (reducing the maximum number of segments by factor of 512).
The virtual address of the buffer should be transferred to the kernel driver via ioctl. Then the addresses and the length of the segments should be calculated and written to the FPGA.
In theory, I could use get_user_pages to create the list of the pages, and then call sg_alloc_table_from_pages to obtain the SG list suitable to program the DMA engine in FPGA.
Unfortunately, in this approach I must prepare the intermediate list of page structures with length of 262144 pages per 1GB of the buffer. This list is stored in RAM, not in the FPGA, so it is less problematic, but anyway it would be good to avoid it.
In fact I don't need to keep the pages maped for the kernel, as the hugepages are protected against swapping out, and they are mapped for the user space application that will process the received data.
So what I'm looking for is a function sg_alloc_table_from_user_hugepages, that could take such a user-space address of the hugepages-based memory buffer, and transfer it directly into the right scatterlist, without performing unnecessary and memory-consuming mapping for the kernel.
Of course such a function should verify that the buffer indeed consists of hugepages.
I have found and read these posts: (A), (B), but couldn't find a good answer.
Is there any official method to do it in the current Linux kernel?
At the moment I have a very inefficient solution based on get_user_pages_fast:
int sgt_prepare(const char __user *buf, size_t count,
struct sg_table * sgt, struct page *** a_pages,
int * a_n_pages)
{
int res = 0;
int n_pages;
struct page ** pages = NULL;
const unsigned long offset = ((unsigned long)buf) & (PAGE_SIZE-1);
//Calculate number of pages
n_pages = (offset + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
printk(KERN_ALERT "n_pages: %d",n_pages);
//Allocate the table for pages
pages = vzalloc(sizeof(* pages) * n_pages);
printk(KERN_ALERT "pages: %p",pages);
if(pages == NULL) {
res = -ENOMEM;
goto sglm_err1;
}
//Now pin the pages
res = get_user_pages_fast(((unsigned long)buf & PAGE_MASK), n_pages, 0, pages);
printk(KERN_ALERT "gupf: %d",res);
if(res < n_pages) {
int i;
for(i=0; i<res; i++)
put_page(pages[i]);
res = -ENOMEM;
goto sglm_err1;
}
//Now create the sg-list
res = sg_alloc_table_from_pages(sgt, pages, n_pages, offset, count, GFP_KERNEL);
printk(KERN_ALERT "satf: %d",res);
if(res < 0)
goto sglm_err2;
*a_pages = pages;
*a_n_pages = n_pages;
return res;
sglm_err2:
//Here we jump if we know that the pages are pinned
{
int i;
for(i=0; i<n_pages; i++)
put_page(pages[i]);
}
sglm_err1:
if(sgt) sg_free_table(sgt);
if(pages) kfree(pages);
* a_pages = NULL;
* a_n_pages = 0;
return res;
}
void sgt_destroy(struct sg_table * sgt, struct page ** pages, int n_pages)
{
int i;
//Free the sg list
if(sgt->sgl)
sg_free_table(sgt);
//Unpin pages
for(i=0; i < n_pages; i++) {
set_page_dirty(pages[i]);
put_page(pages[i]);
}
}
The sgt_prepare function builds the sg_table sgt structure that i can use to create the DMA mapping. I have verified that it contains the number of entries equal to the number of hugepages used.
Unfortunately, it requires that the list of the pages is created (allocated and returned via the a_pages pointer argument), and kept as long as the buffer is used.
Therefore, I really dislike that solution. Now I have 256 2MB hugepages used as a DMA buffer. It means that I have to create and keeep unnecessary 128*1024 page structures. I also waste 512 MB of kernel address space for unnecessary kernel mapping.
The interesting question is if the a_pages may be kept only temporarily (until the sg-list is created)? In theory it should be possible, as the pages are still locked...

How to set GPIO pin output value in device tree

I am using an iMX6 based board and I'd like to set the GPIO value of an arbirtrary ouput to 1 or 0 at boot using the Device Tree.
Is it possible and how can I do that?
I wonder if I have to rely on the gpio-leds feature or if I can define a new node in the DT.
I found some topics on the internet saying I can do as below but doesn't work.
test {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_leds>;
myout {
label = "myoutlabel";
gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
pinctrl_gpio_leds: gpioledsgrp {
fsl,pins = <
MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x80000000 // My output
>;
};
Any hint would be appreciated.
TIA
You can enable/disable the internal Pull-Up/Pull-Down resistor (along with other electrical characteristics) that is attached to that pin, thus forcing a logic 1/0 in the pin. You should seek the "pad control register" for that pad (IOMUXC_SW_PAD_CTL_PAD_GPIO_8) in the processor reference manual, and then look which characteristics you wish to enable/disable by setting the right bits to 1 or 0, as needed, in the aforementioned register. However, I'm not sure if this is exactly what you're looking for.
Maybe this can help to understand the operation: http://cache.freescale.com/files/32bit/doc/app_note/AN5078.pdf

SPI linux driver

I am trying to learn how to write a basic SPI driver and below is the probe function that I wrote.
What I am trying to do here is setup the spi device for fram(datasheet) and use the spi_sync_transfer()api description to get the manufacturer's id from the chip.
When I execute this code, I can see the data on the SPI bus using logic analyzer but I am unable to read it using the rx buffer. Am I missing something here? Could someone please help me with this?
static int fram_probe(struct spi_device *spi)
{
int err;
unsigned char ch16[] = {0x9F,0x00,0x00,0x00};// 0x9F => 10011111
unsigned char rx16[] = {0x00,0x00,0x00,0x00};
printk("[FRAM DRIVER] fram_probe called \n");
spi->max_speed_hz = 1000000;
spi->bits_per_word = 8;
spi->mode = (3);
err = spi_setup(spi);
if (err < 0) {
printk("[FRAM DRIVER::fram_probe spi_setup failed!\n");
return err;
}
printk("[FRAM DRIVER] spi_setup ok, cs: %d\n", spi->chip_select);
spi_element[0].tx_buf = ch16;
spi_element[1].rx_buf = rx16;
err = spi_sync_transfer(spi, spi_element, ARRAY_SIZE(spi_element)/2);
printk("rx16=%x %x %x %x\n",rx16[0],rx16[1],rx16[2],rx16[3]);
if (err < 0) {
printk("[FRAM DRIVER]::fram_probe spi_sync_transfer failed!\n");
return err;
}
return 0;
}
spi_element is not declared in this example. You should show that and also how all elements of that are array are filled. But just from the code that's there I see a couple mistakes.
You need to set the len parameter of spi_transfer. You've assigned the TX or RX buffer to ch16 or rx16 but not set the length of the buffer in either case.
You should zero out all the fields not used in the spi_transfer.
If you set the length to four, you would not be sending the proper command according to the datasheet. RDID expects a one byte command after which will follow four bytes of output data. You are writing a four byte command in your first transfer and then reading four bytes of data. The tx_buf in the first transfer should just be one byte.
And finally the number of transfers specified as the last argument to spi_sync_transfer() is incorrect. It should be 2 in this case because you have defined two, spi_element[0] and spi_element[1]. You could use ARRAY_SIZE() if spi_element was declared for the purpose of this message and you want to sent all transfers in the array.
Consider this as a way to better fill in the spi_transfers. It will take care of zeroing out fields that are not used, defines the transfers in a easy to see way, and changing the buffer sizes or the number of transfers is automatically accounted for in remaining code.
const char ch16[] = { 0x8f };
char rx16[4];
struct spi_transfer rdid[] = {
{ .tx_buf = ch16, .len = sizeof(ch16) },
{ .rx_buf = rx16, .len = sizeof(rx16) },
};
spi_transfer(spi, rdid, ARRAY_SIZE(rdid));
Since you have a scope, be sure to check that this operation happens under a single chip select pulse. I have found more than one Linux SPI driver to have a bug that pulses chip select when it should not. In some cases switching from TX to RX (like done above) will trigger a CS pulse. In other cases a CS pulse is generated for every word (8 bits here) of data.
Another thing you should change is use dev_info(&spi->dev, "device version %d", id)' and also dev_err() to print messages. This inserts the device name in a standard way instead of your hard-coded non-standard and inconsistent "[FRAME DRIVER]::" text. And sets the level of the message as appropriate.
Also, consider supporting device tree in your driver to read device properties. Then you can do things like change the SPI bus frequency for this device without rebuilding the kernel driver.

Custom SPI driver to implement lseek

I am trying to implement a SPI driver for custom hardware. I have started with a copy of the spidev driver, which has support for almost everything I need.
We're using a protocol that has three parts: a command bit (read / write) an address, and an arbitrary amount of data.
I had assumed that simply adding lseek capabilities would be the best way to do this. "Seek" to the desired address, then read or write any number of bytes. I created a custom .llseek in the new driver's file_operations, but I have never seen that function even be called. I have tried using fseek(), lseek(), and pread() and none of those functions seem to call the new my_lseek() function. Every call reports "errno 29 ESPIPE Illegal Seek"
The device is defined in the board.c file:
static struct spi_board_info my_spi_board_info[] __initdata = {
[0] = {
.modalias = "myspi",
.bus_num = 1,
.chip_select = 0,
.max_speed_hz = 3000000,
.mode = SPI_MODE_0,
.controller_data = &spidev_mcspi_config,
}, ...
I suspect there might be something with the way that the dev files get created, mainly because the example that I found references filp->f_pos
static int myspi_llseek(struct file *filp, loff_t off, int whence)
{
...
newpos = filp->f_pos + off;
...
}
So my questions are: Is there a way to have this driver (lightly modified spidev) support the "seek" call? At what point does this get defined to return errno 29? Will I have to start from a new driver and not be able to rely on the spi_board_info() and spi_register_board_info() setup?
Only one driver in the /drivers/spi directory (spi-dw) references lseek, and they use the default_llseek implementation. There are a couple of "hacks" that we've come up with to get everything up and running, but I tend to be a person who wants to learn to get it done the right way.
Any suggestions are greatly appreciated! (PS, the kernel version is 3.4.48 for an OMAP Android system)
Spi driver dose not support any llseek or fseek functionality. It has these many call back functions.
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};
Now drivers/spi/spi-dw.c is register as a charter-driver(debugfs_create_file("registers", S_IFREG | S_IRUGO,
dws->debugfs, (void *)dws, &dw_spi_regs_ops);). So they implement to create a file in the debugfs filesystem. they implement lseek callback function.
static const struct file_operations dw_spi_regs_ops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = dw_spi_show_regs,
.llseek = default_llseek,
};
The file_operations structure is defined in linux/fs.h, and holds pointers to functions defined by the driver that perform various operations on the device. Each field of the structure corresponds to the address of some function defined by the driver to handle a requested operation
lseek -: lseek is a system call that is used to change the location of the read/write pointer of a file descriptor.
SPI -: The "Serial Peripheral Interface" (SPI) is a synchronous four wire serial link used to connect microcontrollers to sensors, memory, and peripherals. SPI can not provide any lseek and fseek functionlity.
There are two type of SPI driver (https://www.kernel.org/doc/Documentation/spi/spi-summary)
Controller drivers ... controllers may be built into System-On-Chip
processors, and often support both Master and Slave roles.
These drivers touch hardware registers and may use DMA.
Or they can be PIO bitbangers, needing just GPIO pins.
Protocol drivers ... these pass messages through the controller
driver to communicate with a Slave or Master device on the
other side of an SPI link.
If you want to user read, write and llseek then you will have to register a charter-driver on top of SPI. Then you will able to achieve your acquirement.

generating EXTI0 interrupt when extenal temperature sensor is connected to STM32F207VC

I am using STM32F207VC controller . I have my external probe temperature sensor connected to one of the internal ADC channel of stm32.
I want to generate an external interrupt when this is connected to controller and i should start measuring from external temperature sensor.
Please could any one provide me code or any help in this
Thanks
I have an STM32F4 processor interfaced with a Solomon Systems SSD1963 GPU. The GPU has a Tearing Signal (TE) that notifies the processor when it is about to do a vertical refresh. I hope you can use this code as an example and adapt it to your solution.
The TE signal is connected to the CPU's GPIO G7 pin. So first I have to configure the GPIO pin.
//GPIO Pin G7
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOG, &GPIO_InitStructure);
Next I have to configure the interrupt and NVIC.
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource7);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_StructInit(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Finally I have to create the interrupt handler. The EXTI9_5_IRQHandler handles external interrupts on lines 5 ~ 7. This method is actually defined in my CPUs startup assembly file as a weak reference. I just need to redefine the method and the linker will do the rest.
extern "C" void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line7) != RESET)
{
//Handle the interrupt
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
I'm using Mentor Graphics' Sourcery Codebench Lite as my toolchain.

Resources