mbed uvisor and EthernetInterface overflowed - c

I am quite new to mbed and uvisor so maybe my problem is about understanding how things work. I have a NXP FRDM-K64F board where I am trying to learn about mbed and uvisor. I have succesfully compiled an run some basic examples of tasks running on different boxes. I am trying to connect to the net one of the boxes in uvisor but something is not working correctly.
This is the main file code:
#include "uvisor-lib/uvisor-lib.h"
#include "mbed.h"
#include "main-hw.h"
/* Create ACLs for main box. */
MAIN_ACL(g_main_acl);
/* Enable uVisor. */
UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_main_acl);
UVISOR_SET_PAGE_HEAP(8 * 1024, 5);
int main(void)
{
printf("----Eup---------\r\n");
DigitalOut led(MAIN_LED);
while (1) {
printf("taka\r\n");
led = !led;
/* Blink once per second. */
Thread::wait(1000);
}
return 0;
}
This is the code in box file:
#include "uvisor-lib/uvisor-lib.h"
#include "mbed.h"
#include "main-hw.h"
#include "EthernetInterface.h"
// Network interface
EthernetInterface net;
struct box_context {
Thread * thread;
uint32_t heartbeat;
};
static const UvisorBoxAclItem acl[] = {
};
static void my_box_main(const void *);
/* Box configuration
* We need 1kB of stack both in the main and interrupt threads as both of them
* use printf. */
UVISOR_BOX_NAMESPACE(NULL);
UVISOR_BOX_HEAPSIZE(3072);
UVISOR_BOX_MAIN(my_box_main, osPriorityNormal, 1024);
UVISOR_BOX_CONFIG(my_box, acl, 1024, box_context);
static void my_box_main(const void *)
{
while (1) {
printf("tan tan\r\n");
Thread::wait(2000);
}
}
I have not yet added the specific connection code, just the definition of the EthernetInterface object and I am getting the following error on compilation:
../../../../arm-none-eabi/bin/ld.exe: Region m_data_2 overflowed with stack and heap
collect2.exe: error: ld returned 1 exit status
I have tried changing the values of the heap size but I have not found a way of making it work. What am I missing?

In your main box, change the value for UVISOR_SET_PAGE_HEAP.
With UVISOR_SET_PAGE_HEAP(8 * 1024, 3) in the main box; and 8K heap in the secure box and UVISOR_BOX_STACK_SIZE stack size in the secure box it compiles and links for me (mbed OS 5.3, GCC ARM on K64F).

Related

Linux block device module is hanging when unloading with rmmod (del_gendisk)

I'm writing a little example for block device drivers in Linux. This example is not complete and I take progress step by step. I registered a block device with blkdev_register and allocated the gendisk structure with alloc_disk. All works fine, when inserting the module. It shows up in /proc/devices. But if I want to unload it with rmmod, it hangs.
I figured out, that in the module unload function, the call to del_gendisk caused the hanging. I know that the gendisk structure has an embedded kobject, which takes care of reference counting. This mechanism hinders you to unload the module, while it's being used. But since I don't call add_disk, there should be no reference's to that structure.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#define BLKDEVNAME "blkdev_test"
#define MINORS 16
struct block_device_operations bdops = {
.owner = THIS_MODULE
};
struct blkdev {
int major;
struct gendisk *disk;
struct request_queue *queue;
} dev;
static int __init blkdev_init(void)
{
dev.major = register_blkdev( 0, BLKDEVNAME );
if( dev.major < 0 )
return -EIO;
dev.disk = alloc_disk(MINORS);
if( dev.disk == NULL )
goto DISK_ERR;
dev.disk->major = dev.major;
dev.disk->first_minor = 0;
snprintf(dev.disk->disk_name, DISK_NAME_LEN, "bd0" );
dev.disk->fops = &bdops;
// dev.disk->queue = dev.queue;
// add_disk( dev.disk );
return 0;
DISK_ERR:
unregister_blkdev( dev.major, BLKDEVNAME );
return -EIO;
}
static void __exit blkdev_exit(void)
{
del_gendisk(dev.disk);
unregister_blkdev( dev.major, BLKDEVNAME );
}
module_init(blkdev_init);
module_exit(blkdev_exit);
MODULE_LICENSE("GPL");
If I issue the command sudo rmmod mod.ko then the command gets killed by the system.
Quoting :
Manipulating gendisks Once you have your gendisk structure set up, you
have to add it to the list of active disks; that is done with:
void add_disk(struct gendisk *disk);
After this call, your device is active. There are a few things worth
keeping in mind about add_disk():
add_disk() can create I/O to the device (to read partition tables and such). You should not call add_disk() until your driver is
sufficiently initialized to handle requests.
If you are calling add_disk() in your driver initialization routine, you should not fail the initialization process after the
first call.
The call to add_disk() increments the disk's reference count; if the disk structure is ever to be released, the driver is responsible
for decrementing that count (with put_disk()).
Should you need to remove a disk from the system, that is accomplished
with:
void del_gendisk(struct gendisk *disk);
This function cleans up all of the information associated with the
given disk, and generally removes it from the system. After a call to
del_gendisk(), no more operations will be sent to the given device.
Your driver's reference to the gendisk object remains, though; you
must explicitly release it with:
void put_disk(struct gendisk *disk);
That call will cause the gendisk structure to be freed, as long as no
other part of the kernel retains a reference to it.

