// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::stack::StackSegment; use libc::c_void; use cast::{transmute, transmute_mut_unsafe, transmute_region, transmute_mut_region}; // XXX: Registers is boxed so that it is 16-byte aligned, for storing // SSE regs. It would be marginally better not to do this. In C++ we // use an attribute on a struct. pub struct Context(~Registers); pub impl Context { static fn empty() -> Context { Context(new_regs()) } /// Create a new context that will resume execution by running ~fn() /// # Safety Note /// The `start` closure must remain valid for the life of the Task static fn new(start: &~fn(), stack: &mut StackSegment) -> Context { // The C-ABI function that is the task entry point extern fn task_start_wrapper(f: &~fn()) { (*f)() } let fp: *c_void = task_start_wrapper as *c_void; let argp: *c_void = unsafe { transmute::<&~fn(), *c_void>(&*start) }; let sp: *uint = stack.end(); let sp: *mut uint = unsafe { transmute_mut_unsafe(sp) }; // Save and then immediately load the current context, // which we will then modify to call the given function when restored let mut regs = new_regs(); unsafe { swap_registers(transmute_mut_region(&mut *regs), transmute_region(&*regs)) }; initialize_call_frame(&mut *regs, fp, argp, sp); return Context(regs); } static fn swap(out_context: &mut Context, in_context: &Context) { let out_regs: &mut Registers = match out_context { &Context(~ref mut r) => r }; let in_regs: &Registers = match in_context { &Context(~ref r) => r }; unsafe { swap_registers(out_regs, in_regs) }; } } extern { fn swap_registers(out_regs: *mut Registers, in_regs: *Registers); } // Definitions of these registers are in rt/arch/x86_64/regs.h #[cfg(target_arch = "x86_64")] type Registers = [uint * 22]; #[cfg(target_arch = "x86_64")] fn new_regs() -> ~Registers { ~[0, .. 22] } #[cfg(target_arch = "x86_64")] fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { // Redefinitions from regs.h const RUSTRT_ARG0: uint = 3; const RUSTRT_RSP: uint = 1; const RUSTRT_IP: uint = 8; const RUSTRT_RBP: uint = 2; let sp = align_down(sp); let sp = mut_offset(sp, -1); // The final return address. 0 indicates the bottom of the stack unsafe { *sp = 0; } rtdebug!("creating call frame"); rtdebug!("fptr %x", fptr as uint); rtdebug!("arg %x", arg as uint); rtdebug!("sp %x", sp as uint); regs[RUSTRT_ARG0] = arg as uint; regs[RUSTRT_RSP] = sp as uint; regs[RUSTRT_IP] = fptr as uint; // Last base pointer on the stack should be 0 regs[RUSTRT_RBP] = 0; } #[cfg(target_arch = "x86")] struct Registers { eax: u32, ebx: u32, ecx: u32, edx: u32, ebp: u32, esi: u32, edi: u32, esp: u32, cs: u16, ds: u16, ss: u16, es: u16, fs: u16, gs: u16, eflags: u32, eip: u32 } #[cfg(target_arch = "x86")] fn new_regs() -> ~Registers { ~Registers { eax: 0, ebx: 0, ecx: 0, edx: 0, ebp: 0, esi: 0, edi: 0, esp: 0, cs: 0, ds: 0, ss: 0, es: 0, fs: 0, gs: 0, eflags: 0, eip: 0 } } #[cfg(target_arch = "x86")] fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: *mut uint) { let sp = align_down(sp); let sp = mut_offset(sp, -4); // XXX: -4 words? Needs this be done at all? unsafe { *sp = arg as uint; } let sp = mut_offset(sp, -1); unsafe { *sp = 0; } // The final return address regs.esp = sp as u32; regs.eip = fptr as u32; // Last base pointer on the stack is 0 regs.ebp = 0; } fn align_down(sp: *mut uint) -> *mut uint { unsafe { let sp = transmute::<*mut uint, uint>(sp); let sp = sp & !(16 - 1); transmute::(sp) } } // XXX: ptr::offset is positive ints only #[inline(always)] pub pure fn mut_offset(ptr: *mut T, count: int) -> *mut T { use core::sys::size_of; unsafe { (ptr as int + count * (size_of::() as int)) as *mut T } }