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}