Thursday, December 27, 2007

Ramshankar's Realm

I'm moving my blog postings from here to my new blog here.

See you there! That means bookmark and link me! :D

Saturday, November 10, 2007

Hotel California... Or?

CHECK THIS

Credits to the original poster, that was truly hilarious!!!!
:)

Monday, November 05, 2007

Injecting debug symbols into a release binary

There is a nice way (using GNU binutils) to inject debug symbols into a release binary. This isn't an unknown trick but certainly not widely known even among most Linux/Unix programmers. Of course you cannot magically just add debugging symbols into a release build, you first need to have the appropriate debug binary built.

Here are the basic steps:

  1. Make the debug build as always.

  2. Run objcopy with --only-keep-debug to create a debug symbols file.

Then when your customer complains of a bug, (assuming he has only the release build), the steps for you to inject symbols back into his release binary:
  1. Run objcopy with --add-gnu-debuglink=[debugfilename]

And that's it! You now have a debug ready binary. Just run it and duplicate the customer's problem. Here's an example (assume the binary is called myProg):
$objcopy --only-keep-debug myProg myProg.debug

To inject the symbols back:
$objcopy --add-gnu-debuglink=myProj.debug myProg

Of course, there is a problem with what I said. I mean "myProg" still has the debug symbols after the first objcopy command. So to strip the debug symbols from myProg, do:
$objcopy --strip-debug myProg

So now your sequence would be:
  1. Copy debug symbols to .debug file

  2. Strip debug binary to produce stripped binary.

  3. When you need to debug, add-gnu-debuglink the .debug file back to the stripped binary.

Hint:
On OpenSolaris you can find GNU binutils under /usr/sfw/bin. And "objcopy" is called "gobjcopy".

Sunday, October 28, 2007

Solaris Nevada build 75

I had to upgrade to Solaris Nevada build 75. I was previously using build 64, which was using a terribly old Gnome and missed some libraries that you would normally expect on a *nix system (xCursor etc.)

Build 75 also comes with the latest Gnome offering 2.20.0 with a much awaited Pidgin. No more crappy Gaim!! With updated GIMP, gedit among several others. As far as the OS goes, it has new libs, has xVM (the hypervisor) all included in nevada itself. It also has the necessary Crossbow components, revised drivers and a revised installer as well.

The new GUI installer looked far better than the crappy CDE one, but just as my luck would have the new installer kept crashing and I had to use the old GUI one. I couldn't perform an upgrade of my earlier build either as the installer (after about 30 agonizing minutes) told me I have not the required disk space. So had to perform a clean re-install, and restored various stuff from local zip backups.

Anyway, so far so good. This has been a good update from Sun.

Wednesday, October 24, 2007

Kernel programming is fun

My sockets bound,
but no bytes found,
my threads all spawn,
but deadlocks live on,
my program compiles,
my program even links,
but the output stinks,
memory pages are locked
but they still get swapped,
core dump, kernel panics,
do I still prefer *nix?,
corrupted is my code,
rebooting in safe mode,
cannot stand another crash,
perhaps time for the trash,
giving one last attempt,
alives comes the ethernet!
my sockets are fillling!
deadlocks all fleeing,
magic numbers equate,
the output is now perfect,
what a stroke of luck,
luck mixed with efforts,
fixed the socket ports,
gone are all the frowns,
life is full of ups and downs,
take it as it comes.

Blah blah... Now. Back to fixing the stubborn driver that won't unload.

Monday, October 08, 2007

Workspace Juggling