uC/OS hardware register read

I am trying to use the low power timer on the Freescale FRDM K64F, with IAR as design framework and uC/OS as operating system. I read the manual and I found that the timer is controlled by means of a bunch of registers, but I am getting in trouble with the register reading and writing.
At first I am just trying to read a register with the code attached below. The code stucks as soon as the read line is reached: the line “after read” is not printed on terminal. Searching through the debugger, I found out that the hardfault_handler exception is raised. I am not a software expert, so: does anyone have an idea about what is the problem here? It should be something related with the OS, but I can’t understand what. I said I am not a software expert, so, should I have forgot to tell something important, please let me know. Thanks in advance.
#include "fsl_interrupt_manager.h"
#include <math.h>
#include <lib_math.h>
#include <cpu_core.h>
#include <app_cfg.h>
#include <os.h>
#include <fsl_os_abstraction.h>
#include <system_MK64F12.h>
#include <board.h>
#include <bsp_ser.h>
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static OS_TCB AppTaskStartTCB;
static CPU_STK AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE];
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void AppTaskStart (void *p_arg);
int main (void)
{
OS_ERR err;
#if (CPU_CFG_NAME_EN == DEF_ENABLED)
CPU_ERR cpu_err;
#endif
hardware_init();
GPIO_DRV_Init(switchPins, ledPins);
#if (CPU_CFG_NAME_EN == DEF_ENABLED)
CPU_NameSet((CPU_CHAR *)"MK64FN1M0VMD12",
(CPU_ERR *)&cpu_err);
#endif
OSA_Init(); /* Init uC/OS-III. */
OSTaskCreate(&AppTaskStartTCB, /* Create the start task */
"App Task Start",
AppTaskStart,
0u,
APP_CFG_TASK_START_PRIO,
&AppTaskStartStk[0u],
(APP_CFG_TASK_START_STK_SIZE / 10u),
APP_CFG_TASK_START_STK_SIZE,
0u,
0u,
0u,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_SAVE_FP),
&err);
OSA_Start(); /* Start multitasking (i.e. give control to uC/OS-III). */
while (DEF_ON) { /* Should Never Get Here */
;
}
}
static void AppTaskStart (void *p_arg)
{
OS_ERR os_err;
(void)p_arg; /* See Note #1 */
char string[800];
CPU_Init(); /* Initialize the uC/CPU Services. */
Mem_Init(); /* Initialize the Memory Management Module */
Math_Init(); /* Initialize the Mathematical Module */
int * PSR = (int *) 0x42040004;
BSP_Ser_Init(115200u);
APP_TRACE_DBG(("Blinking RGB LED...\n\r"));
int value;
APP_TRACE_DBG(("Before read\n"));
value=*PSR;
APP_TRACE_DBG(("After read\n"));
APP_TRACE_DBG(("Before sprintf\n"));
sprintf(string,"Value is %d\n",value);
APP_TRACE_DBG(("After sprintf\n"));
}

CUDA How to access constant memory in device kernel when the constant memory is declared in the host code?

