Skip to content

Commit ed9fcd1

Browse files
committed
reader: add # for "swap" command
Add a reader token that will swap the top two elements on the stack, which means we can do things like this: wrsmn # . setbits 1,4 0b101 . rdsmn @0x1230_0000 Which will, 1. Push 0x12300000 onto the stack 2. Duplicate it so that the stack has two copies of 0x12300000 3. Call `rdsmn` which will pop the first copy and read whatever is at that SMN address (this is just an example; assume it is a valid SMN address) and push it onto the stack. The stack now contains the value it read via SMN, as well as the `0x1230_000` pushed earlier 4. Push 0b101, 1,4 and invoke setbits, which will pop the first three values (1,4, 0b101 and the contents of the SMN register read a moment ago). Set bits will set bits 1..=3 in the SMN value to `101` and push that onto the stack. The stack now contains the modified SMN value and 0x12300000. 5. Swap the top two elements of the stack, so that it now contains the SMN address at the top, then the modified value 6. Invoke `wrsmn` which will pop the address and value, and write the value to that SMN address. And we're done. This may be clearer using the `|` syntax: rdsmn @0x1230_0000 | setbits 1,4 0b101 | wrsmn # Signed-off-by: Dan Cross <cross@oxidecomputer.com>
1 parent 48abd2a commit ed9fcd1

3 files changed

Lines changed: 59 additions & 11 deletions

File tree

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ values (when appropriate). The REPL will always print the value
3131
returned by the last command.
3232

3333
The `@` command duplicates the value at the top of the stack and
34-
pushes the duplicate. The `$` command will push a `nil`.
34+
pushes the duplicate. The `$` command will push a `nil`. The
35+
`#` command swaps the two elements at the top of the stack.
3536

