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

Bring up Linux kernel #508

Merged
merged 5 commits into from
Dec 16, 2024
Merged

Conversation

ChinYikMing
Copy link
Collaborator

@ChinYikMing ChinYikMing commented Oct 28, 2024

Clone the branch:

$ git clone https://github.com/ChinYikMing/rv32emu.git -b feat/bring-up-linux --depth 1

Checkout the repo:

$ cd rv32emu

Fetch prebuilt Linux image and run:

$ make system ENABLE_SYSTEM=1 -j8

To exit VM:

CTRL+a + x

@ChinYikMing ChinYikMing marked this pull request as draft October 28, 2024 19:11
Copy link
Contributor

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks

Benchmark suite Current: 3ef4675 Previous: 0db8b76 Ratio
Dhrystone 1547 Average DMIPS over 10 runs 1545 Average DMIPS over 10 runs 1.00
Coremark 1409.44 Average iterations/sec over 10 runs 1409.235 Average iterations/sec over 10 runs 1.00

This comment was automatically generated by workflow using github-action-benchmark.

@jserv jserv requested a review from vacantron October 28, 2024 19:25
src/common.h Show resolved Hide resolved
src/decode.c Outdated Show resolved Hide resolved
src/decode.h Show resolved Hide resolved
@jserv
Copy link
Contributor

jserv commented Oct 28, 2024

Can you exploit the prebuilt image files used by semu?

@ChinYikMing
Copy link
Collaborator Author

Can you exploit the prebuilt image files used by semu?

Yes, intended. Ultimately, the Image in current build directory will be removed.

Copy link
Contributor

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the hardware model files such as UART and PLIC to the directory src/devices for maintenance purposes.

@jserv jserv added this to the release-2024.2 milestone Oct 28, 2024
@jserv

This comment was marked as outdated.

src/common.h Outdated Show resolved Hide resolved
@jserv

This comment was marked as resolved.

src/emulate.c Outdated Show resolved Hide resolved
src/emulate.c Show resolved Hide resolved
@ChinYikMing ChinYikMing force-pushed the feat/bring-up-linux branch 10 times, most recently from dd0b1c5 to 4012f2a Compare November 2, 2024 15:48
jserv

This comment was marked as resolved.

Makefile Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
@ChinYikMing ChinYikMing force-pushed the feat/bring-up-linux branch 3 times, most recently from 94abf43 to 0db8b76 Compare December 16, 2024 08:05
@vacantron
Copy link
Collaborator

A compilation error is found. Reproducer:

$ make distclean
$ make system ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_T2C=0 

Error messages:

src/riscv.c: In function ‘rv_delete’:
src/riscv.c:586:18: error: ‘attr’ undeclared (first use in this function)
  586 |     u8250_delete(attr->uart);
      |                  ^~~~
src/riscv.c:586:18: note: each undeclared identifier is reported only once for each function it appears in
make: *** [Makefile:257: build/riscv.o] Error 1

One more error should occur if ENABLE_T2C=1 is set

src/emulate.c:45:13: warning: ‘need_clear_block_map’ defined but not used [-Wunused-variable]
   45 | static bool need_clear_block_map = false;
      |             ^~~~~~~~~~~~~~~~~~~~

@jserv, the errors have been fixed. Please check out this branch, and use the following commands to boot Linux Kernel with JIT compilation.

$ make ENABLE_SYSTEM=1 ENABLE_MOP_FUSION=0 ENABLE_JIT=1 ENABLE_T2C=0
$ ./build/rv32emu -k <image> -i <rootfs> -b <dtb>

The 8250 UART and PLIC peripherals, defined in src/devices/uart.[ch]
and src/devices/plic.[ch], have been integrated. Modifications to
src/system.c enable MMIO read and write operations for these
peripherals. The minimal device tree source file is also integrated in
src/devices/minimal.dts.