For the record this is homework so help as little or as much with that in mind. We are using constant memory to store a "mask matrix" that will be used to perform a convolution on a larger matrix. When I am in the host code I am copying the mask to constant memory using the cudaMemcpyToSymbol().
My question is once this is copied over and I launch my device kernel code how does the device know where to access the constant memory mask matrix. Is there a pointer that I need to pass in on kernel launch. Most of the code that the professor gave us is not supposed to be changed (there is no pointer to the mask passed in) but there is always the possibility that he made a mistake ( although it is most likely my understanding of something)
Is the constant memeory declaratoin supposed to be included in the seperate kernel.cu file?
I am minimizing the code to just show the things having to do with the constant memory. As such please don't point out if something is not initialized ect. There is code for that but that is not of concern at this time.
main.cu:
#include <stdio.h>
#include "kernel.cu"
__constant__ float M_d[FILTER_SIZE * FILTER_SIZE];
int main(int argc, char* argv[])
{
Matrix M_h, N_h, P_h; // M: filter, N: input image, P: output image
/* Allocate host memory */
M_h = allocateMatrix(FILTER_SIZE, FILTER_SIZE);
N_h = allocateMatrix(imageHeight, imageWidth);
P_h = allocateMatrix(imageHeight, imageWidth);
/* Initialize filter and images */
initMatrix(M_h);
initMatrix(N_h);
cudaError_t cudda_ret = cudaMemcpyToSymbol(M_d, M_h.elements, M_h.height * M_h.width * sizeof(float), 0, cudaMemcpyHostToDevice);
//char* cudda_ret_pointer = cudaGetErrorString(cudda_ret);
if( cudda_ret != cudaSuccess){
printf("\n\ncudaMemcpyToSymbol failed\n\n");
printf("%s, \n\n", cudaGetErrorString(cudda_ret));
}
// Launch kernel ----------------------------------------------------------
printf("Launching kernel..."); fflush(stdout);
//INSERT CODE HERE
//block size is 16x16
// \\\\\\\\\\\\\**DONE**
dim_grid = dim3(ceil(N_h.width / (float) BLOCK_SIZE), ceil(N_h.height / (float) BLOCK_SIZE));
dim_block = dim3(BLOCK_SIZE, BLOCK_SIZE);
//KERNEL Launch
convolution<<<dim_grid, dim_block>>>(N_d, P_d);
return 0;
}
kernel.cu: THIS IS WHERE I DO NOT KNOW HOW TO ACCESS THE CONSTANT MEMORY.
//__constant__ float M_c[FILTER_SIZE][FILTER_SIZE];
__global__ void convolution(Matrix N, Matrix P)
{
/********************************************************************
Determine input and output indexes of each thread
Load a tile of the input image to shared memory
Apply the filter on the input image tile
Write the compute values to the output image at the correct indexes
********************************************************************/
//INSERT KERNEL CODE HERE
//__shared__ float N_shared[BLOCK_SIZE][BLOCK_SIZE];
//int row = (blockIdx.y * blockDim.y) + threadIdx.y;
//int col = (blockIdx.x * blockDim.x) + threadIdx.x;
}
In "classic" CUDA compilation you must define all code and symbols (textures, constant memory, device functions) and any host API calls which access them (including kernel launches, binding to textures, copying to symbols) within the same translation unit. This means, effectively, in the same file (or via multiple include statements within the same file). This is because "classic" CUDA compilation doesn't include a device code linker.
Since CUDA 5 was released, there is the possibility of using separate compilation mode and linking different device code objects into a single fatbinary payload on architectures which support it. In that case, you need to declare any __constant__ variables using the extern keyword and define the symbol exactly once.
If you can't use separate compilation, then the usual workaround is to define the __constant__ symbol in the same .cu file as your kernel, and include a small host wrapper function which just calls cudaMemcpyToSymbol to set the __constant__ symbol in question. You would probably do the same with kernel calls and texture operations.
Below is a "minimum-sized" example showing the use of __constant__ symbols. You do not need to pass any pointer to the __global__ function.
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdio.h>
__constant__ float test_const;
__global__ void test_kernel(float* d_test_array) {
d_test_array[threadIdx.x] = test_const;
}
#include <conio.h>
int main(int argc, char **argv) {
float test = 3.f;
int N = 16;
float* test_array = (float*)malloc(N*sizeof(float));
float* d_test_array;
cudaMalloc((void**)&d_test_array,N*sizeof(float));
cudaMemcpyToSymbol(test_const, &test, sizeof(float));
test_kernel<<<1,N>>>(d_test_array);
cudaMemcpy(test_array,d_test_array,N*sizeof(float),cudaMemcpyDeviceToHost);
for (int i=0; i<N; i++) printf("%i %f\n",i,test_array[i]);
getch();
return 0;
}

