<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Maciej Dziardziel <fiedzia@gmail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

// spell-checker:ignore (vars/api) fcntl setrlimit setitimer
// spell-checker:ignore (vars/signals) ABRT ALRM CHLD SEGV SIGABRT SIGALRM SIGBUS SIGCHLD SIGCONT SIGEMT SIGFPE SIGHUP SIGILL SIGINFO SIGINT SIGIO SIGIOT SIGKILL SIGPIPE SIGPROF SIGQUIT SIGSEGV SIGSTOP SIGSYS SIGTERM SIGTRAP SIGTSTP SIGTTIN SIGTTOU SIGURG SIGUSR SIGVTALRM SIGWINCH SIGXCPU SIGXFSZ STKFLT TSTP TTIN TTOU VTALRM XCPU XFSZ

pub static DEFAULT_SIGNAL: usize = 15;

/*

Linux Programmer's Manual

 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH
29 POLL    30 PWR     31 SYS

*/

#[cfg(any(target_os = "linux", target_os = "android"))]
pub static ALL_SIGNALS: [&str; 32] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE", "KILL", "USR1", "SEGV",
    "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU",
    "URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "POLL", "PWR", "SYS",
];

/*

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html

No    Name         Default Action       Description
1     SIGHUP       terminate process    terminal line hangup
2     SIGINT       terminate process    interrupt program
3     SIGQUIT      create core image    quit program
4     SIGILL       create core image    illegal instruction
5     SIGTRAP      create core image    trace trap
6     SIGABRT      create core image    abort program (formerly SIGIOT)
7     SIGEMT       create core image    emulate instruction executed
8     SIGFPE       create core image    floating-point exception
9     SIGKILL      terminate process    kill program
10    SIGBUS       create core image    bus error
11    SIGSEGV      create core image    segmentation violation
12    SIGSYS       create core image    non-existent system call invoked
13    SIGPIPE      terminate process    write on a pipe with no reader
14    SIGALRM      terminate process    real-time timer expired
15    SIGTERM      terminate process    software termination signal
16    SIGURG       discard signal       urgent condition present on socket
17    SIGSTOP      stop process         stop (cannot be caught or ignored)
18    SIGTSTP      stop process         stop signal generated from keyboard
19    SIGCONT      discard signal       continue after stop
20    SIGCHLD      discard signal       child status has changed
21    SIGTTIN      stop process         background read attempted from control terminal
22    SIGTTOU      stop process         background write attempted to control terminal
23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
28    SIGWINCH     discard signal       Window size change
29    SIGINFO      discard signal       status request from keyboard
30    SIGUSR1      terminate process    User defined signal 1
31    SIGUSR2      terminate process    User defined signal 2

*/

#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
pub static ALL_SIGNALS: [&str; 32] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", "SEGV",
    "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
    "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2",
];

/*

     The following signals are defined in NetBSD:

     SIGHUP           1     Hangup
     SIGINT           2     Interrupt
     SIGQUIT          3     Quit
     SIGILL           4     Illegal instruction
     SIGTRAP          5     Trace/BPT trap
     SIGABRT          6     Abort trap
     SIGEMT           7     EMT trap
     SIGFPE           8     Floating point exception
     SIGKILL          9     Killed
     SIGBUS           10    Bus error
     SIGSEGV          11    Segmentation fault
     SIGSYS           12    Bad system call
     SIGPIPE          13    Broken pipe
     SIGALRM          14    Alarm clock
     SIGTERM          15    Terminated
     SIGURG           16    Urgent I/O condition
     SIGSTOP          17    Suspended (signal)
     SIGTSTP          18    Suspended
     SIGCONT          19    Continued
     SIGCHLD          20    Child exited, stopped or continued
     SIGTTIN          21    Stopped (tty input)
     SIGTTOU          22    Stopped (tty output)
     SIGIO            23    I/O possible
     SIGXCPU          24    CPU time limit exceeded
     SIGXFSZ          25    File size limit exceeded
     SIGVTALRM        26    Virtual timer expired
     SIGPROF          27    Profiling timer expired
     SIGWINCH         28    Window size changed
     SIGINFO          29    Information request
     SIGUSR1          30    User defined signal 1
     SIGUSR2          31    User defined signal 2
     SIGPWR           32    Power fail/restart
*/

#[cfg(target_os = "netbsd")]
pub static ALL_SIGNALS: [&str; 33] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", "SEGV",
    "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
    "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2", "PWR"
];

pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
    if let Ok(value) = signal_name_or_value.parse() {
        if is_signal(value) {
            return Some(value);
        } else {
            return None;
        }
    }
    let signal_name = signal_name_or_value.trim_start_matches("SIG");

    ALL_SIGNALS.iter().position(|&s| s == signal_name)
}

pub fn is_signal(num: usize) -> bool {
    num < ALL_SIGNALS.len()
}

pub fn signal_name_by_value(signal_value: usize) -> Option<&'static str> {
    ALL_SIGNALS.get(signal_value).copied()
}

#[test]
fn signal_by_value() {
    assert_eq!(signal_by_name_or_value("0"), Some(0));
    for (value, _signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal_by_name_or_value(&value.to_string()), Some(value));
    }
}

#[test]
fn signal_by_short_name() {
    for (value, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal_by_name_or_value(signal), Some(value));
    }
}

#[test]
fn signal_by_long_name() {
    for (value, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(
            signal_by_name_or_value(&format!("SIG{}", signal)),
            Some(value)
        );
    }
}

#[test]
fn name() {
    for (value, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal_name_by_value(value), Some(*signal));
    }
}
<commit_msg>while at it, let's do OpenBSD.<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Maciej Dziardziel <fiedzia@gmail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

// spell-checker:ignore (vars/api) fcntl setrlimit setitimer
// spell-checker:ignore (vars/signals) ABRT ALRM CHLD SEGV SIGABRT SIGALRM SIGBUS SIGCHLD SIGCONT SIGEMT SIGFPE SIGHUP SIGILL SIGINFO SIGINT SIGIO SIGIOT SIGKILL SIGPIPE SIGPROF SIGPWR SIGQUIT SIGSEGV SIGSTOP SIGSYS SIGTERM SIGTRAP SIGTSTP SIGTHR SIGTTIN SIGTTOU SIGURG SIGUSR SIGVTALRM SIGWINCH SIGXCPU SIGXFSZ STKFLT PWR THR TSTP TTIN TTOU VTALRM XCPU XFSZ

pub static DEFAULT_SIGNAL: usize = 15;

/*

Linux Programmer's Manual

 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH
29 POLL    30 PWR     31 SYS

*/

#[cfg(any(target_os = "linux", target_os = "android"))]
pub static ALL_SIGNALS: [&str; 32] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE", "KILL", "USR1", "SEGV",
    "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU",
    "URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "POLL", "PWR", "SYS",
];

/*

https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html

No    Name         Default Action       Description
1     SIGHUP       terminate process    terminal line hangup
2     SIGINT       terminate process    interrupt program
3     SIGQUIT      create core image    quit program
4     SIGILL       create core image    illegal instruction
5     SIGTRAP      create core image    trace trap
6     SIGABRT      create core image    abort program (formerly SIGIOT)
7     SIGEMT       create core image    emulate instruction executed
8     SIGFPE       create core image    floating-point exception
9     SIGKILL      terminate process    kill program
10    SIGBUS       create core image    bus error
11    SIGSEGV      create core image    segmentation violation
12    SIGSYS       create core image    non-existent system call invoked
13    SIGPIPE      terminate process    write on a pipe with no reader
14    SIGALRM      terminate process    real-time timer expired
15    SIGTERM      terminate process    software termination signal
16    SIGURG       discard signal       urgent condition present on socket
17    SIGSTOP      stop process         stop (cannot be caught or ignored)
18    SIGTSTP      stop process         stop signal generated from keyboard
19    SIGCONT      discard signal       continue after stop
20    SIGCHLD      discard signal       child status has changed
21    SIGTTIN      stop process         background read attempted from control terminal
22    SIGTTOU      stop process         background write attempted to control terminal
23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
28    SIGWINCH     discard signal       Window size change
29    SIGINFO      discard signal       status request from keyboard
30    SIGUSR1      terminate process    User defined signal 1
31    SIGUSR2      terminate process    User defined signal 2

*/

#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
pub static ALL_SIGNALS: [&str; 32] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", "SEGV",
    "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
    "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2",
];

/*

     The following signals are defined in NetBSD:

     SIGHUP           1     Hangup
     SIGINT           2     Interrupt
     SIGQUIT          3     Quit
     SIGILL           4     Illegal instruction
     SIGTRAP          5     Trace/BPT trap
     SIGABRT          6     Abort trap
     SIGEMT           7     EMT trap
     SIGFPE           8     Floating point exception
     SIGKILL          9     Killed
     SIGBUS           10    Bus error
     SIGSEGV          11    Segmentation fault
     SIGSYS           12    Bad system call
     SIGPIPE          13    Broken pipe
     SIGALRM          14    Alarm clock
     SIGTERM          15    Terminated
     SIGURG           16    Urgent I/O condition
     SIGSTOP          17    Suspended (signal)
     SIGTSTP          18    Suspended
     SIGCONT          19    Continued
     SIGCHLD          20    Child exited, stopped or continued
     SIGTTIN          21    Stopped (tty input)
     SIGTTOU          22    Stopped (tty output)
     SIGIO            23    I/O possible
     SIGXCPU          24    CPU time limit exceeded
     SIGXFSZ          25    File size limit exceeded
     SIGVTALRM        26    Virtual timer expired
     SIGPROF          27    Profiling timer expired
     SIGWINCH         28    Window size changed
     SIGINFO          29    Information request
     SIGUSR1          30    User defined signal 1
     SIGUSR2          31    User defined signal 2
     SIGPWR           32    Power fail/restart
*/

