blob: e12b99bb371af94831c351bd9248226e37c350ab [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include "lkboot.h"
24
25#include <stdio.h>
26#include <debug.h>
27#include <string.h>
28#include <compiler.h>
29#include <err.h>
30#include <assert.h>
31#include <trace.h>
32#include <stdlib.h>
33#include <lib/cbuf.h>
34#include <app/lkboot.h>
35#include <arch/arm/dcc.h>
36#include <arch/mmu.h>
37#include <kernel/mutex.h>
38
39#include "pdcc.h"
40
41#define LOCAL_TRACE 0
42
43static struct pdcc_buffer_descriptor buffer_desc __ALIGNED(256);
44static paddr_t buffer_desc_phys;
45
46#define DCC_BUFLEN 256
47
48static uint8_t htod_buffer[DCC_BUFLEN] __ALIGNED(CACHE_LINE);
49static uint8_t dtoh_buffer[DCC_BUFLEN] __ALIGNED(CACHE_LINE);
50
51static uint htod_index;
52static uint htod_pos;
53static bool dtoh_filled;
54
55static void send_pdcc_command(uint32_t opcode, uint32_t data)
56{
57 uint32_t word;
58
59 word = PDCC_VALID |
60 ((opcode & 0x7f) << PDCC_OPCODE_SHIFT) |
61 (data & 0x00ffffff);
62
63 // XXX may block forever
64 LTRACEF("sending 0x%x\n", word);
65 arm_dcc_write(&word, 1, INFINITE_TIME);
66}
67
68static void send_buffer_header(void)
69{
70 send_pdcc_command(PDCC_OP_BUF_HEADER, buffer_desc_phys / 256);
71}
72
73static void send_reset(void)
74{
75 send_pdcc_command(PDCC_OP_RESET, 0);
76}
77
78static void send_out_index_update(uint32_t index)
79{
80 send_pdcc_command(PDCC_OP_UPDATE_OUT_INDEX, index);
81}
82
83static void send_buffer_consumed(void)
84{
85 send_pdcc_command(PDCC_OP_CONSUMED_IN, 0);
86}
87
88#define DCC_PROCESS_RESET 1
89static int dcc_process_opcode(uint32_t word)
90{
91 int ret = 0;
92
93 if (word & PDCC_VALID) {
94 uint32_t opcode = PDCC_OPCODE(word);
95 uint32_t data = PDCC_DATA(word);
96 LTRACEF("word 0x%x, opcode 0x%x, data 0x%x\n", word, opcode, data);
97 switch (opcode) {
98 case PDCC_OP_RESET:
99 htod_index = 0;
100 htod_pos = 0;
101 dtoh_filled = false;
102
103 // try to send the buffer header
104 send_buffer_header();
105 ret = DCC_PROCESS_RESET;
106 break;
107 case PDCC_OP_BUF_HEADER:
108 // we shouldn't get this
109 break;
110
111 case PDCC_OP_UPDATE_OUT_INDEX:
112 if (data > DCC_BUFLEN) {
113 // out of range
114 send_reset();
115 } else {
116 htod_index = data;
117 htod_pos = 0;
118 arch_invalidate_cache_range((vaddr_t)htod_buffer, DCC_BUFLEN);
119 }
120 break;
121
122 case PDCC_OP_CONSUMED_IN:
123 arch_invalidate_cache_range((vaddr_t)dtoh_buffer, DCC_BUFLEN);
124 dtoh_filled = false;
125 break;
126 default:
127 TRACEF("bad opcode from host 0x%x\n", opcode);
128 send_reset();
129 }
130 }
131
132 return ret;
133}
134
135static ssize_t dcc_read(void *unused, void *_data, size_t len)
136{
137 unsigned char *data = _data;
138 size_t pos = 0;
139 uint32_t dcc;
140
141 LTRACEF("buf %p, len %zu, htod_pos %u, htod_index %u\n", _data, len, htod_pos, htod_index);
142
143 lk_time_t timeout = 0; // first dcc command should be with no timeout
144 while (pos < len) {
145 // process a dcc command
146 ssize_t err = arm_dcc_read(&dcc, 1, timeout);
147 if (err > 0) {
148 err = dcc_process_opcode(dcc);
149 if (err == DCC_PROCESS_RESET) {
150 return ERR_IO;
151 }
152 }
153
154 // see if there is any data in the incoming buffer
155 if (htod_index > 0) {
156 size_t tocopy = MIN(htod_index - htod_pos, len - pos);
157
158 memcpy(&data[pos], &htod_buffer[htod_pos], tocopy);
159 pos += tocopy;
160 htod_pos += tocopy;
161
162 // if we consumed everything, tell the host we're done with the buffer
163 if (htod_pos == htod_index) {
164 send_buffer_consumed();
165 htod_index = 0;
166 htod_pos = 0;
167 arch_invalidate_cache_range((vaddr_t)htod_buffer, DCC_BUFLEN);
168 }
169 }
170
171 timeout = 1000;
172 }
173
174 return 0;
175}
176
177static ssize_t dcc_write(void *unused, const void *_data, size_t len)
178{
179 const unsigned char *data = _data;
180 size_t pos = 0;
181
182 LTRACEF("buf %p, len %zu\n", _data, len);
183
184 while (pos < len) {
185 LTRACEF("pos %zu, len %zu, dtoh_filled %d\n", pos, len, dtoh_filled);
186 if (!dtoh_filled) {
187 // put as much data as we can in the outgoing buffer
188 size_t tocopy = MIN(len, DCC_BUFLEN);
189
190 LTRACEF("tocopy %zu\n", tocopy);
191 memcpy(dtoh_buffer, data, tocopy);
192 arch_clean_cache_range((vaddr_t)dtoh_buffer, DCC_BUFLEN);
193 send_out_index_update(tocopy);
194 dtoh_filled = true;
195
196 pos += tocopy;
197 }
198
199 // process a dcc command
200 uint32_t dcc;
201 ssize_t err = arm_dcc_read(&dcc, 1, 1000);
202 if (err > 0) {
203 err = dcc_process_opcode(dcc);
204 if (err == DCC_PROCESS_RESET) {
205 return ERR_IO;
206 }
207 }
208 }
209
210 return pos;
211}
212
213lkb_t *lkboot_check_dcc_open(void)
214{
215 lkb_t *lkb = NULL;
216
217 // read a dcc op and process it
218 {
219 uint32_t dcc;
220 ssize_t err = arm_dcc_read(&dcc, 1, 0);
221 if (err > 0) {
222 err = dcc_process_opcode(dcc);
223 }
224 }
225
226 if (htod_index > 0) {
227 // we have data, construct a lkb and return it
228 LTRACEF("we have data on dcc, starting command handler\n");
229 lkb = lkboot_create_lkb(NULL, dcc_read, dcc_write);
230 }
231
232 return lkb;
233}
234
235void lkboot_dcc_init(void)
236{
237 paddr_t pa;
238 __UNUSED status_t err;
239
240 buffer_desc.version = PDCC_VERSION;
241
242 err = arch_mmu_query((vaddr_t)htod_buffer, &pa, NULL);
243 DEBUG_ASSERT(err == NO_ERROR);
244
245 buffer_desc.htod_buffer_phys = pa;
246 buffer_desc.htod_buffer_len = DCC_BUFLEN;
247
248 err = arch_mmu_query((vaddr_t)dtoh_buffer, &pa, NULL);
249 DEBUG_ASSERT(err == NO_ERROR);
250
251 buffer_desc.dtoh_buffer_phys = pa;
252 buffer_desc.dtoh_buffer_len = DCC_BUFLEN;
253
254 err = arch_mmu_query((vaddr_t)&buffer_desc, &buffer_desc_phys, NULL);
255 DEBUG_ASSERT(err == NO_ERROR);
256
257 arch_clean_cache_range((vaddr_t)&buffer_desc, sizeof(buffer_desc));
258}
259