From 9c47834d6f9479bfbc59cca28af8e6cf7e27aa68 Mon Sep 17 00:00:00 2001 From: Pablo Parra Date: Wed, 21 Feb 2018 13:59:46 +0100 Subject: [PATCH] Added Memory Protection Unit Now the system includes a memory protection unit (initially inactive) that is able to restrict the writing access into the memory map. --- src/app/app.component.ts | 44 +-- src/app/cpu.service.ts | 32 +- src/app/memory-view/memory-view.component.ts | 153 +++++++++- src/app/memory.service.ts | 274 +++++++++++++++--- .../textual-display.component.ts | 3 +- .../visual-display.component.ts | 5 +- 6 files changed, 430 insertions(+), 81 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index aa0cf85..fe9e0ce 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -136,28 +136,30 @@ export class AppComponent implements AfterViewInit { 'the value on the textual display.\n\n\tJMP start\n\tJMP isr\t\t; Interrupt ' + 'vector\n\tJMP svc\t\t; System call vector\n\nkeypressed:\t\t; 1 = key ' + 'pressed\n\tDB 0\t\t; 0 = No key pressed\n\nvalue:\t\t\t; The number of ' + - 'the\n\tDB 0\t\t; key pressed in ASCII\n\nstart:\n\tMOV SP, 0x7F\t; Set ' + + 'the\n\tDB 0\t\t; key pressed in ASCII\n\nstart:\n\tMOV SP, 0xFF\t; Set ' + 'Supervisor SP\n\tMOV A, 1\t\t; Set bit 0 of IRQMASK\n\tOUT 0\t\t\t; Unmask ' + - 'keypad IRQ\n\tPUSH 0x0010\t\t; User Task SR: IRQMASK = 1\n\tPUSH 0x17F\t\t; ' + - 'User Task SP = 0x17F\n\tPUSH task\t\t; User Task IP = task\n\tSRET\t\t\t; Jump ' + - 'to user mode\n\tHLT\t\t\t\t; Parachute\n\nisr:\t\t\t\n\tPUSH A\t\t; Read the ' + - 'key pressed\n\tIN 6\t\t; and store the ASCII\n\tADDB AL, 0x30\n\tMOVB [value], ' + - 'AL\n\tMOVB AL, 1\n\tMOVB [keypressed], AL\n\tMOV A, 1\n\tOUT 2\t\t; Write to ' + - 'signal IRQEOI\n\tPOP A\n\tIRET\n\nsvc:\t\t\t\t; Supervisor call\n\tCMP A, ' + - '0\t\t; A = syscall number\n\tJNZ .not0\t\t; 0 -> readchar\n\tCLI\n\tMOV A, ' + - '[keypressed]\t; Write vars\n\tPUSH B\t\t\t\t; with IRQs\n\tMOV B, 0\t\t\t; ' + - 'disabled\n\tMOV [keypressed], B\n\tPOP B\n\tSTI\n\tJMP .return\n.not0:\n\tCMP ' + - 'A, 1\t\t; 1 -> putchar\n\tJNZ .return\n\tMOVB [0x2F0], ' + - 'BL\n.return:\n\tSRET\t\t\t; Return to user space\n\n\tORG 0x100\t; Following ' + - 'instructions\n\t\t\t\t; will be assembled at 0x100\n\ntask:\t\t\t; The user ' + - 'task\n\tMOV A, 0\n\tMOV B, 0\nloop:\n\tCALL readchar\t; Polls the ' + - 'keypad\n\tCMPB AH, 1\t\t; using readchar\n\tJNZ loop\n\tMOVB BL, AL\t\t; If key ' + - 'was pressed use\n\tCALL putchar\t; putchar to print it\n\tJMP loop ' + - '\n\nreadchar:\t\t; User space wrapper\n\tMOV A, 0\t; for readchar ' + - 'syscall\n\tSVC\t\t\t; Syscall #0\n\tRET\t\t\t; A -> syscall ' + - 'number\n\nputchar:\t\t; User space wrapper\n\tPUSH A\t\t; for putchar ' + - 'syscall\n\tMOV A, 1\t; Syscall #1\n\tSVC\t\t\t; A -> syscall number\n\tPOP ' + - 'A\t\t; BL -> char to print\n\tRET'; + 'keypad IRQ\n\tMOV A, 0x02EF\t; Set the end of the\n\tOUT 8\t\t\t; protection ' + + 'to 0x02EF\n\tMOV A, 0x0109\t; Protection in seg. mode\n\tOUT 7\t\t\t; from ' + + '0x0100, S=1, U=0\n\tPUSH 0x0010\t\t; User Task SR: IRQMASK = 1\n\tPUSH ' + + '0x1FF\t\t; User Task SP = 0x1FF\n\tPUSH task\t\t; User Task IP = ' + + 'task\n\tSRET\t\t\t; Jump to user mode\n\tHLT\t\t\t\t; ' + + 'Parachute\n\nisr:\t\t\t\n\tPUSH A\t\t; Read the key pressed\n\tIN 6\t\t; and ' + + 'store the ASCII\n\tADDB AL, 0x30\n\tMOVB [value], AL\n\tMOVB AL, 1\n\tMOVB ' + + '[keypressed], AL\n\tMOV A, 1\n\tOUT 2\t\t; Write to signal IRQEOI\n\tPOP ' + + 'A\n\tIRET\n\nsvc:\t\t\t\t; Supervisor call\n\tCMP A, 0\t\t; A = syscall ' + + 'number\n\tJNZ .not0\t\t; 0 -> readchar\n\tCLI\n\tMOV A, [keypressed]\t; Write ' + + 'vars\n\tPUSH B\t\t\t\t; with IRQs\n\tMOV B, 0\t\t\t; disabled\n\tMOV ' + + '[keypressed], B\n\tPOP B\n\tSTI\n\tJMP .return\n.not0:\n\tCMP A, 1\t\t; 1 -> ' + + 'putchar\n\tJNZ .return\n\tMOVB [0x2F0], BL\n.return:\n\tSRET\t\t\t; Return to ' + + 'user space\n\n\tORG 0x100\t; Following instructions\n\t\t\t\t; will be ' + + 'assembled at 0x100\n\ntask:\t\t\t; The user task\n\tMOV A, 0\n\tMOV B, ' + + '0\nloop:\n\tCALL readchar\t; Polls the keypad\n\tCMPB AH, 1\t\t; using ' + + 'readchar\n\tJNZ loop\n\tMOVB BL, AL\t\t; If key was pressed use\n\tCALL ' + + 'putchar\t; putchar to print it\n\tJMP loop \n\nreadchar:\t\t; User space ' + + 'wrapper\n\tMOV A, 0\t; for readchar syscall\n\tSVC\t\t\t; Syscall ' + + '#0\n\tRET\t\t\t; A -> syscall number\n\nputchar:\t\t; User space ' + + 'wrapper\n\tPUSH A\t\t; for putchar syscall\n\tMOV A, 1\t; Syscall ' + + '#1\n\tSVC\t\t\t; A -> syscall number\n\tPOP A\t\t; BL -> char to print\n\tRET'; public codeText = ''; private instance: any; diff --git a/src/app/cpu.service.ts b/src/app/cpu.service.ts index 8d4fdad..b201cf3 100644 --- a/src/app/cpu.service.ts +++ b/src/app/cpu.service.ts @@ -5,7 +5,7 @@ import { Observable } from 'rxjs/Observable'; import { Subscription } from 'rxjs/Subscription'; import { OpCode, OperandType, Instruction, instructionSet, InstructionSpec } from './instrset'; -import { MemoryService } from './memory.service'; +import { MemoryService, MemoryAccessActor } from './memory.service'; import { IORegMapService } from './ioregmap.service'; import { ClockService} from './clock.service'; import { Exception, ExceptionType } from './exceptions'; @@ -439,7 +439,8 @@ export class CPUService { protected pushByte(value: number) { const currentSP = this.SP.value; - this.memoryService.storeByte(currentSP, value); + this.memoryService.storeByte(currentSP, value, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); this.SP.pushByte(); } @@ -447,7 +448,8 @@ export class CPUService { protected pushWord(value: number) { const currentSP = this.SP.value; - this.memoryService.storeWord(currentSP - 1, value); + this.memoryService.storeWord(currentSP - 1, value, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); this.SP.pushWord(); } @@ -925,7 +927,8 @@ export class CPUService { } try { - this.memoryService.storeWord(toAddress, this.registersBank.get(fromRegister).value); + this.memoryService.storeWord(toAddress, this.registersBank.get(fromRegister).value, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -946,7 +949,8 @@ export class CPUService { } try { - this.memoryService.storeWord(toAddress, this.registersBank.get(fromRegister).value); + this.memoryService.storeWord(toAddress, this.registersBank.get(fromRegister).value, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -974,7 +978,8 @@ export class CPUService { private instrMOV_WORD_TO_ADDRESS(toAddress: number, word: number): boolean { try { - this.memoryService.storeWord(toAddress, word); + this.memoryService.storeWord(toAddress, word, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -988,7 +993,8 @@ export class CPUService { private instrMOV_WORD_TO_REGADDRESS(toAddress: number, word: number): boolean { try { - this.memoryService.storeWord(toAddress, word); + this.memoryService.storeWord(toAddress, word, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -1084,7 +1090,8 @@ export class CPUService { const byteFromRegister = CPUService.getByteFrom8bitsGPR(fromRegister); try { - this.memoryService.storeByte(toAddress, this.registersBank.get(fromRegister)[byteFromRegister]); + this.memoryService.storeByte(toAddress, this.registersBank.get(fromRegister)[byteFromRegister], + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -1107,7 +1114,8 @@ export class CPUService { const byteFromRegister = CPUService.getByteFrom8bitsGPR(fromRegister); try { - this.memoryService.storeByte(toAddress, this.registersBank.get(fromRegister)[byteFromRegister]); + this.memoryService.storeByte(toAddress, this.registersBank.get(fromRegister)[byteFromRegister], + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -1138,7 +1146,8 @@ export class CPUService { private instrMOVB_BYTE_TO_ADDRESS(toAddress: number, byte: number): boolean { try { - this.memoryService.storeByte(toAddress, byte); + this.memoryService.storeByte(toAddress, byte, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); @@ -1152,7 +1161,8 @@ export class CPUService { private instrMOVB_BYTE_TO_REGADDRESS(toAddress: number, byte: number): boolean { try { - this.memoryService.storeByte(toAddress, byte); + this.memoryService.storeByte(toAddress, byte, + (this.SR.supervisor === 1 ? MemoryAccessActor.CPU_SUPERVISOR : MemoryAccessActor.CPU_USER)); } catch (e) { throw new Exception(ExceptionType.MEMORY_ACCESS_ERROR, e.message, this.IP.value, this.SP.value, this.SR.value, toAddress); diff --git a/src/app/memory-view/memory-view.component.ts b/src/app/memory-view/memory-view.component.ts index 04e1e03..317c04f 100644 --- a/src/app/memory-view/memory-view.component.ts +++ b/src/app/memory-view/memory-view.component.ts @@ -7,7 +7,8 @@ import { MemoryOperation, MemoryService, MemoryOperationType, MemoryOperationParamsLoadStore, MemoryOperationParamsStoreBytes, - MemoryOperationParamsAddRegion + MemoryOperationParamsAddRegion, + MemoryAccessActor, MemoryOperationParamsChangeProtectionUnit } from '../memory.service'; import { Subscription } from 'rxjs/Subscription'; @@ -21,6 +22,62 @@ import { } from '../cpuregs'; +class MemoryProtectionUnitView { + + public isActive: boolean; + public startAddress: number; + public endAddress: number; + public blockProtect: boolean; + public userMode: boolean; + public supervisorMode: boolean; + + constructor (isActive: boolean = false, startAddress: number = 0, endAddress: number = 0xFFFF, + blockProtect: boolean = true, supervisorMode: boolean = true, userMode: boolean = true) { + + this.isActive = isActive; + this.startAddress = startAddress; + this.endAddress = endAddress; + this.blockProtect = blockProtect; + this.supervisorMode = supervisorMode; + this.userMode = userMode; + + } + + public checkMemoryCell(address: number, isSupervisorMode: boolean): boolean { + + /* If the unit is inactive, then all accesses are allowed. + * Also, if the user is a device, then the memory protection does not apply */ + if (this.isActive === false) { + return true; + } + + /* If blockProtect is set, then the protected memory is WITHIN the limits */ + if (this.blockProtect === true && address >= this.startAddress && address <= this.endAddress) { + + /* We must check the access */ + if (isSupervisorMode && this.supervisorMode === false) { + return false; + } else { + + } return !(isSupervisorMode === false && this.userMode === false); + } else if (this.blockProtect === false && (address < this.startAddress || address > this.endAddress)) { + + /* We must check the access */ + if (isSupervisorMode === true && this.supervisorMode === false) { + return false; + } else { + return !(isSupervisorMode === false && this.userMode === false); + } + + } else { + return true; + } + + } + +} + + class MemoryCellView { private _value: number; @@ -31,14 +88,20 @@ class MemoryCellView { public memoryRegionStyle: string; public address: number; public isInstruction: boolean; + public supervisorEnabled: boolean; + public userEnabled: boolean; - constructor(address: number, initialValue: number = 0, initialStyle?: string, isInstruction: boolean = false) { + constructor(address: number, initialValue: number = 0, supervisorEnabled: boolean = true, + userEnabled: boolean = true, + initialStyle?: string, isInstruction: boolean = false) { this.style = initialStyle; this._value = initialValue; this._strValue = Utils.pad(initialValue, 16, 2); this.address = address; this.isInstruction = isInstruction; + this.supervisorEnabled = supervisorEnabled; + this.userEnabled = userEnabled; } @@ -118,6 +181,8 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { private registerSR: number; + private protectionUnit: MemoryProtectionUnitView; + constructor(private memoryService: MemoryService, private cpuService: CPUService, private errorBarService: ErrorBarService) { @@ -171,6 +236,11 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { this.updateCellStyle(registerSSPPointer.value); this.updateCellStyle(registerUSPPointer.value); + const protUnit = this.memoryService.protectionUnit; + + this.protectionUnit = new MemoryProtectionUnitView(protUnit.isActive, protUnit.startAddress, + protUnit.endAddress, protUnit.blockProtect, protUnit.supervisorMode, protUnit.userMode); + this.memoryOperationSubscription = this.memoryService.memoryOperation$.subscribe( (memoryOperation) => this.processMemoryOperation(memoryOperation) ); @@ -250,10 +320,16 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { for (let i = 0; i < this.size; i++) { + this.memoryCellViews[i].supervisorEnabled = true; + this.memoryCellViews[i].userEnabled = true; + if (this.memoryCellViews[i].isMemoryRegion === false) { this.memoryCellViews[i].value = 0; + this.updateCellStyle(i); } + + } // And we have to flush the stack @@ -330,9 +406,19 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { let display; if (index === CPURegisterIndex.SR) { + + const previousMode = this.isSupervisorMode(); + this.registerSR = value; - this.updateCellStyle(this.registerPointers.get(CPURegisterIndex.SSP).value); - this.updateCellStyle(this.registerPointers.get(CPURegisterIndex.USP).value); + + if (previousMode !== this.isSupervisorMode()) { + + for (let i = 0; i < this.size; i++) { + this.updateCellStyle(i); + } + + } + return; } @@ -400,15 +486,50 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { if (index === CPURegisterIndex.SR) { + const previousMode = this.isSupervisorMode(); + if (value === 0) { this.registerSR &= ~(1 << bitNumber); } else { this.registerSR |= (1 << bitNumber); } - this.updateCellStyle(this.registerPointers.get(CPURegisterIndex.SSP).value); - this.updateCellStyle(this.registerPointers.get(CPURegisterIndex.USP).value); + if (previousMode !== this.isSupervisorMode()) { + for (let i = 0; i < this.size; i++) { + this.updateCellStyle(i); + } + + } + + } + + } + + private operationChangeMemoryProtection(isActive: boolean, startAddress: number, endAddress: number, + blockProtect: boolean, supervisorMode: boolean, userMode: boolean) { + + const previouslyActive = this.protectionUnit.isActive; + + this.protectionUnit.isActive = isActive; + this.protectionUnit.startAddress = startAddress; + this.protectionUnit.endAddress = endAddress; + this.protectionUnit.blockProtect = blockProtect; + this.protectionUnit.supervisorMode = supervisorMode; + this.protectionUnit.userMode = userMode; + + if (isActive === false && previouslyActive === true) { + for (let i = 0; i < this.size; i++) { + this.memoryCellViews[i].supervisorEnabled = true; + this.memoryCellViews[i].userEnabled = true; + this.updateCellStyle(i); + } + } else if (isActive === true) { + for (let i = 0; i < this.size; i++) { + this.memoryCellViews[i].supervisorEnabled = this.protectionUnit.checkMemoryCell(i, true); + this.memoryCellViews[i].userEnabled = this.protectionUnit.checkMemoryCell(i, false); + this.updateCellStyle(i); + } } } @@ -456,6 +577,15 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { (memoryOperation.data).endAddress, (memoryOperation.data).initialValues); break; + case MemoryOperationType.CHANGE_MEMPROT: + this.operationChangeMemoryProtection( + (memoryOperation.data).isActive, + (memoryOperation.data).startAddress, + (memoryOperation.data).endAddress, + (memoryOperation.data).blockProtect, + (memoryOperation.data).supervisorMode, + (memoryOperation.data).userMode); + break; case MemoryOperationType.STORE_BYTE: this.operationWriteByte( (memoryOperation.data).address, @@ -485,7 +615,7 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { try { - this.memoryService.storeByte(address, parseInt(this.newCellValue, 16), false); + this.memoryService.storeByte(address, parseInt(this.newCellValue, 16), MemoryAccessActor.DEVICE); if (this.memoryCellViews[address].isInstruction === true) { this.memoryCellViews[address].isInstruction = false; @@ -518,13 +648,14 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { * - stack * - mapped instruction > * - region + * - access */ if (address < 0 || address >= this.size) { return; } - this.memoryCellViews[address].style = undefined; + this.memoryCellViews[address].style = ''; if (this.memoryCellViews[address].memoryRegionStyle !== undefined) { this.memoryCellViews[address].style = this.memoryCellViews[address].memoryRegionStyle; @@ -575,6 +706,12 @@ export class MemoryViewComponent implements OnInit, OnDestroy, OnChanges { this.memoryCellViews[address].style = 'marker marker-ip'; } + if (this.isSupervisorMode() === true) { + this.memoryCellViews[address].style += (this.memoryCellViews[address].supervisorEnabled === false) ? ' muted' : ''; + } else { + this.memoryCellViews[address].style += (this.memoryCellViews[address].userEnabled === false) ? ' muted' : ''; + } + } ngOnChanges(changes: SimpleChanges) { diff --git a/src/app/memory.service.ts b/src/app/memory.service.ts index 763e0dd..fec4846 100644 --- a/src/app/memory.service.ts +++ b/src/app/memory.service.ts @@ -3,13 +3,128 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; +import { IORegisterOperation, IORegMapService, + IORegisterType, IORegisterOperationType, + IORegisterOperationParamsReadWrite } from './ioregmap.service'; + import { Utils } from './utils'; import { EventsLogService, SystemEvent } from './events-log.service'; -export enum MemoryCellAccessPermission { +const MEMPTSTART_REGISTER_ADDRESS = 7; +const MEMPTEND_REGISTER_ADDRESS = 8; + +export enum MemoryAccessActor { + + DEVICE = 0, + CPU_SUPERVISOR = 1, + CPU_USER = 2 + +} + +class MemoryProtectionUnit { + + public isActive: boolean; + public startAddress: number; + public endAddress: number; + public blockProtect: boolean; + public userMode: boolean; + public supervisorMode: boolean; + + protected _startRegister: number; + protected _endRegister: number; + + + constructor (isActive: boolean = false, startAddress: number = 0, endAddress: number = 0xFFFF, + blockProtect: boolean = true, supervisorMode: boolean = true, userMode: boolean = true) { + + this.isActive = isActive; + this.startAddress = startAddress; + this.endAddress = endAddress; + this.blockProtect = blockProtect; + this.supervisorMode = supervisorMode; + this.userMode = userMode; + + this._startRegister = this.startAddress & 0xFFF0; + if (this.isActive === true) { + this._startRegister |= 0x0001; + } + if (this.blockProtect === true) { + this._startRegister |= 0x0002; + } + if (this.userMode === true) { + this._startRegister |= 0x0004; + } + if (this.supervisorMode === true) { + this._startRegister |= 0x0008; + } + + this._endRegister = this.endAddress; + + } + + get startRegister(): number { + + return this._startRegister; + + } + + set startRegister(newValue: number) { + + this._startRegister = newValue; + + this.startAddress = (newValue & 0xFFF0); + this.isActive = ((newValue & 0x0001) !== 0); + this.blockProtect = ((newValue & 0x0002) !== 0); + this.userMode = ((newValue & 0x0004) !== 0); + this.supervisorMode = ((newValue & 0x0008) !== 0); + + } + + get endRegister(): number { + + return this._endRegister; + + } + + set endRegister(newValue: number) { + + this._endRegister = newValue; + this.endAddress = newValue; + + } + + public checkMemoryAccess(address: number, actor: MemoryAccessActor): boolean { + + /* If the unit is inactive, then all accesses are allowed. + * Also, if the user is a device, then the memory protection does not apply */ + if (this.isActive === false || actor === MemoryAccessActor.DEVICE) { + return true; + } + + /* If blockProtect is set, then the protected memory is WITHIN the limits */ + if (this.blockProtect === true && address >= this.startAddress && address <= this.endAddress) { + + /* We must check the access */ + if (actor === MemoryAccessActor.CPU_SUPERVISOR && this.supervisorMode === false) { + return false; + } else { + + } return !(actor === MemoryAccessActor.CPU_USER && this.userMode === false); + } else if (this.blockProtect === false && (address < this.startAddress || address > this.endAddress)) { + + /* We must check the access */ + if (actor === MemoryAccessActor.CPU_SUPERVISOR && this.supervisorMode === false) { + return false; + } else { + return !(actor === MemoryAccessActor.CPU_USER && this.userMode === false); + } + + } else { + return true; + } + + } - READ_WRITE = 0, - READ_ONLY = 1 } @@ -21,7 +136,8 @@ export enum MemoryOperationType { STORE_BYTES = 3, LOAD_WORD = 4, STORE_WORD = 5, - ADD_REGION = 6 + ADD_REGION = 6, + CHANGE_MEMPROT = 7 } @@ -46,18 +162,36 @@ export interface MemoryOperationParamsAddRegion { name: string; startAddress: number; endAddress: number; - accessPermissions: MemoryCellAccessPermission; initialValues: Array; } +export interface MemoryOperationParamsChangeProtectionUnit { + + isActive: boolean; + startAddress: number; + endAddress: number; + blockProtect: boolean; + userMode: boolean; + supervisorMode: boolean; + +} + type MemoryOperationParams = MemoryOperationParamsLoadStore | MemoryOperationParamsStoreBytes | - MemoryOperationParamsAddRegion; + MemoryOperationParamsAddRegion | MemoryOperationParamsChangeProtectionUnit; + +enum MemoryOperationState { + + IN_PROGRESS = 0, + FINISHED = 1 + +} export class MemoryOperation implements SystemEvent { public operationType: MemoryOperationType; public data: MemoryOperationParams; + public state: MemoryOperationState; constructor(operationType: MemoryOperationType, data?: MemoryOperationParams) { @@ -104,6 +238,18 @@ export class MemoryOperation implements SystemEvent { ret = `MEM: Add region ${params.name} at addresses ` + `[0x${Utils.pad(params.startAddress, 16, 4)}, 0x${Utils.pad(params.endAddress, 16, 4)}]`; break; + case MemoryOperationType.CHANGE_MEMPROT: + params = this.data; + if (params.isActive === true) { + ret = `MEM: Enabled protection unit with mode ` + (params.blockProtect === true ? `block ` : `segment `) + + `with permissions ` + + (params.supervisorMode === true ? `S` : `-`) + + (params.supervisorMode === true ? `U` : `-`) + ` at addresses ` + + `[0x${Utils.pad(params.startAddress, 16, 4)}, 0x${Utils.pad(params.endAddress, 16, 4)}]`; + } else { + ret = `MEM: Disabled protection unit`; + } + break; default: break; } @@ -117,16 +263,13 @@ export class MemoryOperation implements SystemEvent { class MemoryCell { public address: number; - public accessPermissions: MemoryCellAccessPermission; public dataValue: number; public memoryRegion: MemoryRegion; constructor(address: number, - accessPermissions: MemoryCellAccessPermission = MemoryCellAccessPermission.READ_WRITE, initialValue: number = 0, memoryRegion?: MemoryRegion) { this.address = address; - this.accessPermissions = accessPermissions; this.dataValue = initialValue; this.memoryRegion = memoryRegion; @@ -153,11 +296,6 @@ export class MemoryRegion { */ public endAddress: number; - /** - * Access permissions (Read/write or Read-only). - */ - public accessPermissions: MemoryCellAccessPermission; - /** * Size in bytes of the memory region. */ @@ -176,14 +314,12 @@ export class MemoryRegion { public lastAccess = -1; constructor(regionID: string, name: string, startAddress: number, endAddress: number, - accessPermissions: MemoryCellAccessPermission = MemoryCellAccessPermission.READ_WRITE, publishMemoryOperation?: PublishMemoryOperation) { this.regionID = regionID; this.name = name; this.startAddress = startAddress; this.endAddress = endAddress; - this.accessPermissions = accessPermissions; this.publishMemoryOperation = publishMemoryOperation; this.size = endAddress - startAddress + 1; @@ -206,13 +342,24 @@ export class MemoryService { public memoryOperation$: Observable; - constructor(private eventsLogService: EventsLogService) { + public protectionUnit: MemoryProtectionUnit = new MemoryProtectionUnit(); + + constructor(private ioRegMapService: IORegMapService, + private eventsLogService: EventsLogService) { this.memoryCells = Array(this.size); for (let i = 0; i < this.size; i++) { this.memoryCells[i] = new MemoryCell(i); } + ioRegMapService.addRegister('MEMPTSTART', MEMPTSTART_REGISTER_ADDRESS, this.protectionUnit.startRegister, + IORegisterType.READ_WRITE, (op) => this.processRegisterOperation(op), + 'Memory Protection Unit Start Register'); + + ioRegMapService.addRegister('MEMPTEND', MEMPTEND_REGISTER_ADDRESS, this.protectionUnit.endRegister, + IORegisterType.READ_WRITE, (op) => this.processRegisterOperation(op), + 'Memory Protection Unit End Register'); + this.memoryOperation$ = this.memoryOperationSource.asObservable(); } @@ -230,8 +377,23 @@ export class MemoryService { } + protected publishMemoryOperationStart(operation: MemoryOperation) { + + operation.state = MemoryOperationState.IN_PROGRESS; + this.eventsLogService.startEventGroup(operation); + this.memoryOperationSource.next(operation); + + } + + protected publishMemoryOperationEnd(operation: MemoryOperation) { + + operation.state = MemoryOperationState.FINISHED; + this.eventsLogService.endEventGroup(operation); + this.memoryOperationSource.next(operation); + + } + public addMemoryRegion(name: string, startAddress: number, endAddress: number, - accessPermissions: MemoryCellAccessPermission = MemoryCellAccessPermission.READ_WRITE, initialValues?: Array, publishMemoryOperation?: PublishMemoryOperation): string { /* We need to first check that startAddress and endAddress are valid, i.e.: @@ -295,12 +457,10 @@ export class MemoryService { } /* Now we can insert the new memory region */ - const newMemoryRegion = new MemoryRegion(newID, name, startAddress, endAddress, - accessPermissions, publishMemoryOperation); + const newMemoryRegion = new MemoryRegion(newID, name, startAddress, endAddress, publishMemoryOperation); this.memoryRegions.set(newID, newMemoryRegion); for (let i = startAddress; i <= endAddress; i++) { - this.memoryCells[i].accessPermissions = accessPermissions; this.memoryCells[i].dataValue = initialValues ? initialValues[i] : 0; this.memoryCells[i].memoryRegion = newMemoryRegion; } @@ -310,7 +470,6 @@ export class MemoryService { name: name, startAddress: startAddress, endAddress: endAddress, - accessPermissions: accessPermissions, initialValues: initialValues }; @@ -353,7 +512,7 @@ export class MemoryService { } - public storeByte(address: number, value: number, isInstruction: boolean = true, + public storeByte(address: number, value: number, actor: MemoryAccessActor, publish: boolean = true) { if (address < 0 || address > this.size) { @@ -368,11 +527,8 @@ export class MemoryService { throw Error(`Invalid data value ${value}`); } - if (isInstruction === true && - (this.memoryCells[address].accessPermissions === MemoryCellAccessPermission.READ_ONLY)) { - - throw Error(`Invalid storage into read-only cell ${address} in supervisor mode`); - + if (this.protectionUnit.checkMemoryAccess(address, actor) === false) { + throw Error(`Invalid storage into protected cell ${address}`); } this.lastAccess = address; @@ -473,7 +629,7 @@ export class MemoryService { } - public storeWord(address: number, value: number, isInstruction: boolean = true, + public storeWord(address: number, value: number, actor: MemoryAccessActor, publish: boolean = true) { if (address < 0 || address >= this.size) { @@ -488,12 +644,9 @@ export class MemoryService { throw Error(`Invalid data value ${value}`); } - if (isInstruction === true && - (this.memoryCells[address].accessPermissions === MemoryCellAccessPermission.READ_ONLY || - this.memoryCells[address + 1].accessPermissions === MemoryCellAccessPermission.READ_ONLY)) { - - throw Error(`Invalid storage into read-only cell ${address}`); - + if (this.protectionUnit.checkMemoryAccess(address, actor) === false || + this.protectionUnit.checkMemoryAccess(address + 1, actor) === false) { + throw Error(`Invalid storage into protected cell ${address}`); } this.lastAccess = address; @@ -526,8 +679,51 @@ export class MemoryService { } + private processWriteOperation(address: number, value: number) { + + switch (address) { + case MEMPTSTART_REGISTER_ADDRESS: + this.protectionUnit.startRegister = value; + break; + case MEMPTEND_REGISTER_ADDRESS: + this.protectionUnit.endRegister = value; + break; + } + + const parameters: MemoryOperationParamsChangeProtectionUnit = { + isActive: this.protectionUnit.isActive, + blockProtect: this.protectionUnit.blockProtect, + startAddress: this.protectionUnit.startAddress, + endAddress: this.protectionUnit.endAddress, + supervisorMode: this.protectionUnit.supervisorMode, + userMode: this.protectionUnit.userMode + }; + + this.publishMemoryOperation(new MemoryOperation(MemoryOperationType.CHANGE_MEMPROT, parameters)); + + } + + + private processRegisterOperation(ioRegisterOperation: IORegisterOperation) { + + switch (ioRegisterOperation.operationType) { + case IORegisterOperationType.READ: + break; + case IORegisterOperationType.WRITE: + this.processWriteOperation( + (ioRegisterOperation.data).address, + (ioRegisterOperation.data).value); + break; + } + + } + public reset() { + const operation = new MemoryOperation(MemoryOperationType.RESET); + + this.publishMemoryOperationStart(operation); + this.lastAccess = -1; for (let i = 0; i < this.memoryCells.length; i++) { @@ -538,7 +734,13 @@ export class MemoryService { } - this.publishMemoryOperation(new MemoryOperation(MemoryOperationType.RESET)); + this.protectionUnit.startRegister = 0x000E; + this.protectionUnit.endRegister = 0xFFFF; + + this.ioRegMapService.store(MEMPTSTART_REGISTER_ADDRESS, 0x000E, false, false); + this.ioRegMapService.store(MEMPTEND_REGISTER_ADDRESS, 0xFFFF, false, false); + + this.publishMemoryOperationEnd(operation); } diff --git a/src/app/textual-display/textual-display.component.ts b/src/app/textual-display/textual-display.component.ts index 6730892..2807ded 100644 --- a/src/app/textual-display/textual-display.component.ts +++ b/src/app/textual-display/textual-display.component.ts @@ -5,7 +5,7 @@ import { Observable } from 'rxjs/Observable'; import { MemoryOperation, MemoryService, MemoryOperationParamsLoadStore, - MemoryCellAccessPermission, MemoryOperationType + MemoryOperationType } from '../memory.service'; import { Utils } from '../utils'; @@ -156,7 +156,6 @@ export class TextualDisplayComponent implements OnInit { ngOnInit() { this.memoryService.addMemoryRegion('TextualDisplayRegion', 0x2F0, 0x2FF, - MemoryCellAccessPermission.READ_WRITE, undefined, (op) => this.processMemoryOperation(op)); } diff --git a/src/app/visual-display/visual-display.component.ts b/src/app/visual-display/visual-display.component.ts index 900c422..436180a 100644 --- a/src/app/visual-display/visual-display.component.ts +++ b/src/app/visual-display/visual-display.component.ts @@ -5,7 +5,7 @@ import { Observable } from 'rxjs/Observable'; import { MemoryOperation, MemoryService, MemoryOperationParamsLoadStore, - MemoryCellAccessPermission, MemoryOperationType + MemoryOperationType } from '../memory.service'; import { Utils } from '../utils'; @@ -153,8 +153,7 @@ export class VisualDisplayComponent implements OnInit, AfterViewInit { ngOnInit() { this.memoryService.addMemoryRegion('VisualDisplayRegion', 0x300, 0x3FF, - MemoryCellAccessPermission.READ_WRITE, this.initialValues, - (op) => this.processMemoryOperation(op)); + this.initialValues, (op) => this.processMemoryOperation(op)); }