#[cfg(target_os = "netbsd")]
pub static ALL_SIGNALS: [&str; 33] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", "SEGV",
    "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
    "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2", "PWR",
];

/*

     The following signals are defined in OpenBSD:

     SIGHUP       terminate process    terminal line hangup
     SIGINT       terminate process    interrupt program
     SIGQUIT      create core image    quit program
     SIGILL       create core image    illegal instruction
     SIGTRAP      create core image    trace trap
     SIGABRT      create core image    abort(3) call (formerly SIGIOT)
     SIGEMT       create core image    emulate instruction executed
     SIGFPE       create core image    floating-point exception
     SIGKILL      terminate process    kill program (cannot be caught or
                                       ignored)
     SIGBUS       create core image    bus error
     SIGSEGV      create core image    segmentation violation
     SIGSYS       create core image    system call given invalid argument
     SIGPIPE      terminate process    write on a pipe with no reader
     SIGALRM      terminate process    real-time timer expired
     SIGTERM      terminate process    software termination signal
     SIGURG       discard signal       urgent condition present on socket
     SIGSTOP      stop process         stop (cannot be caught or ignored)
     SIGTSTP      stop process         stop signal generated from keyboard
     SIGCONT      discard signal       continue after stop
     SIGCHLD      discard signal       child status has changed
     SIGTTIN      stop process         background read attempted from control
                                       terminal
     SIGTTOU      stop process         background write attempted to control
                                       terminal
     SIGIO        discard signal       I/O is possible on a descriptor (see
                                       fcntl(2))
     SIGXCPU      terminate process    CPU time limit exceeded (see
                                       setrlimit(2))
     SIGXFSZ      terminate process    file size limit exceeded (see
                                       setrlimit(2))
     SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
     SIGPROF      terminate process    profiling timer alarm (see
                                       setitimer(2))
     SIGWINCH     discard signal       window size change
     SIGINFO      discard signal       status request from keyboard
     SIGUSR1      terminate process    user-defined signal 1
     SIGUSR2      terminate process    user-defined signal 2
     SIGTHR       discard signal       thread AST
*/

#[cfg(target_os = "openbsd")]
pub static ALL_SIGNALS: [&str; 33] = [
    "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", "SEGV",
    "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
    "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2", "THR",
];

pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
    if let Ok(value) = signal_name_or_value.parse() {
        if is_signal(value) {
            return Some(value);
        } else {
            return None;
        }
    }
    let signal_name = signal_name_or_value.trim_start_matches("SIG");

    ALL_SIGNALS.iter().position(|&s| s == signal_name)
}

pub fn is_signal(num: usize) -> bool {
    num < ALL_SIGNALS.len()
}

pub fn signal_name_by_value(signal_value: usize) -> Option<&'static str> {
    ALL_SIGNALS.get(signal_value).copied()
}

#[test]
fn signal_by_value() {
    assert_eq!(signal_by_name_or_value("0"), Some(0));
    for (value, _signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal_by_name_or_value(&value.to_string()), Some(value));
    }
}

#[test]
fn signal_by_short_name() {
    for (value, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal_by_name_or_value(signal), Some(value));
    }
}

#[test]
fn signal_by_long_name() {
    for (value, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(
            signal_by_name_or_value(&format!("SIG{}", signal)),
            Some(value)
        );
    }
}

