Tock V2 port - rebased and updated (#620)

* Changes from #580

* fixes USB cancel panic

* style fixes

* Update src/env/tock/storage.rs

Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>

---------

Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>
This commit is contained in:
kaczmarczyck
2023-05-05 09:55:16 +02:00
committed by GitHub
parent 645c1ba3a7
commit f25cdd6acc
78 changed files with 4079 additions and 4699 deletions

View File

@@ -12,24 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate lang_items;
use libtock_drivers::console::{Console, BUFFER_SIZE};
use libtock_console::Console;
use libtock_drivers::result::FlexUnwrap;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
libtock_core::stack_size! {0x800}
stack_size! {0x800}
set_main! {main}
type Syscalls = TockSyscalls;
fn main() {
// Write messages of length up to the console driver's buffer size.
let mut buf = [0; BUFFER_SIZE];
let mut buf = [0; 1024];
loop {
for i in 1..buf.len() {
for byte in buf.iter_mut().take(i) {
*byte = b'0' + ((i % 10) as u8);
}
buf[i] = b'\n';
Console::write_unbuffered(&mut buf[..(i + 1)]);
Console::<Syscalls>::write(&buf[..(i + 1)])
.map_err(|e| e.into())
.flex_unwrap();
}
}
}

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate alloc;
@@ -22,29 +23,28 @@ use alloc::vec::Vec;
use core::fmt::Write;
use crypto::{aes256, cbc, ecdsa, sha256, Hash256};
use ctap2::env::tock::TockRng;
use libtock_drivers::console::Console;
use libtock_console::{Console, ConsoleWriter};
use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer;
use libtock_drivers::timer::{Timer, Timestamp};
use libtock_runtime::{set_main, stack_size, TockSyscalls};
libtock_core::stack_size! {0x800}
stack_size! {0x2000}
set_main! {main}
type Syscalls = TockSyscalls;
fn main() {
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
// Setup the timer with a dummy callback (we only care about reading the current time, but the
// API forces us to set an alarm callback too).
let mut with_callback = timer::with_callback(|_, _| {});
let mut with_callback = timer::with_callback(|_| {});
let timer = with_callback.init().flex_unwrap();
let mut rng = TockRng {};
let mut rng = TockRng::<Syscalls>::default();
writeln!(console, "****************************************").unwrap();
writeln!(
console,
"Clock frequency: {} Hz",
timer.clock_frequency().hz()
)
.unwrap();
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
// AES
bench(&mut console, &timer, "aes256::EncryptionKey::new", || {
@@ -150,7 +150,7 @@ fn main() {
writeln!(console, "****************************************").unwrap();
}
fn bench<F>(console: &mut Console, timer: &Timer, title: &str, mut f: F)
fn bench<F>(console: &mut ConsoleWriter<Syscalls>, timer: &Timer<Syscalls>, title: &str, mut f: F)
where
F: FnMut(),
{
@@ -159,11 +159,13 @@ where
writeln!(console, "----------------------------------------").unwrap();
let mut count = 1;
for _ in 0..30 {
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let start =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
for _ in 0..count {
f();
}
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let end =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
let elapsed = (end - start).ms();
writeln!(
console,
@@ -173,7 +175,6 @@ where
elapsed / (count as f64)
)
.unwrap();
console.flush();
if elapsed > 1000.0 {
break;
}

View File

@@ -12,18 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate lang_items;
use core::fmt::Write;
use ctap2::env::tock::take_storage;
use libtock_drivers::console::Console;
use libtock_drivers::led;
use libtock_console::Console;
use libtock_drivers::result::FlexUnwrap;
use libtock_leds::Leds;
use libtock_platform as platform;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
use persistent_store::{Storage, StorageIndex};
use platform::DefaultConfig;
libtock_core::stack_size! {0x800}
stack_size! {0x800}
set_main! {main}
type Syscalls = TockSyscalls;
fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
let index = StorageIndex { page, byte: 0 };
@@ -36,20 +43,21 @@ fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
}
fn main() {
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
let mut storage = take_storage().unwrap();
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap(); // red on dongle
let mut storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
let num_pages = storage.num_pages();
writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap();
let mut console = Console::<Syscalls>::writer();
writeln!(console, "Erase {} pages of storage:", num_pages).unwrap();
for page in 0..num_pages {
write!(Console::new(), "- Page {} ", page).unwrap();
write!(console, "- Page {} ", page).unwrap();
if is_page_erased(&storage, page) {
writeln!(Console::new(), "skipped (was already erased).").unwrap();
writeln!(console, "skipped (was already erased).").unwrap();
} else {
storage.erase_page(page).unwrap();
writeln!(Console::new(), "erased.").unwrap();
writeln!(console, "erased.").unwrap();
}
}
writeln!(Console::new(), "Done.").unwrap();
led::get(1).flex_unwrap().off().flex_unwrap();
led::get(0).flex_unwrap().on().flex_unwrap(); // green on dongle
writeln!(console, "Done.").unwrap();
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap();
Leds::<Syscalls>::off(0).map_err(|e| e.into()).flex_unwrap(); // green on dongle
}

View File

@@ -1,3 +1,4 @@
#![no_main]
#![no_std]
extern crate alloc;
@@ -5,27 +6,32 @@ extern crate lang_items;
extern crate libtock_drivers;
use core::fmt::Write;
use libtock_drivers::console::Console;
use libtock_console::{Console, ConsoleWriter};
use libtock_runtime::{set_main, stack_size, TockSyscalls};
libtock_core::stack_size! {0x4000}
stack_size! {0x4000}
set_main! {main}
type Syscalls = TockSyscalls;
#[cfg(not(feature = "with_nfc"))]
mod example {
use super::{Console, Write};
use super::{ConsoleWriter, Syscalls, Write};
pub fn nfc(console: &mut Console) {
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
writeln!(console, "NFC feature flag is missing!").unwrap();
}
}
#[cfg(feature = "with_nfc")]
mod example {
use super::{Console, Write};
use libtock_core::result::CommandError;
use super::{Console, ConsoleWriter, Write};
use crate::Syscalls;
use libtock_drivers::nfc::{NfcTag, RecvOp};
use libtock_drivers::result::{FlexUnwrap, TockError};
use libtock_drivers::timer;
use libtock_drivers::timer::{Timer, Timestamp};
use libtock_platform::{DefaultConfig, ErrorCode};
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
@@ -48,16 +54,15 @@ mod example {
ENOSUPPORT,
}
impl From<isize> for ReturnCode {
fn from(original: isize) -> ReturnCode {
impl From<ErrorCode> for ReturnCode {
fn from(original: ErrorCode) -> ReturnCode {
match original {
0 => ReturnCode::SUCCESS,
-1 => ReturnCode::FAIL,
-2 => ReturnCode::EBUSY,
-4 => ReturnCode::EOFF,
-6 => ReturnCode::EINVAL,
-8 => ReturnCode::ECANCEL,
-9 => ReturnCode::ENOMEM,
ErrorCode::Fail => ReturnCode::FAIL,
ErrorCode::Busy => ReturnCode::EBUSY,
ErrorCode::Off => ReturnCode::EOFF,
ErrorCode::Invalid => ReturnCode::EINVAL,
ErrorCode::Cancel => ReturnCode::ECANCEL,
ErrorCode::NoMem => ReturnCode::ENOMEM,
_ => ReturnCode::ENOSUPPORT,
}
}
@@ -66,34 +71,32 @@ mod example {
/// Helper function to write on console the received packet.
fn print_rx_buffer(buf: &mut [u8]) {
if let Some((last, bytes)) = buf.split_last() {
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
write!(console, "RX:").unwrap();
for byte in bytes {
write!(console, " {:02x?}", byte).unwrap();
}
writeln!(console, " {:02x?}", last).unwrap();
console.flush();
}
}
/// Function to identify the time elapsed for a transmission request.
fn bench_transmit(
console: &mut Console,
timer: &Timer,
console: &mut ConsoleWriter<Syscalls>,
timer: &Timer<Syscalls>,
title: &str,
mut buf: &mut [u8],
buf: &mut [u8],
) -> ReturnCode {
let amount = buf.len();
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
match NfcTag::transmit(&mut buf, amount) {
let start =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
match NfcTag::<Syscalls, DefaultConfig>::transmit(buf, amount as u32) {
Ok(_) => (),
Err(TockError::Command(CommandError {
return_code: -8, /* ECANCEL: No Field*/
..
})) => return ReturnCode::ECANCEL,
Err(_) => writeln!(Console::new(), " -- tx error!").unwrap(),
Err(TockError::Command(ErrorCode::Cancel)) => return ReturnCode::ECANCEL,
Err(_) => writeln!(console, " -- tx error!").unwrap(),
}
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let end =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
let elapsed = (end - start).ms();
writeln!(
console,
@@ -104,21 +107,21 @@ mod example {
(amount as f64) / elapsed * 8.
)
.unwrap();
console.flush();
ReturnCode::SUCCESS
}
fn receive_packet(console: &mut Console, mut buf: &mut [u8; 256]) -> ReturnCode {
match NfcTag::receive(&mut buf) {
fn receive_packet(console: &mut ConsoleWriter<Syscalls>, buf: &mut [u8; 256]) -> ReturnCode {
match NfcTag::<Syscalls, DefaultConfig>::receive(buf) {
Ok(RecvOp {
recv_amount: amount,
..
}) => {
if amount <= buf.len() {
print_rx_buffer(&mut buf[..amount]);
if amount <= buf.len() as u32 {
print_rx_buffer(&mut buf[..amount as usize]);
}
}
Err(TockError::Command(CommandError { return_code, .. })) => return return_code.into(),
Err(TockError::Command(code)) => return code.into(),
Err(_) => {
writeln!(console, " -- RX Err").unwrap();
return ReturnCode::ECANCEL;
@@ -127,54 +130,58 @@ mod example {
ReturnCode::SUCCESS
}
fn transmit_reply(mut console: &mut Console, timer: &Timer, buf: &[u8]) -> ReturnCode {
fn transmit_reply(
console: &mut ConsoleWriter<Syscalls>,
timer: &Timer<Syscalls>,
buf: &[u8],
) -> ReturnCode {
let mut return_code = ReturnCode::SUCCESS;
match buf[0] {
0xe0 /* RATS */=> {
let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
return_code = bench_transmit(console, timer, "TX: ATS", &mut answer_to_select);
}
0xc2 /* DESELECT */ => {
// Ignore the request
let mut command_error = [0x6A, 0x81];
return_code = bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
return_code = bench_transmit(console, timer, "TX: DESELECT", &mut command_error);
}
0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
// If the received packet is applet selection command (FIDO 2)
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
// Vesion: "FIDO_2_0"
let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
return_code = bench_transmit(console, timer, "TX: Version Str", &mut reply);
} else if (buf[6] == 0xd2 && buf[7] == 0x76) || (buf[6] == 0xe1 && (buf[7] == 0x03 || buf[7] == 0x04)){
let mut reply = [buf[0], 0x90, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
} else /* Unknown file */ {
let mut reply = [buf[0], 0x6a, 0x82];
return_code = bench_transmit(&mut console, &timer, "TX: 0x6A82", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x6A82", &mut reply);
}
0xb0 /* READ */ => match buf[5] {
0x02 => {
let mut reply = [buf[0], 0x12, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
return_code = bench_transmit(console, timer, "TX: File Size", &mut reply);
}
0x12 => {
let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
return_code = bench_transmit(console, timer, "TX: NDEF", &mut reply);
}
0x0f => {
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
return_code = bench_transmit(console, timer, "TX: CC", &mut reply);
}
_ => {
let mut reply = [buf[0], 0x90, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
}
}
_ => {
let mut reply = [buf[0], 0x90, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
}
}
0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
@@ -185,31 +192,27 @@ mod example {
return_code
}
pub fn nfc(mut console: &mut Console) {
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
// Setup the timer with a dummy callback (we only care about reading the current time, but the
// API forces us to set an alarm callback too).
let mut with_callback = timer::with_callback(|_, _| {});
let mut with_callback = timer::with_callback(|_| {});
let timer = with_callback.init().flex_unwrap();
writeln!(
console,
"Clock frequency: {} Hz",
timer.clock_frequency().hz()
)
.unwrap();
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
let mut state_change_counter = 0;
loop {
let mut rx_buf = [0; 256];
match receive_packet(&mut console, &mut rx_buf) {
match receive_packet(console, &mut rx_buf) {
ReturnCode::EOFF => {
// Not configured
while !NfcTag::enable_emulation() {}
while !NfcTag::<Syscalls, DefaultConfig>::enable_emulation() {}
// Configure Type 4 tag
while !NfcTag::configure(4) {}
while !NfcTag::<Syscalls, DefaultConfig>::configure(4) {}
}
ReturnCode::ECANCEL /* field lost */ => {
NfcTag::disable_emulation();
NfcTag::<Syscalls, DefaultConfig>::disable_emulation();
}
ReturnCode::EBUSY /* awaiting select*/ => (),
ReturnCode::ENOMEM => {
@@ -220,9 +223,9 @@ mod example {
ReturnCode::ENOSUPPORT => (),
ReturnCode::SUCCESS => {
// If the reader restarts the communication then disable the tag.
match transmit_reply(&mut console, &timer, &rx_buf) {
match transmit_reply(console, &timer, &rx_buf) {
ReturnCode::ECANCEL | ReturnCode::EOFF => {
if NfcTag::disable_emulation() {
if NfcTag::<Syscalls, DefaultConfig>::disable_emulation() {
writeln!(console, " -- TAG DISABLED").unwrap();
}
state_change_counter += 1;
@@ -239,7 +242,7 @@ mod example {
}
fn main() {
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
writeln!(console, "****************************************").unwrap();
writeln!(console, "nfct_test application is installed").unwrap();
example::nfc(&mut console);

View File

@@ -12,24 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate alloc;
extern crate lang_items;
libtock_core::stack_size! {0x800}
use alloc::vec::Vec;
use core::fmt::Write;
use libtock_drivers::console::Console;
use libtock_console::Console;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
stack_size! {0x800}
set_main! {main}
type Syscalls = TockSyscalls;
fn main() {
writeln!(Console::new(), "****************************************").unwrap();
let mut console = Console::<Syscalls>::writer();
writeln!(console, "****************************************").unwrap();
for i in 0.. {
writeln!(Console::new(), "Allocating {} bytes...", 1 << i).unwrap();
writeln!(console, "Allocating {} bytes...", 1 << i).unwrap();
let x: Vec<u8> = Vec::with_capacity(1 << i);
writeln!(Console::new(), "Allocated!").unwrap();
writeln!(console, "Allocated!").unwrap();
drop(x);
writeln!(Console::new(), "Dropped!").unwrap();
writeln!(console, "Dropped!").unwrap();
}
}

View File

@@ -12,11 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate lang_items;
libtock_core::stack_size! {0x800}
use libtock_runtime::{set_main, stack_size};
stack_size! {0x800}
set_main! {main}
fn main() {
panic!("Bye world!")

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate alloc;
@@ -22,25 +23,33 @@ use alloc::vec::Vec;
use alloc::{format, vec};
use core::fmt::Write;
use ctap2::env::tock::{take_storage, Storage};
use libtock_drivers::console::Console;
use libtock_console::Console;
use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
use persistent_store::Store;
use libtock_platform::DefaultConfig;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
use persistent_store::{Storage as _, Store};
libtock_core::stack_size! {0x2000}
stack_size! {0x800}
set_main! {main}
fn timestamp(timer: &Timer) -> Timestamp<f64> {
Timestamp::<f64>::from_clock_value(timer.get_current_clock().ok().unwrap())
type Syscalls = TockSyscalls;
fn timestamp(timer: &Timer<Syscalls>) -> Timestamp<f64> {
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().ok().unwrap())
}
fn measure<T>(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
fn measure<T>(timer: &Timer<Syscalls>, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
let before = timestamp(timer);
let result = operation();
let after = timestamp(timer);
(result, after - before)
}
fn boot_store(mut storage: Storage, erase: bool) -> Store<Storage> {
use persistent_store::Storage;
fn boot_store(
mut storage: Storage<Syscalls, DefaultConfig>,
erase: bool,
) -> Store<Storage<Syscalls, DefaultConfig>> {
let num_pages = storage.num_pages();
if erase {
for page in 0..num_pages {
@@ -55,8 +64,7 @@ struct StorageConfig {
num_pages: usize,
}
fn storage_config(storage: &Storage) -> StorageConfig {
use persistent_store::Storage;
fn storage_config(storage: &Storage<Syscalls, DefaultConfig>) -> StorageConfig {
StorageConfig {
num_pages: storage.num_pages(),
}
@@ -73,19 +81,19 @@ struct Stat {
}
fn compute_latency(
storage: Storage,
timer: &Timer,
storage: Storage<Syscalls, DefaultConfig>,
timer: &Timer<Syscalls>,
num_pages: usize,
key_increment: usize,
word_length: usize,
) -> (Storage, Stat) {
) -> (Storage<Syscalls, DefaultConfig>, Stat) {
let mut stat = Stat {
key_increment,
entry_length: word_length,
..Default::default()
};
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
writeln!(
console,
"\nLatency for key_increment={} word_length={}.",
@@ -155,20 +163,22 @@ fn compute_latency(
}
fn main() {
let mut with_callback = timer::with_callback(|_, _| {});
let timer = with_callback.init().ok().unwrap();
let storage = take_storage().unwrap();
let mut with_callback = timer::with_callback::<Syscalls, DefaultConfig, _>(|_| {});
let timer = with_callback.init().flex_unwrap();
let storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
let config = storage_config(&storage);
let mut stats = Vec::new();
let mut console = Console::<Syscalls>::writer();
writeln!(Console::new(), "\nRunning 2 tests...").unwrap();
writeln!(console, "\nRunning 2 tests...").unwrap();
// Simulate a store full of credentials (of 50 words).
let (storage, stat) = compute_latency(storage, &timer, config.num_pages, 1, 50);
stats.push(stat);
// Simulate a store full of increments of a single counter.
let (_storage, stat) = compute_latency(storage, &timer, config.num_pages, 0, 1);
stats.push(stat);
writeln!(Console::new(), "\nDone.\n").unwrap();
writeln!(console, "\nDone.\n").unwrap();
const HEADERS: &[&str] = &[
"Overwrite",
@@ -189,8 +199,8 @@ fn main() {
format!("{:.1} ms", stat.remove_ms),
]);
}
writeln!(Console::new(), "Copy to examples/store_latency.rs:\n").unwrap();
writeln!(Console::new(), "{:?}", config).unwrap();
writeln!(console, "Copy to examples/store_latency.rs:\n").unwrap();
writeln!(console, "{:?}", config).unwrap();
write_matrix(matrix);
// Results for nrf52840dk_opensk:
@@ -201,10 +211,11 @@ fn main() {
}
fn align(x: &str, n: usize) {
let mut console = Console::<Syscalls>::writer();
for _ in 0..n.saturating_sub(x.len()) {
write!(Console::new(), " ").unwrap();
write!(console, " ").unwrap();
}
write!(Console::new(), "{}", x).unwrap();
write!(console, "{}", x).unwrap();
}
fn write_matrix(mut m: Vec<Vec<String>>) {
@@ -223,6 +234,6 @@ fn write_matrix(mut m: Vec<Vec<String>>) {
for col in 0..num_cols {
align(&row[col], col_len[col] + 2 * (col > 0) as usize);
}
writeln!(Console::new()).unwrap();
writeln!(Console::<Syscalls>::writer()).unwrap();
}
}