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}