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 variabler
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