return halted signal if step/continue fails
[openocd.git] / src / server / gdb_server.c
index 9a5b81a49360d19e188c7931460a734b79bd40af..59ce46472b240a90ee5686c3104530688fd6e265 100644 (file)
@@ -2,6 +2,9 @@
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
+ *   Copyright (C) 2007,2008 Ã˜yvind Harboe                                      *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
  *   the Free Software Foundation; either version 2 of the License, or     *
@@ -63,8 +66,10 @@ enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;
 
 /* set if we are sending a memory map to gdb
  * via qXfer:memory-map:read packet */
-int gdb_use_memory_map = 0;
-int gdb_flash_program = 0;
+/* enabled by default*/
+int gdb_use_memory_map = 1;
+/* enabled by default*/
+int gdb_flash_program = 1;
 
 /* if set, data aborts cause an error to be reported in memory read packets
  * see the code in gdb_read_memory_packet() for further explanations */
@@ -285,7 +290,6 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
         * however sometimes '-' is sent even though we've already received
         * an ACK (+) for everything we've sent off.
         */
-#ifndef _WIN32
        int gotdata;
        for (;;)
        {
@@ -297,7 +301,6 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
                        return retval;
                LOG_WARNING("Discard unexpected char %c", reply);
        }
-#endif
 #endif
 
        while (1)
@@ -411,6 +414,8 @@ int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
                                case '$':
                                        break;
                                case '+':
+                                       /* gdb sends a dummy ack '+' at every remote connect - see remote_start_remote (remote.c)
+                                        * incase anyone tries to debug why they receive this warning every time */
                                        LOG_WARNING("acknowledgment received, but no packet pending");
                                        break;
                                case '-':
@@ -566,7 +571,7 @@ int gdb_output_con(connection_t *connection, const char* line)
        return ERROR_OK;
 }
 
-int gdb_output(struct command_context_s *context, char* line)
+int gdb_output(struct command_context_s *context, const char* line)
 {
        /* this will be dumped to the log and also sent as an O packet if possible */
        LOG_USER_N("%s", line);
@@ -575,66 +580,58 @@ int gdb_output(struct command_context_s *context, char* line)
 
 int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)
 {
-       FILE *script;
        struct command_context_s *cmd_ctx = priv;
 
-       if (target->gdb_program_script)
+       target_invoke_script(cmd_ctx, target, "gdb_program");
+       jtag_execute_queue();
+
+       return ERROR_OK;
+}
+
+static void gdb_frontend_halted(struct target_s *target, connection_t *connection)
+{
+       gdb_connection_t *gdb_connection = connection->priv;
+       
+       /* In the GDB protocol when we are stepping or coninuing execution,
+        * we have a lingering reply. Upon receiving a halted event
+        * when we have that lingering packet, we reply to the original
+        * step or continue packet.
+        *
+        * Executing monitor commands can bring the target in and
+        * out of the running state so we'll see lots of TARGET_EVENT_XXX
+        * that are to be ignored.
+        */
+       if (gdb_connection->frontend_state == TARGET_RUNNING)
        {
-               script = open_file_from_path(target->gdb_program_script, "r");
-               if (!script)
+               char sig_reply[4];
+               int signal;
+               /* stop forwarding log packets! */
+               log_remove_callback(gdb_log_callback, connection);
+
+               if (gdb_connection->ctrl_c)
                {
-                       LOG_ERROR("couldn't open script file %s", target->gdb_program_script);
-                               return ERROR_OK;
+                       signal = 0x2;
+                       gdb_connection->ctrl_c = 0;
+               }
+               else
+               {
+                       signal = gdb_last_signal(target);
                }
 
-               LOG_INFO("executing gdb_program script '%s'", target->gdb_program_script);
-               command_run_file(cmd_ctx, script, COMMAND_EXEC);
-               fclose(script);
-
-               jtag_execute_queue();
+               snprintf(sig_reply, 4, "T%2.2x", signal);
+               gdb_put_packet(connection, sig_reply, 3);
+               gdb_connection->frontend_state = TARGET_HALTED;
        }
-
-       return ERROR_OK;
 }
 
 int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
 {
        connection_t *connection = priv;
-       gdb_connection_t *gdb_connection = connection->priv;
-       char sig_reply[4];
-       int signal;
 
        switch (event)
        {
                case TARGET_EVENT_HALTED:
-                       /* In the GDB protocol when we are stepping or coninuing execution,
-                        * we have a lingering reply. Upon receiving a halted event
-                        * when we have that lingering packet, we reply to the original
-                        * step or continue packet.
-                        *
-                        * Executing monitor commands can bring the target in and
-                        * out of the running state so we'll see lots of TARGET_EVENT_XXX
-                        * that are to be ignored.
-                        */
-                       if (gdb_connection->frontend_state == TARGET_RUNNING)
-                       {
-                               /* stop forwarding log packets! */
-                               log_remove_callback(gdb_log_callback, connection);
-
-                               if (gdb_connection->ctrl_c)
-                               {
-                                       signal = 0x2;
-                                       gdb_connection->ctrl_c = 0;
-                               }
-                               else
-                               {
-                                       signal = gdb_last_signal(target);
-                               }
-
-                               snprintf(sig_reply, 4, "T%2.2x", signal);
-                               gdb_put_packet(connection, sig_reply, 3);
-                               gdb_connection->frontend_state = TARGET_HALTED;
-                       }
+                       gdb_frontend_halted(target, connection);
                        break;
                case TARGET_EVENT_GDB_PROGRAM:
                        gdb_program_handler(target, event, connection->cmd_ctx);
@@ -690,7 +687,13 @@ int gdb_new_connection(connection_t *connection)
         * instantaneous and thus avoiding annoying timeout problems during
         * connect. 
         */
-       gdb_service->target->type->halt(gdb_service->target);
+       target_halt(gdb_service->target);
+       /* FIX!!!! could extended-remote work better here?
+        * 
+        *  wait a tiny bit for halted state or we just continue. The
+        * GDB register packet will then contain garbage 
+        */
+       target_wait_state(gdb_service->target, TARGET_HALTED, 500);
        
        /* remove the initial ACK from the incoming buffer */
        if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
@@ -1211,10 +1214,11 @@ int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, c
        return ERROR_OK;
 }
 
-void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
+int gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        int current = 0;
        u32 address = 0x0;
+       int retval=ERROR_OK;
 
        LOG_DEBUG("-");
 
@@ -1231,13 +1235,15 @@ void gdb_step_continue_packet(connection_t *connection, target_t *target, char *
        if (packet[0] == 'c')
        {
                LOG_DEBUG("continue");
-               target->type->resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */
+               target_invoke_script(connection->cmd_ctx, target, "pre_resume");
+               retval=target_resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */
        }
        else if (packet[0] == 's')
        {
                LOG_DEBUG("step");
-               target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */
+               retval=target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */
        }
+       return retval;
 }
 
 int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
@@ -1417,6 +1423,24 @@ int gdb_calc_blocksize(flash_bank_t *bank)
        return block_size;
 }
 
