1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
//! A module for process management

use alloc::boxed::Box;

use core::fmt::{Debug, Formatter, Result};
use core::sync::atomic::{AtomicUsize, Ordering};

use fs::ROOT_FS;
use interrupts::{esp0, on, off};
use io::NonBlockingBuffer;
use machine::{self, context_switch};
use memory::AddressSpace;
use static_linked_list::StaticLinkedList;

use self::context::KContext;
use self::idle::IDLE_PROCESS;
use self::proc_table::PROCESS_TABLE;

pub mod context;
pub mod focus;
pub mod proc_table;
pub mod ready_queue;

mod idle;
mod init;
mod reaper;
mod user;

/// Size of a kernel stack (number of words)
const STACK_SIZE: usize = 2048;

/// The next available PID
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);

/// The current running process if there is one
pub static mut CURRENT_PROCESS: *mut Process = 0 as *mut Process;

/// Type alias for a queue of Processes
pub type ProcessQueue = StaticLinkedList<*mut Process>;

/// An enum representing change of directory
#[derive(Clone, Copy, PartialEq)]
pub enum CF {
    /// ".."
    Back,
    /// Next file
    Next(usize),
}

/// An enum representing the possible states of a process
#[repr(C)]
#[derive(Clone, Copy, PartialEq)]
pub enum State {
    /// Process is created, but not ready
    INIT,
    /// Process is in the ready q
    READY,
    /// Process is running (not on the ready q or blocked)
    RUNNING,
    /// Process is blocked on a q
    BLOCKED,
    /// Process has died
    TERMINATED,
}

/// Represents a single process, its identity, resources, etc.
pub struct Process {
    /// Name of the process; not necessarily unique
    name: &'static str,

    /// Unique 32-bit identifier
    pid: usize,

    /// The routine of the process
    run: fn(&Process) -> usize,

    /// The current state of the process
    state: State,

    /// A pointer to the kheap-allocated stack space for this process's
    /// kernel stack. This pointer is to the bottom of the stack, not the head.
    stack: usize,

    /// The saved kernel context for context switching
    kcontext: KContext,

    /// The virtual memory address space of the process
    pub addr_space: AddressSpace,

    /// Number of calls to interrupts::on() while this process was running
    /// Interrupts are on if `disable_cnt == 0`
    pub disable_cnt: usize,

    /// A keyboard input buffer
    pub buffer: Option<NonBlockingBuffer>,

    /// Current working file (inode number)
    pub cwf: usize,

    /// Path taken to the cwf (a stack of inode #s), not including the cwf
    pub path: StaticLinkedList<usize>,
}

impl Process {
    /// Create a new process with the given name and routine. Because processes
    /// are a fundamental abstraction, they are too low level for me to use Rust well.
    /// For this reason, a raw pointer is returned to the process, and it is the
    /// job of the caller to arrange for the process to be reaped.
    pub fn new(name: &'static str, run: fn(&Process) -> usize) -> *mut Process {
        let mut p = Process {
            name: name,
            pid: NEXT_ID.fetch_add(1, Ordering::Relaxed),
            run: run,
            state: State::INIT,
            stack: 0,
            kcontext: KContext::new(),
            addr_space: AddressSpace::new(),
            disable_cnt: 0,
            buffer: None,
            cwf: 0,
            path: StaticLinkedList::new(),
        };

        p.get_stack();

        let process = Box::into_raw(box p);

        unsafe {
            PROCESS_TABLE.push(process);
        }

        process
    }

    /// A helper to get a kernel stack for this process
    fn get_stack(&mut self) {
        // TODO: fudge
        // Allocate a stack
        let stack: Box<[usize; STACK_SIZE]> = box [0; STACK_SIZE];

        let stack_ptr = Box::into_raw(stack) as *mut usize;

        // set the pointer
        self.stack = stack_ptr as usize;

        // smash the stack
        unsafe {
            // put RA on stack to return to start_proc
            *stack_ptr.offset(STACK_SIZE as isize - 2) = start_proc as usize;
        }

        self.kcontext.esp = unsafe { stack_ptr.offset(STACK_SIZE as isize - 2) } as usize;

        // printf!("stack for {:?} is at 0x{:x}\n", self, self.stack);
    }

    /// Set the state of the process to `s`
    fn set_state(&mut self, s: State) {
        self.state = s;
    }

    /// Get the state of the process
    pub fn get_state(&self) -> State {
        self.state
    }

    /// Get the PID of this process
    pub fn get_pid(&self) -> usize {
        self.pid
    }

    /// Start accepting keyboard input when this process
    /// gains focussed. The buffer will have the capacity
    /// given.
    pub fn accept_kbd(&mut self, cap: usize) {
        if self.buffer.is_none() {
            self.buffer = Some(NonBlockingBuffer::new(cap));
        }
    }

    /// Return the inode number of the current working file
    pub fn cwf(&self) -> usize {
        self.cwf
    }

