Sunday, June 08, 2008

Reversing the U-Boot's kermit protocol

See http://code.google.com/p/omap-u-boot-utils/ for Code and binary details.
Background:

Few days back, I had written a app which came from a need to be able to script download operations to U-Boot V2. Now, that I have a bit of time on hand.. here is the detail for supporting an app for kermit for U-Boot. Of course, you are free to read ckermit code, or pay up some well deserved money for kermit95.. but there is something called as gkermit(also from columbia univ) which is GPLed. There is also something called as ekermit which is kinda neat.. You can find Loadb here. For me personally, drawing the sequence of code as implemented by loadb was enough and did not require looking at the massive code base of gkermit.

Details:

Kermit protocol in it's simplest form is described as a transmission followed by a end of transmission character. Each transmission consists of multiple packets. Each packet can be a session initiation, data packet or many other packet types. The app I wrote supports only sending data packets. So, it is not complete on it's own, but this is fine, since U-Boot cares only for data packets.

Every packet is an individual entity of it's own. Every packet has a start and end packet marker. Data packets come in two flavors - large packets and small packets. Small packets are simple, but large packets is better for performance reasons, but, it depends on the kind of link you got.. if it is a pretty reliable link, you'd like to send large chunks of data and then retry them all over again... Data packets have their own header. Following the header, there is a bunch of data bytes which end with a CRC and an end character.

The control characters in the middle of data bytes could create confusing state for kermit protocol, hence the control characters are specially encoded by the protocol. encoding is simple: An escape character(#) is prefixed to characters which are specially encoded. The encoding is an XOR with 0x40.

If the reciever gets the packet properly, it acknowledges the receipt of packet. If a NAK (negative acknowledgment) or the recieved ack packet itself is corrupted, the transmitter retries the old packet.

The packet sequence number allows for the sender and receiver to keep track of the packet flow sequence.
Examples of packets:
Large packet:
unsigned char start;
unsigned char length_normal; /* =0 if the large packet is used */
unsigned char sequence_number;
unsigned char packet_type;
unsigned char length_hi;
unsigned char length_lo;
unsigned char header_checksum;
unsigned char data_bytes[length];
unsigned char CRC;
unsigned char end;
Small packet:
unsigned char start;
unsigned char length_normal;
unsigned char sequence_number;
unsigned char packet_type;
unsigned char data_bytes[length];
unsigned char CRC;
unsigned char end;
Ack/Nak type packet:
unsigned char start;
unsigned char length_norm;
unsigned char sequence_number;
unsigned char packet_type;
unsigned char checksum;
unsigned char end;
Conclusion:
Of course kermit adds up extra bunch of bytes to the data stream.. but it is application independent, allows retries and in general pretty darn reliable protocol. Now, if you are using something like a serial cable, you better have something like kermit protecting your data, but in the case of USB and the like, where the protocol layer itself does the error correction, you probably dont need this.

No comments: