capsules_extra/crc.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Provides userspace access to a Crc unit.
6//!
7//! ## Instantiation
8//!
9//! Instantiate the capsule for use as a system call driver with a hardware
10//! implementation and a `Grant` for the `App` type, and set the result as a
11//! client of the hardware implementation. For example, using the SAM4L's `CrcU`
12//! driver:
13//!
14//! ```rust,ignore
15//! # use kernel::static_init;
16//!
17//! let crc_buffer = static_init!([u8; 64], [0; 64]);
18//!
19//! let crc = static_init!(
20//! capsules::crc::CrcDriver<'static, sam4l::crccu::Crccu<'static>>,
21//! capsules::crc::CrcDriver::new(
22//! &mut sam4l::crccu::CRCCU,
23//! crc_buffer,
24//! board_kernel.create_grant(&grant_cap)
25//! )
26//! );
27//! sam4l::crccu::CRCCU.set_client(crc);
28//!
29//! ```
30//!
31//! ## Crc Algorithms
32//!
33//! The capsule supports two general purpose Crc algorithms, as well as a few
34//! hardware specific algorithms implemented on the Atmel SAM4L.
35//!
36//! In the values used to identify polynomials below, more-significant bits
37//! correspond to higher-order terms, and the most significant bit is omitted
38//! because it always equals one. All algorithms listed here consume each input
39//! byte from most-significant bit to least-significant.
40//!
41//! ### Crc-32
42//!
43//! __Polynomial__: `0x04C11DB7`
44//!
45//! This algorithm is used in Ethernet and many other applications. It bit-
46//! reverses and then bit-inverts the output.
47//!
48//! ### Crc-32C
49//!
50//! __Polynomial__: `0x1EDC6F41`
51//!
52//! Bit-reverses and then bit-inverts the output. It *may* be equivalent to
53//! various Crc functions using the same name.
54//!
55//! ### SAM4L-16
56//!
57//! __Polynomial__: `0x1021`
58//!
59//! This algorithm does no post-processing on the output value. The sixteen-bit
60//! Crc result is placed in the low-order bits of the returned result value, and
61//! the high-order bits will all be set. That is, result values will always be
62//! of the form `0xFFFFxxxx` for this algorithm. It can be performed purely in
63//! hardware on the SAM4L.
64//!
65//! ### SAM4L-32
66//!
67//! __Polynomial__: `0x04C11DB7`
68//!
69//! This algorithm uses the same polynomial as `Crc-32`, but does no post-
70//! processing on the output value. It can be performed purely in hardware on
71//! the SAM4L.
72//!
73//! ### SAM4L-32C
74//!
75//! __Polynomial__: `0x1EDC6F41`
76//!
77//! This algorithm uses the same polynomial as `Crc-32C`, but does no post-
78//! processing on the output value. It can be performed purely in hardware on
79//! the SAM4L.
80
81use core::cell::Cell;
82use core::cmp;
83
84use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
85use kernel::hil::crc::{Client, Crc, CrcAlgorithm, CrcOutput};
86use kernel::processbuffer::{ReadableProcessBuffer, ReadableProcessSlice};
87use kernel::syscall::{CommandReturn, SyscallDriver};
88use kernel::utilities::cells::NumericCellExt;
89use kernel::utilities::cells::{OptionalCell, TakeCell};
90use kernel::utilities::leasable_buffer::SubSliceMut;
91use kernel::{ErrorCode, ProcessId};
92
93/// Syscall driver number.
94use capsules_core::driver;
95pub const DRIVER_NUM: usize = driver::NUM::Crc as usize;
96pub const DEFAULT_CRC_BUF_LENGTH: usize = 256;
97
98/// Ids for read-only allow buffers
99mod ro_allow {
100 pub const BUFFER: usize = 0;
101 /// The number of allow buffers the kernel stores for this grant
102 pub const COUNT: u8 = 1;
103}
104
105/// An opaque value maintaining state for one application's request
106#[derive(Default)]
107pub struct App {
108 // if Some, the process is waiting for the result of CRC
109 // of len bytes using the given algorithm
110 request: Option<(CrcAlgorithm, usize)>,
111}
112
113/// Struct that holds the state of the Crc driver and implements the `Driver` trait for use by
114/// processes through the system call interface.
115pub struct CrcDriver<'a, C: Crc<'a>> {
116 crc: &'a C,
117 crc_buffer: TakeCell<'static, [u8]>,
118 grant: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
119 current_process: OptionalCell<ProcessId>,
120 // We need to save our current
121 app_buffer_written: Cell<usize>,
122}
123
124impl<'a, C: Crc<'a>> CrcDriver<'a, C> {
125 /// Create a `Crc` driver
126 ///
127 /// The argument `crc_unit` must implement the abstract `Crc`
128 /// hardware interface. The argument `apps` should be an empty
129 /// kernel `Grant`, and will be used to track application
130 /// requests.
131 ///
132 /// ## Example
133 ///
134 /// ```rust,ignore
135 /// capsules::crc::Crc::new(&sam4l::crccu::CrcCU, board_kernel.create_grant(&grant_cap));
136 /// ```
137 ///
138 pub fn new(
139 crc: &'a C,
140 crc_buffer: &'static mut [u8],
141 grant: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
142 ) -> CrcDriver<'a, C> {
143 CrcDriver {
144 crc,
145 crc_buffer: TakeCell::new(crc_buffer),
146 grant,
147 current_process: OptionalCell::empty(),
148 app_buffer_written: Cell::new(0),
149 }
150 }
151
152 fn do_next_input(&self, data: &ReadableProcessSlice, len: usize) -> usize {
153 let count = self.crc_buffer.take().map_or(0, |kbuffer| {
154 let copy_len = cmp::min(len, kbuffer.len());
155 for i in 0..copy_len {
156 kbuffer[i] = data[i].get();
157 }
158 if copy_len > 0 {
159 let mut leasable = SubSliceMut::new(kbuffer);
160 leasable.slice(0..copy_len);
161 let res = self.crc.input(leasable);
162 match res {
163 Ok(()) => copy_len,
164 Err((_err, leasable)) => {
165 self.crc_buffer.put(Some(leasable.take()));
166 0
167 }
168 }
169 } else {
170 0
171 }
172 });
173 count
174 }
175
176 // Start a new request. Return Ok(()) if one started, Err(FAIL) if not.
177 // Issue callbacks for any requests that are invalid, either because
178 // they are zero-length or requested an invalid algorithm.
179 fn next_request(&self) -> Result<(), ErrorCode> {
180 self.app_buffer_written.set(0);
181 for process in self.grant.iter() {
182 let process_id = process.processid();
183 let started = process.enter(|grant, kernel_data| {
184 // If there's no buffer this means the process is dead, so
185 // no need to issue a callback on this error case.
186 let res: Result<(), ErrorCode> = kernel_data
187 .get_readonly_processbuffer(ro_allow::BUFFER)
188 .and_then(|buffer| {
189 buffer.enter(|buffer| {
190 if let Some((algorithm, len)) = grant.request {
191 let copy_len = cmp::min(len, buffer.len());
192 if copy_len == 0 {
193 // 0-length or 0-size buffer
194 Err(ErrorCode::SIZE)
195 } else {
196 let res = self.crc.set_algorithm(algorithm);
197 match res {
198 Ok(()) => {
199 let copy_len = self.do_next_input(buffer, copy_len);
200 if copy_len > 0 {
201 self.app_buffer_written.set(copy_len);
202 self.current_process.set(process_id);
203 Ok(())
204 } else {
205 // Next input failed
206 Err(ErrorCode::FAIL)
207 }
208 }
209 Err(_) => {
210 // Setting the algorithm failed
211 Err(ErrorCode::INVAL)
212 }
213 }
214 }
215 } else {
216 // no request
217 Err(ErrorCode::FAIL)
218 }
219 })
220 })
221 .unwrap_or(Err(ErrorCode::NOMEM));
222 match res {
223 Ok(()) => Ok(()),
224 Err(e) => {
225 if grant.request.is_some() {
226 let _ = kernel_data.schedule_upcall(
227 0,
228 (kernel::errorcode::into_statuscode(Err(e)), 0, 0),
229 );
230 grant.request = None;
231 }
232 Err(e)
233 }
234 }
235 });
236 if started.is_ok() {
237 return started;
238 }
239 }
240 Err(ErrorCode::FAIL)
241 }
242}
243
244/// Processes can use the Crc system call driver to compute Crc redundancy checks over process
245/// memory.
246///
247/// At a high level, the client first provides a callback for the result of computations through
248/// the `subscribe` system call and `allow`s the driver access to the buffer over-which to compute.
249/// Then, it initiates a Crc computation using the `command` system call. See function-specific
250/// comments for details.
251impl<'a, C: Crc<'a>> SyscallDriver for CrcDriver<'a, C> {
252 /// The `allow` syscall for this driver supports the single
253 /// `allow_num` zero, which is used to provide a buffer over which
254 /// to compute a Crc computation.
255
256 // The `subscribe` syscall supports the single `subscribe_number`
257 // zero, which is used to provide a callback that will receive the
258 // result of a Crc computation. The signature of the callback is
259 //
260 // ```
261 //
262 // fn callback(status: Result<(), ErrorCode>, result: usize) {}
263 // ```
264 //
265 // where
266 //
267 // * `status` is indicates whether the computation
268 // succeeded. The status `BUSY` indicates the unit is already
269 // busy. The status `SIZE` indicates the provided buffer is
270 // too large for the unit to handle.
271 //
272 // * `result` is the result of the Crc computation when `status == BUSY`.
273 //
274
275 /// The command system call for this driver return meta-data about the driver and kicks off
276 /// Crc computations returned through callbacks.
277 ///
278 /// ### Command Numbers
279 ///
280 /// * `0`: Returns non-zero to indicate the driver is present.
281 ///
282 /// * `1`: Requests that a Crc be computed over the buffer
283 /// previously provided by `allow`. If none was provided,
284 /// this command will return `INVAL`.
285 ///
286 /// This command's driver-specific argument indicates what Crc
287 /// algorithm to perform, as listed below. If an invalid
288 /// algorithm specifier is provided, this command will return
289 /// `INVAL`.
290 ///
291 /// If a callback was not previously registered with
292 /// `subscribe`, this command will return `INVAL`.
293 ///
294 /// If a computation has already been requested by this
295 /// application but the callback has not yet been invoked to
296 /// receive the result, this command will return `BUSY`.
297 ///
298 /// When `Ok(())` is returned, this means the request has been
299 /// queued and the callback will be invoked when the Crc
300 /// computation is complete.
301 ///
302 /// ### Algorithm
303 ///
304 /// The Crc algorithms supported by this driver are listed below. In
305 /// the values used to identify polynomials, more-significant bits
306 /// correspond to higher-order terms, and the most significant bit is
307 /// omitted because it always equals one. All algorithms listed here
308 /// consume each input byte from most-significant bit to
309 /// least-significant.
310 ///
311 /// * `0: Crc-32` This algorithm is used in Ethernet and many other
312 /// applications. It uses polynomial 0x04C11DB7 and it bit-reverses
313 /// and then bit-inverts the output.
314 ///
315 /// * `1: Crc-32C` This algorithm uses polynomial 0x1EDC6F41 (due
316 /// to Castagnoli) and it bit-reverses and then bit-inverts the
317 /// output. It *may* be equivalent to various Crc functions using
318 /// the same name.
319 ///
320 /// * `2: Crc-16CCITT` This algorithm uses polynomial 0x1021 and does
321 /// no post-processing on the output value. The sixteen-bit Crc
322 /// result is placed in the low-order bits of the returned result
323 /// value. That is, result values will always be of the form `0x0000xxxx`
324 /// for this algorithm. It can be performed purely in hardware on the SAM4L.
325 fn command(
326 &self,
327 command_num: usize,
328 algorithm_id: usize,
329 length: usize,
330 process_id: ProcessId,
331 ) -> CommandReturn {
332 match command_num {
333 // This driver is present
334 0 => CommandReturn::success(),
335
336 // Request a Crc computation
337 1 => {
338 // Parse the user provided algorithm number
339 let algorithm = if let Some(alg) = alg_from_user_int(algorithm_id) {
340 alg
341 } else {
342 return CommandReturn::failure(ErrorCode::INVAL);
343 };
344 let res = self
345 .grant
346 .enter(process_id, |grant, kernel_data| {
347 if grant.request.is_some() {
348 Err(ErrorCode::BUSY)
349 } else if length
350 > kernel_data
351 .get_readonly_processbuffer(ro_allow::BUFFER)
352 .map_or(0, |buffer| buffer.len())
353 {
354 Err(ErrorCode::SIZE)
355 } else {
356 grant.request = Some((algorithm, length));
357 Ok(())
358 }
359 })
360 .unwrap_or_else(|e| Err(ErrorCode::from(e)));
361
362 match res {
363 Ok(()) => {
364 if self.current_process.is_none() {
365 self.next_request().map_or_else(
366 |e| CommandReturn::failure(ErrorCode::into(e)),
367 |()| CommandReturn::success(),
368 )
369 } else {
370 // Another request is ongoing. We've enqueued this one,
371 // wait for it to be started when it's its turn.
372 CommandReturn::success()
373 }
374 }
375 Err(e) => CommandReturn::failure(e),
376 }
377 }
378 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
379 }
380 }
381
382 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
383 self.grant.enter(processid, |_, _| {})
384 }
385}
386
387impl<'a, C: Crc<'a>> Client for CrcDriver<'a, C> {
388 fn input_done(&self, result: Result<(), ErrorCode>, buffer: SubSliceMut<'static, u8>) {
389 // A call to `input` has finished. This can mean that either
390 // we have processed the entire buffer passed in, or it was
391 // truncated by the CRC unit as it was too large. In the first
392 // case, we can see whether there is more outstanding data
393 // from the app, whereas in the latter we need to advance the
394 // SubSliceMut window and pass it in again.
395 let mut computing = false;
396 // There are three outcomes to this match:
397 // - crc_buffer is not put back: input is ongoing
398 // - crc_buffer is put back and computing is true: compute is ongoing
399 // - crc_buffer is put back and computing is false: something failed, start a new request
400 match result {
401 Ok(()) => {
402 // Completed leasable buffer, either refill it or compute
403 if buffer.len() == 0 {
404 // Put the kernel buffer back
405 self.crc_buffer.replace(buffer.take());
406 self.current_process.map(|pid| {
407 let _res = self.grant.enter(pid, |grant, kernel_data| {
408 // This shouldn't happen unless there's a way to clear out a request
409 // through a system call: regardless, the request is gone, so cancel
410 // the CRC.
411 if grant.request.is_none() {
412 let _ = kernel_data.schedule_upcall(
413 0,
414 (
415 kernel::errorcode::into_statuscode(Err(ErrorCode::FAIL)),
416 0,
417 0,
418 ),
419 );
420 return;
421 }
422
423 // Compute how many remaining bytes to compute over
424 let (alg, size) = grant.request.unwrap();
425 grant.request = Some((alg, size));
426 let size = kernel_data
427 .get_readonly_processbuffer(ro_allow::BUFFER)
428 .map_or(0, |buffer| buffer.len())
429 .min(size);
430 // If the buffer has shrunk, size might be less than
431 // app_buffer_written: don't allow wraparound
432 let remaining = size - cmp::min(self.app_buffer_written.get(), size);
433
434 if remaining == 0 {
435 // No more bytes to input: compute
436 let res = self.crc.compute();
437 match res {
438 Ok(()) => {
439 computing = true;
440 }
441 Err(_) => {
442 grant.request = None;
443 let _ = kernel_data.schedule_upcall(
444 0,
445 (
446 kernel::errorcode::into_statuscode(Err(
447 ErrorCode::FAIL,
448 )),
449 0,
450 0,
451 ),
452 );
453 }
454 }
455 } else {
456 // More bytes: do the next input
457 let amount = kernel_data
458 .get_readonly_processbuffer(ro_allow::BUFFER)
459 .and_then(|buffer| {
460 buffer.enter(|app_slice| {
461 self.do_next_input(
462 &app_slice[self.app_buffer_written.get()..],
463 remaining,
464 )
465 })
466 })
467 .unwrap_or(0);
468 if amount == 0 {
469 grant.request = None;
470 let _ = kernel_data.schedule_upcall(
471 0,
472 (
473 kernel::errorcode::into_statuscode(Err(
474 ErrorCode::NOMEM,
475 )),
476 0,
477 0,
478 ),
479 );
480 } else {
481 self.app_buffer_written.add(amount);
482 }
483 }
484 });
485 });
486 } else {
487 // There's more in the leasable buffer: pass it to input again
488 let res = self.crc.input(buffer);
489 match res {
490 Ok(()) => {}
491 Err((e, returned_buffer)) => {
492 self.crc_buffer.replace(returned_buffer.take());
493 self.current_process.map(|pid| {
494 let _res = self.grant.enter(pid, |grant, kernel_data| {
495 grant.request = None;
496 let _ = kernel_data.schedule_upcall(
497 0,
498 (kernel::errorcode::into_statuscode(Err(e)), 0, 0),
499 );
500 });
501 });
502 }
503 }
504 }
505 }
506 Err(e) => {
507 // The callback returned an error, pass it back to userspace
508 self.crc_buffer.replace(buffer.take());
509 self.current_process.map(|pid| {
510 let _res = self.grant.enter(pid, |grant, kernel_data| {
511 grant.request = None;
512 let _ = kernel_data
513 .schedule_upcall(0, (kernel::errorcode::into_statuscode(Err(e)), 0, 0));
514 });
515 });
516 }
517 }
518 // The buffer was put back (there is no input ongoing) but computing is false,
519 // so no compute is ongoing. Start a new request if there is one.
520 if self.crc_buffer.is_some() && !computing {
521 let _ = self.next_request();
522 }
523 }
524
525 fn crc_done(&self, result: Result<CrcOutput, ErrorCode>) {
526 // First of all, inform the app about the finished operation /
527 // the result
528 self.current_process.take().map(|process_id| {
529 let _ = self.grant.enter(process_id, |grant, kernel_data| {
530 grant.request = None;
531 match result {
532 Ok(output) => {
533 let (val, user_int) = encode_upcall_crc_output(output);
534 let _ = kernel_data.schedule_upcall(
535 0,
536 (
537 kernel::errorcode::into_statuscode(Ok(())),
538 val as usize,
539 user_int as usize,
540 ),
541 );
542 }
543 Err(e) => {
544 let _ = kernel_data
545 .schedule_upcall(0, (kernel::errorcode::into_statuscode(Err(e)), 0, 0));
546 }
547 }
548 });
549 });
550 let _ = self.next_request();
551 }
552}
553
554fn alg_from_user_int(i: usize) -> Option<CrcAlgorithm> {
555 match i {
556 0 => Some(CrcAlgorithm::Crc32),
557 1 => Some(CrcAlgorithm::Crc32C),
558 2 => Some(CrcAlgorithm::Crc16CCITT),
559 _ => None,
560 }
561}
562
563fn encode_upcall_crc_output(output: CrcOutput) -> (u32, u32) {
564 match output {
565 CrcOutput::Crc32(val) => (val, 0),
566 CrcOutput::Crc32C(val) => (val, 1),
567 CrcOutput::Crc16CCITT(val) => (val as u32, 2),
568 }
569}