1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include "lkboot.h"
9
10 #include <stdio.h>
11 #include <lk/debug.h>
12 #include <string.h>
13 #include <lk/compiler.h>
14 #include <lk/err.h>
15 #include <assert.h>
16 #include <lk/trace.h>
17 #include <stdlib.h>
18 #include <lib/cbuf.h>
19 #include <app/lkboot.h>
20 #include <arch/arm/dcc.h>
21 #include <kernel/vm.h>
22 #include <kernel/mutex.h>
23
24 #include "pdcc.h"
25
26 #define LOCAL_TRACE 0
27
28 static struct pdcc_buffer_descriptor buffer_desc __ALIGNED(256);
29 static paddr_t buffer_desc_phys;
30
31 #define DCC_BUFLEN 256
32
33 static uint8_t htod_buffer[DCC_BUFLEN] __ALIGNED(CACHE_LINE);
34 static uint8_t dtoh_buffer[DCC_BUFLEN] __ALIGNED(CACHE_LINE);
35
36 static uint htod_index;
37 static uint htod_pos;
38 static bool dtoh_filled;
39
send_pdcc_command(uint32_t opcode,uint32_t data)40 static void send_pdcc_command(uint32_t opcode, uint32_t data) {
41 uint32_t word;
42
43 word = PDCC_VALID |
44 ((opcode & 0x7f) << PDCC_OPCODE_SHIFT) |
45 (data & 0x00ffffff);
46
47 // XXX may block forever
48 LTRACEF("sending 0x%x\n", word);
49 arm_dcc_write(&word, 1, INFINITE_TIME);
50 }
51
send_buffer_header(void)52 static void send_buffer_header(void) {
53 send_pdcc_command(PDCC_OP_BUF_HEADER, buffer_desc_phys / 256);
54 }
55
send_reset(void)56 static void send_reset(void) {
57 send_pdcc_command(PDCC_OP_RESET, 0);
58 }
59
send_out_index_update(uint32_t index)60 static void send_out_index_update(uint32_t index) {
61 send_pdcc_command(PDCC_OP_UPDATE_OUT_INDEX, index);
62 }
63
send_buffer_consumed(void)64 static void send_buffer_consumed(void) {
65 send_pdcc_command(PDCC_OP_CONSUMED_IN, 0);
66 }
67
68 #define DCC_PROCESS_RESET 1
dcc_process_opcode(uint32_t word)69 static int dcc_process_opcode(uint32_t word) {
70 int ret = 0;
71
72 if (word & PDCC_VALID) {
73 uint32_t opcode = PDCC_OPCODE(word);
74 uint32_t data = PDCC_DATA(word);
75 LTRACEF("word 0x%x, opcode 0x%x, data 0x%x\n", word, opcode, data);
76 switch (opcode) {
77 case PDCC_OP_RESET:
78 htod_index = 0;
79 htod_pos = 0;
80 dtoh_filled = false;
81
82 // try to send the buffer header
83 send_buffer_header();
84 ret = DCC_PROCESS_RESET;
85 break;
86 case PDCC_OP_BUF_HEADER:
87 // we shouldn't get this
88 break;
89
90 case PDCC_OP_UPDATE_OUT_INDEX:
91 if (data > DCC_BUFLEN) {
92 // out of range
93 send_reset();
94 } else {
95 htod_index = data;
96 htod_pos = 0;
97 arch_invalidate_cache_range((vaddr_t)htod_buffer, DCC_BUFLEN);
98 }
99 break;
100
101 case PDCC_OP_CONSUMED_IN:
102 arch_invalidate_cache_range((vaddr_t)dtoh_buffer, DCC_BUFLEN);
103 dtoh_filled = false;
104 break;
105 default:
106 TRACEF("bad opcode from host 0x%x\n", opcode);
107 send_reset();
108 }
109 }
110
111 return ret;
112 }
113
dcc_read(void * unused,void * _data,size_t len)114 static ssize_t dcc_read(void *unused, void *_data, size_t len) {
115 unsigned char *data = _data;
116 size_t pos = 0;
117 uint32_t dcc;
118
119 LTRACEF("buf %p, len %zu, htod_pos %u, htod_index %u\n", _data, len, htod_pos, htod_index);
120
121 lk_time_t timeout = 0; // first dcc command should be with no timeout
122 while (pos < len) {
123 // process a dcc command
124 ssize_t err = arm_dcc_read(&dcc, 1, timeout);
125 if (err > 0) {
126 err = dcc_process_opcode(dcc);
127 if (err == DCC_PROCESS_RESET) {
128 return ERR_IO;
129 }
130 }
131
132 // see if there is any data in the incoming buffer
133 if (htod_index > 0) {
134 size_t tocopy = MIN(htod_index - htod_pos, len - pos);
135
136 memcpy(&data[pos], &htod_buffer[htod_pos], tocopy);
137 pos += tocopy;
138 htod_pos += tocopy;
139
140 // if we consumed everything, tell the host we're done with the buffer
141 if (htod_pos == htod_index) {
142 send_buffer_consumed();
143 htod_index = 0;
144 htod_pos = 0;
145 arch_invalidate_cache_range((vaddr_t)htod_buffer, DCC_BUFLEN);
146 }
147 }
148
149 timeout = 1000;
150 }
151
152 return 0;
153 }
154
dcc_write(void * unused,const void * _data,size_t len)155 static ssize_t dcc_write(void *unused, const void *_data, size_t len) {
156 const unsigned char *data = _data;
157 size_t pos = 0;
158
159 LTRACEF("buf %p, len %zu\n", _data, len);
160
161 while (pos < len) {
162 LTRACEF("pos %zu, len %zu, dtoh_filled %d\n", pos, len, dtoh_filled);
163 if (!dtoh_filled) {
164 // put as much data as we can in the outgoing buffer
165 size_t tocopy = MIN(len, DCC_BUFLEN);
166
167 LTRACEF("tocopy %zu\n", tocopy);
168 memcpy(dtoh_buffer, data, tocopy);
169 arch_clean_cache_range((vaddr_t)dtoh_buffer, DCC_BUFLEN);
170 send_out_index_update(tocopy);
171 dtoh_filled = true;
172
173 pos += tocopy;
174 }
175
176 // process a dcc command
177 uint32_t dcc;
178 ssize_t err = arm_dcc_read(&dcc, 1, 1000);
179 if (err > 0) {
180 err = dcc_process_opcode(dcc);
181 if (err == DCC_PROCESS_RESET) {
182 return ERR_IO;
183 }
184 }
185 }
186
187 return pos;
188 }
189
lkboot_check_dcc_open(void)190 lkb_t *lkboot_check_dcc_open(void) {
191 lkb_t *lkb = NULL;
192
193 // read a dcc op and process it
194 {
195 uint32_t dcc;
196 ssize_t err = arm_dcc_read(&dcc, 1, 0);
197 if (err > 0) {
198 err = dcc_process_opcode(dcc);
199 }
200 }
201
202 if (htod_index > 0) {
203 // we have data, construct a lkb and return it
204 LTRACEF("we have data on dcc, starting command handler\n");
205 lkb = lkboot_create_lkb(NULL, dcc_read, dcc_write);
206 }
207
208 return lkb;
209 }
210
lkboot_dcc_init(void)211 void lkboot_dcc_init(void) {
212 paddr_t pa;
213 __UNUSED status_t err;
214
215 buffer_desc.version = PDCC_VERSION;
216
217 pa = vaddr_to_paddr(htod_buffer);
218 DEBUG_ASSERT(pa);
219
220 buffer_desc.htod_buffer_phys = pa;
221 buffer_desc.htod_buffer_len = DCC_BUFLEN;
222
223 pa = vaddr_to_paddr(dtoh_buffer);
224 DEBUG_ASSERT(pa);
225
226 buffer_desc.dtoh_buffer_phys = pa;
227 buffer_desc.dtoh_buffer_len = DCC_BUFLEN;
228
229 buffer_desc_phys = vaddr_to_paddr(&buffer_desc);
230 DEBUG_ASSERT(buffer_desc_phys);
231
232 arch_clean_cache_range((vaddr_t)&buffer_desc, sizeof(buffer_desc));
233 }
234
235