Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Thu Jun 14, 2018 7:18 pm

Member

Joined: Fri Oct 27, 2006 9:42 amPosts: 1603Location: Athens, GA, USA

TBH, I am not quite certain why you are having this specific issue, but I'll dig deeper shortly.

Having said that: I know that this has been said to you before, but I think it needs to be reiterated that the Duct von Tape approach to writing programs is not a viable strategy in OS dev. This problem, or rather the several problems I see in your GDT, is a good example of why this is the case.

I am not sure how much of this code you wrote yourself, if any, but I can say for certain that you haven't understood all of it. The reason I say this is because

You don't appear to have a null descriptor, which is the one things that is unequivocally necessary for a valid GDT; or rather, you don't explicitly refer to it as such. While you do allocate the space for it, here,

Code:

GDT: dq 0

the fact that it isn't labelled as such makes me think you don't understand why you are allocating that space. I am hoping I am wrong on this, but even if I am, not having an explicit Null entry is at the very least misleading, and poor coding practice (your coding standards are your choice, of course, but I would advise being explicit here).

Every single one of the GDT descriptors has the same set of access settings, 10011010b. Let's break this down, for each of the values you have set, following the wiki page on the Global Descriptor Table and the Intel manuals (also, note that some of the names in the wiki differ from those in the manuals), with the bits numbered for the Access Byte (rather than for the descriptor as a whole):

Bit 7, P: 1 - Present flag. When set, as you have it in your descriptors, it means the segment is present in memory (both in the sense of there being physical memory for it, and that it is paged in).

Bit 4, S: 1 - Segment type (system vs code/data). Set (as it is in this case), this indicates that the segment is either user code or user data. If it is cleared, then it is a system segment, and the Type field is ignored.

Bits 3-0. T: 1010 - the Type field. In the manuals, the next four flags because they are only used if the Segment Type flag is set. The wiki treats them as independent flags, which for most purposes is going to be more meaningful, IMAO.

Bit 3, X: 1 - Executable flag. 1 means it is a Code segment - values in the segment will be treated as executable instructions.

Bit 2, E/C: 0 - Expand/Conform (given as 'Direction/Conformance' in the wiki) flag union. This one is a union field, meaning it means different things depending on whether the segment is code or data. For a code segment, 1 means that the segment can be executed by processes with the same Privilege or lower level as the segment. Since that is Kernel in this case, it means that any process can execute it. For data, setting it to 1 means that the segment 'grows' down in memory; as a rule, only stack segments should be set to grow down.

Bit 1, R/W: 1 - Read/Write flag. As with DC, this is a union flag, with different interpretations for code and data segments. A 1 will mean read access for a code segment (but never write access; code segments are always read-only). For data segments (ones with the E bit cleared), it indicates both Read and Write access.

Bit 0, A: 0 - Accessed flag. This indicates that nothing has actually used the segment yet. As soon as that happened (either by executing code, reading data, or writing data), the flag gets set. This is mostly used when implementing virtual memory systems, IIUC.

(IAN an expert on this, so C&CW.)So, what does all of this mean? It means all of the segments - including those you mean to be system or data - are listed as being "User Code, Present in memory, Readable but not writable, accessible only to kernel code, and not yet accessed." I am pretty sure that the at least some of the segments should be set differently, even though all of these overlap exactly.

Similarly, let's take a look at the Segment Scaling Definition flags (called 'Flags' in the wiki), which are also all the set to the same values in your code (110xxxxx):

Bit 7, G: 1 - Granularity, which decides how the segment limits are scaled. Setting this to 1 indicates that you are using segment limits with a stride of 4 KiB (that is to say, a limit of 0x00001 would be a 4 KiB segment; a limit if 0x00004 would be a 16 KiB segment, etc.).

Bits 6, D/B: 1 - Default Argument Size/Big Segment flag. For code, when set (as it is here), this indicates that the default sizes for instruction operands and addresses are either 32-bit or 8-bit, while when cleared it means they are 16-bit or 8-bit. For data segments, it indicates whether it is a 64KiB maximum size segment, or a 4 GiB maximum size segment.

