Ring 0 Code

I am trying to write a simple driver for a timer device. The board has an AMD GX500, which uses the AMD CS5536 Companion Device, and the timer that I want to use is on this companion device.

The problem I am running into is that to set up this timer properly, I need to access a couple of MSRs (Machine Specific Registers), through the use of RDMSR and WRMSR assembly instructions.

Even if I call ‘ThreadCtl(_NTO_TCTL_IO, 0)’ to get I/O priveledges for my code, I still generate a memory fault when I try to execute either of these instructions to access a MSR.

Documentation says that RDMSR and WRMSR can only run from Ring 0, yet the QNX Knowledge Base (QNX.000009784) article says that Ring 0 is only accessible from the kernel.

Does anyone have some suggestions as to how to read/write MSR’s from a QNX application?

EDIT : I should probably add, although its implied by stating that I’m running on an AMD GX500, that this question is very x86 specific.

Why do you think that you need to access an MSR to control the timer on an external timer?

You can run ring 0 instructions from an ISR.

Or from a TraceEvent handler… although, like mitch, I wonder what kind of hardware design would necessitate this (would be ugly on any virtual mode operating system).

This has gotten me thinking. What is the point of privileged processes not running in ring 0? There are two justifications for limiting what a process can do. Security is not an issue for privileged processes unless you are playing defensive, and think that a process that could clear your hard drive is somehow better than one that could cause a double fault. The other reason is to keep you out of trouble. But if you can get into trouble anyway, via an interrupt, what is the point?

I was thinking about this, because after I read mario’s comment, it occured to me that you could just put whatever code you wanted to, into a timer interrupt, and have it executed within a timer tick. This seems to be a rather awful contortion.

My educated guess is that getting in and out ring 0 would probably be an expensive operation.

In fact i don’t think you can get into ring 0 programatically, hence if the kernel switch to some other ring ( 3 ?) to run the ISR then it has no mean of getting back into ring 0 when getting back into the kernel code.

By expensive, do you mean relative to the executing the ISR? I guess this could be an issue. Of course it is done all the time during context switching, so it can’t be too bad.

Did someone use Ring0() mkernel call for such things?
Looking to a source code of handler can say that it MAY solve a problem of MSR access.


PS: Access to MSRs in timer ISR for setting up some HW looks ugly for me… Sorry.

No I mean on top of the overhead of the ISR. I beleive the processor goes to ring 0 automaticaly when an interrupt occurs, so going to some other ring mode and back to 0 within an ISR is not going to be pretty.

This is just an educated guess I have never look at this stuff in detail.

I think getting out of Ring0 is just a matter of flipping a bit in the stack and doing a RET to jump to the ISR. As far as getting back to Ring0, I think it’s just a jmp through a gate. Now the overhead on the latter might be something they wanted to avoid. On the other hand, maybe they understand that you might want to mask/unmask an interrupt, or turn them all off. I don’t think you can do that without Ring0.

What do you mean by jmp through a gate?

You may recall that a software interrupt can cause a ring change. Likewise the OS can setup a jump gate. You jmp to this address and the hardware changes context. I think you set this up in the selector table.

Of course, this discussion is academic, since the mechanism exists to run interrupt handlers in user mode (InterruptAttachEvent).

The purpose of an O/S is not to disallow an unsafe design, but to allow a safe design. You don’t have to use InterruptAttach; and, in fact, IMO there is seldom a valid reason to use it.

Correct me if I’m wrong please. When you do an InterruptAttachEvent, your code is scheduled, so it may or may not run soon after the interrupt handler returns. With InterruptAttach, your code runs before the interrupt handler returns. In many cases this difference is not crucial or even significant, but not always. Of course in other situations, it may be a superior design.

The interrupt thread is indeed scheduled; and thus you (the system designer) determine exactly when it is placed on the CPU. If you set the priority of the pulse (attached with InterruptAttachEvent) to 255, it will be the first thread scheduled when the kernel exits (assuming that there are no other READY threads at priority 255). If you give it a lower priority than 255, then presumably it is because there is some other task that has a higher priority.

Worse is that your ISR thread of a high priority ISR may get preempted by a ISR thread of a lower interrupt. You have to make sure you do the unmask() as late as possible.

I know this is belaboring a point, but it does have to be scheduled. I haven’t done a reality check on this lately, and with advent of Quad P IV’s and whatever is next this point may be mute, but in the good old days of 8086, 286, 386, and 486, executing the interrupt handler code in an interrupt handler was often required.

I don’t understand this comment.

You are completely correct that any interrupt can “pre-empt”(1) any thread (including the highest priority thread that is READY), but this is precisely why you want to use InterruptAttachEvent, since the interrupt latency is a known quantum, whereas some arbitrary interrupt handlers worst case execution time is (at best) difficult to determine.

As for the unmask() I really don’t understand the point here, because unmask() only applies to the IRQ level associated with the IST (Interrupt Service Thread), and will not prevent other interrupt levels from asserting.

(1) technically, since interrupts are outside the scheduling domain, pre-emption can not occur, since pre-emption is, by definition, a decision taken by the scheduler, and the scheduler is not involved in deciding when an interrupt line is asserted.

Sorry Renny, let me try again.

Hum, I check the doc and seem I was wrong. For some reason I though InterruptMask was masking the interrupt specified and all lower level interrupt as well. That’s why I though it was best to delay the unmask as much as possible to make sure a lower priority interrupt doesn’t disrupt the ISR thread.

To me that meant that interrupt priority would be respected even when InterruptAttachEvent is used. I prefer the interrupt priority ( from a hardware level point of view ) be respected over everything else, but that’s just me.

The point about InterruptAttachEvent being a know quantity is true but only if no other program/driver is using InterruptAttach?

However InterruptAttachEvent has something I really like though, the ability to use multi processor .

Also, an IST can be placed in a partition, and thus even an unintentional “while(1);” at priority 255 in an interrupt handler won’t take your system down, nor will buggy hardware that generates an interrupt storm.

There is a lot to be said for ISTs vs. ISRs…