plan9fox/sys/src/9/pc64/apbootstrap.s
cinap_lenrek 7bb1a9a185 pc64: map kernel text readonly and everything else no-execute
the idea is to catch bugs and make kernel exploitation
harder by mapping the kernel text section readonly
and everything else no-execute.

l.s maps the KZERO address space using 2MB pages so
to get the 4K granularity for the text section we use
the new ptesplit() function to split that mapping up.

we need to set EFER no-execute enable bit early
in apbootstrap so secondary application processors
will understand the NX bit in our shared kernel page
tables. also APBOOTSTRAP needs to be kept executable.

rebootjump() needs to mark REBOOTADDR page executable.
2019-08-29 07:35:22 +02:00

172 lines
3.9 KiB
ArmAsm

/*
* Start an Application Processor. This must be placed on a 4KB boundary
* somewhere in the 1st MB of conventional memory (APBOOTSTRAP). However,
* due to some shortcuts below it's restricted further to within the 1st
* 64KB. The AP starts in real-mode, with
* CS selector set to the startup memory address/16;
* CS base set to startup memory address;
* CS limit set to 64KB;
* CPL and IP set to 0.
*/
#include "mem.h"
#define NOP BYTE $0x90 /* NOP */
#define pFARJMP32(s, o) BYTE $0xea; /* far jmp ptr32:16 */ \
LONG $o; WORD $s
#define rFARJMP16(s, o) BYTE $0xea; /* far jump ptr16:16 */ \
WORD $o; WORD $s;
#define rFARJMP32(s, o) BYTE $0x66; /* far jump ptr32:16 */ \
pFARJMP32(s, o)
#define rLGDT(gdtptr) BYTE $0x0f; /* LGDT */ \
BYTE $0x01; BYTE $0x16; \
WORD $gdtptr
#define rMOVAX(i) BYTE $0xb8; /* i -> AX */ \
WORD $i;
#define DELAY BYTE $0xEB; /* JMP .+2 */ \
BYTE $0x00
MODE $16
TEXT apbootstrap(SB), 1, $-4
rFARJMP16(0, _apbootstrap-KZERO(SB))
NOP; NOP; NOP;
TEXT _apvector(SB), 1, $-4 /* address APBOOTSTRAP+0x08 */
QUAD $0
TEXT _appml4(SB), 1, $-4 /* address APBOOTSTRAP+0x10 */
QUAD $0
TEXT _apapic(SB), 1, $-4 /* address APBOOTSTRAP+0x18 */
QUAD $0
TEXT _apmach(SB), 1, $-4 /* address APBOOTSTRAP+0x20 */
QUAD $0
TEXT _apefer(SB), 1, $-4
QUAD $0x100 /* Long Mode Enable */
TEXT _apbootstrap(SB), 1, $-4
MOVW CS, AX
MOVW AX, DS /* initialise DS */
rLGDT(_gdtptr32p<>-KZERO(SB)) /* load a basic gdt */
MOVL CR0, AX
ORL $1, AX
MOVL AX, CR0 /* turn on protected mode */
DELAY /* JMP .+2 */
rFARJMP16(SELECTOR(3, SELGDT, 0), _ap32-KZERO(SB))
/*
* Enable and activate Long Mode. From the manual:
* make sure Page Size Extentions are off, and Page Global
* Extensions and Physical Address Extensions are on in CR4;
* set Long Mode Enable in the Extended Feature Enable MSR;
* set Paging Enable in CR0;
* make an inter-segment jump to the Long Mode code.
* It's all in 32-bit mode until the jump is made.
*/
MODE $32
TEXT _ap32(SB), 1, $-4
MOVW $SELECTOR(2, SELGDT, 0), AX
MOVW AX, DS
MOVW AX, ES
MOVW AX, FS
MOVW AX, GS
MOVW AX, SS
MOVL _appml4-KZERO(SB), AX /* physical address of PML4 */
MOVL AX, CR3 /* load the mmu */
DELAY
MOVL CR4, AX
ANDL $~0x00000010, AX /* Page Size */
ORL $0x000000A0, AX /* Page Global, Phys. Address */
MOVL AX, CR4
MOVL $0xc0000080, CX /* Extended Feature Enable */
RDMSR
ORL _apefer-KZERO(SB), AX
WRMSR
MOVL CR0, DX
ANDL $~0x6000000a, DX
ORL $0x80010000, DX /* Paging Enable, Write Protect */
MOVL DX, CR0
pFARJMP32(SELECTOR(KESEG, SELGDT, 0), _ap64-KZERO(SB))
/*
* Long mode. Welcome to 2003.
* Jump out of the identity map space;
* load a proper long mode GDT;
* initialise the stack and call the
* C startup code in m->splpc.
*/
MODE $64
TEXT _ap64(SB), 1, $-4
MOVQ $_gdtptr64v<>(SB), AX
MOVL (AX), GDTR
XORQ AX, AX
MOVW AX, DS /* not used in long mode */
MOVW AX, ES /* not used in long mode */
MOVW AX, FS
MOVW AX, GS
MOVW AX, SS /* not used in long mode */
MOVW AX, LDTR
MOVQ _apmach(SB), SP
MOVQ AX, RUSER /* up = 0; */
MOVQ SP, RMACH /* m = apmach */
ADDQ $MACHSIZE, SP
PUSHQ AX /* clear flags */
POPFQ
MOVQ _apvector(SB), AX
MOVQ _apapic(SB), RARG
PUSHQ RARG
CALL *AX
_halt:
HLT
JMP _halt
TEXT _gdt<>(SB), 1, $-4
/* null descriptor */
LONG $0
LONG $0
/* (KESEG) 64 bit long mode exec segment */
LONG $(0xFFFF)
LONG $(SEGL|SEGG|SEGP|(0xF<<16)|SEGPL(0)|SEGEXEC|SEGR)
/* 32 bit data segment descriptor for 4 gigabytes (PL 0) */
LONG $(0xFFFF)
LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
/* 32 bit exec segment descriptor for 4 gigabytes (PL 0) */
LONG $(0xFFFF)
LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
TEXT _gdtptr32p<>(SB), 1, $-4
WORD $(4*8-1)
LONG $_gdt<>-KZERO(SB)
TEXT _gdtptr64p<>(SB), 1, $-4
WORD $(4*8-1)
QUAD $_gdt<>-KZERO(SB)
TEXT _gdtptr64v<>(SB), 1, $-4
WORD $(4*8-1)
QUAD $_gdt<>(SB)