capsules_extra/
kv_driver.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//! KV Store Userspace Driver.
6//!
7//! Provides userspace access to key-value store. Access is restricted based on
8//! `StoragePermissions` so processes must have the required permissions in
9//! their TBF headers to use this interface.
10//!
11//! ```rust,ignore
12//! +===============+
13//! ||  Userspace  ||
14//! +===============+
15//!
16//! -----Syscall Interface-----
17//!
18//! +-------------------------+
19//! |  KV Driver (this file)  |
20//! +-------------------------+
21//!
22//!    hil::kv::KVPermissions
23//!
24//! +-------------------------+
25//! | Virtualizer             |
26//! +-------------------------+
27//!
28//!    hil::kv::KVPermissions
29//!
30//! +-------------------------+
31//! |  K-V store Permissions  |
32//! +-------------------------+
33//!
34//!    hil::kv::KV
35//!
36//! +-------------------------+
37//! |  K-V library            |
38//! +-------------------------+
39//!
40//!    hil::flash
41//! ```
42
43use capsules_core::driver;
44/// Syscall driver number.
45pub const DRIVER_NUM: usize = driver::NUM::Kv as usize;
46
47use core::cmp;
48use kernel::errorcode;
49use kernel::grant::Grant;
50use kernel::grant::{AllowRoCount, AllowRwCount, UpcallCount};
51use kernel::hil::kv;
52use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
53use kernel::syscall::{CommandReturn, SyscallDriver};
54use kernel::utilities::cells::{OptionalCell, TakeCell};
55use kernel::utilities::leasable_buffer::SubSliceMut;
56use kernel::{ErrorCode, ProcessId};
57
58/// IDs for read-only allow buffers.
59mod ro_allow {
60    /// Key.
61    pub const KEY: usize = 0;
62    /// Input value for set/add/update.
63    pub const VALUE: usize = 1;
64    /// The number of RO allow buffers the kernel stores for this grant.
65    pub const COUNT: u8 = 2;
66}
67
68/// IDs for read-write allow buffers.
69mod rw_allow {
70    /// Output value for get.
71    pub const VALUE: usize = 0;
72    /// The number of RW allow buffers the kernel stores for this grant.
73    pub const COUNT: u8 = 1;
74}
75
76/// IDs for upcalls.
77mod upcalls {
78    /// Single upcall.
79    pub const VALUE: usize = 0;
80    /// The number of upcalls the kernel stores for this grant.
81    pub const COUNT: u8 = 1;
82}
83
84#[derive(Copy, Clone, PartialEq)]
85enum UserSpaceOp {
86    Get,
87    Set,
88    Delete,
89    Add,
90    Update,
91    GarbageCollect,
92}
93
94/// Contents of the grant for each app.
95#[derive(Default)]
96pub struct App {
97    op: OptionalCell<UserSpaceOp>,
98}
99
100/// Capsule that provides userspace access to a key-value store.
101pub struct KVStoreDriver<'a, V: kv::KVPermissions<'a>> {
102    /// Underlying k-v store implementation.
103    kv: &'a V,
104    /// Grant storage for each app.
105    apps: Grant<
106        App,
107        UpcallCount<{ upcalls::COUNT }>,
108        AllowRoCount<{ ro_allow::COUNT }>,
109        AllowRwCount<{ rw_allow::COUNT }>,
110    >,
111    /// App that is actively using the k-v store.
112    processid: OptionalCell<ProcessId>,
113    /// Key buffer.
114    key_buffer: TakeCell<'static, [u8]>,
115    /// Value buffer.
116    value_buffer: TakeCell<'static, [u8]>,
117}
118
119impl<'a, V: kv::KVPermissions<'a>> KVStoreDriver<'a, V> {
120    pub fn new(
121        kv: &'a V,
122        key_buffer: &'static mut [u8],
123        value_buffer: &'static mut [u8],
124        grant: Grant<
125            App,
126            UpcallCount<{ upcalls::COUNT }>,
127            AllowRoCount<{ ro_allow::COUNT }>,
128            AllowRwCount<{ rw_allow::COUNT }>,
129        >,
130    ) -> KVStoreDriver<'a, V> {
131        KVStoreDriver {
132            kv,
133            apps: grant,
134            processid: OptionalCell::empty(),
135            key_buffer: TakeCell::new(key_buffer),
136            value_buffer: TakeCell::new(value_buffer),
137        }
138    }
139
140    fn run(&self) -> Result<(), ErrorCode> {
141        self.processid.map_or(Err(ErrorCode::RESERVE), |processid| {
142            self.apps
143                .enter(processid, |app, kernel_data| {
144                    let key_len = if app.op.is_some() {
145                        // For all operations we need to copy in the key.
146                        kernel_data
147                            .get_readonly_processbuffer(ro_allow::KEY)
148                            .and_then(|buffer| {
149                                buffer.enter(|key| {
150                                    self.key_buffer.map_or(Err(ErrorCode::NOMEM), |key_buf| {
151                                        // Error if we cannot fit the key.
152                                        if key_buf.len() < key.len() {
153                                            Err(ErrorCode::SIZE)
154                                        } else {
155                                            key.copy_to_slice(&mut key_buf[..key.len()]);
156                                            Ok(key.len())
157                                        }
158                                    })
159                                })
160                            })
161                            .unwrap_or(Err(ErrorCode::RESERVE))?
162                    } else {
163                        0
164                    };
165
166                    match app.op.get() {
167                        Some(UserSpaceOp::Get) => {
168                            if let Some(Some(e)) = self.key_buffer.take().map(|key_buf| {
169                                self.value_buffer.take().map(|val_buf| {
170                                    let perms = processid
171                                        .get_storage_permissions()
172                                        .ok_or(ErrorCode::INVAL)?;
173
174                                    let mut key = SubSliceMut::new(key_buf);
175                                    key.slice(..key_len);
176
177                                    let value = SubSliceMut::new(val_buf);
178
179                                    if let Err((key_ret, val_ret, e)) =
180                                        self.kv.get(key, value, perms)
181                                    {
182                                        self.key_buffer.replace(key_ret.take());
183                                        self.value_buffer.replace(val_ret.take());
184                                        return Err(e);
185                                    }
186                                    Ok(())
187                                })
188                            }) {
189                                return e;
190                            }
191                        }
192                        Some(UserSpaceOp::Set)
193                        | Some(UserSpaceOp::Add)
194                        | Some(UserSpaceOp::Update) => {
195                            let value_len = kernel_data
196                                .get_readonly_processbuffer(ro_allow::VALUE)
197                                .and_then(|buffer| {
198                                    buffer.enter(|value| {
199                                        self.value_buffer.map_or(Err(ErrorCode::NOMEM), |val_buf| {
200                                            // Make sure there is room for the
201                                            // Tock KV header and the value.
202                                            let header_size = self.kv.header_size();
203                                            let remaining_space = val_buf.len() - header_size;
204                                            if remaining_space < value.len() {
205                                                Err(ErrorCode::SIZE)
206                                            } else {
207                                                value.copy_to_slice(
208                                                    &mut val_buf
209                                                        [header_size..(value.len() + header_size)],
210                                                );
211                                                Ok(value.len())
212                                            }
213                                        })
214                                    })
215                                })
216                                .unwrap_or(Err(ErrorCode::RESERVE))?;
217
218                            if let Some(Some(e)) = self.key_buffer.take().map(|key_buf| {
219                                self.value_buffer.take().map(|val_buf| {
220                                    let perms = processid
221                                        .get_storage_permissions()
222                                        .ok_or(ErrorCode::INVAL)?;
223
224                                    let mut key = SubSliceMut::new(key_buf);
225                                    key.slice(..key_len);
226
227                                    // Make sure we provide a value buffer with
228                                    // space for the tock kv header at the
229                                    // front.
230                                    let header_size = self.kv.header_size();
231                                    let mut value = SubSliceMut::new(val_buf);
232                                    value.slice(..(value_len + header_size));
233
234                                    if let Err((key_ret, val_ret, e)) = match app.op.get() {
235                                        Some(UserSpaceOp::Set) => self.kv.set(key, value, perms),
236                                        Some(UserSpaceOp::Add) => self.kv.add(key, value, perms),
237                                        Some(UserSpaceOp::Update) => {
238                                            self.kv.update(key, value, perms)
239                                        }
240                                        _ => Ok(()),
241                                    } {
242                                        self.key_buffer.replace(key_ret.take());
243                                        self.value_buffer.replace(val_ret.take());
244                                        return Err(e);
245                                    }
246                                    Ok(())
247                                })
248                            }) {
249                                return e;
250                            }
251                        }
252                        Some(UserSpaceOp::Delete) => {
253                            if let Some(e) = self.key_buffer.take().map(|key_buf| {
254                                let perms = processid
255                                    .get_storage_permissions()
256                                    .ok_or(ErrorCode::INVAL)?;
257
258                                let mut key = SubSliceMut::new(key_buf);
259                                key.slice(..key_len);
260
261                                if let Err((key_ret, e)) = self.kv.delete(key, perms) {
262                                    self.key_buffer.replace(key_ret.take());
263                                    return Err(e);
264                                }
265                                Ok(())
266                            }) {
267                                return e;
268                            }
269                        }
270                        Some(UserSpaceOp::GarbageCollect) => {
271                            self.kv.garbage_collect()?;
272                            return Ok(());
273                        }
274
275                        _ => {}
276                    }
277
278                    Ok(())
279                })
280                .unwrap_or_else(|err| Err(err.into()))
281        })
282    }
283
284    fn check_queue(&self) {
285        // If an app is already running let it complete.
286        if self.processid.is_some() {
287            return;
288        }
289
290        for appiter in self.apps.iter() {
291            let processid = appiter.processid();
292            let has_pending_op = appiter.enter(|app, _| {
293                // If this app has a pending command let's use it.
294                app.op.is_some()
295            });
296            let started_command = if has_pending_op {
297                // Mark this driver as being in use.
298                self.processid.set(processid);
299                self.run() == Ok(())
300            } else {
301                false
302            };
303            if started_command {
304                break;
305            } else {
306                self.processid.clear();
307            }
308        }
309    }
310}
311
312impl<'a, V: kv::KVPermissions<'a>> kv::KVClient for KVStoreDriver<'a, V> {
313    fn get_complete(
314        &self,
315        result: Result<(), ErrorCode>,
316        key: SubSliceMut<'static, u8>,
317        value: SubSliceMut<'static, u8>,
318    ) {
319        self.key_buffer.replace(key.take());
320
321        self.processid.map(move |id| {
322            self.apps.enter(id, move |app, upcalls| {
323                if app.op.contains(&UserSpaceOp::Get) {
324                    app.op.clear();
325
326                    if let Err(e) = result {
327                        let _ = upcalls.schedule_upcall(
328                            upcalls::VALUE,
329                            (errorcode::into_statuscode(e.into()), 0, 0),
330                        );
331                    } else {
332                        let value_len = value.len();
333                        let ret = upcalls
334                            .get_readwrite_processbuffer(rw_allow::VALUE)
335                            .and_then(|buffer| {
336                                buffer.mut_enter(|appslice| {
337                                    let copy_len = cmp::min(value_len, appslice.len());
338                                    appslice[..copy_len].copy_from_slice(&value[..copy_len]);
339                                    if copy_len < value_len {
340                                        Err(ErrorCode::SIZE)
341                                    } else {
342                                        Ok(())
343                                    }
344                                })
345                            })
346                            .unwrap_or(Err(ErrorCode::RESERVE));
347
348                        // Signal the upcall, and return the length of the
349                        // value. Userspace should be careful to check for an
350                        // error and only read the portion that would fit in the
351                        // buffer if the value was larger than the provided
352                        // processbuffer.
353                        let _ = upcalls.schedule_upcall(
354                            upcalls::VALUE,
355                            (errorcode::into_statuscode(ret), value_len, 0),
356                        );
357                    }
358                }
359
360                self.value_buffer.replace(value.take());
361            })
362        });
363
364        // We have completed the operation so see if there is a queued operation
365        // to run next.
366        self.processid.clear();
367        self.check_queue();
368    }
369
370    fn set_complete(
371        &self,
372        result: Result<(), ErrorCode>,
373        key: SubSliceMut<'static, u8>,
374        value: SubSliceMut<'static, u8>,
375    ) {
376        self.key_buffer.replace(key.take());
377        self.value_buffer.replace(value.take());
378
379        // Signal the upcall and clear the requested op.
380        self.processid.map(move |id| {
381            self.apps.enter(id, move |app, upcalls| {
382                if app.op.contains(&UserSpaceOp::Set) {
383                    app.op.clear();
384                    let _ = upcalls.schedule_upcall(
385                        upcalls::VALUE,
386                        (errorcode::into_statuscode(result), 0, 0),
387                    );
388                }
389            })
390        });
391
392        // We have completed the operation so see if there is a queued operation
393        // to run next.
394        self.processid.clear();
395        self.check_queue();
396    }
397
398    fn add_complete(
399        &self,
400        result: Result<(), ErrorCode>,
401        key: SubSliceMut<'static, u8>,
402        value: SubSliceMut<'static, u8>,
403    ) {
404        self.key_buffer.replace(key.take());
405        self.value_buffer.replace(value.take());
406
407        // Signal the upcall and clear the requested op.
408        self.processid.map(move |id| {
409            self.apps.enter(id, move |app, upcalls| {
410                if app.op.contains(&UserSpaceOp::Add) {
411                    app.op.clear();
412                    let _ = upcalls.schedule_upcall(
413                        upcalls::VALUE,
414                        (errorcode::into_statuscode(result), 0, 0),
415                    );
416                }
417            })
418        });
419
420        // We have completed the operation so see if there is a queued operation
421        // to run next.
422        self.processid.clear();
423        self.check_queue();
424    }
425
426    fn update_complete(
427        &self,
428        result: Result<(), ErrorCode>,
429        key: SubSliceMut<'static, u8>,
430        value: SubSliceMut<'static, u8>,
431    ) {
432        self.key_buffer.replace(key.take());
433        self.value_buffer.replace(value.take());
434
435        // Signal the upcall and clear the requested op.
436        self.processid.map(move |id| {
437            self.apps.enter(id, move |app, upcalls| {
438                if app.op.contains(&UserSpaceOp::Update) {
439                    app.op.clear();
440                    let _ = upcalls.schedule_upcall(
441                        upcalls::VALUE,
442                        (errorcode::into_statuscode(result), 0, 0),
443                    );
444                }
445            })
446        });
447
448        // We have completed the operation so see if there is a queued operation
449        // to run next.
450        self.processid.clear();
451        self.check_queue();
452    }
453
454    fn delete_complete(&self, result: Result<(), ErrorCode>, key: SubSliceMut<'static, u8>) {
455        self.key_buffer.replace(key.take());
456
457        self.processid.map(move |id| {
458            self.apps.enter(id, move |app, upcalls| {
459                if app.op.contains(&UserSpaceOp::Delete) {
460                    app.op.clear();
461                    let _ = upcalls.schedule_upcall(
462                        upcalls::VALUE,
463                        (errorcode::into_statuscode(result), 0, 0),
464                    );
465                }
466            })
467        });
468
469        // We have completed the operation so see if there is a queued operation
470        // to run next.
471        self.processid.clear();
472        self.check_queue();
473    }
474
475    fn garbage_collection_complete(&self, result: Result<(), ErrorCode>) {
476        self.processid.map(move |id| {
477            self.apps.enter(id, move |app, upcalls| {
478                if app.op.contains(&UserSpaceOp::GarbageCollect) {
479                    app.op.clear();
480                    let _ = upcalls.schedule_upcall(
481                        upcalls::VALUE,
482                        (errorcode::into_statuscode(result), 0, 0),
483                    );
484                }
485            })
486        });
487
488        // We have completed the operation so see if there is a queued operation
489        // to run next.
490        self.processid.clear();
491        self.check_queue();
492    }
493}
494
495impl<'a, V: kv::KVPermissions<'a>> SyscallDriver for KVStoreDriver<'a, V> {
496    fn command(
497        &self,
498        command_num: usize,
499        _data1: usize,
500        _data2: usize,
501        processid: ProcessId,
502    ) -> CommandReturn {
503        match command_num {
504            // check if present
505            0 => CommandReturn::success(),
506
507            // get, set, delete, add, update, garbage collect
508            1 | 2 | 3 | 4 | 5 | 6 => {
509                if self.processid.is_none() {
510                    // Nothing is using the KV store, so we can handle this
511                    // request.
512                    self.processid.set(processid);
513                    let _ = self.apps.enter(processid, |app, _| match command_num {
514                        1 => app.op.set(UserSpaceOp::Get),
515                        2 => app.op.set(UserSpaceOp::Set),
516                        3 => app.op.set(UserSpaceOp::Delete),
517                        4 => app.op.set(UserSpaceOp::Add),
518                        5 => app.op.set(UserSpaceOp::Update),
519                        6 => app.op.set(UserSpaceOp::GarbageCollect),
520                        _ => {}
521                    });
522                    let ret = self.run();
523
524                    if let Err(e) = ret {
525                        self.processid.clear();
526                        self.check_queue();
527                        CommandReturn::failure(e)
528                    } else {
529                        CommandReturn::success()
530                    }
531                } else {
532                    // There is an active app, so queue this request (if
533                    // possible).
534                    self.apps
535                        .enter(processid, |app, _| {
536                            if app.op.is_some() {
537                                // No more room in the queue, nowhere to store
538                                // this request.
539                                CommandReturn::failure(ErrorCode::NOMEM)
540                            } else {
541                                // This app has not already queued a command so
542                                // we can store this.
543                                match command_num {
544                                    1 => app.op.set(UserSpaceOp::Get),
545                                    2 => app.op.set(UserSpaceOp::Set),
546                                    3 => app.op.set(UserSpaceOp::Delete),
547                                    4 => app.op.set(UserSpaceOp::Add),
548                                    5 => app.op.set(UserSpaceOp::Update),
549                                    6 => app.op.set(UserSpaceOp::GarbageCollect),
550                                    _ => {}
551                                }
552                                CommandReturn::success()
553                            }
554                        })
555                        .unwrap_or_else(|err| err.into())
556                }
557            }
558
559            // default
560            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
561        }
562    }
563
564    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
565        self.apps.enter(processid, |_, _| {})
566    }
567}