#[test]
fn name() {
    for (value, signal) in ALL_SIGNALS.iter().enumerate() {
        assert_eq!(signal_name_by_value(value), Some(*signal));
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use goblin::elf64::program_header::{ProgramHeader, PF_W, PF_X, PT_LOAD};
use x86_64::{
    align_down, align_up,
    structures::paging::{
        frame::PhysFrameRange, mapper::MapToError, FrameAllocator, Page, PageSize, PhysFrame,
        Size2MiB, Size4KiB,
    },
    PhysAddr, VirtAddr,
};

use super::{Mapper, PageTableFlags};

/// Map a region of physical memory to a virtual address using 2 MiB pages.
///
/// ## Safety
///
/// There are many ways you can cause memory safety errors and undefined behaviour when creating
/// page mappings. See <Mapper::map_to_with_table_flags> for examples.
pub unsafe fn create_offset_map<S: PageSize, A: FrameAllocator<Size4KiB>, M: Mapper<S>>(
    range: PhysFrameRange<S>,
    offset: VirtAddr,
    flags: PageTableFlags,
    mapper: &mut M,
    frame_allocator: &mut A,
) -> Result<(), MapToError<S>> {
    for (i, frame) in range.enumerate() {
        mapper
            .map_to_with_table_flags(
                Page::<S>::from_start_address(offset + i * (S::SIZE as usize)).unwrap(),
                frame,
                flags,
                PageTableFlags::PRESENT
                    | PageTableFlags::GLOBAL
                    | PageTableFlags::WRITABLE
                    | PageTableFlags::ENCRYPTED,
                frame_allocator,
            )?
            .ignore();
    }
    Ok(())
}

/// Maps the kernel memory ranges (ie. vaddr in last 2GB of memory), respecting section permissions.
///
/// Strictly speaking there's nothing int he ELF file that _requires_ sections to be page-aligned.
/// It's entirely possible to have a section with write privileges and a section with execute
/// privileges be on the same 4K page -- which would defeat the purpose of restricting the
/// permissions, as we'd have to go for the lowest common denominator and mark the pages both
/// writable and executable.
/// For now, calling this function fails if there is any overlap when using 4K pages.
///
/// We also create mappings only for sections with vaddr above 0xFFFF_FFFF_8000_0000, that is, for
/// the memory range where the kernel code should lie.
///
/// The mappings we create respect the `PF_W` and `PF_X` flags; there's no way how to map a page as
/// not readable (but still writable or executable), so `PF_R` is ignored.
///
/// Note that even if this function returns an error, the page map may have been partially updated.
///
/// # Safety
/// There are many ways you can cause memory safety errors and undefined behaviour when creating
/// page mappings. See <Mapper::map_to_with_table_flags> for examples.
///
/// `EferFlags::NO_EXECUTE_ENABLE` needs to be enabled before loading the page tables created by
/// this function.
pub unsafe fn create_kernel_map<
    A: FrameAllocator<Size4KiB>,
    M: Mapper<Size2MiB> + Mapper<Size4KiB>,
>(
    program_headers: &[ProgramHeader],
    mapper: &mut M,
    frame_allocator: &mut A,
) -> Result<(), MapToError<Size4KiB>> {
    program_headers
        .iter()
        .filter(|phdr| phdr.p_type == PT_LOAD && phdr.p_vaddr >= 0xFFFF_FFFF_8000_0000)
        .map(|phdr| {
            (
                PhysFrame::<Size4KiB>::range(
                    PhysFrame::from_start_address(PhysAddr::new(align_down(
                        phdr.p_paddr,
                        Size4KiB::SIZE,
                    )))
                    .unwrap(),
                    PhysFrame::from_start_address(PhysAddr::new(align_up(
                        phdr.p_paddr + phdr.p_memsz,
                        Size4KiB::SIZE,
                    )))
                    .unwrap(),
                ),
                VirtAddr::new(phdr.p_vaddr),
                /* It's not possible to mark a page not readable, so we ignore PF_R. */
                PageTableFlags::PRESENT
                    | PageTableFlags::GLOBAL
                    | PageTableFlags::ENCRYPTED
                    | if phdr.p_flags & PF_W > 0 {
                        PageTableFlags::WRITABLE
                    } else {
                        PageTableFlags::empty()
                    }
                    | if phdr.p_flags & PF_X == 0 {
                        PageTableFlags::NO_EXECUTE
                    } else {
                        PageTableFlags::empty()
                    },
            )
        })
        .try_for_each(|(range, offset, flags)| {
            create_offset_map(range, offset, flags, mapper, frame_allocator)
        })
}
<commit_msg>SEV: don't set `GLOBAL` in parent page table entries<commit_after>//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use goblin::elf64::program_header::{ProgramHeader, PF_W, PF_X, PT_LOAD};
use x86_64::{
    align_down, align_up,
    structures::paging::{
        frame::PhysFrameRange, mapper::MapToError, FrameAllocator, Page, PageSize, PhysFrame,
        Size2MiB, Size4KiB,
    },
    PhysAddr, VirtAddr,
};

use super::{Mapper, PageTableFlags};

/// Map a region of physical memory to a virtual address using 2 MiB pages.
///
/// ## Safety
///
/// There are many ways you can cause memory safety errors and undefined behaviour when creating
/// page mappings. See <Mapper::map_to_with_table_flags> for examples.
pub unsafe fn create_offset_map<S: PageSize, A: FrameAllocator<Size4KiB>, M: Mapper<S>>(
    range: PhysFrameRange<S>,
    offset: VirtAddr,
    flags: PageTableFlags,
    mapper: &mut M,
    frame_allocator: &mut A,
) -> Result<(), MapToError<S>> {
    for (i, frame) in range.enumerate() {
        // We don't set `PageTableFlags::GLOBAL` in `parent_table_flags` because Intel and AMD CPUs
        // behave differently (Intel ignores the `G` bit in parent page table entries, AMD ignores
        // it in lower entries _except_ PML4 and PML5); the `G` bit has semantic meaning only in the
        // lowest level of page tables.
        mapper
            .map_to_with_table_flags(
                Page::<S>::from_start_address(offset + i * (S::SIZE as usize)).unwrap(),
                frame,
                flags,
                PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::ENCRYPTED,
                frame_allocator,
            )?
            .ignore();
    }
    Ok(())
}

/// Maps the kernel memory ranges (ie. vaddr in last 2GB of memory), respecting section permissions.
///
/// Strictly speaking there's nothing int he ELF file that _requires_ sections to be page-aligned.
/// It's entirely possible to have a section with write privileges and a section with execute
/// privileges be on the same 4K page -- which would defeat the purpose of restricting the
/// permissions, as we'd have to go for the lowest common denominator and mark the pages both
/// writable and executable.
/// For now, calling this function fails if there is any overlap when using 4K pages.
///
/// We also create mappings only for sections with vaddr above 0xFFFF_FFFF_8000_0000, that is, for
/// the memory range where the kernel code should lie.
///
/// The mappings we create respect the `PF_W` and `PF_X` flags; there's no way how to map a page as
/// not readable (but still writable or executable), so `PF_R` is ignored.
///
/// Note that even if this function returns an error, the page map may have been partially updated.
///
/// # Safety
/// There are many ways you can cause memory safety errors and undefined behaviour when creating
/// page mappings. See <Mapper::map_to_with_table_flags> for examples.
///
/// `EferFlags::NO_EXECUTE_ENABLE` needs to be enabled before loading the page tables created by
/// this function.
pub unsafe fn create_kernel_map<
    A: FrameAllocator<Size4KiB>,
    M: Mapper<Size2MiB> + Mapper<Size4KiB>,
>(
    program_headers: &[ProgramHeader],
    mapper: &mut M,
    frame_allocator: &mut A,
) -> Result<(), MapToError<Size4KiB>> {
    program_headers
        .iter()
        .filter(|phdr| phdr.p_type == PT_LOAD && phdr.p_vaddr >= 0xFFFF_FFFF_8000_0000)
        .map(|phdr| {
            (
                PhysFrame::<Size4KiB>::range(
                    PhysFrame::from_start_address(PhysAddr::new(align_down(
                        phdr.p_paddr,
                        Size4KiB::SIZE,
                    )))
                    .unwrap(),
                    PhysFrame::from_start_address(PhysAddr::new(align_up(
                        phdr.p_paddr + phdr.p_memsz,
                        Size4KiB::SIZE,
                    )))
                    .unwrap(),
                ),
                VirtAddr::new(phdr.p_vaddr),
                /* It's not possible to mark a page not readable, so we ignore PF_R. */
                PageTableFlags::PRESENT
                    | PageTableFlags::GLOBAL
                    | PageTableFlags::ENCRYPTED
                    | if phdr.p_flags & PF_W > 0 {
                        PageTableFlags::WRITABLE
                    } else {
                        PageTableFlags::empty()
                    }
                    | if phdr.p_flags & PF_X == 0 {
                        PageTableFlags::NO_EXECUTE
                    } else {
                        PageTableFlags::empty()
                    },
            )
        })
        .try_for_each(|(range, offset, flags)| {
            create_offset_map(range, offset, flags, mapper, frame_allocator)
        })
}
<|endoftext|>


<commit_before>use bifrost::vector_clock::{StandardVectorClock, Relation};
use bifrost::utils::time::get_time;
use std::collections::{BTreeSet, BTreeMap};
use chashmap::{CHashMap, WriteGuard};
use ram::types::{Id};
use ram::cell::{Cell, ReadError, WriteError};
use server::NebServer;
use super::*;

pub type CellMetaGuard <'a> = WriteGuard<'a, Id, CellMeta>;
pub type CommitHistory = BTreeMap<Id, CellHistory>;

pub struct CellMeta {
    read: TransactionId,
    write: TransactionId,
    owner: Option<TransactionId>, // transaction that owns the cell in Committing state
    waiting: BTreeSet<TransactionId>, // transactions that waiting for owner to finish
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CommitOp {
    Write(Cell),
    Update(Cell),
    Remove(Id)
}

#[derive(Debug, Serialize, Deserialize)]
pub enum PrepareResult {
    Wait,
    Success,
    TransactionNotExisted,
    NotRealizable,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CommitResult {
    Success,
    WriteError(Id, WriteError, Vec<RollbackFailure>),
    CellChanged(Id, Vec<RollbackFailure>),
    CheckFailed(CheckError),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CheckError {
    CellNumberDoesNotMatch(usize, usize),
    TransactionNotExisted,
    TransactionNotCommitted,
    TransactionAlreadyCommitted,
    TransactionAlreadyAborted,
    TransactionCannotEnd,
}

#[derive(Debug, Eq, PartialEq)]
enum TransactionState {
    Started,
    Aborted,
    Committing,
    Committed,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum AbortResult {
    CheckFailed(CheckError),
    Success(Option<Vec<RollbackFailure>>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum EndResult {
    CheckFailed(CheckError),
    Success,
}

struct Transaction {
    server: u64,
    id: TransactionId,
    state: TransactionState,
    affect_cells: usize,
    last_activity: i64,
    history: Option<CommitHistory>
}

struct CellHistory {
    cell: Option<Cell>,
    current_version: u64
}

impl CellHistory {
    pub fn new (cell: Option<Cell>, current_ver: u64) -> CellHistory {
        CellHistory {
            cell: cell,
            current_version: current_ver,
        }
    }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RollbackFailure {
    id: Id,
    error: WriteError
}

pub struct DataManager {
    cells: CHashMap<Id, CellMeta>,
    tnxs: CHashMap<TransactionId, Transaction>,
    server: Arc<NebServer>
}

service! {
    rpc read(server_id: u64, clock: StandardVectorClock, tid: TransactionId, id: Id) -> DataSiteResponse<TransactionExecResult<Cell, ReadError>>;

    // two phase commit
    rpc prepare(clock :StandardVectorClock, tid: TransactionId, cell_ids: Vec<Id>) -> DataSiteResponse<PrepareResult>;
    rpc commit(clock :StandardVectorClock, tid: TransactionId, cells: Vec<CommitOp>) -> DataSiteResponse<CommitResult>;

    // because there may be some exception on commit, abort have to handle 'committed' and 'committing' transactions
    // for committed transaction, abort need to recover the data according to it's cells history
    rpc abort(clock :StandardVectorClock, tid: TransactionId) -> DataSiteResponse<AbortResult>;

    // there also should be a 'end' from transaction manager to inform data manager to clean up and release cell locks
    rpc end(clock :StandardVectorClock, tid: TransactionId) -> DataSiteResponse<EndResult>;
}

dispatch_rpc_service_functions!(DataManager);

impl DataManager {
    fn local_clock(&self) -> StandardVectorClock {
        self.server.tnx_peer.clock.to_clock()
    }
    fn update_clock(&self, clock: &StandardVectorClock) {
        self.server.tnx_peer.clock.merge_with(clock);
    }
    fn get_transaction(&self, tid: &TransactionId, server: &u64) -> WriteGuard<TransactionId, Transaction> {
        if !self.tnxs.contains_key(tid) {
            self.tnxs.upsert(tid.clone(), ||{
                Transaction {
                    id: tid.clone(),
                    server: *server,
                    state: TransactionState::Started,
                    affect_cells: 0,
                    last_activity: get_time(),
                    history: None
                }
            }, |t|{});
        }
        match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => self.get_transaction(tid, server) // it should be rare
        }
    }
    fn get_cell_meta(&self, id: &Id) -> CellMetaGuard {
        if !self.cells.contains_key(id) {
            self.cells.upsert(*id, || {
                CellMeta {
                    read: TransactionId::new(),
                    write: TransactionId::new(),
                    owner: None,
                    waiting: BTreeSet::new()
                }
            }, |c|{});
        }
        match self.cells.get_mut(id) {
            Some(cell) => cell,
            _ => self.get_cell_meta(id)
        }
    }
    fn response_with<T>(&self, data: T)
        -> Result<DataSiteResponse<T>, ()>{
        Ok(DataSiteResponse::new(&self.server.tnx_peer, data))
    }
    fn rollback(&self, history: &CommitHistory) -> Vec<RollbackFailure> {
        let mut failures: Vec<RollbackFailure> = Vec::new();
        for (id, history) in history.iter() {
            let cell = &history.cell;
            let current_ver = history.current_version;
            let error = if cell.is_none() { // the cell was created, need to remove
                self.server.chunks.remove_cell_by(&id, |cell| {
                    cell.header.version == current_ver
                }).err()
            } else if current_ver > 0 { // the cell was updated, need to update back
                self.server.chunks.update_cell_by(id, |cell_to_update|{
                    if cell_to_update.header.version == current_ver {
                        cell.clone()
                    } else { None }
                }).err()
            } else { // the cell was removed, need to put back
                let mut cell = cell.clone().unwrap();
                self.server.chunks.write_cell(&mut cell).err()
            };
            if let Some(error) = error {
                failures.push(RollbackFailure{id: *id, error: error});
            }
        }
        failures
    }
}

impl Service for DataManager {
    fn read(&self, server_id: &u64, clock: &StandardVectorClock, tid: &TransactionId, id: &Id)
        -> Result<DataSiteResponse<TransactionExecResult<Cell, ReadError>>, ()> {
        self.update_clock(clock);
        let mut meta = self.get_cell_meta(id);
        let committing = meta.owner.is_some();
        let read_too_late = &meta.write > tid;
        let mut tnx = self.get_transaction(tid, server_id);
        if read_too_late { // not realizable
            return self.response_with(TransactionExecResult::Rejected);
        }
        if committing { // ts >= wt but committing, need to wait until it committed
            meta.waiting.insert(tid.clone());
            return self.response_with(TransactionExecResult::Wait);
        }
        let cell = match self.server.chunks.read_cell(id) {
            Ok(cell) => {cell}
            Err(read_error) => {
                // cannot read
                return self.response_with(TransactionExecResult::Error(read_error));
            }
        };
        let update_read = &meta.read < tid;
        if update_read {
            meta.read = tid.clone()
        }
        tnx.last_activity = get_time();
        self.response_with(TransactionExecResult::Accepted(cell))
    }
    fn prepare(&self, clock :&StandardVectorClock, tid: &TransactionId, cell_ids: &Vec<Id>)
        -> Result<DataSiteResponse<PrepareResult>, ()> {
        // In this stage, data manager will not do any write operation but mark cell owner in their meta as a lock
        // It will also check if write are realizable. If not, transaction manager should retry with new id
        // cell_ids must be sorted to avoid deadlock. It can be done from data manager by using BTreeMap keys
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(PrepareResult::TransactionNotExisted); }
        };
        let mut cell_metas: Vec<CellMetaGuard> = Vec::new();
        for cell_id in cell_ids {
            let meta = self.get_cell_meta(cell_id);
            if tid < &meta.read { // write too late
                break;
            }
            if tid < &meta.write {
                if meta.owner.is_some() { // not committed, should wait and try prepare again
                    return self.response_with(PrepareResult::Wait)
                }
            }
            cell_metas.push(meta);
        }
        if cell_metas.len() < cell_ids.len() {
            return self.response_with(PrepareResult::NotRealizable); // need retry
        } else {
            for mut meta in cell_metas {
                meta.owner = Some(tid.clone()) // set owner to lock this cell
            }
            tnx.state = TransactionState::Committing;
            tnx.affect_cells = cell_ids.len(); // for cell number check
            tnx.last_activity = get_time();    // check if transaction timeout
            return self.response_with(PrepareResult::Success);
        }
    }
    fn commit(&self, clock :&StandardVectorClock, tid: &TransactionId, cells: &Vec<CommitOp>)
        -> Result<DataSiteResponse<CommitResult>, ()>  {
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(CommitResult::CheckFailed(CheckError::TransactionNotExisted)); }
        };
        // check state
        match tnx.state {
            TransactionState::Started => {return self.response_with(CommitResult::CheckFailed(CheckError::TransactionNotCommitted))},
            TransactionState::Aborted => {return self.response_with(CommitResult::CheckFailed(CheckError::TransactionAlreadyAborted))},
            TransactionState::Committed => {return self.response_with(CommitResult::CheckFailed(CheckError::TransactionAlreadyCommitted))},
            TransactionState::Committing => {}
        }
        // check cell list integrity
        let prepared_cells_num = tnx.affect_cells;
        let arrived_cells_num = self.cells.len();
        if prepared_cells_num != arrived_cells_num {return self.response_with(CommitResult::CheckFailed(CheckError::CellNumberDoesNotMatch(prepared_cells_num, arrived_cells_num)))}
        let mut commit_history: CommitHistory = BTreeMap::new(); // for rollback in case of write error
        let mut write_error: Option<(Id, WriteError)> = None;
        for cell_op in cells {
            match cell_op {
                &CommitOp::Write(ref cell) => {
                    let mut cell = cell.clone();
                    let write_result = self.server.chunks.write_cell(&mut cell);
                    match write_result {
                        Ok(header) => {
                            commit_history.insert(cell.id(), CellHistory::new(None, header.version));
                        },
                        Err(error) => {
                            write_error = Some((cell.id(), error));
                            break;
                        }
                    };
                },
                &CommitOp::Remove(ref cell_id) => {
                    let original_cell = {
                        match self.server.chunks.read_cell(cell_id) {
                            Ok(cell) => cell,
                            Err(re) => {
                                write_error = Some((*cell_id, WriteError::ReadError(re)));
                                break;
                            }
                        }
                    };
                    let write_result = self.server.chunks.remove_cell_by(cell_id, |cell|{
                        let version = cell.header.version;
                        version == original_cell.header.version
                    });
                    match write_result {
                        Ok(()) => {
                            commit_history.insert(*cell_id, CellHistory::new(Some(original_cell), 0));
                        },
                        Err(error) => {
                            write_error = Some((*cell_id, error));
                            break;
                        }
                    }
                },
                &CommitOp::Update(ref cell) => {
                    let cell_id = cell.id();
                    let original_cell = {
                        match self.server.chunks.read_cell(&cell_id) {
                            Ok(cell) => cell,
                            Err(re) => {
                                write_error = Some((cell_id, WriteError::ReadError(re)));
                                break;
                            }
                        }
                    };
                    let write_result = self.server.chunks.update_cell_by(&cell_id, |cell_to_update| {
                        if cell_to_update.header.version == original_cell.header.version {
                            Some(cell.clone())
                        } else {
                            None
                        }
                    });
                    match write_result {
                        Ok(cell) => {
                            commit_history.insert(cell_id, CellHistory::new(Some(original_cell), cell.header.version));
                        },
                        Err(error) => {
                            write_error = Some((cell_id, error));
                            break;
                        }
                    }
                }
            }
        }
        // check if any of those operations failed, if yes, rollback and fail this commit
        if let Some((id, error)) = write_error {
            let rollback_result = self.rollback(&commit_history);
            tnx.state = TransactionState::Aborted; // set to abort so it can't be abort again
            tnx.last_activity = get_time();
            match error {
                WriteError::DeletionPredictionFailed | WriteError::UserCanceledUpdate => {
                    // in this case, we can inform transaction manager to try again
                    return self.response_with(CommitResult::CellChanged(
                        id, rollback_result
                    ));
                }
                _ => {
                    // other failure due to unfixable error should abort without retry
                    return self.response_with(CommitResult::WriteError(
                        id, error, rollback_result
                    ));
                }
            }
        } else {
            // all set, able to commit
            tnx.state = TransactionState::Committed;
            tnx.history = Some(commit_history);
            tnx.last_activity = get_time();
            return self.response_with(CommitResult::Success)
        }
    }
    fn abort(&self, clock :&StandardVectorClock, tid: &TransactionId)
        -> Result<DataSiteResponse<AbortResult>, ()>  {
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(AbortResult::CheckFailed(CheckError::TransactionNotExisted)); }
        };
        if tnx.state == TransactionState::Aborted {
            return self.response_with(AbortResult::CheckFailed(CheckError::TransactionAlreadyAborted));
        }
        let rollback_failure = {
            if let Some(ref history) = tnx.history {
                let history = self.rollback(history);
                if history.len() == 0 { None } else { Some(history) }
            } else { None }
        };
        tnx.last_activity = get_time();
        tnx.state = TransactionState::Aborted;
        self.response_with(AbortResult::Success(rollback_failure))
    }
    fn end(&self, clock :&StandardVectorClock, tid: &TransactionId)
             -> Result<DataSiteResponse<EndResult>, ()>  {
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(EndResult::CheckFailed(CheckError::TransactionNotExisted)); }
        };
        if !(tnx.state == TransactionState::Aborted || tnx.state == TransactionState::Committed) {
            return self.response_with(EndResult::CheckFailed(CheckError::TransactionCannotEnd))
        }
        // TODO: clean up and inform waiting transaction managers
        self.response_with(EndResult::Success)
    }
}

