* gdb.base/display.c (do_loops): Add float variable `f'.
[deliverable/binutils-gdb.git] / gdb / go32-nat.c
index c9d02d1af62dda82c48d3377281bdd0feb96efe7..3f56088aed3619f0ac80cb2e8b53f368b734d9ab 100644 (file)
 #include <unistd.h>
 #include <sys/utsname.h>
 #include <io.h>
-/* breakpoint.h defines `disable' which is an enum member.  */
-#define disable interrup_disable
 #include <dos.h>
-#undef disable
 #include <dpmi.h>
 #include <go32.h>
+#include <sys/farptr.h>
 #include <debug/v2load.h>
 #include <debug/dbgcom.h>
 #if __DJGPP_MINOR__ > 2
@@ -588,6 +586,7 @@ go32_create_inferior (char *exec_file, char *args, char **env)
   jmp_buf start_state;
   char *cmdline;
   char **env_save = environ;
+  size_t cmdlen;
 
   /* If no exec file handed to us, get it from the exec-file command -- with
      a good, common error message if none is specified.  */
@@ -622,10 +621,24 @@ go32_create_inferior (char *exec_file, char *args, char **env)
   else
     child_cmd.command = xstrdup (args);
 
-  cmdline = (char *) alloca (strlen (args) + 4);
-  cmdline[0] = strlen (args);
+  cmdlen = strlen (args);
+  /* v2loadimage passes command lines via DOS memory, so it cannot
+     possibly handle commands longer than 1MB.  */
+  if (cmdlen > 1024*1024)
+    error ("Command line too long.");
+
+  cmdline = xmalloc (cmdlen + 4);
   strcpy (cmdline + 1, args);
-  cmdline[strlen (args) + 1] = 13;
+  /* If the command-line length fits into DOS 126-char limits, use the
+     DOS command tail format; otherwise, tell v2loadimage to pass it
+     through a buffer in conventional memory.  */
+  if (cmdlen < 127)
+    {
+      cmdline[0] = strlen (args);
+      cmdline[cmdlen + 1] = 13;
+    }
+  else
+    cmdline[0] = 0xff; /* signal v2loadimage it's a long command */
 
   environ = env;
 
@@ -636,6 +649,7 @@ go32_create_inferior (char *exec_file, char *args, char **env)
       exit (1);
     }
   environ = env_save;
+  xfree (cmdline);
 
   edi_init (start_state);
 #if __DJGPP_MINOR__ < 3
@@ -1278,6 +1292,7 @@ static int
 read_memory_region (unsigned long addr, void *dest, size_t len)
 {
   unsigned long dos_ds_limit = __dpmi_get_segment_limit (_dos_ds);
+  int retval = 1;
 
   /* For the low memory, we can simply use _dos_ds.  */
   if (addr <= dos_ds_limit - len)
@@ -1288,14 +1303,43 @@ read_memory_region (unsigned long addr, void *dest, size_t len)
         be able to access that memory.  */
       int sel = __dpmi_allocate_ldt_descriptors (1);
 
-      if (sel <= 0
-         || __dpmi_set_segment_base_address (sel, addr) == -1
-         || __dpmi_set_segment_limit (sel, len - 1) == -1)
-       return 0;
-      movedata (sel, 0, _my_ds (), (unsigned)dest, len);
-      __dpmi_free_ldt_descriptor (sel);
+      if (sel <= 0)
+       retval = 0;
+      else
+       {
+         int access_rights = __dpmi_get_descriptor_access_rights (sel);
+         size_t segment_limit = len - 1;
+
+         /* Make sure the crucial bits in the descriptor access
+            rights are set correctly.  Some DPMI providers might barf
+            if we set the segment limit to something that is not an
+            integral multiple of 4KB pages if the granularity bit is
+            not set to byte-granular, even though the DPMI spec says
+            it's the host's responsibility to set that bit correctly.  */
+         if (len > 1024 * 1024)
+           {
+             access_rights |= 0x8000;
+             /* Page-granular segments should have the low 12 bits of
+                the limit set.  */
+             segment_limit |= 0xfff;
+           }
+         else
+           access_rights &= ~0x8000;
+
+         if (__dpmi_set_segment_base_address (sel, addr) != -1
+             && __dpmi_set_descriptor_access_rights (sel, access_rights) != -1
+             && __dpmi_set_segment_limit (sel, segment_limit) != -1
+             /* W2K silently fails to set the segment limit, leaving
+                it at zero; this test avoids the resulting crash.  */
+             && __dpmi_get_segment_limit (sel) >= segment_limit)
+           movedata (sel, 0, _my_ds (), (unsigned)dest, len);
+         else
+           retval = 0;
+
+         __dpmi_free_ldt_descriptor (sel);
+       }
     }
