How to force stack switch in interrupt call

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
osdevuser1923
Posts: 1
Joined: Wed Jul 12, 2017 2:31 am

How to force stack switch in interrupt call

Post by osdevuser1923 »

I'm writing task switching, which must occur on timer interrupt. But some of threads are in ring 0, others - in ring 3.
So, when task switchs from ring0 to ring3: stack don't switch when entering handler, but on exit, CPU extract 2 extra words from stack and ruins it.

TSS is configured: bochs write the following info:

tr:s=0x2b, base=0x00000000ffffd000, valid=1
ss:esp(0): 0x0010:0x00800ff0
ss:esp(1): 0x0000:0x00000000
ss:esp(2): 0x0000:0x00000000
cr3: 0x00000000
eip: 0x00000000
eflags: 0x00000000
cs: 0x0000 ds: 0x0000 ss: 0x0000
es: 0x0000 fs: 0x0000 gs: 0x0000
eax: 0x00000000 ebx: 0x00000000 ecx: 0x00000000 edx: 0x00000000
esi: 0x00000000 edi: 0x00000000 ebp: 0x00000000 esp: 0x00000000
ldt: 0x0000
i/o map: 0x0000

There is written, that when TSS is configured, stack switchs in any case, not only when ring3-code interrupted. But I can't get such behaviour of CPU.
How to force stack switch even if privilege level don't change?
User avatar
Js2xxx
Member
Member
Posts: 48
Joined: Sat Dec 31, 2016 1:43 am
Libera.chat IRC: wrgq
Location: China

Re: How to force stack switch in interrupt call

Post by Js2xxx »

Well, it seems that you're in x86 mode.
If my memory is correct, TSSes in long mode(x64) contain a Interrupt Stack Table and IntGates(x64) contain a index which points to the table. The IST don't rely on which ring to switch and when interrupts happen, if an index in a IntGate(x64) points to an item in the IST, stack switch will always cause.
But in protected mode, you seems to do it manually :( .
Doing steadfastly, or doing nil.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: How to force stack switch in interrupt call

Post by Brendan »

Hi,
osdevuser1923 wrote:I'm writing task switching, which must occur on timer interrupt.
No.

Task switches occur when:
  • The scheduler has to find something else for CPU to do, because:
    • The currently running task blocks for any reason (e.g. has to wait for disk IO or network or IPC or ....)
    • The currently running task terminates itself (e.g. calls "exit()")
    • The currently running task crashes
  • The scheduler decides that higher priority task should preempt the currently running (lower priority) task immediately, because:
    • The higher priority task was unblocked (whatever it was waiting for happened)
    • A new high priority task is being spawned/created
  • There are 2 or more tasks running (for the CPU), and the currently running task used all of the time slice it was given, and scheduler wants to give other task/s some CPU time
Note that the last reason ("currently running task used all of the time it was given") is relatively unimportant. Most task switches are caused by tasks blocking/unblocking (and it is possible to have perfectly sane scheduler without bothering with "currently running task used all of the time it was given" - e.g. using a "highest priority task that can run does run" approach).
osdevuser1923 wrote:But some of threads are in ring 0, others - in ring 3.
No; all tasks are running in ring 0 when you find out that a task switch needs to happen.

For ring 3 tasks; something causes the CPU to switch to ring 0 (a kernel API call, an exception, an IRQ), then while you're running in ring 0 anyway the kernel decides it should do a task switch for one of the reasons listed above.

Essentially, switching rings (and TSS, etc) has nothing to do with task switching at all.
osdevuser1923 wrote:How to force stack switch even if privilege level don't change?
Because all task switches happen when CPU is running in ring 0; you only ever have to worry about switching from a task that is running at ring 0 to another task that was running at ring 0. The low level task switch code can push a few registers onto the old task's kernel stack, save "kernel stack top" (RSP register) somewhere, load a new "kernel stack top" (RSP register) from somewhere, switch to the new task's virtual address space if necessary, then pop a few registers from the new task's kernel stack.

After the task switch happened, the task might or might not switch back to ring 3; but that still has nothing to do with task switching at all.

For a simple example, imagine that:
  • A task calls the kernel API's "read()" function (causing switch from ring 3 to ring 0)
  • The kernel's "read()" function checks if the data is cached or not, finds out the data isn't cached, and asks the file system code to fetch the data; and the task is blocked (causing a task switch) because it has to wait while the data is read from a hard disk
  • Later on the data arrives from disk/file system/whatever; and the task is unblocked
  • Sooner or later (possibly immediately) the scheduler does a task switch back to the (now unblocked) task
  • Then the kernel figures out how much data was read (or if there was an error, etc) and returns this information from the kernel API's "read()" function (causing a switch from ring 3 back to ring 0).

Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply