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}