summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-08-04 17:06:11 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2017-01-24 15:25:14 +0100
commit8fabbb008c1a31c809a3d97f22351f141a2bd02d (patch)
tree6e16386a7348197920a5ce1d99f623d0e10b6c3c
parentd91d9f6851bba401650912c5cabcfe4c5f1150df (diff)
downloadrockbox-8fabbb008c1a31c809a3d97f22351f141a2bd02d.tar.gz
rockbox-8fabbb008c1a31c809a3d97f22351f141a2bd02d.zip
hwstub: add support for coprocessor operations
At the moment the stub only implement them for MIPS. Change-Id: Ica835a0e9c70fa5675c3d655eae986e812a47de8
-rw-r--r--utils/hwstub/include/hwstub.hpp18
-rw-r--r--utils/hwstub/include/hwstub_net.hpp2
-rw-r--r--utils/hwstub/include/hwstub_protocol.h32
-rw-r--r--utils/hwstub/include/hwstub_usb.hpp4
-rw-r--r--utils/hwstub/include/hwstub_virtual.hpp2
-rw-r--r--utils/hwstub/lib/hwstub.cpp46
-rw-r--r--utils/hwstub/lib/hwstub_net.cpp12
-rw-r--r--utils/hwstub/lib/hwstub_usb.cpp52
-rw-r--r--utils/hwstub/lib/hwstub_virtual.cpp12
-rw-r--r--utils/hwstub/stub/main.c188
-rw-r--r--utils/hwstub/tools/hwstub_shell.cpp72
11 files changed, 423 insertions, 17 deletions
diff --git a/utils/hwstub/include/hwstub.hpp b/utils/hwstub/include/hwstub.hpp
index deac976240..e403857200 100644
--- a/utils/hwstub/include/hwstub.hpp
+++ b/utils/hwstub/include/hwstub.hpp
@@ -62,6 +62,8 @@ enum class error
62 NET_ERROR, /** Network error */ 62 NET_ERROR, /** Network error */
63 TIMEOUT, /** Operation timed out */ 63 TIMEOUT, /** Operation timed out */
64 OVERFLW, /** Operation stopped to prevent buffer overflow */ 64 OVERFLW, /** Operation stopped to prevent buffer overflow */
65 UNIMPLEMENTED, /** Operation has not been implemented */
66 UNSUPPORTED, /** Operation is not supported by device/protocol */
65}; 67};
66 68
67/** Return a string explaining the error */ 69/** Return a string explaining the error */
@@ -266,6 +268,15 @@ public:
266 * according to the buffer size and call read_dev() and write_dev() */ 268 * according to the buffer size and call read_dev() and write_dev() */
267 error read(uint32_t addr, void *buf, size_t& sz, bool atomic); 269 error read(uint32_t addr, void *buf, size_t& sz, bool atomic);
268 error write(uint32_t addr, const void *buf, size_t& sz, bool atomic); 270 error write(uint32_t addr, const void *buf, size_t& sz, bool atomic);
271 /** Execute a general cop operation: if out_data is not null, data is appended to header,
272 * if in_data is not null, a read operation follows to retrieve some data.
273 * The in_data parameters is updated to reflect the number of transfered bytes */
274 error cop_op(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
275 size_t out_size, void *in_data, size_t *in_size);
276 /** Execute a coprocessor read operation */
277 error read32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t& value);
278 /** Execute a coprocessor write operation */
279 error write32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t value);
269 /** Get device buffer size: any read() or write() greater than this size 280 /** Get device buffer size: any read() or write() greater than this size
270 * will be split into several transaction to avoid overflowing device 281 * will be split into several transaction to avoid overflowing device
271 * buffer. */ 282 * buffer. */
@@ -303,6 +314,11 @@ protected:
303 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0; 314 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0;
304 virtual error get_dev_log(void *buf, size_t& buf_sz) = 0; 315 virtual error get_dev_log(void *buf, size_t& buf_sz) = 0;
305 virtual error exec_dev(uint32_t addr, uint16_t flags) = 0; 316 virtual error exec_dev(uint32_t addr, uint16_t flags) = 0;
317 /* cop operation: if out_data is not null, data is appended to header, if
318 * in_data is not null, a READ2 operation follows to retrieve some data
319 * The in_data parameters is updated to reflect the number of transfered bytes*/
320 virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
321 size_t out_size, void *in_data, size_t *in_size) = 0;
306 322
307 std::shared_ptr<device> m_dev; /* pointer to device */ 323 std::shared_ptr<device> m_dev; /* pointer to device */
308 std::atomic<int> m_refcnt; /* reference count */ 324 std::atomic<int> m_refcnt; /* reference count */
@@ -338,6 +354,8 @@ protected:
338 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); 354 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
339 virtual error get_dev_log(void *buf, size_t& buf_sz); 355 virtual error get_dev_log(void *buf, size_t& buf_sz);
340 virtual error exec_dev(uint32_t addr, uint16_t flags); 356 virtual error exec_dev(uint32_t addr, uint16_t flags);
357 virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
358 size_t out_size, void *in_data, size_t *in_size);
341 virtual error status() const; 359 virtual error status() const;
342 virtual size_t get_buffer_size(); 360 virtual size_t get_buffer_size();
343 361
diff --git a/utils/hwstub/include/hwstub_net.hpp b/utils/hwstub/include/hwstub_net.hpp
index 2cf6e07ccb..ae0e09e920 100644
--- a/utils/hwstub/include/hwstub_net.hpp
+++ b/utils/hwstub/include/hwstub_net.hpp
@@ -184,6 +184,8 @@ protected:
184 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); 184 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
185 virtual error get_dev_log(void *buf, size_t& buf_sz); 185 virtual error get_dev_log(void *buf, size_t& buf_sz);
186 virtual error exec_dev(uint32_t addr, uint16_t flags); 186 virtual error exec_dev(uint32_t addr, uint16_t flags);
187 virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
188 size_t out_size, void *in_data, size_t *in_size);
187 virtual error status() const; 189 virtual error status() const;
188 virtual size_t get_buffer_size(); 190 virtual size_t get_buffer_size();
189 191
diff --git a/utils/hwstub/include/hwstub_protocol.h b/utils/hwstub/include/hwstub_protocol.h
index f767e50571..ed26ee78e4 100644
--- a/utils/hwstub/include/hwstub_protocol.h
+++ b/utils/hwstub/include/hwstub_protocol.h
@@ -33,7 +33,7 @@
33 */ 33 */
34 34
35#define HWSTUB_VERSION_MAJOR 4 35#define HWSTUB_VERSION_MAJOR 4
36#define HWSTUB_VERSION_MINOR 2 36#define HWSTUB_VERSION_MINOR 3
37 37
38#define HWSTUB_VERSION__(maj, min) #maj"."#min 38#define HWSTUB_VERSION__(maj, min) #maj"."#min
39#define HWSTUB_VERSION_(maj, min) HWSTUB_VERSION__(maj, min) 39#define HWSTUB_VERSION_(maj, min) HWSTUB_VERSION__(maj, min)
@@ -169,6 +169,7 @@ struct hwstub_net_hdr_t
169#define HWSTUB_EXEC 0x44 169#define HWSTUB_EXEC 0x44
170#define HWSTUB_READ2_ATOMIC 0x45 170#define HWSTUB_READ2_ATOMIC 0x45
171#define HWSTUB_WRITE_ATOMIC 0x46 171#define HWSTUB_WRITE_ATOMIC 0x46
172#define HWSTUB_COPROCESSOR_OP 0x47
172 173
173/* the following commands and the ACK/NACK mechanism are net only */ 174/* the following commands and the ACK/NACK mechanism are net only */
174#define HWSERVER_ACK(n) (0x100|(n)) 175#define HWSERVER_ACK(n) (0x100|(n))
@@ -252,6 +253,35 @@ struct hwstub_exec_req_t
252} __attribute__((packed)); 253} __attribute__((packed));
253 254
254/** 255/**
256 * HWSTUB_COPROCESSOR_OP
257 * Execute a coprocessor operation. The operation is describe in the header of
258 * the structure. There are currently two supported operations:
259 * - read: following the HWSTUB_COPROCESSOR_OP, the host can retrieve the data
260 * by sending a HWSTUB_READ2 request (just like a regular read) of
261 * the appropriate size
262 * - write: the header is followed by the data to write.
263 * If the request has two parts (second being READ2) then both requests must use
264 * the same ID in wValue, otherwise the second request will be STALLed.
265 * If a particular operation is not supported, it must be STALLed by the device.
266 */
267
268#define HWSTUB_COP_READ 0 /* read operation */
269#define HWSTUB_COP_WRITE 1 /* write operation */
270/* for MIPS */
271#define HWSTUB_COP_MIPS_COP 0 /* coprocessor number */
272#define HWSTUB_COP_MIPS_REG 1 /* coprocessor register */
273#define HWSTUB_COP_MIPS_SEL 2 /* coprocessor select */
274
275#define HWSTUB_COP_ARGS 7 /* maximum number of arguments */
276
277struct hwstub_cop_req_t
278{
279 uint8_t bOp; /* operation to execute */
280 uint8_t bArgs[HWSTUB_COP_ARGS]; /* arguments to the operation */
281 /* followed by data for WRITE operation */
282};
283
284/**
255 * HWSERVER_HELLO: 285 * HWSERVER_HELLO:
256 * Say hello to the server, give protocol version and get server version. 286 * Say hello to the server, give protocol version and get server version.
257 * Send: args[0] = major << 8 | minor, no data 287 * Send: args[0] = major << 8 | minor, no data
diff --git a/utils/hwstub/include/hwstub_usb.hpp b/utils/hwstub/include/hwstub_usb.hpp
index 6a9d4d8798..15a0fcaaec 100644
--- a/utils/hwstub/include/hwstub_usb.hpp
+++ b/utils/hwstub/include/hwstub_usb.hpp
@@ -132,6 +132,8 @@ protected:
132 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); 132 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
133 virtual error get_dev_log(void *buf, size_t& buf_sz); 133 virtual error get_dev_log(void *buf, size_t& buf_sz);
134 virtual error exec_dev(uint32_t addr, uint16_t flags); 134 virtual error exec_dev(uint32_t addr, uint16_t flags);
135 virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
136 size_t out_size, void *in_data, size_t *in_size);
135 virtual error status() const; 137 virtual error status() const;
136 virtual size_t get_buffer_size(); 138 virtual size_t get_buffer_size();
137 /* Probe a device to check if it is an hwstub device and return the interface 139 /* Probe a device to check if it is an hwstub device and return the interface
@@ -162,6 +164,8 @@ protected:
162 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); 164 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
163 virtual error get_dev_log(void *buf, size_t& buf_sz); 165 virtual error get_dev_log(void *buf, size_t& buf_sz);
164 virtual error exec_dev(uint32_t addr, uint16_t flags); 166 virtual error exec_dev(uint32_t addr, uint16_t flags);
167 virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
168 size_t out_size, void *in_data, size_t *in_size);
165 virtual error status() const; 169 virtual error status() const;
166 virtual size_t get_buffer_size(); 170 virtual size_t get_buffer_size();
167 error probe(); 171 error probe();
diff --git a/utils/hwstub/include/hwstub_virtual.hpp b/utils/hwstub/include/hwstub_virtual.hpp
index d35f98e0ec..1e35378840 100644
--- a/utils/hwstub/include/hwstub_virtual.hpp
+++ b/utils/hwstub/include/hwstub_virtual.hpp
@@ -147,6 +147,8 @@ protected:
147 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); 147 virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
148 virtual error get_dev_log(void *buf, size_t& buf_sz); 148 virtual error get_dev_log(void *buf, size_t& buf_sz);
149 virtual error exec_dev(uint32_t addr, uint16_t flags); 149 virtual error exec_dev(uint32_t addr, uint16_t flags);
150 virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
151 size_t out_size, void *in_data, size_t *in_size);
150 virtual error status() const; 152 virtual error status() const;
151 virtual size_t get_buffer_size(); 153 virtual size_t get_buffer_size();
152 154
diff --git a/utils/hwstub/lib/hwstub.cpp b/utils/hwstub/lib/hwstub.cpp
index 9dd2915903..5e708c3bb8 100644
--- a/utils/hwstub/lib/hwstub.cpp
+++ b/utils/hwstub/lib/hwstub.cpp
@@ -45,6 +45,8 @@ std::string error_string(error err)
45 case error::PROTOCOL_ERROR: return "network protocol error"; 45 case error::PROTOCOL_ERROR: return "network protocol error";
46 case error::TIMEOUT: return "timeout"; 46 case error::TIMEOUT: return "timeout";
47 case error::OVERFLW: return "overflow"; 47 case error::OVERFLW: return "overflow";
48 case error::UNIMPLEMENTED: return "operation is not implemented";
49 case error::UNSUPPORTED: return "operation unsupported";
48 default: return "unknown error"; 50 default: return "unknown error";
49 } 51 }
50} 52}
@@ -457,6 +459,38 @@ error handle::write(uint32_t addr, const void *buf, size_t& sz, bool atomic)
457 return error::SUCCESS; 459 return error::SUCCESS;
458} 460}
459 461
462error handle::cop_op(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data,
463 size_t out_size, void *in_data, size_t *in_size)
464{
465 std::unique_lock<std::recursive_mutex> lock(m_mutex);
466 /* get a pointer so that it's not destroyed during the runtime of the function,
467 * the pointer will be released at the end of the function */
468 std::shared_ptr<context> ctx = m_dev->get_context();
469 if(!ctx)
470 return error::NO_CONTEXT;
471 /* ensure valid status */
472 error err = status();
473 if(err != error::SUCCESS)
474 return err;
475 return cop_dev(op, args, out_data, out_size, in_data, in_size);
476}
477
478error handle::read32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t& value)
479{
480 size_t sz = sizeof(value);
481 error err = cop_op(HWSTUB_COP_READ, args, nullptr, 0, &value, &sz);
482 if(err != error::SUCCESS)
483 return err;
484 if(sz != sizeof(value))
485 return error::ERROR;
486 return error::SUCCESS;
487}
488
489error handle::write32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t value)
490{
491 return cop_op(HWSTUB_COP_WRITE, args, &value, sizeof(value), nullptr, nullptr);
492}
493
460error handle::status() const 494error handle::status() const
461{ 495{
462 /* check context */ 496 /* check context */
@@ -616,6 +650,18 @@ error dummy_handle::exec_dev(uint32_t addr, uint16_t flags)
616 return error::DUMMY; 650 return error::DUMMY;
617} 651}
618 652
653error dummy_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
654 const void *out_data, size_t out_size, void *in_data, size_t *in_size)
655{
656 (void) op;
657 (void) args;
658 (void) out_data;
659 (void) out_size;
660 (void) in_data;
661 (void) in_size;
662 return error::DUMMY;
663}
664
619error dummy_handle::status() const 665error dummy_handle::status() const
620{ 666{
621 error err = handle::status(); 667 error err = handle::status();
diff --git a/utils/hwstub/lib/hwstub_net.cpp b/utils/hwstub/lib/hwstub_net.cpp
index ddafea6351..c9d201a761 100644
--- a/utils/hwstub/lib/hwstub_net.cpp
+++ b/utils/hwstub/lib/hwstub_net.cpp
@@ -735,6 +735,18 @@ error handle::exec_dev(uint32_t addr, uint16_t flags)
735 return error::SUCCESS; 735 return error::SUCCESS;
736} 736}
737 737
738error handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
739 const void *out_data, size_t out_size, void *in_data, size_t *in_size)
740{
741 (void) op;
742 (void) args;
743 (void) out_data;
744 (void) out_size;
745 (void) in_data;
746 (void) in_size;
747 return error::UNIMPLEMENTED;
748}
749
738error handle::status() const 750error handle::status() const
739{ 751{
740 return hwstub::handle::status(); 752 return hwstub::handle::status();
diff --git a/utils/hwstub/lib/hwstub_usb.cpp b/utils/hwstub/lib/hwstub_usb.cpp
index 6bb1cfa049..37249f5812 100644
--- a/utils/hwstub/lib/hwstub_usb.cpp
+++ b/utils/hwstub/lib/hwstub_usb.cpp
@@ -399,6 +399,46 @@ error rb_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atom
399 return ret; 399 return ret;
400} 400}
401 401
402error rb_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
403 const void *out_data, size_t out_size, void *in_data, size_t *in_size)
404{
405 (void) op;
406 (void) args;
407 (void) out_data;
408 (void) out_size;
409 (void) in_data;
410 (void) in_size;
411 std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
412 if(!hctx)
413 return error::NO_CONTEXT;
414
415 /* construct out request: header followed by (optional) data */
416 size_t hdr_sz = sizeof(struct hwstub_cop_req_t);
417 uint8_t *tmp_buf = new uint8_t[out_size + hdr_sz];
418 struct hwstub_cop_req_t *req = reinterpret_cast<struct hwstub_cop_req_t *>(tmp_buf);
419 req->bOp = op;
420 for(int i = 0; i < HWSTUB_COP_ARGS; i++)
421 req->bArgs[i] = args[i];
422 if(out_size > 0)
423 memcpy(tmp_buf + hdr_sz, out_data, out_size);
424 error ret = interpret_libusb_error(libusb_control_transfer(m_handle,
425 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
426 HWSTUB_COPROCESSOR_OP, m_transac_id++, m_intf,
427 (unsigned char *)req, out_size + hdr_sz, m_timeout), out_size + hdr_sz);
428 delete[] tmp_buf;
429 /* return errors if any */
430 if(ret != error::SUCCESS)
431 return ret;
432 /* return now if there is no read stage */
433 if(in_data == nullptr)
434 return error::SUCCESS;
435 /* perform read stage (use the same transaction ID) */
436 return interpret_libusb_size(libusb_control_transfer(m_handle,
437 LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
438 HWSTUB_READ2, m_transac_id - 1, m_intf,
439 (unsigned char *)in_data, *in_size, m_timeout), *in_size);
440}
441
402bool rb_handle::find_intf(struct libusb_device_descriptor *dev, 442bool rb_handle::find_intf(struct libusb_device_descriptor *dev,
403 struct libusb_config_descriptor *config, int& intf_idx) 443 struct libusb_config_descriptor *config, int& intf_idx)
404{ 444{
@@ -674,6 +714,18 @@ error jz_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atom
674 return ret; 714 return ret;
675} 715}
676 716
717error jz_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
718 const void *out_data, size_t out_size, void *in_data, size_t *in_size)
719{
720 (void) op;
721 (void) args;
722 (void) out_data;
723 (void) out_size;
724 (void) in_data;
725 (void) in_size;
726 return error::UNSUPPORTED;
727}
728
677bool jz_handle::is_boot_dev(struct libusb_device_descriptor *dev, 729bool jz_handle::is_boot_dev(struct libusb_device_descriptor *dev,
678 struct libusb_config_descriptor *config) 730 struct libusb_config_descriptor *config)
679{ 731{
diff --git a/utils/hwstub/lib/hwstub_virtual.cpp b/utils/hwstub/lib/hwstub_virtual.cpp
index fada56fb83..5c9e79e5a1 100644
--- a/utils/hwstub/lib/hwstub_virtual.cpp
+++ b/utils/hwstub/lib/hwstub_virtual.cpp
@@ -221,6 +221,18 @@ error handle::exec_dev(uint32_t addr, uint16_t flags)
221 return p ? p->exec_dev(addr, flags) : error::DISCONNECTED; 221 return p ? p->exec_dev(addr, flags) : error::DISCONNECTED;
222} 222}
223 223
224error handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS],
225 const void *out_data, size_t out_size, void *in_data, size_t *in_size)
226{
227 (void) op;
228 (void) args;
229 (void) out_data;
230 (void) out_size;
231 (void) in_data;
232 (void) in_size;
233 return error::UNSUPPORTED;
234}
235
224error handle::status() const 236error handle::status() const
225{ 237{
226 return hwstub::handle::status(); 238 return hwstub::handle::status();
diff --git a/utils/hwstub/stub/main.c b/utils/hwstub/stub/main.c
index c35872f320..3eee8b6a18 100644
--- a/utils/hwstub/stub/main.c
+++ b/utils/hwstub/stub/main.c
@@ -400,10 +400,12 @@ static bool read_atomic(void *dst, void *src, size_t sz)
400 } 400 }
401} 401}
402 402
403static void *last_read_addr = 0;
404static uint16_t last_read_id = 0xffff;
405static size_t last_read_max_size = 0;
406
403static void handle_read(struct usb_ctrlrequest *req) 407static void handle_read(struct usb_ctrlrequest *req)
404{ 408{
405 static uint32_t last_addr = 0;
406 static uint16_t last_id = 0xffff;
407 uint16_t id = req->wValue; 409 uint16_t id = req->wValue;
408 410
409 if(req->bRequest == HWSTUB_READ) 411 if(req->bRequest == HWSTUB_READ)
@@ -413,26 +415,29 @@ static void handle_read(struct usb_ctrlrequest *req)
413 return usb_drv_stall(EP_CONTROL, true, true); 415 return usb_drv_stall(EP_CONTROL, true, true);
414 asm volatile("nop" : : : "memory"); 416 asm volatile("nop" : : : "memory");
415 struct hwstub_read_req_t *read = (void *)usb_buffer; 417 struct hwstub_read_req_t *read = (void *)usb_buffer;
416 last_addr = read->dAddress; 418 last_read_addr = (void *)read->dAddress;
417 last_id = id; 419 last_read_max_size = usb_buffer_size;
420 last_read_id = id;
418 usb_drv_send(EP_CONTROL, NULL, 0); 421 usb_drv_send(EP_CONTROL, NULL, 0);
419 } 422 }
420 else 423 else
421 { 424 {
422 if(id != last_id) 425 /* NOTE: READ2 is also called after a coprocessor operation */
426 if(id != last_read_id)
423 return usb_drv_stall(EP_CONTROL, true, true); 427 return usb_drv_stall(EP_CONTROL, true, true);
428 size_t len = MIN(req->wLength, last_read_max_size);
424 429
425 if(req->bRequest == HWSTUB_READ2_ATOMIC) 430 if(req->bRequest == HWSTUB_READ2_ATOMIC)
426 { 431 {
427 if(set_data_abort_jmp() == 0) 432 if(set_data_abort_jmp() == 0)
428 { 433 {
429 if(!read_atomic(usb_buffer, (void *)last_addr, req->wLength)) 434 if(!read_atomic(usb_buffer, last_read_addr, len))
430 return usb_drv_stall(EP_CONTROL, true, true); 435 return usb_drv_stall(EP_CONTROL, true, true);
431 } 436 }
432 else 437 else
433 { 438 {
434 logf("trapped read data abort in [0x%x,0x%x]\n", last_addr, 439 logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr,
435 last_addr + req->wLength); 440 last_read_addr + len);
436 return usb_drv_stall(EP_CONTROL, true, true); 441 return usb_drv_stall(EP_CONTROL, true, true);
437 } 442 }
438 } 443 }
@@ -440,19 +445,19 @@ static void handle_read(struct usb_ctrlrequest *req)
440 { 445 {
441 if(set_data_abort_jmp() == 0) 446 if(set_data_abort_jmp() == 0)
442 { 447 {
443 memcpy(usb_buffer, (void *)last_addr, req->wLength); 448 memcpy(usb_buffer, last_read_addr, len);
444 asm volatile("nop" : : : "memory"); 449 asm volatile("nop" : : : "memory");
445 } 450 }
446 else 451 else
447 { 452 {
448 logf("trapped read data abort in [0x%x,0x%x]\n", last_addr, 453 logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr,
449 last_addr + req->wLength); 454 last_read_addr + len);
450 return usb_drv_stall(EP_CONTROL, true, true); 455 return usb_drv_stall(EP_CONTROL, true, true);
451 } 456 }
452 457
453 } 458 }
454 459
455 usb_drv_send(EP_CONTROL, usb_buffer, req->wLength); 460 usb_drv_send(EP_CONTROL, usb_buffer, len);
456 usb_drv_recv(EP_CONTROL, NULL, 0); 461 usb_drv_recv(EP_CONTROL, NULL, 0);
457 } 462 }
458} 463}
@@ -562,6 +567,163 @@ static void handle_exec(struct usb_ctrlrequest *req)
562 } 567 }
563} 568}
564 569
570#ifdef CPU_MIPS
571static uint32_t rw_cp0_inst_buffer[3];
572typedef uint32_t (*read_cp0_inst_buffer_fn_t)(void);
573typedef void (*write_cp0_inst_buffer_fn_t)(uint32_t);
574
575uint32_t mips_read_cp0(unsigned reg, unsigned sel)
576{
577 /* ok this is tricky because the coprocessor read instruction encoding
578 * contains the register and select, so we need to generate the instruction
579 * on the fly, we generate a "function like" buffer with three instructions:
580 * mfc0 v0, reg, sel
581 * jr ra
582 * nop
583 */
584 rw_cp0_inst_buffer[0] = 0x40000000 | /*v0*/2 << 16 | (sel & 0x7) | (reg & 0x1f) << 11;
585 rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */
586 rw_cp0_inst_buffer[2] = 0; /* nop */
587#ifdef CONFIG_FLUSH_CACHES
588 target_flush_caches();
589#endif
590 read_cp0_inst_buffer_fn_t fn = (read_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer;
591 return fn();
592}
593
594void mips_write_cp0(unsigned reg, unsigned sel, uint32_t val)
595{
596 /* ok this is tricky because the coprocessor write instruction encoding
597 * contains the register and select, so we need to generate the instruction
598 * on the fly, we generate a "function like" buffer with three instructions:
599 * mtc0 a0, reg, sel
600 * jr ra
601 * nop
602 */
603 rw_cp0_inst_buffer[0] = 0x40800000 | /*a0*/4 << 16 | (sel & 0x7) | (reg & 0x1f) << 11;
604 rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */
605 rw_cp0_inst_buffer[2] = 0; /* nop */
606#ifdef CONFIG_FLUSH_CACHES
607 target_flush_caches();
608#endif
609 write_cp0_inst_buffer_fn_t fn = (write_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer;
610 fn(val);
611}
612#endif
613
614/* coprocessor read: return <0 on error (-2 for dull dump), or size to return
615 * to host otherwise */
616int cop_read(uint8_t args[HWSTUB_COP_ARGS], void *out_data, size_t out_max_sz)
617{
618 /* virtually all targets do register-based operation, so 32-bit */
619 if(out_max_sz < 4)
620 {
621 logf("cop read failed: output buffer is too small\n");
622 return -1;
623 }
624#ifdef CPU_MIPS
625 if(args[HWSTUB_COP_MIPS_COP] != 0)
626 {
627 logf("cop read failed: only mips cp0 is supported\n");
628 return -2;
629 }
630 *(uint32_t *)out_data = mips_read_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL]);
631 return 4;
632#else
633 (void) args;
634 (void) out_data;
635 (void) out_max_sz;
636 logf("cop read failed: unsupported cpu\n");
637 return -1;
638#endif
639}
640
641/* coprocessor write: return <0 on error (-2 for dull dump), or 0 on success */
642int cop_write(uint8_t args[HWSTUB_COP_ARGS], const void *in_data, size_t in_sz)
643{
644 /* virtually all targets do register-based operation, so 32-bit */
645 if(in_sz != 4)
646 {
647 logf("cop read failed: input buffer has wrong size\n");
648 return -1;
649 }
650#ifdef CPU_MIPS
651 if(args[HWSTUB_COP_MIPS_COP] != 0)
652 {
653 logf("cop read failed: only mips cp0 is supported\n");
654 return -2;
655 }
656 mips_write_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL], *(uint32_t *)in_data);
657 return 0;
658#else
659 (void) args;
660 (void) in_data;
661 (void) in_sz;
662 logf("cop write failed: unsupported cpu\n");
663 return -1;
664#endif
665}
666
667/* return size to return to host or <0 on error */
668int do_cop_op(struct hwstub_cop_req_t *cop, void *in_data, size_t in_sz,
669 void *out_data, size_t out_max_sz)
670{
671 int ret = -2; /* -2 means full debug dump */
672 /* handle operations */
673 if(cop->bOp == HWSTUB_COP_READ)
674 {
675 /* read cannot have extra data */
676 if(in_sz > 0)
677 goto Lerr;
678 ret = cop_read(cop->bArgs, out_data, out_max_sz);
679 }
680 else if(cop->bOp == HWSTUB_COP_WRITE)
681 {
682 ret = cop_write(cop->bArgs, in_data, in_sz);
683 }
684
685Lerr:
686 if(ret == -2)
687 {
688 /* debug output */
689 logf("invalid cop op: %d, ", cop->bOp);
690 for(int i = 0; i < HWSTUB_COP_ARGS; i++)
691 logf("%c0x%x", i == 0 ? '[' : ',', cop->bArgs[i]);
692 logf("] in:%d\n", in_sz);
693 }
694 return ret;
695}
696
697static void handle_cop(struct usb_ctrlrequest *req)
698{
699 int size = usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength);
700 int hdr_sz = sizeof(struct hwstub_cop_req_t);
701 asm volatile("nop" : : : "memory");
702 struct hwstub_cop_req_t *cop = (void *)usb_buffer;
703 /* request should at least contain the header */
704 if(size < hdr_sz)
705 return usb_drv_stall(EP_CONTROL, true, true);
706 /* perform coprocessor operation: put output buffer after the input one,
707 * limit output buffer size to maximum buffer size */
708 uint8_t *in_buf = usb_buffer + hdr_sz;
709 size_t in_sz = req->wLength - hdr_sz;
710 uint8_t *out_buf = in_buf + in_sz;
711 size_t out_max_sz = usb_buffer_size - req->wLength;
712 int ret = do_cop_op(cop, in_buf, in_sz, out_buf, out_max_sz);
713 /* STALL on error */
714 if(ret < 0)
715 return usb_drv_stall(EP_CONTROL, true, true);
716 /* acknowledge */
717 usb_drv_send(EP_CONTROL, NULL, 0);
718 /* if there is a read stage, prepare everything for the READ2 */
719 if(ret > 0)
720 {
721 last_read_id = req->wValue;
722 last_read_addr = out_buf;
723 last_read_max_size = ret;
724 }
725}
726
565static void handle_class_intf_req(struct usb_ctrlrequest *req) 727static void handle_class_intf_req(struct usb_ctrlrequest *req)
566{ 728{
567 unsigned intf = req->wIndex & 0xff; 729 unsigned intf = req->wIndex & 0xff;
@@ -581,6 +743,8 @@ static void handle_class_intf_req(struct usb_ctrlrequest *req)
581 return handle_write(req); 743 return handle_write(req);
582 case HWSTUB_EXEC: 744 case HWSTUB_EXEC:
583 return handle_exec(req); 745 return handle_exec(req);
746 case HWSTUB_COPROCESSOR_OP:
747 return handle_cop(req);
584 default: 748 default:
585 usb_drv_stall(EP_CONTROL, true, true); 749 usb_drv_stall(EP_CONTROL, true, true);
586 } 750 }
diff --git a/utils/hwstub/tools/hwstub_shell.cpp b/utils/hwstub/tools/hwstub_shell.cpp
index b2cf2cec60..336b7fed6b 100644
--- a/utils/hwstub/tools/hwstub_shell.cpp
+++ b/utils/hwstub/tools/hwstub_shell.cpp
@@ -310,9 +310,11 @@ int my_lua_call(lua_State *state)
310{ 310{
311 int n = lua_gettop(state); 311 int n = lua_gettop(state);
312 if(n != 1) 312 if(n != 1)
313 luaL_error(state, "call takes target address argument"); 313 luaL_error(state, "call takes one argument: target address");
314 314
315 g_hwdev->exec(mylua_checkunsigned(state, 1), HWSTUB_EXEC_CALL); 315 error ret = g_hwdev->exec(luaL_checkunsigned(state, 1), HWSTUB_EXEC_CALL);
316 if(ret != error::SUCCESS)
317 luaL_error(state, "call failed");
316 return 0; 318 return 0;
317} 319}
318 320
@@ -320,9 +322,67 @@ int my_lua_jump(lua_State *state)
320{ 322{
321 int n = lua_gettop(state); 323 int n = lua_gettop(state);
322 if(n != 1) 324 if(n != 1)
323 luaL_error(state, "jump takes target address argument"); 325 luaL_error(state, "jump takes one argument: target address");
326
327 error ret = g_hwdev->exec(luaL_checkunsigned(state, 1), HWSTUB_EXEC_JUMP);
328 if(ret != error::SUCCESS)
329 luaL_error(state, "jump failed");
330 return 0;
331}
332
333int my_lua_read32_cop(lua_State *state)
334{
335 int n = lua_gettop(state);
336 if(n != 1)
337 luaL_error(state, "read32_cop takes one argument: arguments (array)");
338 uint8_t args[HWSTUB_COP_ARGS] = {0};
339 /* parse array */
340 luaL_checktype(state, 1, LUA_TTABLE);
341 size_t sz = lua_rawlen(state, 1);
342 if(sz > HWSTUB_COP_ARGS)
343 luaL_error(state, "coprocessor operation take at most %d arguments", HWSTUB_COP_ARGS);
344 for(size_t i = 0; i < sz; i++)
345 {
346 lua_pushinteger(state, i + 1); /* index start at 1 */
347 lua_gettable(state, 1);
348 /* check it is an integer */
349 args[i] = luaL_checkunsigned(state, -1);
350 /* pop it */
351 lua_pop(state, 1);
352 }
324 353
325 g_hwdev->exec(mylua_checkunsigned(state, 1), HWSTUB_EXEC_JUMP); 354 uint32_t val;
355 error ret = g_hwdev->read32_cop(args, val);
356 if(ret != error::SUCCESS)
357 luaL_error(state, "coprocessor read failed");
358 lua_pushunsigned(state, val);
359 return 1;
360}
361
362int my_lua_write32_cop(lua_State *state)
363{
364 int n = lua_gettop(state);
365 if(n != 2)
366 luaL_error(state, "write32_cop takes two arguments: arguments (array) and value");
367 uint8_t args[HWSTUB_COP_ARGS] = {0};
368 /* parse array */
369 luaL_checktype(state, 1, LUA_TTABLE);
370 size_t sz = lua_rawlen(state, 1);
371 if(sz > HWSTUB_COP_ARGS)
372 luaL_error(state, "coprocessor operation take at most %d arguments", HWSTUB_COP_ARGS);
373 for(size_t i = 0; i < sz; i++)
374 {
375 lua_pushinteger(state, i + 1); /* index start at 1 */
376 lua_gettable(state, 1);
377 /* check it is an integer */
378 args[i] = luaL_checkunsigned(state, -1);
379 /* pop it */
380 lua_pop(state, 1);
381 }
382
383 error ret = g_hwdev->write32_cop(args, luaL_checkunsigned(state, 2));
384 if(ret != error::SUCCESS)
385 luaL_error(state, "coprocessor write failed");
326 return 0; 386 return 0;
327} 387}
328 388
@@ -753,6 +813,10 @@ bool my_lua_import_hwstub()
753 lua_setfield(g_lua, -2, "call"); 813 lua_setfield(g_lua, -2, "call");
754 lua_pushcclosure(g_lua, my_lua_jump, 0); 814 lua_pushcclosure(g_lua, my_lua_jump, 0);
755 lua_setfield(g_lua, -2, "jump"); 815 lua_setfield(g_lua, -2, "jump");
816 lua_pushcclosure(g_lua, my_lua_read32_cop, 0);
817 lua_setfield(g_lua, -2, "read32_cop");
818 lua_pushcclosure(g_lua, my_lua_write32_cop, 0);
819 lua_setfield(g_lua, -2, "write32_cop");
756 820
757 lua_setfield(g_lua, -2, "dev"); 821 lua_setfield(g_lua, -2, "dev");
758 822