start at support for multiple CPUs
This commit is contained in:
parent
ec3d3a1fce
commit
f1a727b971
|
@ -1,3 +1,4 @@
|
||||||
|
set confirm off
|
||||||
python
|
python
|
||||||
gdb.execute("target remote localhost:26000")
|
gdb.execute("target remote localhost:26000")
|
||||||
gdb.execute("set architecture i386")
|
gdb.execute("set architecture i386")
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
set confirm off
|
||||||
set architecture riscv
|
set architecture riscv
|
||||||
target remote 127.0.0.1:1234
|
target remote 127.0.0.1:1234
|
||||||
symbol-file kernel
|
symbol-file kernel
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -187,13 +187,13 @@ endif
|
||||||
QEMUOPTS = -machine virt -kernel kernel -m 3G -smp $(CPUS) -nographic
|
QEMUOPTS = -machine virt -kernel kernel -m 3G -smp $(CPUS) -nographic
|
||||||
QEMUOPTS += -initrd fs.img
|
QEMUOPTS += -initrd fs.img
|
||||||
|
|
||||||
qemu: kernel
|
qemu: kernel fs.img
|
||||||
$(QEMU) $(QEMUOPTS)
|
$(QEMU) $(QEMUOPTS)
|
||||||
|
|
||||||
.gdbinit: .gdbinit.tmpl-riscv
|
.gdbinit: .gdbinit.tmpl-riscv
|
||||||
sed "s/:1234/:$(GDBPORT)/" < $^ > $@
|
sed "s/:1234/:$(GDBPORT)/" < $^ > $@
|
||||||
|
|
||||||
qemu-gdb: kernel .gdbinit
|
qemu-gdb: kernel .gdbinit fs.img
|
||||||
@echo "*** Now run 'gdb'." 1>&2
|
@echo "*** Now run 'gdb'." 1>&2
|
||||||
$(QEMU) $(QEMUOPTS) -S $(QEMUGDB)
|
$(QEMU) $(QEMUOPTS) -S $(QEMUGDB)
|
||||||
|
|
||||||
|
|
18
asm.h
18
asm.h
|
@ -1,18 +0,0 @@
|
||||||
//
|
|
||||||
// assembler macros to create x86 segments
|
|
||||||
//
|
|
||||||
|
|
||||||
#define SEG_NULLASM \
|
|
||||||
.word 0, 0; \
|
|
||||||
.byte 0, 0, 0, 0
|
|
||||||
|
|
||||||
// The 0xC0 means the limit is in 4096-byte units
|
|
||||||
// and (for executable segments) 32-bit mode.
|
|
||||||
#define SEG_ASM(type,base,lim) \
|
|
||||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
|
||||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
|
||||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
|
||||||
|
|
||||||
#define STA_X 0x8 // Executable segment
|
|
||||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
|
||||||
#define STA_R 0x2 // Readable (executable segments)
|
|
96
bootmain.c
96
bootmain.c
|
@ -1,96 +0,0 @@
|
||||||
// Boot loader.
|
|
||||||
//
|
|
||||||
// Part of the boot block, along with bootasm.S, which calls bootmain().
|
|
||||||
// bootasm.S has put the processor into protected 32-bit mode.
|
|
||||||
// bootmain() loads an ELF kernel image from the disk starting at
|
|
||||||
// sector 1 and then jumps to the kernel entry routine.
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "elf.h"
|
|
||||||
#include "x86.h"
|
|
||||||
#include "memlayout.h"
|
|
||||||
|
|
||||||
#define SECTSIZE 512
|
|
||||||
|
|
||||||
void readseg(uchar*, uint, uint);
|
|
||||||
|
|
||||||
void
|
|
||||||
bootmain(void)
|
|
||||||
{
|
|
||||||
struct elfhdr *elf;
|
|
||||||
struct proghdr *ph, *eph;
|
|
||||||
void (*entry)(void);
|
|
||||||
uchar* pa;
|
|
||||||
|
|
||||||
elf = (struct elfhdr*)0x10000; // scratch space
|
|
||||||
|
|
||||||
// Read 1st page off disk
|
|
||||||
readseg((uchar*)elf, 4096, 0);
|
|
||||||
|
|
||||||
// Is this an ELF executable?
|
|
||||||
if(elf->magic != ELF_MAGIC)
|
|
||||||
return; // let bootasm.S handle error
|
|
||||||
|
|
||||||
// Load each program segment (ignores ph flags).
|
|
||||||
ph = (struct proghdr*)((uchar*)elf + elf->phoff);
|
|
||||||
eph = ph + elf->phnum;
|
|
||||||
for(; ph < eph; ph++){
|
|
||||||
pa = (uchar*)ph->paddr;
|
|
||||||
readseg(pa, ph->filesz, ph->off);
|
|
||||||
if(ph->memsz > ph->filesz)
|
|
||||||
stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the entry point from the ELF header.
|
|
||||||
// Does not return!
|
|
||||||
entry = (void(*)(void))(elf->entry);
|
|
||||||
entry();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
waitdisk(void)
|
|
||||||
{
|
|
||||||
// Wait for disk ready.
|
|
||||||
while((inb(0x1F7) & 0xC0) != 0x40)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a single sector at offset into dst.
|
|
||||||
void
|
|
||||||
readsect(void *dst, uint offset)
|
|
||||||
{
|
|
||||||
// Issue command.
|
|
||||||
waitdisk();
|
|
||||||
outb(0x1F2, 1); // count = 1
|
|
||||||
outb(0x1F3, offset);
|
|
||||||
outb(0x1F4, offset >> 8);
|
|
||||||
outb(0x1F5, offset >> 16);
|
|
||||||
outb(0x1F6, (offset >> 24) | 0xE0);
|
|
||||||
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
|
|
||||||
|
|
||||||
// Read data.
|
|
||||||
waitdisk();
|
|
||||||
insl(0x1F0, dst, SECTSIZE/4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
|
|
||||||
// Might copy more than asked.
|
|
||||||
void
|
|
||||||
readseg(uchar* pa, uint count, uint offset)
|
|
||||||
{
|
|
||||||
uchar* epa;
|
|
||||||
|
|
||||||
epa = pa + count;
|
|
||||||
|
|
||||||
// Round down to sector boundary.
|
|
||||||
pa -= offset % SECTSIZE;
|
|
||||||
|
|
||||||
// Translate from bytes to sectors; kernel starts at sector 1.
|
|
||||||
offset = (offset / SECTSIZE) + 1;
|
|
||||||
|
|
||||||
// If this is too slow, we could read lots of sectors at a time.
|
|
||||||
// We'd write more to memory than asked, but it doesn't matter --
|
|
||||||
// we load in increasing order.
|
|
||||||
for(; pa < epa; pa += SECTSIZE, offset++)
|
|
||||||
readsect(pa, offset);
|
|
||||||
}
|
|
13
entry.S
13
entry.S
|
@ -10,12 +10,15 @@
|
||||||
.section .text
|
.section .text
|
||||||
.globl _entry
|
.globl _entry
|
||||||
_entry:
|
_entry:
|
||||||
# set up a stack for C; stack0 is declared in start.
|
# set up a stack for C.
|
||||||
|
# stack0 is declared in start,
|
||||||
|
# with 4096 bytes per CPU.
|
||||||
la sp, stack0
|
la sp, stack0
|
||||||
addi sp, sp, 1024
|
li a0, 1024*4
|
||||||
addi sp, sp, 1024
|
csrr a1, mhartid
|
||||||
addi sp, sp, 1024
|
addi a1, a1, 1
|
||||||
addi sp, sp, 1024
|
mul a0, a0, a1
|
||||||
|
add sp, sp, a0
|
||||||
# jump to mstart() in start.c
|
# jump to mstart() in start.c
|
||||||
call mstart
|
call mstart
|
||||||
junk:
|
junk:
|
||||||
|
|
50
kbd.c
50
kbd.c
|
@ -1,50 +0,0 @@
|
||||||
#include "types.h"
|
|
||||||
#include "x86.h"
|
|
||||||
#include "defs.h"
|
|
||||||
#include "kbd.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
kbdgetc(void)
|
|
||||||
{
|
|
||||||
static uint shift;
|
|
||||||
static uchar *charcode[4] = {
|
|
||||||
normalmap, shiftmap, ctlmap, ctlmap
|
|
||||||
};
|
|
||||||
uint st, data, c;
|
|
||||||
|
|
||||||
st = inb(KBSTATP);
|
|
||||||
if((st & KBS_DIB) == 0)
|
|
||||||
return -1;
|
|
||||||
data = inb(KBDATAP);
|
|
||||||
|
|
||||||
if(data == 0xE0){
|
|
||||||
shift |= E0ESC;
|
|
||||||
return 0;
|
|
||||||
} else if(data & 0x80){
|
|
||||||
// Key released
|
|
||||||
data = (shift & E0ESC ? data : data & 0x7F);
|
|
||||||
shift &= ~(shiftcode[data] | E0ESC);
|
|
||||||
return 0;
|
|
||||||
} else if(shift & E0ESC){
|
|
||||||
// Last character was an E0 escape; or with 0x80
|
|
||||||
data |= 0x80;
|
|
||||||
shift &= ~E0ESC;
|
|
||||||
}
|
|
||||||
|
|
||||||
shift |= shiftcode[data];
|
|
||||||
shift ^= togglecode[data];
|
|
||||||
c = charcode[shift & (CTL | SHIFT)][data];
|
|
||||||
if(shift & CAPSLOCK){
|
|
||||||
if('a' <= c && c <= 'z')
|
|
||||||
c += 'A' - 'a';
|
|
||||||
else if('A' <= c && c <= 'Z')
|
|
||||||
c += 'a' - 'A';
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
kbdintr(void)
|
|
||||||
{
|
|
||||||
consoleintr(kbdgetc);
|
|
||||||
}
|
|
112
kbd.h
112
kbd.h
|
@ -1,112 +0,0 @@
|
||||||
// PC keyboard interface constants
|
|
||||||
|
|
||||||
#define KBSTATP 0x64 // kbd controller status port(I)
|
|
||||||
#define KBS_DIB 0x01 // kbd data in buffer
|
|
||||||
#define KBDATAP 0x60 // kbd data port(I)
|
|
||||||
|
|
||||||
#define NO 0
|
|
||||||
|
|
||||||
#define SHIFT (1<<0)
|
|
||||||
#define CTL (1<<1)
|
|
||||||
#define ALT (1<<2)
|
|
||||||
|
|
||||||
#define CAPSLOCK (1<<3)
|
|
||||||
#define NUMLOCK (1<<4)
|
|
||||||
#define SCROLLLOCK (1<<5)
|
|
||||||
|
|
||||||
#define E0ESC (1<<6)
|
|
||||||
|
|
||||||
// Special keycodes
|
|
||||||
#define KEY_HOME 0xE0
|
|
||||||
#define KEY_END 0xE1
|
|
||||||
#define KEY_UP 0xE2
|
|
||||||
#define KEY_DN 0xE3
|
|
||||||
#define KEY_LF 0xE4
|
|
||||||
#define KEY_RT 0xE5
|
|
||||||
#define KEY_PGUP 0xE6
|
|
||||||
#define KEY_PGDN 0xE7
|
|
||||||
#define KEY_INS 0xE8
|
|
||||||
#define KEY_DEL 0xE9
|
|
||||||
|
|
||||||
// C('A') == Control-A
|
|
||||||
#define C(x) (x - '@')
|
|
||||||
|
|
||||||
static uchar shiftcode[256] =
|
|
||||||
{
|
|
||||||
[0x1D] CTL,
|
|
||||||
[0x2A] SHIFT,
|
|
||||||
[0x36] SHIFT,
|
|
||||||
[0x38] ALT,
|
|
||||||
[0x9D] CTL,
|
|
||||||
[0xB8] ALT
|
|
||||||
};
|
|
||||||
|
|
||||||
static uchar togglecode[256] =
|
|
||||||
{
|
|
||||||
[0x3A] CAPSLOCK,
|
|
||||||
[0x45] NUMLOCK,
|
|
||||||
[0x46] SCROLLLOCK
|
|
||||||
};
|
|
||||||
|
|
||||||
static uchar normalmap[256] =
|
|
||||||
{
|
|
||||||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
|
||||||
'7', '8', '9', '0', '-', '=', '\b', '\t',
|
|
||||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
|
|
||||||
'o', 'p', '[', ']', '\n', NO, 'a', 's',
|
|
||||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
|
|
||||||
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
|
|
||||||
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
|
|
||||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
|
||||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
|
||||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
||||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
|
||||||
[0x9C] '\n', // KP_Enter
|
|
||||||
[0xB5] '/', // KP_Div
|
|
||||||
[0xC8] KEY_UP, [0xD0] KEY_DN,
|
|
||||||
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
|
|
||||||
[0xCB] KEY_LF, [0xCD] KEY_RT,
|
|
||||||
[0x97] KEY_HOME, [0xCF] KEY_END,
|
|
||||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
|
||||||
};
|
|
||||||
|
|
||||||
static uchar shiftmap[256] =
|
|
||||||
{
|
|
||||||
NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
|
|
||||||
'&', '*', '(', ')', '_', '+', '\b', '\t',
|
|
||||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
|
|
||||||
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
|
|
||||||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
|
|
||||||
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
|
|
||||||
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
|
|
||||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
|
||||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
|
||||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
|
||||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
|
||||||
[0x9C] '\n', // KP_Enter
|
|
||||||
[0xB5] '/', // KP_Div
|
|
||||||
[0xC8] KEY_UP, [0xD0] KEY_DN,
|
|
||||||
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
|
|
||||||
[0xCB] KEY_LF, [0xCD] KEY_RT,
|
|
||||||
[0x97] KEY_HOME, [0xCF] KEY_END,
|
|
||||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
|
||||||
};
|
|
||||||
|
|
||||||
static uchar ctlmap[256] =
|
|
||||||
{
|
|
||||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
|
||||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
|
||||||
C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
|
|
||||||
C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
|
|
||||||
C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
|
|
||||||
NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
|
|
||||||
C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
|
|
||||||
[0x9C] '\r', // KP_Enter
|
|
||||||
[0xB5] C('/'), // KP_Div
|
|
||||||
[0xC8] KEY_UP, [0xD0] KEY_DN,
|
|
||||||
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
|
|
||||||
[0xCB] KEY_LF, [0xCD] KEY_RT,
|
|
||||||
[0x97] KEY_HOME, [0xCF] KEY_END,
|
|
||||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
|
||||||
};
|
|
||||||
|
|
229
lapic.c
229
lapic.c
|
@ -1,229 +0,0 @@
|
||||||
// The local APIC manages internal (non-I/O) interrupts.
|
|
||||||
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
|
|
||||||
|
|
||||||
#include "param.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "defs.h"
|
|
||||||
#include "date.h"
|
|
||||||
#include "memlayout.h"
|
|
||||||
#include "traps.h"
|
|
||||||
#include "mmu.h"
|
|
||||||
#include "x86.h"
|
|
||||||
|
|
||||||
// Local APIC registers, divided by 4 for use as uint[] indices.
|
|
||||||
#define ID (0x0020/4) // ID
|
|
||||||
#define VER (0x0030/4) // Version
|
|
||||||
#define TPR (0x0080/4) // Task Priority
|
|
||||||
#define EOI (0x00B0/4) // EOI
|
|
||||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
|
||||||
#define ENABLE 0x00000100 // Unit Enable
|
|
||||||
#define ESR (0x0280/4) // Error Status
|
|
||||||
#define ICRLO (0x0300/4) // Interrupt Command
|
|
||||||
#define INIT 0x00000500 // INIT/RESET
|
|
||||||
#define STARTUP 0x00000600 // Startup IPI
|
|
||||||
#define DELIVS 0x00001000 // Delivery status
|
|
||||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
|
||||||
#define DEASSERT 0x00000000
|
|
||||||
#define LEVEL 0x00008000 // Level triggered
|
|
||||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
|
||||||
#define BUSY 0x00001000
|
|
||||||
#define FIXED 0x00000000
|
|
||||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
|
||||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
|
||||||
#define X1 0x0000000B // divide counts by 1
|
|
||||||
#define PERIODIC 0x00020000 // Periodic
|
|
||||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
|
||||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
|
||||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
|
||||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
|
||||||
#define MASKED 0x00010000 // Interrupt masked
|
|
||||||
#define TICR (0x0380/4) // Timer Initial Count
|
|
||||||
#define TCCR (0x0390/4) // Timer Current Count
|
|
||||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
|
||||||
|
|
||||||
volatile uint *lapic; // Initialized in mp.c
|
|
||||||
|
|
||||||
//PAGEBREAK!
|
|
||||||
static void
|
|
||||||
lapicw(int index, int value)
|
|
||||||
{
|
|
||||||
lapic[index] = value;
|
|
||||||
lapic[ID]; // wait for write to finish, by reading
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
lapicinit(void)
|
|
||||||
{
|
|
||||||
if(!lapic)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Enable local APIC; set spurious interrupt vector.
|
|
||||||
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
|
|
||||||
|
|
||||||
// The timer repeatedly counts down at bus frequency
|
|
||||||
// from lapic[TICR] and then issues an interrupt.
|
|
||||||
// If xv6 cared more about precise timekeeping,
|
|
||||||
// TICR would be calibrated using an external time source.
|
|
||||||
lapicw(TDCR, X1);
|
|
||||||
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
|
|
||||||
lapicw(TICR, 10000000);
|
|
||||||
|
|
||||||
// Disable logical interrupt lines.
|
|
||||||
lapicw(LINT0, MASKED);
|
|
||||||
lapicw(LINT1, MASKED);
|
|
||||||
|
|
||||||
// Disable performance counter overflow interrupts
|
|
||||||
// on machines that provide that interrupt entry.
|
|
||||||
if(((lapic[VER]>>16) & 0xFF) >= 4)
|
|
||||||
lapicw(PCINT, MASKED);
|
|
||||||
|
|
||||||
// Map error interrupt to IRQ_ERROR.
|
|
||||||
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
|
|
||||||
|
|
||||||
// Clear error status register (requires back-to-back writes).
|
|
||||||
lapicw(ESR, 0);
|
|
||||||
lapicw(ESR, 0);
|
|
||||||
|
|
||||||
// Ack any outstanding interrupts.
|
|
||||||
lapicw(EOI, 0);
|
|
||||||
|
|
||||||
// Send an Init Level De-Assert to synchronise arbitration ID's.
|
|
||||||
lapicw(ICRHI, 0);
|
|
||||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
|
||||||
while(lapic[ICRLO] & DELIVS)
|
|
||||||
;
|
|
||||||
|
|
||||||
// Enable interrupts on the APIC (but not on the processor).
|
|
||||||
lapicw(TPR, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
lapicid(void)
|
|
||||||
{
|
|
||||||
if (!lapic)
|
|
||||||
return 0;
|
|
||||||
return lapic[ID] >> 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acknowledge interrupt.
|
|
||||||
void
|
|
||||||
lapiceoi(void)
|
|
||||||
{
|
|
||||||
if(lapic)
|
|
||||||
lapicw(EOI, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spin for a given number of microseconds.
|
|
||||||
// On real hardware would want to tune this dynamically.
|
|
||||||
void
|
|
||||||
microdelay(int us)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CMOS_PORT 0x70
|
|
||||||
#define CMOS_RETURN 0x71
|
|
||||||
|
|
||||||
// Start additional processor running entry code at addr.
|
|
||||||
// See Appendix B of MultiProcessor Specification.
|
|
||||||
void
|
|
||||||
lapicstartap(uchar apicid, uint addr)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
ushort *wrv;
|
|
||||||
|
|
||||||
// "The BSP must initialize CMOS shutdown code to 0AH
|
|
||||||
// and the warm reset vector (DWORD based at 40:67) to point at
|
|
||||||
// the AP startup code prior to the [universal startup algorithm]."
|
|
||||||
outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code
|
|
||||||
outb(CMOS_PORT+1, 0x0A);
|
|
||||||
wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector
|
|
||||||
wrv[0] = 0;
|
|
||||||
wrv[1] = addr >> 4;
|
|
||||||
|
|
||||||
// "Universal startup algorithm."
|
|
||||||
// Send INIT (level-triggered) interrupt to reset other CPU.
|
|
||||||
lapicw(ICRHI, apicid<<24);
|
|
||||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
|
||||||
microdelay(200);
|
|
||||||
lapicw(ICRLO, INIT | LEVEL);
|
|
||||||
microdelay(100); // should be 10ms, but too slow in Bochs!
|
|
||||||
|
|
||||||
// Send startup IPI (twice!) to enter code.
|
|
||||||
// Regular hardware is supposed to only accept a STARTUP
|
|
||||||
// when it is in the halted state due to an INIT. So the second
|
|
||||||
// should be ignored, but it is part of the official Intel algorithm.
|
|
||||||
// Bochs complains about the second one. Too bad for Bochs.
|
|
||||||
for(i = 0; i < 2; i++){
|
|
||||||
lapicw(ICRHI, apicid<<24);
|
|
||||||
lapicw(ICRLO, STARTUP | (addr>>12));
|
|
||||||
microdelay(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CMOS_STATA 0x0a
|
|
||||||
#define CMOS_STATB 0x0b
|
|
||||||
#define CMOS_UIP (1 << 7) // RTC update in progress
|
|
||||||
|
|
||||||
#define SECS 0x00
|
|
||||||
#define MINS 0x02
|
|
||||||
#define HOURS 0x04
|
|
||||||
#define DAY 0x07
|
|
||||||
#define MONTH 0x08
|
|
||||||
#define YEAR 0x09
|
|
||||||
|
|
||||||
static uint
|
|
||||||
cmos_read(uint reg)
|
|
||||||
{
|
|
||||||
outb(CMOS_PORT, reg);
|
|
||||||
microdelay(200);
|
|
||||||
|
|
||||||
return inb(CMOS_RETURN);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fill_rtcdate(struct rtcdate *r)
|
|
||||||
{
|
|
||||||
r->second = cmos_read(SECS);
|
|
||||||
r->minute = cmos_read(MINS);
|
|
||||||
r->hour = cmos_read(HOURS);
|
|
||||||
r->day = cmos_read(DAY);
|
|
||||||
r->month = cmos_read(MONTH);
|
|
||||||
r->year = cmos_read(YEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// qemu seems to use 24-hour GWT and the values are BCD encoded
|
|
||||||
void
|
|
||||||
cmostime(struct rtcdate *r)
|
|
||||||
{
|
|
||||||
struct rtcdate t1, t2;
|
|
||||||
int sb, bcd;
|
|
||||||
|
|
||||||
sb = cmos_read(CMOS_STATB);
|
|
||||||
|
|
||||||
bcd = (sb & (1 << 2)) == 0;
|
|
||||||
|
|
||||||
// make sure CMOS doesn't modify time while we read it
|
|
||||||
for(;;) {
|
|
||||||
fill_rtcdate(&t1);
|
|
||||||
if(cmos_read(CMOS_STATA) & CMOS_UIP)
|
|
||||||
continue;
|
|
||||||
fill_rtcdate(&t2);
|
|
||||||
if(memcmp(&t1, &t2, sizeof(t1)) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert
|
|
||||||
if(bcd) {
|
|
||||||
#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
|
|
||||||
CONV(second);
|
|
||||||
CONV(minute);
|
|
||||||
CONV(hour );
|
|
||||||
CONV(day );
|
|
||||||
CONV(month );
|
|
||||||
CONV(year );
|
|
||||||
#undef CONV
|
|
||||||
}
|
|
||||||
|
|
||||||
*r = t1;
|
|
||||||
r->year += 2000;
|
|
||||||
}
|
|
5
main.c
5
main.c
|
@ -8,11 +8,12 @@
|
||||||
// Allocate a real stack and switch to it, first
|
// Allocate a real stack and switch to it, first
|
||||||
// doing some setup required for memory allocator to work.
|
// doing some setup required for memory allocator to work.
|
||||||
void
|
void
|
||||||
main()
|
main(int hartid)
|
||||||
{
|
{
|
||||||
|
w_tp(hartid); // save hartid where cpuid() can find it
|
||||||
uartinit(); // serial port
|
uartinit(); // serial port
|
||||||
consoleinit();
|
consoleinit();
|
||||||
printf("entering main()\n");
|
printf("entering main() on hart %d\n", hartid);
|
||||||
kinit(); // physical page allocator
|
kinit(); // physical page allocator
|
||||||
kvminit(); // kernel page table
|
kvminit(); // kernel page table
|
||||||
procinit(); // process table
|
procinit(); // process table
|
||||||
|
|
60
memide.c
60
memide.c
|
@ -1,60 +0,0 @@
|
||||||
// Fake IDE disk; stores blocks in memory.
|
|
||||||
// Useful for running kernel without scratch disk.
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "defs.h"
|
|
||||||
#include "param.h"
|
|
||||||
#include "mmu.h"
|
|
||||||
#include "proc.h"
|
|
||||||
#include "x86.h"
|
|
||||||
#include "traps.h"
|
|
||||||
#include "spinlock.h"
|
|
||||||
#include "sleeplock.h"
|
|
||||||
#include "fs.h"
|
|
||||||
#include "buf.h"
|
|
||||||
|
|
||||||
extern uchar _binary_fs_img_start[], _binary_fs_img_size[];
|
|
||||||
|
|
||||||
static int disksize;
|
|
||||||
static uchar *memdisk;
|
|
||||||
|
|
||||||
void
|
|
||||||
ideinit(void)
|
|
||||||
{
|
|
||||||
memdisk = _binary_fs_img_start;
|
|
||||||
disksize = (uint)_binary_fs_img_size/BSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interrupt handler.
|
|
||||||
void
|
|
||||||
ideintr(void)
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync buf with disk.
|
|
||||||
// If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID.
|
|
||||||
// Else if B_VALID is not set, read buf from disk, set B_VALID.
|
|
||||||
void
|
|
||||||
iderw(struct buf *b)
|
|
||||||
{
|
|
||||||
uchar *p;
|
|
||||||
|
|
||||||
if(!holdingsleep(&b->lock))
|
|
||||||
panic("iderw: buf not locked");
|
|
||||||
if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
|
|
||||||
panic("iderw: nothing to do");
|
|
||||||
if(b->dev != 1)
|
|
||||||
panic("iderw: request not for disk 1");
|
|
||||||
if(b->blockno >= disksize)
|
|
||||||
panic("iderw: block out of range");
|
|
||||||
|
|
||||||
p = memdisk + b->blockno*BSIZE;
|
|
||||||
|
|
||||||
if(b->flags & B_DIRTY){
|
|
||||||
b->flags &= ~B_DIRTY;
|
|
||||||
memmove(p, b->data, BSIZE);
|
|
||||||
} else
|
|
||||||
memmove(b->data, p, BSIZE);
|
|
||||||
b->flags |= B_VALID;
|
|
||||||
}
|
|
|
@ -23,8 +23,7 @@
|
||||||
|
|
||||||
// local interrupt controller, which contains the timer.
|
// local interrupt controller, which contains the timer.
|
||||||
#define CLINT 0x2000000L
|
#define CLINT 0x2000000L
|
||||||
#define CLINT_MSIP0 (CLINT + 0x0)
|
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid))
|
||||||
#define CLINT_MTIMECMP0 (CLINT + 0x4000)
|
|
||||||
#define CLINT_MTIME (CLINT + 0xBFF8)
|
#define CLINT_MTIME (CLINT + 0xBFF8)
|
||||||
|
|
||||||
// qemu puts programmable interrupt controller here.
|
// qemu puts programmable interrupt controller here.
|
||||||
|
|
139
mp.c
139
mp.c
|
@ -1,139 +0,0 @@
|
||||||
// Multiprocessor support
|
|
||||||
// Search memory for MP description structures.
|
|
||||||
// http://developer.intel.com/design/pentium/datashts/24201606.pdf
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "defs.h"
|
|
||||||
#include "param.h"
|
|
||||||
#include "memlayout.h"
|
|
||||||
#include "mp.h"
|
|
||||||
#include "x86.h"
|
|
||||||
#include "mmu.h"
|
|
||||||
#include "proc.h"
|
|
||||||
|
|
||||||
struct cpu cpus[NCPU];
|
|
||||||
int ncpu;
|
|
||||||
uchar ioapicid;
|
|
||||||
|
|
||||||
static uchar
|
|
||||||
sum(uchar *addr, int len)
|
|
||||||
{
|
|
||||||
int i, sum;
|
|
||||||
|
|
||||||
sum = 0;
|
|
||||||
for(i=0; i<len; i++)
|
|
||||||
sum += addr[i];
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for an MP structure in the len bytes at addr.
|
|
||||||
static struct mp*
|
|
||||||
mpsearch1(uint64 a, int len)
|
|
||||||
{
|
|
||||||
uchar *e, *p, *addr;
|
|
||||||
|
|
||||||
addr = P2V(a);
|
|
||||||
e = addr+len;
|
|
||||||
for(p = addr; p < e; p += sizeof(struct mp))
|
|
||||||
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
|
|
||||||
return (struct mp*)p;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for the MP Floating Pointer Structure, which according to the
|
|
||||||
// spec is in one of the following three locations:
|
|
||||||
// 1) in the first KB of the EBDA;
|
|
||||||
// 2) in the last KB of system base memory;
|
|
||||||
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
|
|
||||||
static struct mp*
|
|
||||||
mpsearch(void)
|
|
||||||
{
|
|
||||||
uchar *bda;
|
|
||||||
uint p;
|
|
||||||
struct mp *mp;
|
|
||||||
|
|
||||||
bda = (uchar *) P2V(0x400);
|
|
||||||
if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){
|
|
||||||
if((mp = mpsearch1(p, 1024)))
|
|
||||||
return mp;
|
|
||||||
} else {
|
|
||||||
p = ((bda[0x14]<<8)|bda[0x13])*1024;
|
|
||||||
if((mp = mpsearch1(p-1024, 1024)))
|
|
||||||
return mp;
|
|
||||||
}
|
|
||||||
return mpsearch1(0xF0000, 0x10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for an MP configuration table. For now,
|
|
||||||
// don't accept the default configurations (physaddr == 0).
|
|
||||||
// Check for correct signature, calculate the checksum and,
|
|
||||||
// if correct, check the version.
|
|
||||||
// To do: check extended table checksum.
|
|
||||||
static struct mpconf*
|
|
||||||
mpconfig(struct mp **pmp)
|
|
||||||
{
|
|
||||||
struct mpconf *conf;
|
|
||||||
struct mp *mp;
|
|
||||||
|
|
||||||
if((mp = mpsearch()) == 0 || mp->physaddr == 0)
|
|
||||||
return 0;
|
|
||||||
conf = (struct mpconf*) P2V((uint64) mp->physaddr);
|
|
||||||
if(memcmp(conf, "PCMP", 4) != 0)
|
|
||||||
return 0;
|
|
||||||
if(conf->version != 1 && conf->version != 4)
|
|
||||||
return 0;
|
|
||||||
if(sum((uchar*)conf, conf->length) != 0)
|
|
||||||
return 0;
|
|
||||||
*pmp = mp;
|
|
||||||
return conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mpinit(void)
|
|
||||||
{
|
|
||||||
uchar *p, *e;
|
|
||||||
int ismp;
|
|
||||||
struct mp *mp;
|
|
||||||
struct mpconf *conf;
|
|
||||||
struct mpproc *proc;
|
|
||||||
struct mpioapic *ioapic;
|
|
||||||
|
|
||||||
if((conf = mpconfig(&mp)) == 0)
|
|
||||||
panic("Expect to run on an SMP");
|
|
||||||
ismp = 1;
|
|
||||||
lapic = P2V((uint64)conf->lapicaddr_p);
|
|
||||||
for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
|
|
||||||
switch(*p){
|
|
||||||
case MPPROC:
|
|
||||||
proc = (struct mpproc*)p;
|
|
||||||
if(ncpu < NCPU) {
|
|
||||||
cpus[ncpu].apicid = proc->apicid; // apicid may differ from ncpu
|
|
||||||
ncpu++;
|
|
||||||
}
|
|
||||||
p += sizeof(struct mpproc);
|
|
||||||
continue;
|
|
||||||
case MPIOAPIC:
|
|
||||||
ioapic = (struct mpioapic*)p;
|
|
||||||
ioapicid = ioapic->apicno;
|
|
||||||
p += sizeof(struct mpioapic);
|
|
||||||
continue;
|
|
||||||
case MPBUS:
|
|
||||||
case MPIOINTR:
|
|
||||||
case MPLINTR:
|
|
||||||
p += 8;
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
ismp = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!ismp)
|
|
||||||
panic("Didn't find a suitable machine");
|
|
||||||
|
|
||||||
if(mp->imcrp){
|
|
||||||
// Bochs doesn't support IMCR, so this doesn't run on Bochs.
|
|
||||||
// But it would on real hardware.
|
|
||||||
outb(0x22, 0x70); // Select IMCR
|
|
||||||
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
|
|
||||||
}
|
|
||||||
}
|
|
56
mp.h
56
mp.h
|
@ -1,56 +0,0 @@
|
||||||
// See MultiProcessor Specification Version 1.[14]
|
|
||||||
|
|
||||||
struct mp { // floating pointer
|
|
||||||
uchar signature[4]; // "_MP_"
|
|
||||||
uint32 physaddr; // phys addr of MP config table
|
|
||||||
uchar length; // 1
|
|
||||||
uchar specrev; // [14]
|
|
||||||
uchar checksum; // all bytes must add up to 0
|
|
||||||
uchar type; // MP system config type
|
|
||||||
uchar imcrp;
|
|
||||||
uchar reserved[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mpconf { // configuration table header
|
|
||||||
uchar signature[4]; // "PCMP"
|
|
||||||
ushort length; // total table length
|
|
||||||
uchar version; // [14]
|
|
||||||
uchar checksum; // all bytes must add up to 0
|
|
||||||
uchar product[20]; // product id
|
|
||||||
uint32 oemtable; // OEM table pointer
|
|
||||||
ushort oemlength; // OEM table length
|
|
||||||
ushort entry; // entry count
|
|
||||||
uint32 lapicaddr_p; // address of local APIC
|
|
||||||
ushort xlength; // extended table length
|
|
||||||
uchar xchecksum; // extended table checksum
|
|
||||||
uchar reserved;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mpproc { // processor table entry
|
|
||||||
uchar type; // entry type (0)
|
|
||||||
uchar apicid; // local APIC id
|
|
||||||
uchar version; // local APIC verison
|
|
||||||
uchar flags; // CPU flags
|
|
||||||
#define MPBOOT 0x02 // This proc is the bootstrap processor.
|
|
||||||
uchar signature[4]; // CPU signature
|
|
||||||
uint feature; // feature flags from CPUID instruction
|
|
||||||
uchar reserved[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mpioapic { // I/O APIC table entry
|
|
||||||
uchar type; // entry type (2)
|
|
||||||
uchar apicno; // I/O APIC id
|
|
||||||
uchar version; // I/O APIC version
|
|
||||||
uchar flags; // I/O APIC flags
|
|
||||||
uint32 addr_p; // I/O APIC address
|
|
||||||
};
|
|
||||||
|
|
||||||
// Table entry types
|
|
||||||
#define MPPROC 0x00 // One per processor
|
|
||||||
#define MPBUS 0x01 // One per bus
|
|
||||||
#define MPIOAPIC 0x02 // One per I/O APIC
|
|
||||||
#define MPIOINTR 0x03 // One per bus interrupt source
|
|
||||||
#define MPLINTR 0x04 // One per system interrupt source
|
|
||||||
|
|
||||||
//PAGEBREAK!
|
|
||||||
// Blank page.
|
|
19
picirq.c
19
picirq.c
|
@ -1,19 +0,0 @@
|
||||||
#include "types.h"
|
|
||||||
#include "x86.h"
|
|
||||||
#include "traps.h"
|
|
||||||
|
|
||||||
// I/O Addresses of the two programmable interrupt controllers
|
|
||||||
#define IO_PIC1 0x20 // Master (IRQs 0-7)
|
|
||||||
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
|
|
||||||
|
|
||||||
// Don't use the 8259A interrupt controllers. Xv6 assumes SMP hardware.
|
|
||||||
void
|
|
||||||
picinit(void)
|
|
||||||
{
|
|
||||||
// mask all interrupts
|
|
||||||
outb(IO_PIC1+1, 0xFF);
|
|
||||||
outb(IO_PIC2+1, 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
//PAGEBREAK!
|
|
||||||
// Blank page.
|
|
27
proc.c
27
proc.c
|
@ -32,28 +32,33 @@ procinit(void)
|
||||||
initlock(&ptable.lock, "ptable");
|
initlock(&ptable.lock, "ptable");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called with interrupts disabled.
|
// Must be called with interrupts disabled,
|
||||||
// XXX riscv
|
// to prevent race with process being moved
|
||||||
|
// to a different CPU.
|
||||||
int
|
int
|
||||||
cpuid() {
|
cpuid()
|
||||||
return 0;
|
{
|
||||||
|
int id = r_tp();
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return this core's cpu struct.
|
// Return this core's cpu struct.
|
||||||
// XXX riscv
|
// Interrupts must be disabled.
|
||||||
struct cpu*
|
struct cpu*
|
||||||
mycpu(void) {
|
mycpu(void) {
|
||||||
struct cpu *c;
|
int id = cpuid();
|
||||||
c = &cpus[0];
|
struct cpu *c = &cpus[id];
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable interrupts so that we are not rescheduled
|
// Return the current struct proc *.
|
||||||
// while reading proc from the cpu structure
|
|
||||||
// XXX riscv
|
|
||||||
struct proc*
|
struct proc*
|
||||||
myproc(void) {
|
myproc(void) {
|
||||||
return cpus[0].proc;
|
// XXX push intr off
|
||||||
|
struct cpu *c = mycpu();
|
||||||
|
struct proc *p = c->proc;
|
||||||
|
// XXX pop intr
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
//PAGEBREAK: 32
|
//PAGEBREAK: 32
|
||||||
|
|
8
proc.h
8
proc.h
|
@ -20,10 +20,7 @@ struct context {
|
||||||
|
|
||||||
// Per-CPU state
|
// Per-CPU state
|
||||||
struct cpu {
|
struct cpu {
|
||||||
uint64 syscallno; // Temporary used by sysentry
|
|
||||||
uint64 usp; // Temporary used by sysentry
|
|
||||||
struct proc *proc; // The process running on this cpu or null
|
struct proc *proc; // The process running on this cpu or null
|
||||||
struct cpu *cpu; // XXX
|
|
||||||
struct context scheduler; // swtch() here to enter scheduler
|
struct context scheduler; // swtch() here to enter scheduler
|
||||||
volatile uint started; // Has the CPU started?
|
volatile uint started; // Has the CPU started?
|
||||||
int ncli; // Depth of pushcli nesting.
|
int ncli; // Depth of pushcli nesting.
|
||||||
|
@ -31,7 +28,6 @@ struct cpu {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct cpu cpus[NCPU];
|
extern struct cpu cpus[NCPU];
|
||||||
extern int ncpu;
|
|
||||||
|
|
||||||
//PAGEBREAK: 17
|
//PAGEBREAK: 17
|
||||||
|
|
||||||
|
@ -47,9 +43,9 @@ extern int ncpu;
|
||||||
struct trapframe {
|
struct trapframe {
|
||||||
/* 0 */ uint64 kernel_satp;
|
/* 0 */ uint64 kernel_satp;
|
||||||
/* 8 */ uint64 kernel_sp;
|
/* 8 */ uint64 kernel_sp;
|
||||||
/* 16 */ uint64 kernel_trap; // address of trap()
|
/* 16 */ uint64 kernel_trap; // usertrap()
|
||||||
/* 24 */ uint64 epc; // saved user program counter
|
/* 24 */ uint64 epc; // saved user program counter
|
||||||
/* 32 */ uint64 unused;
|
/* 32 */ uint64 hartid;
|
||||||
/* 40 */ uint64 ra;
|
/* 40 */ uint64 ra;
|
||||||
/* 48 */ uint64 sp;
|
/* 48 */ uint64 sp;
|
||||||
/* 56 */ uint64 gp;
|
/* 56 */ uint64 gp;
|
||||||
|
|
25
riscv.h
25
riscv.h
|
@ -1,3 +1,12 @@
|
||||||
|
// which hart (core) is this?
|
||||||
|
static inline uint64
|
||||||
|
r_mhartid()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm("csrr %0, mhartid" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
// Machine Status Register, mstatus
|
// Machine Status Register, mstatus
|
||||||
|
|
||||||
#define MSTATUS_MPP_MASK (3L << 11)
|
#define MSTATUS_MPP_MASK (3L << 11)
|
||||||
|
@ -279,6 +288,22 @@ r_sp()
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read and write tp, the thread pointer, which holds
|
||||||
|
// this core's hartid (core number), the index into cpus[].
|
||||||
|
static inline uint64
|
||||||
|
r_tp()
|
||||||
|
{
|
||||||
|
uint64 x;
|
||||||
|
asm("mv %0, tp" : "=r" (x) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w_tp(uint64 x)
|
||||||
|
{
|
||||||
|
asm("mv tp, %0" : : "r" (x));
|
||||||
|
}
|
||||||
|
|
||||||
#define PGSIZE 4096 // bytes per page
|
#define PGSIZE 4096 // bytes per page
|
||||||
#define PGSHIFT 12 // bits of offset within a page
|
#define PGSHIFT 12 // bits of offset within a page
|
||||||
|
|
||||||
|
|
34
start.c
34
start.c
|
@ -1,25 +1,19 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "param.h"
|
||||||
#include "memlayout.h"
|
#include "memlayout.h"
|
||||||
#include "riscv.h"
|
#include "riscv.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
|
|
||||||
void main();
|
void main();
|
||||||
|
|
||||||
// entry.S uses this as the initial stack.
|
// entry.S needs one stack per CPU.
|
||||||
__attribute__ ((aligned (16))) char stack0[4096];
|
__attribute__ ((aligned (16))) char stack0[4096 * NCPU];
|
||||||
|
|
||||||
// assembly code in kernelvec for machine-mode timer interrupt.
|
// assembly code in kernelvec for machine-mode timer interrupt.
|
||||||
extern void machinevec();
|
extern void machinevec();
|
||||||
|
|
||||||
// scratch area for timer interrupt.
|
// scratch area for timer interrupt, one per CPU.
|
||||||
uint64 mscratch0[8];
|
uint64 mscratch0[NCPU * 32];
|
||||||
|
|
||||||
__attribute__ ((aligned (16)))
|
|
||||||
void
|
|
||||||
xyzzy()
|
|
||||||
{
|
|
||||||
uartputc('I');
|
|
||||||
}
|
|
||||||
|
|
||||||
// entry.S jumps here in machine mode on stack0.
|
// entry.S jumps here in machine mode on stack0.
|
||||||
void
|
void
|
||||||
|
@ -42,15 +36,19 @@ mstart()
|
||||||
w_medeleg(0xffff);
|
w_medeleg(0xffff);
|
||||||
w_mideleg(0xffff);
|
w_mideleg(0xffff);
|
||||||
|
|
||||||
// set up to receive timer interrupts in machine mode.
|
// set up to receive timer interrupts in machine mode,
|
||||||
*(uint64*)CLINT_MTIMECMP0 = *(uint64*)CLINT_MTIME + 10000;
|
// for pre-emptive switching and (on hart 0) to drive time.
|
||||||
mscratch0[4] = CLINT_MTIMECMP0;
|
int id = r_mhartid();
|
||||||
mscratch0[5] = 10000000;
|
uint64 *scratch = &mscratch0[32 * id];
|
||||||
w_mscratch((uint64)mscratch0);
|
*(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + 10000;
|
||||||
|
scratch[4] = CLINT_MTIMECMP(id);
|
||||||
|
scratch[5] = 10000000;
|
||||||
|
w_mscratch((uint64)scratch);
|
||||||
w_mtvec((uint64)machinevec);
|
w_mtvec((uint64)machinevec);
|
||||||
w_mstatus(r_mstatus() | MSTATUS_MIE);
|
w_mstatus(r_mstatus() | MSTATUS_MIE);
|
||||||
w_mie(r_mie() | MIE_MTIE);
|
w_mie(r_mie() | MIE_MTIE);
|
||||||
|
|
||||||
// jump to main in supervisor mode.
|
// call main(hartid) in supervisor mode.
|
||||||
asm("mret");
|
asm("csrr a0, mhartid ; \
|
||||||
|
mret");
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,9 @@ trampin:
|
||||||
# restore kernel stack pointer from p->tf->kernel_sp
|
# restore kernel stack pointer from p->tf->kernel_sp
|
||||||
ld sp, 8(a0)
|
ld sp, 8(a0)
|
||||||
|
|
||||||
|
# make tp hold the current hartid, from p->tf->hartid
|
||||||
|
ld tp, 32(a0)
|
||||||
|
|
||||||
# remember the address of usertrap(), p->tf->kernel_trap
|
# remember the address of usertrap(), p->tf->kernel_trap
|
||||||
ld t0, 16(a0)
|
ld t0, 16(a0)
|
||||||
|
|
||||||
|
|
21
trap.c
21
trap.c
|
@ -24,8 +24,6 @@ trapinit(void)
|
||||||
// set up to take exceptions and traps while in the kernel.
|
// set up to take exceptions and traps while in the kernel.
|
||||||
w_stvec((uint64)kernelvec);
|
w_stvec((uint64)kernelvec);
|
||||||
|
|
||||||
// time, cycle, instret CSRs
|
|
||||||
|
|
||||||
initlock(&tickslock, "time");
|
initlock(&tickslock, "time");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,10 +43,6 @@ usertrap(void)
|
||||||
// since we're now in the kernel.
|
// since we're now in the kernel.
|
||||||
w_stvec((uint64)kernelvec);
|
w_stvec((uint64)kernelvec);
|
||||||
|
|
||||||
//printf("mtimecmp %x mtime %x\n", *(uint64*)CLINT_MTIMECMP0, *(uint64*)CLINT_MTIME);
|
|
||||||
|
|
||||||
*(uint64*)CLINT_MTIMECMP0 = *(uint64*)CLINT_MTIME + 10000;
|
|
||||||
|
|
||||||
struct proc *p = myproc();
|
struct proc *p = myproc();
|
||||||
|
|
||||||
// save user program counter.
|
// save user program counter.
|
||||||
|
@ -102,6 +96,7 @@ usertrapret(void)
|
||||||
p->tf->kernel_satp = r_satp();
|
p->tf->kernel_satp = r_satp();
|
||||||
p->tf->kernel_sp = (uint64)p->kstack + PGSIZE;
|
p->tf->kernel_sp = (uint64)p->kstack + PGSIZE;
|
||||||
p->tf->kernel_trap = (uint64)usertrap;
|
p->tf->kernel_trap = (uint64)usertrap;
|
||||||
|
p->tf->hartid = r_tp();
|
||||||
|
|
||||||
// set up the registers that trampoline.S's sret will use
|
// set up the registers that trampoline.S's sret will use
|
||||||
// to get to user space.
|
// to get to user space.
|
||||||
|
@ -132,9 +127,12 @@ kerneltrap()
|
||||||
{
|
{
|
||||||
uint64 sstatus = r_sstatus();
|
uint64 sstatus = r_sstatus();
|
||||||
uint64 scause = r_scause();
|
uint64 scause = r_scause();
|
||||||
|
uint64 sepc = r_sepc(); // XXX needed only for check at end?
|
||||||
|
|
||||||
if((sstatus & SSTATUS_SPP) == 0)
|
if((sstatus & SSTATUS_SPP) == 0)
|
||||||
panic("kerneltrap: not from supervisor mode");
|
panic("kerneltrap: not from supervisor mode");
|
||||||
|
if(intr_get() != 0)
|
||||||
|
panic("kerneltrap: interrupts enabled");
|
||||||
|
|
||||||
if(devintr() == 0){
|
if(devintr() == 0){
|
||||||
printf("scause 0x%p\n", scause);
|
printf("scause 0x%p\n", scause);
|
||||||
|
@ -142,12 +140,11 @@ kerneltrap()
|
||||||
panic("kerneltrap");
|
panic("kerneltrap");
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn off interrupts to ensure we
|
// XXX assert that we don't have to save/restore sstatus or sepc.
|
||||||
// return with the correct sstatus.
|
if(r_sstatus() != sstatus)
|
||||||
intr_off();
|
panic("kerneltrap sstatus");
|
||||||
|
if(r_sepc() != sepc)
|
||||||
// restore previous interrupt status.
|
panic("kerneltrap sepc");
|
||||||
w_sstatus(sstatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if it's an external interrupt or software interrupt,
|
// check if it's an external interrupt or software interrupt,
|
||||||
|
|
Loading…
Reference in a new issue