(* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)

MODULE MemCache; (** AUTHOR "pjm"; PURPOSE "Memory cache control"; *)

IMPORT SYSTEM, Machine;

CONST
		(** cache properties *)
	UC* = 0; WC* = 1; WT* = 4; WP* = 5; WB* = 6;

	PS = 4096;	(* page size in bytes *)
	M = 100000H;	(* 1K, 1M, 1G *)

	Ok = 0;

TYPE
	SetCacheMessage = POINTER TO RECORD (Machine.Message)
		physAdr: SYSTEM.ADDRESS; size, type: LONGINT;
		res: ARRAY Machine.MaxCPU OF LONGINT
	END;

(* Return the value of the MTTRcap register. *)

PROCEDURE -GetMTTRcapLow(): SET;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	MOV ECX, 0FEH	; MTTRcap
	RDMSR
END GetMTTRcapLow;

(*
(* Return the value of the MTTRdefType register. *)

PROCEDURE -GetMTTRdefTypeLow(): SET;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	MOV ECX, 2FFH	; MTTRdefType
	RDMSR
END GetMTTRdefTypeLow;
*)

(* Return the value of the specified MTTRphysBase register. *)

PROCEDURE -GetMTTRphysBaseLow(n: SYSTEM.ADDRESS): SET;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	POP ECX
	SHL ECX, 1
	ADD ECX, 200H	; MTTRphysBase0
	RDMSR
END GetMTTRphysBaseLow;

(* Return the value of the specified MTTRphysMask register. *)

PROCEDURE -GetMTTRphysMaskLow(n: SYSTEM.ADDRESS): SET;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	POP ECX
	SHL ECX, 1
	ADD ECX, 201H	; MTTRphysMask0
	RDMSR
END GetMTTRphysMaskLow;

(* Set the specified MTTRphysBase register. *)

PROCEDURE -SetMTTRphysBase(n: SYSTEM.ADDRESS; high, low: SET);
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	POP EAX
	POP EDX
	POP ECX
	SHL ECX, 1
	ADD ECX, 200H	; MTTRphysBase0
	WRMSR
END SetMTTRphysBase;

(* Set the specified MTTRphysMask register. *)

PROCEDURE -SetMTTRphysMask(n: SYSTEM.ADDRESS; high, low: SET);
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	POP EAX
	POP EDX
	POP ECX
	SHL ECX, 1
	ADD ECX, 201H	; MTTRphysMask0
	WRMSR
END SetMTTRphysMask;

(** Set the cache properties of the specified physical memory area on the current processor. {physAdr, size MOD PS = 0}  Must be called from supervisor mode. *)