Default termios control flags are set to ICANON | ECHO | ISIG, allowing
all keyboard input to be captured by the guestOS.

The CLI parser has been updated to support custom Linux Kernel Images,
rootfs, and Device Tree Blobs.

The ELF_LOADER has been introduced to support test suites involving
single ELF files that use the rv32emu built-in syscall. It helps to
choose the right executable loader and requires differentiation from
guestOS syscalls.

The csrrw instruction is now branchable since it may overwrite the SATP
CSR during guestOS process scheduling.

last_pc and is_branch_taken are now updated only when not in a trapped
state, otherwise might lost the original state of them.

A system make target has been added to simplify running system
emulation.

Misc changes:
- Rename 'ARRAYS_SIZE' to 'ARRAY_SIZE' for better readability
- Move JIT related variables to proper conditional build flag
All Buildroot, BusyBox, and Linux configuration files are saved in the
assets directory to allow someone to build on their own. The
mk/external.mk functions has been updated to enable downloading via
'git clone' and verifying the downloaded files excluding the
git-related files. The Linux kernel image and Buildroot version are
controlled in mk/external.mk, when the LINUX_VERSION or
BUILDROOT_VERSION changes, the new Linux kernel image and rootfs.cpio
are built and pushed to the rv32emu-prebuilt repository, the user ELF
executables and system image are identified by suffix 'ELF' and
'Linux-Image' at release of rv32emu-prebuilt repository. Additionally,
a 'build-linux-image' make target has been introduced to simplify the
cloning and automatic building process.
The .ci/boot-linux.sh script has been introduced to automatically run
booting verification.
Block chaining and macro-operation (MOP) fusion are accelerator
techniques designed to enhance performance. To conduct further ablation
studies, two feature options, MOP_FUSION and BLOCK_CHAINING, are
introduced. These features can be freely combined with the other
emulator features.

Ablation study of these two techniques in system emulation:
| Metric             | Block Chaining & | Block Chaining | MOP fusion |
|                    | MOP fusion       |                |            |
|--------------------|------------------|----------------|------------|
| Dhrystone (DMIPS)  | 175              | 123            | 175        |
| (1000000 runs)     |                  |                |            |
|                    |                  |                |            |
| Coremark  (Iter/s) | 222              | 204            | 222        |
Mention experimental system emulation and explain how to build custom
Linux image. In addition, introduce the ablation study features for
performance analysis.
@jserv jserv merged commit 04e7396 into sysprog21:master Dec 16, 2024
8 checks passed
@jserv
Copy link
Contributor

jserv commented Dec 16, 2024

Thank @ChinYikMing for contributing!

