Friday, November 29, 2013

Rolling Your Own Operating System

I started an extremely ambitious project a while back -- working on my own operating system. This isn't one that I ever intend to gain any ground or even boot on real hardware. (I'm targeting VirtualBox, VirtualPC/HyperV, and Bochs emulator environments right now.) It's just a place for me to play around with OS concepts. At work I write driver code for windows, but this code relies on a system of interfaces and mechanisms that have been designed and implemented by people much smarter than I. I'd like to start working on those more basic concepts. Sort of like peeling back the layers of an onion by recreating it. I anticipate I will learn much not only about how my OS works and can work, but about the tradeoffs that most OS designers have encountered along the way.

My current "OS" is very limited. It uses the standard DOS (well, Windows 7 -- because there are differences...) bootloader from the partition table. I created my own volume boot record to receive control from there. I'm able to load from the beginning of the partition. I'm still working on the loader. This is not be choice, but actually some unexpected first bit of learning. Here's the deal...

The BIOS loads and calls into the MBR code. The MBR code loads and calls into my volume boot record code. Now the fun starts. There are numerous extremely important interfaces the BIOS provides to early boot code. Before protected mode, paging, multi-tasking, and all the other things that really make an operating system an operating system you could just happily keep calling these BIOS interfaces to get this functionality. However, the BIOS code is legacy... It's so legacy that it isn't all that compatible with the way a modern OS sets up the CPU. (There are a few more compatible extensions -- VESA graphics BIOS stuff for example, but even this is severely limited)

So, it's already decision time. A lot sooner than I thought it would be. How do I start laying the ground work for the more modern features of my OS when I need to use the BIOS routines to talk to the hardware? Well, the short answer is I need to stop at some point, and probably the sooner the better. I don't want to litter my kernel code with a bunch of BIOS dependencies -- anything I write that requires this cannot really be reused anywhere else. Ideally, I would switch over from BIOS interfaces to my own all at once, however that will be difficult. Instead, I've decided a better approach is to rely on the BIOS interfaces until I'm ready to create my own. So, what does the BIOS give me that I need to reproduce?

  1. VGA/VESA support -- in order to know my OS is doing something, I should probably have a way to see it working. 1
  2. Keyboard support.
  3. Disk IO support.
  4. ...

I'm sure there are many more but these are the interfaces I'm immediately aware of. The first item, VGA support was easiest. This is probably because it's also the only one that's moved past legacy support. I am able to select a video mode which allows me to draw 24/32 bit RGB directly into a flat buffer space. This buffer space is directly mapped to the screen buffer, so I'm set as far as rudimentary drawing is concerned. I don't get any hardware acceleration and I'm stuck having to implement all my own line drawing, bit-blitting, and other APIs, but at least it's doable. The most annoying thing about this is I cannot adjust for changing the monitor resolution, but if I look at my above goals of not really caring if this runs on real hardware this is acceptable.

Now comes disk IO. This is a bit sticky, since there is no easy way to go about this. I'm probably going to have to support at least IDE and AHCI. I'm not exactly sure if I can get away with IDE only and rely on hard disks to support the legacy commands... I think so, but not sure. I also cannot keep thunking back and forth between BIOS code for this support -- every time I do that I'll probably have to tear down the things I really wanted to learn about -- memory paging, scheduling, etc. I see some work on a storage driver in my near future... It does indeed look like I can just go with IDE. Hopefully, IDE will be pretty easy to work with. It looks like the specs are open as well -- http://www.t10.org/t13/technical/d98120r0.pdf

Here's what my volume boot record code looks like so far: http://www.darkautomata.com/blog/os/vbr_2013_11_29.asm.txt


1 I can just use a COM port interface for logging what is actually happening in the OS, but there is going to come a time relatively early in the OS where I'll actually want to SEE what I've created. Maybe this isn't as big of a requirement as I think it is right now...

Monday, July 23, 2012

Screen DPI vs Remoting issues

It seems to me the user experience for high DPI displays like the Apple Retina display will be horrible for certain scenarios.

If I'm remoting into a windows machine using Remote Desktop or displaying an X application using X11 remoting then the host DPI settings are probably going to result in a bad experience. I already see this in when using my work laptop to remote into my desktop.

At work I have multiple 24" 1920x1080 monitors. When I RDP into the machine from my 14" 1920x1080 work laptop I get text in the code editor that is barely legible. I can't imagine how bad this would be on the Retina display!

The Remote Desktop and other remote display clients should enable a pass-through mechanism for client DPI. This way the host can probably format the display for remoting.

