I am new to driver programming and I have written a simple char device driver code. When I wrote it without using pointers, it crashed.
When writing to a driver using echo, it works. But when reading from it, there is no output. Someone please help. File operations part of the code is shown below. 'p' and 'q' are normal character pointers. 'max' value was set as 10. 'ptr' is of static int type initialized as '0'.
int my_open(struct inode *inode,struct file *filp)
{
printk("In open.\n");
if((filp->f_flags & O_ACCMODE) == O_WRONLY){
p = (char *)buffer;
ptr = 0;
}
else if((filp->f_flags & O_ACCMODE) == O_RDONLY)
q = (char *)buffer;
return 0;
}
int my_close(struct inode *inode,struct file *filp)
{
printk("In close.\n");
return 0;
}
ssize_t my_read(struct file *filp,char *buff,size_t count,loff_t *pos)
{
long ret;
printk("In read.\n");
ret = copy_to_user(buff,q,max);
q += max;
*pos += max;
if(ptr -= max)
return max;
else
return 0;
}
ssize_t my_write(struct file *filp,const char *buff,size_t count,loff_t *pos)
{
long ret;
printk("In write.\n");
ret = copy_from_user(p,buff,max);
p += max;
*pos += max;
ptr += max;
return max;
}
module_init(my_init);
module_exit(my_exit);
In both read and write you are not taking into account the "count" parameter, as your code seems to assume that "count>=max", that is not guaranteed. This by itself may lead to any sort of troubles in the process executing read. Also, you copy_to/from_user before checking if the current read or write position is over the buffer limit. Moreover, the assignment/test if (ptr -= max) only works if ptr is exactly equal to max, also not guaranteed it you execute the read more than once.
NOTE: since definitions of p, q, buffer, ptr and max are missing, I'll assume that they look like:
static char *p;
static char *q;
statint int ptr = 0;
static char buffer[10];
static int max=10;
Related
I'm getting physical and virtual address bits size with C by using CPUID command in windows.
I can get the processor information this way, but I'm confused by getting the address bits.
Looks like I should you the 80000008 instruction but I do this way, only 7-8 digits change continuously are displayed.
I want to learn how this command works and solve this problem
#include <stdio.h>
void getcpuid(int T, int* val) {
int reg_ax;
int reg_bx;
int reg_cx;
int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
int val[5]; val[4] = 0;
getcpuid(0x80000002, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000003, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000004, val);
printf("%s\r\n", &val[0]);
return 0;
}
when operate this code with putting EAX = 80000002, 80000003, 80000004, Intel processor brand string was displayed.
And I put 80000008 To getting physical and virtual address bits but random numbers changing constantly was displayed.
I want to know how to use this cpuid commend with 80000008 to get those address bits
i'm programming and operating system beginner.
Please let me know what I have to do.
The inline assembly you're using may be right; but this depends on which compiler it is. I think it is right for Microsoft's MSVC (but I've never used it and can't be sure). For GCC (and CLANG) you'd have to inform the compiler that you're modifying the contents of registers and memory (via. a clobber list), and it would be more efficient to tell the compiler that you're outputting 4 values in 4 registers.
The main problem is that you're trying to treat the output as a (null terminated) string; and the data returned by CPUID is never a null terminated string (even for "get vendor string" and "get brand name string", it's a whitespace padded string with no zero terminator).
To fix that you could:
void getcpuid(int T, int* val) {
unsigned int reg_ax;
unsigned int reg_bx;
unsigned int reg_cx;
unsigned int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
uint32_t val[5]; val[4] = 0;
getcpuid(0x80000002U, val);
printf("0x%08X\r\n", val[0]);
getcpuid(0x80000003U, val);
printf("0x%08X\r\n", val[1]);
getcpuid(0x80000004U, val);
printf("0x%08X\r\n", val[2]);
return 0;
}
The next problem is extracting the virtual address size and physical address size values. These are 8-bit values packed into the first and second byte of eax; so:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
That should work on most recent CPUs; which means that it's still awful and broken on older CPUs.
To start fixing that you want to check that the CPU supports "CPUID leaf 0x80000008" before you assume it exists:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
physicalAddressSize = -1;
virtualAddressSize = -1;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
You can return correct results when "CPUID leaf 0x80000008" doesn't exist. For all CPUs that don't support "CPUID leaf 0x80000008"; virtual address size is 32 bits, and the physical address size is either 36 bits (if PAE is supported) or 32 bits (if PAE is not supported). You can use CPUID to determine if the CPU supports PAE, so it ends up a bit like this:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
getcpuid(0x00000000U, val);
if(val[0] == 0) {
physicalAddressSize = 32; // "CPUID leaf 0x00000001" not supported
} else {
getcpuid(0x00000001U, val);
if( val[3] & (1 << 6) != 0) {
physicalAddressSize = 36; // PAE is supported
} else {
physicalAddressSize = 32; // PAE not supported
}
}
virtualAddressSize = 32;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
The other problem is that sometimes CPUID is buggy; which means that you have to trawl through every single errata sheet for every CPU (from Intel, AMD, VIA, etc) to be sure the results from CPUID are actually correct. For example, there are 3 models of "Intel Pentium 4 Processor on 90 nm Process" where "CPUID leaf 0x800000008" is wrong and says "physical addresses are 40 bits" when they are actually 36 bits.
For all of these cases you need to implement work-arounds (e.g. get the CPU vendor/family/model/stepping information from CPUID and if it matches one of the 3 buggy models of Pentium 4, do an "if(physicalAddressSize == 40) physicalAddressSize = 36;" to fix the CPU's bug).
I'm learning to programming using pthread for a adder program, after reference several codes still don't get how to pass multiple arguments into a thread using a struct, here is my buggy program:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
typedef struct s_addition {
int num1;
int num2;
int sum;
} addition;
void *thread_add_function (void *ad)
{
printf ("ad.num1:%d, ad.num2:%d\n",ad.num1, ad.num2);
ad.sum = ad.num1 + ad.num2;
pthread_exit(0);
}
int main()
{
int N = 5;
int a[N], b[N], c[N];
srand (time(NULL));
// fill them with random numbers
for ( int j = 0; j < N; j++ ) {
a[j] = rand() % 392;
b[j] = rand() % 321;
}
addition ad1;
pthread_t thread[N];
for (int i = 0; i < N; i++) {
ad1.num1 = a[i];
ad1.num2 = b[i];
printf ("ad1.num1:%d, ad1.num2:%d\n",ad1.num1, ad1.num2);
pthread_create (&thread[i], NULL, thread_add_function, &ad1);
pthread_join(thread[i], NULL);
c[i] = ad.sum;
}
printf( "This is the result of using pthread.\n");
for ( int i = 0; i < N; i++) {
printf( "%d + %d = %d\n", a[i], b[i], c[i]);
}
}
But when compiling I got the following error:
vecadd_parallel.c:15:39: error: member reference base type 'void *' is not a
structure or union
printf ("ad.num1:%d, ad.num2:%d\n",ad.num1, ad.num2);
I tried but still cannot get a clue, what I am doing wrong with it?
Seems like you have a problem with trying to access the members of a void datatype.
You will need to add a line to cast your parameter to thread_add_function to the correct datatype similar to addition* add = (addition*)ad;, and then use this variable in your function (note that you also have to change you r .'s to -> because it's a pointer)
You also should only pass data to threads that was malloc()'d, as stack allocated data may not be permanent. It should be fine for the current implementation, but changes later could easily give strange, unpredictable behaviour.
I am a student who is trying to learn about Linux character Device Driver. I've been following the tutorial which is written here with some modifications. The read and write system calls for this driver only read and write on a char buffer. The problem is that it seems to me the parameter loff_t *off changes its value when it is supposed no to do that. What I mean is, if you see the next piece of code.
static ssize_t my_write(struct file *f, const char __user *buf,
size_t len, loff_t *off){
int i;
char byte;
printk(KERN_INFO "Driver: write()\n");
printk(KERN_INFO "1. The value of *off is:%i\n",*off);
if (*off >= (BUFFER_SIZE - 1)){
return 0;
}
printk(KERN_INFO "2. The value of *off is:%i\n",*off);
if ((*off + len) > (BUFFER_SIZE - 1)){
len = BUFFER_SIZE - 1 - *off;
}
printk(KERN_INFO "3. The value of *off is:%i\n",*off);
for (i = 0; i < len; i++){
if (copy_from_user(&byte, buf + i, 1)){
return -EFAULT;
}
c[i]=byte;
}
printk(KERN_INFO "4. The value of *off is:%i\n",*off);
c[len]='\n';
*off += len;
printk(KERN_INFO "5. The value of *off is:%i\n",*off);
return len;
}
The outcomes on the console for the printk calls are:
The value of *off is:0
The value of *off is:-1
The value of *off is:0
The value of *off is:693260
The value of *off is:-2132049164
Could you tell me why? Thank you.
From the Chapter 3: Char Drivers in LDD...
off is a pointer to a “long offset type” object that indicates the file position the user is accessing.
Whatever the amount of data the methods transfer, they should generally update the
file position at *offp to represent the current file position after successful completion
of the system call.
For more information refer Figure 3-2 in https://lwn.net/images/pdf/LDD3/ch03.pdf
So I have a struct named task that initializes a few things.
typedef struct _task{
char *task_name;
int priority; // higher numbers have higher priority
char date_entered[11];
char date_completed[11]; // not used yet
} task;
I'm trying to write a function named task *makeTask that takes in (char *name, char *date, and char *priority)
I need to allocate memory for the new task, the name within the new task, and I think the date. So far, I've reached a segmentation fault using this:
task *makeTask(char *name, char *date, char *priority)
{
int i;
int j;
int k;
task *newtask = malloc(sizeof(task));
for(i=0; name[i] != '\0'; i++){
if(name[i] == '\n')
name[i] = '\0';
}
newtask->task_name = malloc(sizeof(char *)*(strlen(name)+1));
strcpy(newtask->task_name, name);
newtask->priority = atoi(priority);
for(j=0; date[j] != '\0'; j++){
if(date[j] == '\n')
date[j] == '\0';
}
return newtask;// FILE THIS IN
}
I think I don't have a really solid understanding of strings and how to manipulate them. Why is my code here giving me a segmentation fault? Is it the loops or the way I've allocated memory for the struct? Any help would be appreciated.
I haven't found anything yet that would guarantee a segmentation fault, but here are some issues.
your loops,
for(i=0; name[i] != '\0'; i++){
if(name[i] == '\n')
name[i] = '\0';
}
aside from being somewhat bizarre, You're presuming that name has been properly initialized. If it hasn't been, then that loop will throw a segmentation fault. the same goes for your date loop
the following line:
newtask->task_name = malloc(sizeof(char )(strlen(name)+1));
sizeof(char *), is a little odd, because you're initializing an array of characters, rather than an array of character pointers(an array of arrays). I don't know if that really makes too much of a difference, but it is odd.
I use
size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
to convert UTF-16BE to GB2312.
inbytesleft is bytes number to be convert. After conversion, inbytesleft is bytes number of not converted.
After one call, I found inbytesleft is -2, according to iconv man page this function should read at most inbytesleft.
Who can tell my why and how to fix this?
code to be convert is
"保单验证"
Thanks
How are you getting the input data into your program?
I've tested the situation using this code and it seems to work:
#include <stdio.h>
#include <iconv.h>
#include <errno.h>
int main(){
char data[10] = {0x4f,0xdd,0x53,0x55,0x9a,0x8c,0x8b,0xc1, 0, 0};
char outdata[20];
char *dataptr;
char *outdataptr;
iconv_t cd;
size_t result;
size_t inbytesleft = 8;
size_t outbytesleft = 20;
int i;
cd = iconv_open("GB2312", "UTF-16BE");
dataptr = data;
outdataptr = outdata;
result = iconv(cd, &dataptr, &inbytesleft, &outdataptr, &outbytesleft);
if(result == -1)
printf("Error: %d\n", errno);
printf(" result: %zd\n", result);
printf(" inbytesleft: %zd\n", inbytesleft);
printf("outbytesleft: %zd\n", outbytesleft);
for(i = 20; i > outbytesleft; i--){
if(i != 20)
printf(",");
printf("0x%02x", *((unsigned char *)&(outdata[20-i])));
}
printf("\n");
return 0;
}
It prints
result: 0
inbytesleft: 0
outbytesleft: 12
0xb1,0xa3,0xb5,0xa5,0xd1,0xe9,0xd6,0xa4
Which appears to be correct.
The array of items in the variable data is the UTF-16BE encoding of 保单验证
If this doesn't help, could you post your code for analysis?