commit
41dab25f9c
@ -0,0 +1,20 @@
|
||||
.section .text.boot
|
||||
.globl _start
|
||||
|
||||
_start:
|
||||
# read cpu affinity, start core 0, halt the rest
|
||||
mfc0 $8, $15, 1
|
||||
beqz $8, setup
|
||||
andi $8, $8, 0x3ff # use bits 11 ~ 0
|
||||
|
||||
halt:
|
||||
# core affinity != 0, halt it
|
||||
b halt
|
||||
nop
|
||||
|
||||
setup:
|
||||
# put the bootstack at 8MB offset of physical mem
|
||||
la $29, 0x80800000 # $sp
|
||||
la $28, _gp # $gp
|
||||
b boot_main
|
||||
nop
|
@ -0,0 +1,41 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
|
||||
/* MIPS entry point after cold reset */
|
||||
. = 0xBFC00000;
|
||||
|
||||
.text : {
|
||||
KEEP(*(.text.boot)) /* from boot.S */
|
||||
*(.text .text.* .gnu.linkonce.t*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data .data.* .gnu.linkonce.d*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.bss : {
|
||||
_sbss = .;
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4K);
|
||||
_ebss = .;
|
||||
}
|
||||
|
||||
.payload : {
|
||||
*(.payload)
|
||||
}
|
||||
|
||||
.dtb : {
|
||||
*(.dtb)
|
||||
}
|
||||
|
||||
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
use core::ptr;
|
||||
use fixedvec::FixedVec;
|
||||
use xmas_elf::program::{ProgramHeader32, Type};
|
||||
|
||||
const KERNEL_OFFSET: u32 = 0x80000000;
|
||||
|
||||
global_asm!(include_str!("boot.S"));
|
||||
|
||||
pub fn copy_kernel(kernel_start: usize, segments: &FixedVec<ProgramHeader32>) {
|
||||
// reverse program headers to avoid overlapping in memory copying
|
||||
let mut space = alloc_stack!([ProgramHeader32; 32]);
|
||||
let mut rev_segments = FixedVec::new(&mut space);
|
||||
for i in (0..segments.len()).rev() {
|
||||
rev_segments.push(segments[i]).unwrap();
|
||||
}
|
||||
|
||||
for segment in &rev_segments {
|
||||
if segment.get_type() != Ok(Type::Load) {
|
||||
continue;
|
||||
}
|
||||
let virt_addr = segment.virtual_addr;
|
||||
let offset = segment.offset;
|
||||
let file_size = segment.file_size;
|
||||
let mem_size = segment.mem_size;
|
||||
|
||||
unsafe {
|
||||
let src = (kernel_start as u32 + offset) as *const u8;
|
||||
let dst = virt_addr.wrapping_sub(KERNEL_OFFSET) as *mut u8;
|
||||
ptr::copy(src, dst, file_size as usize);
|
||||
ptr::write_bytes(dst.offset(file_size as isize), 0, (mem_size - file_size) as usize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
{
|
||||
"arch": "mips",
|
||||
"cpu": "mips32r2",
|
||||
"llvm-target": "mipsel-unknown-none",
|
||||
"data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "32",
|
||||
"target-c-int-width": "32",
|
||||
"os": "none",
|
||||
"features": "+mips32r2",
|
||||
"max-atomic-width": "32",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"-Tsrc/arch/mipsel/boot.ld"
|
||||
]
|
||||
},
|
||||
"executables": true,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"abi-blacklist": [
|
||||
"cdecl",
|
||||
"stdcall",
|
||||
"fastcall",
|
||||
"vectorcall",
|
||||
"thiscall",
|
||||
"aapcs",
|
||||
"win64",
|
||||
"sysv64",
|
||||
"ptx-kernel",
|
||||
"msp430-interrupt",
|
||||
"x86-interrupt"
|
||||
],
|
||||
"eliminate-frame-pointer": false
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
/// board specific constants
|
||||
pub const MEMORY_END: usize = 0x8800_0000;
|
||||
pub const KERNEL_HEAP_SIZE: usize = 0x00a0_0000;
|
@ -0,0 +1,51 @@
|
||||
/dts-v1/;
|
||||
|
||||
|
||||
/ {
|
||||
model = "qemu malta";
|
||||
compatible = "qemu,malta";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
chosen {
|
||||
stdio = &uart2;
|
||||
bootargs = "rust/sh";
|
||||
};
|
||||
|
||||
aliases { };
|
||||
|
||||
cpu_intc: interrupt-controller {
|
||||
compatible = "mti,cpu-interrupt-controller";
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
main_memory: memory@0 {
|
||||
device_type = "memory";
|
||||
reg = <0x00000000 0x10000000>;
|
||||
};
|
||||
|
||||
uart0: serial@b80003f8 {
|
||||
compatible = "ns16550a";
|
||||
reg = <0xb80003f8 0x8>;
|
||||
clock-frequency = <1843200>;
|
||||
};
|
||||
|
||||
uart2: serial@bf000900 {
|
||||
compatible = "ns16550a";
|
||||
reg = <0xbf000900 0x40>;
|
||||
reg-shift = <3>;
|
||||
clock-frequency = <1843200>;
|
||||
/* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
|
||||
interrupt-parent = <&cpu_intc>;
|
||||
interrupts = <4>;
|
||||
};
|
||||
|
||||
nor0: flash@be000000 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <0xbe000000 0x00400000>;
|
||||
};
|
||||
|
||||
// TODO: add graphics and ethernet adapter
|
||||
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
use crate::drivers::bus::pci;
|
||||
use alloc::string::String;
|
||||
use mips::registers::cp0;
|
||||
use once::*;
|
||||
|
||||
#[path = "../../../../drivers/console/mod.rs"]
|
||||
pub mod console;
|
||||
pub mod consts;
|
||||
#[path = "../../../../drivers/gpu/fb.rs"]
|
||||
pub mod fb;
|
||||
#[path = "../../../../drivers/serial/ti_16c550c.rs"]
|
||||
pub mod serial;
|
||||
#[path = "../../../../drivers/gpu/qemu_stdvga.rs"]
|
||||
pub mod vga;
|
||||
|
||||
use fb::FramebufferInfo;
|
||||
|
||||
/// Initialize serial port first
|
||||
pub fn init_serial_early() {
|
||||
assert_has_not_been_called!("board::init must be called only once");
|
||||
// initialize serial driver
|
||||
serial::init(0xbf000900);
|
||||
// Enable serial interrupt
|
||||
unsafe {
|
||||
let mut status = cp0::status::read();
|
||||
status.enable_hard_int2();
|
||||
cp0::status::write(status);
|
||||
}
|
||||
println!("Hello QEMU Malta!");
|
||||
}
|
||||
|
||||
/// Initialize other board drivers
|
||||
pub fn init_driver() {
|
||||
// TODO: add possibly more drivers
|
||||
vga::init(0xbbe00000, 0xb2050000, 800, 600);
|
||||
pci::init();
|
||||
fb::init();
|
||||
}
|
||||
|
||||
pub fn probe_fb_info(_width: u32, _height: u32, _depth: u32) -> fb::FramebufferResult {
|
||||
let fb_info = FramebufferInfo {
|
||||
xres: 800,
|
||||
yres: 600,
|
||||
xres_virtual: 800,
|
||||
yres_virtual: 600,
|
||||
xoffset: 0,
|
||||
yoffset: 0,
|
||||
depth: 8,
|
||||
pitch: 800,
|
||||
bus_addr: 0xb0000000,
|
||||
screen_size: 800 * 600,
|
||||
};
|
||||
Ok((fb_info, fb::ColorConfig::VgaPalette, 0xb0000000))
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
/// board specific constants
|
||||
pub const MEMORY_END: usize = 0x8800_0000;
|
||||
pub const KERNEL_HEAP_SIZE: usize = 0x00a0_0000;
|
@ -0,0 +1,36 @@
|
||||
/dts-v1/;
|
||||
|
||||
|
||||
/ {
|
||||
model = "qemu mipssim";
|
||||
compatible = "qemu,mipssim";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
chosen {
|
||||
stdio = &uart0;
|
||||
};
|
||||
|
||||
aliases { };
|
||||
|
||||
cpu_intc: interrupt-controller {
|
||||
compatible = "mti,cpu-interrupt-controller";
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
main_memory: memory@0 {
|
||||
device_type = "memory";
|
||||
reg = <0x00000000 0x10000000>;
|
||||
};
|
||||
|
||||
uart0: serial@bfd003f8 {
|
||||
compatible = "ns16550a";
|
||||
reg = <0xbfd003f8 0x8>;
|
||||
clock-frequency = <1843200>;
|
||||
/* attached to the MIPS CPU INT2 pin, ie interrupt 4 */
|
||||
interrupt-parent = <&cpu_intc>;
|
||||
interrupts = <4>;
|
||||
};
|
||||
|
||||
};
|
@ -0,0 +1,27 @@
|
||||
use alloc::string::String;
|
||||
use once::*;
|
||||
|
||||
#[path = "../../../../drivers/console/mod.rs"]
|
||||
pub mod console;
|
||||
pub mod consts;
|
||||
#[path = "../../../../drivers/gpu/fb.rs"]
|
||||
pub mod fb;
|
||||
#[path = "../../../../drivers/serial/16550_reg.rs"]
|
||||
pub mod serial;
|
||||
|
||||
/// Initialize serial port first
|
||||
pub fn init_serial_early() {
|
||||
assert_has_not_been_called!("board::init must be called only once");
|
||||
serial::init(0xbfd003f8);
|
||||
println!("Hello QEMU MIPSSIM!");
|
||||
}
|
||||
|
||||
/// Initialize other board drivers
|
||||
pub fn init_driver() {
|
||||
// TODO: add possibly more drivers
|
||||
// timer::init();
|
||||
}
|
||||
|
||||
pub fn probe_fb_info(_width: u32, _height: u32, _depth: u32) -> fb::FramebufferResult {
|
||||
Err(String::from("Framebuffer not usable on mipssim board"))
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
/// board specific constants
|
||||
pub const MEMORY_END: usize = 0x8080_0000;
|
||||
pub const KERNEL_HEAP_SIZE: usize = 0x0020_0000;
|
@ -0,0 +1,85 @@
|
||||
/dts-v1/;
|
||||
|
||||
|
||||
/ {
|
||||
model = "thinpad trivialmips";
|
||||
compatible = "tsinghua,thinpad";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
chosen {
|
||||
stdio = &uart;
|
||||
bootargs = "";
|
||||
};
|
||||
|
||||
aliases { };
|
||||
|
||||
cpu_intc: interrupt-controller {
|
||||
compatible = "mti,cpu-interrupt-controller";
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
memory: memory@80000000 {
|
||||
device_type = "memory";
|
||||
reg = <0x80000000 0x00800000>;
|
||||
};
|
||||
|
||||
bus: trivial_bus@a0000000 {
|
||||
compatible = "thinpad,bus";
|
||||
reg = <0xa0000000 0x800000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
flash: flash@a1000000 {
|
||||
compatible = "cfi-flash";
|
||||
reg = <0xa1000000 0x00800000>;
|
||||
};
|
||||
|
||||
framebuffer: framebuffer@a2000000 {
|
||||
compatible = "thinpad,framebuffer";
|
||||
reg = <0xa2000000 0x75300
|
||||
0xa2075300 0x4>;
|
||||
};
|
||||
|
||||
uart: serial@a3000000 {
|
||||
compatible = "thinpad,uart";
|
||||
reg = <0xa3000000 0x1
|
||||
0xa3000004 0x1>;
|
||||
clock-frequency = <115200>;
|
||||
interrupt-parent = <&cpu_intc>;
|
||||
interrupts = <1>;
|
||||
};
|
||||
|
||||
timer: gpio@a4000000 {
|
||||
compatible = "thinpad,timer";
|
||||
reg = <0xa400000 0x8>;
|
||||
};
|
||||
|
||||
eth: ethernet@a5000000 {
|
||||
compatible = "davicom,dm9000";
|
||||
reg = <0xa5000000 0x2
|
||||
0xa5000004 0x2>;
|
||||
interrupt-parent = <&cpu_intc>;
|
||||
interrupts = <2>;
|
||||
davicom,no-eeprom;
|
||||
mac-address = [00 0a 2d 98 01 29];
|
||||
};
|
||||
|
||||
gpio: gpio@a6000000 {
|
||||
compatible = "thinpad,gpio";
|
||||
reg = <0xa6000000 0x2
|
||||
0xa6000004 0x2
|
||||
0xa6000008 0x2>;
|
||||
reg-io-width = <2>;
|
||||
};
|
||||
|
||||
usb: usb@a7000000 {
|
||||
compatible = "cypress,sl811";
|
||||
reg = <0xa7000000 0x1
|
||||
0xa7000004 0x1>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
use alloc::string::String;
|
||||
use once::*;
|
||||
|
||||
#[path = "../../../../drivers/console/mod.rs"]
|
||||
pub mod console;
|
||||
pub mod consts;
|
||||
#[path = "../../../../drivers/gpu/fb.rs"]
|
||||
pub mod fb;
|
||||
#[path = "../../../../drivers/serial/simple_uart.rs"]
|
||||
pub mod serial;
|
||||
|
||||
use fb::FramebufferInfo;
|
||||
use fb::FramebufferResult;
|
||||
|
||||
/// Initialize serial port first
|
||||
pub fn init_serial_early() {
|
||||
assert_has_not_been_called!("board::init must be called only once");
|
||||
serial::init(0xa3000000);
|
||||
println!("Hello ThinPad!");
|
||||
}
|
||||
|
||||
/// Initialize other board drivers
|
||||
pub fn init_driver() {
|
||||
// TODO: add possibly more drivers
|
||||
// timer::init();
|
||||
}
|
||||
|
||||
pub fn probe_fb_info(width: u32, height: u32, depth: u32) -> FramebufferResult {
|
||||
let fb_info = FramebufferInfo {
|
||||
xres: 800,
|
||||
yres: 600,
|
||||
xres_virtual: 800,
|
||||
yres_virtual: 600,
|
||||
xoffset: 0,
|
||||
yoffset: 0,
|
||||
depth: 8,
|
||||
pitch: 800,
|
||||
bus_addr: 0xa2000000,
|
||||
screen_size: 800 * 600,
|
||||
};
|
||||
Ok((fb_info, fb::ColorConfig::RGB332, 0xa2000000))
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
#include "regdef.h"
|
||||
|
||||
.set noat
|
||||
.set noreorder
|
||||
|
||||
.section .text.context
|
||||
.globl switch_context
|
||||
.extern _root_page_table_ptr
|
||||
.extern _cur_kstack_ptr
|
||||
|
||||
switch_context:
|
||||
// save from's registers
|
||||
addi sp, sp, (-4*14)
|
||||
sw sp, 0(a0)
|
||||
sw ra, 0(sp)
|
||||
sw s0, 2*4(sp)
|
||||
sw s1, 3*4(sp)
|
||||
sw s2, 4*4(sp)
|
||||
sw s3, 5*4(sp)
|
||||
sw s4, 6*4(sp)
|
||||
sw s5, 7*4(sp)
|
||||
sw s6, 8*4(sp)
|
||||
sw s7, 9*4(sp)
|
||||
sw s8, 10*4(sp)
|
||||
sw gp, 11*4(sp)
|
||||
// sw ra, 12*4(sp)
|
||||
// sw sp, 13*4(sp)
|
||||
|
||||
// save page table address
|
||||
la s0, _root_page_table_ptr
|
||||
lw s1, 0(s0)
|
||||
sw s1, 4(sp)
|
||||
|
||||
// restore to's registers
|
||||
lw sp, 0(a1)
|
||||
lw s1, 4(sp)
|
||||
sw s1, 0(s0)
|
||||
|
||||
// restore kstack ptr
|
||||
// la s0, _cur_kstack_ptr
|
||||
// addi s1, sp, 4 * 14
|
||||
// sw s1, 0(s0)
|
||||
|
||||
lw ra, 0(sp)
|
||||
lw s0, 2*4(sp)
|
||||
lw s1, 3*4(sp)
|
||||
lw s2, 4*4(sp)
|
||||
lw s3, 5*4(sp)
|
||||
lw s4, 6*4(sp)
|
||||
lw s5, 7*4(sp)
|
||||
lw s6, 8*4(sp)
|
||||
lw s7, 9*4(sp)
|
||||
lw s8, 10*4(sp)
|
||||
lw gp, 11*4(sp)
|
||||
addi sp, sp, (4*14)
|
||||
|
||||
sw zero, 0(a1)
|
||||
jr ra
|
||||
nop
|
@ -0,0 +1,40 @@
|
||||
#include "regdef.h"
|
||||
|
||||
.set noreorder
|
||||
.section .text.entry
|
||||
.globl _start
|
||||
.extern _root_page_table_buffer
|
||||
.extern _cur_kstack_ptr
|
||||
|
||||
_start:
|
||||
# setup stack and gp
|
||||
la sp, bootstacktop
|
||||
la gp, _gp
|
||||
|
||||
la t0, _cur_kstack_ptr
|
||||
la t1, _root_page_table_buffer
|
||||
sw t1, 0(t0)
|
||||
|
||||
# set ebase
|
||||
la t0, trap_entry
|
||||
mfc0 t1, $15, 1 # C0_EBASE
|
||||
or t1, t1, t0
|
||||
mtc0 t1, $15, 1
|
||||
|
||||
# exit bootstrap mode
|
||||
mfc0 t0, $12 # C0_STATUS
|
||||
li t1, 0xFFBFFFFF # set BEV (bit 22) to 0
|
||||
and t0, t0, t1
|
||||
mtc0 t0, $12
|
||||
|
||||
# directly jump to main function
|
||||
jal rust_main
|
||||
nop
|
||||
|
||||
.section .bss.stack
|
||||
.align 12 #PGSHIFT
|
||||
.global bootstack
|
||||
bootstack:
|
||||
.space 4096 * 16 * 8
|
||||
.global bootstacktop
|
||||
bootstacktop:
|
@ -0,0 +1,50 @@
|
||||
/* Simple linker script for the ucore kernel.
|
||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
|
||||
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
BASE_ADDRESS = 0x80100000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
. = BASE_ADDRESS;
|
||||
start = .;
|
||||
|
||||
.text : {
|
||||
stext = .;
|
||||
*(.text.entry)
|
||||
. = ALIGN(4K);
|
||||
*(.text.ebase)
|
||||
*(.text .text.*)
|
||||
. = ALIGN(4K);
|
||||
etext = .;
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
srodata = .;
|
||||
*(.rodata .rodata.*)
|
||||
*(.dtb)
|
||||
. = ALIGN(4K);
|
||||
erodata = .;
|
||||
}
|
||||
|
||||
.data : {
|
||||
sdata = .;
|
||||
*(.data .data.*)
|
||||
edata = .;
|
||||
}
|
||||
|
||||
.stack : {
|
||||
*(.bss.stack)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
sbss = .;
|
||||
*(.bss .bss.*)
|
||||
ebss = .;
|
||||
}
|
||||
|
||||
PROVIDE(end = .);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 1985 MIPS Computer Systems, Inc.
|
||||
* Copyright (C) 1994, 95, 99, 2003 by Ralf Baechle
|
||||
* Copyright (C) 1990 - 1992, 1999 Silicon Graphics, Inc.
|
||||
*/
|
||||
#ifndef _ASM_REGDEF_H
|
||||
#define _ASM_REGDEF_H
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Symbolic register names for 32 bit ABI
|
||||
*/
|
||||
#define zero $0 /* wired zero */
|
||||
#define AT $1 /* assembler temp - uppercase because of ".set at" */
|
||||
#define v0 $2 /* return value */
|
||||
#define v1 $3
|
||||
#define a0 $4 /* argument registers */
|
||||
#define a1 $5
|
||||
#define a2 $6
|
||||
#define a3 $7
|
||||
#define t0 $8 /* caller saved */
|
||||
#define t1 $9
|
||||
#define t2 $10
|
||||
#define t3 $11
|
||||
#define t4 $12
|
||||
#define t5 $13
|
||||
#define t6 $14
|
||||
#define t7 $15
|
||||
#define s0 $16 /* callee saved */
|
||||
#define s1 $17
|
||||
#define s2 $18
|
||||
#define s3 $19
|
||||
#define s4 $20
|
||||
#define s5 $21
|
||||
#define s6 $22
|
||||
#define s7 $23
|
||||
#define t8 $24 /* caller saved */
|
||||
#define t9 $25
|
||||
#define jp $25 /* PIC jump register */
|
||||
#define k0 $26 /* kernel scratch */
|
||||
#define k1 $27
|
||||
#define gp $28 /* global pointer */
|
||||
#define sp $29 /* stack pointer */
|
||||
#define fp $30 /* frame pointer */
|
||||
#define s8 $30 /* same like fp! */
|
||||
#define ra $31 /* return address */
|
||||
|
||||
|
||||
|
||||
#endif /* _ASM_REGDEF_H */
|
@ -0,0 +1,179 @@
|
||||
#include "regdef.h"
|
||||
|
||||
.set noat
|
||||
.set noreorder
|
||||
.section .text.ebase
|
||||
.globl trap_entry
|
||||
|
||||
.org 0x0
|
||||
trap_entry:
|
||||
# +0x000: TLB-miss vector
|
||||
b general_trap_vec
|
||||
|
||||
# +0x180: general vector
|
||||
.org 0x180
|
||||
general_trap_vec:
|
||||
move k1, sp # save stack pointer to k1
|
||||
mfc0 k0, $12 # read cp0.status
|
||||
andi k0, k0, 0x10 # extract cp0.status.ksu
|
||||
beq k0, zero, trap_from_kernel
|
||||
nop # delayslot
|
||||
|
||||
trap_from_user:
|
||||
# load kstack, we can use k0 to store something
|
||||
# la k0, kernel_stack
|
||||
# la sp, kernel_stack_top
|
||||
la k0, _cur_kstack_ptr
|
||||
lw sp, 0(k0)
|
||||
|
||||
trap_from_kernel:
|
||||
/*
|
||||
* k0 is damaged
|
||||
* k1 = old stack pointer
|
||||
* sp = kernel stack */
|
||||
|
||||
# allocate 38 words for trapframe + 4 extra words
|
||||
addiu sp, sp, -168
|
||||
|
||||
# save general registers
|
||||
sw ra, 160(sp)
|
||||
sw fp, 156(sp)
|
||||
sw k1, 152(sp) # k1 = old sp
|
||||
sw gp, 148(sp)
|
||||
sw k1, 144(sp) # real k1 is damaged
|
||||
sw k0, 140(sp) # real k0 is damaged
|
||||
sw t9, 136(sp)
|
||||
sw t8, 132(sp)
|
||||
sw s7, 128(sp)
|
||||
sw s6, 124(sp)
|
||||
sw s5, 120(sp)
|
||||
sw s4, 116(sp)
|
||||
sw s3, 112(sp)
|
||||
sw s2, 108(sp)
|
||||
sw s1, 104(sp)
|
||||
sw s0, 100(sp)
|
||||
sw t7, 96(sp)
|
||||
sw t6, 92(sp)
|
||||
sw t5, 88(sp)
|
||||
sw t4, 84(sp)
|
||||
sw t3, 80(sp)
|
||||
sw t2, 76(sp)
|
||||
sw t1, 72(sp)
|
||||
sw t0, 68(sp)
|
||||
sw a3, 64(sp)
|
||||
sw a2, 60(sp)
|
||||
sw a1, 56(sp)
|
||||
sw a0, 52(sp)
|
||||
sw v1, 48(sp)
|
||||
sw v0, 44(sp)
|
||||
sw AT, 40(sp)
|
||||
nop
|
||||
|
||||
# save hi/lo
|
||||
mflo t1
|
||||
sw t1, 36(sp)
|
||||
mfhi t0
|
||||
sw t0, 32(sp)
|
||||
|
||||
# save special registers
|
||||
mfc0 t0, $8 # cp0.vaddr
|
||||
sw t0, 28(sp)
|
||||
|
||||
mfc0 t1, $14 # cp0.epc
|
||||
sw t1, 24(sp)
|
||||
|
||||
mfc0 t0, $13 # cp0.cause
|
||||
sw t0, 20(sp)
|
||||
|
||||
mfc0 t1, $12 # cp0.status
|
||||
sw t1, 16(sp)
|
||||
|
||||
# support nested interrupt
|
||||
la t0, ~0x1b # reset status.ksu, status.exl, status.ie
|
||||
and t1, t1, t0
|
||||
mtc0 t1, $12 # cp0.status
|
||||
|
||||
# prepare to call rust_trap
|
||||
ori a0, sp, 0 /* set argument (trapframe) */
|
||||
jal rust_trap
|
||||
nop
|
||||
|
||||
.globl trap_return
|
||||
trap_return:
|
||||
# restore special registers
|
||||
lw t1, 16(sp)
|
||||
ori t1, t1, 0x2 # status.exl
|
||||
nop
|
||||
mtc0 t1, $12 # cp0.status
|
||||
|
||||
lw k0, 24(sp)
|
||||
mtc0 k0, $14 # cp0.epc
|
||||
|
||||
lw t0, 32(sp)
|
||||
mthi t0
|
||||
lw t1, 36(sp)
|
||||
mtlo t1
|
||||
|
||||
# restore general registers
|
||||
lw AT, 40(sp)
|
||||
lw v0, 44(sp)
|
||||
lw v1, 48(sp)
|
||||
lw a0, 52(sp)
|
||||
lw a1, 56(sp)
|
||||
lw a2, 60(sp)
|
||||
lw a3, 64(sp)
|
||||
lw t0, 68(sp)
|
||||
lw t1, 72(sp)
|
||||
lw t2, 76(sp)
|
||||
lw t3, 80(sp)
|
||||
lw t4, 84(sp)
|
||||
lw t5, 88(sp)
|
||||
lw t6, 92(sp)
|
||||
lw t7, 96(sp)
|
||||
lw s0, 100(sp)
|
||||
lw s1, 104(sp)
|
||||
lw s2, 108(sp)
|
||||
lw s3, 112(sp)
|
||||
lw s4, 116(sp)
|
||||
lw s5, 120(sp)
|
||||
lw s6, 124(sp)
|
||||
lw s7, 128(sp)
|
||||
lw t8, 132(sp)
|
||||
lw t9, 136(sp)
|
||||
|
||||
# lw k0, 140(sp)
|
||||
# lw k1, 144(sp)
|
||||
lw gp, 148(sp)
|
||||
lw fp, 156(sp)
|
||||
lw ra, 160(sp)
|
||||
|
||||
// save kernel stack
|
||||
la k0, _cur_kstack_ptr
|
||||
addiu k1, sp, 168
|
||||
sw k1, 0(k0)
|
||||
nop
|
||||
|
||||
// restore stack
|
||||
lw sp, 152(sp)
|
||||
|
||||
eret
|
||||
nop
|
||||
|
||||
.section .bss.stack
|
||||
.align 12 #PGSHIFT
|
||||
.global kernel_stack
|
||||
kernel_stack:
|
||||
.space 1024 * 16 # 16KB for kernel stack
|
||||
.global kernel_stack_top
|
||||
kernel_stack_top:
|
||||
|
||||
.align 12 #PGSHIFT
|
||||
.global _root_page_table_buffer
|
||||
_root_page_table_buffer:
|
||||
.space 1024 * 64 # 64KB
|
||||
.global _root_page_table_ptr
|
||||
_root_page_table_ptr:
|
||||
.space 4 # 4bytes
|
||||
.global _cur_kstack_ptr
|
||||
_cur_kstack_ptr:
|
||||
.space 4 # 4bytes
|
@ -0,0 +1,8 @@
|
||||
//! Workaround for missing compiler-builtin symbols
|
||||
//!
|
||||
//! [atomic](http://llvm.org/docs/Atomics.html#libcalls-atomic)
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn abort() {
|
||||
panic!("abort");
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
/// Platform specific constants
|
||||
///
|
||||
pub use super::board::consts::*;
|
||||
|
||||
pub const KERNEL_OFFSET: usize = 0x80100000;
|
||||
|
||||
pub const MEMORY_OFFSET: usize = 0x8000_0000;
|
||||
|
||||
pub const USER_STACK_OFFSET: usize = 0x80000000 - USER_STACK_SIZE;
|
||||
pub const USER_STACK_SIZE: usize = 0x10000;
|
||||
pub const USER32_STACK_OFFSET: usize = 0x80000000 - USER_STACK_SIZE;
|
||||
|
||||
pub const MAX_DTB_SIZE: usize = 0x2000;
|
@ -0,0 +1,273 @@
|
||||
use mips::registers::cp0;
|
||||
use mips::tlb;
|
||||
|
||||
/// Saved registers on a trap.
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
pub struct TrapFrame {
|
||||
/// unused 16 bytes
|
||||
pub unused: [usize; 4],
|
||||
/// CP0 status register
|
||||
pub status: cp0::status::Status,
|
||||
/// CP0 cause register
|
||||
pub cause: cp0::cause::Cause,
|
||||
/// CP0 EPC register
|
||||
pub epc: usize,
|
||||
/// CP0 vaddr register
|
||||
pub vaddr: usize,
|
||||
/// HI/LO registers
|
||||
pub hi: usize,
|
||||
pub lo: usize,
|
||||
/// General registers
|
||||
pub at: usize,
|
||||
pub v0: usize,
|
||||
pub v1: usize,
|
||||
pub a0: usize,
|
||||
pub a1: usize,
|
||||
pub a2: usize,
|
||||
pub a3: usize,
|
||||
pub t0: usize,
|
||||
pub t1: usize,
|
||||
pub t2: usize,
|
||||
pub t3: usize,
|
||||
pub t4: usize,
|
||||
pub t5: usize,
|
||||
pub t6: usize,
|
||||
pub t7: usize,
|
||||
pub s0: usize,
|
||||
pub s1: usize,
|
||||
pub s2: usize,
|
||||
pub s3: usize,
|
||||
pub s4: usize,
|
||||
pub s5: usize,
|
||||
pub s6: usize,
|
||||
pub s7: usize,
|
||||
pub t8: usize,
|
||||
pub t9: usize,
|
||||
pub k0: usize,
|
||||
pub k1: usize,
|
||||
pub gp: usize,
|
||||
pub sp: usize,
|
||||
pub fp: usize,
|
||||
pub ra: usize,
|
||||
/// Reserve space for hartid
|
||||
pub _hartid: usize,
|
||||
}
|
||||
|
||||
impl TrapFrame {
|
||||
/// Constructs TrapFrame for a new kernel thread.
|
||||
///
|
||||
/// The new thread starts at function `entry` with an usize argument `arg`.
|
||||
/// The stack pointer will be set to `sp`.
|
||||
fn new_kernel_thread(entry: extern "C" fn(usize) -> !, arg: usize, sp: usize) -> Self {
|
||||
use core::mem::zeroed;
|
||||
let mut tf: Self = unsafe { zeroed() };
|
||||
tf.a0 = arg;
|
||||
tf.sp = sp;
|
||||
tf.epc = entry as usize;
|
||||
tf.status = cp0::status::read();
|
||||
tf.status.set_kernel_mode();
|
||||
tf.status.set_ie();
|
||||
tf.status.set_exl();
|
||||
tf
|
||||
}
|
||||
|
||||
/// Constructs TrapFrame for a new user thread.
|
||||
///
|
||||
/// The new thread starts at `entry_addr`.
|
||||
/// The stack pointer will be set to `sp`.
|
||||
fn new_user_thread(entry_addr: usize, sp: usize) -> Self {
|
||||
use core::mem::zeroed;
|
||||
let mut tf: Self = unsafe { zeroed() };
|
||||
tf.sp = sp;
|
||||
tf.epc = entry_addr;
|
||||
tf.status = cp0::status::read();
|
||||
tf.status.set_user_mode();
|
||||
tf.status.set_ie();
|
||||
tf.status.set_exl();
|
||||
tf
|
||||
}
|
||||
}
|
||||
|
||||
use core::fmt::{Debug, Error, Formatter};
|
||||
impl Debug for TrapFrame {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
f.debug_struct("TrapFrame")
|
||||
.field("status", &self.status.bits)
|
||||
.field("epc", &self.epc)
|
||||
.field("cause", &self.cause.bits)
|
||||
.field("vaddr", &self.vaddr)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Kernel stack contents for a new thread
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct InitStack {
|
||||
context: ContextData,
|
||||
tf: TrapFrame,
|
||||
}
|
||||
|
||||
impl InitStack {
|
||||
/// Push the InitStack on the stack and transfer to a Context.
|
||||
unsafe fn push_at(self, stack_top: usize) -> Context {
|
||||
let ptr = (stack_top as *mut Self).sub(1); //real kernel stack top
|
||||
*ptr = self;
|
||||
Context { sp: ptr as usize }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn trap_return();
|
||||
}
|
||||
|
||||
/// Saved registers for kernel context switches.
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
struct ContextData {
|
||||
/// Return address
|
||||
ra: usize,
|
||||
/// Page table token
|
||||
satp: usize,
|
||||
/// Callee-saved registers
|
||||
s: [usize; 12],
|
||||
}
|
||||
|
||||
impl ContextData {
|
||||
fn new(satp: usize) -> Self {
|
||||
ContextData {
|
||||
ra: trap_return as usize,
|
||||
satp,
|
||||
..ContextData::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context of a kernel thread.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
/// The stack pointer of the suspended thread.
|
||||
/// A `ContextData` is stored here.
|
||||
sp: usize,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Switch to another kernel thread.
|
||||
///
|
||||
/// Push all callee-saved registers at the current kernel stack.
|
||||
/// Store current sp, switch to target.
|
||||
/// Pop all callee-saved registers, then return to the target.
|
||||
#[inline(always)]
|
||||
pub unsafe fn switch(&mut self, target: &mut Self) {
|
||||
extern "C" {
|
||||
fn switch_context(src: *mut Context, dst: *mut Context);
|
||||
}
|
||||
|
||||
tlb::clear_all_tlb();
|
||||
switch_context(self as *mut Context, target as *mut Context);
|
||||
}
|
||||
|
||||
/// Constructs a null Context for the current running thread.
|
||||
pub unsafe fn null() -> Self {
|
||||
Context { sp: 0 }
|
||||
}
|
||||
|
||||
/// Constructs Context for a new kernel thread.
|
||||
///
|
||||
/// The new thread starts at function `entry` with an usize argument `arg`.
|
||||
/// The stack pointer will be set to `kstack_top`.
|
||||
/// The SATP register will be set to `satp`.
|
||||
pub unsafe fn new_kernel_thread(
|
||||
entry: extern "C" fn(usize) -> !,
|
||||
arg: usize,
|
||||
kstack_top: usize,
|
||||
satp: usize,
|
||||
) -> Self {
|
||||
info!(
|
||||
"New kernel thread @ {:x}, stack = {:x}",
|
||||
entry as usize, kstack_top
|
||||
);
|
||||
|
||||
InitStack {
|
||||
context: ContextData::new(satp),
|
||||
tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top),
|
||||
}
|
||||
.push_at(kstack_top)
|
||||
}
|
||||
|
||||
/// Constructs Context for a new user thread.
|
||||
///
|
||||
/// The new thread starts at `entry_addr`.
|
||||
/// The stack pointer of user and kernel mode will be set to `ustack_top`, `kstack_top`.
|
||||
/// The SATP register will be set to `satp`.
|
||||
pub unsafe fn new_user_thread(
|
||||
entry_addr: usize,
|
||||
ustack_top: usize,
|
||||
kstack_top: usize,
|
||||
_is32: bool,
|
||||
satp: usize,
|
||||
) -> Self {
|
||||
info!(
|
||||
"New user thread @ {:x}, stack = {:x}",
|
||||
entry_addr, kstack_top
|
||||
);
|
||||
|
||||
InitStack {
|
||||
context: ContextData::new(satp),
|
||||
tf: TrapFrame::new_user_thread(entry_addr, ustack_top),
|
||||
}
|
||||
.push_at(kstack_top)
|
||||
}
|
||||
|
||||
/// Fork a user process and get the new Context.
|
||||
///
|
||||
/// The stack pointer in kernel mode will be set to `kstack_top`.
|
||||
/// The SATP register will be set to `satp`.
|
||||
/// All the other registers are same as the original.
|
||||
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, satp: usize) -> Self {
|
||||
InitStack {
|
||||
context: ContextData::new(satp),
|
||||
tf: {
|
||||
let mut tf = tf.clone();
|
||||
// fork function's ret value, the new process is 0
|
||||
tf.v0 = 0;
|
||||
tf
|
||||
},
|
||||
}
|
||||
.push_at(kstack_top)
|
||||
}
|
||||
|
||||
/// Fork a user thread and get the new Context.
|
||||
///
|
||||
/// The stack pointer in kernel mode will be set to `kstack_top`.
|
||||
/// The SATP register will be set to `satp`.
|
||||
/// The new user stack will be set to `ustack_top`.
|
||||
/// The new thread pointer will be set to `tls`.
|
||||
/// All the other registers are same as the original.
|
||||
pub unsafe fn new_clone(
|
||||
tf: &TrapFrame,
|
||||
ustack_top: usize,
|
||||
kstack_top: usize,
|
||||
satp: usize,
|
||||
tls: usize,
|
||||
) -> Self {
|
||||
InitStack {
|
||||
context: ContextData::new(satp),
|
||||
tf: {
|
||||
let mut tf = tf.clone();
|
||||
tf.sp = ustack_top; // sp
|
||||
tf.v1 = tls;
|
||||
tf.v0 = 0; // return value
|
||||
tf
|
||||
},
|
||||
}
|
||||
.push_at(kstack_top)
|
||||
}
|
||||
|
||||
/// Used for getting the init TrapFrame of a new user context in `sys_exec`.
|
||||
pub unsafe fn get_init_tf(&self) -> TrapFrame {
|
||||
(*(self.sp as *const InitStack)).tf.clone()
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
use crate::consts::MAX_CPU_NUM;
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
use mips::registers::cp0;
|
||||
|
||||
static mut STARTED: [bool; MAX_CPU_NUM] = [false; MAX_CPU_NUM];
|
||||
|
||||
pub fn id() -> usize {
|
||||
(cp0::ebase::read_u32() as usize) & 0x3ff
|
||||
}
|
||||
|
||||
pub unsafe fn has_started(cpu_id: usize) -> bool {
|
||||
read_volatile(&STARTED[cpu_id])
|
||||
}
|
||||
|
||||
pub unsafe fn start_others(hart_mask: usize) {
|
||||
for cpu_id in 0..MAX_CPU_NUM {
|
||||
if (hart_mask >> cpu_id) & 1 != 0 {
|
||||
write_volatile(&mut STARTED[cpu_id], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn halt() {
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
pub unsafe fn exit_in_qemu(error_code: u8) -> ! {
|
||||
/* nothing to do */
|
||||
loop {}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
//! mipsel drivers
|
||||
|
||||
use super::board;
|
||||
use once::*;
|
||||
|
||||
pub use self::board::fb;
|
||||
pub use self::board::serial;
|
||||
#[path = "../../../drivers/console/mod.rs"]
|
||||
pub mod console;
|
||||
|
||||
/// Initialize common drivers
|
||||
pub fn init() {
|
||||
assert_has_not_been_called!("driver::init must be called only once");
|
||||
board::init_driver();
|
||||
console::init();
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
pub use self::context::*;
|
||||
use crate::arch::paging::get_root_page_table_ptr;
|
||||
use crate::drivers::DRIVERS;
|
||||
use log::*;
|
||||
use mips::addr::*;
|
||||
use mips::interrupts;
|
||||
use mips::paging::{
|
||||
PageTable as MIPSPageTable, PageTableEntry, PageTableFlags as EF, TwoLevelPageTable,
|
||||
};
|
||||
use mips::registers::cp0;
|
||||
use mips::tlb;
|
||||
|
||||
#[path = "context.rs"]
|
||||
mod context;
|
||||
|
||||
/// Initialize interrupt
|
||||
pub fn init() {
|
||||
extern "C" {
|
||||
fn trap_entry();
|
||||
}
|
||||
unsafe {
|
||||
// Set the exception vector address
|
||||
cp0::ebase::write_u32(trap_entry as u32);
|
||||
println!("Set ebase = {:x}", trap_entry as u32);
|
||||
|
||||
let mut status = cp0::status::read();
|
||||
// Enable IPI
|
||||
status.enable_soft_int0();
|
||||
status.enable_soft_int1();
|
||||
// Enable clock interrupt
|
||||
status.enable_hard_int5();
|
||||
|
||||
cp0::status::write(status);
|
||||
}
|
||||
info!("interrupt: init end");
|
||||
}
|
||||
|
||||
/// Enable interrupt
|
||||
#[inline]
|
||||
pub unsafe fn enable() {
|
||||
interrupts::enable();
|
||||
}
|
||||
|
||||
/// Disable interrupt and return current interrupt status
|
||||
#[inline]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
let e = cp0::status::read_u32() & 1;
|
||||
interrupts::disable();
|
||||
e as usize
|
||||
}
|
||||
|
||||
/// Enable interrupt if `flags` != 0
|
||||
#[inline]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
if flags != 0 {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch and handle interrupt.
|
||||
///
|
||||
/// This function is called from `trap.asm`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_trap(tf: &mut TrapFrame) {
|
||||
use cp0::cause::Exception as E;
|
||||
trace!("Exception @ CPU{}: {:?} ", 0, tf.cause.cause());
|
||||
match tf.cause.cause() {
|
||||
E::Interrupt => interrupt_dispatcher(tf),
|
||||
E::Syscall => syscall(tf),
|
||||
E::TLBModification => page_fault(tf),
|
||||
E::TLBLoadMiss => page_fault(tf),
|
||||
E::TLBStoreMiss => page_fault(tf),
|
||||
_ => {
|
||||
error!("Unhandled Exception @ CPU{}: {:?} ", 0, tf.cause.cause());
|
||||
crate::trap::error(tf)
|
||||
}
|
||||
}
|
||||
trace!("Interrupt end");
|
||||
}
|
||||
|
||||
fn interrupt_dispatcher(tf: &mut TrapFrame) {
|
||||
let pint = tf.cause.pending_interrupt();
|
||||
trace!(" Interrupt {:08b} ", pint);
|
||||
if (pint & 0b100_000_00) != 0 {
|
||||
timer();
|
||||
} else if (pint & 0b011_111_00) != 0 {
|
||||
external();
|
||||
} else {
|
||||
ipi();
|
||||
}
|
||||
}
|
||||
|
||||
fn external() {
|
||||
// true means handled, false otherwise
|
||||
let handlers = [try_process_serial, try_process_drivers];
|
||||
for handler in handlers.iter() {
|
||||
if handler() == true {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_process_serial() -> bool {
|
||||
match super::io::getchar_option() {
|
||||
Some(ch) => {
|
||||
trace!("Get char {} from serial", ch);
|
||||
crate::trap::serial(ch);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_process_drivers() -> bool {
|
||||
// TODO
|
||||
for driver in DRIVERS.read().iter() {
|
||||
if driver.try_handle_interrupt(None) == true {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn ipi() {
|
||||
debug!("IPI");
|
||||
cp0::cause::reset_soft_int0();
|
||||
cp0::cause::reset_soft_int1();
|
||||
}
|
||||
|
||||
fn timer() {
|
||||
super::timer::set_next();
|
||||
crate::trap::timer();
|
||||
}
|
||||
|
||||
fn syscall(tf: &mut TrapFrame) {
|
||||
tf.epc += 4; // Must before syscall, because of fork.
|
||||
let arguments = [tf.a0, tf.a1, tf.a2, tf.a3, tf.t0, tf.t1];
|
||||
trace!("MIPS syscall {} invoked with {:?}", tf.v0, arguments);
|
||||
|
||||
let ret = crate::syscall::syscall(tf.v0, arguments, tf) as isize;
|
||||
// comply with mips n32 abi, always return a positive value
|
||||
// https://git.musl-libc.org/cgit/musl/tree/arch/mipsn32/syscall_arch.h
|
||||
if (ret < 0) {
|
||||
tf.v0 = (-ret) as usize;
|
||||
tf.a3 = 1;
|
||||
} else {
|
||||
tf.v0 = ret as usize;
|
||||
tf.a3 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn page_fault(tf: &mut TrapFrame) {
|
||||
// TODO: set access/dirty bit
|
||||
let addr = tf.vaddr;
|
||||
trace!("\nEXCEPTION: Page Fault @ {:#x}", addr);
|
||||
|
||||
let virt_addr = VirtAddr::new(addr);
|
||||
let root_table = unsafe { &mut *(get_root_page_table_ptr() as *mut MIPSPageTable) };
|
||||
let tlb_result = root_table.lookup(addr);
|
||||
match tlb_result {
|
||||
Ok(tlb_entry) => {
|
||||
trace!(
|
||||
"PhysAddr = {:x}/{:x}",
|
||||
tlb_entry.entry_lo0.get_pfn() << 12,
|
||||
tlb_entry.entry_lo1.get_pfn() << 12
|
||||
);
|
||||
tlb::write_tlb_random(tlb_entry)
|
||||
}
|
||||
Err(()) => {
|
||||
if !crate::memory::handle_page_fault(addr) {
|
||||
crate::trap::error(tf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
//! Input/output for mipsel.
|
||||
|
||||
use super::driver::console::CONSOLE;
|
||||
use super::driver::serial::*;
|
||||
use core::fmt::{Arguments, Write};
|
||||
|
||||
pub fn getchar() -> char {
|
||||
unsafe { SERIAL_PORT.force_unlock() }
|
||||
SERIAL_PORT.lock().getchar()
|
||||
}
|
||||
|
||||
pub fn getchar_option() -> Option<char> {
|
||||
unsafe { SERIAL_PORT.force_unlock() }
|
||||
SERIAL_PORT.lock().getchar_option()
|
||||
}
|
||||
|
||||
pub fn putfmt(fmt: Arguments) {
|
||||
unsafe { SERIAL_PORT.force_unlock() }
|
||||
SERIAL_PORT.lock().write_fmt(fmt).unwrap();
|
||||
|
||||
unsafe { CONSOLE.force_unlock() }
|
||||
if let Some(console) = CONSOLE.lock().as_mut() {
|
||||
console.write_fmt(fmt).unwrap();
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
use crate::arch::paging::*;
|
||||
use crate::consts::{KERNEL_OFFSET, MEMORY_END, MEMORY_OFFSET};
|
||||
use crate::memory::{init_heap, Linear, MemoryAttr, MemorySet, FRAME_ALLOCATOR};
|
||||
use core::mem;
|
||||
use log::*;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
|
||||
/// Initialize the memory management module
|
||||
pub fn init() {
|
||||
// initialize heap and Frame allocator
|
||||
init_frame_allocator();
|
||||
init_heap();
|
||||
|
||||
set_root_page_table_ptr(0xFFFF_FFFF);
|
||||
extern "C" {
|
||||
fn _root_page_table_buffer();
|
||||
fn _root_page_table_ptr();
|
||||
}
|
||||
|
||||
println!("_root_page_table_ptr {:x}", _root_page_table_ptr as usize);
|
||||
}
|
||||
|
||||
pub fn init_other() {
|
||||
// TODO: init other CPU cores
|
||||
}
|
||||
|
||||
fn init_frame_allocator() {
|
||||
use bitmap_allocator::BitAlloc;
|
||||
use core::ops::Range;
|
||||
|
||||
let mut ba = FRAME_ALLOCATOR.lock();
|
||||
let range = to_range(
|
||||
(end as usize) - KERNEL_OFFSET + MEMORY_OFFSET + PAGE_SIZE,
|
||||
MEMORY_END,
|
||||
);
|
||||
ba.insert(range);
|
||||
|
||||
info!("frame allocator: init end");
|
||||
|
||||
/// Transform memory area `[start, end)` to integer range for `FrameAllocator`
|
||||
fn to_range(start: usize, end: usize) -> Range<usize> {
|
||||
let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE;
|
||||
let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1;
|
||||
assert!(page_start < page_end, "illegal range for frame allocator");
|
||||
page_start..page_end
|
||||
}
|
||||
}
|
||||
|
||||
// First core stores its SATP here.
|
||||
// Other cores load it later.
|
||||
static mut SATP: usize = 0;
|
||||
|
||||
pub unsafe fn clear_bss() {
|
||||
let start = sbss as usize;
|
||||
let end = ebss as usize;
|
||||
let step = core::mem::size_of::<usize>();
|
||||
for i in (start..end).step_by(step) {
|
||||
(i as *mut usize).write(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Symbols provided by linker script
|
||||
#[allow(dead_code)]
|
||||
extern "C" {
|
||||
fn stext();
|
||||
fn etext();
|
||||
fn sdata();
|
||||
fn edata();
|
||||
fn srodata();
|
||||
fn erodata();
|
||||
fn sbss();
|
||||
fn ebss();
|
||||
fn start();
|
||||
fn end();
|
||||
fn bootstack();
|
||||
fn bootstacktop();
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
pub mod compiler_rt;
|
||||
pub mod consts;
|
||||
pub mod cpu;
|
||||
pub mod driver;
|
||||
pub mod interrupt;
|
||||
pub mod io;
|
||||
pub mod memory;
|
||||
pub mod paging;
|
||||
pub mod rand;
|
||||
pub mod syscall;
|
||||
pub mod timer;
|
||||
|
||||
use log::*;
|
||||
use mips::registers::cp0;
|
||||
|
||||
#[cfg(feature = "board_malta")]
|
||||
#[path = "board/malta/mod.rs"]
|
||||
pub mod board;
|
||||
|
||||
#[cfg(feature = "board_thinpad")]
|
||||
#[path = "board/thinpad/mod.rs"]
|
||||
pub mod board;
|
||||
|
||||
#[cfg(feature = "board_mipssim")]
|
||||
#[path = "board/mipssim/mod.rs"]
|
||||
pub mod board;
|
||||
|
||||
extern "C" {
|
||||
fn _dtb_start();
|
||||
fn _dtb_end();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> ! {
|
||||
// unsafe { cpu::set_cpu_id(hartid); }
|
||||
|
||||
let ebase = cp0::ebase::read_u32();
|
||||
let cpu_id = ebase & 0x3ff;
|
||||
let dtb_start = _dtb_start as usize;
|
||||
|
||||
if cpu_id != BOOT_CPU_ID {
|
||||
// TODO: run others_main on other CPU
|
||||
// while unsafe { !cpu::has_started(hartid) } { }
|
||||
// println!("Hello RISCV! in hart {}, dtb @ {:#x}", hartid, dtb);
|
||||
// others_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
memory::clear_bss();
|
||||
}
|
||||
|
||||
board::init_serial_early();
|
||||
crate::logging::init();
|
||||
|
||||
interrupt::init();
|
||||
memory::init();
|
||||
timer::init();
|
||||
driver::init();
|
||||
|
||||
println!("Hello MIPS 32 from CPU {}, dtb @ {:#x}", cpu_id, dtb_start);
|
||||
|
||||
crate::drivers::init(dtb_start);
|
||||
crate::process::init();
|
||||
|
||||
// TODO: start other CPU
|
||||
// unsafe { cpu::start_others(hart_mask); }
|
||||
crate::kmain();
|
||||
}
|
||||
|
||||
fn others_main() -> ! {
|
||||
interrupt::init();
|
||||
memory::init_other();
|
||||
timer::init();
|
||||
crate::kmain();
|
||||
}
|
||||
|
||||
const BOOT_CPU_ID: u32 = 0;
|
||||
|
||||
global_asm!(include_str!("boot/context.gen.s"));
|
||||
global_asm!(include_str!("boot/entry.gen.s"));
|
||||
global_asm!(include_str!("boot/trap.gen.s"));
|
||||
global_asm!(include_str!("boot/dtb.gen.s"));
|
@ -0,0 +1,232 @@
|
||||
// Depends on kernel
|
||||
use crate::memory::{active_table, alloc_frame, dealloc_frame};
|
||||
use log::*;
|
||||
use mips::addr::*;
|
||||
use mips::paging::{FrameAllocator, FrameDeallocator};
|
||||
use mips::paging::{
|
||||
Mapper, PageTable as MIPSPageTable, PageTableEntry, PageTableFlags as EF, TwoLevelPageTable,
|
||||
};
|
||||
use mips::tlb::*;
|
||||
use rcore_memory::paging::*;
|
||||
|
||||
pub struct ActivePageTable(TwoLevelPageTable<'static>, PageEntry);
|
||||
|
||||
/// PageTableEntry: the contents of this entry.
|
||||
/// Page: this entry is the pte of page `Page`.
|
||||
pub struct PageEntry(&'static mut PageTableEntry, Page);
|
||||
|
||||
impl PageTable for ActivePageTable {
|
||||
fn map(&mut self, addr: usize, target: usize) -> &mut Entry {
|
||||
// map the 4K `page` to the 4K `frame` with `flags`
|
||||
let flags = EF::VALID | EF::WRITABLE | EF::CACHEABLE;
|
||||
let page = Page::of_addr(VirtAddr::new(addr));
|
||||
let frame = Frame::of_addr(PhysAddr::new(target));
|
||||
// map the page to the frame using FrameAllocatorForRiscv
|
||||
// we may need frame allocator to alloc frame for new page table(first/second)
|
||||
self.0
|
||||
.map_to(page, frame, flags, &mut FrameAllocatorForRiscv)
|
||||
.unwrap()
|
||||
.flush();
|
||||
self.get_entry(addr).expect("fail to get entry")
|
||||
}
|
||||
|
||||
fn unmap(&mut self, addr: usize) {
|
||||
let page = Page::of_addr(VirtAddr::new(addr));
|
||||
let (_, flush) = self.0.unmap(page).unwrap();
|
||||
flush.flush();
|
||||
}
|
||||
|
||||
fn get_entry(&mut self, vaddr: usize) -> Option<&mut Entry> {
|
||||
let page = Page::of_addr(VirtAddr::new(vaddr));
|
||||
if let Ok(e) = self.0.ref_entry(page.clone()) {
|
||||
let e = unsafe { &mut *(e as *mut PageTableEntry) };
|
||||
self.1 = PageEntry(e, page);
|
||||
Some(&mut self.1 as &mut Entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn _root_page_table_buffer();
|
||||
fn _root_page_table_ptr();
|
||||
}
|
||||
|
||||
pub fn set_root_page_table_ptr(ptr: usize) {
|
||||
unsafe {
|
||||
clear_all_tlb();
|
||||
*(_root_page_table_ptr as *mut usize) = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root_page_table_ptr() -> usize {
|
||||
unsafe { *(_root_page_table_ptr as *mut usize) }
|
||||
}
|
||||
|
||||
pub fn root_page_table_buffer() -> &'static mut MIPSPageTable {
|
||||
unsafe { &mut *(_root_page_table_ptr as *mut MIPSPageTable) }
|
||||
}
|
||||
|
||||
impl PageTableExt for ActivePageTable {}
|
||||
|
||||
/// The virtual address of root page table
|
||||
|
||||
impl ActivePageTable {
|
||||
pub unsafe fn new() -> Self {
|
||||
ActivePageTable(
|
||||
TwoLevelPageTable::new(root_page_table_buffer()),
|
||||
::core::mem::uninitialized(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// implementation for the Entry trait in /crate/memory/src/paging/mod.rs
|
||||
impl Entry for PageEntry {
|
||||
fn update(&mut self) {
|
||||
unsafe {
|
||||
clear_all_tlb();
|
||||
}
|
||||
}
|
||||
fn accessed(&self) -> bool {
|
||||
self.0.flags().contains(EF::ACCESSED)
|
||||
}
|
||||
fn dirty(&self) -> bool {
|
||||
self.0.flags().contains(EF::DIRTY)
|
||||
}
|
||||
fn writable(&self) -> bool {
|
||||
self.0.flags().contains(EF::WRITABLE)
|
||||
}
|
||||
fn present(&self) -> bool {
|
||||
self.0.flags().contains(EF::VALID)
|
||||
}
|
||||
fn clear_accessed(&mut self) {
|
||||
self.0.flags_mut().remove(EF::ACCESSED);
|
||||
}
|
||||
fn clear_dirty(&mut self) {
|
||||
self.0.flags_mut().remove(EF::DIRTY);
|
||||
}
|
||||
fn set_writable(&mut self, value: bool) {
|
||||
self.0.flags_mut().set(EF::WRITABLE, value);
|
||||
}
|
||||
fn set_present(&mut self, value: bool) {
|
||||
self.0.flags_mut().set(EF::VALID, value);
|
||||
}
|
||||
fn target(&self) -> usize {
|
||||
self.0.addr().as_usize()
|
||||
}
|
||||
fn set_target(&mut self, target: usize) {
|
||||
let flags = self.0.flags();
|
||||
let frame = Frame::of_addr(PhysAddr::new(target));
|
||||
self.0.set(frame, flags);
|
||||
}
|
||||
fn writable_shared(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn readonly_shared(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn set_shared(&mut self, writable: bool) {}
|
||||
fn clear_shared(&mut self) {}
|
||||
fn swapped(&self) -> bool {
|
||||
self.0.flags().contains(EF::RESERVED1)
|
||||
}
|
||||
fn set_swapped(&mut self, value: bool) {
|
||||
self.0.flags_mut().set(EF::RESERVED1, value);
|
||||
}
|
||||
fn user(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn set_user(&mut self, value: bool) {}
|
||||
fn execute(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn set_execute(&mut self, value: bool) {}
|
||||
fn mmio(&self) -> u8 {
|
||||
0
|
||||
}
|
||||
fn set_mmio(&mut self, _value: u8) {}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InactivePageTable0 {
|
||||
root_frame: Frame,
|
||||
}
|
||||
|
||||
impl InactivePageTable for InactivePageTable0 {
|
||||
type Active = ActivePageTable;
|
||||
|
||||
fn new_bare() -> Self {
|
||||
let target = alloc_frame().expect("failed to allocate frame");
|
||||
let frame = Frame::of_addr(PhysAddr::new(target));
|
||||
|
||||
let table = unsafe { &mut *(target as *mut MIPSPageTable) };
|
||||
|
||||
table.zero();
|
||||
InactivePageTable0 { root_frame: frame }
|
||||
}
|
||||
|
||||
fn map_kernel(&mut self) {
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
fn token(&self) -> usize {
|
||||
self.root_frame.to_kernel_unmapped().as_usize()
|
||||
}
|
||||
|
||||
unsafe fn set_token(token: usize) {
|
||||
set_root_page_table_ptr(token);
|
||||
}
|
||||
|
||||
fn active_token() -> usize {
|
||||
get_root_page_table_ptr()
|
||||
}
|
||||
|
||||
fn flush_tlb() {
|
||||
unsafe {
|
||||
clear_all_tlb();
|
||||
}
|
||||
}
|
||||
|
||||
fn edit<T>(&mut self, f: impl FnOnce(&mut Self::Active) -> T) -> T {
|
||||
let pt: *mut MIPSPageTable = unsafe { self.token() as *mut MIPSPageTable };
|
||||
|
||||
unsafe {
|
||||
clear_all_tlb();
|
||||
}
|
||||
|
||||
let mut active = unsafe {
|
||||
ActivePageTable(
|
||||
TwoLevelPageTable::new(&mut *pt),
|
||||
::core::mem::uninitialized(),
|
||||
)
|
||||
};
|
||||
|
||||
let ret = f(&mut active);
|
||||
|
||||
unsafe {
|
||||
clear_all_tlb();
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InactivePageTable0 {
|
||||
fn drop(&mut self) {
|
||||
dealloc_frame(self.root_frame.start_address().as_usize());
|
||||
}
|
||||
}
|
||||
|
||||
struct FrameAllocatorForRiscv;
|
||||
|
||||
impl FrameAllocator for FrameAllocatorForRiscv {
|
||||
fn alloc(&mut self) -> Option<Frame> {
|
||||
alloc_frame().map(|addr| Frame::of_addr(PhysAddr::new(addr)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameDeallocator for FrameAllocatorForRiscv {
|
||||
fn dealloc(&mut self, frame: Frame) {
|
||||
dealloc_frame(frame.start_address().as_usize());
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
pub fn rand() -> u64 {
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,391 @@
|
||||
//! MIPS N32 ABI syscall ids
|
||||
//! Reference: https://git.linux-mips.org/cgit/ralf/linux.git/tree/arch/mips/include/uapi/asm/unistd.h
|
||||
|
||||
extern crate paste;
|
||||
|
||||
pub const MIPS_SYSCALL_OFFSET: usize = 4000;
|
||||
|
||||
macro_rules! define_syscall {
|
||||
($name: ident, $id: expr) => {
|
||||
paste::item! {
|
||||
pub const [<SYS_ $name>] : usize = MIPS_SYSCALL_OFFSET + $id;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_syscall!(SYSCALL, 0);
|
||||
define_syscall!(EXIT, 1);
|
||||
define_syscall!(FORK, 2);
|
||||
define_syscall!(READ, 3);
|
||||
define_syscall!(WRITE, 4);
|
||||
define_syscall!(OPEN, 5);
|
||||
define_syscall!(CLOSE, 6);
|
||||
define_syscall!(WAITPID, 7);
|
||||
define_syscall!(CREAT, 8);
|
||||
define_syscall!(LINK, 9);
|
||||
define_syscall!(UNLINK, 10);
|
||||
define_syscall!(EXECVE, 11);
|
||||
define_syscall!(CHDIR, 12);
|
||||
define_syscall!(TIME, 13);
|
||||
define_syscall!(MKNOD, 14);
|
||||
define_syscall!(CHMOD, 15);
|
||||
define_syscall!(LCHOWN, 16);
|
||||
define_syscall!(BREAK, 17);
|
||||
define_syscall!(UNUSED18, 18);
|
||||
define_syscall!(LSEEK, 19);
|
||||
define_syscall!(GETPID, 20);
|
||||
define_syscall!(MOUNT, 21);
|
||||
define_syscall!(UMOUNT, 22);
|
||||
define_syscall!(SETUID, 23);
|
||||
define_syscall!(GETUID, 24);
|
||||
define_syscall!(STIME, 25);
|
||||
define_syscall!(PTRACE, 26);
|
||||
define_syscall!(ALARM, 27);
|
||||
define_syscall!(UNUSED28, 28);
|
||||
define_syscall!(PAUSE, 29);
|
||||
define_syscall!(UTIME, 30);
|
||||
define_syscall!(STTY, 31);
|
||||
define_syscall!(GTTY, 32);
|
||||
define_syscall!(ACCESS, 33);
|
||||
define_syscall!(NICE, 34);
|
||||
define_syscall!(FTIME, 35);
|
||||
define_syscall!(SYNC, 36);
|
||||
define_syscall!(KILL, 37);
|
||||
define_syscall!(RENAME, 38);
|
||||
define_syscall!(MKDIR, 39);
|
||||
define_syscall!(RMDIR, 40);
|
||||
define_syscall!(DUP, 41);
|
||||
define_syscall!(PIPE, 42);
|
||||
define_syscall!(TIMES, 43);
|
||||
define_syscall!(PROF, 44);
|
||||
define_syscall!(BRK, 45);
|
||||
define_syscall!(SETGID, 46);
|
||||
define_syscall!(GETGID, 47);
|
||||
define_syscall!(SIGNAL, 48);
|
||||
define_syscall!(GETEUID, 49);
|
||||
define_syscall!(GETEGID, 50);
|
||||
define_syscall!(ACCT, 51);
|
||||
define_syscall!(UMOUNT2, 52);
|
||||
define_syscall!(LOCK, 53);
|
||||
define_syscall!(IOCTL, 54);
|
||||
define_syscall!(FCNTL, 55);
|
||||
define_syscall!(MPX, 56);
|
||||
define_syscall!(SETPGID, 57);
|
||||
define_syscall!(ULIMIT, 58);
|
||||
define_syscall!(UNUSED59, 59);
|
||||
define_syscall!(UMASK, 60);
|
||||
define_syscall!(CHROOT, 61);
|
||||
define_syscall!(USTAT, 62);
|
||||
define_syscall!(DUP2, 63);
|
||||
define_syscall!(GETPPID, 64);
|
||||
define_syscall!(GETPGRP, 65);
|
||||
define_syscall!(SETSID, 66);
|
||||
define_syscall!(SIGACTION, 67);
|
||||
define_syscall!(SGETMASK, 68);
|
||||
define_syscall!(SSETMASK, 69);
|
||||
define_syscall!(SETREUID, 70);
|
||||
define_syscall!(SETREGID, 71);
|
||||
define_syscall!(SIGSUSPEND, 72);
|
||||
define_syscall!(SIGPENDING, 73);
|
||||
define_syscall!(SETHOSTNAME, 74);
|
||||
define_syscall!(SETRLIMIT, 75);
|
||||
define_syscall!(GETRLIMIT, 76);
|
||||
define_syscall!(GETRUSAGE, 77);
|
||||
define_syscall!(GETTIMEOFDAY, 78);
|
||||
define_syscall!(SETTIMEOFDAY, 79);
|
||||
define_syscall!(GETGROUPS, 80);
|
||||
define_syscall!(SETGROUPS, 81);
|
||||
define_syscall!(RESERVED82, 82);
|
||||
define_syscall!(SYMLINK, 83);
|
||||
define_syscall!(UNUSED84, 84);
|
||||
define_syscall!(READLINK, 85);
|
||||
define_syscall!(USELIB, 86);
|
||||
define_syscall!(SWAPON, 87);
|
||||
define_syscall!(REBOOT, 88);
|
||||
define_syscall!(READDIR, 89);
|
||||
define_syscall!(MMAP, 90);
|
||||
define_syscall!(MUNMAP, 91);
|
||||
define_syscall!(TRUNCATE, 92);
|
||||
define_syscall!(FTRUNCATE, 93);
|
||||
define_syscall!(FCHMOD, 94);
|
||||
define_syscall!(FCHOWN, 95);
|
||||
define_syscall!(GETPRIORITY, 96);
|
||||
define_syscall!(SETPRIORITY, 97);
|
||||
define_syscall!(PROFIL, 98);
|
||||
define_syscall!(STATFS, 99);
|
||||
define_syscall!(FSTATFS, 100);
|
||||
define_syscall!(IOPERM, 101);
|
||||
define_syscall!(SOCKETCALL, 102);
|
||||
define_syscall!(SYSLOG, 103);
|
||||
define_syscall!(SETITIMER, 104);
|
||||
define_syscall!(GETITIMER, 105);
|
||||
define_syscall!(STAT, 106);
|
||||
define_syscall!(LSTAT, 107);
|
||||
define_syscall!(FSTAT, 108);
|
||||
define_syscall!(UNUSED109, 109);
|
||||
define_syscall!(IOPL, 110);
|
||||
define_syscall!(VHANGUP, 111);
|
||||
define_syscall!(IDLE, 112);
|
||||
define_syscall!(VM86, 113);
|
||||
define_syscall!(WAIT4, 114);
|
||||
define_syscall!(SWAPOFF, 115);
|
||||
define_syscall!(SYSINFO, 116);
|
||||
define_syscall!(IPC, 117);
|
||||
define_syscall!(FSYNC, 118);
|
||||
define_syscall!(SIGRETURN, 119);
|
||||
define_syscall!(CLONE, 120);
|
||||
define_syscall!(SETDOMAINNAME, 121);
|
||||
define_syscall!(UNAME, 122);
|
||||
define_syscall!(MODIFY_LDT, 123);
|
||||
define_syscall!(ADJTIMEX, 124);
|
||||
define_syscall!(MPROTECT, 125);
|
||||
define_syscall!(SIGPROCMASK, 126);
|
||||
define_syscall!(CREATE_MODULE, 127);
|
||||
define_syscall!(INIT_MODULE, 128);
|
||||
define_syscall!(DELETE_MODULE, 129);
|
||||
define_syscall!(GET_KERNEL_SYMS, 130);
|
||||
define_syscall!(QUOTACTL, 131);
|
||||
define_syscall!(GETPGID, 132);
|
||||
define_syscall!(FCHDIR, 133);
|
||||
define_syscall!(BDFLUSH, 134);
|
||||
define_syscall!(SYSFS, 135);
|
||||
define_syscall!(PERSONALITY, 136);
|
||||
define_syscall!(AFS_SYSCALL, 137);
|
||||
define_syscall!(SETFSUID, 138);
|
||||
define_syscall!(SETFSGID, 139);
|
||||
define_syscall!(_LLSEEK, 140);
|
||||
define_syscall!(GETDENTS, 141);
|
||||
define_syscall!(_NEWSELECT, 142);
|
||||
define_syscall!(FLOCK, 143);
|
||||
define_syscall!(MSYNC, 144);
|
||||
define_syscall!(READV, 145);
|
||||
define_syscall!(WRITEV, 146);
|
||||
define_syscall!(CACHEFLUSH, 147);
|
||||
define_syscall!(CACHECTL, 148);
|
||||
define_syscall!(SYSMIPS, 149);
|
||||
define_syscall!(UNUSED150, 150);
|
||||
define_syscall!(GETSID, 151);
|
||||
define_syscall!(FDATASYNC, 152);
|
||||
define_syscall!(_SYSCTL, 153);
|
||||
define_syscall!(MLOCK, 154);
|
||||
define_syscall!(MUNLOCK, 155);
|
||||
define_syscall!(MLOCKALL, 156);
|
||||
define_syscall!(MUNLOCKALL, 157);
|
||||
define_syscall!(SCHED_SETPARAM, 158);
|
||||
define_syscall!(SCHED_GETPARAM, 159);
|
||||
define_syscall!(SCHED_SETSCHEDULER, 160);
|
||||
define_syscall!(SCHED_GETSCHEDULER, 161);
|
||||
define_syscall!(SCHED_YIELD, 162);
|
||||
define_syscall!(SCHED_GET_PRIORITY_MAX, 163);
|
||||
define_syscall!(SCHED_GET_PRIORITY_MIN, 164);
|
||||
define_syscall!(SCHED_RR_GET_INTERVAL, 165);
|
||||
define_syscall!(NANOSLEEP, 166);
|
||||
define_syscall!(MREMAP, 167);
|
||||
define_syscall!(ACCEPT, 168);
|
||||
define_syscall!(BIND, 169);
|
||||
define_syscall!(CONNECT, 170);
|
||||
define_syscall!(GETPEERNAME, 171);
|
||||
define_syscall!(GETSOCKNAME, 172);
|
||||
define_syscall!(GETSOCKOPT, 173);
|
||||
define_syscall!(LISTEN, 174);
|
||||
define_syscall!(RECV, 175);
|
||||
define_syscall!(RECVFROM, 176);
|
||||
define_syscall!(RECVMSG, 177);
|
||||
define_syscall!(SEND, 178);
|
||||
define_syscall!(SENDMSG, 179);
|
||||
define_syscall!(SENDTO, 180);
|
||||
define_syscall!(SETSOCKOPT, 181);
|
||||
define_syscall!(SHUTDOWN, 182);
|
||||
define_syscall!(SOCKET, 183);
|
||||
define_syscall!(SOCKETPAIR, 184);
|
||||
define_syscall!(SETRESUID, 185);
|
||||
define_syscall!(GETRESUID, 186);
|
||||
define_syscall!(QUERY_MODULE, 187);
|
||||
define_syscall!(POLL, 188);
|
||||
define_syscall!(NFSSERVCTL, 189);
|
||||
define_syscall!(SETRESGID, 190);
|
||||
define_syscall!(GETRESGID, 191);
|
||||
define_syscall!(PRCTL, 192);
|
||||
define_syscall!(RT_SIGRETURN, 193);
|
||||
define_syscall!(RT_SIGACTION, 194);
|
||||
define_syscall!(RT_SIGPROCMASK, 195);
|
||||
define_syscall!(RT_SIGPENDING, 196);
|
||||
define_syscall!(RT_SIGTIMEDWAIT, 197);
|
||||
define_syscall!(RT_SIGQUEUEINFO, 198);
|
||||
define_syscall!(RT_SIGSUSPEND, 199);
|
||||
define_syscall!(PREAD64, 200);
|
||||
define_syscall!(PWRITE64, 201);
|
||||
define_syscall!(CHOWN, 202);
|
||||
define_syscall!(GETCWD, 203);
|
||||
define_syscall!(CAPGET, 204);
|
||||
define_syscall!(CAPSET, 205);
|
||||
define_syscall!(SIGALTSTACK, 206);
|
||||
define_syscall!(SENDFILE, 207);
|
||||
define_syscall!(GETPMSG, 208);
|
||||
define_syscall!(PUTPMSG, 209);
|
||||
define_syscall!(MMAP2, 210);
|
||||
define_syscall!(TRUNCATE64, 211);
|
||||
define_syscall!(FTRUNCATE64, 212);
|
||||
define_syscall!(STAT64, 213);
|
||||
define_syscall!(LSTAT64, 214);
|
||||
define_syscall!(FSTAT64, 215);
|
||||
define_syscall!(PIVOT_ROOT, 216);
|
||||
define_syscall!(MINCORE, 217);
|
||||
define_syscall!(MADVISE, 218);
|
||||
define_syscall!(GETDENTS64, 219);
|
||||
define_syscall!(FCNTL64, 220);
|
||||
define_syscall!(RESERVED221, 221);
|
||||
define_syscall!(GETTID, 222);
|
||||
define_syscall!(READAHEAD, 223);
|
||||
define_syscall!(SETXATTR, 224);
|
||||
define_syscall!(LSETXATTR, 225);
|
||||
define_syscall!(FSETXATTR, 226);
|
||||
define_syscall!(GETXATTR, 227);
|
||||
define_syscall!(LGETXATTR, 228);
|
||||
define_syscall!(FGETXATTR, 229);
|
||||
define_syscall!(LISTXATTR, 230);
|
||||
define_syscall!(LLISTXATTR, 231);
|
||||
define_syscall!(FLISTXATTR, 232);
|
||||
define_syscall!(REMOVEXATTR, 233);
|
||||
define_syscall!(LREMOVEXATTR, 234);
|
||||
define_syscall!(FREMOVEXATTR, 235);
|
||||
define_syscall!(TKILL, 236);
|
||||
define_syscall!(SENDFILE64, 237);
|
||||
define_syscall!(FUTEX, 238);
|
||||
define_syscall!(SCHED_SETAFFINITY, 239);
|
||||
define_syscall!(SCHED_GETAFFINITY, 240);
|
||||
define_syscall!(IO_SETUP, 241);
|
||||
define_syscall!(IO_DESTROY, 242);
|
||||
define_syscall!(IO_GETEVENTS, 243);
|
||||
define_syscall!(IO_SUBMIT, 244);
|
||||
define_syscall!(IO_CANCEL, 245);
|
||||
define_syscall!(EXIT_GROUP, 246);
|
||||
define_syscall!(LOOKUP_DCOOKIE, 247);
|
||||
define_syscall!(EPOLL_CREATE, 248);
|
||||
define_syscall!(EPOLL_CTL, 249);
|
||||
define_syscall!(EPOLL_WAIT, 250);
|
||||
define_syscall!(REMAP_FILE_PAGES, 251);
|
||||
define_syscall!(SET_TID_ADDRESS, 252);
|
||||
define_syscall!(RESTART_SYSCALL, 253);
|
||||
define_syscall!(FADVISE64, 254);
|
||||
define_syscall!(STATFS64, 255);
|
||||
define_syscall!(FSTATFS64, 256);
|
||||
define_syscall!(TIMER_CREATE, 257);
|
||||
define_syscall!(TIMER_SETTIME, 258);
|
||||
define_syscall!(TIMER_GETTIME, 259);
|
||||
define_syscall!(TIMER_GETOVERRUN, 260);
|
||||
define_syscall!(TIMER_DELETE, 261);
|
||||
define_syscall!(CLOCK_SETTIME, 262);
|
||||
define_syscall!(CLOCK_GETTIME, 263);
|
||||
define_syscall!(CLOCK_GETRES, 264);
|
||||
define_syscall!(CLOCK_NANOSLEEP, 265);
|
||||
define_syscall!(TGKILL, 266);
|
||||
define_syscall!(UTIMES, 267);
|
||||
define_syscall!(MBIND, 268);
|
||||
define_syscall!(GET_MEMPOLICY, 269);
|
||||
define_syscall!(SET_MEMPOLICY, 270);
|
||||
define_syscall!(MQ_OPEN, 271);
|
||||
define_syscall!(MQ_UNLINK, 272);
|
||||
define_syscall!(MQ_TIMEDSEND, 273);
|
||||
define_syscall!(MQ_TIMEDRECEIVE, 274);
|
||||
define_syscall!(MQ_NOTIFY, 275);
|
||||
define_syscall!(MQ_GETSETATTR, 276);
|
||||
define_syscall!(VSERVER, 277);
|
||||
define_syscall!(WAITID, 278);
|
||||
define_syscall!(SYS_SETALTROOT, 279);
|
||||
define_syscall!(ADD_KEY, 280);
|
||||
define_syscall!(REQUEST_KEY, 281);
|
||||
define_syscall!(KEYCTL, 282);
|
||||
define_syscall!(SET_THREAD_AREA, 283);
|
||||
define_syscall!(INOTIFY_INIT, 284);
|
||||
define_syscall!(INOTIFY_ADD_WATCH, 285);
|
||||
define_syscall!(INOTIFY_RM_WATCH, 286);
|
||||
define_syscall!(MIGRATE_PAGES, 287);
|
||||
define_syscall!(OPENAT, 288);
|
||||
define_syscall!(MKDIRAT, 289);
|
||||
define_syscall!(MKNODAT, 290);
|
||||
define_syscall!(FCHOWNAT, 291);
|
||||
define_syscall!(FUTIMESAT, 292);
|
||||
define_syscall!(FSTATAT64, 293);
|
||||
define_syscall!(UNLINKAT, 294);
|
||||
define_syscall!(RENAMEAT, 295);
|
||||
define_syscall!(LINKAT, 296);
|
||||
define_syscall!(SYMLINKAT, 297);
|
||||
define_syscall!(READLINKAT, 298);
|
||||
define_syscall!(FCHMODAT, 299);
|
||||
define_syscall!(FACCESSAT, 300);
|
||||
define_syscall!(PSELECT6, 301);
|
||||
define_syscall!(PPOLL, 302);
|
||||
define_syscall!(UNSHARE, 303);
|
||||
define_syscall!(SPLICE, 304);
|
||||
define_syscall!(SYNC_FILE_RANGE, 305);
|
||||
define_syscall!(TEE, 306);
|
||||
define_syscall!(VMSPLICE, 307);
|
||||
define_syscall!(MOVE_PAGES, 308);
|
||||
define_syscall!(SET_ROBUST_LIST, 309);
|
||||
define_syscall!(GET_ROBUST_LIST, 310);
|
||||
define_syscall!(KEXEC_LOAD, 311);
|
||||
define_syscall!(GETCPU, 312);
|
||||
define_syscall!(EPOLL_PWAIT, 313);
|
||||
define_syscall!(IOPRIO_SET, 314);
|
||||
define_syscall!(IOPRIO_GET, 315);
|
||||
define_syscall!(UTIMENSAT, 316);
|
||||
define_syscall!(SIGNALFD, 317);
|
||||
define_syscall!(TIMERFD, 318);
|
||||
define_syscall!(EVENTFD, 319);
|
||||
define_syscall!(FALLOCATE, 320);
|
||||
define_syscall!(TIMERFD_CREATE, 321);
|
||||
define_syscall!(TIMERFD_GETTIME, 322);
|
||||
define_syscall!(TIMERFD_SETTIME, 323);
|
||||
define_syscall!(SIGNALFD4, 324);
|
||||
define_syscall!(EVENTFD2, 325);
|
||||
define_syscall!(EPOLL_CREATE1, 326);
|
||||
define_syscall!(DUP3, 327);
|
||||
define_syscall!(PIPE2, 328);
|
||||
define_syscall!(INOTIFY_INIT1, 329);
|
||||
define_syscall!(PREADV, 330);
|
||||
define_syscall!(PWRITEV, 331);
|
||||
define_syscall!(RT_TGSIGQUEUEINFO, 332);
|
||||
define_syscall!(PERF_EVENT_OPEN, 333);
|
||||
define_syscall!(ACCEPT4, 334);
|
||||
define_syscall!(RECVMMSG, 335);
|
||||
define_syscall!(FANOTIFY_INIT, 336);
|
||||
define_syscall!(FANOTIFY_MARK, 337);
|
||||
define_syscall!(PRLIMIT64, 338);
|
||||
define_syscall!(NAME_TO_HANDLE_AT, 339);
|
||||
define_syscall!(OPEN_BY_HANDLE_AT, 340);
|
||||
define_syscall!(CLOCK_ADJTIME, 341);
|
||||
define_syscall!(SYNCFS, 342);
|
||||
define_syscall!(SENDMMSG, 343);
|
||||
define_syscall!(SETNS, 344);
|
||||
define_syscall!(PROCESS_VM_READV, 345);
|
||||
define_syscall!(PROCESS_VM_WRITEV, 346);
|
||||
define_syscall!(KCMP, 347);
|
||||
define_syscall!(FINIT_MODULE, 348);
|
||||
define_syscall!(SCHED_SETATTR, 349);
|
||||
define_syscall!(SCHED_GETATTR, 350);
|
||||
define_syscall!(RENAMEAT2, 351);
|
||||
define_syscall!(SECCOMP, 352);
|
||||
define_syscall!(GETRANDOM, 353);
|
||||
define_syscall!(MEMFD_CREATE, 354);
|
||||
define_syscall!(BPF, 355);
|
||||
define_syscall!(EXECVEAT, 356);
|
||||
define_syscall!(USERFAULTFD, 357);
|
||||
define_syscall!(MEMBARRIER, 358);
|
||||
define_syscall!(MLOCK2, 359);
|
||||
define_syscall!(COPY_FILE_RANGE, 360);
|
||||
define_syscall!(PREADV2, 361);
|
||||
define_syscall!(PWRITEV2, 362);
|
||||
define_syscall!(PKEY_MPROTECT, 363);
|
||||
define_syscall!(PKEY_ALLOC, 364);
|
||||
define_syscall!(PKEY_FREE, 365);
|
||||
define_syscall!(STATX, 366);
|
||||
define_syscall!(RSEQ, 367);
|
||||
define_syscall!(IO_PGETEVENTS, 368);
|
||||
|
||||
// non-existent syscalls, will not be called or matched
|
||||
pub const SYS_NEWFSTATAT: usize = 0;
|
||||
|
||||
// custom temporary syscall
|
||||
pub const SYS_MAP_PCI_DEVICE: usize = 999;
|
||||
pub const SYS_GET_PADDR: usize = 998;
|
@ -0,0 +1,24 @@
|
||||
use log::*;
|
||||
use mips::registers::cp0;
|
||||
|
||||
pub fn read_epoch() -> u64 {
|
||||
// TODO: support RTC
|
||||
0
|
||||
}
|
||||
|
||||
/// Enable timer interrupt
|
||||
pub fn init() {
|
||||
// Enable supervisor timer interrupt
|
||||
cp0::status::enable_hard_int5(); // IP(7), timer interrupt
|
||||
cp0::count::write_u32(0);
|
||||
set_next();
|
||||
info!("timer: init end");
|
||||
}
|
||||
|
||||
/// Set the next timer interrupt
|
||||
pub fn set_next() {
|
||||
// 100Hz @ QEMU
|
||||
let timebase = 250000;
|
||||
cp0::count::write_u32(0);
|
||||
cp0::compare::write_u32(timebase);
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "mips"))]
|
||||
pub mod pci;
|
||||
pub mod virtio_mmio;
|
||||
|
@ -0,0 +1,153 @@
|
||||
//! driver for qemu stdvga (Cirrus)
|
||||
|
||||
use crate::util::{read, write};
|
||||
|
||||
const VGA_MMIO_OFFSET: usize = 0x400 - 0x3C0;
|
||||
const VBE_MMIO_OFFSET: usize = 0x500;
|
||||
|
||||
const VGA_AR_ADDR: u16 = 0x3C0;
|
||||
const VBE_DISPI_INDEX_XRES: u16 = 0x1;
|
||||
const VBE_DISPI_INDEX_YRES: u16 = 0x2;
|
||||
const VBE_DISPI_INDEX_BPP: u16 = 0x3;
|
||||
const VBE_DISPI_INDEX_ENABLE: u16 = 0x4;
|
||||
const VBE_DISPI_INDEX_BANK: u16 = 0x5;
|
||||
const VBE_DISPI_INDEX_VIRT_WIDTH: u16 = 0x6;
|
||||
const VBE_DISPI_INDEX_VIRT_HEIGHT: u16 = 0x7;
|
||||
const VBE_DISPI_INDEX_X_OFFSET: u16 = 0x8;
|
||||
const VBE_DISPI_INDEX_Y_OFFSET: u16 = 0x9;
|
||||
const VBE_DISPI_INDEX_VIDEO_MEMORY_64K: u16 = 0xa;
|
||||
|
||||
const VGA_AR_PAS: u8 = 0x20;
|
||||
const VBE_DISPI_ENABLED: u16 = 0x01;
|
||||
const VBE_DISPI_8BIT_DAC: u16 = 0x20;
|
||||
const VBE_DISPI_LFB_ENABLED: u16 = 0x40;
|
||||
|
||||
const PCI_COMMAND: u8 = 0x04;
|
||||
const PCI_COMMAND_IO: u32 = 0x1;
|
||||
const PCI_COMMAND_MEMORY: u32 = 0x2;
|
||||
const PCI_COMMAND_MASTER: u32 = 0x4;
|
||||
const PCI_COMMAND_SPECIAL: u32 = 0x8;
|
||||
const PCI_COMMAND_SERR: u32 = 0x100;
|
||||
|
||||
fn pci_read_config(pci_base: usize, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
|
||||
// write config address
|
||||
let address = (1 << 31)
|
||||
| ((bus as u32) << 16)
|
||||
| ((slot as u32) << 11)
|
||||
| ((func as u32) << 8)
|
||||
| (offset as u32);
|
||||
write(pci_base + 0xcf8, address);
|
||||
// do the actual work
|
||||
let value = read(pci_base + 0xcfc);
|
||||
debug!(
|
||||
"Read {:08x} from PCI address: {:02x}:{:02x}.{:02x} @ 0x{:02x}",
|
||||
value, bus, slot, func, offset
|
||||
);
|
||||
value
|
||||
}
|
||||
|
||||
fn pci_write_config(pci_base: usize, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
|
||||
// write config address
|
||||
let address = (1 << 31)
|
||||
| ((bus as u32) << 16)
|
||||
| ((slot as u32) << 11)
|
||||
| ((func as u32) << 8)
|
||||
| (offset as u32);
|
||||
debug!(
|
||||
"Write {:08x} to PCI address: {:02x}:{:02x}.{:02x} @ 0x{:02x}",
|
||||
value, bus, slot, func, offset
|
||||
);
|
||||
write(pci_base + 0xcf8, address);
|
||||
// do the actual work
|
||||
write(pci_base + 0xcfc, value)
|
||||
}
|
||||
|
||||
pub fn init(pci_base: usize, vga_base: usize, x_res: u16, y_res: u16) {
|
||||
debug!(
|
||||
"PCI Controller Base: {:08x}",
|
||||
pci_read_config(pci_base, 0x00, 0x00, 0x00, 0x20)
|
||||
);
|
||||
|
||||
let controller = pci_read_config(pci_base, 0x00, 0x00, 0x00, PCI_COMMAND);
|
||||
pci_write_config(
|
||||
pci_base,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
PCI_COMMAND,
|
||||
controller | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR,
|
||||
);
|
||||
|
||||
let pci_vendor = pci_read_config(pci_base, 0x00, 0x12, 0x00, 0x0);
|
||||
debug!("VGA PCI Device ID: {:08x}", pci_vendor);
|
||||
|
||||
// enable port and MMIO for vga card
|
||||
pci_write_config(
|
||||
pci_base,
|
||||
0x00,
|
||||
0x12,
|
||||
0x00,
|
||||
PCI_COMMAND,
|
||||
pci_read_config(pci_base, 0x00, 0x12, 0x00, PCI_COMMAND) | PCI_COMMAND_MEMORY,
|
||||
);
|
||||
// bar 0
|
||||
pci_write_config(pci_base, 0x00, 0x12, 0x00, 0x10, 0x10000000);
|
||||
debug!(
|
||||
"VGA PCI BAR 0: {:08x}",
|
||||
pci_read_config(pci_base, 0x00, 0x12, 0x00, 0x10)
|
||||
);
|
||||
// bar 2
|
||||
pci_write_config(pci_base, 0x00, 0x12, 0x00, 0x18, 0x12050000);
|
||||
debug!(
|
||||
"VGA PCI BAR 2: {:08x}",
|
||||
pci_read_config(pci_base, 0x00, 0x12, 0x00, 0x18)
|
||||
);
|
||||
|
||||
// vga operations
|
||||
let vga_write_io = |offset: u16, value: u8| {
|
||||
write(vga_base + VGA_MMIO_OFFSET + (offset as usize), value);
|
||||
};
|
||||
|
||||
let vga_read_io = |offset: u16| -> u8 { read(vga_base + VGA_MMIO_OFFSET + (offset as usize)) };
|
||||
|
||||
let vga_write_vbe = |offset: u16, value: u16| {
|
||||
write(vga_base + VBE_MMIO_OFFSET + (offset as usize) * 2, value);
|
||||
};
|
||||
|
||||
let vga_read_vbe =
|
||||
|offset: u16| -> u16 { read(vga_base + VBE_MMIO_OFFSET + (offset as usize) * 2) };
|
||||
|
||||
debug!("VGA Endianess: {:x}", read::<u32>(vga_base + 0x604));
|
||||
|
||||
// unblank vga output
|
||||
vga_write_io(VGA_AR_ADDR, VGA_AR_PAS);
|
||||
debug!("VGA AR: {}", vga_read_io(VGA_AR_ADDR));
|
||||
|
||||
vga_write_vbe(VBE_DISPI_INDEX_ENABLE, 0);
|
||||
|
||||
// set resolution and color depth
|
||||
vga_write_vbe(VBE_DISPI_INDEX_XRES, x_res);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_YRES, y_res);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_VIRT_WIDTH, x_res);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_VIRT_HEIGHT, y_res);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_BANK, 0);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_X_OFFSET, 0);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_Y_OFFSET, 0);
|
||||
vga_write_vbe(VBE_DISPI_INDEX_BPP, 8);
|
||||
debug!(
|
||||
"VGA Resolution: {}*{}@{}bit",
|
||||
vga_read_vbe(VBE_DISPI_INDEX_XRES),
|
||||
vga_read_vbe(VBE_DISPI_INDEX_YRES),
|
||||
vga_read_vbe(VBE_DISPI_INDEX_BPP)
|
||||
);
|
||||
|
||||
// enable vbe
|
||||
let vbe_enable = vga_read_vbe(VBE_DISPI_INDEX_ENABLE);
|
||||
vga_write_vbe(
|
||||
VBE_DISPI_INDEX_ENABLE,
|
||||
vbe_enable | VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED | VBE_DISPI_8BIT_DAC,
|
||||
);
|
||||
debug!("VBE Status: {:04x}", vga_read_vbe(VBE_DISPI_INDEX_ENABLE));
|
||||
|
||||
info!("QEMU STDVGA driver initialized @ {:x}", vga_base);
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
//! 16550 serial adapter driver for malta board
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::util::{read, write};
|
||||
use core::fmt::{Arguments, Result, Write};
|
||||
use spin::Mutex;
|
||||
|
||||
pub struct SerialPort {
|
||||
base: usize,
|
||||
}
|
||||
|
||||
impl SerialPort {
|
||||
fn new() -> SerialPort {
|
||||
SerialPort { base: 0 }
|
||||
}
|
||||
|
||||
pub fn init(&mut self, base: usize) {
|
||||
self.base = base;
|
||||
// Turn off the FIFO
|
||||
write(self.base + COM_FCR, 0 as u8);
|
||||
// Set speed; requires DLAB latch
|
||||
write(self.base + COM_LCR, COM_LCR_DLAB);
|
||||
write(self.base + COM_DLL, (115200 / 9600) as u8);
|
||||
write(self.base + COM_DLM, 0 as u8);
|
||||
|
||||
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
|
||||
write(self.base + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB);
|
||||
|
||||
// No modem controls
|
||||
write(self.base + COM_MCR, 0 as u8);
|
||||
// Enable rcv interrupts
|
||||
write(self.base + COM_IER, COM_IER_RDI);
|
||||
}
|
||||
|
||||
/// non-blocking version of putchar()
|
||||
pub fn putchar(&mut self, c: u8) {
|
||||
write(self.base + COM_TX, c);
|
||||
}
|
||||
|
||||
/// blocking version of getchar()
|
||||
pub fn getchar(&mut self) -> char {
|
||||
loop {
|
||||
if (read::<u8>(self.base + COM_LSR) & COM_LSR_DATA) == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let c = read::<u8>(self.base + COM_RX);
|
||||
match c {
|
||||
255 => '\0', // null
|
||||
c => c as char,
|
||||
}
|
||||
}
|
||||
|
||||
/// non-blocking version of getchar()
|
||||
pub fn getchar_option(&mut self) -> Option<char> {
|
||||
match read::<u8>(self.base + COM_LSR) & COM_LSR_DATA {
|
||||
0 => None,
|
||||
_ => Some(read::<u8>(self.base + COM_RX) as u8 as char),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn putfmt(&mut self, fmt: Arguments) {
|
||||
self.write_fmt(fmt).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for SerialPort {
|
||||
fn write_str(&mut self, s: &str) -> Result {
|
||||
for c in s.bytes() {
|
||||
if c == 127 {
|
||||
self.putchar(8);
|
||||
self.putchar(b' ');
|
||||
self.putchar(8);
|
||||
} else {
|
||||
self.putchar(c);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const COM_RX: usize = 0; // In: Receive buffer (DLAB=0)
|
||||
const COM_TX: usize = 0; // Out: Transmit buffer (DLAB=0)
|
||||
const COM_DLL: usize = 0; // Out: Divisor Latch Low (DLAB=1)
|
||||
const COM_DLM: usize = 1; // Out: Divisor Latch High (DLAB=1)
|
||||
const COM_IER: usize = 1; // Out: Interrupt Enable Register
|
||||
const COM_IER_RDI: u8 = 0x01; // Enable receiver data interrupt
|
||||
const COM_IIR: usize = 2; // In: Interrupt ID Register
|
||||
const COM_FCR: usize = 2; // Out: FIFO Control Register
|
||||
const COM_LCR: usize = 3; // Out: Line Control Register
|
||||
const COM_LCR_DLAB: u8 = 0x80; // Divisor latch access bit
|
||||
const COM_LCR_WLEN8: u8 = 0x03; // Wordlength: 8 bits
|
||||
const COM_MCR: usize = 4; // Out: Modem Control Register
|
||||
const COM_MCR_RTS: u8 = 0x02; // RTS complement
|
||||
const COM_MCR_DTR: u8 = 0x01; // DTR complement
|
||||
const COM_MCR_OUT2: u8 = 0x08; // Out2 complement
|
||||
const COM_LSR: usize = 5; // In: Line Status Register
|
||||
const COM_LSR_DATA: u8 = 0x01; // Data available
|
||||
const COM_LSR_TXRDY: u8 = 0x20; // Transmit buffer avail
|
||||
const COM_LSR_TSRE: u8 = 0x40; // Transmitter off
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||
}
|
||||
|
||||
pub fn init(base: usize) {
|
||||
SERIAL_PORT.lock().init(base);
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
//! naive serial adapter driver for thinpad
|
||||
|
||||
use crate::util::{read, write};
|
||||
use core::fmt::{Arguments, Result, Write};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SerialPort {
|
||||
base: usize,
|
||||
}
|
||||
|
||||
const UART_STATUS: usize = 0x0;
|
||||
const UART_DATA: usize = 0x4;
|
||||
|
||||
const UART_STATUS_CTS: u8 = 0x1; // clear to send signal
|
||||
const UART_STATUS_DR: u8 = 0x2; // data ready signal
|
||||
|
||||
impl SerialPort {
|
||||
pub fn init(&mut self, base: usize) {
|
||||
self.base = base;
|
||||
}
|
||||
|
||||
/// non-blocking version of putchar()
|
||||
pub fn putchar(&mut self, c: u8) {
|
||||
write(self.base + UART_DATA, c);
|
||||
}
|
||||
|
||||
/// blocking version of getchar()
|
||||
pub fn getchar(&mut self) -> char {
|
||||
loop {
|
||||
if (read::<u8>(self.base + UART_STATUS) & UART_STATUS_DR) == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let c = read::<u8>(self.base + UART_DATA);
|
||||
match c {
|
||||
255 => '\0', // null
|
||||
c => c as char,
|
||||
}
|
||||
}
|
||||
|
||||
/// non-blocking version of getchar()
|
||||
pub fn getchar_option(&mut self) -> Option<char> {
|
||||
match read::<u8>(self.base + UART_STATUS) & UART_STATUS_DR {
|
||||
0 => None,
|
||||
_ => Some(read::<u8>(self.base + UART_DATA) as u8 as char),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn putfmt(&mut self, fmt: Arguments) {
|
||||
self.write_fmt(fmt).unwrap();
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> SerialPort {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
pub fn force_unlock(&self) {}
|
||||
}
|
||||
|
||||
impl Write for SerialPort {
|
||||
fn write_str(&mut self, s: &str) -> Result {
|
||||
for c in s.bytes() {
|
||||
if c == 127 {
|
||||
self.putchar(8);
|
||||
self.putchar(b' ');
|
||||
self.putchar(8);
|
||||
} else {
|
||||
self.putchar(c);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub static SERIAL_PORT: SerialPort = SerialPort { base: 0 };
|
||||
|
||||
pub fn init(base: usize) {
|
||||
SERIAL_PORT.lock().init(base);
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
//! TI 16c550c serial adapter driver for malta board
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::util::{read, write};
|
||||
use core::fmt::{Arguments, Result, Write};
|
||||
use spin::Mutex;
|
||||
|
||||
pub struct SerialPort {
|
||||
base: usize,
|
||||
}
|
||||
|
||||
impl SerialPort {
|
||||
fn new() -> SerialPort {
|
||||
SerialPort { base: 0 }
|
||||
}
|
||||
|
||||
pub fn init(&mut self, base: usize) {
|
||||
self.base = base;
|
||||
// Turn off the FIFO
|
||||
// write(self.base + COM_FCR, 0 as u8);
|
||||
// Set speed; requires DLAB latch
|
||||
// write(self.base + COM_LCR, COM_LCR_DLAB);
|
||||
// write(self.base + COM_DLL, (115200 / 9600) as u8);
|
||||
// write(self.base + COM_DLM, 0 as u8);
|
||||
|
||||
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
|
||||
// write(self.base + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB);
|
||||
|
||||
// No modem controls
|
||||
// write(self.base + COM_MCR, 0 as u8);
|
||||
// Enable rcv interrupts
|
||||
write(self.base + COM_INT_EN, 0x1);
|
||||
}
|
||||
|
||||
/// non-blocking version of putchar()
|
||||
pub fn putchar(&mut self, c: u8) {
|
||||
write(self.base + COM_TX, c);
|
||||
}
|
||||
|
||||
/// blocking version of getchar()
|
||||
pub fn getchar(&mut self) -> char {
|
||||
loop {
|
||||
if (read::<u8>(self.base + COM_LSR) & 0x01) == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let c = read::<u8>(self.base + COM_RX);
|
||||
match c {
|
||||
255 => '\0', // null
|
||||
c => c as char,
|
||||
}
|
||||
}
|
||||
|
||||
/// non-blocking version of getchar()
|
||||
pub fn getchar_option(&mut self) -> Option<char> {
|
||||
match read::<u8>(self.base + COM_LSR) & 0x01 {
|
||||
0 => None,
|
||||
_ => Some(read::<u8>(self.base + COM_RX) as u8 as char),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn putfmt(&mut self, fmt: Arguments) {
|
||||
self.write_fmt(fmt).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for SerialPort {
|
||||
fn write_str(&mut self, s: &str) -> Result {
|
||||
for c in s.bytes() {
|
||||
if c == 127 {
|
||||
self.putchar(8);
|
||||
self.putchar(b' ');
|
||||
self.putchar(8);
|
||||
} else {
|
||||
self.putchar(c);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const COM_RX: usize = 0x00; // In: Receive buffer (DLAB=0)
|
||||
const COM_TX: usize = 0x00; // Out: Transmit buffer (DLAB=0)
|
||||
const COM_INT_EN: usize = 0x08; // In: Interrupt enable
|
||||
const COM_INT_ID: usize = 0x10; // Out: Interrupt identification
|
||||
const COM_LSR: usize = 0x28; // In: Line status register
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||
}
|
||||
|
||||
pub fn init(base: usize) {
|
||||
SERIAL_PORT.lock().init(base);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
{
|
||||
"arch": "mips",
|
||||
"cpu": "mips32r2",
|
||||
"llvm-target": "mipsel-unknown-none",
|
||||
"data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "32",
|
||||
"target-c-int-width": "32",
|
||||
"os": "none",
|
||||
"features": "+mips32r2,+soft-float",
|
||||
"max-atomic-width": "32",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"-Tsrc/arch/mipsel/boot/linker.ld"
|
||||
]
|
||||
},
|
||||
"executables": true,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"abi-blacklist": [
|
||||
"cdecl",
|
||||
"stdcall",
|
||||
"fastcall",
|
||||
"vectorcall",
|
||||
"thiscall",
|
||||
"aapcs",
|
||||
"win64",
|
||||
"sysv64",
|
||||
"ptx-kernel",
|
||||
"msp430-interrupt",
|
||||
"x86-interrupt"
|
||||
],
|
||||
"eliminate-frame-pointer": false
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit b09e4e24ad1cfe3b6eae2b40231f389070cdb9e2
|
||||
Subproject commit aeea2b569efe90e38b39d3c72edb4bc015837d14
|
Loading…
Reference in new issue