The security about the DBR is simply that it's effectively ignored and matches the PBR all the time, as enforced by the hardware, save for when running in the kernel space.
POC VERSION TWO
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC Version 2
whartung wrote:
I guess all I was thinking is that Bank $00 can be considered the privileged bank, and each time an opcode is fetched you capture the current bank address for that opcode. When that value is $00, you can set a latch or something.
Quote:
In the decode logic, when it detects a DATA access is taking place, if the latch is set, then the bank address for the data access can just go through from the DB/BA0-7 pins. If the latch is NOT set, then the data bank address is hidden, and the one that was captured during the opcode fetch is used instead (thus limiting non-privileged banks to their own 64K block).
The security about the DBR is simply that it's effectively ignored and matches the PBR all the time, as enforced by the hardware, save for when running in the kernel space.
The security about the DBR is simply that it's effectively ignored and matches the PBR all the time, as enforced by the hardware, save for when running in the kernel space.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC Version 2
I guess I'm not following along. I thought you were looking for a way to secure the individual banks, and have a kernel in a dedicated bank, let the kernel access all banks while the "user" banks could only address their own. Each process would be pinned to their own bank. During an software interrupt, the kernel will "know" what the current SP is, which would naturally be pointing in to the source process (thus it could be used as a natural indicator of the calling process by looking at the bank byte of the information on the stack).
I don't know how the "protected" decoder logic would work during a software interrupt when the CPU tries to send control to the interrupt vector, since the PC at the time of the INT would not be from within the protected kernel space. That might muck up the works right there unless the interrupt can be detected somehow.
I don't know how the "protected" decoder logic would work during a software interrupt when the CPU tries to send control to the interrupt vector, since the PC at the time of the INT would not be from within the protected kernel space. That might muck up the works right there unless the interrupt can be detected somehow.
-
teamtempest
- Posts: 443
- Joined: 08 Nov 2009
- Location: Minnesota
- Contact:
Re: POC Version 2
Quote:
teamtempest wrote:
Quote:
Segmentation is not without its drawbacks. Since any process is not allowed to access another process's segment, the time-honored method of accessing a kernel API as a subroutine is out—the JSR would cause a hardware exception by virtue of accessing "privileged" RAM (the kernel's space, in this case). Allowing such access would mean a special case condition would have to exist in the glue logic to allow a JSR to a kernel API address, but nowhere else outside of user process space. A lot of CPLD capability could be eaten up trying to enforce this very narrow access rule, as the data bus would have to be monitored for one unique instruction.
What if the hardware "mirrored" a bit of the kernel in every 64K block? 1K or 512 bytes or something like that at the top of every 64K block? Perhaps just enough code space to save enough context to "know" which process is calling and then switch to the "real" kernel?
I'm reminded of the C128's memory management registers, which appeared as I/O at $FF00+ in all 256 possible configurations, even ones which in theory had no I/O registers at all (thus avoiding the problem of getting "stuck" in a configuration with no way out because of being unable to write to the memory management registers).
That could be done but it would then be necessary to somehow write-protect the mirrored area, since it would appear to be in part of the user process' space. If kernel APIs are called via software interrupts then mirroring isn't necessary, but then the problem of the kernel accessing the caller's stack gets involved. It's messy, either way.
Quote:
Segmentation is not without its drawbacks. Since any process is not allowed to access another process's segment, the time-honored method of accessing a kernel API as a subroutine is out—the JSR would cause a hardware exception by virtue of accessing "privileged" RAM (the kernel's space, in this case). Allowing such access would mean a special case condition would have to exist in the glue logic to allow a JSR to a kernel API address, but nowhere else outside of user process space. A lot of CPLD capability could be eaten up trying to enforce this very narrow access rule, as the data bus would have to be monitored for one unique instruction.
What if the hardware "mirrored" a bit of the kernel in every 64K block? 1K or 512 bytes or something like that at the top of every 64K block? Perhaps just enough code space to save enough context to "know" which process is calling and then switch to the "real" kernel?
I'm reminded of the C128's memory management registers, which appeared as I/O at $FF00+ in all 256 possible configurations, even ones which in theory had no I/O registers at all (thus avoiding the problem of getting "stuck" in a configuration with no way out because of being unable to write to the memory management registers).
That could be done but it would then be necessary to somehow write-protect the mirrored area, since it would appear to be in part of the user process' space. If kernel APIs are called via software interrupts then mirroring isn't necessary, but then the problem of the kernel accessing the caller's stack gets involved. It's messy, either way.
Regarding software interrupts - doesn't any multi-tasking kernel, pre-emptive or co-operative, have to have some knowledge of what the current running process is? Otherwise it wouldn't be possible to rationally switch to another process. And shouldn't only the current process be capable of triggering a software interrupt? I have a hard time imagining a stopped process doing so, anyway.
So...shouldn't the kernel be able to easily figure out which process issued a software interrupt? And therefore which context holds any parameters passed on that process' stack? Or isn't that the problem?
- GARTHWILSON
- Forum Moderator
- Posts: 8774
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: POC Version 2
teamtempest wrote:
Regarding software interrupts - doesn't any multi-tasking kernel, pre-emptive or co-operative, have to have some knowledge of what the current running process is? Otherwise it wouldn't be possible to rationally switch to another process.
[Edit, 5/15/14: I posted an article on simple methods of doing multitasking without a multitasking OS, at http://wilsonminesco.com/multitask/index.html.]
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC Version 2
whartung wrote:
I guess I'm not following along. I thought you were looking for a way to secure the individual banks, and have a kernel in a dedicated bank, let the kernel access all banks while the "user" banks could only address their own. Each process would be pinned to their own bank. During an software interrupt, the kernel will "know" what the current SP is, which would naturally be pointing in to the source process (thus it could be used as a natural indicator of the calling process by looking at the bank byte of the information on the stack).
I don't know how the "protected" decoder logic would work during a software interrupt when the CPU tries to send control to the interrupt vector, since the PC at the time of the INT would not be from within the protected kernel space. That might muck up the works right there unless the interrupt can be detected somehow.
I don't know how the "protected" decoder logic would work during a software interrupt when the CPU tries to send control to the interrupt vector, since the PC at the time of the INT would not be from within the protected kernel space. That might muck up the works right there unless the interrupt can be detected somehow.
I'm not sure you understand the nature of the problem. Sandboxing per se would not all that difficult to achieve, if it weren't for a design "feature" of the 65C816. First off, let's review how the '816 generates valid addresses and how that information would be used to set up memory protection.
A0-A15 are generated from the MPU program counter, the same as the 65(c)02, and A16-A23 are generated on D0-D7 when Ø2 is low and the address bus is declared to be valid, the latter being determined by the VDA and VPA signals. The origin of the A16-A23 component varies as an instruction is executed. The VDA/VPA and address generation relationships are as follows:
Code: Select all
A16-A23
Ref VDA VPA Address Bus Source MPU Activity
—————————————————————————————————————————————————————————————
1 0 0 Invalid -- Internal operation
2 0 1 Valid PB Operand fetch
3 1 0 Valid DB Data fetch or store
4 1 1 Valid PB Opcode fetch
—————————————————————————————————————————————————————————————In the above, "address bus" includes the A16-A23 component that is multiplexed on D0-D7 during valid memory cycles when Ø2 low. If a memory cycle is not valid the state of D0-D7 is undefined. The entries in the A16-A23 Source Column refer to the program bank (PB) and data bank (DB) registers. The conditions in reference #1 occur during the intermediate execution stages of some instructions, especially those that involve indexed addressing. Chip selects must never be valid during the condition defined in reference #1, as the effective address is undefined. Incidentally, the conditions in reference #4 correspond to when the 65C02 asserts the SYNC signal.
At any given time, the CPLD logic will "know" from watching VDA and VPA when the effective address is valid and, indirectly, what is on the data bus during any given clock cycle. Hence such information could be used to police memory accesses. However, this is a stateless situation—there is no way to know what transpired in the past, unless the CPLD institutes stateful inspection of each instruction cycle and records the condition of VDA, VPA, A0-A23 and D0-D7 for posterity. That's not practical, nor is it necessary in order to sandbox processes. All that has to be known is what is on D0-D7 when the expression !Ø2 & (VDA | VPA) is true, where & is logical AND, | is logical OR and ! is logical NOT. At that time, if the bit pattern on D0-D7 is not the same as the bank number of the currently executing process, and if the current process is not the kernel, a memory access violation has occurred. So the basic means of memory protection is implicitly defined in the CPLD logic.
The real, and so far intractable, problem is two-fold in nature and is due to a design choice made when the '816 was conceived:
- All zero page and stack accesses are forced to bank $00 without regard to the setting of DB, which makes that bank a "community" sandbox. Therefore, the bank comparison rule has to be ignored for ZP and stack activity if the system is to be able to function—it's kind of tough to do much without ZP and a stack.
- When any interrupt occurs the '816 automatically forces PB to $00 immediately after pushing the 24 bit program counter (PB followed by PC) and the status register (SR). Pushes occur to the currently-executing process' stack, since that is where SP will be pointing when the interrupt hits. Naturally, the relevant interrupt vector must be located in bank $00, since the MPU effectively does a JMP (<addr>) to get to the interrupt service routine (ISR), where <addr> is always $00xxxx. This characteristic implies that the ISR, or at least part of it, must be in bank $00 (it can, however, jump to another bank). Once again, the sandbox rule cannot be applied to bank $00 if the interrupt system is to be functional.
Given the requirement that bank $00 be accessible to all processes under all conditions, the approach to be taken at first blush would be to prohibit any user-space process from "manually" changing the direct page (DP) register and/or stack pointer (SP). Such a rule would prevent a user-space process from, say, making the I/O block its new stack (Ouch!) or selecting another process' stack as its new ZP. However, blocking DP and SP changes will hobble programming to some extent (being able to change DP on the fly is real useful) and obviously won't prevent a rogue process from overwriting much of bank $00 due to failure to maintain a clean stack.
Given all this, I need more than bank comparisons to prevent ZP and stack space corruption. Bank comparisons will protect all non-zero banks from improper accesses and thus provide sandboxing. Keeping bank $00 in shape is a different matter. My tiny dinosaur brain is having trouble visualizing what needs to be done.
Last edited by BigDumbDinosaur on Tue Jun 26, 2012 5:10 pm, edited 1 time in total.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC Version 2
teamtempest wrote:
What if the hardware "mirrored" a bit of the kernel in every 64K block?...
If the mirrored section looked like ROM to the user process that ought to be sufficient write protection. Would the HMU be able to make that happen?
BigDumbDinosaur wrote:
That could be done but it would then be necessary to somehow write-protect the mirrored area
In theory, yes. Practically speaking, the logic might be too complicated to implement in a reasonably sized CPLD. What would have to be done is to somehow prevent /WD (write data) from being asserted when a very specific address range appears on A0-A15, except when it is the kernel that is addressing that very specific range. In any case, it would only be required if kernel APIs were treated as subroutines, which most modern operating systems don't do. I'm looking at using software interrupts (INT N) to call kernel APIs, as doing so eliminates the need for every application to have knowledge of a jump table. Applications would only need to know the API numbers, and that sort of information can be buried in macros (e.g., READ CH,BUF,NBYTES instead of LDA #CH, LDX #BUF, LDY #NBYTES, INT 4).
Quote:
Regarding software interrupts - doesn't any multi-tasking kernel, pre-emptive or co-operative, have to have some knowledge of what the current running process is? Otherwise it wouldn't be possible to rationally switch to another process. And shouldn't only the current process be capable of triggering a software interrupt? I have a hard time imagining a stopped process doing so, anyway.
You're correct. Only a running process would be able to cause a software interrupt and hence the kernel would have to know which process is in context at any given instant.
Using UNIX as the example, each running process has a process ID (PID) assigned by the kernel, usually a signed 16 bit number. All processes are listed in a table, and each table entry has information to tell the kernel where the process is in memory so the kernel can keep processes away from each other. In the '816 environment, the bank (A16-A23) is really all that is needed for this purpose, since assigning RAM in 64 KB chunks is convenient. Along with the process table entries, information about each process' state, whether running or waiting, must be maintained, along with MPU state information, such as the stack pointer (SP). The MPU registers can be pushed on the process' stack when interrupted for any reason. If SP is restored when the process is selected to run after being idle, an RTI-like sequence can reload the registers and the process will resume where it left off.
When any interrupt is serviced by the '816, the current program bank (PB) is pushed to the top of the stack, followed by the PC and SR. PB is then changed to $00 and the MPU jumps through the relevant vector to service the interrupt (the ISR must be in bank $00). The key to this is it is the process' stack on which the pushes are occurring, so the kernel implicitly knows where to find the stack (just look at SP). If the interrupt is an IRQ, the stack frame provides the data needed to restart the process after the IRQ has been serviced. If the interrupt is an API call (INT N), the stack frame can be interpreted as parameters need by the API and the stack be manipulated by the kernel as required, using the '816's handy stack addressing functions.
Part and parcel to all this is the necessity of telling the CPLD logic when the system is in user or kernel mode, the latter occurring when any interrupt occurs. Switching between modes would change some of the memory protection rules on the fly, allowing, for example, a kernel function to touch RAM in a user-process space. This system behavior would be somewhat analogous to the use of the "privilege ring" capabilities of other microprocessors, but more difficult to implement, due to the use of bank $00 for ZP and stack accesses.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC Version 2
Would it be tricky to implement some kind of 'shadow memory' for bank $00? So that kernel mode would see the shadow memory, and non-kernel mode would see the 'normal' memory. Would presumably need a 'kernel mode' bit or some such in the logic, which would be switched on when an interrupt occurs, and could be manually manipulated by kernel functions (well, that would need two bits then.. an interrupt would set the 'kernel' and 'shadow' bits, and sw could only change the 'shadow' bit if the 'kernel' (or 'privileged') bit is on.)
(All solutions based on external logic will start to sound like a description of typical memory management hardware I suppose..)
-Tor
(All solutions based on external logic will start to sound like a description of typical memory management hardware I suppose..)
-Tor
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC Version 2
Tor wrote:
Would it be tricky to implement some kind of 'shadow memory' for bank $00?
Probably not and it appears you are mirroring thoughts that are slowly taking shape in my dinosaurish brain.
Quote:
So that kernel mode would see the shadow memory, and non-kernel mode would see the 'normal' memory. Would presumably need a 'kernel mode' bit or some such in the logic, which would be switched on when an interrupt occurs, and could be manually manipulated by kernel functions (well, that would need two bits then.. an interrupt would set the 'kernel' and 'shadow' bits, and sw could only change the 'shadow' bit if the 'kernel' (or 'privileged') bit is on.)
That line of thought is similar to what I am now starting to envision. The ATF1508AS CPLD should/will have sufficient room to set up some flops to act as various flags. There are also enough address bus inputs to the CPLD to decode multiple virtual registers, some of which could be machine state registers, such as whether in user-mode or kernel-mode, is an interrupt being handled, etc.
Quote:
(All solutions based on external logic will start to sound like a description of typical memory management hardware I suppose..)
Funny how that works. I'd have to accomplish with the CPLD what most 32/64 bit MPUs (PA-RISC, x86, etc.) are able to accomplish on their own.
My thoughts on this bank $00 dilemma are slowly gelling and I will expound on them in another post once I get to where I can put them "to paper," so to speak.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC V2 Bank $00 Dilemma
I have been contemplating the "bank $00 dilemma" for quite a while, even before POC V2 was more than some passing thoughts. To briefly recapitulate, the "bank $00 dilemma" exists due to a characteristic of the 65C816 that causes it to "hard wire" all zero page (ZP) and stack accesses, as well as indirect absolute addressing, to the first 64 KB of RAM, resulting in a $00xxxx effective address. Also, response to an interrupt of any kind causes the program bank to be forced to $00, thus requiring that the MPU's hardware vectors and interrupt service routines be located in bank $00. This convergence on bank $00 is inconvenient in any system that is expected to support preemptive multitasking, as it complicates the implementation of hardware memory protection.
Ideally, the natural memory segmentation produce by the '816's bank-oriented architecture should allow the system designer to confine each running process to an arbitrarily assigned bank, and prevent inter-bank access without the permission and cooperation of a supervisory program (e.g., an operating system kernel). This is the architecture I envision with POC V2. As ZP and stack access is necessary in most programs, a dilemma is created about what to do with the fact that ZP and stack accesses are directed to bank $00, thus violating the rule that no process be allowed to access any bank other than its own. If an exception to this rule is created so ZP and stack accesses will work, what would prevent any process from overwriting parts of bank $00 that it shouldn't touch?
In current RISC and x86 hardware, the MPU has a feature called "privilege rings," which can be utilized to prevent user-mode programs from executing instructions that should only be executed by the kernel. In such hardware, the kernel operates with maximum privileges and user applications operate with much lower privileges. Hence an attempt by a user-space process to execute potentially destructive instructions can be blocked. Unfortunately, the '816 doesn't have this feature, which if present, would solve the bank $00 dilemma. Therefore, the system glue logic has to somehow take over and create a "privilege rings" analog. I've started to develop some ideas on how to go about doing this. So the rest of this post will be a sort of sounding board for a possible solution to the bank $00 dilemma. I won't go into any hardware or software specifics, since the solution can't be designed until the problem has been defined.
Architectures that support memory protection typically use virtual addressing rather than physical, both for segregation of running processes and for economizing on RAM consumption. In virtual addressing architectures, the smallest amount of contiguous RAM that can be protected is often referred to as a page (not to be confused with the 65xx notion of a page) and, in many cases, is 4 KB in size. Multiple pages can be ganged to produce a larger protected execution space when needed.
In POC V2, I'm not particularly concerned about RAM consumption at this time. Therefore, I've decided to use fixed sized protected space equal in size to a bank, or 64 KB. As the '816's architecture is bank-oriented, I'll use that term instead of page when talking about protected space. V2 will have 1024 KB of RAM, which is equal to 16 banks, enough to prove that my virtual addressing concept will work—or not.
The CPLD will be tasked with generating the A16-A23 bank bits, which means it will always determine into which bank an access will occur. If my logic is correctly implemented, accesses that would normally be forced into bank $00 by the MPU could instead be directed to a different bank without the running process being aware of the change. The result would be a solution to the bank $00 dilemma.
In order to remap bank $00 accesses, the CPLD logic has to know where to remap the accesses and when such a translation is appropriate. The former would be determine by consulting some virtual registers in the CPLD that the operating system kernel would maintain. The latter would be determined by whether the hardware is running in "real" or "protected" mode. Also, when in protected mode, a distinction will be made between user or kernel mode. Additional rules will apply when an interrupt is serviced:
Also essential to effective protection is preventing execution of certain MPU instructions that could take down the system. Some instructions will automatically cause a memory fault because the associated operand uses a 24 bit address. However, there are instructions that should never be executed and some that should not be executable by user-mode processes. In the first group are COP, STP, WDM and XCE. In the second group are JML, JSL, RTI, RTL and SEI. SEP is potentially dangerous in one case: SEP #%00000100, which is effectively an SEI instruction. The sequence:
which would likewise disable IRQs, causing a total system halt (which a watchdog timer could fix by triggering an NMI whose handler would re-enable IRQs).
It is possible for CPLD code to look at D0-D7 while the MPU is fetching an opcode and compare it to the above list for further action. Checking SEP would mean also looking at the operand. Unfortunately, I can't think of any way to prevent a program from pulling a stack value into SR that would shut down IRQs.
Anyhow, now that I've got some of this down on paper the next step is to create some logic tables that describe what I'm trying to achieve.
Ideally, the natural memory segmentation produce by the '816's bank-oriented architecture should allow the system designer to confine each running process to an arbitrarily assigned bank, and prevent inter-bank access without the permission and cooperation of a supervisory program (e.g., an operating system kernel). This is the architecture I envision with POC V2. As ZP and stack access is necessary in most programs, a dilemma is created about what to do with the fact that ZP and stack accesses are directed to bank $00, thus violating the rule that no process be allowed to access any bank other than its own. If an exception to this rule is created so ZP and stack accesses will work, what would prevent any process from overwriting parts of bank $00 that it shouldn't touch?
In current RISC and x86 hardware, the MPU has a feature called "privilege rings," which can be utilized to prevent user-mode programs from executing instructions that should only be executed by the kernel. In such hardware, the kernel operates with maximum privileges and user applications operate with much lower privileges. Hence an attempt by a user-space process to execute potentially destructive instructions can be blocked. Unfortunately, the '816 doesn't have this feature, which if present, would solve the bank $00 dilemma. Therefore, the system glue logic has to somehow take over and create a "privilege rings" analog. I've started to develop some ideas on how to go about doing this. So the rest of this post will be a sort of sounding board for a possible solution to the bank $00 dilemma. I won't go into any hardware or software specifics, since the solution can't be designed until the problem has been defined.
Architectures that support memory protection typically use virtual addressing rather than physical, both for segregation of running processes and for economizing on RAM consumption. In virtual addressing architectures, the smallest amount of contiguous RAM that can be protected is often referred to as a page (not to be confused with the 65xx notion of a page) and, in many cases, is 4 KB in size. Multiple pages can be ganged to produce a larger protected execution space when needed.
In POC V2, I'm not particularly concerned about RAM consumption at this time. Therefore, I've decided to use fixed sized protected space equal in size to a bank, or 64 KB. As the '816's architecture is bank-oriented, I'll use that term instead of page when talking about protected space. V2 will have 1024 KB of RAM, which is equal to 16 banks, enough to prove that my virtual addressing concept will work—or not.
In order to remap bank $00 accesses, the CPLD logic has to know where to remap the accesses and when such a translation is appropriate. The former would be determine by consulting some virtual registers in the CPLD that the operating system kernel would maintain. The latter would be determined by whether the hardware is running in "real" or "protected" mode. Also, when in protected mode, a distinction will be made between user or kernel mode. Additional rules will apply when an interrupt is serviced:
- User mode. Full protection is enabled. The effective address is always in the bank set in the CPLD's user bank virtual register. Bank $00 accesses are always remapped to the bank in which the user mode process is executing. Attempted accesses to another bank will cause an memory fault and trigger a hardware abort.
- Kernel mode. Reduced memory protection is enabled. The effective address is normally in the bank set in the CPLD's kernel bank virtual register but can be changed to access another process' bank. Bank $00 accesses are normally remapped to the kernel bank but can be remapped to any other bank, such as for accessing another process' stack.
- Interrupt mode. This mode occurs when the MPU starts to service an interrupt. The following generalized steps would occur:
- The current mode and bank $00 remapping is maintained until the MPU has pushed the program bank (PB), program counter (PC) and status register (SR). As bank $00 remapping has not been changed during this step, PB, PC and SR will be pushed onto the interrupted process' stack.
- As soon as the MPU has pushed SR (cycle 6 of the interrupt sequence), it will assert (negate) its VPB (vector pull) output, which will cause the CPLD to enable interrupt mode. Protection will be changed to kernel mode and bank $00 remapping will be disabled, causing the MPU hardware vectors to appear in the range $00FE00-$00FFFF.
- During cycles 7 and 8 of the interrupt sequence, the MPU will load PC from the relevant interrupt service routine (ISR) jump vector and execute the ISR.
- With bank $00 remapping disabled, any stack activity performed by the ISR will be directed to physical bank $00. The ISR is permitted to access any bank and may also enable bank $00 remapping as long as when doing so subsequent ISR code is not in the real bank $00. Special handling of the stack pointer would be required if an ISR itself was interrupted. Proper coding, however, can prevent this from occurring.
- Real mode. All protection is disabled. The effective address is always that generated by the MPU—the CPLD will use the DB (data bank) or PB register contents to assert the appropriate address bus and chip select lines. Real mode is the default following reset.
Also essential to effective protection is preventing execution of certain MPU instructions that could take down the system. Some instructions will automatically cause a memory fault because the associated operand uses a 24 bit address. However, there are instructions that should never be executed and some that should not be executable by user-mode processes. In the first group are COP, STP, WDM and XCE. In the second group are JML, JSL, RTI, RTL and SEI. SEP is potentially dangerous in one case: SEP #%00000100, which is effectively an SEI instruction. The sequence:
Code: Select all
LDA #%00000100
PHA
PLPwhich would likewise disable IRQs, causing a total system halt (which a watchdog timer could fix by triggering an NMI whose handler would re-enable IRQs).
It is possible for CPLD code to look at D0-D7 while the MPU is fetching an opcode and compare it to the above list for further action. Checking SEP would mean also looking at the operand. Unfortunately, I can't think of any way to prevent a program from pulling a stack value into SR that would shut down IRQs.
Anyhow, now that I've got some of this down on paper the next step is to create some logic tables that describe what I'm trying to achieve.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC Version 2
Just wondering - because I have never programmed the 65C816 (only the 6502): Will the user process running in bank NN be aware of the physical bank it's running in? Should it be made to "think" that it's always running in bank $00? Or maybe it doesn't matter? (as you can see, I'm not familiar with the 65816
)
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
-Tor
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
-Tor
Re: POC Version 2
Tor wrote:
Just wondering - because I have never programmed the 65C816 (only the 6502): Will the user process running in bank NN be aware of the physical bank it's running in? Should it be made to "think" that it's always running in bank $00? Or maybe it doesn't matter? (as you can see, I'm not familiar with the 65816
)
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
My only question regarding the interrupt handling is it seems that the kernel will not be able to access the context information that was pushed on to the restricted banks stacks, is that correct? By the time kernel mode is invoked, all of that context switching is done, but there's no record of it, so the kernel won't be able to even get access to the stack of the caller?
How does that work?
- GARTHWILSON
- Forum Moderator
- Posts: 8774
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: POC Version 2
Tor wrote:
Just wondering - because I have never programmed the 65C816 (only the 6502): Will the user process running in bank NN be aware of the physical bank it's running in? Should it be made to "think" that it's always running in bank $00? Or maybe it doesn't matter? (as you can see, I'm not familiar with the 65816
)
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC Version 2
Tor wrote:
Just wondering - because I have never programmed the 65C816 (only the 6502): Will the user process running in bank NN be aware of the physical bank it's running in? Should it be made to "think" that it's always running in bank $00? Or maybe it doesn't matter? (as you can see, I'm not familiar with the 65816
)
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
I guess I'm asking if your POC V2 will look like a (better/improved) 65C02 to each of the user level programs?
As Garth pointed out, almost any 65C816 register can be retrieved for inspection. So a user-mode process can determine in which bank it is running with the following sequence:
Code: Select all
shorta ;8 bit .A (a macro that performs SEP #%00100000)
phk ;push program bank (PB) &...
pla ;retrieve itNote that the '816 doesn't have an instruction that can pull PB from the stack. Hence it would be difficult (but not impossible) for a user-mode process to change its execution bank. If it were to do so, however, a memory protection fault would immediately occur because the A16-A23 part of the effective address at which the next instruction would be located wouldn't match the active bank set in the CPLD logic.
whartung wrote:
That's the way it sounds to me. Everything should just be believe they're running in $00, and effectively act like a 6502+extra '816 instructions.
Correct.
Quote:
My only question regarding the interrupt handling is it seems that the kernel will not be able to access the context information that was pushed on to the restricted banks stacks, is that correct? By the time kernel mode is invoked, all of that context switching is done, but there's no record of it, so the kernel won't be able to even get access to the stack of the caller?
How does that work?
How does that work?
The CPLD maintains context state information in several virtual registers:
- HMU_IBNK. The bank in context when a hardware interrupt occurs. This register is changed by the CPLD logic when it detects an IRQ or NMI, and is consulted by the hardware interrupt handlers so they know to which bank to return when interrupt processing is completed.
- HMU_KBNK. The bank in which the kernel is running, which is established at boot time (it would not change thereafter).
- HMU_UBNK. The bank in which the current user-mode process is running. This register is changed by the kernel when any software context switch is made. When a kernel API has completed processing it returns to the bank in this register.
- HMU_BNK0. If bit 7 is set in this register, bank $00 remapping rules are in effect, causing effective addresses that are "hard wired" to bank $00 to be redirected to the bank currently in context. The effective address seen by the MPU will be in the range $BB0000-$BBFFFF, where BB is the bank currently in context. If bit 7 is clear, effective addresses that are "hard wired" to bank $00 are directed to physical bank $00. The effective address seen by the MPU will be in the range $000000-$00FFFF.
HMU refers to the "hardware management unit," which is a virtual device inside the CPLD. All HMU virtual registers are accessible only to the kernel when operating in protected mode.
The kernel also has the ability to perform cross-bank loads and stores by loading the MPU's DB (data bank) register with any bank and then executing an absolute addressing instruction, such as LDA STACK_U,X. As long A8-A15 of the generated effective address is non-zero the load or store will go to the bank defined by DB. A user-mode process could likewise change DB and do the same thing, but would cause a memory protection fault on the next absolute memory referencing instruction.
The following code examples illustrates a preliminary version of the kernel's API entry code. Kernel APIs are called with the INT API_NUM macro, in which API_NUM is a non-zero API number. The macro assembles as:
Code: Select all
brk
.byte API_NUMIn order to know which API has been requested, the kernel has to be able to get API_NUM from the caller's bank, which it would do with several cross-bank reads:
Code: Select all
;KERNEL API CALL FRONT END
;
; ————————————————————————————————————————————————————————————————————————————————
; Kernel APIs are called with the INT API_NUM macro, where API_NUM is a (non-zero)
; API number. Prior to making the API call, the caller must load the MPU reg-
; isters with whatever parameters are required by the API. Upon entry, the MPU
; will have pushed PB, PC & SR to the caller's stack & disabled IRQs. The
; caller's stack frame will be as follows:
;
; SP + 4 PB
; SP + 3 PC MSB
; SP + 2 PC LSB
; SP + 1 SR w/the I bit set
;
; PC points to the location immediately following API_NUM.
;
; For programming convenience, the stack frame is defined as follows:
;
stkptr =0 ;current stack location
sr_call =stkptr+s_byte ;SR stack offset
pc_call =sr_call+s_byte ;PC LSB offset
pb_call =pc_call+s_word ;PB offset
;
; The values S_BYTE & S_WORD are 1 & 2 respectively.
;
; At the point when the MPU jumps through the BRK vector the kernel's bank
; bank is in context & stack references go to the kernel's stack, not the
; caller's. The 1st step is to preserve the entry register values, as well as
; the caller's stack pointer. Preserved values are written to zero page loc-
; ations that, due to bank $00 remapping, always appear in the kernel's bank,
; even though the MPU DB register is pointing to the caller's bank.
; ————————————————————————————————————————————————————————————————————————————————
;
longr ;16 bit writes
sta ca_ent ;protect entry values
stx cx_ent
sty cy_ent
tsx ;get caller's stack pointer &...
stx sp_ent ;protect
lda sp_kern
tcs ;set kernal's stack pointer
shorta ;select 8 bit .A & memory
cli ;re-enable IRQs
;
; ——————————————————————————————————————————————————————————————————————
; At this point, the switch to kernel mode has been completed. The next
; step is to determine which API has been requested.
; ——————————————————————————————————————————————————————————————————————
;
longa ;16 bit .A & memory
lda pc_call,x ;read caller's stack to get PC
dec a ;move PC back to API number
tax ;now an index
shorta ;8 bit .A
lda $0000,x ;get API number...
;
; —————————————————————————————————————————————————————————————————
; Due to the way the MPU generates the effective address, the above
; instruction will not be affected by bank $00 remapping, as the
; A8-A15 bit pattern will be non-zero & therefore not a ZP address.
; —————————————————————————————————————————————————————————————————
;
shortx ;8 bit .X & .Y
ldx hmu_kbnk ;kernel bank number (in CPLD)
phx ;direct loads/stores to...
plb ;kernel's bank
asl a ;API number becomes...
tax ;an index
jsr (kapitab,x) ;run requested API
;
; API common exit point would be here...omitted for clarity.
;
...program continues...Obviously, all of this is subject to change, as POC V2 is only a paper computer at this time.
Last edited by BigDumbDinosaur on Wed Jun 27, 2012 7:25 pm, edited 1 time in total.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC V2 Logic
BTW, there are some differences in what transpires during a kernel API call as described in my previous post, than as described in the post where I originally laid out my thoughts. Further rumination suggested that bank $00 remapping not be disabled when in kernel mode, except in some very narrow circumstances that have yet to be defined. It's all subject to change as my thoughts gradually come into clearer focus.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC Version 2
I've slept on the design for a while and made a few modifications, mostly to simplify it. Also, I've fleshed out the HMU (hardware management unit) definitions. The PCB layout is also a little tighter. See updated schematics and PCB layout. I've spread the illustrations over two posts to get around the five attachment limit per post. Schematic pages one through five are below. Pages six and seven, as well as the PCB layout are in the next post.
Last edited by BigDumbDinosaur on Thu Jul 19, 2012 4:25 pm, edited 1 time in total.
x86? We ain't got no x86. We don't NEED no stinking x86!