Author: wmb Date: 2008-01-17 10:19:13 +0100 (Thu, 17 Jan 2008) New Revision: 778
Added: cpu/x86/pc/biosload/callbios.fth cpu/x86/pc/biosload/rmenter.fth Log: x86 real mode support - initial checkin
Added: cpu/x86/pc/biosload/callbios.fth =================================================================== --- cpu/x86/pc/biosload/callbios.fth (rev 0) +++ cpu/x86/pc/biosload/callbios.fth 2008-01-17 09:19:13 UTC (rev 778) @@ -0,0 +1,211 @@ +purpose: Call real-mode BIOS interrupts from protected mode + +\ For now we assume the following descriptors: +\ 08 - 16-bit code 0..ffff +\ 10 - 16-bit data 0..ffffffff +\ 18 - 16-bit data 0..ffff +\ 20 - 32-bit code 0..ffffffff +\ 28 - 32-bit data 0..ffffffff + +\ We assume that we can use memory from h# 200 to h# 3ff + +\ Bounce buffer at h# 90000 + +h# 8 constant rm-code-desc +h# 18 constant rm-data-desc +h# 20 constant pm-code-desc +h# 28 constant pm-data-desc + +h# 002 constant rm-flagval \ Flags for IRET + +\ Low-memory addresses +h# 200 constant rm-go \ Real mode enter code +h# 280 constant rm-return \ Real mode return code +h# 300 constant rm-idt \ For loading RM IDT with LIDT +h# 308 constant rm-sp \ For loading RM SP with LSS +h# 30c constant pm-sp-save \ Save/restore area for PM SP +h# 310 constant pm-gdt-save \ For loading PM GDT with LGDT +h# 318 constant pm-idt-save \ For loading PM IDT with LIDT +h# 3c0 constant rm-regs \ Real-mode register buffer (in/out) +rm-regs constant rm-gs +rm-gs wa1+ constant rm-fs +rm-fs wa1+ constant rm-es +rm-es wa1+ constant rm-ds +rm-ds wa1+ constant rm-di +rm-di la1+ constant rm-si +rm-si la1+ constant rm-bp +rm-bp la1+ constant rm-xx \ Would be stack pointer +rm-xx la1+ constant rm-bx +rm-bx la1+ constant rm-dx +rm-dx la1+ constant rm-cx +rm-cx la1+ constant rm-ax +rm-ax la1+ constant rm-flags +rm-flags la1+ constant rm-target \ Full pointer for RM call target address +rm-target la1+ constant rm-retloc \ Full pointer to rm-return +rm-retloc la1+ constant rm-rflags \ Flags in case of return via IRET + +label rmint-enter + real-mode + \ This must be copied to low memory + + rm-data-desc # ax mov \ 16-bit data segment + ax ds mov ax es mov ax fs mov ax gs mov ax ss mov + + pm-gdt-save #) sgdt \ So we can get back + pm-idt-save #) sidt \ So we can get back + + rm-idt #) lidt + cr0 ax mov h# fe # al and ax cr0 mov \ Enter real mode + + here 5 + rmint-enter - rm-go + 0 #) far jmp \ Jump to set cs + + \ Now we are running in real mode; fix segments again + cs ax mov ax ds mov ax es mov ax fs mov ax gs mov + + rm-sp #) sp lss + + \ Load the 16-bit registers from the rm-regs area + gs pop fs pop es pop ds pop op: popa op: popf + + far ret \ Now we are running the real-mode target code +end-code +here rmint-enter - constant /rmint-enter + +label rmint-exit + rm-target # sp mov \ Set the stack pointer to the top of the rm reg area + + \ Copy the real-mode registers to the buffer + op: pushf op: pusha ds push es push fs push gs push + + cli + cs ax mov ax ds mov + + op: pm-gdt-save #) lgdt + cr0 ax mov 1 # al or ax cr0 mov + + here 5 + rmint-exit - rm-return + pm-code-desc #) far jmp + + protected-mode + + pm-data-desc # ax mov ax ds mov ax es mov ax gs mov ax gs mov ax ss mov + pm-idt-save #) lidt + + pm-sp-save #) sp mov + popf + popa +c; +here rmint-exit - constant /rmint-exit + +\ Must set up the registers area with register values and the target address +code rm-call ( -- ) + pusha + pushf + + sp pm-sp-save #) mov + cld + cli + sp sp xor + rm-go rm-code-desc #) far jmp +c; + +: >seg:off ( linear -- offset segment ) lwsplit d# 12 lshift ; +: seg:off! ( linear adr -- ) >r >seg:off r@ 2+ w! r> w! ; + +0 value bios-prepped? +: ?prep-bios-call ( -- ) + bios-prepped? if exit then true to bios-prepped? + rmint-enter rm-go /rmint-enter move + rmint-exit rm-return /rmint-exit move + h# ffff rm-idt w! + 0 rm-idt 2+ l! + rm-regs rm-sp seg:off! \ Setup real-mode full pointer for lss + rm-return rm-retloc seg:off! \ Setup return address full pointer + rm-flagval rm-retloc 4 + w! \ Flags for return +; + +: dr + ." ax: " rm-ax ? 2 spaces + ." bx: " rm-bx ? 2 spaces + ." cx: " rm-cx ? 2 spaces + ." dx: " rm-dx ? 2 spaces + ." bp: " rm-bp ? 2 spaces + ." si: " rm-si ? 2 spaces + ." di: " rm-di ? 2 spaces +; + +: { ( int# -- ) ?prep-bios-call rm-regs d# 44 erase 4 * @ rm-target ! ; +: } rm-call ; + +: ax rm-ax ! ; +: ah rm-ax 1+ c! ; +: al rm-ax c! ; +: bx rm-bx l! ; +: cx rm-cx l! ; +: dx rm-dx l! ; +: cf@ rm-flags @ 1 and ; +: es:di >seg:off rm-es w! rm-di l! ; +: video-mode ( mode# -- ) + d# 55 set-tick-limit h# 10 { 3 al bx } d# 10 set-tick-limit +; +: text-mode 3 video-mode ; +: 1024x768x8 h# 105 video-mode ; +: 1024x768x16 h# 116 video-mode ; +: apm-connect16 h# 15 { h# 5302 ax 0 bx } ; +: apm-power-off apm-connect16 h# 15 { h# 5307 ax 1 bx 3 cx } ; +: bios-memsize ( -- n ) + h# 15 { h# e801 ax } + rm-ax @ h# 400 * rm-bx @ h# 10000 * + h# 100000 + +; +: .bios-memory-map ( -- ) + 0 + begin ( continuation ) + h# 15 { bx h# e820 ax h# 10000 es:di d# 20 cx " PAMS" drop @ dx } + rm-flags @ 1 and abort" Not supported" + rm-cx @ + h# 10000 rm-cx @ bounds ?do i @ 9 u.r 4 +loop cr + rm-bx @ ( continuation' ) + ?dup 0= until +; +: bios-config-b@ ( adr -- b ) lwsplit h# 1a { h# b108 ax bx rm-di ! } rm-cx @ ; +: bios-config-w@ ( adr -- w ) lwsplit h# 1a { h# b109 ax bx rm-di ! } rm-cx @ ; +: bios-config-l@ ( adr -- l ) lwsplit h# 1a { h# b10a ax bx rm-di ! } rm-cx @ ; +: bios-config-b! ( b adr -- ) lwsplit h# 1a { h# b10b ax bx rm-di ! rm-cx ! } ; +: bios-config-w! ( w adr -- ) lwsplit h# 1a { h# b10c ax bx rm-di ! rm-cx ! } ; +: bios-config-l! ( l adr -- ) lwsplit h# 1a { h# b10d ax bx rm-di ! rm-cx ! } ; +: disk-status ( -- stat ) h# 13 { h# 0100 ax } rm-ax @ ; +0 value sect/trk +0 value trk/head +0 value #heads +: get-drive-params ( -- ) + h# 13 { 8 ah h# 80 dx } + rm-flags @ 1 and if true exit then + rm-cx @ wbsplit ( cl ch ) + over h# 3f and to sect/trk ( cl ch ) + swap 6 rshift bwjoin 1+ to trk/head ( ) + rm-dx @ 8 rshift 1+ to #heads ( ) +; +: #drive-sectors ( -- n ) + h# 13 { h# 15 ah h# 80 dx } + rm-dx @ rm-cx @ wljoin +; +: reset-disks ( -- ) h# 13 { 0 ah h# 80 dx } ; +: reset-hard-disks ( -- ) h# 13 { h# 0d ah h# 80 dx } ; +: do-rw ( sector1 cyl0 head0 drive0 #sectors ah-val -- ) + h# 13 { ah al ( sector1 cyl0 head0 drive0 ) + swap bwjoin dx ( sector1 cyl0 ) + wbsplit ( sector1 cyl.lo cyl.hi ) + 6 lshift rot or ( cyl.lo cyl.hi|sector ) + swap bwjoin cx + h# 10000 >seg:off rm-es w! bx + } +; +: read-sectors ( sector1 cyl0 head0 drive0 #sectors -- ) 2 do-rw ; +: write-sectors ( sector1 cyl0 head0 drive0 #sectors -- ) 3 do-rw ; + +: lbn>sch ( lbn -- sector cyl head ) + sect/trk /mod swap 1+ swap ( sector rem ) + trk/head /mod ( sector cyl head ) + dup #heads > abort" LBN out of range" +; +: lbn-read-sectors ( lbn drive #sectors -- ) 2>r lbn>sch 2r> read-sectors ; +: lbn-write-sectors ( lbn drive #sectors -- ) 2>r lbn>sch 2r> write-sectors ;
Added: cpu/x86/pc/biosload/rmenter.fth =================================================================== --- cpu/x86/pc/biosload/rmenter.fth (rev 0) +++ cpu/x86/pc/biosload/rmenter.fth 2008-01-17 09:19:13 UTC (rev 778) @@ -0,0 +1,214 @@ +: >seg:off ( linear -- offset segment ) lwsplit d# 12 lshift ; +: seg:off! ( linear adr -- ) >r >seg:off r@ wa1+ w! r> w! ; +: seg:off> ( offset segment -- linear ) 4 lshift + ; +: seg:off@ ( adr -- linear ) dup w@ swap wa1+ w@ seg:off> ; + +h# 8 constant rm-cs +h# 18 constant rm-ds +h# 20 constant pm-cs +h# 28 constant pm-ds + +h# 0.0000 constant rm-base +: +rm ( offset -- adr ) rm-base + ; + +\ Place for the initial registers upon entry to real mode. +\ The real-mode stack pointer will start here +h# f00 +rm constant 'rm-regs +\ (00) 4 * 2: GS,FS,ES,DS +\ (08) 8 * 4: EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX +\ (28) 1 * 4: CS:IP of return address +\ (2c) 1 * 2: flags +\ size is 2e + +h# f30 +rm constant 'rm-idt \ For loading RM IDT with LIDT +h# f38 +rm constant 'rm-sp \ SS:SP For loading RM SP with LSS +h# f3c +rm constant 'pm-sp \ Save/restore area for PM SP +h# f40 +rm constant 'pm-gdt \ For loading PM GDT with LGDT +h# f48 +rm constant 'pm-idt \ For loading PM IDT with LGDT + +h# f50 +rm constant 'rm-to-pm +h# fa0 +rm constant 'pm-to-rm +h# ff0 +rm constant 'rm-enter + +: caller-regs 'rm-sp seg:off@ ; +struct + 2 field >rm-gs + 2 field >rm-fs + 2 field >rm-es + 2 field >rm-ds + 4 field >rm-edi + 4 field >rm-esi + 4 field >rm-ebp + 4 field >rm-exx + 4 field >rm-ebx + 4 field >rm-edx + 4 field >rm-ecx + 4 field >rm-eax + 4 field >rm-retaddr + 2 field >rm-flags +drop + +: rm-ah@ caller-regs >rm-eax 1+ c@ ; +: rm-ah! caller-regs >rm-eax 1+ c! ; +: rm-al@ caller-regs >rm-eax c@ ; +: rm-al! caller-regs >rm-eax c! ; +: rm-ax@ caller-regs >rm-eax w@ ; +: rm-ax! caller-regs >rm-eax w! ; +: rm-dh@ caller-regs >rm-edx 1+ c@ ; +: rm-dh! caller-regs >rm-edx 1+ c! ; +: rm-dl@ caller-regs >rm-edx c@ ; +: rm-dl! caller-regs >rm-edx c! ; +: rm-dx@ caller-regs >rm-edx w@ ; +: rm-dx! caller-regs >rm-edx w! ; + +: rm-set-cf caller-regs >rm-flags dup w@ 1 or swap w! ; +: rm-clr-cf caller-regs >rm-flags dup w@ 1 or swap w! ; + +\ 80ff0 is the target address of the interrupt vector +\ We use different segment:offset representations of that address in +\ the vector table, so the handler code can determine the vector +\ number by inspecting the code segment register value +\ 00: 8000:0ff0 +\ 01: 8001:0fe0 +\ ... +\ ff: 80ff:0000 + +: make-vector-table ( -- ) + h# 100 0 do + h# ff0 i 4 lshift - i /l* w! \ Set offset + rm-base 4 rshift i + i /l* wa1+ w! \ Set segment + loop +; + +label rm-to-pm + real-mode + + \ Stack: (high address) + \ flags (from INT) + \ CS:IP return address (from INT) + \ (low) EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX (high) + \ (low) GS,FS,ES,DS (high) + \ CS for vector + + cli \ This is unnecessary since we got here from an INT + cs push ds pop + + sp 'rm-sp #) mov + ss 'rm-sp 2+ #) mov + + op: 'pm-gdt #) lgdt + cr0 ax mov 1 # al or ax cr0 mov \ Enter protected mode + + here 5 + rm-to-pm - 'rm-to-pm + pm-cs #) far jmp + + protected-mode + + ax ax xor pm-ds # al mov ax ds mov ax es mov ax gs mov ax gs mov ax ss mov + 'rm-idt #) sidt + 'pm-idt #) lidt + 'pm-sp #) sp mov + + popa + popf +c; +here rm-to-pm - constant /rm-to-pm + +label pm-to-rm + \ Interrupts must be off. We don't have a stack at the moment. + \ We got here via a far jmp to a 16-bit code segment, so we are + \ using the 16-bit instruction set, but we're not yet in real mode + \ The assembler uses "real-mode" to mean "16-bit code". + real-mode + + \ This must be copied to low memory + + ax ax xor rm-ds # al mov \ 16-bit data segment + ax ds mov ax es mov ax fs mov ax gs mov ax ss mov + + 'pm-gdt #) sgdt \ So we can get back + 'pm-idt #) sidt \ So we can get back + 'rm-idt #) lidt + cr0 ax mov h# fe # al and ax cr0 mov \ Enter real mode + + here 5 + pm-to-rm - 'pm-to-rm + >seg:off #) far jmp \ Jump to set cs + + \ Now we are running in real mode; fix segments again + cs ax mov ax ds mov ax es mov ax fs mov ax gs mov + + 'rm-sp #) sp lss \ Now we are back on the caller stack + + \ Load the 16-bit registers from the rm-regs area + gs pop fs pop es pop ds pop op: popa + + iret \ Now we are back to the caller +end-code +here pm-to-rm - constant /pm-to-rm + +code rm-return ( -- ) + protected-mode + pushf + pusha + sp 'pm-sp #) mov + + cli + sp sp xor + 'pm-to-rm rm-cs #) far jmp +end-code + +\ This is the common target of all the real-mode interrupt vectors. +\ It lives at 8.0ff0. Upon entry, the code segment register contains +\ 80xx where xx is the vector number, and the IP contains 0yy0 where +\ yy is ff - vector_number. +label rm-enter + real-mode + op: pusha ds push es push fs push gs push + cs push \ Save the Code Segment value + 'rm-to-pm >seg:off #) far jmp \ Normalize the CS value + protected-mode +end-code +here rm-enter - constant /rm-enter + +h# 400 buffer: saved-rm-vectors + +0 value rm-prepped? +: prep-rm ( -- ) + rm-prepped? if exit then true to rm-prepped? + 0 saved-rm-vectors h# 400 move + make-vector-table + + rm-enter 'rm-enter /rm-enter move + rm-to-pm 'rm-to-pm /rm-to-pm move + pm-to-rm 'pm-to-rm /pm-to-rm move + + h# ffff 'rm-idt w! 0 'rm-idt wa1+ l! \ Limit and base +; + +: rm-init-program ( pc -- ) + prep-rm + 'rm-regs h# 2e erase ( pc ) + 'rm-regs h# 28 + seg:off! ( ) + 'rm-regs 'rm-sp seg:off! \ Initial stack pointer below regs +; + +: handle-bios-call ( -- ) + 'rm-sp seg:off@ w@ h# ff and ( int# ) + 'rm-sp w@ 2+ 'rm-sp w! ( int# ) + ." Interrupt " . cr + interact +; +: rm-go ( -- ) + \ Load boot image at 7c00 + h# 7c00 rm-init-program + begin + rm-return + handle-bios-call + again +; +label xx h# 99 # al mov al h# 80 # out begin again end-code +here xx - constant /xx +: put-xx ( adr -- ) xx swap /xx move ; +: get-mbr + " /ide@0" open-dev >r + h# 7c00 h# 3f 1 " read-blocks" r@ $call-method . + r> close-dev +;