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                        upcalls
328                            .schedule_upcall(
329                                upcalls::VALUE,
330                                (errorcode::into_statuscode(e.into()), 0, 0),
331                            )
332                            .ok();
333                    } else {
334                        let value_len = value.len();
335                        let ret = upcalls
336                            .get_readwrite_processbuffer(rw_allow::VALUE)
337                            .and_then(|buffer| {
338                                buffer.mut_enter(|appslice| {
339                                    let copy_len = cmp::min(value_len, appslice.len());
340                                    appslice[..copy_len].copy_from_slice(&value[..copy_len]);
341                                    if copy_len < value_len {
342                                        Err(ErrorCode::SIZE)
343                                    } else {
344                                        Ok(())
345                                    }
346                                })
347                            })
348                            .unwrap_or(Err(ErrorCode::RESERVE));
349
350                        // Signal the upcall, and return the length of the
351                        // value. Userspace should be careful to check for an
352                        // error and only read the portion that would fit in the
353                        // buffer if the value was larger than the provided
354                        // processbuffer.
355                        upcalls
356                            .schedule_upcall(
357                                upcalls::VALUE,
358                                (errorcode::into_statuscode(ret), value_len, 0),
359                            )
360                            .ok();
361                    }
362                }
363
364                self.value_buffer.replace(value.take());
365            })
366        });
367
368        // We have completed the operation so see if there is a queued operation
369        // to run next.
370        self.processid.clear();
371        self.check_queue();
372    }
373
374    fn set_complete(
375        &self,
376        result: Result<(), ErrorCode>,
377        key: SubSliceMut<'static, u8>,
378        value: SubSliceMut<'static, u8>,
379    ) {
380        self.key_buffer.replace(key.take());
381        self.value_buffer.replace(value.take());
382
383        // Signal the upcall and clear the requested op.
384        self.processid.map(move |id| {
385            self.apps.enter(id, move |app, upcalls| {
386                if app.op.contains(&UserSpaceOp::Set) {
387                    app.op.clear();
388                    upcalls
389                        .schedule_upcall(upcalls::VALUE, (errorcode::into_statuscode(result), 0, 0))
390                        .ok();
391                }
392            })
393        });
394
395        // We have completed the operation so see if there is a queued operation
396        // to run next.
397        self.processid.clear();
398        self.check_queue();
399    }
400
401    fn add_complete(
402        &self,
403        result: Result<(), ErrorCode>,
404        key: SubSliceMut<'static, u8>,
405        value: SubSliceMut<'static, u8>,
406    ) {
407        self.key_buffer.replace(key.take());
408        self.value_buffer.replace(value.take());
409
410        // Signal the upcall and clear the requested op.
411        self.processid.map(move |id| {
412            self.apps.enter(id, move |app, upcalls| {
413                if app.op.contains(&UserSpaceOp::Add) {
414                    app.op.clear();
415                    upcalls
416                        .schedule_upcall(upcalls::VALUE, (errorcode::into_statuscode(result), 0, 0))
417                        .ok();
418                }
419            })
420        });
421
422        // We have completed the operation so see if there is a queued operation
423        // to run next.
424        self.processid.clear();
425        self.check_queue();
426    }
427
428    fn update_complete(
429        &self,
430        result: Result<(), ErrorCode>,
431        key: SubSliceMut<'static, u8>,
432        value: SubSliceMut<'static, u8>,
433    ) {
434        self.key_buffer.replace(key.take());
435        self.value_buffer.replace(value.take());
436
437        // Signal the upcall and clear the requested op.
438        self.processid.map(move |id| {
439            self.apps.enter(id, move |app, upcalls| {
440                if app.op.contains(&UserSpaceOp::Update) {
441                    app.op.clear();
442                    upcalls
443                        .schedule_upcall(upcalls::VALUE, (errorcode::into_statuscode(result), 0, 0))
444                        .ok();
445                }
446            })
447        });
448
449        // We have completed the operation so see if there is a queued operation
450        // to run next.
451        self.processid.clear();
452        self.check_queue();
453    }
454
455    fn delete_complete(&self, result: Result<(), ErrorCode>, key: SubSliceMut<'static, u8>) {
456        self.key_buffer.replace(key.take());
457
458        self.processid.map(move |id| {
459            self.apps.enter(id, move |app, upcalls| {
460                if app.op.contains(&UserSpaceOp::Delete) {
461                    app.op.clear();
462                    upcalls
463                        .schedule_upcall(upcalls::VALUE, (errorcode::into_statuscode(result), 0, 0))
464                        .ok();
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                    upcalls
481                        .schedule_upcall(upcalls::VALUE, (errorcode::into_statuscode(result), 0, 0))
482                        .ok();
483                }
484            })
485        });
486
487        // We have completed the operation so see if there is a queued operation
488        // to run next.
489        self.processid.clear();
490        self.check_queue();
491    }
492}
493
494impl<'a, V: kv::KVPermissions<'a>> SyscallDriver for KVStoreDriver<'a, V> {
495    fn command(
496        &self,
497        command_num: usize,
498        _data1: usize,
499        _data2: usize,
500        processid: ProcessId,
501    ) -> CommandReturn {
502        match command_num {
503            // check if present
504            0 => CommandReturn::success(),
505
506            // get, set, delete, add, update, garbage collect
507            1 | 2 | 3 | 4 | 5 | 6 => {
508                if self.processid.is_none() {
509                    // Nothing is using the KV store, so we can handle this
510                    // request.
511                    self.processid.set(processid);
512                    let _ = self.apps.enter(processid, |app, _| match command_num {
513                        1 => app.op.set(UserSpaceOp::Get),
514                        2 => app.op.set(UserSpaceOp::Set),
515                        3 => app.op.set(UserSpaceOp::Delete),
516                        4 => app.op.set(UserSpaceOp::Add),
517                        5 => app.op.set(UserSpaceOp::Update),
518                        6 => app.op.set(UserSpaceOp::GarbageCollect),
519                        _ => {}
520                    });
521                    let ret = self.run();
522
523                    if let Err(e) = ret {
524                        self.processid.clear();
525                        self.check_queue();
526                        CommandReturn::failure(e)
527                    } else {
528                        CommandReturn::success()
529                    }
530                } else {
531                    // There is an active app, so queue this request (if
532                    // possible).
533                    self.apps
534                        .enter(processid, |app, _| {
535                            if app.op.is_some() {
536                                // No more room in the queue, nowhere to store
537                                // this request.
538                                CommandReturn::failure(ErrorCode::NOMEM)
539                            } else {
540                                // This app has not already queued a command so
541                                // we can store this.
542                                match command_num {
543                                    1 => app.op.set(UserSpaceOp::Get),
544                                    2 => app.op.set(UserSpaceOp::Set),
545                                    3 => app.op.set(UserSpaceOp::Delete),
546                                    4 => app.op.set(UserSpaceOp::Add),
547                                    5 => app.op.set(UserSpaceOp::Update),
548                                    6 => app.op.set(UserSpaceOp::GarbageCollect),
549                                    _ => {}
550                                }
551                                CommandReturn::success()
552                            }
553                        })
554                        .unwrap_or_else(|err| err.into())
555                }
556            }
557
558            // default
559            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
560        }
561    }
562
563    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
564        self.apps.enter(processid, |_, _| {})
565    }
566}