|
@@ -5,7 +5,8 @@ use std::{
|
|
fs::{copy, create_dir_all, metadata, File},
|
|
fs::{copy, create_dir_all, metadata, File},
|
|
io::{BufRead as _, BufReader, ErrorKind, Write as _},
|
|
io::{BufRead as _, BufReader, ErrorKind, Write as _},
|
|
path::{Path, PathBuf},
|
|
path::{Path, PathBuf},
|
|
- process::{Child, Command, Output, Stdio},
|
|
|
|
|
|
+ process::{Child, ChildStdin, Command, Output, Stdio},
|
|
|
|
+ sync::{Arc, Mutex},
|
|
thread,
|
|
thread,
|
|
};
|
|
};
|
|
|
|
|
|
@@ -392,7 +393,7 @@ pub fn run(opts: Options) -> Result<()> {
|
|
qemu.args(["-cpu", cpu]);
|
|
qemu.args(["-cpu", cpu]);
|
|
}
|
|
}
|
|
let console = OsString::from("ttyS0");
|
|
let console = OsString::from("ttyS0");
|
|
- let kernel_args = std::iter::once(("console", &console))
|
|
|
|
|
|
+ let mut kernel_args = std::iter::once(("console", &console))
|
|
.chain(run_args.clone().map(|run_arg| ("init.arg", run_arg)))
|
|
.chain(run_args.clone().map(|run_arg| ("init.arg", run_arg)))
|
|
.enumerate()
|
|
.enumerate()
|
|
.fold(OsString::new(), |mut acc, (i, (k, v))| {
|
|
.fold(OsString::new(), |mut acc, (i, (k, v))| {
|
|
@@ -404,6 +405,12 @@ pub fn run(opts: Options) -> Result<()> {
|
|
acc.push(v);
|
|
acc.push(v);
|
|
acc
|
|
acc
|
|
});
|
|
});
|
|
|
|
+ // We sometimes see kernel panics containing:
|
|
|
|
+ //
|
|
|
|
+ // [ 0.064000] Kernel panic - not syncing: IO-APIC + timer doesn't work! Boot with apic=debug and send a report. Then try booting with the 'noapic' option.
|
|
|
|
+ //
|
|
|
|
+ // Heed the advice and boot with noapic. We don't know why this happens.
|
|
|
|
+ kernel_args.push(" noapic");
|
|
qemu.args(["-no-reboot", "-nographic", "-m", "512M", "-smp", "2"])
|
|
qemu.args(["-no-reboot", "-nographic", "-m", "512M", "-smp", "2"])
|
|
.arg("-append")
|
|
.arg("-append")
|
|
.arg(kernel_args)
|
|
.arg(kernel_args)
|
|
@@ -462,32 +469,49 @@ pub fn run(opts: Options) -> Result<()> {
|
|
stderr,
|
|
stderr,
|
|
..
|
|
..
|
|
} = &mut qemu_child;
|
|
} = &mut qemu_child;
|
|
- let mut stdin = stdin.take().unwrap();
|
|
|
|
|
|
+ let stdin = stdin.take().unwrap();
|
|
|
|
+ let stdin = Arc::new(Mutex::new(stdin));
|
|
let stdout = stdout.take().unwrap();
|
|
let stdout = stdout.take().unwrap();
|
|
let stdout = BufReader::new(stdout);
|
|
let stdout = BufReader::new(stdout);
|
|
let stderr = stderr.take().unwrap();
|
|
let stderr = stderr.take().unwrap();
|
|
let stderr = BufReader::new(stderr);
|
|
let stderr = BufReader::new(stderr);
|
|
|
|
|
|
- let stderr = thread::Builder::new()
|
|
|
|
- .spawn(move || {
|
|
|
|
- for line in stderr.lines() {
|
|
|
|
- let line = line.context("failed to read line from stderr")?;
|
|
|
|
- eprintln!("{}", line);
|
|
|
|
- // Try to get QEMU to exit on kernel panic; otherwise it might hang indefinitely.
|
|
|
|
- if line.contains("end Kernel panic") {
|
|
|
|
- stdin
|
|
|
|
- .write_all(&[0x01, b'x'])
|
|
|
|
- .context("failed to write to stdin")?;
|
|
|
|
|
|
+ fn terminate_if_contains_kernel_panic(
|
|
|
|
+ line: &str,
|
|
|
|
+ stdin: &Arc<Mutex<ChildStdin>>,
|
|
|
|
+ ) -> anyhow::Result<()> {
|
|
|
|
+ if line.contains("end Kernel panic") {
|
|
|
|
+ println!("kernel panic detected; terminating QEMU");
|
|
|
|
+ let mut stdin = stdin.lock().unwrap();
|
|
|
|
+ stdin
|
|
|
|
+ .write_all(&[0x01, b'x'])
|
|
|
|
+ .context("failed to write to stdin")?;
|
|
|
|
+ println!("waiting for QEMU to terminate");
|
|
|
|
+ }
|
|
|
|
+ Ok(())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let stderr = {
|
|
|
|
+ let stdin = stdin.clone();
|
|
|
|
+ thread::Builder::new()
|
|
|
|
+ .spawn(move || {
|
|
|
|
+ for line in stderr.lines() {
|
|
|
|
+ let line = line.context("failed to read line from stderr")?;
|
|
|
|
+ eprintln!("{}", line);
|
|
|
|
+ // Try to get QEMU to exit on kernel panic; otherwise it might hang indefinitely.
|
|
|
|
+ terminate_if_contains_kernel_panic(&line, &stdin)?;
|
|
}
|
|
}
|
|
- }
|
|
|
|
- anyhow::Ok(())
|
|
|
|
- })
|
|
|
|
- .unwrap();
|
|
|
|
|
|
+ anyhow::Ok(())
|
|
|
|
+ })
|
|
|
|
+ .unwrap()
|
|
|
|
+ };
|
|
|
|
|
|
let mut outcome = None;
|
|
let mut outcome = None;
|
|
for line in stdout.lines() {
|
|
for line in stdout.lines() {
|
|
let line = line.context("failed to read line from stdout")?;
|
|
let line = line.context("failed to read line from stdout")?;
|
|
println!("{}", line);
|
|
println!("{}", line);
|
|
|
|
+ // Try to get QEMU to exit on kernel panic; otherwise it might hang indefinitely.
|
|
|
|
+ terminate_if_contains_kernel_panic(&line, &stdin)?;
|
|
// The init program will print "init: success" or "init: failure" to indicate
|
|
// The init program will print "init: success" or "init: failure" to indicate
|
|
// the outcome of running the binaries it found in /bin.
|
|
// the outcome of running the binaries it found in /bin.
|
|
if let Some(line) = line.strip_prefix("init: ") {
|
|
if let Some(line) = line.strip_prefix("init: ") {
|