@ChinYikMing ChinYikMing deleted the feat/bring-up-linux branch December 16, 2024 13:22
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 20, 2024
The configuration files for the Linux images have been stored in the
assets/system directory. To avoid mixing the Linux image configurations
with the existing wasm assets, the wasm assets have been moved to a
separate assets/wasm directory since sysprog21#508. However, the old wasm assets
were not removed, so delete the redundant ones.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 20, 2024
The configuration files for the Linux images have been stored in the
assets/system directory. To avoid mixing the Linux image configurations
with the existing Wasm assets, the Wasm assets have been moved to a
separate assets/wasm directory since sysprog21#508. However, the old Wasm assets
were not removed, so delete the redundant ones.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 20, 2024
The configuration files for the Linux images have been stored in the
assets/system directory. To avoid mixing the Linux image configurations
with the existing Wasm assets, the Wasm assets have been moved to a
separate assets/wasm directory since sysprog21#508. However, the old Wasm assets
were not removed, so delete the redundant ones.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 20, 2024
The configuration files for the Linux images have been stored in the
assets/system directory. To avoid mixing the Linux image configurations
with the existing Wasm assets, the Wasm assets have been moved to a
separate assets/wasm directory since sysprog21#508. However, the old Wasm assets
were not removed, so delete the redundant ones.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 21, 2024
The configuration files for the Linux images have been stored in the
assets/system directory. To avoid mixing the Linux image configurations
with the existing Wasm assets, the Wasm assets have been moved to a
separate assets/wasm directory since sysprog21#508. However, the old Wasm assets
were not removed, so delete the redundant ones.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 26, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                           # Emulator crashes
3. $ mem_null_write                          # Emulator crashes
4. $ vi                                      # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 26, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                           # Emulator crashes
3. $ mem_null_write                          # Emulator crashes
4. $ vi                                      # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 27, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                           # Emulator crashes
3. $ mem_null_write                          # Emulator crashes
4. $ vi                                      # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 27, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 27, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 27, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 27, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)
2. $ mem_null_read       # NULL read causes SIGSEGV without crashing
3. $ mem_null_write      # NULL write causes SIGSEGV without crashing
4. $ vi                  # w/o filename causes SIGSEGV without crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 29, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read       # NULL read causes SIGSEGV with no crashing
3. $ mem_null_write      # NULL write causes SIGSEGV with no crashing
4. $ vi                  # w/o filename causes SIGSEGV with no crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 29, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read       # NULL read causes SIGSEGV with no crashing
3. $ mem_null_write      # NULL write causes SIGSEGV with no crashing
4. $ vi                  # w/o filename causes SIGSEGV with no crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 30, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read       # NULL read causes SIGSEGV with no crashing
3. $ mem_null_write      # NULL write causes SIGSEGV with no crashing
4. $ vi                  # w/o filename causes SIGSEGV with no crashing

[1] sysprog21#508
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request Dec 30, 2024
Page faults trigger a trap, which is handled by do_page_fault(). This
function calls lock_mm_and_find_vma() to locate and validate the virtual
memory area (VMA), returning the VMA if valid, or NULL otherwise.
Typically, attempts to read or write to a NULL VMA result in a NULL
return. If the VMA is invalid, bad_area_nosemaphore() is invoked, which
checks whether the fault originated in kernel or user space.

For user-space faults, a SIGSEGV signal is sent to the user process via
do_trap(), which determines if the signal should be ignored or blocked,
and if not, adds it to the task's pending signal list. Kernel-space
faults cause the kernel to crash via die_kernel_fault().

Before returning to user space (via the resume_userspace label), pending
work (indicated by the _TIF_WORK_MASK mask) is processed by
do_work_pending(). Signals are handled by do_signal(), which in turn
calls handle_signal(). handle_signal() creates a signal handler frame
that will be jumped to upon returning to user space. This frame creation
process might modifies the Control and Status Register (CSR) SEPC.
If there are a signal pending, the SEPC CSR overwritten the original
trap/fault PC. This caused an assertion failure in get_ppn_and_offset()
when running the vi program, reported in [1].

To address this, a variable last_csr_sepc was introduced to store the
original SEPC CSR value before entering the trap path. After returning
to user space, last_csr_sepc is compared with the current SEPC CSR
value. If they differ, the fault ld/st instruction returns early and
jumps to the signal handler frame.

This commit prevents emulator crashes when the guest OS accesses invalid
memory. Consequently, reads or writes to a NULL value now correctly
result in a segmentation fault. In addition, two user-space programs:
mem_null_read and mem_null_write are bundled into the rootfs for
verification.

Original behaviour:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read                            # Emulator crashes
3. $ mem_null_write                           # Emulator crashes
4. $ vi                                       # Emulator crashes

Patch Reproduce / Testing procedure:
1. $ make system ENABLE_SYSTEM=1 -j$(nproc)   # run system emulation
2. $ mem_null_read       # NULL read causes SIGSEGV with no crashing
3. $ mem_null_write      # NULL write causes SIGSEGV with no crashing
4. $ vi                  # w/o filename causes SIGSEGV with no crashing

[1] sysprog21#508
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

Successfully merging this pull request may close these issues.

6 participants