-  return 1;
+  return retval;
 }
 
 /* Get a segment descriptor stored at index IDX in the descriptor
@@ -1587,7 +1631,7 @@ go32_sidt (char *arg, int from_tty)
        {
          idt_entry = parse_and_eval_long (arg);
          if (idt_entry < 0)
-           error ("Invalid (negative) IDT entry 0x%03x.", idt_entry);
+           error ("Invalid (negative) IDT entry %d.", idt_entry);
        }
     }
 
@@ -1613,6 +1657,245 @@ go32_sidt (char *arg, int from_tty)
     }
 }
 
+/* Cached linear address of the base of the page directory.  For
+   now, available only under CWSDPMI.  Code based on ideas and
+   suggestions from Charles Sandmann <sandmann@clio.rice.edu>.  */
+static unsigned long pdbr;
+
+static unsigned long
+get_cr3 (void)
+{
+  unsigned offset;
+  unsigned taskreg;
+  unsigned long taskbase, cr3;
+  struct dtr_reg gdtr;
+
+  if (pdbr > 0 && pdbr <= 0xfffff)
+    return pdbr;
+
+  /* Get the linear address of GDT and the Task Register.  */
+  __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
+  __asm__ __volatile__ ("str    %0" : "=m" (taskreg) : /* no inputs */ );
+
+  /* Task Register is a segment selector for the TSS of the current
+     task.  Therefore, it can be used as an index into the GDT to get
+     at the segment descriptor for the TSS.  To get the index, reset
+     the low 3 bits of the selector (which give the CPL).  Add 2 to the
+     offset to point to the 3 low bytes of the base address.  */
+  offset = gdtr.base + (taskreg & 0xfff8) + 2;
+
+
+  /* CWSDPMI's task base is always under the 1MB mark.  */
+  if (offset > 0xfffff)
+    return 0;
+
+  _farsetsel (_dos_ds);
+  taskbase  = _farnspeekl (offset) & 0xffffffU;
+  taskbase += _farnspeekl (offset + 2) & 0xff000000U;
+  if (taskbase > 0xfffff)
+    return 0;
+
+  /* CR3 (a.k.a. PDBR, the Page Directory Base Register) is stored at
+     offset 1Ch in the TSS.  */
+  cr3 = _farnspeekl (taskbase + 0x1c) & ~0xfff;
+  if (cr3 > 0xfffff)
+    {
+#if 0  /* not fully supported yet */
+      /* The Page Directory is in UMBs.  In that case, CWSDPMI puts
+        the first Page Table right below the Page Directory.  Thus,
+        the first Page Table's entry for its own address and the Page
+        Directory entry for that Page Table will hold the same
+        physical address.  The loop below searches the entire UMB
+        range of addresses for such an occurence.  */
+      unsigned long addr, pte_idx;
+
+      for (addr = 0xb0000, pte_idx = 0xb0;
+          pte_idx < 0xff;
+          addr += 0x1000, pte_idx++)
+       {
+         if (((_farnspeekl (addr + 4 * pte_idx) & 0xfffff027) ==
+              (_farnspeekl (addr + 0x1000) & 0xfffff027))
+             && ((_farnspeekl (addr + 4 * pte_idx + 4) & 0xfffff000) == cr3))
+           {
+             cr3 = addr + 0x1000;
+             break;
+           }
+       }
+#endif
+
+      if (cr3 > 0xfffff)
+       cr3 = 0;
+    }
+
+  return cr3;
+}
+
+/* Return the N'th Page Directory entry.  */
+static unsigned long
+get_pde (int n)
+{
+  unsigned long pde = 0;
+
+  if (pdbr && n >= 0 && n < 1024)
+    {
+      pde = _farpeekl (_dos_ds, pdbr + 4*n);
+    }
+  return pde;
+}
+
+/* Return the N'th entry of the Page Table whose Page Directory entry
+   is PDE.  */
+static unsigned long
+get_pte (unsigned long pde, int n)
+{
+  unsigned long pte = 0;
+
+  /* pde & 0x80 tests the 4MB page bit.  We don't support 4MB
+     page tables, for now.  */
+  if ((pde & 1) && !(pde & 0x80) && n >= 0 && n < 1024)
+    {
+      pde &= ~0xfff;   /* clear non-address bits */
+      pte = _farpeekl (_dos_ds, pde + 4*n);
+    }
+  return pte;
+}
+
+/* Display a Page Directory or Page Table entry.  IS_DIR, if non-zero,
+   says this is a Page Directory entry.  If FORCE is non-zero, display
+   the entry even if its Present flag is off.  OFF is the offset of the
+   address from the page's base address.  */
+static void
+display_ptable_entry (unsigned long entry, int is_dir, int force, unsigned off)
+{
+  if ((entry & 1) != 0)
+    {
+      printf_filtered ("Base=0x%05lx000", entry >> 12);
+      if ((entry & 0x100) && !is_dir)
+       puts_filtered (" Global");
+      if ((entry & 0x40) && !is_dir)
+       puts_filtered (" Dirty");
+      printf_filtered (" %sAcc.", (entry & 0x20) ? "" : "Not-");
+      printf_filtered (" %sCached", (entry & 0x10) ? "" : "Not-");
+      printf_filtered (" Write-%s", (entry & 8) ? "Thru" : "Back");
+      printf_filtered (" %s", (entry & 4) ? "Usr" : "Sup");
+      printf_filtered (" Read-%s", (entry & 2) ? "Write" : "Only");
+      if (off)
+       printf_filtered (" +0x%x", off);
+      puts_filtered ("\n");
+    }
+  else if (force)
+    printf_filtered ("Page%s not present or not supported; value=0x%lx.\n",
+                    is_dir ? " Table" : "", entry >> 1);
+}
+
+static void
+go32_pde (char *arg, int from_tty)
+{
+  long pde_idx = -1, i;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         pde_idx = parse_and_eval_long (arg);
+         if (pde_idx < 0 || pde_idx >= 1024)
+           error ("Entry %ld is outside valid limits [0..1023].", pde_idx);
+       }
+    }
+
+  pdbr = get_cr3 ();
+  if (!pdbr)
+    puts_filtered ("Access to Page Directories is not supported on this system.\n");
+  else if (pde_idx >= 0)
+    display_ptable_entry (get_pde (pde_idx), 1, 1, 0);
+  else
+    for (i = 0; i < 1024; i++)
+      display_ptable_entry (get_pde (i), 1, 0, 0);
+}
+
+/* A helper function to display entries in a Page Table pointed to by
+   the N'th entry in the Page Directory.  If FORCE is non-zero, say
+   something even if the Page Table is not accessible.  */
+static void
+display_page_table (long n, int force)
+{
+  unsigned long pde = get_pde (n);
+
+  if ((pde & 1) != 0)
+    {
+      int i;
+
+      printf_filtered ("Page Table pointed to by Page Directory entry 0x%lx:\n", n);
+      for (i = 0; i < 1024; i++)
+       display_ptable_entry (get_pte (pde, i), 0, 0, 0);
+      puts_filtered ("\n");
+    }
+  else if (force)
+    printf_filtered ("Page Table not present; value=0x%lx.\n", pde >> 1);
+}
+
+static void
+go32_pte (char *arg, int from_tty)
+{
+  long pde_idx = -1, i;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         pde_idx = parse_and_eval_long (arg);
+         if (pde_idx < 0 || pde_idx >= 1024)
+           error ("Entry %d is outside valid limits [0..1023].", pde_idx);
+       }
+    }
+
+  pdbr = get_cr3 ();
+  if (!pdbr)
+    puts_filtered ("Access to Page Tables is not supported on this system.\n");
+  else if (pde_idx >= 0)
+    display_page_table (pde_idx, 1);
+  else
+    for (i = 0; i < 1024; i++)
+      display_page_table (i, 0);
+}
+
+static void
+go32_pte_for_address (char *arg, int from_tty)
+{
+  CORE_ADDR addr = 0, i;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       addr = parse_and_eval_address (arg);
+    }
+  if (!addr)
+    error_no_arg ("linear address");
+
+  pdbr = get_cr3 ();
+  if (!pdbr)
+    puts_filtered ("Access to Page Tables is not supported on this system.\n");
+  else
+    {
+      int pde_idx = (addr >> 22) & 0x3ff;
+      int pte_idx = (addr >> 12) & 0x3ff;
+      unsigned offs = addr & 0xfff;
+
+      printf_filtered ("Page Table entry for address 0x%llx:\n",
+                      (unsigned long long)addr);
+      display_ptable_entry (get_pte (get_pde (pde_idx), pte_idx), 0, 1, offs);
+    }
+}
+
 static struct cmd_list_element *info_dos_cmdlist = NULL;
 
 static void
