In this lecture, we will talk about Task state segment, TSS and enable interrupts in user mode.
When we are running in ring3, an interrupt is fired
the control will be transferred to the interrupt handler which runs in ring0 in our system.
Remember the DPL of the segment descriptor which the interrupt handler is at is set to 0.
In this process, the ss segment register is also loaded with null descriptor by the processor.
and rsp register is also loaded with a new value.
The value of RSP is stored in the TSS.
Therefore, what we need to do is set up the TSS and specify the new value of RSP in the TSS.
as you see, this is a structure of tss.
The field of RSP0 is what we want.
When the control transferred from low privilege level to ring0, the value of RSP0 is loaded into RSP register.
Since we don’t use ring1 and ring2, we are not gonna set those fields. The interrupt stack table,
as we saw in the interrupt descriptor,
if the IST field in the interrupt descriptor is non-zero, then it is the index of IST here.
For example, if it is 1, then the value of IST1 is loaded in RSP instead of RSP0 value.
.
Since we have set the interrupt descriptors with IST field being 0, we are not gonna use the IST fields here.
.
The last item is the address of io permission bitmap which is used for protection for io port addresses.
It is not used in our system.
The TSS descriptor is stored in the GDT. Unlike the code segment and data segment descriptors where the base and limit are ignored,
the base and limit attributes in TSS descriptor are used.
So when we set up the descriptor, the base address and limit need to be set correctly.
the value 0,1,0,0,1 means that this is the 64-bit TSS descriptor.
The meaning of P and DPL is the same as in the code and data segment descriptors.
The G and available bit are not used in our system, so this part of the attribute will be set to 0.
In the upper part of the descriptor, we have 5 0s,
and the part in grey means it is reserved.
Right now, we have seen the tss descriptor.
we also need a selector to reference the descriptor.
Loading the TSS selector is different from loading the code segment selector.
Here we load the selector using instruction load task register.
After the selector is loaded, the processor then uses the selector to
locate the TSS descriptor in the GDT.
Let’s see how to implement it in the project.
The first thing I’m gonna do is define the Task state segment structure.
We call it TSS.
As we saw in the slide, the first four bytes are reserved,
so we assign 0 to it.
The next field is RSP0,
this is the field we need to set with the new address of RSP.
Here we want the RSP loaded with
150000 when the interrupt handler is called. Other fields are not used in the system, we simply set them to 0.
.
So we use directive times.
The last field is the address of IO permission bitmap. We assign the size of TSS to it
which means IO permission bitmap is not used.
Ok, we define the length of the TSS.
Once the Task state segment is defined, what we are gonna do next is define the TSS descriptor.
.
In the GDT, we add a new entry called tss descriptor
so that we can see it easily.
If you don’t want the label tss descriptor, you can simply remove it.
The first two bytes are the lower 16 bits of tss limit.
We set it to the length of TSS minus 1.
The base address of TSS is set to 0 for the moment,
we will assign the address in the code.
So we set the next 3 bytes to 0s
which is the lower 24 bits of the base address. The next byte is attribute field. We assign the value.
.
89
In the binary format, you can see the present bit is 1, DPL is 0,
and then 0,1,0,0,1 specifies this is the 64-bit TSS.
As for other fields, simply set them to zero.
OK, we are done. At the start of the code,
we will load the TSS descriptor.
First, we need to copy the address of TSS to the descriptor,
We move rax, tss.
The lower 16 bits of the address is in the third bytes of the tss descriptor.
We move
tss descriptor plus 2 and ax.
Bit 16 to 23 of the address is in the fifth bytes of the descriptor.
We shift right
the value in rax by 16 bits.
Bit 16 to 23 of the address is now in register al and then move tss descriptor
plus 4 and al.
Then we continue to shift right rax
8 bits.
The next part of the base address is in the 8th byte of the descriptor,
so we move
tss descriptor plus 7 and al.
The upper 32bits of the address is in the next location. We shift rax 8bits.
Eax holds the upper 32bits of the address now and we move
tss descriptor plus 8,
,
OK.
the tss descriptor is set at this point.
We can load the selector using load task register instruction. The selector we use is 20 in hexadecimal,
since the descriptor is the fifth entry in the GDT.
we move ax, 20 and load task register,
ax.
All right.
setting up the tss is finally done.
Before we test it out, we need to enable the interrupt. When we get from ring0 to ring3,
,
the interrupt flag is cleared, bit 9 of rflags is the interrupt flag.
So instead of pushing the value 2,
we push 202, indicating that the interrupt is enabled. Once we return to user entry,
the value will be loaded in rflags and the interrupt is enabled.
Remember the timer is configured and working now.
Once the timer interrupt is fired, the execution is interrupted
and the processor responds to the interrupt, we will see the timer handler is called
if everything goes as expected.
So we save the file,
and build a project.
OK.
As you can see we have 3 characters K, U, and T. So the character T is what we expect.
the TSS is working.