Zephyr - K_FIFO Crash - c

I have a strange error in the k_fifo-useage:
Basis is the project "peripheral uart" from nordic
I want to answer on specific commands send via bluetooth
static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
uint16_t len)
{
int err;
char addr[BT_ADDR_LE_STR_LEN] = {0};
struct answer_buffer_t *buf;
uint8_t answer[UART_BUF_SIZE];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
LOG_INF("Received %i data %s from: %s", len, data, log_strdup(addr));
if (strncmp(data, "&I", 2) == 0 )
{
uint8_t stringbuffer[100];
buf = k_malloc(sizeof(*buf));
iCounter++;
LOG_INF("&I %i", iCounter);
buf->len = sprintf(stringbuffer, "&I %i\n", iCounter);
memcpy(stringbuffer, &buf, buf->len);
k_fifo_put(&answer_buffer, buf);
}
}
And the bluetooth-sender-thread:
void ble_write_thread(void)
{
/* Don't go any further until BLE is initialized */
k_sem_take(&ble_init_ok, K_FOREVER);
for (;;) {
/* Wait indefinitely for data to be sent over bluetooth */
struct answer_buffer_t *buf = k_fifo_get(&answer_buffer,
K_FOREVER);
if (bt_nus_send(NULL, buf->data, buf->len)) {
LOG_WRN("Failed to send data over BLE connection");
}
k_free(buf);
}
}
K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
NULL, PRIORITY, 0, 0);
But when I send "&I" zephyr crashes:
[00:00:34.164,001] [0m<inf> peripheral_uart: &I 1[0m
[00:00:35.179,046] [1;31m<err> os: r0/a1: 0x00000004 r1/a2: 0x000000d1 r2/a3: 0x00000001[0m
[00:00:35.179,046] [1;31m<err> os: r3/a4: 0x0001dbbd r12/ip: 0x00000000 r14/lr: 0x00011d2b[0m
[00:00:35.179,046] [1;31m<err> os: xpsr: 0x41000000[0m
[00:00:35.179,046] [1;31m<err> os: Faulting instruction address (r15/pc): 0x00028d2c[0m
[00:00:35.179,077] [1;31m<err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0[0m
[00:00:35.179,077] [1;31m<err> os: Current thread: 0x200017c8 (unknown)[0m
[00:00:35.376,464] [1;31m<err> fatal_error: Resetting system[0m
What do I do wrong?

I found out this line:
LOG_INF("Received %i data %s from: %s", len, data, log_strdup(addr));
Without this its working but i had another error here (caused by my try and arror):
This is the right code:
if (strncmp(data, "&I", 2) == 0 )
{
buf = k_malloc(sizeof(*buf));
iCounter++;
LOG_INF("&I %i", iCounter);
buf->len = sprintf(buf->data, "&I %i\n", iCounter);
k_fifo_put(&answer_buffer, buf);
}

Related

Asynchronus DMA mem2mem copy doesn't transfer data

I'm working on a device driver that needs to preform mem to mem copies via dma on a Beaglebone Black (ARM) with Linux Kernel 5.4.106. So far I've managed to successfully request a mem to mem compatible channel (and release it when I'm done), but I'm unable to perform the actual transfer.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dmaengine.h>
struct dma_chan *chan;
void *src;
void *dst;
static int __init mod_init(void)
{
dma_cap_mask_t mask;
int ret;
struct dma_async_tx_descriptor *tx = NULL;
dma_cookie_t cookie;
int *writer;
enum dma_status status;
printk("mod_init called\n");
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
chan = dma_request_channel(mask, NULL, NULL);
if(!chan){
printk("no mem2mem channels available");
ret = -EAGAIN;
goto fail_chan;
}
printk("requested channel");
src = kzalloc(16,GFP_KERNEL);
if(src == NULL){
ret = -ENOMEM;
goto fail_m1;
}
dst = kzalloc(16,GFP_KERNEL);
if(dst == NULL){
ret = -ENOMEM;
goto fail_m2;
}
writer = (int *)src;
*writer = 20;
tx = chan->device->device_prep_dma_memcpy(chan, virt_to_phys(dst), virt_to_phys(src), 16, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
if (!tx) {
printk("prep error");
}
printk("slave configured");
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
printk("submit error");
}
dma_async_issue_pending(chan);
status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
if(status != DMA_COMPLETE){
printk("something went wrong");
}
printk("dst: %d, src: %d", *(int *)dst, *(int *)src);
printk("done");
return 0;
fail_m2:
kfree(src);
fail_m1:
dma_release_channel(chan);
fail_chan:
return ret;
}
static void __exit mod_exit(void)
{
printk("mod_exit called\n");
dma_release_channel(chan);
printk("dst: %d, src: %d", *(int *)dst, *(int *)src);
kfree(src);
kfree(dst);
printk("released channel");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_AUTHOR("Me");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA engine test.");
This kernel module compiles without any issues and when installed does not cause any errors. Eventho the cookie returns a DMA_COMPLETE status, the value in dst stays 0 (it should become 20).
Most of the code is based on the dmatest driver, which preforms flawlessly, but my copy is missing the mark.
What could be the issue here? Am I missing a step?
Try adding GFP_DMA flag to your kzalloc() calls - the memory you allocate should be suitable for DMA transfers, that is platform-dependent.

setting up the stack and heap in Unicorn emulator in C

I'm trying to learn x86 assembly using the Unicorn emulator, everything goes fine until I try using push or pop, I've searched the web for tutorials but I could'nt find anything useful that deals with the stack. I get this error when I try to use push or pop "Failed on uc_emu_start() with error returned 7: Invalid memory write (UC_ERR_WRITE_UNMAPPED)".
#include <unicorn/unicorn.h>
// code to be emulated
#define X86_CODE32 "\xB9\x05\x00\x00\x00\x51\x5A"
// memory address where emulation starts
#define ADDRESS 0x600000
//#define ADDRESS_STC 0x100000
#define ADDRESS_STC 0x6000000
int main(int argc, char **argv, char **envp)
{
uc_engine *uc;
//uc_engine *uc0;
uc_err err;
uc_err err0;
//int r_ecx = 0x1234; // ECX register
int r_ecx; // ECX register
int r_edx; // EDX register
int r_esp;
int r_esp0 = 0x7FFFFF;
printf("Emulate i386 code\n");
// Initialize emulator in X86-32bit mode
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
if (err != UC_ERR_OK) {
printf("Failed on uc_open() with error returned: %u\n", err);
return -1;
}
// map 2MB memory for this emulation
uc_mem_map(uc, ADDRESS, 1 * 1024 * 1024, UC_PROT_ALL);
//uc_mem_map(uc, ADDRESS_STC, 2 * 1024 * 1024, UC_PROT_ALL);
printf("Mike \n");
// write machine code to be emulated to memory
if (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1)) {
printf("Failed to write emulation code to memory, quit!\n");
return -1;
}
printf("Mike 0\n");
// initialize machine registers
uc_reg_write(uc,UC_X86_REG_EBP, &r_esp0+512);
uc_reg_write(uc,UC_X86_REG_ESP, &r_esp0+512);
printf("Mike 01\n");
// emulate code in infinite time & unlimited instructions
err=uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
if (err) {
printf("Failed on uc_emu_start() with error returned %u: %s\n",
err, uc_strerror(err));
uc_reg_read(uc,UC_X86_REG_RSP,&r_esp0);
printf("esp0 0x%x\n",r_esp0);
}
// now print out some registers
printf("Emulation done. Below is the CPU context\n");
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
uc_reg_read(uc, UC_X86_REG_EBP, &r_edx);
uc_reg_read(uc, UC_X86_REG_ESP, &r_esp);
printf(">>> EX = 0x%x\n", r_ecx);
printf(">>> EDX = 0x%x\n", r_edx);
printf(">>> ESP = 0x%x\n", r_esp);
uc_close(uc);
return 0;
}
ESP is set to 0x600, but memory is mapped starting at 0x400000.

Linux kernel module read from process VMA

I'm trying to build a small demonstration kernel module for Linux which finds a specific process and reads a value from that process' memory.
I have put together the following code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <asm/uaccess.h>
static void read_process_memory(struct task_struct *task) {
char buf[1024];
unsigned long size;
unsigned long ret;
void __user *addr;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct *vma;
if (!mm) {
// Abort
return;
}
// Lock for reading
down_read(&mm->mmap_sem);
vma = mm->mmap;
memset(buf, 0, 1024);
// Make sure read is enabled for this region
if (vma && vma->vm_flags & VM_READ) {
// Read without overflowing
size = vma->vm_end - vma->vm_start;
if (size > 1023) {
size = 1023;
}
// Attempt to get the data from the start of the vma
addr = (void __user *)vma->vm_start;
if (access_ok(VERIFY_READ, addr, size)) {
ret = copy_from_user(buf, addr, size);
if (ret == 0) {
// Probably doesn't contain anything relevent
printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
} else {
printk(KERN_ALERT "mymodule: Failed to copy %lu bytes from userspace\n", ret);
}
} else {
printk(KERN_ALERT "mymodule: access_ok check failed\n");
}
// Release the lock
up_read(&mm->mmap_sem);
mmput(mm);
}
}
static int __init mymodule_init(void) {
struct task_struct *task;
bool found = false;
printk(KERN_ALERT "mymodule: Starting\n");
// Find the process
rcu_read_lock();
for_each_process(task) {
if (strcmp(task->comm, "example-process") == 0) {
printk(KERN_ALERT "mymodule: Found pid %d for process %s\n", task->pid, task->comm);
found = true;
break;
}
}
rcu_read_unlock();
if (!found) {
printk(KERN_ALERT "mymodule: Process not found, aborting\n");
return -1;
}
read_process_memory(task);
return 0;
}
static void __exit mymodule_exit(void) {
printk(KERN_ALERT "mymodule: Stopped\n");
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("user");
Everything works fine until the copy_from_user call, which returns a non-zero count (actually, it returns size, meaning it didn't ready any data), regardless of the process and vma I try to read from.
Is there a misunderstanding on my part, or anything I'm doing wrong?
EDIT:
Here's a working version of read_process_memory:
static void read_process_memory(struct task_struct *task) {
char buf[1024];
unsigned long size;
int ret;
void *addr;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct *vma;
struct page *pages[1];
if (!mm) {
// Abort
return;
}
// Lock for reading
down_read(&mm->mmap_sem);
vma = mm->mmap;
memset(buf, 0, 1024);
// Make sure read is enabled for this region
if (vma && vma->vm_flags & VM_READ) {
// Get the first page
ret = get_user_pages_remote(task, mm, vma->vm_start, 1, FOLL_FORCE, pages, NULL);
if (ret > 0) {
// We got our page, we should now be able to map it and read directly
addr = kmap(*pages);
// Read without overflowing
size = vma->vm_end - vma->vm_start;
if (size > 1023) {
size = 1023;
}
strncpy(buf, addr, size);
// Probably doesn't contain anything relevent
printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
// Make sure to release our page
kunmap(*pages);
put_page(*pages);
} else {
printk(KERN_ALERT "mymodule: Failed to read page at %p (errno=%d)\n", (void *)vma->vm_start, ret);
}
// Release the lock
up_read(&mm->mmap_sem);
mmput(mm);
}
}
I'd say your code is wrong. Please be noted you are accessing memory of remote process (not current). To do that correctly you must use one of the GUP APIs(e.g. get_user_pages_remote()) to grab the pages first. The uaccess APIs (e.g. copy_from_user) only works for current process.
Here is an example in iouring:
https://elixir.bootlin.com/linux/latest/source/fs/io_uring.c#L4839

How to support properly kernel suspend/resume feature in kernel driver?

I have a kernel driver with char device :
static const struct file_operations xxxxx_fops = {
.owner = THIS_MODULE,
.read = xxxxx_read,
};
A read callback on this char device :
static int XXXX_read(struct file *filp, char __user *data, size_t len, loff_t *ppos)
{
int err = 0;
int retry = 0;
do {
if (retry == 5) return -3;
err = down_interruptible(&priv.XXXX_sem);
if (err == 0) {
retry = 0;
break;
} else {
retry++;
continue;
}
} while (1);
err = copy_to_user(data, &priv.XXXX_queue[priv.XXXX_read_offset], XXXX_TOTAL_SIZE);
priv.XXXXX_read_offset += XXXXX_TOTAL_SIZE;
if (priv.XXXX_read_offset == XXXX_QUEUE_SIZE) priv.XXXX_read_offset = 0;
if (err) printk ("Error during XXXX copy\n");
return (XXXX_TOTAL_SIZE - err);
};
In my userland code, I open the char device and use read call in a loop :
(...)
while (1)
{
err = read (fd, &ctx->xxxx_queue[xxxxx_offset], XXXX_TOTAL);
if (err != XXXX_TOTAL)
{
PRINT_WARN ("XXXX read byte : %d (errno : %d)", err, errno);
continue;
}
(...)
}
(...)
Everything is running fine.
When I used suspend feature from the kernel by calling :
echo mem > /sys/power/state, an issue occured on resume :
XXXX read byte : -1 (errno : 3)
At this moment most of the time my driver read callback is block
in the semaphore wait (down_interruptible)
The read return -1 on kernel resume. Is there a way to avoid this ?
I tried to looking for a suspend/resume callback to declare on
struct file_operations but nothing.
Do I have just to ignore -3 error fro ioctl ?

Data bus error when using ioread32 in PCIE driver

I am developing a PCIE device driver for Openwrt, which is also a linux system. Here is a weird situation. After Initializing the driver in probe function, I can read(by ioread32) correct data (preset value:123456) from the buffer address obtained from ioremap_nocache. Then when I try to read it every 1 second in periodic timer interrupt handler, the function ioread32 will crash and the serial console presents a Data bus error. Below are code details.
// content of function my_driver_request_mem, this function is called in probe function
int my_driver_request_mem(struct gps_time *gt) {
u32 start, len;
int ret;
int bar = 0;
u32 flags;
ret = pcim_iomap_regions(gt->pdev, BIT(0), "My Driver");
if (ret) {
gt_log("Fail to request IO mem: err: %d\n", ret);
return ret;
}
// gt is a custom struct, and gt->pdev is the pci_dev struct
// obtained from probe function
start = pci_resource_start(gt->pdev, bar);
len = pci_resource_len(gt->pdev, bar);
flags = pci_resource_flags(gt->pdev, bar);
printk(KERN_ALERT "region start: 0x%x, len: %u\n", start, len);
printk(KERN_ALERT "region flags: 0x%x\n", flags);
gt->buffer = ioremap_nocache(start, len);
gt->buffer_len = len;
gt->buffer_start = start;
return 0;
}
Afte the function above is invoked, I read data through gt->buffer:
u32 d = 0;
d = ioread32(gt->buffer); // this operation does not cause fatal error
printk(KERN_ALERT "initial value is: %u", d);
By reading the console output, the ioread32 here is successful, and the right value 123456 is printed. Then I start a timer to read data multiple times
setup_timer(&gt->g_timer, _gps_timer_handler, gt);
mod_timer(&gt->g_timer, jiffies + msecs_to_jiffies(20000));
printk(KERN_ALERT "GPS_TIME: timer created.\n");
The handler function is quit simple:
void _gps_timer_handler(unsigned long data) {
struct gps_time *gt = (struct gps_time*)data;
u32 d;
d = ioread32(gt->buffer); // fatal error in this line
printk(KERN_ALERT "Value: %u\n", d);
mod_timer(&gt->g_timer, jiffies + msecs_to_jiffies(1000));
}
The ioread32 here will cause a fatal error here, and the error info is:
Data bus error, epc == 82db8030, ra == 8009a000
Oops[#1]
CPU: 0 PID: 853 Comm: dropbearkey Tainted: G W 4.4.14 #2
task: 82dd1900 ti:8209a000 task.ti: 8209a000
...
(bunch of numbers)
...
Status: 1100d403 KERNEL EXL IE
Cause : 8080001c (ExcCode 07)
PrId: 0001974c (MIPS 74Kc)
...
First I though this is because IO process should not be done in interrupt context, so I put ioread32 in a tasklet, and invoke that tasklet by tasklet_schedule, but it still fails.
======== Update
A. Below are my ->probe function:
static int gps_time_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret = 0;
struct gps_time *gt;
u8 tmp = 0;
u32 bar_val = 0;
u8 csz = 0;
unsigned long start, end, len, flag;
int bar = 0;
if (gps_time_global_time) {
printk(KERN_ALERT "GPS_TIME: more than one device detected\n");
return -1;
}
gt = gps_time_alloc();
if (gt == NULL) {
printk(KERN_WARNING "GPS_TIME: out of memory\n");
return -ENOMEM;
}
gt->pdev = pdev;
gt->irq = pdev->irq;
ret = pcim_enable_device(pdev);
if (ret) {
printk(KERN_ALERT "GPS_TIME: Fail to enable device %d\n", ret);
goto err;
}
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret) {
printk(KERN_WARNING "GPS_TIME: 32-bit DMA not available\n");
return ret;
}
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret) {
printk(KERN_WARNING "GPS_TIME: 32-bit DMA consistent DMA enable failed\n");
return ret;
}
my_driver_request_mem(gt);
ret = pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &tmp);
if (ret) {
printk(KERN_ALERT "GPS_TIME: Fail to read cache line size\n");
goto err;
}
if (tmp == 0) {
printk(KERN_ALERT "GPS_TIME: Write pci cache line size\n");
pci_write_config_byte(
pdev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / sizeof(u32));
}
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
pci_set_master(pdev);
pci_set_drvdata(pdev, gt);
// This function is very simple. I just create timer here. The first ioread32 is also included in this function.
gps_time_init_device(gt);
ret = request_irq(pdev->irq, gps_time_isq, IRQF_SHARED, "gps_time", gt);
if (ret) {
printk(KERN_ALERT "GPS_TIME: Fail to request IRQ: %d", ret);
goto err_irq;
}
return 0;
err_region:
pci_release_regions(pdev);
err_irq:
err:
gps_time_free(gt);
return ret;
}
B. More info about the device:
This device is a self-designed chip with PCIE interface. It is built around a Altera Cyclone IV FPGA. The firmware in the chip does nothing except writing constant 123456 into its memory.

Resources