Character Driver Issues Implementing ‘/dev/physmem’

advertisement
Character Driver Issues
Implementing ‘/dev/physmem’
Goals for ‘physmem’
•
•
•
•
Let user-programs ‘read’ physical memory
Make this convenient for the programmer
Treat physical memory as just one big file
Then can use standard C library functions:
i.e., open(), lseek(), read(), close()
• But don’t give user-programs write-access!
The ‘mknod’ command
• Device-file: usually created by command
# mknod /dev/physmem c 251 0
• ‘mknod’ takes 4 command-line arguments:
the pathname of the special file
‘c’ indicates it’s a character device
251 is driver’s ID-number (i.e., major)
0 is the device’s ID-number (i.e., minor)
• Need ‘root’ privileges to create /dev file
Default file permissions
• Without ‘root’ privileges, we can’t try it
• But if we could, we’d see file-permissions
• Example:
# mknod /dev/xxx c 251 0
# ls -l /dev/xxx
crw-r—r-- 1 root root 251, 0 25 Feb dev/xxx
• We don’t want users to ‘write’ to physmem
Modifying file-permissions
• Supervisor can change file-permissions
• ‘chmod’ is the privileged command to use
• Example:
# chmod 0444 /dev/xxx
# ls -l /dev/xxx
cr--r—r-- 1 root root 251, 0 25 Feb dev/xxx
• Now the device-file has ‘read-only’ access
A ‘smarter’ driver
• Our drivers can create their own files:
syscall: mknod( pathname, mode, devid);
• Our drivers can modify file-permissions:
syscall: chmod( pathname, mode);
• Our drivers can also delete their own files:
syscall: unlink( pathname );
Some manifest constants
• Header-file: #include <linux/stat.h>
#define S_IFCHR 0020000
#define S_IRUSR 0000400
#define S_IRGRP 0000040
#define S_IROTH 0000004
By ‘or’-ing the latter three values, can get:
#define S_IRUGO 0000444
Driver’s ‘read()’ function
•
•
•
•
Must conform to standard prototype
Otherwise: can’t ‘register_chrdev()’
Return-value has type ‘ssize_t’
Function takes four arguments:
struct file *filp; // pointer to file-structure
char
*buf; // pointer to user-buffer
size_t
count; // size of user-buffer
loff_t
*pos; // points to file-position
Algorithm for ‘read()’
• Step 1: Check for an invalid request:
if ( *pos >= filesize ) return –EINVAL;
• Step 2: Adjust ‘count’ for remaining bytes:
If ( *pos + count > filesize )
count = filesize - *pos;
Algorithm (continued)
• Step 3: Find location of next data bytes
void *from = phys_to_virt( *pos );
• Step 4: Copy data into user-space buffer
more = copy_to_user( buf, from, count );
• Step 5: Return error if copying unfinished
if ( more ) return –EFAULT;
Algorithm (conclusion)
• Step 6: Advance file’s current position
*pos += count;
• Step 7: Return number of bytes copied
return count;
Driver needs ‘llseek()’
• ‘fileview.cpp’ checks for size of device-file
• Standard C library function is ‘lseek()’:
filesize = lseek( fd, 0, SEEK_END );
‘fd’ is the file-descriptor for an opened file
0 is the ‘offset’ to seek to
SEEK_END says offset is from end-of-file
Default ‘llseek()’ won’t work
• No way for kernel to know device’s filesize
• So default ‘llseek()’ will give a wrong result
• Driver must implement its own ‘llseek()’
• For ‘/dev/physmen’ with 1GB memory:
FILESIZE = 0x38000000
• But more generally:
FILESIZE = virt_to_phys( high_memory );
Sample ‘llseek()’ in LDD text
• ‘llseek()’ must handle three cases:
#define SEEK_SET
#define SEEK_CUR
#define SEEK_END
0
1
2
(See author’s example-code on page 163)
Prototype for ‘llseek()’
• Function has three arguments:
struct file *filp;
loff_t offset;
int
whence;
// pointer to file-structure
// file-offset to seek to
// where to seek from
• Return-value has type ‘loff_t’ (new offset)
Algorithm for ‘llseek()’
• Step 1: Use ‘switch’ statement:
switch( whence )
{
case 0: // for SEEK_SET
case 1: // for SEEK_CUR
case 2: // for SEEK_END
default: // should never happen!
}
Actions for ‘switch’ cases
• case SEEK_SET:
newpos = offset; break;
• case SEEK_CUR:
newpos = file->f_pos + offset; break;
• case SEEK_SET:
newpos = filesize + offset; break;
• default:
return -EINVAL;
Algotithm (conclusion)
• Step 2: Check for illegal ‘newpos’ value:
if ( newpos < 0 ) return –EINVAL;
• Step 3: Update ‘f_pos’ in file-structure:
filp->f_pos = newpos;
• Step 4: Return new file-pointer position:
return newpos;
‘physmem’ is read-only
• If device-file lacks ‘write’ permissions, then
kernel won’t call driver’s ‘write()’ function
• So omit write-function from device-driver
in order to avoid having ‘dead code’
Consider your legacy
• Your driver-code may have a future life!
• Another programmer could ‘inherit’ it
• Or you, yourself, may want to ‘reuse’ it
•
•
•
•
Keeping ‘dead code’ will cause confusion
Omitting commentary will cause frustration
Need the author’s name (and contact-info)
Need code’s revision-history (when/why)
Download