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
145
146
|
#include <common/test.h>
#include <kmi/utils.h>
#include <libfdt.h>
/* important bits taken from src/debug.c */
#define UART_8250_DATA 0
#define UART_8250_LSR 5
#define LSR_THRE (1 << 5)
static bool __tx_empty(char *ptr, size_t shift)
{
volatile char *lsr = ptr + (UART_8250_LSR << shift);
return (*lsr) & LSR_THRE;
}
static void __print_char(char *ptr, size_t shift, char c)
{
while (__tx_empty(ptr, shift) == 0)
;
volatile char *data = ptr + (UART_8250_DATA << shift);
*data = c;
}
static void __print_ok(char *ptr, size_t shift)
{
/* theoretically the kernel might jump in and print something itself,
* but for now let's ignore that possibility. In the future, we might
* want to move printing completely to userspace to avoid that issue? */
__print_char(ptr, shift, 'O');
__print_char(ptr, shift, 'K');
__print_char(ptr, shift, '\n');
}
static bool __supported(const char *dev_name)
{
/* qemu, for example */
if (strncmp("ns16550", dev_name, 7) == 0)
return true;
/* mentioned in dtc documentation as an example */
if (strncmp("ns8250", dev_name, 7) == 0)
return true;
/* starfive visionfive 2, for example (hopefully works) */
if (strncmp("snps,dw-apb-uart", dev_name, 16) == 0)
return true;
return false;
}
START(pid, tid, d0, d1, d2, d3)
{
UNUSED(d2);
UNUSED(d3);
/* this starting bit is more or less directly shared with the fdt test,
* but that can be seen as a requirement for the dmem setup to work I
* guess? */
check(pid == 0, "wrong src process ID");
check(tid == 1, "wrong root thread ID");
check(d0 == SYS_USER_SPAWNED, "wrong op");
printf("fdt at %lx\n", d1);
void *fdt = (void *)d1;
int chosen_offset = fdt_path_offset(fdt, "/chosen");
check(chosen_offset > 0, "couldn't find /chosen");
const char *stdout =
fdt_getprop(fdt, chosen_offset, "stdout-path", NULL);
check(stdout != NULL, "couldn't find stdout-path");
printf("stdout at %s\n", stdout);
/* discard options */
size_t baselen = strlen(stdout);
const char *options = strchr(stdout, ':');
if (options)
baselen = options - stdout;
int stdout_offset = fdt_path_offset_namelen(fdt, stdout, baselen);
check(stdout_offset > 0, "couldn't find %s offset", stdout);
/* get serial device type */
const char *dev_name = (const char *)fdt_getprop(fdt, stdout_offset,
"compatible", NULL);
check(dev_name != NULL, "couldn't get %s devicename", stdout);
printf("devname %s\n", dev_name);
check(__supported(dev_name), "unsupported device: %s", dev_name);
/* get serial device address */
const void *reg_ptr = fdt_getprop(fdt, stdout_offset, "reg", NULL);
check(reg_ptr != NULL, "couldn't get 'reg' property");
struct cell_info ci = get_cellinfo(fdt, stdout_offset);
pm_t dbg_ptr = (pm_t)fdt_load_reg_addr(ci, reg_ptr, 0);
printf("uart reg at %lx\n", dbg_ptr);
/* get serial device offset if present */
size_t shift = 0;
const void *shift_ptr = fdt_getprop(fdt, stdout_offset, "reg-shift",
NULL);
if (shift_ptr)
shift = (size_t)fdt_load_int32_ptr(shift_ptr);
printf("shift: %zu\n", shift);
/* get size of zeroth page order, as all physical memory mappings are
* aligned to them and we must take care to align things ourselves */
size_t page_size = sys_conf_get(CONF_PAGE_SIZE, 0);
printf("page size: %zu\n", page_size);
size_t offset = dbg_ptr - align_down(dbg_ptr, page_size);
printf("offset: %zu\n", offset);
/* map to userspace, size is not really accurate here but definitely
* large enough to handle the 8250 */
void *ptr = sys_req_dmem(dbg_ptr, 1024, VM_R | VM_W);
check(ptr != NULL, "failed mapping device memory");
printf("user mapping at %p + %zu\n", ptr, offset);
/* check that we can't allocate the same region twice */
void *nptr = sys_req_dmem(dbg_ptr, 1024, VM_R | VM_W);
check(nptr == NULL, "allocated same devmem twice");
/* check that we can free the device mem */
enum sys_status ok = sys_free_mem((uintptr_t)ptr);
check(ok == OK, "failed freeing devmem");
/* try to map the memory again now that it's free */
ptr = sys_req_dmem(dbg_ptr, 1024, VM_R | VM_W);
check(ptr != NULL, "failed reallocating devmem");
/* try printing */
__print_ok((char *)ptr + offset, shift);
/** @todo check that a fork/thread creation handled the devmem
* references correctly */
/* don't run off the end of the _start */
sys_poweroff(SYS_SHUTDOWN);
}
|