Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generating instruction footprints for different privilege modes. #86

Open
neeluk7 opened this issue Nov 25, 2024 · 6 comments
Open

Generating instruction footprints for different privilege modes. #86

neeluk7 opened this issue Nov 25, 2024 · 6 comments

Comments

@neeluk7
Copy link

neeluk7 commented Nov 25, 2024

Hello,

I am wondering if it's possible to generate footprints for each instruction that are specific to different privilege modes?

Particularly for RISC-V, I tried to set the cur_privilege to Supervisor in the config. Here's what the trace looked like:

(trace
(assume-reg |monomorphize_reads| nil false)
(assume-reg |monomorphize_writes| nil false)
(assume-reg |cur_privilege| nil |Supervisor|)
(assume-reg |misa| nil (
struct (|bits| #x0000000000000000)))
(assume-reg |mstatus| nil (
struct (|bits| #x0000000000000000)))
(assume-reg |mcountinhibit| nil (
struct (|bits| #x00000000)))
(assume-reg |tlb| nil (|None| (
unit)))
(assume-reg |satp| nil #x0000000000000000)
(define-enum |Privilege| 3 (|User| |Supervisor| |Machine|)) ; 0:0 - 0:0
(define-enum |Architecture| 3 (|RV32| |RV64| |RV128|)) ; 0:0 - 0:0
(define-enum |PmpAddrMatchType| 4 (|OFF| |TOR| |NA4| |NAPOT|)) ; 0:0 - 0:0
(cycle)
(read-reg |cur_privilege| nil |Machine|)
(read-reg |cur_privilege| nil |Machine|)
(declare-const v442 (_ BitVec 64)) ; model/riscv_insts_base.sail 652:16 - 652:64
(read-reg |PC| nil v442)
(read-reg |cur_privilege| nil |Machine|)
(read-reg |mstatus| ((_ field |bits|)) (_ struct (|bits| #x0000000a00000000)))
(read-reg |mstatus| nil (_ struct (|bits| #x0000000a00000000)))
(read-reg |mstatus| nil (_ struct (|bits| #x0000000a00000000)))
(write-reg |mstatus| nil (_ struct (|bits| #x0000000a00000000)))
(read-reg |mstatus| ((_ field |bits|)) (_ struct (|bits| #x0000000a00000000)))
(read-reg |mstatus| nil (_ struct (|bits| #x0000000a00000000)))
(write-reg |mstatus| nil (_ struct (|bits| #x0000000a00000080)))
(read-reg |mstatus| nil (_ struct (|bits| #x0000000a00000080)))
(write-reg |cur_privilege| nil |User|)
(read-reg |mstatus| ((_ field |bits|)) (_ struct (|bits| #x0000000a00000080)))
(read-reg |misa| nil (_ struct (|bits| #x8000000000141105)))
(read-reg |mstatus| nil (_ struct (|bits| #x0000000a00000080)))
(write-reg |mstatus| nil (_ struct (|bits| #x0000000a00000080)))
(read-reg |cur_privilege| nil |User|)
(read-reg |mstatus| ((_ field |bits|)) (_ struct (|bits| #x0000000a00000080)))
(read-reg |mstatus| nil (_ struct (|bits| #x0000000a00000080)))
(write-reg |mstatus| nil (_ struct (|bits| #x0000000a00000080)))
(read-reg |mepc| nil #x0000000000000000)
(read-reg |misa| nil (_ struct (|bits| #x8000000000141105)))
(branch-address #x0000000000000000)
(write-reg |nextPC| nil #x0000000000000000)
(define-enum |Retired| 2 (|RETIRE_SUCCESS| |RETIRE_FAIL|)) ; 0:0 - 0:0)

Even though I can see the assume-reg cur_privilege with value Supervisor, eventually it says "define-enum" and sets that to Machine, so when cur_privilege is read after the (cycle) event, it's set to Machine and not Supervisor.

I haven't deep dived into the code to see what's causing this yet, it would be great if you can point me to that.

What I want to achieve is for instance, an instruction like "mret" should trap if executed by Supervisor mode.
Similarly, a "csrw mstatus, " should also trap if executed by Supervisor mode. Is this possible to observe in the footprints generated by Isla?

@neeluk7
Copy link
Author

neeluk7 commented Jan 13, 2025

Hello,

So I debugged further on the above and found out that even after specifying the register value in the riscv64_config.toml, it was being overwritten when there is a Define-Enum event for Privilege.

So I tried to update the privilege mode (cur_privilege) by overwriting it to what I wanted to set it with, in isla-lib/src/executor.rs in the get_id_and_initialize function. Here’s the diff:

@@ -125,8 +127,15 @@ fn get_id_and_initialize<’state, ’ir, B: BV>(
                 Some(value) => Borrowed(value),
                 None => match shared_state.type_info.enum_members.get(&id) {
                     Some((member, enum_size, enum_id)) => {
+                        let name_id = Name::from_u32(1683);
+                        let mut new_member: usize = *member;
+                        if *enum_id == name_id {
+                            new_member = 1; //for supervisor mode
+                        }
                         let enum_id = solver.get_enum(*enum_id, *enum_size);
-                        Owned(Val::Enum(EnumMember { enum_id, member: *member }))
+                        Owned(Val::Enum(EnumMember { enum_id, member: new_member }))
                     }
                     None => return Err(ExecError::VariableNotFound(zencode::decode([shared_state.symtab.to](http://shared_state.symtab.to/)_str(id)))),
                 },

It's a bit of a hack, I check using the id 1683 when cur_privilege is being checked for, and I return the desired value instead of what "shared_state.type_info.enum_members.get" returns.
With this, cur_privilege has the value I expect.

But I am not sure if it's working as expected, because for instance, with supervisor mode especially, I observe certain unexpected outputs.

Following are some of those scenarios,
but my first question is, is it a correct way to update the privilege mode or am I missing something and the above patch could generate incorrect results?

The unexpected outputs:

(1) I see an illegal instruction trap in the trace (a write to mcause in the trace with the value "2", write to mepc with current PC, etc.) on trying to read the S-mode registers like 'sstatus', 'stvec', 'sscratch', 'sepc', 'scause', 'stval', 'sip', 'sie', 'satp', 'scounteren', 'senvcfg' with Supervisor mode priivleges.
I see that the config file says that the mmu is not being used for RISC-V,
However, misa does have the supervisor mode bit enabled and reading these CSRs with machine mode privileges works as expected. Also, I see the expected traps in the trace on attempting to read these CSRs with user mode privileges.
Please see the trace attached (sup_sstatus_read.txt).

(2) Further, executing the mret instruction doesn't trap on setting the privilege mode to Supervisor or User. (trace attached: sup_mret.txt)
sup_mret.txt
sup_sstatus_read.txt

@bacam
Copy link
Contributor

bacam commented Jan 13, 2025

By default, init_model is called when isla-footprint is started, which resets cur_privilege to Machine. However, it will also call a special Isla function to set up some registers before executing the instruction, which you can configure with the registers.reset section of the configuration file, or the -R command line option.

Another option is to avoid init_model entirely, which you can do using the -f isla_footprint_no_init command line option.

The hack you've tried won't work in general because it will override all accesses even if the register is changed by the instruction, and it's fragile - when the numbering changes because of changes to the Sail source code it will break.

@neeluk7
Copy link
Author

neeluk7 commented Jan 16, 2025

Hello Brian,

Thanks for your response. Though configuring the cur_privilege by adding it to registers.reset in the config file doesn't work (not sure why), using the -R command line option works!

Indeed the hack I tried caused some issues, because the discrepancies I mentioned earlier (that occurred with my hack) are gone now.

I have one more question regarding the traces:

In the trace, some of the events are like the following where you have a field of a register (mstatus in this case) being read. Is there a way to also retrieve which field of mstatus is being read? e.g. MIE/MPP, etc.

(read-reg |mstatus| ((_ field |bits|)) (_ struct (|bits| #x0000000a00000000)))

@bacam
Copy link
Contributor

bacam commented Jan 16, 2025

Not directly, especially as you can access arbitrary bit-ranges in the register without giving a field. That said, most of the time they are accessed via helper functions, so if you add (for example) --trace-function _get_Mstatus_MPP to the command line there will be an entry in the trace for it just after the corresponding read-reg when MPP is read.

@Alasdair
Copy link
Collaborator

Hmm, the registers.reset in the config and the -R command line option should both just use the same code path, so I'm not sure why one would work and the other wouldn't.

@neeluk7
Copy link
Author

neeluk7 commented Jan 16, 2025

Hello Brian,

indeed the --trace-function option works in that case. Thanks a lot!

Hello Alasdair,
I am not sure either, I just double checked and registers.reset still doesn't work (but -R works).
I will recompile after removing all my changes and see if the registers.reset works then, maybe my changes are affecting that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants