shithub: riscv

Download patch

ref: 20b7a19c58c79568424bf40b826b681af86e6ed5
parent: b6039915934ec99e85daa5d4ee964dbda3503352
author: cinap_lenrek <[email protected]>
date: Sun Jul 20 18:49:02 EDT 2014

pc64: preserve user extern registers R14 and R15 across syscalls, use Ureg.bp (RARG) for syscall number

the 6c compiler reserves R14 and R15 for extern register variables,
which is used by the kernel to hold the m and up pointers. until
now, the meaning of R14 and R15 was undefined for userspace and
extern register would not work as the kernel trashes R14 and R15
on syscalls. with this change, user extern registers R14 and R15
are zeroed on exec and otherwise preserved across syscalls. so
userspace *could* use them for per process variables like the
kernel does.

use Ureg.bp (RARG) for syscall number instead of Ureg.ax. this is
less confusing and mirrors the amd64 calling convention.

--- a/sys/src/9/pc64/l.s
+++ b/sys/src/9/pc64/l.s
@@ -686,15 +686,20 @@
 TEXT touser(SB), 1, $-4
 	CLI
 	SWAPGS
-	MOVQ	$UDSEL, AX
+
+	MOVW	$UDSEL, AX
 	MOVW	AX, DS
 	MOVW	AX, ES
+
+	MOVW	$NULLSEL, AX
 	MOVW	AX, FS
 	MOVW	AX, GS
 
-	MOVQ	$(UTZERO+0x28), CX		/* ip */
-	MOVQ	$0x200, R11			/* flags */
+	MOVL	$0, RMACH
+	MOVL	$0, RUSER
 
+	MOVQ	$(UTZERO+0x28), CX		/* ip */
+	MOVL	$0x200, R11			/* flags */
 	MOVQ	RARG, SP			/* sp */
 
 	BYTE $0x48; SYSRET			/* SYSRETQ */
@@ -703,11 +708,12 @@
  */
 TEXT syscallentry(SB), 1, $-4
 	SWAPGS
-	BYTE $0x65; MOVQ 0, RMACH		/* m-> (MOVQ GS:0x0, R15) */
-	MOVQ	16(RMACH), RUSER		/* m->proc */
+	BYTE $0x65; MOVQ 0, AX			/* m-> (MOVQ GS:0x0, AX) */
+	MOVQ	16(AX), BX			/* m->proc */
 	MOVQ	SP, R13
-	MOVQ	16(RUSER), SP			/* m->proc->kstack */
+	MOVQ	16(BX), SP			/* m->proc->kstack */
 	ADDQ	$KSTACK, SP
+
 	PUSHQ	$UDSEL				/* old stack segment */
 	PUSHQ	R13				/* old sp */
 	PUSHQ	R11				/* old flags */
@@ -714,8 +720,7 @@
 	PUSHQ	$UESEL				/* old code segment */
 	PUSHQ	CX				/* old ip */
 
-	SUBQ	$(17*8), SP			/* unsaved registers */
-	PUSHQ	RARG				/* system call number */
+	SUBQ	$(16+16*8), SP			/* unsaved registers */
 
 	MOVW	$UDSEL, (15*8+0)(SP)
 	MOVW	ES, (15*8+2)(SP)
@@ -722,26 +727,36 @@
 	MOVW	FS, (15*8+4)(SP)
 	MOVW	GS, (15*8+6)(SP)
 
+	MOVQ	RMACH, (14*8)(SP)
+	MOVQ	RUSER, (13*8)(SP)
+
+	MOVQ	RARG, (6*8)(SP)			/* system call number */
+
+	MOVQ	AX, RMACH			/* m */
+	MOVQ	BX, RUSER			/* up */
+
 	MOVQ	SP, RARG
-	PUSHQ	SP				/* Ureg* */
+	PUSHQ	SP
 	CALL	syscall(SB)
 
 TEXT forkret(SB), 1, $-4