How to create a simple sysfs class attribute in Linux kernel v3.2

I'm learning how to use sysfs in my Linux modules, but I'm having the hardest time finding current documentation on these topics. The Linux Device Drivers 3rd Edition book I've been using seems to be rather dated in this area unfortunately (e.g. the class_device structure appears to be completely gone in current Linux versions).
I'm simply trying to get an attribute to appear, under the respective sysfs class for my module, that will allow me to read the value of a module variable from kernel space.
In my code, I have a class created that allows udev to create a device node at /dev/foo for my module:
dev_t foo_dev;
alloc_chrdev_region(&foo_dev, 0, 1, "bar");
struct class *bar = class_create(THIS_MODULE, "bar");
device_create(bar, NULL, foo_dev, NULL, "foo");
struct cdev foo_dev_file;
cdev_init(&foo_dev_file, &fops); /* fops defined earlier */
cdev_add(&foo_dev_file, foo_dev, 1);
When I insert the module I get a sysfs class directory created and populated with some default attributes at /sys/class/bar/foo/. How can I create attributes that show up under this new directory?
I have the concepts down pretty well I believe -- create attribute structure, define sysfs_ops functions, etc -- my problem is that I don't know which particular kernel structure to use (class_attribute?), nor how to make these attributes appear under the right sysfs directory.
Would anyone point me to a tutorial or article detailing the process for current Linux kernels?
Even though my knowledge is still fairly low on the topic, I'm going to post an answer just because of the age of this question. If somebody else has a better answer, please post! :)
First off, I'm going to assume that you've read that whole chapter (specifically about kobjects & ksets). So just about every struct in the device driver model has these cutely included in them. If you want to manipulate the kobject for the class its self (not sure if that's wise or not), that's your struct class's dev_kobj member.
However, you want to manipulate the attributes of that class. I believe you do this by defining a (usually static), NULL-terminated array of them as follows and then assigning its address to the struct class's class_attrs member (taken from drivers/uwb/driver.c):
static struct class_attribute uwb_class_attrs[] = {
__ATTR(beacon_timeout_ms, S_IWUSR | S_IRUGO,
beacon_timeout_ms_show, beacon_timeout_ms_store),
__ATTR_NULL,
};
/** Device model classes */
struct class uwb_rc_class = {
.name = "uwb_rc",
.class_attrs = uwb_class_attrs,
};
When I don't know how to use something, I usually git grep the repository for somebody else who has used it and try to learn from it that way. It would seem that this is why they tend to say kernel "hackers" and not "developers".
Minimal runnable example
Usage:
insmod /sysfs.ko
cd /sys/kernel/lkmc_sysfs
printf 12345 >foo
cat foo
# => 1234
dd if=foo bs=1 count=2 skip=1 status=none
# => 23
sysfs.c
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <uapi/linux/stat.h> /* S_IRUSR, S_IWUSR */
enum { FOO_SIZE_MAX = 4 };
static int foo_size;
static char foo_tmp[FOO_SIZE_MAX];
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buff)
{
strncpy(buff, foo_tmp, foo_size);
return foo_size;
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buff, size_t count)
{
foo_size = min(count, (size_t)FOO_SIZE_MAX);
strncpy(foo_tmp, buff, foo_size);
return count;
}
static struct kobj_attribute foo_attribute =
__ATTR(foo, S_IRUGO | S_IWUSR, foo_show, foo_store);
static struct attribute *attrs[] = {
&foo_attribute.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static struct kobject *kobj;
static int myinit(void)
{
int ret;
kobj = kobject_create_and_add("lkmc_sysfs", kernel_kobj);
if (!kobj)
return -ENOMEM;
ret = sysfs_create_group(kobj, &attr_group);
if (ret)
kobject_put(kobj);
return ret;
}
static void myexit(void)
{
kobject_put(kobj);
}
module_init(myinit);
module_exit(myexit);
MODULE_LICENSE("GPL");
GitHub upstream.
Tested with Linux kernel 5.0.
There is a good tutorial in the link below
http://pete.akeo.ie/2011/08/writing-linux-device-driver-for-kernels.html
parrot_driver.c:
/*
* Linux 2.6 and 3.0 'parrot' sample device driver
*
* Copyright (c) 2011, Pete Batard <pete#akeo.ie>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/kfifo.h>
#include "parrot_driver.h"
/* Module information */
MODULE_AUTHOR(AUTHOR);
MODULE_DESCRIPTION(DESCRIPTION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
/* Device variables */
static struct class* parrot_class = NULL;
static struct device* parrot_device = NULL;
static int parrot_major;
/* Flag used with the one_shot mode */
static bool message_read;
/* A mutex will ensure that only one process accesses our device */
static DEFINE_MUTEX(parrot_device_mutex);
/* Use a Kernel FIFO for read operations */
static DECLARE_KFIFO(parrot_msg_fifo, char, PARROT_MSG_FIFO_SIZE);
/* This table keeps track of each message length in the FIFO */
static unsigned int parrot_msg_len[PARROT_MSG_FIFO_MAX];
/* Read and write index for the table above */
static int parrot_msg_idx_rd, parrot_msg_idx_wr;
/* Module parameters that can be provided on insmod */
static bool debug = false; /* print extra debug info */
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "enable debug info (default: false)");
static bool one_shot = true; /* only read a single message after open() */
module_param(one_shot, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "disable the readout of multiple messages at once (default: true)");
static int parrot_device_open(struct inode* inode, struct file* filp)
{
dbg("");
/* Our sample device does not allow write access */
if ( ((filp->f_flags & O_ACCMODE) == O_WRONLY)
|| ((filp->f_flags & O_ACCMODE) == O_RDWR) ) {
warn("write access is prohibited\n");
return -EACCES;
}
/* Ensure that only one process has access to our device at any one time
* For more info on concurrent accesses, see http://lwn.net/images/pdf/LDD3/ch05.pdf */
if (!mutex_trylock(&parrot_device_mutex)) {
warn("another process is accessing the device\n");
return -EBUSY;
}
message_read = false;
return 0;
}
static int parrot_device_close(struct inode* inode, struct file* filp)
{
dbg("");
mutex_unlock(&parrot_device_mutex);
return 0;
}
static ssize_t parrot_device_read(struct file* filp, char __user *buffer, size_t length, loff_t* offset)
{
int retval;
unsigned int copied;
/* The default from 'cat' is to issue multiple reads until the FIFO is depleted
* one_shot avoids that */
if (one_shot && message_read) return 0;
dbg("");
if (kfifo_is_empty(&parrot_msg_fifo)) {
dbg("no message in fifo\n");
return 0;
}
retval = kfifo_to_user(&parrot_msg_fifo, buffer, parrot_msg_len[parrot_msg_idx_rd], &copied);
/* Ignore short reads (but warn about them) */
if (parrot_msg_len[parrot_msg_idx_rd] != copied) {
warn("short read detected\n");
}
/* loop into the message length table */
parrot_msg_idx_rd = (parrot_msg_idx_rd+1)%PARROT_MSG_FIFO_MAX;
message_read = true;
return retval ? retval : copied;
}
/* The file_operation scructure tells the kernel which device operations are handled.
* For a list of available file operations, see http://lwn.net/images/pdf/LDD3/ch03.pdf */
static struct file_operations fops = {
.read = parrot_device_read,
.open = parrot_device_open,
.release = parrot_device_close
};
/* Placing data into the read FIFO is done through sysfs */
static ssize_t sys_add_to_fifo(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
unsigned int copied;
dbg("");
if (kfifo_avail(&parrot_msg_fifo) < count) {
warn("not enough space left on fifo\n");
return -ENOSPC;
}
if ((parrot_msg_idx_wr+1)%PARROT_MSG_FIFO_MAX == parrot_msg_idx_rd) {
/* We've looped into our message length table */
warn("message length table is full\n");
return -ENOSPC;
}
/* The buffer is already in kernel space, so no need for ..._from_user() */
copied = kfifo_in(&parrot_msg_fifo, buf, count);
parrot_msg_len[parrot_msg_idx_wr] = copied;
if (copied != count) {
warn("short write detected\n");
}
parrot_msg_idx_wr = (parrot_msg_idx_wr+1)%PARROT_MSG_FIFO_MAX;
return copied;
}
/* This sysfs entry resets the FIFO */
static ssize_t sys_reset(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
dbg("");
/* Ideally, we would have a mutex around the FIFO, to ensure that we don't reset while in use.
* To keep this sample simple, and because this is a sysfs operation, we don't do that */
kfifo_reset(&parrot_msg_fifo);
parrot_msg_idx_rd = parrot_msg_idx_wr = 0;
return count;
}
/* Declare the sysfs entries. The macros create instances of dev_attr_fifo and dev_attr_reset */
static DEVICE_ATTR(fifo, S_IWUSR, NULL, sys_add_to_fifo);
static DEVICE_ATTR(reset, S_IWUSR, NULL, sys_reset);
/* Module initialization and release */
static int __init parrot_module_init(void)
{
int retval;
dbg("");
/* First, see if we can dynamically allocate a major for our device */
parrot_major = register_chrdev(0, DEVICE_NAME, &fops);
if (parrot_major < 0) {
err("failed to register device: error %d\n", parrot_major);
retval = parrot_major;
goto failed_chrdevreg;
}
/* We can either tie our device to a bus (existing, or one that we create)
* or use a "virtual" device class. For this example, we choose the latter */
parrot_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(parrot_class)) {
err("failed to register device class '%s'\n", CLASS_NAME);
retval = PTR_ERR(parrot_class);
goto failed_classreg;
}
/* With a class, the easiest way to instantiate a device is to call device_create() */
parrot_device = device_create(parrot_class, NULL, MKDEV(parrot_major, 0), NULL, CLASS_NAME "_" DEVICE_NAME);
if (IS_ERR(parrot_device)) {
err("failed to create device '%s_%s'\n", CLASS_NAME, DEVICE_NAME);
retval = PTR_ERR(parrot_device);
goto failed_devreg;
}
/* Now we can create the sysfs endpoints (don't care about errors).
* dev_attr_fifo and dev_attr_reset come from the DEVICE_ATTR(...) earlier */
retval = device_create_file(parrot_device, &dev_attr_fifo);
if (retval < 0) {
warn("failed to create write /sys endpoint - continuing without\n");
}
retval = device_create_file(parrot_device, &dev_attr_reset);
if (retval < 0) {
warn("failed to create reset /sys endpoint - continuing without\n");
}
mutex_init(&parrot_device_mutex);
/* This device uses a Kernel FIFO for its read operation */
INIT_KFIFO(parrot_msg_fifo);
parrot_msg_idx_rd = parrot_msg_idx_wr = 0;
return 0;
failed_devreg:
class_unregister(parrot_class);
class_destroy(parrot_class);
failed_classreg:
unregister_chrdev(parrot_major, DEVICE_NAME);
failed_chrdevreg:
return -1;
}
static void __exit parrot_module_exit(void)
{
dbg("");
device_remove_file(parrot_device, &dev_attr_fifo);
device_remove_file(parrot_device, &dev_attr_reset);
device_destroy(parrot_class, MKDEV(parrot_major, 0));
class_unregister(parrot_class);
class_destroy(parrot_class);
unregister_chrdev(parrot_major, DEVICE_NAME);
}
/* Let the kernel know the calls for module init and exit */
module_init(parrot_module_init);
module_exit(parrot_module_exit);
parrot_driver.h:
/*
* Linux 2.6 and 3.0 'parrot' sample device driver
*
* Copyright (c) 2011, Pete Batard <pete#akeo.ie>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DEVICE_NAME "device"
#define CLASS_NAME "parrot"
#define PARROT_MSG_FIFO_SIZE 1024
#define PARROT_MSG_FIFO_MAX 128
#define AUTHOR "Pete Batard <pete#akeo.ie>"
#define DESCRIPTION "'parrot' sample device driver"
#define VERSION "0.3"
/* We'll use our own macros for printk */
#define dbg(format, arg...) do { if (debug) pr_info(CLASS_NAME ": %s: " format , __FUNCTION__ , ## arg); } while (0)
#define err(format, arg...) pr_err(CLASS_NAME ": " format, ## arg)
#define info(format, arg...) pr_info(CLASS_NAME ": " format, ## arg)
#define warn(format, arg...) pr_warn(CLASS_NAME ": " format, ## arg)