    /// Change files to the given inode number if that file exists and is reachable from the cwf
    pub fn cf(&mut self, new_cwf: CF) {
        match new_cwf {
            CF::Back => {
                if self.path.len() > 0 {
                    self.cwf = self.path.pop_back().unwrap();
                }
            }
            CF::Next(new) => {
                // check if new exists
                match unsafe { (*ROOT_FS).stat(new) } {
                    Some(inode) => {
                        // check if they are linked
                        let cwf_inode = unsafe { (*ROOT_FS).stat(self.cwf()).unwrap() };
                        let mut linked = false;

                        for i in inode.links.iter() {
                            if *i == self.cwf() {
                                linked = true;
                                break;
                            }
                        }

                        // if linked actually change paths
                        if linked {
                            self.path.push_back(new);
                            self.cwf = new;
                        }
                    }
                    None => {}
                }
            }
        }
    }
}

impl Drop for Process {
    /// When the process is reaped, we must free its stack
    fn drop(&mut self) {
        unsafe {
            Box::from_raw(self.stack as *mut [usize; 2048]);
        }
        // let the box go out of scope to dealloc
    }
}

impl Debug for Process {
    /// Allow processes to be printed elegantly in format strings
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "Process #{} @ 0x{:x}: {}",
               self.pid, self as *const Process as usize, self.name)
    }
}

impl PartialEq for Process {
    /// Two processes are the same if they have the same PID
    fn eq(&self, other: &Process) -> bool {
        self.pid == other.pid
    }
}

/// Initialize the process subsystem.
/// This creates the init, idle, and reaper processes, but does not
/// start any of them yet.
pub fn init() {
    // Add the init process to the ready q
    ready_queue::make_ready(Process::new("init", self::init::run));

    // Create the idle process
    idle::init();

    // Create the reaper process
    reaper::init();

    printf!("processes inited\n");
}

/// The entry point of all processes
#[allow(private_no_mangle_fns)]
#[no_mangle]
fn start_proc() {
    // TODO check killed

    // run current process
    unsafe {
        let code = if !CURRENT_PROCESS.is_null() {
            printf!("{:?} [Starting]\n", *CURRENT_PROCESS);
            (*CURRENT_PROCESS).set_state(State::RUNNING);
            ((*CURRENT_PROCESS).run)(&*CURRENT_PROCESS)
        } else {
            panic!("No current process to start")
        };

        exit(code);
    }
}

/// A safe wrapper around machine::proc_yield.
///
/// This function is called when the current process wants to
/// yield the rest of its quantum. The process can specify a
/// `ProcessQueue` to yield onto, or if None is specified, it
/// yields onto the ready queue.
pub fn proc_yield<'a>(q: Option<&'a mut ProcessQueue>) {
    unsafe {
        off();
        machine::proc_yield(q);
        on();
    }
}

/// The unsafe function that does the actual work of choosing the
/// next process and switching to it.
///
/// NOTE: Processes should not call this function directly; they
/// should use `process::proc_yield` instead.
/// NOTE: Interrupts should already be disabled here
#[no_mangle]
#[inline(never)]
pub unsafe fn _proc_yield<'a>(q: Option<&'a mut ProcessQueue>) {
    // yield onto the right queue
    if !CURRENT_PROCESS.is_null() {
        if let Some(queue) = q {
            (*CURRENT_PROCESS).set_state(State::BLOCKED);
            //bootlog!("{:?} [Blocking]\n", *CURRENT_PROCESS);
            queue.push_back(CURRENT_PROCESS);
        } else {
            ready_queue::make_ready(CURRENT_PROCESS);
        }

        CURRENT_PROCESS = 0 as *mut Process;
    }

    // get next process from ready q
    let mut next = ready_queue::get_next();

    // run the idle process when everyone is done
    if next.is_null() {
        next = IDLE_PROCESS;
    }

    // switch address spaces
    (*next).addr_space.activate();

    // switch stacks
    esp0((*next).stack + STACK_SIZE*4);

    // set the CURRENT_PROCESS
    CURRENT_PROCESS = next;

    //bootlog!("{:?} [Switching]\n", *CURRENT_PROCESS);

    // context switch
    context_switch((*CURRENT_PROCESS).kcontext,
                   if (*CURRENT_PROCESS).disable_cnt == 0 {
                       1 << 9
                   } else {
                       0
                   });

    panic!("The impossible has happened!");
}

/// Called by the current process to exit with the given exit code
pub fn exit(code: usize) {
    unsafe {
        if CURRENT_PROCESS.is_null() {
            panic!("Exiting with no current process!\n");
        }

        (*CURRENT_PROCESS).set_state(State::TERMINATED);

        // clean up address space
        (*CURRENT_PROCESS).addr_space.clear();

        // Disable interrupts
        off();

        // NOTE: need to print *before* adding to reaper q
        bootlog!("{:?} [Exit 0x{:X}]\n", *CURRENT_PROCESS, code);

        self::reaper::reaper_add(CURRENT_PROCESS);

        // set current to None, so we will never run this again
        CURRENT_PROCESS = 0 as *mut Process;

        // switch to next ready process
        _proc_yield(None);
    }

    panic!("The impossible has happened!");
}