// TODO: update 'read' and 'write' in CellMeta and deal with abort recover<commit_msg>release cell locks in 'end' procedure<commit_after>use bifrost::vector_clock::{StandardVectorClock, Relation};
use bifrost::utils::time::get_time;
use std::collections::{BTreeSet, BTreeMap};
use chashmap::{CHashMap, WriteGuard};
use ram::types::{Id};
use ram::cell::{Cell, ReadError, WriteError};
use server::NebServer;
use super::*;

pub type CellMetaGuard <'a> = WriteGuard<'a, Id, CellMeta>;
pub type CommitHistory = BTreeMap<Id, CellHistory>;

pub struct CellMeta {
    read: TransactionId,
    write: TransactionId,
    owner: Option<TransactionId>, // transaction that owns the cell in Committing state
    waiting: BTreeSet<TransactionId>, // transactions that waiting for owner to finish
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CommitOp {
    Write(Cell),
    Update(Cell),
    Remove(Id)
}

#[derive(Debug, Serialize, Deserialize)]
pub enum PrepareResult {
    Wait,
    Success,
    TransactionNotExisted,
    NotRealizable,
    TransactionStateError(TransactionState)
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CommitResult {
    Success,
    WriteError(Id, WriteError, Vec<RollbackFailure>),
    CellChanged(Id, Vec<RollbackFailure>),
    CheckFailed(CheckError),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum CheckError {
    CellNumberDoesNotMatch(usize, usize),
    TransactionNotExisted,
    TransactionNotCommitted,
    TransactionAlreadyCommitted,
    TransactionAlreadyAborted,
    TransactionCannotEnd,
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Copy, Clone)]
enum TransactionState {
    Started,
    Aborted,
    Committing,
    Committed,
}

#[derive(Debug, Serialize, Deserialize)]
pub enum AbortResult {
    CheckFailed(CheckError),
    Success(Option<Vec<RollbackFailure>>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum EndResult {
    CheckFailed(CheckError),
    SomeLocksNotReleased,
    Success,
}

struct Transaction {
    server: u64,
    id: TransactionId,
    state: TransactionState,
    affected_cells: Vec<Id>,
    last_activity: i64,
    history: CommitHistory
}

struct CellHistory {
    cell: Option<Cell>,
    current_version: u64
}

impl CellHistory {
    pub fn new (cell: Option<Cell>, current_ver: u64) -> CellHistory {
        CellHistory {
            cell: cell,
            current_version: current_ver,
        }
    }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RollbackFailure {
    id: Id,
    error: WriteError
}

pub struct DataManager {
    cells: CHashMap<Id, CellMeta>,
    tnxs: CHashMap<TransactionId, Transaction>,
    server: Arc<NebServer>
}

service! {
    rpc read(server_id: u64, clock: StandardVectorClock, tid: TransactionId, id: Id) -> DataSiteResponse<TransactionExecResult<Cell, ReadError>>;

    // two phase commit
    rpc prepare(clock :StandardVectorClock, tid: TransactionId, cell_ids: Vec<Id>) -> DataSiteResponse<PrepareResult>;
    rpc commit(clock :StandardVectorClock, tid: TransactionId, cells: Vec<CommitOp>) -> DataSiteResponse<CommitResult>;

    // because there may be some exception on commit, abort have to handle 'committed' and 'committing' transactions
    // for committed transaction, abort need to recover the data according to it's cells history
    rpc abort(clock :StandardVectorClock, tid: TransactionId) -> DataSiteResponse<AbortResult>;

    // there also should be a 'end' from transaction manager to inform data manager to clean up and release cell locks
    rpc end(clock :StandardVectorClock, tid: TransactionId) -> DataSiteResponse<EndResult>;
}

dispatch_rpc_service_functions!(DataManager);

impl DataManager {
    fn local_clock(&self) -> StandardVectorClock {
        self.server.tnx_peer.clock.to_clock()
    }
    fn update_clock(&self, clock: &StandardVectorClock) {
        self.server.tnx_peer.clock.merge_with(clock);
    }
    fn get_transaction(&self, tid: &TransactionId, server: &u64) -> WriteGuard<TransactionId, Transaction> {
        if !self.tnxs.contains_key(tid) {
            self.tnxs.upsert(tid.clone(), ||{
                Transaction {
                    id: tid.clone(),
                    server: *server,
                    state: TransactionState::Started,
                    affected_cells: Vec::new(),
                    last_activity: get_time(),
                    history: BTreeMap::new(),
                }
            }, |t|{});
        }
        match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => self.get_transaction(tid, server) // it should be rare
        }
    }
    fn get_cell_meta(&self, id: &Id) -> CellMetaGuard {
        if !self.cells.contains_key(id) {
            self.cells.upsert(*id, || {
                CellMeta {
                    read: TransactionId::new(),
                    write: TransactionId::new(),
                    owner: None,
                    waiting: BTreeSet::new()
                }
            }, |c|{});
        }
        match self.cells.get_mut(id) {
            Some(cell) => cell,
            _ => self.get_cell_meta(id)
        }
    }
    fn response_with<T>(&self, data: T)
        -> Result<DataSiteResponse<T>, ()>{
        Ok(DataSiteResponse::new(&self.server.tnx_peer, data))
    }
    fn rollback(&self, history: &CommitHistory) -> Vec<RollbackFailure> {
        let mut failures: Vec<RollbackFailure> = Vec::new();
        for (id, history) in history.iter() {
            let cell = &history.cell;
            let current_ver = history.current_version;
            let error = if cell.is_none() { // the cell was created, need to remove
                self.server.chunks.remove_cell_by(&id, |cell| {
                    cell.header.version == current_ver
                }).err()
            } else if current_ver > 0 { // the cell was updated, need to update back
                self.server.chunks.update_cell_by(id, |cell_to_update|{
                    if cell_to_update.header.version == current_ver {
                        cell.clone()
                    } else { None }
                }).err()
            } else { // the cell was removed, need to put back
                let mut cell = cell.clone().unwrap();
                self.server.chunks.write_cell(&mut cell).err()
            };
            if let Some(error) = error {
                failures.push(RollbackFailure{id: *id, error: error});
            }
        }
        failures
    }
    fn update_cell_write(&self, cell_id: &Id, tid: &TransactionId) {
        let mut meta = self.get_cell_meta(cell_id);
        meta.write = tid.clone();
    }
}

impl Service for DataManager {
    fn read(&self, server_id: &u64, clock: &StandardVectorClock, tid: &TransactionId, id: &Id)
        -> Result<DataSiteResponse<TransactionExecResult<Cell, ReadError>>, ()> {
        self.update_clock(clock);
        let mut meta = self.get_cell_meta(id);
        let committing = meta.owner.is_some();
        let read_too_late = &meta.write > tid;
        let mut tnx = self.get_transaction(tid, server_id);
        if read_too_late { // not realizable
            return self.response_with(TransactionExecResult::Rejected);
        }
        if committing { // ts >= wt but committing, need to wait until it committed
            meta.waiting.insert(tid.clone());
            return self.response_with(TransactionExecResult::Wait);
        }
        let cell = match self.server.chunks.read_cell(id) {
            Ok(cell) => {cell}
            Err(read_error) => {
                // cannot read
                return self.response_with(TransactionExecResult::Error(read_error));
            }
        };
        let update_read = &meta.read < tid;
        if update_read {
            meta.read = tid.clone()
        }
        tnx.last_activity = get_time();
        self.response_with(TransactionExecResult::Accepted(cell))
    }
    fn prepare(&self, clock :&StandardVectorClock, tid: &TransactionId, cell_ids: &Vec<Id>)
        -> Result<DataSiteResponse<PrepareResult>, ()> {
        // In this stage, data manager will not do any write operation but mark cell owner in their meta as a lock
        // It will also check if write are realizable. If not, transaction manager should retry with new id
        // cell_ids must be sorted to avoid deadlock. It can be done from data manager by using BTreeMap keys
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(PrepareResult::TransactionNotExisted); }
        };
        if tnx.state != TransactionState::Started {
            return self.response_with(PrepareResult::TransactionStateError(tnx.state))
        }
        let mut cell_metas: Vec<CellMetaGuard> = Vec::new();
        for cell_id in cell_ids {
            let meta = self.get_cell_meta(cell_id);
            if tid < &meta.read { // write too late
                break;
            }
            if tid < &meta.write {
                if meta.owner.is_some() { // not committed, should wait and try prepare again
                    return self.response_with(PrepareResult::Wait)
                }
            }
            cell_metas.push(meta);
        }
        if cell_metas.len() != cell_ids.len() {
            return self.response_with(PrepareResult::NotRealizable); // need retry
        } else {
            for mut meta in cell_metas {
                meta.owner = Some(tid.clone()) // set owner to lock this cell
            }
            tnx.state = TransactionState::Committing;
            tnx.affected_cells = cell_ids.clone(); // for cell number check
            tnx.last_activity = get_time();    // check if transaction timeout
            return self.response_with(PrepareResult::Success);
        }
    }
    fn commit(&self, clock :&StandardVectorClock, tid: &TransactionId, cells: &Vec<CommitOp>)
        -> Result<DataSiteResponse<CommitResult>, ()>  {
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(CommitResult::CheckFailed(CheckError::TransactionNotExisted)); }
        };
        // check state
        match tnx.state {
            TransactionState::Started => {return self.response_with(CommitResult::CheckFailed(CheckError::TransactionNotCommitted))},
            TransactionState::Aborted => {return self.response_with(CommitResult::CheckFailed(CheckError::TransactionAlreadyAborted))},
            TransactionState::Committed => {return self.response_with(CommitResult::CheckFailed(CheckError::TransactionAlreadyCommitted))},
            TransactionState::Committing => {}
        }
        // check cell list integrity
        let prepared_cells_num = tnx.affected_cells.len();
        let arrived_cells_num = self.cells.len();
        if prepared_cells_num != arrived_cells_num {return self.response_with(CommitResult::CheckFailed(CheckError::CellNumberDoesNotMatch(prepared_cells_num, arrived_cells_num)))}
        let mut commit_history: CommitHistory = BTreeMap::new(); // for rollback in case of write error
        let mut write_error: Option<(Id, WriteError)> = None;
        for cell_op in cells {
            match cell_op {
                &CommitOp::Write(ref cell) => {
                    let mut cell = cell.clone();
                    let write_result = self.server.chunks.write_cell(&mut cell);
                    match write_result {
                        Ok(header) => {
                            commit_history.insert(cell.id(), CellHistory::new(None, header.version));
                            self.update_cell_write(&cell.id(), tid);
                        },
                        Err(error) => {
                            write_error = Some((cell.id(), error));
                            break;
                        }
                    };
                },
                &CommitOp::Remove(ref cell_id) => {
                    let original_cell = {
                        match self.server.chunks.read_cell(cell_id) {
                            Ok(cell) => cell,
                            Err(re) => {
                                write_error = Some((*cell_id, WriteError::ReadError(re)));
                                break;
                            }
                        }
                    };
                    let write_result = self.server.chunks.remove_cell_by(cell_id, |cell|{
                        let version = cell.header.version;
                        version == original_cell.header.version
                    });
                    match write_result {
                        Ok(()) => {
                            commit_history.insert(*cell_id, CellHistory::new(Some(original_cell), 0));
                            self.update_cell_write(cell_id, tid);
                        },
                        Err(error) => {
                            write_error = Some((*cell_id, error));
                            break;
                        }
                    }
                },
                &CommitOp::Update(ref cell) => {
                    let cell_id = cell.id();
                    let original_cell = {
                        match self.server.chunks.read_cell(&cell_id) {
                            Ok(cell) => cell,
                            Err(re) => {
                                write_error = Some((cell_id, WriteError::ReadError(re)));
                                break;
                            }
                        }
                    };
                    let write_result = self.server.chunks.update_cell_by(&cell_id, |cell_to_update| {
                        if cell_to_update.header.version == original_cell.header.version {
                            Some(cell.clone())
                        } else {
                            None
                        }
                    });
                    match write_result {
                        Ok(cell) => {
                            commit_history.insert(cell_id, CellHistory::new(Some(original_cell), cell.header.version));
                            self.update_cell_write(&cell_id, tid);
                        },
                        Err(error) => {
                            write_error = Some((cell_id, error));
                            break;
                        }
                    }
                }
            }
        }
        // check if any of those operations failed, if yes, rollback and fail this commit
        if let Some((id, error)) = write_error {
            let rollback_result = self.rollback(&commit_history);
            tnx.state = TransactionState::Aborted; // set to abort so it can't be abort again
            tnx.last_activity = get_time();
            match error {
                WriteError::DeletionPredictionFailed | WriteError::UserCanceledUpdate => {
                    // in this case, we can inform transaction manager to try again
                    return self.response_with(CommitResult::CellChanged(
                        id, rollback_result
                    ));
                }
                _ => {
                    // other failure due to unfixable error should abort without retry
                    return self.response_with(CommitResult::WriteError(
                        id, error, rollback_result
                    ));
                }
            }
        } else {
            // all set, able to commit
            tnx.state = TransactionState::Committed;
            tnx.history = commit_history;
            tnx.last_activity = get_time();
            return self.response_with(CommitResult::Success)
        }
    }
    fn abort(&self, clock :&StandardVectorClock, tid: &TransactionId)
        -> Result<DataSiteResponse<AbortResult>, ()>  {
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(AbortResult::CheckFailed(CheckError::TransactionNotExisted)); }
        };
        if tnx.state == TransactionState::Aborted {
            return self.response_with(AbortResult::CheckFailed(CheckError::TransactionAlreadyAborted));
        }
        let rollback_failures = {
            let failures = self.rollback(&tnx.history);
            if failures.len() == 0 { None } else { Some(failures) }
        };
        tnx.last_activity = get_time();
        tnx.state = TransactionState::Aborted;
        self.response_with(AbortResult::Success(rollback_failures))
    }
    fn end(&self, clock :&StandardVectorClock, tid: &TransactionId)
             -> Result<DataSiteResponse<EndResult>, ()>  {
        self.update_clock(clock);
        let mut tnx = match self.tnxs.get_mut(tid) {
            Some(tnx) => tnx,
            _ => { return self.response_with(EndResult::CheckFailed(CheckError::TransactionNotExisted)); }
        };
        if !(tnx.state == TransactionState::Aborted || tnx.state == TransactionState::Committed) {
            return self.response_with(EndResult::CheckFailed(CheckError::TransactionCannotEnd))
        }
        let mut cell_metas: Vec<CellMetaGuard> = Vec::new();
        for cell_id in &tnx.affected_cells {
            if let Some(meta) = self.cells.get_mut(cell_id){
                cell_metas.push(meta)
            }
        }
        let mut released_locks = 0;
        for mut meta in cell_metas {
            if meta.owner == Some(tid.clone()) {
                meta.owner = None;
                released_locks += 1;
            }
        }
        // TODO: remove transaction and unuseful cell meta data
        if released_locks == tnx.affected_cells.len() {
            self.response_with(EndResult::Success)
        } else {
            self.response_with(EndResult::SomeLocksNotReleased)
        }
    }
}<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate jam;
extern crate cgmath;

