add qemu pci virtio-blk device and trap registers

This commit is contained in:
Yuke Peng 2022-09-14 22:27:03 -07:00
parent 528888e632
commit 964a7413cd
12 changed files with 277 additions and 126 deletions

9
src/Cargo.lock generated
View File

@ -100,6 +100,7 @@ dependencies = [
"font8x8",
"lazy_static",
"linked_list_allocator",
"pci",
"spin 0.9.4",
"uart_16550",
"volatile",
@ -168,6 +169,14 @@ dependencies = [
"autocfg",
]
[[package]]
name = "pci"
version = "0.0.1"
source = "git+https://github.com/rcore-os/pci-rs#a4e7cea640f8a92ae4b51c6c51fee3f1f0317af2"
dependencies = [
"bitflags",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"

View File

@ -1,28 +1,24 @@
use anyhow::anyhow;
use std::{
fs::OpenOptions,
path::{Path, PathBuf},
process::{Command, ExitStatus},
time::Duration,
};
const RUN_ARGS: &[&str] = &[
const COMMON_ARGS: &[&str] = &[
"--no-reboot",
"-s",
"-device",
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-serial",
"stdio",
"-display",
"none",
];
const TEST_ARGS: &[&str] = &[
"-device",
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-device",
"virtio-blk-pci,bus=pci.0,addr=0x6,drive=x0",
"-serial",
"stdio",
"-display",
"none",
"--no-reboot",
];
const RUN_ARGS: &[&str] = &["-s"];
const TEST_ARGS: &[&str] = &[];
const TEST_TIMEOUT_SECS: u64 = 10;
fn main() -> anyhow::Result<()> {
let mut args = std::env::args().skip(1); // skip executable name
@ -53,8 +49,13 @@ fn main() -> anyhow::Result<()> {
.arg(format!("format=raw,file={}", bios.display()));
let binary_kind = runner_utils::binary_kind(&kernel_binary_path);
let mut args = COMMON_ARGS.clone().to_vec();
args.push("-drive");
let binding = create_fs_image(kernel_binary_path.as_path())?;
args.push(binding.as_str());
if binary_kind.is_test() {
run_cmd.args(TEST_ARGS);
args.append(&mut TEST_ARGS.to_vec());
run_cmd.args(args);
let exit_status = run_test_command(run_cmd)?;
match exit_status.code() {
@ -62,7 +63,8 @@ fn main() -> anyhow::Result<()> {
other => return Err(anyhow!("Test failed (exit code: {:?})", other)),
}
} else {
run_cmd.args(RUN_ARGS);
args.append(&mut RUN_ARGS.to_vec());
run_cmd.args(args);
let exit_status = run_cmd.status()?;
if !exit_status.success() {
@ -72,6 +74,29 @@ fn main() -> anyhow::Result<()> {
Ok(())
}
fn create_fs_image(path: &Path) -> anyhow::Result<String> {
let mut fs_img_path = path.parent().unwrap().to_str().unwrap().to_string();
fs_img_path.push_str("/fs.img");
let path = Path::new(fs_img_path.as_str());
if path.exists() {
return Ok(format!(
"file={},if=none,format=raw,id=x0",
fs_img_path.as_str()
));
}
let f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(fs_img_path.as_str())?;
// 64MiB
f.set_len(1 * 1024 * 1024).unwrap();
Ok(format!(
"file={},if=none,format=raw,id=x0",
fs_img_path.as_str()
))
}
pub fn create_disk_images(kernel_binary_path: &Path) -> PathBuf {
let bootloader_manifest_path = bootloader_locator::locate_bootloader("bootloader").unwrap();
let kernel_manifest_path = locate_cargo_manifest::locate_manifest().unwrap();

View File

@ -15,7 +15,7 @@ linked_list_allocator = "0.9.0"
bootloader = {version="0.10.12"}
font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"]}
uart_16550 = "0.2.0"
pci = { git = "https://github.com/rcore-os/pci-rs" }
[dependencies.lazy_static]
version = "1.0"

View File

@ -27,6 +27,16 @@ pub struct CpuContext {
pub gp_regs: GpRegs,
pub fs_base: u64,
pub fp_regs: FpRegs,
pub trap_information: Option<TrapInformation>,
}
#[derive(Clone, Default, Copy)]
#[repr(C)]
pub struct TrapInformation {
pub cr2: u64,
pub id: u64,
pub err: u64,
pub cs: u64,
pub ss: u64,
}
/// The general-purpose registers of CPU.
@ -57,27 +67,28 @@ impl From<SyscallFrame> for CpuContext {
fn from(syscall: SyscallFrame) -> Self {
Self {
gp_regs: GpRegs {
r8: syscall.caller.r8 as u64,
r9: syscall.caller.r9 as u64,
r10: syscall.caller.r10 as u64,
r11: syscall.caller.r11 as u64,
r12: syscall.callee.r12 as u64,
r13: syscall.callee.r13 as u64,
r14: syscall.callee.r14 as u64,
r15: syscall.callee.r15 as u64,
rdi: syscall.caller.rdi as u64,
rsi: syscall.caller.rsi as u64,
rbp: syscall.callee.rbp as u64,
rbx: syscall.callee.rbx as u64,
rdx: syscall.caller.rdx as u64,
rax: syscall.caller.rax as u64,
rcx: syscall.caller.rcx as u64,
rsp: syscall.callee.rsp as u64,
rip: syscall.caller.rcx as u64,
r8: syscall.caller.r8,
r9: syscall.caller.r9,
r10: syscall.caller.r10,
r11: syscall.caller.r11,
r12: syscall.callee.r12,
r13: syscall.callee.r13,
r14: syscall.callee.r14,
r15: syscall.callee.r15,
rdi: syscall.caller.rdi,
rsi: syscall.caller.rsi,
rbp: syscall.callee.rbp,
rbx: syscall.callee.rbx,
rdx: syscall.caller.rdx,
rax: syscall.caller.rax,
rcx: syscall.caller.rcx,
rsp: syscall.callee.rsp,
rip: syscall.caller.rcx,
rflag: 0,
},
fs_base: 0,
fp_regs: FpRegs::default(),
trap_information: None,
}
}
}
@ -86,24 +97,24 @@ impl Into<SyscallFrame> for CpuContext {
fn into(self) -> SyscallFrame {
SyscallFrame {
caller: CallerRegs {
rax: self.gp_regs.rax as usize,
rcx: self.gp_regs.rcx as usize,
rdx: self.gp_regs.rdx as usize,
rsi: self.gp_regs.rsi as usize,
rdi: self.gp_regs.rdi as usize,
r8: self.gp_regs.r8 as usize,
r9: self.gp_regs.r9 as usize,
r10: self.gp_regs.r10 as usize,
r11: self.gp_regs.r11 as usize,
rax: self.gp_regs.rax,
rcx: self.gp_regs.rcx,
rdx: self.gp_regs.rdx,
rsi: self.gp_regs.rsi,
rdi: self.gp_regs.rdi,
r8: self.gp_regs.r8,
r9: self.gp_regs.r9,
r10: self.gp_regs.r10,
r11: self.gp_regs.r11,
},
callee: CalleeRegs {
rsp: self.gp_regs.rsp as usize,
rbx: self.gp_regs.rbx as usize,
rbp: self.gp_regs.rbp as usize,
r12: self.gp_regs.r12 as usize,
r13: self.gp_regs.r13 as usize,
r14: self.gp_regs.r14 as usize,
r15: self.gp_regs.r15 as usize,
rsp: self.gp_regs.rsp,
rbx: self.gp_regs.rbx,
rbp: self.gp_regs.rbp,
r12: self.gp_regs.r12,
r13: self.gp_regs.r13,
r14: self.gp_regs.r14,
r15: self.gp_regs.r15,
},
}
}
@ -113,52 +124,70 @@ impl From<TrapFrame> for CpuContext {
fn from(trap: TrapFrame) -> Self {
Self {
gp_regs: GpRegs {
r8: trap.regs.r8 as u64,
r9: trap.regs.r9 as u64,
r10: trap.regs.r10 as u64,
r11: trap.regs.r11 as u64,
r12: trap.id as u64,
r13: trap.err as u64,
r14: trap.cs as u64,
r15: trap.ss as u64,
rdi: trap.regs.rdi as u64,
rsi: trap.regs.rsi as u64,
rbp: 0 as u64,
rbx: 0 as u64,
rdx: trap.regs.rdx as u64,
rax: trap.regs.rax as u64,
rcx: trap.regs.rcx as u64,
rsp: trap.rsp as u64,
rip: trap.rip as u64,
rflag: trap.rflags as u64,
r8: trap.caller.r8,
r9: trap.caller.r9,
r10: trap.caller.r10,
r11: trap.caller.r11,
r12: trap.callee.r12,
r13: trap.callee.r13,
r14: trap.callee.r14,
r15: trap.callee.r15,
rdi: trap.caller.rdi,
rsi: trap.caller.rsi,
rbp: trap.callee.rbp,
rbx: trap.callee.rbx,
rdx: trap.caller.rdx,
rax: trap.caller.rax,
rcx: trap.caller.rcx,
rsp: trap.rsp,
rip: trap.rip,
rflag: trap.rflags,
},
fs_base: 0,
fp_regs: FpRegs::default(),
trap_information: Some(TrapInformation {
cr2: trap.cr2,
id: trap.id,
err: trap.err,
cs: trap.cs,
ss: trap.ss,
}),
}
}
}
impl Into<TrapFrame> for CpuContext {
fn into(self) -> TrapFrame {
let trap_information = self.trap_information.unwrap();
TrapFrame {
regs: CallerRegs {
rax: self.gp_regs.rax as usize,
rcx: self.gp_regs.rcx as usize,
rdx: self.gp_regs.rdx as usize,
rsi: self.gp_regs.rsi as usize,
rdi: self.gp_regs.rdi as usize,
r8: self.gp_regs.r8 as usize,
r9: self.gp_regs.r9 as usize,
r10: self.gp_regs.r10 as usize,
r11: self.gp_regs.r11 as usize,
caller: CallerRegs {
rax: self.gp_regs.rax,
rcx: self.gp_regs.rcx,
rdx: self.gp_regs.rdx,
rsi: self.gp_regs.rsi,
rdi: self.gp_regs.rdi,
r8: self.gp_regs.r8,
r9: self.gp_regs.r9,
r10: self.gp_regs.r10,
r11: self.gp_regs.r11,
},
id: self.gp_regs.r12 as usize,
err: self.gp_regs.r13 as usize,
rip: self.gp_regs.rip as usize,
cs: self.gp_regs.r14 as usize,
rflags: self.gp_regs.rflag as usize,
rsp: self.gp_regs.rsp as usize,
ss: self.gp_regs.r15 as usize,
callee: CalleeRegs {
rsp: self.gp_regs.rsp,
rbx: self.gp_regs.rbx,
rbp: self.gp_regs.rbp,
r12: self.gp_regs.r12,
r13: self.gp_regs.r13,
r14: self.gp_regs.r14,
r15: self.gp_regs.r15,
},
id: trap_information.id,
err: trap_information.err,
cr2: trap_information.cr2,
rip: self.gp_regs.rip,
cs: trap_information.cs,
rflags: self.gp_regs.rflag,
rsp: self.gp_regs.rsp,
ss: trap_information.ss,
}
}
}

View File

@ -0,0 +1,5 @@
mod pci;
pub fn init() {
pci::init();
}

View File

@ -0,0 +1,62 @@
use crate::*;
use pci::*;
const PCI_COMMAND: u16 = 0x04;
const PCI_CAP_PTR: u16 = 0x34;
const PCI_INTERRUPT_LINE: u16 = 0x3c;
const PCI_INTERRUPT_PIN: u16 = 0x3d;
const PCI_MSI_CTRL_CAP: u16 = 0x00;
const PCI_MSI_ADDR: u16 = 0x04;
const PCI_MSI_UPPER_ADDR: u16 = 0x08;
const PCI_MSI_DATA_32: u16 = 0x08;
const PCI_MSI_DATA_64: u16 = 0x0C;
const PCI_CAP_ID_MSI: u8 = 0x05;
struct PortOpsImpl;
impl PortOps for PortOpsImpl {
unsafe fn read8(&self, port: u16) -> u8 {
x86_64_util::in8(port)
}
unsafe fn read16(&self, port: u16) -> u16 {
x86_64_util::in16(port)
}
unsafe fn read32(&self, port: u16) -> u32 {
x86_64_util::in32(port)
}
unsafe fn write8(&self, port: u16, val: u8) {
x86_64_util::out8(port, val);
}
unsafe fn write16(&self, port: u16, val: u16) {
x86_64_util::out16(port, val);
}
unsafe fn write32(&self, port: u16, val: u32) {
x86_64_util::out32(port, val);
}
}
pub fn init() {
for dev in unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) } {
info!(
"pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) irq: {}:{:?}",
dev.loc.bus,
dev.loc.device,
dev.loc.function,
dev.id.vendor_id,
dev.id.device_id,
dev.id.class,
dev.id.subclass,
dev.pic_interrupt_line,
dev.interrupt_pin
);
if dev.id.vendor_id == 0x1af4
&& (dev.id.device_id == 0x1001 || dev.id.device_id == 0x1042)
&& dev.id.class == 0x01
{
// virtio block device mass storage
info!("found virtio pci block device");
}
}
}

View File

@ -16,6 +16,7 @@ pub(crate) mod cell;
pub mod config;
pub mod cpu;
pub mod device;
pub(crate) mod drivers;
mod error;
pub mod log;
pub(crate) mod mm;
@ -75,6 +76,7 @@ pub fn init(boot_info: &'static mut BootInfo) {
if !memory_init {
panic!("memory init failed");
}
drivers::init();
unsafe {
for i in 0..256 {
IRQ_CALLBACK_LIST.push(IrqLine::acquire(i as u8).on_active(general_handler))
@ -82,9 +84,7 @@ pub fn init(boot_info: &'static mut BootInfo) {
}
}
fn general_handler(trap_frame: TrapFrame) {
println!("{:?}", trap_frame);
println!("rip = 0x{:x}", trap_frame.rip);
panic!("couldn't handler trap right now");
debug!("TrapFrame:{:#x?}", trap_frame);
}
#[inline(always)]

View File

@ -69,10 +69,9 @@ lazy_static! {
};
task.task_inner.exclusive_access().task_status = TaskStatus::Runnable;
task.task_inner.exclusive_access().ctx.rip = context_switch_to_user_space as usize;
task.task_inner.exclusive_access().ctx.regs.rsp = task.kstack.frame.end_pa().unwrap().kvaddr().0
as usize
task.task_inner.exclusive_access().ctx.regs.rsp = (task.kstack.frame.end_pa().unwrap().kvaddr().0
- size_of::<usize>()
- size_of::<SyscallFrame>();
- size_of::<SyscallFrame>()) as u64;
task
});
}
@ -172,16 +171,15 @@ impl Task {
result.task_inner.exclusive_access().task_status = TaskStatus::Runnable;
result.task_inner.exclusive_access().ctx.rip = kernel_task_entry as usize;
result.task_inner.exclusive_access().ctx.regs.rsp =
result.kstack.frame.end_pa().unwrap().kvaddr().0 as usize
(result.kstack.frame.end_pa().unwrap().kvaddr().0
- size_of::<usize>()
- size_of::<SyscallFrame>();
- size_of::<SyscallFrame>()) as u64;
let arc_self = Arc::new(result);
switch_to_task(arc_self.clone());
Ok(arc_self)
}
/// create a new task data structure without schedule it
pub fn new<F, T>(
task_fn: F,
task_data: T,
@ -217,14 +215,13 @@ impl Task {
result.task_inner.exclusive_access().task_status = TaskStatus::Runnable;
result.task_inner.exclusive_access().ctx.rip = kernel_task_entry as usize;
result.task_inner.exclusive_access().ctx.regs.rsp =
result.kstack.frame.end_pa().unwrap().kvaddr().0 as usize
(result.kstack.frame.end_pa().unwrap().kvaddr().0
- size_of::<usize>()
- size_of::<SyscallFrame>();
- size_of::<SyscallFrame>()) as u64;
Ok(Arc::new(result))
}
/// send the task to schedule
pub fn send_to_scheduler(self: &Arc<Self>) {
switch_to_task(self.clone());
}

View File

@ -40,7 +40,7 @@ pub(crate) extern "C" fn trap_handler(f: &'static mut TrapFrame) {
}
}
fn is_from_kernel(cs: usize) -> bool {
fn is_from_kernel(cs: u64) -> bool {
if cs & 0x3 == 0 {
true
} else {

View File

@ -12,27 +12,27 @@ core::arch::global_asm!(include_str!("vector.S"));
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct CallerRegs {
pub rax: usize,
pub rcx: usize,
pub rdx: usize,
pub rsi: usize,
pub rdi: usize,
pub r8: usize,
pub r9: usize,
pub r10: usize,
pub r11: usize,
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rsi: u64,
pub rdi: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
}
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct CalleeRegs {
pub rsp: usize,
pub rbx: usize,
pub rbp: usize,
pub r12: usize,
pub r13: usize,
pub r14: usize,
pub r15: usize,
pub rsp: u64,
pub rbx: u64,
pub rbp: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
}
#[derive(Debug, Default, Clone, Copy)]
@ -45,15 +45,18 @@ pub struct SyscallFrame {
#[derive(Debug, Default, Clone, Copy)]
#[repr(C)]
pub struct TrapFrame {
pub regs: CallerRegs,
pub id: usize,
pub err: usize,
pub cr2: u64,
pub caller: CallerRegs,
// do not use the rsp inside the callee, use another rsp instead
pub callee: CalleeRegs,
pub id: u64,
pub err: u64,
// Pushed by CPU
pub rip: usize,
pub cs: usize,
pub rflags: usize,
pub rsp: usize,
pub ss: usize,
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub rsp: u64,
pub ss: u64,
}
const TSS_SIZE: usize = 104;

View File

@ -31,20 +31,40 @@ TSS:
.global __trap_entry
__trap_entry:
#
#
push r15
push r14
push r13
push r12
push rbp
push rbx
mov rdi, 0
push rdi
save
# save cr2
mov rdi, cr2
push rdi
# trap_handler
mov rdi, rsp
call trap_handler
__trap_return:
# judge whether the trap from kernel mode
mov rax, [rsp + 96] # 96 = offsetof(TrapFrame, cs)
mov rax, [rsp + 160] # 160 = offsetof(TrapFrame, cs)
and rax, 0x3
jz __from_kernel
lea rax, [rsp + 128] # prepare new TSS.sp0, 128 = sizeof(TrapFrame)
lea rax, [rsp + 192] # prepare new TSS.sp0, 192 = sizeof(TrapFrame)
mov [TSS + rip + 4], rax
__from_kernel:
add rsp, 8 # skip cr2
restore
add rsp,8 # skip rsp in callee
pop rbx
pop rbp
pop r12
pop r13
pop r14
pop r15
add rsp, 16 # skip TrapFrame.err and id
iretq
@ -104,4 +124,3 @@ trap_switch_to_user_space: # (cpu_context: *CpuContext,reg: *TrapFrame)
jmp __trap_return

View File

@ -114,9 +114,9 @@ impl<'a> UserMode<'a> {
self.user_space.vm_space().activate();
}
if !self.executed {
self.current.syscall_frame().caller.rcx = self.user_space.cpu_ctx.gp_regs.rip as usize;
self.current.syscall_frame().callee.rsp = self.user_space.cpu_ctx.gp_regs.rsp as usize;
self.current.syscall_frame().caller.rax = self.user_space.cpu_ctx.gp_regs.rax as usize;
self.current.syscall_frame().caller.rcx = self.user_space.cpu_ctx.gp_regs.rip;
self.current.syscall_frame().callee.rsp = self.user_space.cpu_ctx.gp_regs.rsp;
self.current.syscall_frame().caller.rax = self.user_space.cpu_ctx.gp_regs.rax;
self.executed = true;
} else {
if self.current.inner_exclusive_access().is_from_trap {
@ -144,6 +144,8 @@ impl<'a> UserMode<'a> {
self.context = CpuContext::from(*self.current.syscall_frame());
debug!("[kernel] syscall id:{}", self.context.gp_regs.rax);
debug!("[kernel] rsp: 0x{:x}", self.context.gp_regs.rsp);
debug!("[kernel] rcx: 0x{:x}", self.context.gp_regs.rcx);
debug!("[kernel] rip: 0x{:x}", self.context.gp_regs.rip);
UserEvent::Syscall
}
}