Fix multiple issues in the initproc path

This commit is contained in:
Zhang Junyang
2023-08-03 11:05:06 +08:00
committed by Tate, Hongliang Tian
parent 4a33020b4f
commit acfbc7efdc
6 changed files with 53 additions and 91 deletions

View File

@ -18,4 +18,4 @@ jobs:
- name: Syscall Test - name: Syscall Test
id: syscall_test id: syscall_test
run: make syscall_test ENABLE_KVM=false run: make run AUTO_SYSCALL_TEST=1 ENABLE_KVM=0

View File

@ -1,10 +1,10 @@
//! jinux-build is the Jinux runner script to ease the pain of running //! jinux-build is the Jinux runner script to ease the pain of running
//! and testing Jinux inside a QEMU VM, which should be called as the //! and testing Jinux inside a QEMU VM, which should be called as the
//! cargo runner: https://doc.rust-lang.org/cargo/reference/config.html //! cargo runner: https://doc.rust-lang.org/cargo/reference/config.html
//! //!
//! The runner generates the the filesystem image and the containing //! The runner generates the the filesystem image and the containing
//! boot device image. Then it invokes QEMU to boot Jinux. //! boot device image. Then it invokes QEMU to boot Jinux.
//! //!
use std::{ use std::{
fs::{self, OpenOptions}, fs::{self, OpenOptions},
@ -13,14 +13,13 @@ use std::{
process::Command, process::Command,
}; };
use clap::{Parser, builder::Str}; use clap::{builder::Str, Parser};
/// The CLI of this runner. /// The CLI of this runner.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {
// Positional arguments. // Positional arguments.
/// The Jinux binary path. /// The Jinux binary path.
path: PathBuf, path: PathBuf,
@ -29,7 +28,6 @@ struct Args {
kcmdline: String, kcmdline: String,
// Optional arguments. // Optional arguments.
/// Enable KVM when running QEMU. /// Enable KVM when running QEMU.
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
enable_kvm: bool, enable_kvm: bool,

View File

@ -5,5 +5,5 @@
//! on its way. //! on its way.
//! //!
pub mod multiboot2; mod multiboot2;
pub use self::multiboot2::init_boot_args; pub use self::multiboot2::init_boot_args;

View File

@ -13,12 +13,9 @@ use alloc::{
vec, vec,
vec::Vec, vec::Vec,
}; };
use log::debug;
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
struct InitprocArg { struct InitprocArgs {
// Since environment arguments can precede the init path argument, we
// have no choice but to wrap the path in `Option` and check it later.
path: Option<String>, path: Option<String>,
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
@ -27,7 +24,7 @@ struct InitprocArg {
/// The struct to store the parsed kernel command-line arguments. /// The struct to store the parsed kernel command-line arguments.
#[derive(Debug)] #[derive(Debug)]
pub struct KCmdlineArg { pub struct KCmdlineArg {
initproc: Option<InitprocArg>, initproc: InitprocArgs,
module_args: BTreeMap<String, Vec<CString>>, module_args: BTreeMap<String, Vec<CString>>,
} }
@ -35,18 +32,15 @@ pub struct KCmdlineArg {
impl KCmdlineArg { impl KCmdlineArg {
/// Get the path of the initprocess. /// Get the path of the initprocess.
pub fn get_initproc_path(&self) -> Option<&str> { pub fn get_initproc_path(&self) -> Option<&str> {
self.initproc self.initproc.path.as_ref().map(|s| s.as_str())
.as_ref()
.and_then(|i| i.path.as_ref())
.map(|s| s.as_str())
} }
/// Get the argument vector(argv) of the initprocess. /// Get the argument vector(argv) of the initprocess.
pub fn get_initproc_argv(&self) -> Option<&Vec<CString>> { pub fn get_initproc_argv(&self) -> &Vec<CString> {
self.initproc.as_ref().map(|i| &i.argv) &self.initproc.argv
} }
/// Get the environment vector(envp) of the initprocess. /// Get the environment vector(envp) of the initprocess.
pub fn get_initproc_envp(&self) -> Option<&Vec<CString>> { pub fn get_initproc_envp(&self) -> &Vec<CString> {
self.initproc.as_ref().map(|i| &i.argv) &self.initproc.envp
} }
/// Get the argument vector of a kernel module. /// Get the argument vector of a kernel module.
pub fn get_module_args(&self, module: &str) -> Option<&Vec<CString>> { pub fn get_module_args(&self, module: &str) -> Option<&Vec<CString>> {
@ -72,8 +66,12 @@ fn split_arg(input: &str) -> impl Iterator<Item = &str> {
impl From<&str> for KCmdlineArg { impl From<&str> for KCmdlineArg {
fn from(cmdline: &str) -> Self { fn from(cmdline: &str) -> Self {
// What we construct. // What we construct.
let mut result = KCmdlineArg { let mut result: KCmdlineArg = KCmdlineArg {
initproc: None, initproc: InitprocArgs {
path: None,
argv: Vec::new(),
envp: Vec::new(),
},
module_args: BTreeMap::new(), module_args: BTreeMap::new(),
}; };
@ -87,11 +85,10 @@ impl From<&str> for KCmdlineArg {
// KernelArg => Arg "\s+" KernelArg | %empty // KernelArg => Arg "\s+" KernelArg | %empty
// InitArg => Arg "\s+" InitArg | %empty // InitArg => Arg "\s+" InitArg | %empty
if kcmdline_end { if kcmdline_end {
if let Some(&mut ref mut i) = result.initproc.as_mut() { if result.initproc.path == None {
i.argv.push(CString::new(arg).unwrap());
} else {
panic!("Initproc arguments provided but no initproc path specified!"); panic!("Initproc arguments provided but no initproc path specified!");
} }
result.initproc.argv.push(CString::new(arg).unwrap());
continue; continue;
} }
if arg == "--" { if arg == "--" {
@ -134,32 +131,16 @@ impl From<&str> for KCmdlineArg {
// The option has a value. // The option has a value.
match option { match option {
"init" => { "init" => {
if let Some(&mut ref mut i) = result.initproc.as_mut() { if let Some(v) = &result.initproc.path {
if let Some(v) = &i.path { panic!("Initproc assigned twice in the command line!");
panic!("Initproc assigned twice in the command line!");
}
i.path = Some(value.to_string());
} else {
result.initproc = Some(InitprocArg {
path: Some(value.to_string()),
argv: Vec::new(),
envp: Vec::new(),
});
} }
result.initproc.path = Some(value.to_string());
} }
_ => { _ => {
// If the option is not recognized, it is passed to the initproc. // If the option is not recognized, it is passed to the initproc.
// Pattern 'option=value' is treated as the init environment. // Pattern 'option=value' is treated as the init environment.
let envp_entry = CString::new(option.to_string() + "=" + value).unwrap(); let envp_entry = CString::new(option.to_string() + "=" + value).unwrap();
if let Some(&mut ref mut i) = result.initproc.as_mut() { result.initproc.envp.push(envp_entry);
i.envp.push(envp_entry);
} else {
result.initproc = Some(InitprocArg {
path: None,
argv: Vec::new(),
envp: vec![envp_entry],
});
}
} }
} }
} else { } else {
@ -169,28 +150,12 @@ impl From<&str> for KCmdlineArg {
// If the option is not recognized, it is passed to the initproc. // If the option is not recognized, it is passed to the initproc.
// Pattern 'option' without value is treated as the init argument. // Pattern 'option' without value is treated as the init argument.
let argv_entry = CString::new(option.to_string()).unwrap(); let argv_entry = CString::new(option.to_string()).unwrap();
if let Some(&mut ref mut i) = result.initproc.as_mut() { result.initproc.argv.push(argv_entry);
i.argv.push(argv_entry);
} else {
result.initproc = Some(InitprocArg {
path: None,
argv: vec![argv_entry],
envp: Vec::new(),
});
}
} }
} }
} }
} }
debug!("{:?}", result);
if let Some(&ref i) = result.initproc.as_ref() {
if i.path == None {
panic!("Initproc arguments provided but no initproc! Maybe have bad option.");
}
}
result result
} }
} }

View File

@ -2,8 +2,6 @@
//! from the bootloader to the rest of the framework. //! from the bootloader to the rest of the framework.
//! //!
use crate::arch::boot::init_boot_args;
pub mod kcmdline; pub mod kcmdline;
use kcmdline::KCmdlineArg; use kcmdline::KCmdlineArg;
@ -13,6 +11,8 @@ use self::memory_region::MemoryRegion;
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use spin::Once; use spin::Once;
/// ACPI information from the bootloader.
///
/// The boot crate can choose either providing the raw RSDP physical address or /// The boot crate can choose either providing the raw RSDP physical address or
/// providing the RSDT/XSDT physical address after parsing RSDP. /// providing the RSDT/XSDT physical address after parsing RSDP.
/// This is because bootloaders differ in such behaviors. /// This is because bootloaders differ in such behaviors.
@ -36,40 +36,39 @@ pub struct BootloaderFramebufferArg {
pub bpp: usize, pub bpp: usize,
} }
// Use a macro to simplify coding.
macro_rules! define_global_static_boot_arguments { macro_rules! define_global_static_boot_arguments {
( $( $lower:ident, $upper:ident, $typ:ty; )* ) => { ( $( $lower:ident, $upper:ident, $typ:ty; )* ) => {
// Define statics and corresponding public get APIs. // Define statics and corresponding public getter APIs.
$( $(
static $upper: Once<$typ> = Once::new(); static $upper: Once<$typ> = Once::new();
/// Macro generated public get API. /// Macro generated public getter API.
pub fn $lower() -> &'static $typ { pub fn $lower() -> &'static $typ {
$upper.get().unwrap() $upper.get().unwrap()
} }
)* )*
// Produce a init function call. The init function must // Produce a init function call. The init function must
// be defined in the `arch::boot` module conforming to this // be defined in the `arch::boot` module conforming to this
// definition. // definition.
fn arch_init_boot_args() { fn arch_init_boot_args() {
init_boot_args( $( &$upper, )* ); crate::arch::boot::init_boot_args( $( &$upper, )* );
} }
}; };
} }
// Define a series of static variable definitions and its APIs. The names in // Define a series of static variables and their getter APIs.
// each line are:
// 1. The lowercase name of the variable, also the name of the get API;
// 2. the uppercase name of the variable;
// 3. the type of the variable.
define_global_static_boot_arguments!( define_global_static_boot_arguments!(
bootloader_name, BOOTLOADER_NAME, String; // Getter Names | Static Variables | Variable Types
kernel_cmdline, KERNEL_CMDLINE, KCmdlineArg; bootloader_name, BOOTLOADER_NAME, String;
initramfs, INITRAMFS, &'static [u8]; kernel_cmdline, KERNEL_CMDLINE, KCmdlineArg;
acpi_arg, ACPI_ARG, BootloaderAcpiArg; initramfs, INITRAMFS, &'static [u8];
framebuffer_arg, FRAMEBUFFER_ARG, BootloaderFramebufferArg; acpi_arg, ACPI_ARG, BootloaderAcpiArg;
memory_regions, MEMORY_REGIONS, Vec<MemoryRegion>; framebuffer_arg, FRAMEBUFFER_ARG, BootloaderFramebufferArg;
memory_regions, MEMORY_REGIONS, Vec<MemoryRegion>;
); );
/// The initialization method of the boot module.
///
/// After initializing the boot module, the get functions could be called. /// After initializing the boot module, the get functions could be called.
/// The initialization must be done after the heap is set and before physical /// The initialization must be done after the heap is set and before physical
/// mappings are cancelled. /// mappings are cancelled.

View File

@ -81,25 +81,25 @@ fn init_thread() {
let initproc = Process::spawn_user_process( let initproc = Process::spawn_user_process(
karg.get_initproc_path().unwrap(), karg.get_initproc_path().unwrap(),
karg.get_initproc_argv().unwrap().to_vec(), karg.get_initproc_argv().to_vec(),
karg.get_initproc_envp().unwrap().to_vec(), karg.get_initproc_envp().to_vec(),
) )
.expect("Run init process failed."); .expect("Run init process failed.");
loop { // Wait till initproc become zombie.
// If initproc becomes zombie, then exit qemu. while *initproc.status().lock() != ProcessStatus::Zombie {
if *initproc.status().lock() == ProcessStatus::Zombie {
let exit_code = if initproc.exit_code().load(Ordering::Relaxed) == 0 {
QemuExitCode::Success
} else {
QemuExitCode::Failed
};
exit_qemu(exit_code);
}
// We don't have preemptive scheduler now. // We don't have preemptive scheduler now.
// The long running init thread should yield its own execution to allow other tasks to go on. // The long running init thread should yield its own execution to allow other tasks to go on.
Thread::yield_now(); Thread::yield_now();
} }
// TODO: exit via qemu isa debug device should not be the only way.
let exit_code = if initproc.exit_code().load(Ordering::Relaxed) == 0 {
QemuExitCode::Success
} else {
QemuExitCode::Failed
};
exit_qemu(exit_code);
} }
/// first process never return /// first process never return