use jam::render::shader::ShaderPair;
use jam::render::texture::texture_array::TextureDirectory;
use jam::input::InputState;
use jam::render::shader::fatter;
use jam::render::shader::fatter::Command::*;
use jam::render::shader::fatter::{Seconds, Dimensions, Application, Command, Uniforms};
use jam::camera::Camera;
use jam::color;
use jam::color::Color;
use jam::render::quads::GeometryTesselator;
use jam::render::texture::TextureRegion;
use jam::Vec3;
use std::f64::consts::PI;

use cgmath::Rad;

fn main() {
    let app = App {
        name: "mixalot".into(),
        camera: Camera {
            at: Vec3::new(0.0, 0.0, 0.0),
            pitch: Rad(PI / 4.0_f64),
            viewport: (800, 600),
            pixels_per_unit: 16.0 * 1.0,
        },
        zoom: 1.0,
        pixels_per_unit: 16.0,
        n: 0,
    };

    let shader_pair = ShaderPair::for_paths("resources/shader/fat.vert", "resources/shader/fat.frag");

    let texture_dir = TextureDirectory::for_path("resources/textures");

    fatter::fat_example(app, shader_pair, texture_dir, (600, 600));
}

struct App {
    name : String,
    camera : Camera,
    zoom : f64,
    pixels_per_unit : f64,
    n : u64,
}

