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 kernel_data
227 .schedule_upcall(
228 0,
229 (kernel::errorcode::into_statuscode(Err(e)), 0, 0),
230 )
231 .ok();
232 grant.request = None;
233 }
234 Err(e)
235 }
236 }
237 });
238 if started.is_ok() {
239 return started;
240 }
241 }
242 Err(ErrorCode::FAIL)
243 }
244}
245
246/// Processes can use the Crc system call driver to compute Crc redundancy checks over process
247/// memory.
248///
249/// At a high level, the client first provides a callback for the result of computations through
250/// the `subscribe` system call and `allow`s the driver access to the buffer over-which to compute.
251/// Then, it initiates a Crc computation using the `command` system call. See function-specific
252/// comments for details.
253impl<'a, C: Crc<'a>> SyscallDriver for CrcDriver<'a, C> {
254 /// The `allow` syscall for this driver supports the single
255 /// `allow_num` zero, which is used to provide a buffer over which
256 /// to compute a Crc computation.
257
258 // The `subscribe` syscall supports the single `subscribe_number`
259 // zero, which is used to provide a callback that will receive the
260 // result of a Crc computation. The signature of the callback is
261 //
262 // ```
263 //
264 // fn callback(status: Result<(), ErrorCode>, result: usize) {}
265 // ```
266 //
267 // where
268 //
269 // * `status` is indicates whether the computation
270 // succeeded. The status `BUSY` indicates the unit is already
271 // busy. The status `SIZE` indicates the provided buffer is
272 // too large for the unit to handle.
273 //
274 // * `result` is the result of the Crc computation when `status == BUSY`.
275 //
276
277 /// The command system call for this driver return meta-data about the driver and kicks off
278 /// Crc computations returned through callbacks.
279 ///
280 /// ### Command Numbers
281 ///
282 /// * `0`: Returns non-zero to indicate the driver is present.
283 ///
284 /// * `1`: Requests that a Crc be computed over the buffer
285 /// previously provided by `allow`. If none was provided,
286 /// this command will return `INVAL`.
287 ///
288 /// This command's driver-specific argument indicates what Crc
289 /// algorithm to perform, as listed below. If an invalid
290 /// algorithm specifier is provided, this command will return
291 /// `INVAL`.
292 ///
293 /// If a callback was not previously registered with
294 /// `subscribe`, this command will return `INVAL`.
295 ///
296 /// If a computation has already been requested by this
297 /// application but the callback has not yet been invoked to
298 /// receive the result, this command will return `BUSY`.
299 ///
300 /// When `Ok(())` is returned, this means the request has been
301 /// queued and the callback will be invoked when the Crc
302 /// computation is complete.
303 ///
304 /// ### Algorithm
305 ///
306 /// The Crc algorithms supported by this driver are listed below. In
307 /// the values used to identify polynomials, more-significant bits
308 /// correspond to higher-order terms, and the most significant bit is
309 /// omitted because it always equals one. All algorithms listed here
310 /// consume each input byte from most-significant bit to
311 /// least-significant.
312 ///
313 /// * `0: Crc-32` This algorithm is used in Ethernet and many other
314 /// applications. It uses polynomial 0x04C11DB7 and it bit-reverses
315 /// and then bit-inverts the output.
316 ///
317 /// * `1: Crc-32C` This algorithm uses polynomial 0x1EDC6F41 (due
318 /// to Castagnoli) and it bit-reverses and then bit-inverts the
319 /// output. It *may* be equivalent to various Crc functions using
320 /// the same name.
321 ///
322 /// * `2: Crc-16CCITT` This algorithm uses polynomial 0x1021 and does
323 /// no post-processing on the output value. The sixteen-bit Crc
324 /// result is placed in the low-order bits of the returned result
325 /// value. That is, result values will always be of the form `0x0000xxxx`
326 /// for this algorithm. It can be performed purely in hardware on the SAM4L.
327 fn command(
328 &self,
329 command_num: usize,
330 algorithm_id: usize,
331 length: usize,
332 process_id: ProcessId,
333 ) -> CommandReturn {
334 match command_num {
335 // This driver is present
336 0 => CommandReturn::success(),
337
338 // Request a Crc computation
339 1 => {
340 // Parse the user provided algorithm number
341 let algorithm = if let Some(alg) = alg_from_user_int(algorithm_id) {
342 alg
343 } else {
344 return CommandReturn::failure(ErrorCode::INVAL);
345 };
346 let res = self
347 .grant
348 .enter(process_id, |grant, kernel_data| {
349 if grant.request.is_some() {
350 Err(ErrorCode::BUSY)
351 } else if length
352 > kernel_data
353 .get_readonly_processbuffer(ro_allow::BUFFER)
354 .map_or(0, |buffer| buffer.len())
355 {
356 Err(ErrorCode::SIZE)
357 } else {
358 grant.request = Some((algorithm, length));
359 Ok(())
360 }
361 })
362 .unwrap_or_else(|e| Err(ErrorCode::from(e)));
363
364 match res {
365 Ok(()) => {
366 if self.current_process.is_none() {
367 self.next_request().map_or_else(
368 |e| CommandReturn::failure(ErrorCode::into(e)),
369 |()| CommandReturn::success(),
370 )
371 } else {
372 // Another request is ongoing. We've enqueued this one,
373 // wait for it to be started when it's its turn.
374 CommandReturn::success()
375 }
376 }
377 Err(e) => CommandReturn::failure(e),
378 }
379 }
380 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
381 }
382 }
383
384 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
385 self.grant.enter(processid, |_, _| {})
386 }
387}
388
389impl<'a, C: Crc<'a>> Client for CrcDriver<'a, C> {
390 fn input_done(&self, result: Result<(), ErrorCode>, buffer: SubSliceMut<'static, u8>) {
391 // A call to `input` has finished. This can mean that either
392 // we have processed the entire buffer passed in, or it was
393 // truncated by the CRC unit as it was too large. In the first
394 // case, we can see whether there is more outstanding data
395 // from the app, whereas in the latter we need to advance the
396 // SubSliceMut window and pass it in again.
397 let mut computing = false;
398 // There are three outcomes to this match:
399 // - crc_buffer is not put back: input is ongoing
400 // - crc_buffer is put back and computing is true: compute is ongoing
401 // - crc_buffer is put back and computing is false: something failed, start a new request
402 match result {
403 Ok(()) => {
404 // Completed leasable buffer, either refill it or compute
405 if buffer.len() == 0 {
406 // Put the kernel buffer back
407 self.crc_buffer.replace(buffer.take());
408 self.current_process.map(|pid| {
409 let _res = self.grant.enter(pid, |grant, kernel_data| {
410 // This shouldn't happen unless there's a way to clear out a request
411 // through a system call: regardless, the request is gone, so cancel
412 // the CRC.
413 if grant.request.is_none() {
414 kernel_data
415 .schedule_upcall(
416 0,
417 (
418 kernel::errorcode::into_statuscode(Err(
419 ErrorCode::FAIL,
420 )),
421 0,
422 0,
423 ),
424 )
425 .ok();
426 return;
427 }
428
429 // Compute how many remaining bytes to compute over
430 let (alg, size) = grant.request.unwrap();
431 grant.request = Some((alg, size));
432 let size = kernel_data
433 .get_readonly_processbuffer(ro_allow::BUFFER)
434 .map_or(0, |buffer| buffer.len())
435 .min(size);
436 // If the buffer has shrunk, size might be less than
437 // app_buffer_written: don't allow wraparound
438 let remaining = size - cmp::min(self.app_buffer_written.get(), size);
439
440 if remaining == 0 {
441 // No more bytes to input: compute
442 let res = self.crc.compute();
443 match res {
444 Ok(()) => {
445 computing = true;
446 }
447 Err(_) => {
448 grant.request = None;
449 kernel_data
450 .schedule_upcall(
451 0,
452 (
453 kernel::errorcode::into_statuscode(Err(
454 ErrorCode::FAIL,
455 )),
456 0,
457 0,
458 ),
459 )
460 .ok();
461 }
462 }
463 } else {
464 // More bytes: do the next input
465 let amount = kernel_data
466 .get_readonly_processbuffer(ro_allow::BUFFER)
467 .and_then(|buffer| {
468 buffer.enter(|app_slice| {
469 self.do_next_input(
470 &app_slice[self.app_buffer_written.get()..],
471 remaining,
472 )
473 })
474 })
475 .unwrap_or(0);
476 if amount == 0 {
477 grant.request = None;
478 kernel_data
479 .schedule_upcall(
480 0,
481 (
482 kernel::errorcode::into_statuscode(Err(
483 ErrorCode::NOMEM,
484 )),
485 0,
486 0,
487 ),
488 )
489 .ok();
490 } else {
491 self.app_buffer_written.add(amount);
492 }
493 }
494 });
495 });
496 } else {
497 // There's more in the leasable buffer: pass it to input again
498 let res = self.crc.input(buffer);
499 match res {
500 Ok(()) => {}
501 Err((e, returned_buffer)) => {
502 self.crc_buffer.replace(returned_buffer.take());
503 self.current_process.map(|pid| {
504 let _res = self.grant.enter(pid, |grant, kernel_data| {
505 grant.request = None;
506 kernel_data
507 .schedule_upcall(
508 0,
509 (kernel::errorcode::into_statuscode(Err(e)), 0, 0),
510 )
511 .ok();
512 });
513 });
514 }
515 }
516 }
517 }
518 Err(e) => {
519 // The callback returned an error, pass it back to userspace
520 self.crc_buffer.replace(buffer.take());
521 self.current_process.map(|pid| {
522 let _res = self.grant.enter(pid, |grant, kernel_data| {
523 grant.request = None;
524 kernel_data
525 .schedule_upcall(0, (kernel::errorcode::into_statuscode(Err(e)), 0, 0))
526 .ok();
527 });
528 });
529 }
530 }
531 // The buffer was put back (there is no input ongoing) but computing is false,
532 // so no compute is ongoing. Start a new request if there is one.
533 if self.crc_buffer.is_some() && !computing {
534 let _ = self.next_request();
535 }
536 }
537
538 fn crc_done(&self, result: Result<CrcOutput, ErrorCode>) {
539 // First of all, inform the app about the finished operation /
540 // the result
541 self.current_process.take().map(|process_id| {
542 let _ = self.grant.enter(process_id, |grant, kernel_data| {
543 grant.request = None;
544 match result {
545 Ok(output) => {
546 let (val, user_int) = encode_upcall_crc_output(output);
547 kernel_data
548 .schedule_upcall(
549 0,
550 (
551 kernel::errorcode::into_statuscode(Ok(())),
552 val as usize,
553 user_int as usize,
554 ),
555 )
556 .ok();
557 }
558 Err(e) => {
559 kernel_data
560 .schedule_upcall(0, (kernel::errorcode::into_statuscode(Err(e)), 0, 0))
561 .ok();
562 }
563 }
564 });
565 });
566 let _ = self.next_request();
567 }
568}
569
570fn alg_from_user_int(i: usize) -> Option<CrcAlgorithm> {
571 match i {
572 0 => Some(CrcAlgorithm::Crc32),
573 1 => Some(CrcAlgorithm::Crc32C),
574 2 => Some(CrcAlgorithm::Crc16CCITT),
575 _ => None,
576 }
577}
578
579fn encode_upcall_crc_output(output: CrcOutput) -> (u32, u32) {
580 match output {
581 CrcOutput::Crc32(val) => (val, 0),
582 CrcOutput::Crc32C(val) => (val, 1),
583 CrcOutput::Crc16CCITT(val) => (val as u32, 2),
584 }
585}