PROCEDURE LocalSetCacheProperties*(physAdr: SYSTEM.ADDRESS; size, type: LONGINT; VAR res: LONGINT);
VAR i, n, f: LONGINT; mask, base: SET; j, k: SYSTEM.ADDRESS;
BEGIN
	ASSERT((physAdr MOD PS = 0) & (size MOD PS = 0) & (size # 0));
	IF (physAdr >= M) OR (physAdr < 0) THEN
		k := size; WHILE k > 0 DO k := ASH(k, 1) END;	(* shift highest set bit into bit 31 *)
		IF k = SHORT(80000000H) THEN	(* only one bit was set => size is power of 2 *)
			IF physAdr MOD size = 0 THEN
				Machine.Acquire(Machine.Memory);	(* hack *)
				IF Machine.MTTR IN Machine.features THEN	(* MTTRs supported *)
					mask := GetMTTRcapLow();
					IF (type # WC) OR (10 IN mask) THEN
						n := SYSTEM.VAL(LONGINT, mask * {0..7});
						i := 0; f := -1; res := Ok;
						WHILE (i # n) & (res = Ok) DO
							mask := GetMTTRphysMaskLow(i);
							IF 11 IN mask THEN	(* entry is valid *)
								mask := mask * {12..MAX(SET)};
								base := GetMTTRphysBaseLow(i) * mask;
								j := physAdr; k := physAdr+size;
								WHILE (j # k) & (SYSTEM.VAL(SET, j) * mask # base) DO INC(j, PS) END;	(* performance! *)
								IF j # k THEN res := 1508 END	(* cache type of region already set *)
							ELSE
								IF f = -1 THEN f := i END	(* first free entry *)
							END;
							INC(i)
						END;
						IF res = Ok THEN
							IF f # -1 THEN
								SetMTTRphysBase(f, {}, SYSTEM.VAL(SET, physAdr) * {12..31} + SYSTEM.VAL(SET, type) * {0..7});
								SetMTTRphysMask(f, {0..3}, (-SYSTEM.VAL(SET, size-1)) * {12..31} + {11})
							ELSE
								res := 1506	(* out of cache control entries *)
							END
						ELSE
							(* skip *)
						END
					ELSE
						res := 1511	(* region type not supported *)
					END
				ELSE
					res := 1505	(* MTTRs not supported *)
				END;
				Machine.Release(Machine.Memory)
			ELSE
				res := 1510	(* region base must be aligned on size *)
			END
		ELSE
			res := 1509	(* region size must be power of 2 *)
		END
	ELSE
		res := 1507	(* implementation restriction - fixed entries not supported *)
	END
END LocalSetCacheProperties;

PROCEDURE HandleSetCacheProperties(id: LONGINT; CONST state: Machine.State; msg: Machine.Message);
BEGIN
	WITH msg: SetCacheMessage DO
		(* to do: page 11-25 *)
		LocalSetCacheProperties(msg.physAdr, msg.size, msg.type, msg.res[id])
	END
END HandleSetCacheProperties;

(** Broadcast a LocalSetCacheProperties operation to all processors. *)

PROCEDURE GlobalSetCacheProperties*(physAdr: SYSTEM.ADDRESS; size, type: LONGINT; VAR res: LONGINT);
VAR i: LONGINT; msg: SetCacheMessage;
BEGIN
	NEW(msg); msg.physAdr := physAdr; msg.size := size; msg.type := type;
	FOR i := 0 TO Machine.MaxCPU-1 DO msg.res[i] := 2304 END;	(* default result *)
	Machine.Broadcast(HandleSetCacheProperties, msg, {Machine.Self, Machine.FrontBarrier, Machine.BackBarrier});
	res := 0;
	FOR i := 0 TO Machine.MaxCPU-1 DO
		IF (res = 0) & (msg.res[i] # 0) THEN res := msg.res[i] END	(* return first non-ok result found *)
	END
END GlobalSetCacheProperties;

(** Disable all caching on the current processor. *)

PROCEDURE LocalDisableCaching*;
CODE {SYSTEM.Pentium, SYSTEM.Privileged}
	PUSHFD
	CLI

	MOV EAX, CR0
	OR EAX, 40000000H
	AND EAX, 0DFFFFFFFH
	MOV CR0, EAX

	WBINVD

	MOV EAX, CR4
	AND EAX, 0FFFFFF7FH
	MOV CR4, EAX

	MOV EAX, CR3
	MOV CR3, EAX

	MOV ECX, 2FFH	; MTTRdefType
	MOV EAX, 0
	MOV EDX, 0
	WRMSR

	WBINVD

	MOV EAX, CR3
	MOV CR3, EAX

	MOV EAX, CR0
	OR EAX, 60000000H
	MOV CR0, EAX

	POPFD
END LocalDisableCaching;

PROCEDURE HandleDisableCaching(id: LONGINT; CONST state: Machine.State; msg: Machine.Message);
BEGIN
	LocalDisableCaching
END HandleDisableCaching;

(** Broadcast a LocalDisableCaching operation to all processors. *)

PROCEDURE GlobalDisableCaching*;
BEGIN
	Machine.Broadcast(HandleDisableCaching, NIL, {Machine.Self, Machine.FrontBarrier, Machine.BackBarrier})
END GlobalDisableCaching;

END MemCache.

(*
to do:
o change error codes
*)