impl App {
    fn units_per_pixel(&self) -> f64 {
        1.0 / self.pixels_per_unit
    }

    fn tesselator(&self) -> GeometryTesselator {
        let upp = self.units_per_pixel();
        let tesselator_scale = Vec3::new(upp, upp, upp);
        GeometryTesselator::new(tesselator_scale)
    }

     fn raster(&self, color:Color, x:f64, z:f64) -> GeometryTesselator {
         let texture_region = TextureRegion {
            u_min: 0,
            u_max: 128,
            v_min: 0,
            v_max: 128,
            texture_size: 128,
        };

        let mut t = self.tesselator();
        t.color = color.float_raw();
        t.draw_floor_tile(&texture_region, 0, x, 0.0, z, 0.0, false);
        t
    }
}

impl Application for App {
    fn new(&mut self) {
        println!("new! => {:?}", self.name);
    }

    fn render(&mut self, input_state:&InputState, dimensions:Dimensions, delta_time: Seconds) -> Vec<Command> {
        println!("delta time is -> {:?}", delta_time);
        self.n += 1;

        self.camera.at = Vec3::new(17.0, 0.0, 17.0);
        self.camera.pixels_per_unit = self.pixels_per_unit * self.zoom;
        self.camera.viewport = dimensions;

        let colors = vec![color::WHITE, color::BLUE, color::RED];

        let mut commands : Vec<fatter::Command> = Vec::new();

        let an = self.n / 60;

        let on_second = (self.n % 60) == 0;

        let raster_color = colors[(((an / 16) % 16) % 3) as usize]; // cycles every 16 seconds

        if on_second && an % 5 == 0 { // every fifth second
            let column = (an / 4) % 4;
            let name = format!("zone_{}", column);
            println!("delete {}", name);
            commands.push(Delete {prefix : name});
        }

        let n = (((an % 16) as f64) / 16.0 * 255.0) as u8;
        let render_color = color::rgb(n, n, n);

        for i in 0..16 {
            let xo = i % 4;
            let zo = i / 4;
            let name : String = format!("zone_{}_{}", xo, zo);

            if (an % 16) == i && on_second {
                let t = self.raster(raster_color, (xo * 9) as f64, (zo * 9) as f64);
                commands.push(DrawNew {
                    key: Some(name),
                    vertices: t.tesselator.vertices,
                    uniforms: Uniforms {
                        transform : fatter::down_size_m4(self.camera.view_projection().into()),
                        color: render_color,
                    }
                });
            } else if ((an+8) % 16) == i && on_second {
                let t = self.raster(color::GREEN, (xo * 9) as f64, (zo * 9) as f64);
                commands.push(Update {
                    key: name,
                    vertices: t.tesselator.vertices,
                });
            } else {
                commands.push(Draw {
                    key: name,
                     uniforms: Uniforms {
                        transform : fatter::down_size_m4(self.camera.view_projection().into()),
                        color: render_color,
                    }
                });
            }
        }

        if input_state.close {
            commands.push(Close);
        }

        commands
    }
}
<commit_msg>Removed debug<commit_after>#![allow(dead_code)]

extern crate jam;
extern crate cgmath;

use jam::render::shader::ShaderPair;
use jam::render::texture::texture_array::TextureDirectory;
use jam::input::InputState;
use jam::render::shader::fatter;
use jam::render::shader::fatter::Command::*;
use jam::render::shader::fatter::{Seconds, Dimensions, Application, Command, Uniforms};
use jam::camera::Camera;
use jam::color;
use jam::color::Color;
use jam::render::quads::GeometryTesselator;
use jam::render::texture::TextureRegion;
use jam::Vec3;
use std::f64::consts::PI;

use cgmath::Rad;