+static int compare_bank (const void * a, const void * b)
+{
+       flash_bank_t *b1, *b2;
+       b1=*((flash_bank_t **)a);
+       b2=*((flash_bank_t **)b);
+       
+       if (b1->base==b2->base)
+       {
+               return 0;
+       } else if (b1->base>b2->base)
+       {
+               return 1;
+       } else
+       {
+               return -1;
+       }
+}
+
 int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
 {
        command_context_t *cmd_ctx = connection->cmd_ctx;
@@ -1498,7 +1522,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
 
                xml_printf(&retval, &buffer, &pos, &size,
                                "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-",
-                               (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-');
+                               (GDB_BUFFER_SIZE - 1), ((gdb_use_memory_map == 1)&&(flash_get_bank_count()>0)) ? '+' : '-');
 
                if (retval != ERROR_OK)
                {
@@ -1511,7 +1535,7 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
 
                return ERROR_OK;
        }
-       else if (strstr(packet, "qXfer:memory-map:read::"))
+       else if (strstr(packet, "qXfer:memory-map:read::")&&(flash_get_bank_count()>0))
        {
                /* We get away with only specifying flash here. Regions that are not
                 * specified are treated as if we provided no memory map(if not we
@@ -1537,23 +1561,63 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i
                length = strtoul(separator + 1, &separator, 16);
 
                xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");
-
+       
+               /* 
+               sort banks in ascending order, we need to make non-flash memory be ram(or rather
+               read/write) by default for GDB.
+               GDB does not have a concept of non-cacheable read/write memory.
+                */
+               flash_bank_t **banks=malloc(sizeof(flash_bank_t *)*flash_get_bank_count());
                int i;
+               
                for (i=0; i<flash_get_bank_count(); i++)
                {
                        p = get_flash_bank_by_num(i);
                        if (p == NULL)
-                               break;
-
+                       {
+                               free(banks);
+                               retval = ERROR_FAIL;
+                               gdb_send_error(connection, retval);
+                               return retval;
+                       }
+                       banks[i]=p;
+               }
+               
+               qsort(banks, flash_get_bank_count(), sizeof(flash_bank_t *), compare_bank);
+               
+               u32 ram_start=0;
+               for (i=0; i<flash_get_bank_count(); i++)
+               {
+                       p = banks[i];
+                       
+                       if (ram_start<p->base)
+                       {
+                               xml_printf(&retval, &xml, &pos, &size, "<memory type=\"ram\" start=\"0x%x\" length=\"0x%x\"/>\n",
+                                       ram_start, p->base-ram_start);
+                       }
+                       
                        /* if device has uneven sector sizes, eg. str7, lpc
                         * we pass the smallest sector size to gdb memory map */
                        blocksize = gdb_calc_blocksize(p);
-
+       
                        xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" start=\"0x%x\" length=\"0x%x\">\n" \
                                "<property name=\"blocksize\">0x%x</property>\n" \
                                "</memory>\n", \
                                p->base, p->size, blocksize);
+                       ram_start=p->base+p->size;                      
                }
+               if (ram_start!=0)
+               {
+                       xml_printf(&retval, &xml, &pos, &size, "<memory type=\"ram\" start=\"0x%x\" length=\"0x%x\"/>\n",
+                               ram_start, 0-ram_start);
+               } else
+               {
+                       /* a flash chip could be at the very end of the 32 bit address space, in which case
+                       ram_start will be precisely 0 */
+               }
+               
+               free(banks);
+               banks = NULL;
 
                xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
 
@@ -1755,15 +1819,17 @@ int gdb_detach(connection_t *connection, target_t *target)
        switch( detach_mode )
        {
                case GDB_DETACH_RESUME:
-                       target->type->resume(target, 1, 0, 1, 0);
+                       target_invoke_script(connection->cmd_ctx, target, "pre_resume");
+                       target_resume(target, 1, 0, 1, 0);
                        break;
 
                case GDB_DETACH_RESET:
-                       target_process_reset(connection->cmd_ctx);
+                       /* FIX?? make this configurable?? */
+                       target_process_reset(connection->cmd_ctx, RESET_HALT);
                        break;
 
                case GDB_DETACH_HALT:
-                       target->type->halt(target);
+                       target_halt(target);
                        break;
 
                case GDB_DETACH_NOTHING:
@@ -1790,11 +1856,22 @@ static void gdb_log_callback(void *priv, const char *file, int line,
        gdb_output_con(connection, string);
 }
 
+/* Do not allocate this on the stack */
+char gdb_packet_buffer[GDB_BUFFER_SIZE];
+
+static void gdb_sig_halted(connection_t *connection)
+{
+       char sig_reply[4];
+       snprintf(sig_reply, 4, "T%2.2x", 2);
+       gdb_put_packet(connection, sig_reply, 3);
+
+}
+
 int gdb_input_inner(connection_t *connection)
 {
        gdb_service_t *gdb_service = connection->service->priv;
        target_t *target = gdb_service->target;
-       char packet[GDB_BUFFER_SIZE];
+       char *packet=gdb_packet_buffer;
        int packet_size;
        int retval;
        gdb_connection_t *gdb_con = connection->priv;
@@ -1855,12 +1932,27 @@ int gdb_input_inner(connection_t *connection)
                                case 'c':
                                case 's':
                                        {
-                                       /* We're running/stepping, in which case we can
-                                        * forward log output until the target is halted */
-                                               gdb_connection_t *gdb_con = connection->priv;
-                                               gdb_con->frontend_state = TARGET_RUNNING;
-                                               log_add_callback(gdb_log_callback, connection);
-                                               gdb_step_continue_packet(connection, target, packet, packet_size);
+                                               if (target->state != TARGET_HALTED)
+                                               {
+                                                       /* If the target isn't in the halted state, then we can't
+                                                        * step/continue. This might be early setup, etc.
+                                                        */
+                                                       gdb_sig_halted(connection);
+                                               } else
+                                               {
+                                                       /* We're running/stepping, in which case we can
+                                                        * forward log output until the target is halted 
+                                                        */
+                                                       gdb_connection_t *gdb_con = connection->priv;
+                                                       gdb_con->frontend_state = TARGET_RUNNING;
+                                                       log_add_callback(gdb_log_callback, connection);
+                                                       int retval=gdb_step_continue_packet(connection, target, packet, packet_size);
+                                                       if (retval!=ERROR_OK)
+                                                       {
+                                                               /* we'll never receive a halted condition... issue a false one.. */
+                                                               gdb_frontend_halted(target, connection); 
+                                                       }
+                                               }
                                        }
                                        break;
                                case 'v':
@@ -1886,7 +1978,7 @@ int gdb_input_inner(connection_t *connection)
                                        break;
                                case 'R':
                                        /* handle extended restart packet */
-                                       target_process_reset(connection->cmd_ctx);
+                                       command_run_linef(connection->cmd_ctx, "ocd_gdb_restart %d", get_num_by_target(target));
                                        break;
                                default:
                                        /* ignore unkown packets */
@@ -1904,7 +1996,7 @@ int gdb_input_inner(connection_t *connection)
                {
                        if (target->state == TARGET_RUNNING)
                        {
-                               target->type->halt(target);
+                               target_halt(target);
                                gdb_con->ctrl_c = 0;
                        }
                }
@@ -1929,7 +2021,7 @@ int gdb_input(connection_t *connection)
        return ERROR_OK;
 }
 
-int gdb_init()
+int gdb_init(void)
 {
        gdb_service_t *gdb_service;
        target_t *target = targets;

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)