aboutsummaryrefslogtreecommitdiff
path: root/machine/htif.c
blob: 18457d632b93f352f5242a69f37a20c3aefeed10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "htif.h"
#include "atomic.h"
#include "mtrap.h"

#include <string.h>

volatile uint64_t tohost __attribute__((section(".htif")));
volatile uint64_t fromhost __attribute__((section(".htif")));
volatile int htif_console_buf;
static spinlock_t htif_lock = SPINLOCK_INIT;

static void __check_fromhost()
{
  uint64_t fh = fromhost;
  if (!fh)
    return;
  fromhost = 0;

  // this should be from the console
  assert(FROMHOST_DEV(fh) == 1);
  switch (FROMHOST_CMD(fh)) {
    case 0:
      htif_console_buf = 1 + (uint8_t)FROMHOST_DATA(fh);
      break;
    case 1:
      break;
    default:
      assert(0);
  }
}

static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
  while (tohost)
    __check_fromhost();
  tohost = TOHOST_CMD(dev, cmd, data);
}

int htif_console_getchar()
{
  spinlock_lock(&htif_lock);
    __check_fromhost();
    int ch = htif_console_buf;
    if (ch >= 0) {
      htif_console_buf = -1;
      __set_tohost(1, 0, 0);
    }
  spinlock_unlock(&htif_lock);

  return ch - 1;
}

static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
  spinlock_lock(&htif_lock);
    __set_tohost(dev, cmd, data);

    while (1) {
      uint64_t fh = fromhost;
      if (fh) {
        if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
          fromhost = 0;
          break;
        }
        __check_fromhost();
      }
    }
  spinlock_unlock(&htif_lock);
}

void htif_syscall(uintptr_t arg)
{
  do_tohost_fromhost(0, 0, arg);
}

void htif_console_putchar(uint8_t ch)
{
  spinlock_lock(&htif_lock);
    __set_tohost(1, 1, ch);
  spinlock_unlock(&htif_lock);
}

void htif_poweroff()
{
  while (1) {
    fromhost = 0;
    tohost = 1;
  }
}

struct request {
  uint64_t addr;
  uint64_t offset;
  uint64_t size;
  uint64_t tag;
};

void htif_disk_read(uintptr_t addr, uintptr_t offset, size_t size)
{
  struct request req;

  req.addr = addr;
  req.offset = offset;
  req.size = size;
  req.tag = 0;

  do_tohost_fromhost(2, 0, (uintptr_t) &req);
}

void htif_disk_write(uintptr_t addr, uintptr_t offset, size_t size)
{
  struct request req;

  req.addr = addr;
  req.offset = offset;
  req.size = size;
  req.tag = 0;

  do_tohost_fromhost(2, 1, (uintptr_t) &req);
}

unsigned long htif_disk_size(void)
{
  char idbuf[128];
  uintptr_t addr = (uintptr_t) idbuf;
  unsigned long payload;
  char *id = idbuf, *s;

  // The buffer address needs to be aligned to 64 bytes
  if (addr % 64 != 0) {
    unsigned long inc = 64 - (addr % 64);
    addr += inc;
    id += inc;
  }

  payload = (addr << 8) | 0xff;
  do_tohost_fromhost(2, 255, payload);

  s = strstr(id, "size=");
  if (s == NULL)
    return 0;
  s += 5;
  return atol(s);
}