fn main() {
    let app = App {
        name: "mixalot".into(),
        camera: Camera {
            at: Vec3::new(0.0, 0.0, 0.0),
            pitch: Rad(PI / 4.0_f64),
            viewport: (800, 600),
            pixels_per_unit: 16.0 * 1.0,
        },
        zoom: 1.0,
        pixels_per_unit: 16.0,
        n: 0,
    };

    let shader_pair = ShaderPair::for_paths("resources/shader/fat.vert", "resources/shader/fat.frag");

    let texture_dir = TextureDirectory::for_path("resources/textures");

    fatter::fat_example(app, shader_pair, texture_dir, (600, 600));
}

struct App {
    name : String,
    camera : Camera,
    zoom : f64,
    pixels_per_unit : f64,
    n : u64,
}

impl App {
    fn units_per_pixel(&self) -> f64 {
        1.0 / self.pixels_per_unit
    }

    fn tesselator(&self) -> GeometryTesselator {
        let upp = self.units_per_pixel();
        let tesselator_scale = Vec3::new(upp, upp, upp);
        GeometryTesselator::new(tesselator_scale)
    }

     fn raster(&self, color:Color, x:f64, z:f64) -> GeometryTesselator {
         let texture_region = TextureRegion {
            u_min: 0,
            u_max: 128,
            v_min: 0,
            v_max: 128,
            texture_size: 128,
        };

        let mut t = self.tesselator();
        t.color = color.float_raw();
        t.draw_floor_tile(&texture_region, 0, x, 0.0, z, 0.0, false);
        t
    }
}

impl Application for App {
    fn new(&mut self) {
        println!("new! => {:?}", self.name);
    }

    fn render(&mut self, input_state:&InputState, dimensions:Dimensions, delta_time: Seconds) -> Vec<Command> {
        self.n += 1;

        self.camera.at = Vec3::new(17.0, 0.0, 17.0);
        self.camera.pixels_per_unit = self.pixels_per_unit * self.zoom;
        self.camera.viewport = dimensions;

        let colors = vec![color::WHITE, color::BLUE, color::RED];

        let mut commands : Vec<fatter::Command> = Vec::new();

        let an = self.n / 60;

        let on_second = (self.n % 60) == 0;

        let raster_color = colors[(((an / 16) % 16) % 3) as usize]; // cycles every 16 seconds

        if on_second && an % 5 == 0 { // every fifth second
            let column = (an / 4) % 4;
            let name = format!("zone_{}", column);
            println!("delete {}", name);
            commands.push(Delete {prefix : name});
        }

        let n = (((an % 16) as f64) / 16.0 * 255.0) as u8;
        let render_color = color::rgb(n, n, n);

        for i in 0..16 {
            let xo = i % 4;
            let zo = i / 4;
            let name : String = format!("zone_{}_{}", xo, zo);

            if (an % 16) == i && on_second {
                let t = self.raster(raster_color, (xo * 9) as f64, (zo * 9) as f64);
                commands.push(DrawNew {
                    key: Some(name),
                    vertices: t.tesselator.vertices,
                    uniforms: Uniforms {
                        transform : fatter::down_size_m4(self.camera.view_projection().into()),
                        color: render_color,
                    }
                });
            } else if ((an+8) % 16) == i && on_second {
                let t = self.raster(color::GREEN, (xo * 9) as f64, (zo * 9) as f64);
                commands.push(Update {
                    key: name,
                    vertices: t.tesselator.vertices,
                });
            } else {
                commands.push(Draw {
                    key: name,
                     uniforms: Uniforms {
                        transform : fatter::down_size_m4(self.camera.view_projection().into()),
                        color: render_color,
                    }
                });
            }
        }

        if input_state.close {
            commands.push(Close);
        }

        commands
    }
}
<|endoftext|>


<commit_before>use cfg_if::cfg_if;
use wasm_bindgen::prelude::*;

cfg_if! {
    // When the `console_error_panic_hook` feature is enabled, we can call the
    // `set_panic_hook` function to get better error messages if we ever panic.
    if #[cfg(feature = "console_error_panic_hook")] {
        use console_error_panic_hook::set_once as set_panic_hook;
    } else {
        #[inline]
        fn set_panic_hook() {}
    }
}

cfg_if! {
    // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
    // allocator.
    if #[cfg(feature = "wee_alloc")] {
        #[global_allocator]
        static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
    }
}

#[wasm_bindgen]
pub fn run(name: &str, input: &str, options: &str) -> String {
    set_panic_hook();

    let opts = match json_typegen_shared::parse::options(options) {
        Ok(opts) => opts,
        Err(msg) => return msg,
    };

    match json_typegen_shared::codegen(name, input, opts) {
        Ok(res) => res,
        Err(err) => format!("{}", err),
    }
}
<commit_msg>wasm/web: Show error cause<commit_after>use cfg_if::cfg_if;
use std::error::Error;
use wasm_bindgen::prelude::*;

cfg_if! {
    // When the `console_error_panic_hook` feature is enabled, we can call the
    // `set_panic_hook` function to get better error messages if we ever panic.
    if #[cfg(feature = "console_error_panic_hook")] {
        use console_error_panic_hook::set_once as set_panic_hook;
    } else {
        #[inline]
        fn set_panic_hook() {}
    }
}

cfg_if! {
    // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
    // allocator.
    if #[cfg(feature = "wee_alloc")] {
        #[global_allocator]
        static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
    }
}

#[wasm_bindgen]
pub fn run(name: &str, input: &str, options: &str) -> String {
    set_panic_hook();

    let opts = match json_typegen_shared::parse::options(options) {
        Ok(opts) => opts,
        Err(msg) => return format!("Error: {}", msg),
    };

    match json_typegen_shared::codegen(name, input, opts) {
        Ok(res) => res,
        Err(err) => {
            let mut output = format!("Error: {}", err);
            let mut err: &dyn Error = &err;
            while let Some(source) = err.source() {
                output = format!("{}\n  Caused by: {}", output, source);
                err = source;
            }
            output
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cell::Cell;
use std::mem::swap;

// Just a grab bag of stuff that you wouldn't want to actually write.

fn strange() -> bool { let _x: bool = return true; }

fn funny() {
    fn f(_x: ()) { }
    f(return);
}

fn what() {
    fn the(x: &Cell<bool>) {
        return while !x.get() { x.set(true); };
    }
    let i = &Cell::new(false);
    let dont = {||the(i)};
    dont();
    assert!((i.get()));
}

fn zombiejesus() {
    loop {
        while (return) {
            if (return) {
                match (return) {
                    1 => {
                        if (return) {
                            return
                        } else {
                            return
                        }
                    }
                    _ => { return }
                };
            } else if (return) {
                return;
            }
        }
        if (return) { break; }
    }
}

fn notsure() {
    let mut _x: isize;
    let mut _y = (_x = 0) == (_x = 0);
    let mut _z = (_x = 0) < (_x = 0);
    let _a = (_x += 0) == (_x = 0);
    let _b = swap(&mut _y, &mut _z) == swap(&mut _y, &mut _z);
}

fn canttouchthis() -> usize {
    fn p() -> bool { true }
    let _a = (assert!((true)) == (assert!(p())));
    let _c = (assert!((p())) == ());
    let _b: bool = (println!("{}", 0) == (return 0));
}

fn angrydome() {
    loop { if break { } }
    let mut i = 0;
    loop { i += 1; if i == 1 { match (continue) { 1 => { }, _ => panic!("wat") } }
      break; }
}

fn evil_lincoln() { let _evil = println!("lincoln"); }

fn dots() {
    assert_eq!(String::from(".................................................."),
               format!("{:?}", .. .. .. .. .. .. .. .. .. .. .. .. ..
                               .. .. .. .. .. .. .. .. .. .. .. ..));
}

fn you_eight() {
    assert_eq!(8, {
        macro_rules! u8 {
            (u8) => {
                mod u8 {
                    pub fn u8<'u8>(u8: &'u8 u8) -> &'u8 u8 {
                        "u8";
                        u8
                    }
                }
            };
        }

        u8!(u8);
        let &u8: &u8 = u8::u8(&8u8);
        u8
    });
}

fn fishy() {
    assert_eq!(String::from("><>"),
               String::<>::from::<>("><>").chars::<>().rev::<>().collect::<String>());
}

pub fn main() {
    strange();
    funny();
    what();
    zombiejesus();
    notsure();
    canttouchthis();
    angrydome();
    evil_lincoln();
    dots();
    you_eight();
    fishy();
}
<commit_msg>Rollup merge of #43752 - arshiamufti:union-test, r=estebank<commit_after>// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cell::Cell;
use std::mem::swap;

// Just a grab bag of stuff that you wouldn't want to actually write.

fn strange() -> bool { let _x: bool = return true; }

fn funny() {
    fn f(_x: ()) { }
    f(return);
}

fn what() {
    fn the(x: &Cell<bool>) {
        return while !x.get() { x.set(true); };
    }
    let i = &Cell::new(false);
    let dont = {||the(i)};
    dont();
    assert!((i.get()));
}

fn zombiejesus() {
    loop {
        while (return) {
            if (return) {
                match (return) {
                    1 => {
                        if (return) {
                            return
                        } else {
                            return
                        }
                    }
                    _ => { return }
                };
            } else if (return) {
                return;
            }
        }
        if (return) { break; }
    }
}

fn notsure() {
    let mut _x: isize;
    let mut _y = (_x = 0) == (_x = 0);
    let mut _z = (_x = 0) < (_x = 0);
    let _a = (_x += 0) == (_x = 0);
    let _b = swap(&mut _y, &mut _z) == swap(&mut _y, &mut _z);
}

fn canttouchthis() -> usize {
    fn p() -> bool { true }
    let _a = (assert!((true)) == (assert!(p())));
    let _c = (assert!((p())) == ());
    let _b: bool = (println!("{}", 0) == (return 0));
}

fn angrydome() {
    loop { if break { } }
    let mut i = 0;
    loop { i += 1; if i == 1 { match (continue) { 1 => { }, _ => panic!("wat") } }
      break; }
}

fn evil_lincoln() { let _evil = println!("lincoln"); }

fn dots() {
    assert_eq!(String::from(".................................................."),
               format!("{:?}", .. .. .. .. .. .. .. .. .. .. .. .. ..
                               .. .. .. .. .. .. .. .. .. .. .. ..));
}

fn you_eight() {
    assert_eq!(8, {
        macro_rules! u8 {
            (u8) => {
                mod u8 {
                    pub fn u8<'u8>(u8: &'u8 u8) -> &'u8 u8 {
                        "u8";
                        u8
                    }
                }
            };
        }

        u8!(u8);
        let &u8: &u8 = u8::u8(&8u8);
        u8
    });
}

