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}