Here's my workspace juggling feat...

  1. Chat: IRC and IM
    Since I'm running gaim, it luckily integrates my IRC and Instant Messenger into a single window (with tabs of course), so just a couple of windows to deal with here.

  2. Code: Main code space
    This is my main coding workspace. It contains several terminals (compiling, tests, root terminal etc.) and gedit my main code editor. Again thanks to gedit being tabbed it reduces hanging windows a lot.

  3. Example code space
    This would be my reference workspace where I read example code. Usually has some nautilus windows and a gedit window.

  4. Reference PDFs
    This workspace is dedicated for viewing technical White papers, such as device White papers, Virtualization and possibly even books related to my work. Typically again a nautilus window and the PDF viewer.

  5. Build files
    Now I would normally never use a seperate workspace for editing build-files and makefiles. But we have got our own build-system with our own build-files that are considerably more complex than traditional ones. I also need to alter and keep track of them carefully at times and I prefer to not mix them with the rest of my code windows. Juggling workspace is tight enough, juggling within a workspace must be avoided as much as possible :)

  6. Internet
    Firefox. Has the browser and download window. Here I mainly browse, checking online documentation, look-up man pages online (I find it easier to scroll/read than the damn crappy terminal man-page), visit orkut and so on.

  7. Mail
    I download my mails onto Evolution hooked up with Google mail, hence this would have Evolution and its sub-windows like Compose mail, and at times Filters, as I create a huge number of filters for automatic filing of my mail.

  8. System headers
    This workspace is exclusively for looking system headers. But why? I already have the docs? Yes but unfortunately my work requires using a great deal of undocumented calls, and also the docs don't exactly document all things. Like for finding what exact type diskaddr_t, I need to grep through hundreds of headers. So this workspace typically contains gedit with several headers opened, terminal with GNU Grep ready to go.

  9. Miscellaneous
    This usually usually is free. I would use it for anything like music or video playing, or for extra tasks like file-searches, browsing CD/DVDs, USB devices etc.

  10. Games (eliminated... for the moment)
    If this was only a Linux machine, I would have another workspace for games. Because its Solaris and I'm bored to hacking pkg-get commands which keeps whining about ssl-keys and crap each time I try to upgrade my system, and more importantly, due to lack of space, I don't keep games on it.

I could do with another workspace for my code needs as sometimes I am forced to work with over 15 files simultaneously and it sort of defeats the purpose of tabs with so many of them around, I would prefer to split it into 2 different gedit windows ;)

But I can't really ctrl+f12 with my fingers, so I've restricted it to 9-10 workspace. 11th is also a stretch on my fingers. I also use ctrl+left/right for quick switches across a couple of workspaces.

Wednesday, September 12, 2007

Allocating physically contiguous memory in Solaris

Solaris doesn't easily expose an API to allocate on-demand physically contiguous, custom aligned memory. It has no calls like BSD's contigmalloc() or Darwin's IOMallocContiguous(). Here's a code snippet on how to do it:

struct ddi_dma_attr g_SolarisX86PhysMemLimits = 
{


DMA_ATTR_V0, /* Version Number */
(
uint64_t)0, /* lower limit */
(
uint64_t)0xffffffff, /* high limit (32-bit PA, 4G) */

(
uint64_t)0xffffffff, /* counter limit */
(
uint64_t)MMU_PAGESIZE, /* alignment */

(
uint64_t)MMU_PAGESIZE, /* burst size */
(
uint64_t)MMU_PAGESIZE, /* effective DMA size */

(
uint64_t)0xffffffff, /* max DMA xfer size */
(
uint64_t)0xffffffff, /* segment boundary */

1
, /* scatter-gather list length */
1
, /* device granularity */
0
/* bus-specific flags */

};


caddr_t kernVirtAddr;
int
rc = i_ddi_mem_alloc(NULL, &g_SolarisX86PhysMemLimits, sizeInBytes,
1
, 0, NULL, &kernVirtAddr, NULL, NULL);

The prototype of i_ddi_mem_alloc is:
i_ddi_mem_alloc(dev_info_t *dip, ddi_dma_attr_t *attr,
size_t length, int cansleep, int flags,
ddi_device_acc_attr_t *accattrp, caddr_t *kaddrp,
size_t *real_length, ddi_acc_hdl_t *ap)

So obviously, Sun intended this to be called only from within drivers, but we can safely pass NULL to those arguments we don't have as we have done in our above example. Don't forget to pass a non-zero value for cansleep unless you have a need for not allowing the allocation to be a non-waiting one.