fn fishy() {
    assert_eq!(String::from("><>"),
               String::<>::from::<>("><>").chars::<>().rev::<>().collect::<String>());
}

fn union() {
    union union<'union> { union: &'union union<'union>, }
}

pub fn main() {
    strange();
    funny();
    what();
    zombiejesus();
    notsure();
    canttouchthis();
    angrydome();
    evil_lincoln();
    dots();
    you_eight();
    fishy();
    union();
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Common functionality for the x86 and x86_64 Interrupt Descriptor Table.

use super::super::{DTablePtr, DTable};
use core::mem::size_of;
use core::fmt;

pub type Handler = unsafe extern "C" fn() -> ();
pub const IDT_ENTRIES: usize = 256;

/// x86 interrupt gate types.
///
/// Bit-and this with the attribute half-byte to produce the
/// `type_attr` field for a `Gate`
#[repr(u8)]
#[derive(Debug)]
pub enum GateType { Absent    = 0b0000_0000
                  , Interrupt = 0b1000_1110
                  , Call      = 0b1000_1100
                  , Trap      = 0b1000_1111
                  }

impl fmt::Display for GateType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self { &Absent    => write!(f, "Absent")
                   , &Interrupt => write!(f, "Interrupt")
                   , &Call      => write!(f, "Call")
                   , &Trap      => write!(f, "Trap")
                   }
    }
}

#[derive(Debug)]
pub struct ExceptionDescr { pub description: &'static str
                          , pub mnemonic: &'static str
                          ,
}

/// x86 exceptions.
///
/// Taken from the list at
/// [http://wiki.osdev.org/Exceptions](http://wiki.osdev.org/Exceptions)
pub static EXCEPTIONS: &'static [&'static str] = &[
    "Divide-by-zero Error",
    "Debug",
    "Non-maskable Interrupt",
    "Breakpoint",
    "Overflow",
    "Bound Range Exceeded",
    "Invalid Opcode",
    "Device Not Available",
    "Double Fault",
    "Coprocessor Segment Overrun",
    "Invalid TSS",
    "Segment Not Present",
    "Stack-Segment Fault",
    "General Protection Fault",
    "Page Fault",
    "Reserved",
    "x87 Floating-Point Exception",
    "Alignment Check",
    "Machine Check",
    "SIMD Floating-Point Exception",
    "Virtualization Exception",
];

pub trait Gate {
    fn from_handler(handler: Handler) -> Self;
}

// /// This is the format that `lidt` expects for the pointer to the IDT.
// /// ...apparently.
// #[repr(C, packed)]
// pub struct IdtPtr<I>
// where I: Idt { pub limit: u16
//              , pub base: *const I
//              }
//
// pub trait IdtPtrOps {
//     unsafe fn load(&self);
// }

pub trait InterruptContext {
    type Registers;

    fn err_no(&self) -> u32;
    fn int_id(&self) -> u32;
    fn registers(&self) -> Self::Registers;

    #[inline]
    unsafe fn exception(&self) -> &str {
        EXCEPTIONS[self.int_id() as usize]
    }

}

pub trait Idt: Sized {
    type Ctx: InterruptContext;
    type GateSize: Gate;

    /// Get the IDT pointer struct to pass to `lidt`
    fn get_ptr(&self) -> DTablePtr<Self> {
        DTablePtr { limit: (size_of::<Self::GateSize>() * IDT_ENTRIES) as u16
                  , base: self as *const Self
                  }
    }
    //
    // /// This is just a wrapper for prettiness reasons.
    // #[inline]
    // unsafe fn load(&self) {
    //     self.get_ptr()
    //         .load()
    // }

    /// Enable interrupts
    unsafe fn enable_interrupts() {
        asm!("sti" :::: "volatile")
    }

    fn disable_interrupts() {
        unsafe { asm!("cli" :::: "volatile"); }
    }

    fn add_gate(&mut self, idx: usize, handler: Handler);

    fn handle_cpu_exception(state: &Self::Ctx)  {
        // TODO: we can handle various types of CPU exception differently
        // TODO: make some nice debugging dumps
        unsafe {
            panic!( "CPU EXCEPTION {:#04x}: {}"
                  , state.err_no()
                  , state.exception() );
              }
    }

    extern "C" fn handle_interrupt(state: &Self::Ctx);
}
<commit_msg>De-stupid-ize pattern match in fmt::Display for GateType<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Common functionality for the x86 and x86_64 Interrupt Descriptor Table.

use super::super::{DTablePtr, DTable};
use core::mem::size_of;
use core::fmt;

pub type Handler = unsafe extern "C" fn() -> ();
pub const IDT_ENTRIES: usize = 256;

/// x86 interrupt gate types.
///
/// Bit-and this with the attribute half-byte to produce the
/// `type_attr` field for a `Gate`
#[repr(u8)]
#[derive(Debug)]
pub enum GateType { Absent    = 0b0000_0000
                  , Interrupt = 0b1000_1110
                  , Call      = 0b1000_1100
                  , Trap      = 0b1000_1111
                  }

impl fmt::Display for GateType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self { &GateType::Absent    => write!(f, "Absent")
                   , &GateType::Interrupt => write!(f, "Interrupt")
                   , &GateType::Call      => write!(f, "Call")
                   , &GateType::Trap      => write!(f, "Trap")
                   }
    }
}

#[derive(Debug)]
pub struct ExceptionDescr { pub description: &'static str
                          , pub mnemonic: &'static str
                        //   , pub
}

/// x86 exceptions.
///
/// Taken from the list at
/// [http://wiki.osdev.org/Exceptions](http://wiki.osdev.org/Exceptions)
pub static EXCEPTIONS: &'static [&'static str] = &[
    "Divide-by-zero Error",
    "Debug",
    "Non-maskable Interrupt",
    "Breakpoint",
    "Overflow",
    "Bound Range Exceeded",
    "Invalid Opcode",
    "Device Not Available",
    "Double Fault",
    "Coprocessor Segment Overrun",
    "Invalid TSS",
    "Segment Not Present",
    "Stack-Segment Fault",
    "General Protection Fault",
    "Page Fault",
    "Reserved",
    "x87 Floating-Point Exception",
    "Alignment Check",
    "Machine Check",
    "SIMD Floating-Point Exception",
    "Virtualization Exception",
];

pub trait Gate {
    fn from_handler(handler: Handler) -> Self;
}

// /// This is the format that `lidt` expects for the pointer to the IDT.
// /// ...apparently.
// #[repr(C, packed)]
// pub struct IdtPtr<I>
// where I: Idt { pub limit: u16
//              , pub base: *const I
//              }
//
// pub trait IdtPtrOps {
//     unsafe fn load(&self);
// }

pub trait InterruptContext {
    type Registers;

    fn err_no(&self) -> u32;
    fn int_id(&self) -> u32;
    fn registers(&self) -> Self::Registers;

    #[inline]
    unsafe fn exception(&self) -> &str {
        EXCEPTIONS[self.int_id() as usize]
    }

}

pub trait Idt: Sized {
    type Ctx: InterruptContext;
    type GateSize: Gate;

    /// Get the IDT pointer struct to pass to `lidt`
    fn get_ptr(&self) -> DTablePtr<Self> {
        DTablePtr { limit: (size_of::<Self::GateSize>() * IDT_ENTRIES) as u16
                  , base: self as *const Self
                  }
    }
    //
    // /// This is just a wrapper for prettiness reasons.
    // #[inline]
    // unsafe fn load(&self) {
    //     self.get_ptr()
    //         .load()
    // }

    /// Enable interrupts
    unsafe fn enable_interrupts() {
        asm!("sti" :::: "volatile")
    }

    fn disable_interrupts() {
        unsafe { asm!("cli" :::: "volatile"); }
    }

    fn add_gate(&mut self, idx: usize, handler: Handler);

    fn handle_cpu_exception(state: &Self::Ctx)  {
        // TODO: we can handle various types of CPU exception differently
        // TODO: make some nice debugging dumps
        unsafe {
            panic!( "CPU EXCEPTION {:#04x}: {}"
                  , state.err_no()
                  , state.exception() );
              }
    }

    extern "C" fn handle_interrupt(state: &Self::Ctx);
}
<|endoftext|>