\ vandys
only os definitions
0 constant PRIV_KERN
\ Trap types
0 constant T_INVAL 2 constant T_LDT 9 constant T_TSS
14 constant T_INTR 15 constant T_TRAP 16 constant T_MEMRO
18 constant T_MEMRW 26 constant T_MEMXR
256 constant NIDT
32 constant CPUIDT
16 constant IDTISA
struct tss
int32 link int32 esp0 int32 ss0 int32 esp1 int32 ss1
int32 esp2 int32 ss2 int32 cr3 int32 eip int32 eflags
int32 eax int32 ecx int32 edx int32 ebx int32 esp
int32 ebp int32 esi int32 edi int32 es int32 cs
int32 ss int32 ds int32 fs int32 gs int32 ldt
int32 iomap
endstruct
\ vandys
0 constant PRIV_KERN
struct seg
int16 limit0 int16 base0 int8 base1 int8 type
int8 limit1 int8 base2
endstruct
: >seg ( ptr u -- ptr' ) seg.size * + ;
struct gate
int16 off0 int16 sel int8 stkwds int8 type int16 off1
endstruct
struct linmem
int16 len int32 addr
endstruct
$20 constant ICU0 $A0 constant ICU1
8 constant SLAVE_IRQ 2 constant MASTER_SLAVE
struct trapframe
int32 esds int32 edi int32 esi int32 ebp int32 espdummy
int32 ebx int32 edx int32 ecx int32 eax
int32 traptype int32 errcode int32 eip int32 ecs
int32 eflags int32 esp int32 ess
endstruct
\ vandys
20 constant HZ
$43 constant PIT_CTRL $40 constant PIT_CH0
$34 constant CMD_SQR_WAVE $00 constant CMD_LATCH
1193180 constant PIT_TICK
HZ 2 / PIT_TICK + HZ / constant PIT_LATCH
4 constant NGDT
: >sel ( u -- u' ) 3 lshift ;
0 constant GDT_NULL 1 constant GDT_KDATA 2 constant GDT_KTEXT
3 constant GDT_TSS
create tss here tss.size dup allot erase
GDT_KTEXT tss tss>cs !
GDT_KDATA dup tss tss>ss0 ! dup tss tss>ds !
dup tss tss>es ! tss tss>ss !
sp0 @ dup tss tss>esp0 tss tss>esp !
\ vandys
: setseg ( base size type segptr -- ) dup seg.size erase
tuck swap 3 shiftl 1 or swap seg>type !
2dup seg>limit0 ! tuck swap 16 shiftr swap !
2dup seg>base0 ! swap 16 shiftr swap ! ;
create gdt here NGDT seg.size * dup allot erase
0 -1 T_MEMRW gdt GDT_KDATA >seg segset
0 -1 T_MEMXR gdt GDT_KTEXT >seg segset
tss tss.size T_TSS gdt GDT_TSS >seg segset ;
create gdtptr linmem.size allot
gdt.size NGDT * 1- gdtptr linmem>len !
gdt gdtptr linmem>addr !
\ TBD: gdtptr lgdt
\ TBD: GDT_TSS >sel ltr
: init_icu ( -- ) $11 ICU0 outb CPUIDT ICU0 1+ outb
4 ICU0 1+ outb 1 ICU0 1+ outb $FF ICU0 1+ outb
2 ICU0 outb
$11 ICU1 outb CPUIDT 8 + ICU1 1+ outb 2 ICU1 1+ outb
1 ICU1 1+ outb $FF ICU1 1+ outb 2 ICU1 outb ;
: init_pit ( -- ) CMD_SQR_WAVE PIT_CTRL outb
PIT_LATCH dup PIT_CH0 outb 8 shiftr PIT_CH0 outb ;
\ vandys
struct gate int16 off0 int16 sel int8 stkwds int8 type
int16 off1 endstruct
: set_idt ( fn type gateptr -- ) gate.size over erase
swap 3 shiftl 1 or
over gate>type c! 2dup gate>off0 w! swap 16 shiftr
over gate>off1 w! GDT_KTEXT >sel swap gate>sel w! ;
create idt NIDT gate.size * allot
: init_trap ( -- ) setup_gdt init_icu init_pit
idt NIDT 0 do ['] stray_intr over T_TRAP swap set_idt loop
: eoi ( -- ) EOI_FLAG dup ICU0 outb ICU1 outb ;
: trap sp@ ." Trap" ;
\ vandys
: interrupt sp@ dup trapframe>traptype @ eoi
Approach for interrupt/exception system of ForthOS:
All memory data structures are set up with run-time code. This means
that we don't metacompile this handling. Fine.
Setup of machine registers is done via init_* routines.
The core init routine is threaded onto the init dictionary to be
run later than any interrupt-potential driver.
Interrupt potential drivers are coded to be polled until they start
fielding interrupts. Clock polls the RTC register, console polls
for keystrokes, etcetera.
During init of such a driver it registers its ISR number and handler
in a table.
When machine interrupt handling is compiled in and activated, this table
is used to vector out a machine level call (no state saved) to the
handler.
The routine saves registers as needed to the stack, then does its thing.
As appropriate, the interrupt routine should flag that its polled mode
aspect should disable itself.
(more on shadow)
\
We'll keep all of the x86 goop hidden inside OS
Selector priv value for kernel mode--we're always in kernel mode
Invalid type Local descriptor Task state segment
Interrupt Traps Memory R/O
Read/write mem Code, readable
# of entries in IDT
First 32 are used for CPU exceptions
Following CPU ones are the 16 ISA interrupt sources
Task State Segment struct definition
Selector value for kernel privilege mode
Shape of memory-type segment entries
Shape of a gateway segment descriptor
Shape of memory argument to lgdt/lidt
Base ports of the two interrupt controllers
Ports on slave ICU, and level on master connected to slave
Layout of registers on entry to a trap
Number of clock interrupts per second
Programmable interval timer control ports
Command to configure count interval
Internal count rate of PIT
Calculate latch count for desired HZ configuration
# of entries in our GDT
: >sel Convert selector index into actual selector value in GDT
Selector values used
Our dummy TSS to keep the x86 happy.
Fill in fields of this dummy TSS.
: setseg Initialize a descriptor table slot with the given
values. Most values are defaulted (4k size, present, etc.)
Set up our GDT slots
4 gig read/write for kernel data
4 gig read/execute for kernel text
Our statically allocated TSS
The argument to l[gli]dt is a linear memory pointer argument
Fill in pointer to our GDT
Then stuff it into the hardware
Now load our task register via the GDT
Reset Vectors served
Chain to ICU1 8086 mode Mask intrs for now
ISR mode
Reset Slave vectors We are slave
8086 mode No intrs here either ISR mode here too
: init_pit Set interrupt rate by using our divided PIT rate by
our selected HZ value.
: eoi Clear interrupt status on both ICU's
\ vandys
How to use multiprocessing edisms()... upper levels need to be converted
to not poll things like key?. To keep event check routines short, a
standard sort of interface needs to be provided so the caller gets a
pointer to a location whose value will change when the caller may need
to wake up.
Console needs a "read count" field, clock needs a running count of seconds
and microseconds, and so forth. Eventually an async API into block(),
but let's not worry about that yet.