The above code allocates physically contiguous, page-aligned memory. This can be used for DMA transfers or for other specific tasks that has these requirements.

The key to getting this to be contiguous is the scatter gather list length member of the structure. Setting this to 1 forces the kernel to allocate 1 physically contiguous block of memory!

The high and low limit in my example maxes out at 4 GB (0xffffffff). If you're building on 64-bit platforms or for Blade servers etc., you can safely go beyond this.

I thought I'd document this little 'trick' if you will as none of this as far as I know is really documented publically, you must dig deep into the bowels of the OpenSolaris kernel to find them. Even the allocation call isn't a documented one, but one that isn't likely to change in the kernel.

For freeing memory allocated you use would use:
i_ddi_mem_free(kernVirtAddr, NULL)


Warning:
For almost all your needs you can probably go through the well document DDI functions of Solaris. Don't use what I've suggested unless you really know what you are doing, and you are in a situation where the exposed DDI functions are not sufficient.

Tuesday, August 21, 2007

Difference between add_drv and modload

After some exploration and googling the Sun forums, I now know the difference between add_drv and modload. The man pages for these 2 commands don't really point out the difference between them. Here's a better picture of their potential usage...

modload:
Loads the module into memory whether it's needed or not. The driver gets initialized but the kernel doesn't call the Attach routine.

This means its initialized and ready for a speedy-attach! For example when plugging a USB device the USB driver requires lesser time if it's already initialized and just needs to attach. This can also be useful for filesystem drivers.

However it must be noted that the kernel module isn't reloaded upon reboot.

add_drv:
Adds the module to the system. The driver gets initialized and also attached. For non-device (pseudo) the driver would initialize and attach immediately. For drivers that attach to a particular device, the system would call modload when necessary (example for USB when the device is plugged in).

The driver remains installed and does not get removed after a reboot.

Sunday, August 19, 2007

Back From Singapore

So after a short one week hectic vacation in Singapore I'm back in Chennai. As for what I did in Singapore, the usual: Bus rides, MRT, visit the zoo, some specific gadget shopping. I haven't had time to copy the pictures to my computer yet. Next, back to work as well.

Tuesday, July 24, 2007

An Example OpenSolaris Device Driver

Writing device drivers under a new Operating System can be a bit of challenge. Especially if its not similar to other OS's you're used to.

Here's a simple skeleton opensolaris pseudo character driver that simple aims to simple publish an entry under /devices/pseudo.

#include <sys/types.h>
#include <sys/param.h>

#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/conf.h>

#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#define DEVICE_NAME "mytdrv"
#define DEVICE_DESC "My Test Driver"

static
int mtd_open (dev_t* pDev, int flag, int otyp, cred_t* pCred);

static
int mtd_close (dev_t pDev, int flag, int otyp, cred_t* pCred);

static
int mtd_read (dev_t dev, struct uio* pUio, cred_t* pCred);

static
int mtd_write (dev_t dev, struct uio* pUio, cred_t* pCred);

static
int mtd_attach (dev_info_t* pDip, ddi_attach_cmd_t cmd);

static
int mtd_detach(dev_info_t* pDip, ddi_detach_cmd_t cmd);
static
int mtd_getinfo (dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result);

static struct
cb_ops mtd_cb_ops =
{

mtd_open,
mtd_close,
nodev, /* b strategy */

nodev, /* b dump */
nodev, /* b print */
mtd_read,
mtd_write,
nodev, /* c ioctl */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
nochpoll, /* c poll */
ddi_prop_op, /* property ops */
NULL, /* streamtab */
D_NEW | D_MP, /* compat. flag */
CB_REV /* revision */

};


static struct
dev_ops mtd_dev_ops =
{

DEVO_REV, /* driver build revision */
0
, /* ref count */

nodev,
nulldev, /* identify */
nulldev, /* probe */
mtd_attach,

mtd_detach,
nodev, /* reset */
&
mtd_cb_ops,
(
struct bus_ops *)0,

nodev /* power */
};


