Jack Li's Blog

Write a simple shell

We write a shell that can run the following commands:

  • help
  • hello
  • timestamp
  • reboot
typedef struct cmd_struct {
  char* name;
  char* msg;
  int (*func)(char**);
} cmd_struct;

int help(char**);
int hello(char**);
int timestamp(char**);
int reboot(char**);

// TODO: argv for each commands
char* argv[10];

cmd_struct cmds[NCOMMANDS] = {
  {
    .name = "help",
    .msg = "print available commands",
    .func = help
  },
  {
    .name = "hello",
    .msg = "print hello world",
    .func = hello
  },
  {
    .name = "timestamp",
    .msg = "get current timestamp",
    .func = timestamp
  },
  {
    .name = "reboot",
    .msg = "restart the computer",
    .func = reboot
  }
};

int
execute(char* cmd)
{
  for(int i=0; i < NCOMMANDS; i++){
    if(strncmp(cmd, cmds[i].name, 100) == 0){
      return cmds[i].func(argv);
    }
  }
  printk("sh.c: unknown command");
  return -1;
}

void
runsh(void)
{
  uartflush();
  static char buf[100];

  while(1){
    printk("$ ");
    
    /* Read command */
    int i = 0;
    char c;
    memset(buf, 0, sizeof(buf));
    buf[0] = '\0';

    while((c = uartgetc()) != '\n'){
      buf[i++] = c;
      uartputc(c);
    }
    buf[i] = '\0';
    uartputc('\n');
    /* Execute command */
    execute(buf);
    uartputc('\n');
  }
}

Get timestamp #

timestamp is calculated by timer counter divides timer frequency. These two value are stored in registers cntfrq_el0 and cntpct_el0.

int
timestamp(char**)
{
  uint64 cnt = r_cntpct_el0();
  uint64 freq = r_cntfrq_el0();

  printk("cntpct_el0=%d, cntfrq_el0=%d\n", cnt, freq);
  printk("%d", cnt/freq);
  return 0;
}

I put assembly code at arm64.h. The arm docs said that we can use mrs %0 cntfrq_elo to read the register.

  • =r means write register result to variable
  • r means write variable to register
static inline uint64
r_cntfrq_el0()
{
  uint64 x;
  asm volatile("mrs %0, cntfrq_el0": "=r" (x) );
  return x;
}

static inline uint64
r_cntpct_el0()
{
  uint64 x;
  asm volatile("mrs %0, cntpct_el0": "=r" (x) );
  return x;
}

Finally

$ make qemu

$ timestamp
cntpct_el0=462388643, cntfrq_el0=62500000
7