Bit 5, L: 0 - Long mode flag. Since it is cleared, it means you are in 32-bit protected mode, instead of 64-bit protected mode. When set, this further modifies the meanings of the G and D/B flags to indicate 64-bit values where it would have been 32-bit ones. The Intel manuals seem to indicate that this can in fact still be set even if the system as a whole is in 32-bit mode, but I am pretty sure things get weird if you do that... If anyone can add more on this, feel free.

Since you are also setting all the segments to long strides, and all of them have a base of 0x00000000 and a limit of 0xFFFFF, this would mean that all of the segments are 4 GiB in total size, and all overlap each other exactly.

The implications of these facts are left as an exercise for the reader.

_________________Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTFμή εἶναι βασιλικήν ἀτραπόν ἐπί γεωμετρίανLisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.

Last edited by Schol-R-LEA on Mon Jun 18, 2018 8:00 am, edited 4 times in total.

Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Thu Jun 14, 2018 7:21 pm

Member

Joined: Fri Oct 27, 2006 9:42 amPosts: 1603Location: Athens, GA, USA

simeonz wrote:

db 00100000b, should be db 00010000b or db 00000000b. You are setting the long mode flag for the TSS, but it needs to be always cleared (even for 64-bit TSS)

OK, that would probably do it. There may be other issues as well, but that alone would be a showstopper.

I will add something that occurred to me earlier: the OP would do well to look at the macro and structure-definition features of their assembler (I am guessing it is NASM, but I am not sure) as a way of simplifying these declarations, both for avoiding repetition (by using the default initializations, if it allows them) and for making the setting process more meaningful by giving names to the fields and so forth.

You could, for example, have a struct definition like this NASM version:

Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Fri Jun 15, 2018 3:20 am

Member

Joined: Fri Aug 19, 2016 10:28 pmPosts: 360

Schol-R-LEA wrote:

I will add something that occurred to me earlier: the OP would do well to look at the macro and structure-definition features of their assembler (I am guessing it is NASM, but I am not sure) as a way of simplifying these declarations, both for avoiding repetition (by using the default initializations, if it allows them) and for making the setting process more meaningful by giving names to the fields and so forth.

True. I was thinking the same thing while counting the bit positions. The error may not have occurred, assuming this is the error, if the data was more "literate". Since I am not myself an avid assembler enthusiast, I am curious. Macros are somewhat less expressive then structures/records, so may we assume that the advice is to use NASM (for portability sake)? Or is using gas with a stronger preprocessor (like m4) a better option? Or NASM with m4? Or C/C++ with mostly inline assembler?

Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Fri Jun 15, 2018 10:16 am

Member

Joined: Fri Oct 27, 2006 9:42 amPosts: 1603Location: Athens, GA, USA

BTW, I noticed a mistake in the earlier post, which I've now corrected; I had the last part of base field declared as word, when it should be byte.

simeonz wrote:

Schol-R-LEA wrote:

I will add something that occurred to me earlier: the OP would do well to look at the macro and structure-definition features of their assembler (I am guessing it is NASM, but I am not sure) as a way of simplifying these declarations, both for avoiding repetition (by using the default initializations, if it allows them) and for making the setting process more meaningful by giving names to the fields and so forth.

True. I was thinking the same thing while counting the bit positions. The error may not have occurred, assuming this is the error, if the data was more "literate". Since I am not myself an avid assembler enthusiast, I am curious. Macros are somewhat less expressive then structures/records, so may we assume that the advice is to use NASM (for portability sake)? Or is using gas with a stronger preprocessor (like m4) a better option? Or NASM with m4? Or C/C++ with mostly inline assembler?

I wasn't really trying to make a recommendation, and honestly, I agree that NASM macros are less expressive than NASM structs, or for that matter, macros for other assemblers. I was using NASM because it seems to be the one most often used for x86 OS dev, and I am more familiar with it than, say, FASM.

A stronger macro preprocessor would definitely be a good idea; I've used m4 to good effect in the past myself (though it was an odd situation¹ and I misused it somewhat), and have even tried using the stand-alone version of cpp (it works, but it isn't terribly well suited for assembly work).