static struct
modldrv md =
{
&
mod_driverops, /* extern from kernel */

DEVICE_DESC,
&
mtd_dev_ops
};


static struct
modlinkage ml =
{

MODREV_1, /* loadable module system revision */

&
md,
NULL /* terminate array of linkage structures */
};


/** Track module instances */

dev_info_t* dip;

int
_init (void)
{

cmn_err(CE_NOTE, "MyDrvSolaris _init");

return
mod_install(&ml);
}


int
_fini (void)
{


cmn_err(CE_NOTE, "MyDrvSolaris _fini");
return
mod_remove (&ml);
}


int
_info (struct modinfo* pModInfo)
{

cmn_err(CE_NOTE, "MyDrvSolaris _info");

return
mod_info (&ml, pModInfo);
}


static
int mtd_open (dev_t *pDev, int flag, int otyp, cred_t* cred)
{


cmn_err(CE_CONT, "MyDrvSolarisOpen");
return
DDI_SUCCESS;
}


static
int mtd_close (dev_t pDev, int flag, int otyp, cred_t* cred)
{


cmn_err(CE_CONT, "MyDrvSolarisClose");
return
DDI_SUCCESS;
}


static
int mtd_read (dev_t dev, struct uio* pUio, cred_t* credp)
{


cmn_err(CE_CONT, "MyDrvSolarisRead");
return
DDI_SUCCESS;
}


static
int mtd_write (dev_t dev, struct uio* pUio, cred_t* credp)
{


cmn_err(CE_CONT, "MyDrvSolarisWrite");
return
DDI_SUCCESS;
}


static
int mtd_attach (dev_info_t* pDip, ddi_attach_cmd_t enmCmd)
{


cmn_err(CE_NOTE, "MyDrvSolarisAttach");

switch
(enmCmd)
{


case
DDI_ATTACH:
{

int
instance = ddi_get_instance (pDip);

dip = pDip;

if
(ddi_create_minor_node(pDip, "0", S_IFCHR,

instance, DDI_PSEUDO, 0) == DDI_SUCCESS)
{

cmn_err(CE_NOTE, "MyDrvSolarisAttach: successful.");

return
DDI_SUCCESS;
}

else

{

/** Is this really necessary? */

ddi_remove_minor_node(pDip, NULL);
}


break
;
}


default
:
return
DDI_FAILURE;
}


return
DDI_FAILURE;
}



static
int mtd_detach (dev_info_t* pDip, ddi_detach_cmd_t enmCmd)
{


cmn_err(CE_CONT, "MyDrvSolarisDetach");
switch
(enmCmd)
{

case
DDI_DETACH:
{


dip = NULL;
ddi_remove_minor_node (pDip, NULL);


return
DDI_SUCCESS;
break
;
}


default
:
return
DDI_FAILURE;
}
}

And don't forget the .conf file for the driver:

#
#
name="mytdrv" parent="pseudo";

Now compile and link it using:
gcc -D_KERNEL -ffreestanding -nodefaultlibs -c mytdrv.c
ld -r -o mytdrv mytdrv.o

Now copy the mytdrv and the mytdrv.conf file into /usr/kernel/drv. Then:
su
cp mytdrv /usr/kernel/drv
cp mytdrv.conf /usr/kernel/drv
add_drv mytdrv

This shouldn't give you any warnings/errors. No output from add_drv means things are fine.
Then load the kernel module using
modload mytdrv

This too shouldn't give you any output normally, which means things are fine. Now check if it has published the entry under /devices/pseudo/mytdrv.
Unload by using the id (first number) found using:
modinfo |grep mytdrv
modunload -i (id)

Then you may remove the driver using rem_drv mytdrv.
rem_drv mytdrv

Remember: For the driver to successfully attach, you'll need a .conf file specifying the name and parent of your driver so that the kernel knows where to attach it!! If you don't you will get an error saying:
devfsadm: driver failed to attach: mytdrv


Happy kernel coding..