A New Simulator, part 3

Download PDF

IOS communication

We’ve left of with the simulator and UNICOS just starting up. I’ve gotten them as far as getting the first packet exchange between the OS and the IO subsystem. However ‘exciting’ that sounds (well, it was for me but I’m not exactly sane as I’m sure you’ve realized already) that’s not exactly the end-all and be-all of the function of an operating system.

My excitement was due to several reasons. Getting this far shows that the mainframe simulation is more or less functional. It also shows that my IO simulator is capable of capturing packets coming from the mainframe. Finally it shows that the bootstrap process is what I expected it to be and even more importantly the parameter file is properly processed. This last one needs a little explanation: how do I know? The number of CPUs are set in the parameter file. Since the kernel only took out one processor from reset, it shows that it used the parameter from the config file.

The next and current task is to start parsing and interpreting the packets that I receive from the mainframe.

So what does the first packet look like? Here’s the hex-dump of the data that arrived:

Not a lot to look at but not nothing either. To interpret the information, I need to know the layout. Luckily I’ve already found the old (X-MP style) and new (Y-MP and newer style) packet layout in the usr/include/sys directory. This layout seems to match what’s in epack.h

So, let’s try to parse this packet! (Note: the machine is little-endean so the byte-order is reversed from x86) The first word is a packet header with some reasonable sequence numbers (0 and 1). I have no idea what the logical path is (hope I won’t need it). Flags are set to 0, whatever that is. The process is set to 2, the cluster is zeroed out. The source is 0, the kernel ID is 1, which makes sense. Then comes the length, which is set to 8. This is one less than the number of words received, but that might be OK: the last word is a repetition of the first one according to the Ep_t struct so this last word might not be accounted for in the packet length. The remaining field is the ‘magic’ and its value is 45, just as the header specifies it.

All looks good, this definitely looks like a valid ‘E’ packet. Now on to the second word: The most important field here is Ep_type, which is set to ‘O’. That – according to the header – is not a valid value. Or is it? It actually falls within PKG_BEG_OUT and PKT_END_OUT, just not listed as one of the known packet types.

Here’s where prior knowledge comes handy. The way COS handled these packets was this: requests from the mainframe used capital letters as request codes, responses from the IOS used the lower-case version of it. So, for example a disk request would go out with request code ‘D’ and the response would come back with code ‘d’. In that context, the packet makes total sense: this is an OWS request (code ‘O’) to which a reply with code ‘o’ have to be sent. This response code (‘o’) is indeed listed in epack.h. In fact, the list only seem to contain response codes.

So far so good, but what’s inside the packet?

It’s lucky that most tools list files in a directory in alphabetical order. Right next to epack.h, there are a set of files, one of which is epacko.h. It contains the ‘o’ packet layout, the one we’re looking at.

This allows us to further crack the structure:

Request code is 7, which is OP_CLOCK and all other fields are set to 0.

This is fantastic! This is how the old COS OS-to-IOS communication was started though the details are different; by synchronizing the clocks between the IOS and the mainframe. This makes me fairly certain that I’m on the right path. So, how to answer to this request?

From what we’ve seen so far in the IOSD code for the X-MP, communication is packetized, the packet requests/responses use the same layout, only swap the upper-case request code to lower-case one.

From epacko.h it’s a bit unclear how big the response should be (op_t is clearly larger than the packet we’ve just received). Another trick I’ve seen in the IOSD code though is that responses often use the same buffer as the reply. They patch things up that need to be changed, fill-in what needs to be filled in, but otherwise keep the buffer the same.

If a similar mechanism is at play in the current implementation, the response should have the same length as the request – why else would they include all the 0 fields?

There are new fields though to consider, especially Ep_seq and Ep_ackseq in the header. It seems the response should acknowledge the reception of the response. My current hypothesis, is that both sides (OS and IOS) keep a running count of messages sent. They increment this number then put it in the Ep_seq field whenever they send a packet. They also send the latest (successfully) received packets’ Ep_seq field into their Ep_ackseq field, letting the other side know how far they’ve gotten in receiving messages. What happened if the received sequence numbers skipped? I don’t know but I don’t really have to worry about it unless I see that happening. And of course for now, I have a sample of 1.

At any rate, this is a simple-enough logic to code up:

Copy the request into the response, increment the internal Ep_seq counter, copy the requests’ Ep_seq into Ep_ackseq, change the message code from ‘O’ to ‘o’ and send it back.

Will it work though? I’ve been told to set the time, so I presume some of the response fields should contain the current time, date or both. But in what format? Is it a UNIX-style epoch? Or some string (like in COS)? Or a tm struct? All of these make some level of sense… One way to figure this out is to put a special value into the response (something that’s easy to search for) and see how it gets parsed and processed by generating an instruction trace. I did this once for COS, and I’m not looking forward for a repeat exercise. And setting the time properly is not terribly high on my priority list, so I decided to just zero out the response and call it good. If the parsing code is unhappy with this, I’ll know soon enough and it’s only then that I’ll have to dig deeper.

After patching up the simulator to generate a response and push it back into the channel, I re-ran the simulation. The request-response went as expected, and I received my next packet:

String! I have strings! Something clearly meant for human consumption. And the packet type is (durm-roll) ‘Z’. Quick, what could that be? epack.h and epackz.h to the rescue: this is a terminal packet. The request code is set to ‘4’ which is ‘ZP_DATA’.

That’s a bit weird! I would have expected a ZP_CON_RQ first… I guess establishing a connection is implicit in sending data to a new terminal. Or maybe the main terminal is always assumed to exist?

Well, that again will have to wait a little, there’s more work to be done: I have to whip up a small terminal packet interpreter that can also generate the proper responses. Talking about which… There’s something curious going on here: this packet contains a second set of sequence and acknowledge number fields (zp_seq and zp_ack). Why do we need a second set? And how are these handled? For now, I’ll assume that these work in a similar way as the packet header ones and see how far that gets me.

With that – and some additional not too interesting bug-fixing – I’ve gotten a terminal open. Next, I had to implement disk read and write commands (I won’t bore you with the details), I was finally greeted by the following display:

There are some bugs as you can see (what is all that garbage?!) but hey, I have UNICOS talking to me!

Let’s finish up this piece right here. Next, I’ll talk about how the file-system worked and why is that important.