I would be hesitant to suggest using C, simply because I don't know what CandyMan's goals and intentions are - if they are planning to use assembly across the board, the recommendation probably wouldn't go over terribly well.

In any case, I wouldn't recommend using inline assembly extensively or exclusively for large amounts of assembly work. However, for the GDT and related system data structures, there's no reason C can't be used directly for a large part of it so long as you remember to set the alignments and packing using the GCC attribute pragma or the equivalent thereof for the given compiler. For GCC, this would be

I am pretty sure that the alignment should be 8 for both 32-bit and 64-bit modes, but if anyone can confirm or refute this I would appreciate it. Also, I separated the declarations of the access and flags_limit_16_19 bytes, rather than lumping all the uint8_t declarations together, in case someone wants to try messing about with bit fields (which can be dicey in general, WRT differences in compilers, but if you are sticking to a specific compiler with a known bit ordering and implementation of the bit accesses, it would make working with the individual fields much easier).

Footnote1. It was for an assembly language course that used MIPS as the target, and SPIM as the emulator. SPIM's built in assembler is severely limited, lacking even the most rudimentary extra features such as structures, macros, or even support for multiple source files, so when I had a large-ish final project - a Sudoku solver - I convinced my lab partner to use m4 to make the work easier. It was still a hassle - we had to manually preprocess the macro-fied source file separately into a final source file, which made it a bit annoying to test the code - but it worked well, or would have if I hadn't started overdoing things with the macrology to the point where it started getting more confusing rather than less.

_________________Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTFμή εἶναι βασιλικήν ἀτραπόν ἐπί γεωμετρίανLisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.

Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Mon Jun 18, 2018 8:05 am

Member

Joined: Fri Oct 27, 2006 9:42 amPosts: 1603Location: Athens, GA, USA

Sorry for the sequential posts, but I just want to clarify a point: if my reading of the segment descriptors earlier was correct, and the code isn't changing them or a adding any new ones, then it shouldn't be possible for you to be running anything in ring 3 to begin with - I am pretty sure that a DPL of 00 across the board means those segments are all set to ring 0 only access.

Can anyone confirm or refute this? Have I missed any details on that? As I've mentioned before, this is an area I have not really delved into.

@CandyMan: Could you provide a link to your offsite repo so we can see the whole code if we need to? If you don't have any offsite repo... well, in that case, you don't want do anything else on your OS until you've set one up. Trust me on this; they are too darn useful not to have for even a small project.

_________________Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTFμή εἶναι βασιλικήν ἀτραπόν ἐπί γεωμετρίανLisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.

Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Mon Jun 18, 2018 10:14 am

Member

Joined: Fri Aug 19, 2016 10:28 pmPosts: 360

Schol-R-LEA wrote:

I am pretty sure that a DPL of 00 across the board means those segments are all set to ring 0 only access.

I actually see a lot of DPL 3 segment descriptors. Also, your breakdown of the bits was correct, but if you go to Code643, the flags are 11111010b, which means present, DPL = 11b, etc. I am not sure what the 64-bit data segment descriptors are for, but may be unused.

Post subject: Re: [x64] can not switch from ring3 to ring0 by call gate

Posted: Mon Jun 18, 2018 10:29 am

Member

Joined: Fri Oct 27, 2006 9:42 amPosts: 1603Location: Athens, GA, USA

simeonz wrote:

Schol-R-LEA wrote:

I am pretty sure that a DPL of 00 across the board means those segments are all set to ring 0 only access.

I actually see a lot of DPL 3 segment descriptors. Also, your breakdown of the bits was correct, but if you go to Code643, the flags are 11111010b, which means present, DPL = 11b, etc. I am not sure what the 64-bit data segment descriptors are for, but may be unused.

facepalm I could have sworn I had checked all of them and that they were all identical. .. that was careless of me.

_________________Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTFμή εἶναι βασιλικήν ἀτραπόν ἐπί γεωμετρίανLisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.

Who is online

Users browsing this forum: No registered users and 2 guests

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum