VOGONS


Reply 20 of 32, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

Yes, it's the fetch that exceeds the segment limit that causes the first fault. Using -1 is guaranteed to fail no matter how large the segment is.
Try mov ax,[ffff] in regular real mode (i.e. 64kb segments), you'll see the same thing.

Reply 21 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++

You say it's because the segment limit is exceeded. But when -1 is loaded, the segment limit is effectively 0xFFFF? So address 0x000-0x800 fall within range, not triggering an exception?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 22 of 32, by Scali

User metadata
Rank l33t
Rank
l33t
peterferrie wrote:

Try mov ax,[ffff] in regular real mode (i.e. 64kb segments), you'll see the same thing.

If I understand you correctly, mov al, [ffff] would work... but mov ax, [ffff] will read a byte from [ffff], which will work... but then it will read another byte from [ffff+1], and this will cause a fault? Rather than just wrapping around to [0] and completing without error, although perhaps not with the result one expected?

In which case it'd mean that the CPU will load the pointer to the IDT base/limit pair correctly, then tries to access the IDT base/limit, but since the IDT base/limit is more than 1 byte, it only reads one byte successfully, and then fault on the rest?

Last edited by Scali on 2016-08-12, 10:23. Edited 1 time in total.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 23 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++

Logically thinking, I see no reason why the 2nd byte and onwards should fail: the offset into the IDT(since it's a 32-bit CPU) can't wrap to 0(causing a fault because it exceeds 0xFFFF) with the interrupt vectors available(16-bits, meaning possible offsets 0-800h)? Or is this simply because, after the linear memory address(base+offset) wraps, the fault is caused by the resulting address being outside of the 'scope' of the IDTR? So (base+offset)<base(32-bit logic), which causes the exception, because it's outside of the given range(thus the check to not throw an exception is:

(base <= (base+addr) <= (base+limit))?NOEXCEPTION:EXCEPTION

That would cause the interrupts to always throw exceptions when all are 32-bit numbers, but otherwise valid addresses without overflow will work? Probably this is for segments in general? But reversed(NOT) with topdown segments?

But when NOT is done for the topdown segment, the area before the base would become accessable(unmapped), so it would need to be expanded some:

[NOT](base <= (base+addr) <= (base+limit)) && (base<=(base+addr))?NOEXCEPTION:EXCEPTION

Where NOT is used with top-down segments only.

Also, with the 80186 there was a bug with offset 0xFFFF word accesses, the second byte being read/written from offset 0x10000(of the linear memory, which still wraps at 1MB due to 20 address lines) instead, which might be happening on the 386+ as well, with 32-bit calculations(e.g. 1234:FFFF+10 resolving to 1234:1000F, where 1000F>segment limit, thus a fault?)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 24 of 32, by crazyc

User metadata
Rank Member
Rank
Member
superfury wrote:

You say it's because the segment limit is exceeded. But when -1 is loaded, the segment limit is effectively 0xFFFF? So address 0x000-0x800 fall within range, not triggering an exception?

-1 is the pointer the idt base and limit are loaded from, not the base and limit itself and the read will be 5 bytes (3 base and 2 limit). If any of those 5 bytes is past the limit (the limit of the segment being addressed not the idtr limit being read) it'll fault and there's no wrapping around.

Scali wrote:

In which case it'd mean that the CPU will load the pointer to the IDT correctly, then tries to access the IDT, but since the IDT is more than 1 byte, it only reads one byte successfully, and then fault on the rest of the table?

Lidt loads base and limit into the idtr descriptor cache so no access check other than the limit will occur on accesses to the idt. If the idt were located at the top of ram (0xffffff) I assume it would wrap around.

superfury wrote:

(since it's a 32-bit CPU)

We're talking about the 286. On the 386 much of this doesn't apply as you could triple fault if there were a page fault on access to the idt and you'd exit pmode by clearing the lsb of cr0 anyway.

Reply 25 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++
crazyc wrote:
superfury wrote:

You say it's because the segment limit is exceeded. But when -1 is loaded, the segment limit is effectively 0xFFFF? So address 0x000-0x800 fall within range, not triggering an exception?

-1 is the pointer the idt base and limit are loaded from, not the base and limit itself and the read will be 5 bytes (3 base and 2 limit). If any of those 5 bytes is past the limit (the limit of the segment being addressed not the idtr limit being read) it'll fault and there's no wrapping around.

But that would mean that the pointer it loads from is a signed number? I can't find anything in any 286/386 manual about the pointer being signed? Actually, it doesn't say anything about signedness, so I assumed it's, like all other pointers, an unsigned number?

Btw, if the address of the LIDT instruction exceeds the addressed segment and the double/#GP fault handler exists, won't it just be executed? Since the actual load has failed, the IDTR still has it's old(valid) value? Or is it invalidated using a hidden flag?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 26 of 32, by Scali

User metadata
Rank l33t
Rank
l33t
superfury wrote:

But that would mean that the pointer it loads from is a signed number?

Why?
I'm not sure if you fully understand 2s completement notation.
There is no sign for pointers, and even if there were, it'd be meaningless, as pointers are only added and subtracted, which is the same for signed and unsigned numbers under 2s complement.
Writing '-1' is just a shorthand way to say 'all bits in the number are set to 1', so a number of the form '0xFFFFFF'.

In most cases, you can also use -1 in your code. Sometimes you might need a cast so your compiler or assembler understands it should actually treat it as unsigned.
But in C, something like 'unsigned char x = -1' will just give you the value of 0xFF.
When you have a stronger typed compiler, you might need to write 'unsigned char x = (unsigned char)-1', but you'd still end up with 0xFF.
Because in 2s complement, -1 and 0xFF have the same bit pattern. Whether it is signed or unsigned is just semantics. It's what you do with the number. Eg when you perform a cmp and a conditional jump, do you use jae or jge? That makes the difference between signed and unsigned at the CPU level.
Compilers don't have separate if-statements for signed and unsigned, so in their case, the type of the variables determine what to do.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 27 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++

So I was right in the LIDT loading data from 0xFFFFFF to 0xFFFFFF+6. But that wouldn't throw any exception, would it? It would simply wrap around to physical address 0x000000(24-bits address(lines)), resulting in byte 1-7 being read from memory locations 0x000000-0x00006 and actually loading that, together with the byte at 0xFFFFFF into the IDTR. So the IDTR contains those bytes read from memory, which might or might not actually throw an exception when used, unless those bytes at physical memory 0xFFFFFF contain 0x00 and byte at physical memory 0x000000 contains values 0x00-0x0D. Otherwise, the interrupt handler would be read correctly and the interrupt might fire, depending on the entry contents in the table(interrupt 1h descriptor).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 28 of 32, by crazyc

User metadata
Rank Member
Rank
Member
superfury wrote:

Btw, if the address of the LIDT instruction exceeds the addressed segment and the double/#GP fault handler exists, won't it just be executed? Since the actual load has failed, the IDTR still has it's old(valid) value?

Yes, this is why (assuming the idtr is valid) lidt -1 won't cause a triple fault.

superfury wrote:

So I was right in the LIDT loading data from 0xFFFFFF to 0xFFFFFF+5.

No, that isn't relevant. The 286 can't address 0xffffff directly, any address in pmode has to go though a segment descriptor and the segment limit cannot be larger than 0xffff. If an access exceeds the limit you'll get a fault, it will not wrap around.

Reply 29 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is what happens as far as I understand it now:
1. LIDT -1 => LIDT DS:[0xFFFF], since only 16-bit offsets exist in a 80286.
2. Then it starts to check the segment against it's limit(which cannot be higher than 0xFFFF), so it loads the first byte from memory offset DS:FFFF into a temporary/intermediate buffer inside the CPU(not directly into IDTR, because it might still error out, and we don't want to load interrupted data into the register, right?).
3. Then it starts to check the next offset, which resolves to DS:10000. 10000 is above the segment limit, thus it tries to call the #GP exception handler.
4. The #GP exception handler is loaded from the table, which is based at the position defined by the old IDTR(which is unchanged by the LIDT instruction, because of the exception, still having the loaded data in the intermediate buffer(the first byte from offset 0xFFFF)).
5. The GP interrupt handler flags are checked etc. e.g. Normal interrupt execution of the #GP handler.

So how can this cause a triple fault on a 286? The IDTR cannot be modified because the load to the IDTR can't even complete(it causes an exception loading the pointer).

If the first byte is immediately stored in the register, the low byte of the IDTR limit will contain the value loaded from the segment, unless DS is already invalid to begin with, which causes an exception without changing the IDTR?

But the address specified to load from cannot exceed 0xFFFF, unless it's wider than 16-bits? Is this address used bigger than 16-bits(when adding the relative offset of +1 to +4 for the rest of the IDTR's data)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 30 of 32, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

regarding (2) and (3), there's no fetch - the limit check is performed first and fails immediately. Otherwise you might get a page fault instead of a general protection fault.
The limit check also ensures that the fetch can be made in one access, not byte-by-byte. Otherwise it might get interrupted while updating the register.

Reply 31 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++

So it's actually fetching 64-bits at once from memory? Or does it only check the limit first(before fetching anything from the specified memory address), then starts retrieving data using byte/word/dword fetches?

So it sees an offset of 0xFFFF, knows it's going to fetch 6 or 8 bytes from memory, performs the limit check(which when not failing then actually starts reading the data from memory), which when fails (for offset to offset+8 range) for the specified memory range(0xFFFF to 0xFFFF+6(286)/8(386+)) raises #GP?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 32 of 32, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've implemented various new parts of the 286/386+ protected mode:
https://bitbucket.org/superfury/unipcemu/src/ … pu/protection.c
https://bitbucket.org/superfury/unipcemu/src/ … ing.c?at=master

I've added and implemented(based on the i286.cpp combined with i386 documentation):
- Full i286/386 task switching
- IDT now fully supported.
- I/O input/output protection
- Flags protection(STI/CLI/POPF)

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io