diff options
author | Jack Sangdahl <[email protected]> | 2024-04-02 23:09:00 -0600 |
---|---|---|
committer | Jack Sangdahl <[email protected]> | 2024-04-02 23:09:00 -0600 |
commit | 8dbe92136c33a2254b617e6d5273d5499de5ad90 (patch) | |
tree | 310c981ca2d57559d00d87366984715286c2e28c | |
parent | 4485201ffbadee4126f04575b1a4aa991152b22c (diff) | |
download | csci3753-8dbe92136c33a2254b617e6d5273d5499de5ad90.tar.gz csci3753-8dbe92136c33a2254b617e6d5273d5499de5ad90.zip |
pa3
-rw-r--r-- | pa3_device_driver/.char_driver_sample | bin | 0 -> 260976 bytes | |||
-rw-r--r-- | pa3_device_driver/Makefile | 8 | ||||
-rw-r--r-- | pa3_device_driver/char_driver.c | 100 | ||||
-rw-r--r-- | pa3_device_driver/char_driver_sample.s | 393 | ||||
-rw-r--r-- | pa3_device_driver/driver.jpg | bin | 0 -> 19419 bytes | |||
-rwxr-xr-x | pa3_device_driver/module.sh | 24 | ||||
-rw-r--r-- | pa3_device_driver/readme.md | 98 |
7 files changed, 623 insertions, 0 deletions
diff --git a/pa3_device_driver/.char_driver_sample b/pa3_device_driver/.char_driver_sample Binary files differnew file mode 100644 index 0000000..7a5c5e8 --- /dev/null +++ b/pa3_device_driver/.char_driver_sample diff --git a/pa3_device_driver/Makefile b/pa3_device_driver/Makefile new file mode 100644 index 0000000..cf7e700 --- /dev/null +++ b/pa3_device_driver/Makefile @@ -0,0 +1,8 @@ +obj-m += char_driver.o +KBUILD_DIR ?= /lib/modules/`uname -r`/build + +all: + make -C $(KBUILD_DIR) M=`pwd` + +clean: + make -C $(KBUILD_DIR) M=`pwd` clean diff --git a/pa3_device_driver/char_driver.c b/pa3_device_driver/char_driver.c new file mode 100644 index 0000000..7ca63e3 --- /dev/null +++ b/pa3_device_driver/char_driver.c @@ -0,0 +1,100 @@ +// Jack Sangdahl +// CSCI3753 SP24 +// PA3 Device Driver +#include <linux/fs.h> +#include <linux/module.h> + +MODULE_AUTHOR("jasa7131"); +MODULE_LICENSE("GPL"); + +#define BUFFER_SIZE 900 +#define DEVICE_NAME "char_dev" +static int major, counter = 0; +static char *data; + +int char_driver_open(struct inode *, struct file *); +int char_driver_close(struct inode *, struct file *); +ssize_t char_driver_read(struct file *, char *, size_t, loff_t *); +ssize_t char_driver_write(struct file *, const char *, size_t, loff_t *); +loff_t char_driver_seek(struct file *, loff_t, int); + +struct file_operations char_fops = {.owner = THIS_MODULE, + .open = char_driver_open, + .release = char_driver_close, + .read = char_driver_read, + .write = char_driver_write, + .llseek = char_driver_seek}; + +int char_driver_open(struct inode *pinode, struct file *pfile) { + counter++; + printk("Device opened %d times\n", counter); + return 0; +} + +int char_driver_close(struct inode *pinode, struct file *pfile) { + printk("Device closed %d times\n", counter); + return 0; +} + +ssize_t char_driver_read(struct file *pfile, char __user *buffer, size_t length, + loff_t *offset) { + ssize_t data_read = simple_read_from_buffer(buffer, length, offset, + data + *offset, BUFFER_SIZE); + if (data_read < 0) { + printk("READ FAILED: Failed to read from user buffer.\n"); + return -EFAULT; + } + if (data_read) { + printk("READ SUCCESS: Read %ld bytes from %s.\n", data_read, DEVICE_NAME); + if (data_read >= BUFFER_SIZE) + printk("Reached EOF\n"); + } + return data_read; +} + +ssize_t char_driver_write(struct file *pfile, const char __user *buffer, + size_t length, loff_t *offset) { + ssize_t data_written = simple_write_to_buffer(data + *offset, BUFFER_SIZE, + offset, buffer, length); + printk("WRITE SUCCESS: Wrote %ld bytes to %s.\n", data_written, DEVICE_NAME); + if (data_written > BUFFER_SIZE) + printk("WRITE ERR: Buffer is filled.\n"); + return data_written; +} + +loff_t char_driver_seek(struct file *pfile, loff_t offset, int whence) { + switch (whence) { + case SEEK_SET: + pfile->f_pos = offset; + break; + case SEEK_CUR: + pfile->f_pos += offset; + break; + case SEEK_END: + pfile->f_pos = strlen(data) - offset; + break; + } + return pfile->f_pos; +} + +static int char_driver_init(void) { + printk("Registering device %s\n", DEVICE_NAME); + data = (char *)kmalloc(BUFFER_SIZE * sizeof(char), GFP_KERNEL); + major = register_chrdev(511, DEVICE_NAME, &char_fops); + if (major < 0) { + printk(KERN_ALERT "REGISTRATION FAILED: %s could not be reigstered.\n", + DEVICE_NAME); + return -EFAULT; + } + printk("REGISTRATION SUCCESS: %s successfully registered.\n", DEVICE_NAME); + return 0; +} + +static void char_driver_exit(void) { + kfree(data); + unregister_chrdev(major, DEVICE_NAME); + printk("UNREGISTER SUCCESS: %s successfully unregistered\n", DEVICE_NAME); +} + +module_init(char_driver_init); +module_exit(char_driver_exit); diff --git a/pa3_device_driver/char_driver_sample.s b/pa3_device_driver/char_driver_sample.s new file mode 100644 index 0000000..52b856b --- /dev/null +++ b/pa3_device_driver/char_driver_sample.s @@ -0,0 +1,393 @@ + +char_driver_sample.ko: file format elf64-x86-64 + + +Disassembly of section .text: + +0000000000000000 <__pfx_my_open>: + 0: 90 nop + 1: 90 nop + 2: 90 nop + 3: 90 nop + 4: 90 nop + 5: 90 nop + 6: 90 nop + 7: 90 nop + 8: 90 nop + 9: 90 nop + a: 90 nop + b: 90 nop + c: 90 nop + d: 90 nop + e: 90 nop + f: 90 nop + +0000000000000010 <my_open>: + 10: e8 00 00 00 00 call 15 <my_open+0x5> + 15: 55 push %rbp + 16: 31 c0 xor %eax,%eax + 18: 83 05 00 00 00 00 01 addl $0x1,0x0(%rip) # 1f <my_open+0xf> + 1f: 48 89 e5 mov %rsp,%rbp + 22: 5d pop %rbp + 23: e9 00 00 00 00 jmp 28 <my_open+0x18> + 28: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) + 2f: 00 + +0000000000000030 <__pfx_my_release>: + 30: 90 nop + 31: 90 nop + 32: 90 nop + 33: 90 nop + 34: 90 nop + 35: 90 nop + 36: 90 nop + 37: 90 nop + 38: 90 nop + 39: 90 nop + 3a: 90 nop + 3b: 90 nop + 3c: 90 nop + 3d: 90 nop + 3e: 90 nop + 3f: 90 nop + +0000000000000040 <my_release>: + 40: e8 00 00 00 00 call 45 <my_release+0x5> + 45: 55 push %rbp + 46: 31 c0 xor %eax,%eax + 48: 83 2d 00 00 00 00 01 subl $0x1,0x0(%rip) # 4f <my_release+0xf> + 4f: 48 89 e5 mov %rsp,%rbp + 52: 5d pop %rbp + 53: e9 00 00 00 00 jmp 58 <my_release+0x18> + 58: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) + 5f: 00 + +0000000000000060 <__pfx_my_llseek>: + 60: 90 nop + 61: 90 nop + 62: 90 nop + 63: 90 nop + 64: 90 nop + 65: 90 nop + 66: 90 nop + 67: 90 nop + 68: 90 nop + 69: 90 nop + 6a: 90 nop + 6b: 90 nop + 6c: 90 nop + 6d: 90 nop + 6e: 90 nop + 6f: 90 nop + +0000000000000070 <my_llseek>: + 70: e8 00 00 00 00 call 75 <my_llseek+0x5> + 75: 55 push %rbp + 76: 48 89 e5 mov %rsp,%rbp + 79: 83 fa 01 cmp $0x1,%edx + 7c: 74 23 je a1 <my_llseek+0x31> + 7e: 83 fa 02 cmp $0x2,%edx + 81: 74 43 je c6 <my_llseek+0x56> + 83: 85 d2 test %edx,%edx + 85: 74 1e je a5 <my_llseek+0x35> + 87: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 8e: e8 00 00 00 00 call 93 <my_llseek+0x23> + 93: 31 c0 xor %eax,%eax + 95: 5d pop %rbp + 96: 31 d2 xor %edx,%edx + 98: 31 f6 xor %esi,%esi + 9a: 31 ff xor %edi,%edi + 9c: e9 00 00 00 00 jmp a1 <my_llseek+0x31> + a1: 48 03 77 40 add 0x40(%rdi),%rsi + a5: 48 85 f6 test %rsi,%rsi + a8: 78 25 js cf <my_llseek+0x5f> + aa: b8 00 04 00 00 mov $0x400,%eax + af: 48 39 c6 cmp %rax,%rsi + b2: 48 0f 4e c6 cmovle %rsi,%rax + b6: 48 89 47 40 mov %rax,0x40(%rdi) + ba: 5d pop %rbp + bb: 31 d2 xor %edx,%edx + bd: 31 f6 xor %esi,%esi + bf: 31 ff xor %edi,%edi + c1: e9 00 00 00 00 jmp c6 <my_llseek+0x56> + c6: 48 81 c6 00 04 00 00 add $0x400,%rsi + cd: eb d6 jmp a5 <my_llseek+0x35> + cf: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + d6: e8 00 00 00 00 call db <my_llseek+0x6b> + db: 48 c7 c0 ea ff ff ff mov $0xffffffffffffffea,%rax + e2: 5d pop %rbp + e3: 31 d2 xor %edx,%edx + e5: 31 f6 xor %esi,%esi + e7: 31 ff xor %edi,%edi + e9: e9 00 00 00 00 jmp ee <my_llseek+0x7e> + ee: 66 90 xchg %ax,%ax + +00000000000000f0 <__pfx_my_read>: + f0: 90 nop + f1: 90 nop + f2: 90 nop + f3: 90 nop + f4: 90 nop + f5: 90 nop + f6: 90 nop + f7: 90 nop + f8: 90 nop + f9: 90 nop + fa: 90 nop + fb: 90 nop + fc: 90 nop + fd: 90 nop + fe: 90 nop + ff: 90 nop + +0000000000000100 <my_read>: + 100: e8 00 00 00 00 call 105 <my_read+0x5> + 105: 55 push %rbp + 106: 48 8b 39 mov (%rcx),%rdi + 109: 48 89 e5 mov %rsp,%rbp + 10c: 41 56 push %r14 + 10e: 41 55 push %r13 + 110: 41 54 push %r12 + 112: 53 push %rbx + 113: bb 00 04 00 00 mov $0x400,%ebx + 118: 48 29 fb sub %rdi,%rbx + 11b: 74 72 je 18f <my_read+0x8f> + 11d: 48 39 d3 cmp %rdx,%rbx + 120: 4c 8d a7 00 00 00 00 lea 0x0(%rdi),%r12 + 127: 48 0f 47 da cmova %rdx,%rbx + 12b: 48 81 fb ff ff ff 7f cmp $0x7fffffff,%rbx + 132: 77 7e ja 1b2 <my_read+0xb2> + 134: 49 89 f6 mov %rsi,%r14 + 137: ba 01 00 00 00 mov $0x1,%edx + 13c: 48 89 de mov %rbx,%rsi + 13f: 4c 89 e7 mov %r12,%rdi + 142: 49 89 cd mov %rcx,%r13 + 145: e8 00 00 00 00 call 14a <my_read+0x4a> + 14a: 48 89 da mov %rbx,%rdx + 14d: 4c 89 e6 mov %r12,%rsi + 150: 4c 89 f7 mov %r14,%rdi + 153: e8 00 00 00 00 call 158 <my_read+0x58> + 158: 48 85 c0 test %rax,%rax + 15b: 75 57 jne 1b4 <my_read+0xb4> + 15d: 49 01 5d 00 add %rbx,0x0(%r13) + 161: 48 c7 c2 00 00 00 00 mov $0x0,%rdx + 168: 48 89 de mov %rbx,%rsi + 16b: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 172: e8 00 00 00 00 call 177 <my_read+0x77> + 177: 48 89 d8 mov %rbx,%rax + 17a: 5b pop %rbx + 17b: 41 5c pop %r12 + 17d: 41 5d pop %r13 + 17f: 41 5e pop %r14 + 181: 5d pop %rbp + 182: 31 d2 xor %edx,%edx + 184: 31 c9 xor %ecx,%ecx + 186: 31 f6 xor %esi,%esi + 188: 31 ff xor %edi,%edi + 18a: e9 00 00 00 00 jmp 18f <my_read+0x8f> + 18f: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 196: e8 00 00 00 00 call 19b <my_read+0x9b> + 19b: 31 c0 xor %eax,%eax + 19d: 5b pop %rbx + 19e: 41 5c pop %r12 + 1a0: 41 5d pop %r13 + 1a2: 41 5e pop %r14 + 1a4: 5d pop %rbp + 1a5: 31 d2 xor %edx,%edx + 1a7: 31 c9 xor %ecx,%ecx + 1a9: 31 f6 xor %esi,%esi + 1ab: 31 ff xor %edi,%edi + 1ad: e9 00 00 00 00 jmp 1b2 <my_read+0xb2> + 1b2: 0f 0b ud2 + 1b4: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 1bb: e8 00 00 00 00 call 1c0 <my_read+0xc0> + 1c0: 48 c7 c0 f2 ff ff ff mov $0xfffffffffffffff2,%rax + 1c7: eb b1 jmp 17a <my_read+0x7a> + 1c9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) + +00000000000001d0 <__pfx_init_module>: + 1d0: 90 nop + 1d1: 90 nop + 1d2: 90 nop + 1d3: 90 nop + 1d4: 90 nop + 1d5: 90 nop + 1d6: 90 nop + 1d7: 90 nop + 1d8: 90 nop + 1d9: 90 nop + 1da: 90 nop + 1db: 90 nop + 1dc: 90 nop + 1dd: 90 nop + 1de: 90 nop + 1df: 90 nop + +00000000000001e0 <init_module>: + 1e0: e8 00 00 00 00 call 1e5 <init_module+0x5> + 1e5: 55 push %rbp + 1e6: 48 c7 c6 00 00 00 00 mov $0x0,%rsi + 1ed: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 1f4: 48 89 e5 mov %rsp,%rbp + 1f7: 53 push %rbx + 1f8: e8 00 00 00 00 call 1fd <init_module+0x1d> + 1fd: 49 c7 c0 00 00 00 00 mov $0x0,%r8 + 204: 48 c7 c1 00 00 00 00 mov $0x0,%rcx + 20b: 31 f6 xor %esi,%esi + 20d: ba 00 01 00 00 mov $0x100,%edx + 212: bf ff 01 00 00 mov $0x1ff,%edi + 217: e8 00 00 00 00 call 21c <init_module+0x3c> + 21c: 89 c3 mov %eax,%ebx + 21e: 85 c0 test %eax,%eax + 220: 75 2a jne 24c <init_module+0x6c> + 222: 48 c7 c6 00 00 00 00 mov $0x0,%rsi + 229: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 230: e8 00 00 00 00 call 235 <init_module+0x55> + 235: 89 d8 mov %ebx,%eax + 237: 48 8b 5d f8 mov -0x8(%rbp),%rbx + 23b: c9 leave + 23c: 31 d2 xor %edx,%edx + 23e: 31 c9 xor %ecx,%ecx + 240: 31 f6 xor %esi,%esi + 242: 31 ff xor %edi,%edi + 244: 45 31 c0 xor %r8d,%r8d + 247: e9 00 00 00 00 jmp 24c <init_module+0x6c> + 24c: 89 c6 mov %eax,%esi + 24e: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 255: e8 00 00 00 00 call 25a <init_module+0x7a> + 25a: 89 d8 mov %ebx,%eax + 25c: 48 8b 5d f8 mov -0x8(%rbp),%rbx + 260: c9 leave + 261: 31 d2 xor %edx,%edx + 263: 31 c9 xor %ecx,%ecx + 265: 31 f6 xor %esi,%esi + 267: 31 ff xor %edi,%edi + 269: 45 31 c0 xor %r8d,%r8d + 26c: e9 00 00 00 00 jmp 271 <init_module+0x91> + 271: 66 66 2e 0f 1f 84 00 data16 cs nopw 0x0(%rax,%rax,1) + 278: 00 00 00 00 + 27c: 0f 1f 40 00 nopl 0x0(%rax) + +0000000000000280 <__pfx_cleanup_module>: + 280: 90 nop + 281: 90 nop + 282: 90 nop + 283: 90 nop + 284: 90 nop + 285: 90 nop + 286: 90 nop + 287: 90 nop + 288: 90 nop + 289: 90 nop + 28a: 90 nop + 28b: 90 nop + 28c: 90 nop + 28d: 90 nop + 28e: 90 nop + 28f: 90 nop + +0000000000000290 <cleanup_module>: + 290: e8 00 00 00 00 call 295 <cleanup_module+0x5> + 295: 55 push %rbp + 296: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 29c <cleanup_module+0xc> + 29c: 48 89 e5 mov %rsp,%rbp + 29f: 85 c0 test %eax,%eax + 2a1: 75 3b jne 2de <cleanup_module+0x4e> + 2a3: 48 c7 c1 00 00 00 00 mov $0x0,%rcx + 2aa: ba 00 01 00 00 mov $0x100,%edx + 2af: 31 f6 xor %esi,%esi + 2b1: bf ff 01 00 00 mov $0x1ff,%edi + 2b6: e8 00 00 00 00 call 2bb <cleanup_module+0x2b> + 2bb: 48 c7 c6 00 00 00 00 mov $0x0,%rsi + 2c2: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 2c9: e8 00 00 00 00 call 2ce <cleanup_module+0x3e> + 2ce: 5d pop %rbp + 2cf: 31 c0 xor %eax,%eax + 2d1: 31 d2 xor %edx,%edx + 2d3: 31 c9 xor %ecx,%ecx + 2d5: 31 f6 xor %esi,%esi + 2d7: 31 ff xor %edi,%edi + 2d9: e9 00 00 00 00 jmp 2de <cleanup_module+0x4e> + 2de: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 2e5: e8 00 00 00 00 call 2ea <cleanup_module+0x5a> + 2ea: 5d pop %rbp + 2eb: 31 c0 xor %eax,%eax + 2ed: 31 d2 xor %edx,%edx + 2ef: 31 c9 xor %ecx,%ecx + 2f1: 31 f6 xor %esi,%esi + 2f3: 31 ff xor %edi,%edi + 2f5: e9 00 00 00 00 jmp 2fa <cleanup_module+0x6a> + 2fa: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) + +0000000000000300 <__pfx_my_write>: + 300: 90 nop + 301: 90 nop + 302: 90 nop + 303: 90 nop + 304: 90 nop + 305: 90 nop + 306: 90 nop + 307: 90 nop + 308: 90 nop + 309: 90 nop + 30a: 90 nop + 30b: 90 nop + 30c: 90 nop + 30d: 90 nop + 30e: 90 nop + 30f: 90 nop + +0000000000000310 <my_write>: + 310: e8 00 00 00 00 call 315 <my_write+0x5> + 315: 55 push %rbp + 316: 48 8b 39 mov (%rcx),%rdi + 319: 48 89 e5 mov %rsp,%rbp + 31c: 41 56 push %r14 + 31e: 41 55 push %r13 + 320: 41 54 push %r12 + 322: 53 push %rbx + 323: bb 00 04 00 00 mov $0x400,%ebx + 328: 48 29 fb sub %rdi,%rbx + 32b: 0f 84 85 00 00 00 je 3b6 <my_write+0xa6> + 331: 48 39 d3 cmp %rdx,%rbx + 334: 4c 8d a7 00 00 00 00 lea 0x0(%rdi),%r12 + 33b: 48 0f 47 da cmova %rdx,%rbx + 33f: 48 81 fb ff ff ff 7f cmp $0x7fffffff,%rbx + 346: 77 57 ja 39f <my_write+0x8f> + 348: 31 d2 xor %edx,%edx + 34a: 49 89 f5 mov %rsi,%r13 + 34d: 4c 89 e7 mov %r12,%rdi + 350: 48 89 de mov %rbx,%rsi + 353: 49 89 ce mov %rcx,%r14 + 356: e8 00 00 00 00 call 35b <my_write+0x4b> + 35b: 48 89 da mov %rbx,%rdx + 35e: 4c 89 ee mov %r13,%rsi + 361: 4c 89 e7 mov %r12,%rdi + 364: e8 00 00 00 00 call 369 <my_write+0x59> + 369: 48 85 c0 test %rax,%rax + 36c: 75 33 jne 3a1 <my_write+0x91> + 36e: 49 01 1e add %rbx,(%r14) + 371: 48 c7 c2 00 00 00 00 mov $0x0,%rdx + 378: 48 89 de mov %rbx,%rsi + 37b: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 382: e8 00 00 00 00 call 387 <my_write+0x77> + 387: 48 89 d8 mov %rbx,%rax + 38a: 5b pop %rbx + 38b: 41 5c pop %r12 + 38d: 41 5d pop %r13 + 38f: 41 5e pop %r14 + 391: 5d pop %rbp + 392: 31 d2 xor %edx,%edx + 394: 31 c9 xor %ecx,%ecx + 396: 31 f6 xor %esi,%esi + 398: 31 ff xor %edi,%edi + 39a: e9 00 00 00 00 jmp 39f <my_write+0x8f> + 39f: 0f 0b ud2 + 3a1: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 3a8: e8 00 00 00 00 call 3ad <my_write+0x9d> + 3ad: 48 c7 c0 f2 ff ff ff mov $0xfffffffffffffff2,%rax + 3b4: eb d4 jmp 38a <my_write+0x7a> + 3b6: 48 c7 c7 00 00 00 00 mov $0x0,%rdi + 3bd: e8 00 00 00 00 call 3c2 <my_write+0xb2> + 3c2: 48 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%rax + 3c9: eb bf jmp 38a <my_write+0x7a> diff --git a/pa3_device_driver/driver.jpg b/pa3_device_driver/driver.jpg Binary files differnew file mode 100644 index 0000000..55cc01f --- /dev/null +++ b/pa3_device_driver/driver.jpg diff --git a/pa3_device_driver/module.sh b/pa3_device_driver/module.sh new file mode 100755 index 0000000..3aec3a1 --- /dev/null +++ b/pa3_device_driver/module.sh @@ -0,0 +1,24 @@ +#!/bin/bash +if [ "$1" == "sample" ]; then + MAJOR=511 + MODULE="pa3_char_driver" + DEVICE="/dev/simple_character_device" + if [ ! -f "$MODULE.ko" ]; then + cp .char_driver_sample $MODULE.ko + fi +elif [ "$1" == "test" ]; then + MAJOR=241 + MODULE="char_driver" + DEVICE="/dev/char_dev" +else + echo "usage: ./module.sh [sample] [test]" +fi + +if [ $(rg -q $MODULE /proc/modules) ]; then + rmmod $MODULE +fi +if [ -f $DEVICE ]; then + rm $DEVICE +fi +mknod -m 777 $DEVICE c $MAJOR 0 +insmod "$MODULE.ko" diff --git a/pa3_device_driver/readme.md b/pa3_device_driver/readme.md new file mode 100644 index 0000000..c96b2a6 --- /dev/null +++ b/pa3_device_driver/readme.md @@ -0,0 +1,98 @@ +## PA3 - Device Driver +### introduction +this assignment will explore file i/o in the kernel and how to implement a basic device driver +using a loadable kernel module (lkm) + +### devices in linux +device i/o is modeled using files. reading from and writing to a file will invoke the associated +device driver to do the actual reading and writing. all device drivers have a major and minor +number where the major number is unique to every driver, and the minor number differentiates all +the devices belonging to that driver. eg, a typical system will have multiple hard disks, or at +least partitions on a single disk. a single major number number is used to specify the hard disk +device driver, but each partition has a different minor number. +```shell +$ file /dev/sda* +/dev/sda: block special (8/0) +/dev/sda1: block special (8/1) +/dev/sda2: block special (8/2) +/dev/sda3: block special (8/3) +``` + +there are two kinds of device drivers: +* **character driver** reads and writes to/from the device character by character. this is the +most basic type of device driver and is simplest to implement +* **block device driver** reads and writes chunks (blocks) of data with a single read/write +operation. these types of drivers are more complex, but usually more efficient in use of system +resources. network interfaces and disk controller generally prefer to use a block driver. + +### creating device file +to access device driver, a correspondingfile in the `/dev` directory is needed. create it with +`mknod -m <permission> <path> <type> <major> <minor>`, for example +`mknod -m 777 /dev/simple_char_device c 511 0` creates a file where the permission is set so all +users can read, write, and execute the file, `c` specifies the type of driver as a character +driver, 511 is the major number, and 0 is the minor number. + +### driver overview +a userspace program will be used to issue calles `open()`, `read()`, `write()`, `seek()`, or +`close()`. these calls will access the file created in `/dev`, which is associated with driver. +for example. the driver by way of its major and minor numbers indicates to the kernel that you +are trying to perform file operations on a device. the kernel will invoke corresponding file +operations in on driver file. driver then executes it's implementation of these file operations +against the physical device. (our device driver will only read and write to memory regions) +![image](driver.jpg) + +### implementation +along with header files necessary for module programming, `linux/fs.h` and `linux/uaccess.h` files +will also need to be included. delcare `init()` and `exit()` functions with `module_init()` and +`module_exit()` to bind these functions. in your init function, register your character driver +using `register_chrdev()`. in exit function, unregister the driver using `unregister_chrdev()`. +`register_chrdev()` takes three parameters; major number, unique name, and pointer to file +operations struct. we'll use dynamically allocated kernel buffer (`device_buffer`) with a fixed +size of 900 bytes to store the data written to device. there are two core functions to manage +memory in the kernel defined in `linux/slab.h`: `void* kmalloc(size, flags)` and +`void kfree(kptr)`. use `GFP_KERNEL` as the flags argument above. + +### operations +to perform operations on device driver, a `file_operations` struct is needed. system defined struct +is found in `/lib/modules/.../build/include/linux/fs.h`. create a similar structure with the same +`struct file_operations` type, but different name. define only the open, close, seek, read, and +write operations. **these are the five functions you need to implement**, and set the function +pointer in `file_operations` struct to point to your implementations. note, there is no close +function here. use `release()` instead. specifications are found below + +**open** -- function takes two parameters; a pointer to an inode struct which represents the +physical file on the disk, and a pointer to a file struct which represents the state of a file, +and returns an integer indicating success or failure. this function does not need to do anything +other than log the number of times the device has been opened + +**release** -- function takes the same two arguments as `open()` an**release** - function takes the same two arguments as `open()` and again returns an integer indicating success or failure. use +`printk()` to output the number of times the device has been closed + +**read** - function expects four parameters: a file pointer, a pointer to a userspace buffer, the +size of that buffer, and a pointer to the current position. use the function `copy_to_user()` to +copy data from the `device_buffer` starting at the current position to the userspace buffer. if +successful, update the current position and return the number of bytes read and print the value + +**write** -- function is similar to read. copy data stored in the userspace buffer into the +`device_buffer` using `copy_from_user()`. the write starts from the current position and if +successful, the position should be updated. print the number of bytes written. finally, return +the number of bytes written to the caller. if the user sets the current position back to the +beginning of the file (using seek), this operation may overwrite existing data + +**seek** -- function takes three parameters: a file pointer, an offset, and the whence value. +whence describes how to interpret the offset (which can be negative). if the value of whence is +0 (`SEEK_SET`), the position is set to the value of the offset. if the value of whence is 1 +(`SEEK_CUR`), the current position is incremented (or decremented if negative offset) by offset +bytes. lastly if the value of whence is 2 (`SEEK_END`), the position is set to the offset bytes +from the end of the file + +if a user attempts to seek before the beginning or beyond the end of the `device_buffer`, the seek +should set the current position to the beginning or end of the file respectively. in other words, +a seek should never fail. if a user attempts to read or write where the number of bytes would pass +the end of the `device_buffer`, driver should only read or write until the end of the buffer and +then return the number of bytes actually read or written. + +### install & test module +verify it is installed by checking kernel log for `printk()` outputs, the `lsmod` command, or by +looking in `/proc/devices`. try to write into and read from device using commands like echo, cat, +head, tail, etc. |