2

In bootasm.S

.p2align 2                                # force 4 byte alignment
gdt:
  SEG_NULLASM                             # null seg
  SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)   # code seg
  SEG_ASM(STA_W, 0x0, 0xffffffff)         # data seg

gdtdesc:
  .word   (gdtdesc - gdt - 1)             # sizeof(gdt) - 1
  .long   gdt                             # address gdt

And this is used in

lgdt gdtdesc

Shouldn't the first word of gdtdesc be the size of gdt in bytes? In this case, it's 3*8=24, which equals to gdtdesc - gdt. Why gdtdesc - gdt - 1 here?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
rapiz
  • 117
  • 11
  • 1
    The reason why its a limit and not a size is that you can't fit the size of a max-length gdt (8192 entries) into 16 bits. – fuz Jun 09 '21 at 09:55

1 Answers1

5

According to the manual, lgdt wants the GDT size in bytes, but also describes it as the "limit". That wording is ambiguous between size vs. address of the last byte. (Which would make sense as a way to allow a higher limit without wrapping the 16-bit limit.)

But the examples in https://wiki.osdev.org/GDT_Tutorial use sizeof(gdt) so that's either a bug in xv6 or in the osdev tutorial.

https://wiki.osdev.org/Global_Descriptor_Table agrees with xv6, saying "limit" is size-1, unlike the GDT tutorial. This makes sense:

The size is the size of the table subtracted by 1. This is because the maximum value of size is 65535, while the GDT can be up to 65536 bytes (a maximum of 8192 entries). Further no GDT can have a size of 0.

If you want to confirm the details, check Intel or AMD's manuals; they will hopefully clarify this point somewhere in the system development details, separate from the instruction-set reference entry for lgdt.

Or you'd hope they would. But unfortunately Intel says:

Section 2.4.1 Global Descriptor Table Register (GDTR)

"the table limit specifies the number of bytes in the table".

Which probably just means that whoever wrote it is so caught up in the limit = offset of last byte = size idea that they didn't even realize this isn't clear. Segment limits themselves (in GDT entries) also work this way, using 0xFFFFF (with granularity=page) to specify the limit as the very top of the 4GiB address space, i.e. unlimited.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    You'd think. Instead, it's more confusion :) _Section 2.4.1 Global Descriptor Table Register (GDTR)_ says _"the table limit specifies the number of bytes in the table"_. That's the intel one, haven't checked AMD yet. – Jester Jun 09 '21 at 10:11
  • @Jester: Is there any actual doubt that limit should be the offset of the last byte, not `sizeof`? e.g. has anyone double-checked by setting limit=15 and then trying to use the 2nd entry? I'm assuming that whoever wrote that section of the manual was just thinking in terms of the same way segment limits in GDT entries work, specifying the highest valid address (or page). – Peter Cordes Jun 09 '21 at 10:23
  • `15` works at least with kvm. It seems to be a limit, yes. – Jester Jun 09 '21 at 16:33
  • 1
    AMD system programming guide seems to be more explicit: _"Limit. 2 bytes. These bits define the 16-bit limit, or size, of the GDT in bytes. The limit value is added to the base address to yield the ending byte address of the GDT."_ The "or size" is a little confusing here too, but it does then go on to explain that it gives the address of the last byte. It goes on to say _" A general-protection exception (#GP) occurs if software attempts to access a descriptor beyond the GDT limit."_ Futhermore it's considered to be a "pseudo descriptor" which implies the usual limit semantics. – Jester Jun 09 '21 at 16:56
  • Oh, and I tested on an AMD Ryzen 1700x. So intel could do it differently. If there is any doubt, I would nevertheless set the field to be the size not the limit as that would work the same with both implementations. – Jester Jun 09 '21 at 16:57