3637
To push an element onto the stack, use the `push` command. To
3738
pop the top element, one may use the `pop` command. Note also
@@ -95,6 +96,8 @@ equivalent to:
9596
rz | @inflate | mount | load /platform/oxide/kernel/amd64/unix | call
9697
```
9798

99+
## Commands
100+
98101
The reader supports a handful of "reader commands":
99102

100103
* `clear` clears the terminal window
@@ -126,6 +129,10 @@ Supported commands include:
126129
entry point.
127130
* `loadmem <addr>,<len>` to load an ELF object from the given
128131
region of memory.
132+
* `call <location> [<up to 6 args>]` calls the System V ABI
133+
compliant function at `<location>`, passing up to six
134+
arguments taken from the environment stack argument list
135+
terminated by nil.
129136
* `rdmsr <u32>` to read the numbered MSR (note some MSRs can be
130137
specified by name, such as `IA32_APIC_BASE`).
131138
* `wrmsr <u32> <u64>` to write the given value to the given MSR.

src/repl/mod.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ fn evalcmd(
250250
"outw" => pio::outw(config, env),
251251
"peek" => memory::read(config, env),
252252
"poke" => memory::write(config, env),
253-
"pop" => Ok(popenv(env)),
253+
"pop" => Ok(pop2(env)),
254254
"push" => Ok(Value::Nil),
255255
"rdmsr" => msr::read(config, env),
256256
"rdsmn" => smn::read(config, env),
@@ -276,24 +276,43 @@ fn dup(env: &mut Vec<Value>) -> Value {
276276
}
277277
}
278278

279+
fn swaptop(env: &mut [Value]) -> Value {
280+
let len = env.len();
281+
if len > 1 {
282+
env.swap(len - 1, len - 2);
283+
env[len - 1].clone()
284+
} else {
285+
Value::Nil
286+
}
287+
}
288+
279289
fn popenv(env: &mut Vec<Value>) -> Value {
280290
if let Some(v) = env.pop() { v } else { Value::Nil }
281291
}
282292

293+
fn pop2(env: &mut Vec<Value>) -> Value {
294+
popenv(env);
295+
popenv(env)
296+
}
297+
283298
fn eval(
284299
config: &mut bldb::Config,
285300
cmd: &reader::Command,
286301
env: &mut Vec<Value>,
287302
) -> Result<Value> {
288303
match cmd {
289304
reader::Command::Push => Ok(dup(env)),
305+
reader::Command::Swap => Ok(swaptop(env)),
290306
reader::Command::Cmd(_, tokens) => {
291307
let mut tokens = tokens.clone();
292308
while let Some(token) = tokens.pop() {
293309
match token {
294310
reader::Token::Push => {
295311
dup(env);
296312
}
313+
reader::Token::Swap => {
314+
swaptop(env);
315+
}
297316
reader::Token::Term => env.push(Value::Nil),
298317
reader::Token::Value(v) => env.push(v),
299318
}
@@ -304,9 +323,7 @@ fn eval(
304323
match evalcmd(config, &cmd, env)? {
305324
Value::Nil => Ok(Value::Nil),
306325
v => {
307-
if &cmd != "pop" {
308-
env.push(v.clone());
309-
}
326+
env.push(v.clone());
310327
Ok(v)
311328
}
312329
}

src/repl/reader.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ use core::fmt;
1515
#[derive(Clone, Debug)]
1616
pub enum Token {
1717
Push,
18+
Swap,
1819
Term,
1920
Value(Value),
2021
}
2122

2223
#[derive(Clone)]
2324
pub enum Command {
2425
Push,
26+
Swap,
2527
Cmd(String, Vec<Token>),
2628
}
2729

@@ -32,6 +34,7 @@ impl fmt::Debug for Command {
3234
) -> core::result::Result<(), fmt::Error> {
3335
match self {
3436
Self::Push => write!(f, "Push"),
37+
Self::Swap => write!(f, "Swap"),
3538
Self::Cmd(cmd, _) => write!(f, "{cmd}"),
3639
}
3740
}
@@ -159,21 +162,35 @@ pub fn read(
159162
for cmd in cs {
160163
let mut cmd = cmd.trim();
161164
let cmdline = String::from(cmd);
162-
while let Some(rest) = cmd.strip_prefix("@") {
163-
cmds.push(Command::Push);
164-
cmd = rest.trim();
165+
while !cmd.is_empty() {
166+
if let Some(rest) = cmd.strip_prefix("@") {
167+
cmds.push(Command::Push);
168+
cmd = rest.trim();
169+
continue;
170+
}
171+
if let Some(rest) = cmd.strip_prefix("#") {
172+
cmds.push(Command::Swap);
173+
cmd = rest.trim();
174+
continue;
175+
}
176+
break;
165177
}
166178
let mut tokens = Vec::<Token>::new();
167179
for mut tok in cmd.split_ascii_whitespace() {
168180
while !tok.is_empty() {
169181
if let Some(rest) = tok.strip_prefix("@") {
170182
tokens.push(Token::Push);
171-
tok = rest;
183+
tok = rest.trim();
184+
continue;
185+
}
186+
if let Some(rest) = tok.strip_prefix("#") {
187+
tokens.push(Token::Swap);
188+
tok = rest.trim();
172189
continue;
173190
}
174191
if let Some(rest) = tok.strip_prefix("$") {
175192
tokens.push(Token::Term);
176-
tok = rest;
193+
tok = rest.trim();
177194
continue;
178195
}
179196
tokens.push(Token::Value(parse_value(tok)?));
@@ -225,7 +242,8 @@ values (when appropriate). The REPL will always print the value
225242
returned by the last command.
226243
227244
The `@` command duplicates the value at the top of the stack and
228-
pushes the duplicate. The `$` command will push a `nil`.
245+
pushes the duplicate. The `$` command will push a `nil`. The
246+
`#` command swaps the two elements at the top of the stack.
229247
230248
To push an element onto the stack, use the `push` command. To
231249
pop the top element, one may use the `pop` command. Note also
@@ -265,6 +283,8 @@ equivalent to:
265283
rz | @inflate | mount | load /platform/oxide/kernel/amd64/unix | call
266284
```
267285
286+
## Commands
287+
268288
The reader supports a handful of "reader commands":
269289
270290
* `clear` clears the terminal window
@@ -296,6 +316,10 @@ Supported commands include:
296316
entry point
297317
* `loadmem <addr>,<len>` to load an ELF object from the given
298318
region of memory.
319+
* `call <location> [<up to 6 args>]` calls the System V ABI
320+
compliant function at `<location>`, passing up to six
321+
arguments taken from the environment stack argument list
322+
terminated by nil.
299323
* `rdmsr <u32>` to read the numbered MSR (note some MSRs can be
300324
specified by name, such as `IA32_APIC_BASE`)
301325
* `wrmsr <u32> <u64>` to write the given value to the given MSR

0 commit comments

Comments
 (0)