To expand on this idea, if all UI settings relating to size were stored in a device independent way (for example: twips, 1/1440 of an inch) then a translation could take place to always match the physical dimensions of the UI. You could even have a constant scaling factor so you can change this for a device. For example, I may not want my cell phone displaying at full physical dimensions, maybe it's okay to go with 50% or 25% and I know I have to squint at it, but I want my laptop or tablet to use 80-90% physical dimensions, etc...

If no one has thought of this yet well it's here first... prior art recorded.

Thursday, February 23, 2012

Native or Managed?

I want to start a project with a friend to upgrade the UI of windbg. It's not too bad right now, but more importantly I think it could be so much more.

So here's the problem: Making an "awesome" UI is much easier in C# than in C++ using native code. However, the APIs for the debugging engine are all COM based and there aren't bindings for C#. There are a few unofficial bindings, but nothing really available. So, we'd probably have to roll our own bindings, which is not something I want to do.

I think a hybrid option would be to use C++/CLI. At least we'd be able to use the COM methods pretty easily and maybe even use the header files without any modification. We could also then use the managed API for the GUI. I think I'm going to suggest this as the route to take.

There's some concerns that .Net wouldn't be installed on all the environments you'd want to use the new UI but I don't think that should stop us. It's pretty easy to install and a version of .Net ships with modern Windows distributions already.

A bit to think about, but I think the C++/CLI approach is going to be the way to go. It will get me writing code faster and having SOMETHING working much faster than a native solution. I've recently worked on native GDI+ code and although it's nicer than GDI it's still not something I'd want to force myself on for a GUI intensive project.

Monday, January 30, 2012

ERROR_WORKING_SET_QUOTA and IO Completion Ports

While working on a streaming engine I came across an interesting little hole in the MSDN documentation for Completion Ports. Completion Ports allow for extremely efficient throughput of data. The way this is accomplished is by queuing IO to a "Completion Port" and then associating the Completion Port with one or more threads.

The reason this is so fast is because Windows can then chose which thread will complete the IO operation. Using a thread pool allows Windows to always pick the last executed thread in LIFO order. This greatly reduces TLB thrashing and other issues associated with a context switch on the CPU. When the threads aren't processing IO they are in a wait state. The IO is processed in a FIFO order.

The usual Completion Port architecture looks something like this:
  1. Create a Completion Port using CreateIoCompletionPort.
  2. Create the threads for the thread pool and call GetQueuedCompletionStatus to associate the threads with the Completion Port.
  3. Associate file HANDLEs (opened in Overlapped IO mode) to the Completion Port.
  4. Issue IO operations using ReadFile/WriteFile.
  5. Process the IO operations in the thread pool threads.


When any IO operation completes Windows will smartly choose a thread waiting on GetQueuedCompletionStatus to wake up and send the IO result. The call to GetQueueCompletionStatus will return and data processing can begin. Ideally, an application would probably only have one Completion Port and perform all IO processing on this port/thread pool pair.

Everything about this is awesome, except... The documentation is really vague about how to handle ReadFile/WriteFile operations returning success (and thus not being queued). You need to make sure you call GetOverlappedResult (and probably with the Wait parameter set to FALSE) or you will start getting strange errors.

After a few of these immediate IO completions my streaming engine started hitting ReadFile failures described by "ERROR_WORKING_SET_QUOTA." Nowhere in the documentation for Completion Ports or GetOverlappedResult does it indicate this should be called in the Completion Port case. I suppose it's implied by the fact that you're using Overlapped IO, but still an explicit indication on MSDN would probably be useful.

This may be obvious to some but I wasted about an hour on this, so hopefully this post will shorten that time for someone else.

Thursday, September 22, 2011

"R6025 - pure virtual function call" Uh oh...

A while ago I stumbled upon a "purecall" crash. The actual fix is pretty boring, but it is interesting to think about why pure-call crashes can happen at all. Since I like looking at disassembly and seeing what the compiler actually does with my code I'll take this approach here.

I recently spoke with a smart friend who's going through the process of learning C++ coding at a university. We started talking about abstract classes and one thing led to another until I brought up pure-call exceptions. More discussion ensued and I posed the question, "How can you actually cause a pure-call exception?"

If you think about it a bit, this should be impossible. There's no way to instantiate an abstract class. The compiler just won't let you. For any real subclasses of the abstract class the compiler will fill in an appropriate function pointer table. So what's the deal? How does this happen?

First, let's have a look at the memory structure of a typical C++ object containing virtual functions.

0:000:x86> ?? tmp
class BaseReal * 0x004a49a0
   +0x000 __VFN_table      : 0x01312110
   +0x004 m_data           : 0x1337beef