@@ -1646,6 +1929,25 @@ _initialize_go32_nat (void)
           "Display entries in the IDT (Interrupt Descriptor Table).\n"
           "Entry number (an expression) as an argument means display only that entry.",
           &info_dos_cmdlist);
+  add_cmd ("pde", class_info, go32_pde,
+          "Display entries in the Page Directory.\n"
+          "Entry number (an expression) as an argument means display only that entry.",
+          &info_dos_cmdlist);
+  add_cmd ("pte", class_info, go32_pte,
+          "Display entries in Page Tables.\n"
+          "Entry number (an expression) as an argument means display only entries\n"
+          "from the Page Table pointed to by the specified Page Directory entry.",
+          &info_dos_cmdlist);
+  add_cmd ("address-pte", class_info, go32_pte_for_address,
+          "Display a Page Table entry for a linear address.\n"
+          "The address argument must be a linear address, after adding to\n"
+          "it the base address of the appropriate segment.\n"
+          "The base address of variables and functions in the debuggee's data\n"
+          "or code segment is stored in the variable __djgpp_base_address,\n"
+          "so use `__djgpp_base_address + (char *)&var' as the argument.\n"
+          "For other segments, look up their base address in the output of\n"
+          "the `info dos ldt' command.",
+          &info_dos_cmdlist);
 }
 
 pid_t
This page took 0.027741 seconds and 4 git commands to generate.