wsdisplay color map not being retrieved correctly

I've been trying to use wscons and wsdisplay on NetBSD 5.1.2 (using the VESA framebuffer implementation) recently and I've encountered a bit of a problem:
I can set color maps successfully and they look correct but getting color maps seems to return incorrect data, such that when I try to restore the original color map once the program has finished, all the colors are incorrect:
Here's a reduced program causing the problem (note that it must be run either as root or as the user logged in on the second virtual terminal (/dev/ttyE1)):
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <dev/wscons/wsconsio.h>
int main(int argc, char **argv) {
(void)argc, (void)argv;
int tty = open("/dev/ttyE1", O_RDWR | O_EXCL);
if(tty == -1) {
perror("error opening tty");
return EXIT_FAILURE;
}
struct wsdisplay_fbinfo fbinfo;
if(ioctl(tty, WSDISPLAYIO_GINFO, &fbinfo) == -1) {
perror("error retrieving framebuffer info");
close(tty);
return EXIT_FAILURE;
}
uint8_t *cmap_data = malloc(fbinfo.cmsize * 3);
if(cmap_data == NULL) {
perror("error allocating memory for color map data");
close(tty);
return EXIT_FAILURE;
}
struct wsdisplay_cmap cmap;
cmap.index = 0;
cmap.count = fbinfo.cmsize;
cmap.red = &cmap_data[fbinfo.cmsize * 0];
cmap.green = &cmap_data[fbinfo.cmsize * 1];
cmap.blue = &cmap_data[fbinfo.cmsize * 2];
if(ioctl(tty, WSDISPLAYIO_GETCMAP, &cmap) == -1) {
perror("error getting color map");
close(tty), free(cmap_data);
return EXIT_FAILURE;
}
if(ioctl(tty, WSDISPLAYIO_PUTCMAP, &cmap) == -1) {
perror("error putting color map");
close(tty), free(cmap_data);
return EXIT_FAILURE;
}
free(cmap_data);
close(tty);
return EXIT_SUCCESS;
}
What am I doing wrong and how can I make it retrieve and restore color maps correctly?
Cause
I looked into the problem more and it appears that some kernel memory is either uninitialized or getting corrupted. Specifically, sc_cmap_red, sc_cmap_green, and sc_cmap_blue of struct vesafb_softc (in vesafbvar.h on lines 89 to 91) contain incorrect data. This is somewhat surprising, as lines 719 to 722 of vesafb.c initialize it:
/* Fill in the softc colourmap arrays */
sc->sc_cmap_red[i / 3] = rasops_cmap[i + 0];
sc->sc_cmap_green[i / 3] = rasops_cmap[i + 1];
sc->sc_cmap_blue[i / 3] = rasops_cmap[i + 2];
It contains incorrect data even if I move that out of the if statement it's in, so it might be getting corrupted rather than being uninitialized.
The driver is able to get and set color maps correctly, however; it just cannot seem to get the initial one in struct vesafb_softc right.
Workaround
A simple solution would be to make a program re-set the default color map. As the above snippet indicated, it was supposed to get its initial colors from rasops_cmap, which happens to be defined on lines 55 to 122 in rasops.c:
/* ANSI colormap (R,G,B). Upper 8 are high-intensity */
const u_char rasops_cmap[256*3] = {
/* ... */
};
With those colors, you can make a program that sets that as the current color map. I had to make a few changes so the cursor didn't disappear, but it mostly worked.
Better Solution
As I was looking around for more information, I had found this blog post. When I recompiled the kernel with genfb(4) rather than vesafb(4), the kernel hung at boot. It turns out this was because the bootloader I was using was not new enough to pass the required parameters to the kernel.
I happened to look at the NetBSD 6.0 changelog and noticed this entry:
amd64, i386
The bootloader has been enhanced to support framebuffer consoles using VESA BIOS extensions. These changes allow the x86 ports to work with the genfb(4) driver, and obsoletes the i386-only vesafb(4) driver. [jmcneill 20090216]
I downloaded NetBSD 6.0_BETA and booted it from the boot prompt like this:
> vesa 640x480x8
> boot netbsd
...and everything worked.
In short, using a newer version of NetBSD and ditching vesafb(4) solves the problem.

Resources