0:000:x86> dps 0x01312110 L5
01312110  013110c0 cppstuff!BaseReal::`scalar deleting destructor'
01312114  01311120 cppstuff!BaseReal::Get 
01312118  01311140 cppstuff!BaseReal::Sum 
0131211c  00000000
01312120  00000048


Here's an object tmp which contains a virtual function table pointer __VFN_table and then a single data element called m_data. I can see from dumping pointer-sized chunks of __VFN_table with symbol matching turned on that it's actually got the function pointers for the class called BaseReal.

This corresponds to the following code listing:

#include <stdio.h>
#include <stdlib.h>

class BaseAbstract
{
public:
    BaseAbstract ();
    virtual ~BaseAbstract ();
    
    virtual unsigned int Get () = 0;
    virtual unsigned int Sum ();
};

class BaseReal : public BaseAbstract
{
public:
    BaseReal ();
    virtual ~BaseReal ();
    
    virtual unsigned int Get ();
    virtual unsigned int Sum ();
    
    unsigned int m_data;
};

BaseAbstract::BaseAbstract ()
{
    //Sum ();  // BOOM.
}

BaseAbstract::~BaseAbstract ()
{
}

unsigned int BaseAbstract::Sum ()
{
    return Get () + 0;
}

BaseReal::BaseReal () : BaseAbstract ()
{
    m_data = 0x1337BEEF;
}

BaseReal::~BaseReal ()
{
    m_data = 0xDEADBEEF;
}

unsigned int BaseReal::Get ()
{
    return m_data;
}

unsigned int BaseReal::Sum ()
{
    return Get () + m_data;
}

int main (int argc, char* argv[])
{
    BaseReal *tmp = new BaseReal ();
    
    unsigned int value = tmp->Sum ();   // <<--- break point here.

    delete tmp;
    return 0;
}


If I uncomment the "BOOM" line and run again the application will crash before it gets to the break point. The interesting part is what happens before the crash. Let's have a look at the constructors disassembly. First, the BaseReal constructor:

0:000:x86> uf cppstuff!BaseReal::BaseReal
cppstuff!BaseReal::BaseReal :

   // Function prologue...
   41 00df1090 55              push    ebp
   41 00df1091 8bec            mov     ebp,esp

   // Setting up the "this" pointer (ecx usually contains 'this') and then
   // calling the BaseAbstract constructor.
   41 00df1093 51              push    ecx
   41 00df1094 894dfc          mov     dword ptr [ebp-4],ecx
   41 00df1097 8b4dfc          mov     ecx,dword ptr [ebp-4]
   41 00df109a e861ffffff      call    cppstuff!BaseAbstract::BaseAbstract (00df1000)

   // Loading eax with pointer to 'this' and then storing the virtual function
   // table for BaseReal (cppstuff!BaseReal::`vftable' (00df2120)
   41 00df109f 8b45fc          mov     eax,dword ptr [ebp-4]
   41 00df10a2 c7002021df00    mov     dword ptr [eax],
            offset cppstuff!BaseReal::`vftable' (00df2120)

   // Saving 0x1337BEEF to m_data.
   42 00df10a8 8b4dfc          mov     ecx,dword ptr [ebp-4]
   42 00df10ab c74104efbe3713  mov     dword ptr [ecx+4],1337BEEFh

   // Function epilogue...
   43 00df10b2 8b45fc          mov     eax,dword ptr [ebp-4]
   43 00df10b5 8be5            mov     esp,ebp
   43 00df10b7 5d              pop     ebp
   43 00df10b8 c3              ret


// Dumping the function table...
0:000:x86> dps cppstuff!BaseReal::`vftable' L3
00df2120  00df10c0 cppstuff!BaseReal::`scalar deleting destructor'
00df2124  00df1120 cppstuff!BaseReal::Get 
00df2128  00df1140 cppstuff!BaseReal::Sum 


This looks pretty reasonable. First there's the function prologue and then we do some C++ "this pointer" setup to make all that work. After that we immediately jump into the constructor for BaseAbstract. Once that work is done the m_data member is initialized. And now a look at the BaseAbstract constructor:

0:000:x86> uf cppstuff!BaseAbstract::BaseAbstract
cppstuff!BaseAbstract::BaseAbstract :

   // Function prologue...
   27 00df1000 55              push    ebp
   27 00df1001 8bec            mov     ebp,esp

   // Setting up the "this" pointer (ecx usually contains 'this') and 
   // saving it on the stack as a local in preparation for calling
   // the "Sum" function.
   27 00df1003 51              push    ecx
   27 00df1004 894dfc          mov     dword ptr [ebp-4],ecx

   // Loading eax with pointer to 'this' and then storing the virtual function
   // table for BaseReal (cppstuff!BaseAbstract::`vftable' (00df2110)
   27 00df1007 8b45fc          mov     eax,dword ptr [ebp-4]
   27 00df100a c7001021df00    mov     dword ptr [eax],offset 
            cppstuff!BaseAbstract::`vftable' (00df2110)
   28 00df1010 8b4dfc          mov     ecx,dword ptr [ebp-4]

   // Calling Sum -- which will fail.
   28 00df1013 e858000000      call    cppstuff!BaseAbstract::Sum (00df1070)

   // Function epilogue...
   29 00df1018 8b45fc          mov     eax,dword ptr [ebp-4]
   29 00df101b 8be5            mov     esp,ebp
   29 00df101d 5d              pop     ebp
   29 00df101e c3              ret


// Dumping the function table...
0:000:x86> dps cppstuff!BaseAbstract::`vftable' L3
00df2110  00df1020 cppstuff!BaseAbstract::`scalar deleting destructor'
00df2114  00df1224 cppstuff!purecall
00df2118  00df1070 cppstuff!BaseAbstract::Sum 


// Disassembly for BaseAbstract::Sum -- called in the constructor.
0:000:x86> uf cppstuff!BaseAbstract::Sum
cppstuff!BaseAbstract::Sum :

   // Prologue...
   36 00df1070 55              push    ebp
   36 00df1071 8bec            mov     ebp,esp

   // Saving ecx.  This is somewhat important.  Note: the "this call"
   // calling convention requires the "this pointer" to be in ecx.  The code
   // is using ebp-4 to stash the "this" pointer.
   36 00df1073 51              push    ecx

   // Copying "this" (ecx) to local storage -- anything with negative
   // ebp references is a local / spill location.
   36 00df1074 894dfc          mov     dword ptr [ebp-4],ecx

   // Load the address of the function table into eax.
   37 00df1077 8b45fc          mov     eax,dword ptr [ebp-4]

   // Dereference eax into edx -- now we have the function table.
   37 00df107a 8b10            mov     edx,dword ptr [eax]

   // Set ecx to "this" for the call, per calling convention.
   37 00df107c 8b4dfc          mov     ecx,dword ptr [ebp-4]

   // Deference the 2nd function table entry (the one for "Get").
   37 00df107f 8b4204          mov     eax,dword ptr [edx+4]
   37 00df1082 ffd0            call    eax

   // Epilogue...
   38 00df1084 8be5            mov     esp,ebp
   38 00df1086 5d              pop     ebp
   38 00df1087 c3              ret


There's the same "this" pointer initialization (although this was probably already done, these constructors have to work in a vacuum, so they may duplicate a little work). Next, the setup of the virtual function table and then the call to Sum. I think the compiler took a nice optimization here and didn't use the virtual function table to get the address of Sum. If this were code anywhere other than the constructor I imagine it would have used the function table pointer instead.

So now this brings us to the Sum code, which I also dumped. You can see it dereferences the virtual function table for the Get function call and then calls it. The problem is this is a pure virtual function so the table entry is for cppstuff!purecall; which is a function added by the compiler as a placeholder to indicate failure.

What are the lessons learned? You should never call virtual functions (or functions that call virtual functions) in the constructor or destructor. I didn't show the destructor code, but the whole process of loading the proper function table pointer and setting it is reversed.

Clear as mud?

Wednesday, September 14, 2011

Header Files and Named Parameters

If you're as much of a geek as I am you may hang out in IRC channels chatting it up with your other coder friends. Occasionally, you may also get into a heated debate about one topic or another.

About a week ago I got into just such a debate (and the geekness of it is a little embarrassing). We were debating whether you should name your parameters in header files or not. As a "software engineer" it's my belief that you should make the actual act of coding as easy as possible. There are enough ways to shoot yourself in the foot and it's always best to minimize complexity and leave that intellectual horsepower to the actual problem at hand.

I find that most seasoned "software engineers" feel this way as well. I've run into a few "programming is just hard" coders, but I believe a real "engineer" would care a little more about their users and also the interesting and unexpected problems that can arise from using other software packages. Given all this, of course I was on the "please, for the love of God, name those parameters" side of the discussion. Well, turns out I was wrong. My decision to include named parameters can actually make programming a LOT harder. Here's why:

Suppose I have 3 files, defined here:

// lib.h
#ifndef __LIB_H__
#define __LIB_H__

long sum (long int_arg, long int_arg2);

#endif
// lib.c
#include "lib.h"

long sum (long int_arg, long int_arg2)
{
    return (int_arg + int_arg2);
}
// main.c
#include <stdio.h>
#include <stdlib.h>
#include "lib.h"

int main (int argc, char *argv[])
{
    fprintf (stdout, "%d\n", sum (10, 10));
    return 0;
}


This all seems like it's going to work perfectly. If I compile and run this, I get "20" for my output. Everything is perfect.

Now then, suppose someone changes my main.c like so:

// main.c
#define int_arg long

#include <stdio.h>
#include <stdlib.h>
#include "lib.h"

int main (int argc, char *argv[])
{
    fprintf (stdout, "%d\n", sum (10, 10));
    return 0;
}


Now my output is the most unexpected: "10"

I know you may wonder why exactly someone would define int_arg to be something like long and I agree this is somewhat contrived. But, it can happen. This was the simplest plausible case I could come up with. Perhaps the coder is using a library that allows you to change the certain behavior by defining what an "int_arg" should be. Perhaps the library is using the "int_arg" type to indicate what type of integers it should read from a file. I know all of this is a stretch, but the important thing to remember is that this can happen. As all good engineers know, if it can happen it most likely will happen eventually.

Hmmm, so what happened here? Let's have a look at some disassembly to see.

// outputs "20"
_main:
  004113E0: 55                 push        ebp
  004113E1: 8B EC              mov         ebp,esp
  004113E3: 81 EC C0 00 00 00  sub         esp,0C0h
  004113E9: 53                 push        ebx
  004113EA: 56                 push        esi
  004113EB: 57                 push        edi
  004113EC: 8D BD 40 FF FF FF  lea         edi,[ebp-0C0h]
  004113F2: B9 30 00 00 00     mov         ecx,30h
  004113F7: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  004113FC: F3 AB              rep stos    dword ptr es:[edi]
  004113FE: 6A 0A              push        0Ah
  00411400: 6A 0A              push        0Ah
  00411402: E8 EE FC FF FF     call        @ILT+240(_sum)
  00411407: 83 C4 08           add         esp,8
  0041140A: 8B F4              mov         esi,esp

// outputs "10"
_main:
  004113E0: 55                 push        ebp
  004113E1: 8B EC              mov         ebp,esp
  004113E3: 81 EC C0 00 00 00  sub         esp,0C0h
  004113E9: 53                 push        ebx
  004113EA: 56                 push        esi
  004113EB: 57                 push        edi
  004113EC: 8D BD 40 FF FF FF  lea         edi,[ebp-0C0h]
  004113F2: B9 30 00 00 00     mov         ecx,30h
  004113F7: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  004113FC: F3 AB              rep stos    dword ptr es:[edi]
  004113FE: 6A 0A              push        0Ah
  00411400: 6A 00              push        0
  00411402: 6A 0A              push        0Ah
  00411404: E8 EC FC FF FF     call        @ILT+240(_sum)
  00411409: 83 C4 0C           add         esp,0Ch
  0041140C: 8B F4              mov         esi,esp


Huh, so that's weird. Looks like if int_arg is defined as a long then the compiler seems to think it should pass an extra 32-bits to the "sum" function. You can also see this in the "stack cleanup" code directly after the function call -- there's an extra 4 bytes being pulled off the stack. The extra 4 bytes are coming from that extra push onto the stack before the function call. So why is this?

Well, the compiler is converting long int_arg to be long long and then assuming it's an unnamed parameter. Later on, the actual implementation doesn't have the define and is compiled as expecting a long, not a long long. Well, that's odd, but you may ask, "It's going to throw some linker warnings, right?"

Well, no. The reason this whole thing compiles and links without warnings is because these are C functions. There is no name decoration. The linker is simply looking for a function called sum that it can use. It finds one and no linker-time type-checking is performed. The only reason C++ has any sort of name declaration is because functions can be overloaded with different parameter types. In C++ this would be a linker error but in C it's just a horrible horrible bug.

So there's a case where I was advocating for making things easier only to possibly introduce some really hard-to-find bugs. How would I fix this? I'd add some nice comments to these functions in the header file, which is a common convention. Additionally, I know understand why this convention is used.

// lib.h
#ifndef __LIB_H__
#define __LIB_H__

long sum (long /* int_arg */, long /* int_arg2 */);

#endif


Thanks!

Friday, September 2, 2011

WinDBG microtip

I constantly find myself reading variables and structures the hard way in WinDBG... Why don't I ever just use the "??" command!?