-	MOVQ	8(SP), AX			/* Ureg.ax */
-	MOVQ	(8+6*8)(SP), BP			/* Ureg.bp */
-	ADDQ	$(16*8), SP			/* registers + arguments */
+	MOVQ	8(SP), AX
+	ADDQ	$(8+13*8), SP			/* unsaved registers */
 
 	CLI
 	SWAPGS
-	MOVW	0(SP), DS
-	MOVW	2(SP), ES
-	MOVW	4(SP), FS
-	MOVW	6(SP), GS
 
-	MOVQ	24(SP), CX			/* ip */
-	MOVQ	40(SP), R11			/* flags */
+	MOVW	22(SP), GS
+	MOVW	20(SP), FS
+	MOVW	18(SP), ES
+	MOVW	16(SP), DS
 
-	MOVQ	48(SP), SP			/* sp */
+	MOVQ	8(SP), RMACH
+	MOVQ	0(SP), RUSER
+
+	MOVQ	40(SP), CX			/* ip */
+	MOVQ	56(SP), R11			/* flags */
+	MOVQ	64(SP), SP			/* sp */
 
 	BYTE $0x48; SYSRET			/* SYSRETQ */
 
--- a/sys/src/9/pc64/trap.c
+++ b/sys/src/9/pc64/trap.c
@@ -686,7 +686,7 @@
 	up->dbgreg = ureg;
 
 	sp = ureg->sp;
-	scallnr = ureg->ax;
+	scallnr = ureg->bp;	/* RARG */
 	up->scallnr = scallnr;
 
 	spllo();
@@ -735,13 +735,6 @@
 				up->errlab[i].sp, up->errlab[i].pc);
 		panic("error stack");
 	}
-
-	/*
-	 *  Put return value in frame.  On the x86 the syscall is
-	 *  just another trap and the return value from syscall is
-	 *  ignored.  On other machines the return value is put into
-	 *  the results register by caller of syscall.
-	 */
 	ureg->ax = ret;
 
 	if(0){
@@ -902,13 +895,9 @@
 		pexit("Suicide", 0);
 	}
 
-	/* don't let user change system flags */
-	nureg->flags = (ureg->flags & ~0xCD5) | (nureg->flags & 0xCD5);
-	nureg->cs |= 3;
-	nureg->ss |= 3;
+	/* don't let user change system flags or segment registers */
+	setregisters(ureg, (char*)ureg, (char*)nureg, sizeof(Ureg));
 
-	memmove(ureg, nureg, sizeof(Ureg));
-
 	switch(arg0){
 	case NCONT:
 	case NRSTR:
@@ -965,6 +954,7 @@
 	ureg->cs = UESEL;
 	ureg->ss = ureg->ds = ureg->es = UDSEL;
 	ureg->fs = ureg->gs = NULLSEL;
+	ureg->r14 = ureg->r15 = 0;	/* extern user registers */
 	return (uintptr)USTKTOP-sizeof(Tos);		/* address of kernel/user shared data */
 }
 
@@ -981,7 +971,7 @@
 }
 
 /* This routine must save the values of registers the user is not permitted
- * to write from devproc and then restore the saved values before returning.
+ * to write from devproc and noted() and then restore the saved values before returning.
  */
 void
 setregisters(Ureg* ureg, char* pureg, char* uva, int n)
@@ -995,7 +985,7 @@
 	if(ureg->fs != UDSEL)
 		ureg->fs = NULLSEL;
 	if(ureg->gs != UDSEL)
-		ureg->gs = 0;
+		ureg->gs = NULLSEL;
 	ureg->flags = (ureg->flags & 0x00ff) | (flags & 0xff00);
 	ureg->pc &= UADDRMASK;
 }
@@ -1063,8 +1053,7 @@
 	Ureg *ureg;
 
 	ureg = p->dbgreg;
-	if(ureg == 0)
+	if(ureg == nil)
 		return 0;
-
 	return ureg->pc;
 }