Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.

Commit 4795eb3

Browse files
committed
Start hacking towards a test case
1 parent a720a1a commit 4795eb3

File tree

6 files changed

+170
-1
lines changed

6 files changed

+170
-1
lines changed

build.rs

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use std::process::Command;
88

99
const INCLUDED_TYPES: &[&str] = &["file_system_type", "mode_t", "umode_t", "ctl_table"];
1010
const INCLUDED_FUNCTIONS: &[&str] = &[
11+
"cdev_add",
12+
"cdev_alloc",
13+
"cdev_del",
1114
"register_filesystem",
1215
"unregister_filesystem",
1316
"krealloc",

src/bindings_helper.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
#include <linux/module.h>
1+
#include <linux/cdev.h>
22
#include <linux/fs.h>
3+
#include <linux/module.h>
34
#include <linux/slab.h>
45
#include <linux/uaccess.h>
56

src/chrdev.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use core::mem;
12
use core::ops::Range;
23

34
use crate::bindings;
@@ -33,6 +34,30 @@ impl DeviceNumberRegion {
3334
}
3435
return Ok(DeviceNumberRegion { dev, count });
3536
}
37+
38+
pub fn register_device<T: FileOperations>(
39+
&mut self,
40+
) -> error::KernelResult<DeviceRegistration> {
41+
let cdev = unsafe { bindings::cdev_alloc() };
42+
if cdev.is_null() {
43+
return Err(error::Error::ENOMEM);
44+
}
45+
unsafe {
46+
(*cdev).owner = &mut bindings::__this_module;
47+
(*cdev).ops = &T::VTABLE.0 as *const bindings::file_operations;
48+
}
49+
let cdev = DeviceRegistration {
50+
cdev,
51+
_registration: self,
52+
};
53+
// TODO: Need to handle multiple register_device calls by going through the devs and
54+
// erroring if we run out.
55+
let rc = unsafe { bindings::cdev_add(cdev.cdev, self.dev, 1) };
56+
if rc != 0 {
57+
return Err(error::Error::from_kernel_errno(rc));
58+
}
59+
return Ok(cdev);
60+
}
3661
}
3762

3863
impl Drop for DeviceNumberRegion {
@@ -42,3 +67,28 @@ impl Drop for DeviceNumberRegion {
4267
}
4368
}
4469
}
70+
71+
pub struct DeviceRegistration<'a> {
72+
_registration: &'a DeviceNumberRegion,
73+
cdev: *mut bindings::cdev,
74+
}
75+
76+
impl Drop for DeviceRegistration<'_> {
77+
fn drop(&mut self) {
78+
unsafe {
79+
bindings::cdev_del(self.cdev);
80+
}
81+
}
82+
}
83+
84+
pub struct FileOperationsVtable(bindings::file_operations);
85+
86+
impl FileOperationsVtable {
87+
fn new<T: FileOperations>() -> FileOperationsVtable {
88+
return FileOperationsVtable(unsafe { mem::zeroed() });
89+
}
90+
}
91+
92+
pub trait FileOperations {
93+
const VTABLE: FileOperationsVtable;
94+
}

tests/chrdev/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "chrdev-tests"
3+
version = "0.1.0"
4+
authors = ["Alex Gaynor <[email protected]>", "Geoffrey Thomas <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["staticlib"]
9+
10+
[dependencies]
11+
linux-kernel-module = { path = "../.." }

tests/chrdev/src/lib.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![no_std]
2+
#![feature(const_str_as_bytes)]
3+
4+
use linux_kernel_module;
5+
6+
struct CycleFile;
7+
8+
impl linux_kernel_module::chrdev::FileOperations for CycleFile {
9+
const VTABLE: linux_kernel_module::chrdev::FileOperationsVtable =
10+
linux_kernel_module::chrdev::FileOperationsVtable::new::<Self>();
11+
12+
fn read(
13+
&mut self,
14+
buf: &linux_kernel_module::user_ptr::UserSlicePtrWriter,
15+
) -> linux_kernel_module::KernelResult<()> {
16+
for c in b"123456789".iter().cycle().take(buf.len()) {
17+
buf.write(c)?;
18+
}
19+
return Ok(());
20+
}
21+
}
22+
23+
struct ChrdevTestModule {
24+
_dev_region: linux_kernel_module::chrdev::DeviceNumberRegion,
25+
_chrdev_registration: linux_kernel_module::chrdev::DeviceRegistration<CycleFile>,
26+
}
27+
28+
impl linux_kernel_module::KernelModule for ChrdevTestModule {
29+
fn init() -> linux_kernel_module::KernelResult<Self> {
30+
let dev_region =
31+
linux_kernel_module::chrdev::DeviceNumberRegion::allocate(0..1, "chrdev-tests\x00")?;
32+
let chrdev_registration = dev_region.register_device::<CycleFile>()?;
33+
Ok(ChrdevTestModule {
34+
_dev_region: dev_region,
35+
_chrdev_registration: chrdev_registration,
36+
})
37+
}
38+
}
39+
40+
linux_kernel_module::kernel_module!(
41+
ChrdevTestModule,
42+
author: "Alex Gaynor and Geoffrey Thomas",
43+
description: "A module for testing character devices",
44+
license: "GPL"
45+
);

tests/chrdev/tests.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use kernel_module_tests::with_kernel_module;
2+
use std::fs;
3+
4+
fn get_device_number() -> u32 {
5+
let devices = fs::read_to_string("/proc/devices").unwrap();
6+
let dev_no_line = devices
7+
.lines()
8+
.find(|l| l.ends_with("chrdev-tests"))
9+
.unwrap();
10+
let elements = dev_no_line.rsplitn(2, " ").collect::<Vec<_>>();
11+
assert_eq!(elements.len(), 2);
12+
assert_eq!(elements[0], "chrdev-tests");
13+
return elements[1].trim().parse().unwrap();
14+
}
15+
16+
fn temporary_file_path() -> PathBuf {
17+
let p = env::temp_dir();
18+
p.push("chrdev-test-device");
19+
return p
20+
}
21+
22+
struct<'a> UnlinkOnDelete<'a> {
23+
path: &'a Path,
24+
}
25+
26+
impl Drop for UnlinkOnDrop {
27+
fn drop(&mut self) {
28+
fs::remove_file(self.path);
29+
}
30+
}
31+
32+
fn mknod(path: &Path, mode: u32, device_number: u32) -> UnlinkOnDrop {
33+
let result = libc::mknod(CString::new(p).unwrap(), mode, device_number);
34+
assert_eq(result, 0);
35+
return UnlinkOnDrop{path};
36+
}
37+
38+
#[test]
39+
fn test_mknod() {
40+
with_kernel_module(|| {
41+
let device_number = get_device_number();
42+
let _u = mknod(temporary_file_path(), 0o600, device_number);
43+
});
44+
}
45+
46+
#[test]
47+
fn test_read() {
48+
with_kernel_module(|| {
49+
let device_number = get_device_number();
50+
let p = temporary_file_path()
51+
let _u = mknod(temporary_file_path(), 0o600, device_number);
52+
53+
let f = fs::File::open(p);
54+
let data = vec![0; 12];
55+
f.read_exact(&mut data).unwrap();
56+
assert_eq(data, "123456789123")
57+
});
58+
59+
}

0 commit comments

Comments
 (0)