* go32-nat.c (go32_get_windows_version, print_mem, go32_sysinfo)
authorEli Zaretskii <eliz@gnu.org>
Sun, 8 Jul 2001 10:28:20 +0000 (10:28 +0000)
committerEli Zaretskii <eliz@gnu.org>
Sun, 8 Jul 2001 10:28:20 +0000 (10:28 +0000)
(read_memory_region, get_descriptor, display_descriptor)
(go32_sldt, go32_sgdt, go32_sidt): New functions.
(top-level): Include ctype.h, utsname.h, dos.h, and go32.h.  Ifdef
away `disable' from dos.h, since breakpoint.h defines an enum
member of the same name, and GCC 2.7.2 barfs.
(_initialize_go32_nat): Provide new commands dos-sysinfo, dos-ldt,
dos-gdt, and dos-idt, all of them in the "info" class

gdb/ChangeLog
gdb/go32-nat.c

index 1f99bed2959728f0e794a62ab6463586abf62bb9..ac621c7c7d4d8eaa33589cc259a23695945de1fd 100644 (file)
@@ -1,3 +1,14 @@
+2001-07-08  Eli Zaretskii  <eliz@is.elta.co.il>
+
+       * go32-nat.c (go32_get_windows_version, print_mem, go32_sysinfo)
+       (read_memory_region, get_descriptor, display_descriptor)
+       (go32_sldt, go32_sgdt, go32_sidt): New functions.
+       (top-level): Include ctype.h, utsname.h, dos.h, and go32.h.  Ifdef
+       away `disable' from dos.h, since breakpoint.h defines an enum
+       member of the same name, and GCC 2.7.2 barfs.
+       (_initialize_go32_nat): Provide new commands dos-sysinfo, dos-ldt,
+       dos-gdt, and dos-idt, all of them in the "info" class
+
 2001-07-07  Kevin Buettner  <kevinb@redhat.com>
 
        * procfs.c (create_procinfo): Allocate space for saved_entryset
index d7647e959eed31b2ff3d4485eaf70cc6753c9dfe..94b8a84b2b11c5024089e2bfe527d30a82e9f7eb 100644 (file)
 #include "regcache.h"
 #include "gdb_string.h"
 
-#include <stdio.h>             /* required for __DJGPP_MINOR__ */
+#include <stdio.h>             /* might be required for __DJGPP_MINOR__ */
 #include <stdlib.h>
+#include <ctype.h>
 #include <errno.h>
 #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 <debug/v2load.h>
 #include <debug/dbgcom.h>
 #if __DJGPP_MINOR__ > 2
@@ -878,11 +885,750 @@ init_go32_ops (void)
   processing_gcc_compilation = 2;
 }
 
+unsigned short windows_major, windows_minor;
+
+/* Compute the version Windows reports via Int 2Fh/AX=1600h.  */
+static void
+go32_get_windows_version(void)
+{
+  __dpmi_regs r;
+
+  r.x.ax = 0x1600;
+  __dpmi_int(0x2f, &r);
+  if (r.h.al > 2 && r.h.al != 0x80 && r.h.al != 0xff
+      && (r.h.al > 3 || r.h.ah > 0))
+    {
+      windows_major = r.h.al;
+      windows_minor = r.h.ah;
+    }
+  else
+    windows_major = 0xff;      /* meaning no Windows */
+}
+
+/* A subroutine of go32_sysinfo to display memory info.  */
+static void
+print_mem (unsigned long datum, const char *header, int in_pages_p)
+{
+  if (datum != 0xffffffffUL)
+    {
+      if (in_pages_p)
+       datum <<= 12;
+      puts_filtered (header);
+      if (datum > 1024)
+       {
+         printf_filtered ("%lu KB", datum >> 10);
+         if (datum > 1024 * 1024)
+           printf_filtered (" (%lu MB)", datum >> 20);
+       }
+      else
+       printf_filtered ("%lu Bytes", datum);
+      puts_filtered ("\n");
+    }
+}
+
+/* Display assorted information about the underlying OS.  */
+static void
+go32_sysinfo (char *arg, int from_tty)
+{
+  struct utsname u;
+  char cpuid_vendor[13];
+  unsigned cpuid_max = 0, cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx;
+  unsigned true_dos_version = _get_dos_version (1);
+  unsigned advertized_dos_version = ((unsigned int)_osmajor << 8) | _osminor;
+  int dpmi_flags;
+  char dpmi_vendor_info[129];
+  int dpmi_vendor_available =
+    __dpmi_get_capabilities (&dpmi_flags, dpmi_vendor_info);
+  __dpmi_version_ret dpmi_version_data;
+  long eflags;
+  __dpmi_free_mem_info mem_info;
+  __dpmi_regs regs;
+
+  cpuid_vendor[0] = '\0';
+  if (uname (&u))
+    strcpy (u.machine, "Unknown x86");
+  else if (u.machine[0] == 'i' && u.machine[1] > 4)
+    {
+      /* CPUID with EAX = 0 returns the Vendor ID.  */
+      __asm__ __volatile__ ("xorl   %%ebx, %%ebx;"
+                           "xorl   %%ecx, %%ecx;"
+                           "xorl   %%edx, %%edx;"
+                           "movl   $0,    %%eax;"
+                           "cpuid;"
+                           "movl   %%ebx,  %0;"
+                           "movl   %%edx,  %1;"
+                           "movl   %%ecx,  %2;"
+                           "movl   %%eax,  %3;"
+                           : "=m" (cpuid_vendor[0]),
+                             "=m" (cpuid_vendor[4]),
+                             "=m" (cpuid_vendor[8]),
+                             "=m" (cpuid_max)
+                           :
+                           : "%eax", "%ebx", "%ecx", "%edx");
+      cpuid_vendor[12] = '\0';
+    }
+
+  printf_filtered ("CPU Type.......................%s", u.machine);
+  if (cpuid_vendor[0])
+    printf_filtered (" (%s)", cpuid_vendor);
+  puts_filtered ("\n");
+
+  /* CPUID with EAX = 1 returns processor signature and features.  */
+  if (cpuid_max >= 1)
+    {
+      static char *brand_name[] = {
+       "",
+       " Celeron",
+       " III",
+       " III Xeon",
+       "", "", "", "",
+       " 4"
+      };
+      char cpu_string[80];
+      char cpu_brand[20];
+      unsigned brand_idx;
+      int intel_p = strcmp (cpuid_vendor, "GenuineIntel") == 0;
+      int amd_p = strcmp (cpuid_vendor, "AuthenticAMD") == 0;
+      unsigned cpu_family, cpu_model;
+
+      __asm__ __volatile__ ("movl   $1, %%eax;"
+                           "cpuid;"
+                           : "=a" (cpuid_eax),
+                             "=b" (cpuid_ebx),
+                             "=d" (cpuid_edx)
+                           :
+                           : "%ecx");
+      brand_idx = cpuid_ebx & 0xff;
+      cpu_family = (cpuid_eax >> 8) & 0xf;
+      cpu_model  = (cpuid_eax >> 4) & 0xf;
+      cpu_brand[0] = '\0';
+      if (intel_p)
+       {
+         if (brand_idx > 0
+             && brand_idx < sizeof(brand_name)/sizeof(brand_name[0])
+             && *brand_name[brand_idx])
+           strcpy (cpu_brand, brand_name[brand_idx]);
+         else if (cpu_family == 5)
+           {
+             if (((cpuid_eax >> 12) & 3) == 0 && cpu_model == 4)
+               strcpy (cpu_brand, " MMX");
+             else if (cpu_model > 1 && ((cpuid_eax >> 12) & 3) == 1)
+               strcpy (cpu_brand, " OverDrive");
+             else if (cpu_model > 1 && ((cpuid_eax >> 12) & 3) == 2)
+               strcpy (cpu_brand, " Dual");
+           }
+         else if (cpu_family == 6 && cpu_model < 8)
+           {
+             switch (cpu_model)
+               {
+                 case 1:
+                   strcpy (cpu_brand, " Pro");
+                   break;
+                 case 3:
+                   strcpy (cpu_brand, " II");
+                   break;
+                 case 5:
+                   strcpy (cpu_brand, " II Xeon");
+                   break;
+                 case 6:
+                   strcpy (cpu_brand, " Celeron");
+                   break;
+                 case 7:
+                   strcpy (cpu_brand, " III");
+                   break;
+               }
+           }
+       }
+      else if (amd_p)
+       {
+         switch (cpu_family)
+           {
+             case 4:
+               strcpy (cpu_brand, "486/5x86");
+               break;
+             case 5:
+               switch (cpu_model)
+                 {
+                   case 0:
+                   case 1:
+                   case 2:
+                   case 3:
+                     strcpy (cpu_brand, "-K5");
+                     break;
+                   case 6:
+                   case 7:
+                     strcpy (cpu_brand, "-K6");
+                     break;
+                   case 8:
+                     strcpy (cpu_brand, "-K6-2");
+                     break;
+                   case 9:
+                     strcpy (cpu_brand, "-K6-III");
+                     break;
+                 }
+               break;
+             case 6:
+               switch (cpu_model)
+                 {
+                   case 1:
+                   case 2:
+                   case 4:
+                     strcpy (cpu_brand, " Athlon");
+                     break;
+                   case 3:
+                     strcpy (cpu_brand, " Duron");
+                     break;
+                 }
+               break;
+           }
+       }
+      sprintf (cpu_string, "%s%s Model %d Stepping %d",
+              intel_p ? "Pentium" : (amd_p ? "AMD" : "ix86"),
+              cpu_brand, cpu_model, cpuid_eax & 0xf);
+      printfi_filtered (31, "%s\n", cpu_string);
+      if (((cpuid_edx & (6 | (0x0d << 23))) != 0)
+         || ((cpuid_edx & 1) == 0)
+         || (amd_p && (cpuid_edx & (3 << 30)) != 0))
+       {
+         puts_filtered ("CPU Features...................");
+         /* We only list features which might be useful in the DPMI
+            environment.  */
+         if ((cpuid_edx & 1) == 0)
+           puts_filtered ("No FPU "); /* it's unusual to not have an FPU */
+         if ((cpuid_edx & (1 << 1)) != 0)
+           puts_filtered ("VME ");
+         if ((cpuid_edx & (1 << 2)) != 0)
+           puts_filtered ("DE ");
+         if ((cpuid_edx & (1 << 4)) != 0)
+           puts_filtered ("TSC ");
+         if ((cpuid_edx & (1 << 23)) != 0)
+           puts_filtered ("MMX ");
+         if ((cpuid_edx & (1 << 25)) != 0)
+           puts_filtered ("SSE ");
+         if ((cpuid_edx & (1 << 26)) != 0)
+           puts_filtered ("SSE2 ");
+         if (amd_p)
+           {
+             if ((cpuid_edx & (1 << 31)) != 0)
+               puts_filtered ("3DNow! ");
+             if ((cpuid_edx & (1 << 30)) != 0)
+               puts_filtered ("3DNow!Ext");
+           }
+         puts_filtered ("\n");
+       }
+    }
+  puts_filtered ("\n");
+  printf_filtered ("DOS Version....................%s %s.%s",
+                  _os_flavor, u.release, u.version);
+  if (true_dos_version != advertized_dos_version)
+    printf_filtered (" (disguised as v%d.%d)", _osmajor, _osminor);
+  puts_filtered ("\n");
+  if (!windows_major)
+    go32_get_windows_version ();
+  if (windows_major != 0xff)
+    {
+      const char *windows_flavor;
+
+      printf_filtered ("Windows Version................%d.%02d (Windows ",
+                      windows_major, windows_minor);
+      switch (windows_major)
+       {
+         case 3:
+           windows_flavor = "3.X";
+           break;
+         case 4:
+           switch (windows_minor)
+             {
+               case 0:
+                 windows_flavor = "95, 95A, or 95B";
+                 break;
+               case 3:
+                 windows_flavor = "95B OSR2.1 or 95C OSR2.5";
+                 break;
+               case 10:
+                 windows_flavor = "98 or 98 SE";
+                 break;
+               case 90:
+                 windows_flavor = "ME";
+                 break;
+               default:
+                 windows_flavor = "9X";
+                 break;
+             }
+           break;
+         default:
+           windows_flavor = "??";
+           break;
+       }
+      printf_filtered ("%s)\n", windows_flavor);
+    }
+  else if (true_dos_version == 0x532 && advertized_dos_version == 0x500)
+    printf_filtered ("Windows Version................Windows NT or Windows 2000\n");
+  puts_filtered ("\n");
+  if (dpmi_vendor_available == 0)
+    {
+      /* The DPMI spec says the vendor string should be ASCIIZ, but
+        I don't trust the vendors to follow that...  */
+      if (!memchr (&dpmi_vendor_info[2], 0, 126))
+       dpmi_vendor_info[128] = '\0';
+      printf_filtered ("DPMI Host......................%s v%d.%d (capabilities: %#x)\n",
+                      &dpmi_vendor_info[2],
+                      (unsigned)dpmi_vendor_info[0],
+                      (unsigned)dpmi_vendor_info[1],
+                      ((unsigned)dpmi_flags & 0x7f));
+    }
+  __dpmi_get_version (&dpmi_version_data);
+  printf_filtered ("DPMI Version...................%d.%02d\n",
+                  dpmi_version_data.major, dpmi_version_data.minor);
+  printf_filtered ("DPMI Info......................%s-bit DPMI, with%s Virtual Memory support\n",
+                  (dpmi_version_data.flags & 1) ? "32" : "16",
+                  (dpmi_version_data.flags & 4) ? "" : "out");
+  printfi_filtered (31, "Interrupts reflected to %s mode\n",
+                  (dpmi_version_data.flags & 2) ? "V86" : "Real");
+  printfi_filtered (31, "Processor type: i%d86\n",
+                  dpmi_version_data.cpu);
+  printfi_filtered (31, "PIC base interrupt: Master: %#x  Slave: %#x\n",
+                  dpmi_version_data.master_pic, dpmi_version_data.slave_pic);
+
+  /* a_tss is only initialized when the debuggee is first run.  */
+  if (prog_has_started)
+    {
+      __asm__ __volatile__ ("pushfl ; popl %0" : "=g" (eflags));
+      printf_filtered ("Protection.....................Ring %d (in %s), with%s I/O protection\n",
+                      a_tss.tss_cs & 3, (a_tss.tss_cs & 4) ? "LDT" : "GDT",
+                      (a_tss.tss_cs & 3) > ((eflags >> 12) & 3) ? "" : "out");
+    }
+  puts_filtered ("\n");
+  __dpmi_get_free_memory_information (&mem_info);
+  print_mem (mem_info.total_number_of_physical_pages,
+            "DPMI Total Physical Memory.....", 1);
+  print_mem (mem_info.total_number_of_free_pages,
+            "DPMI Free Physical Memory......", 1);
+  print_mem (mem_info.size_of_paging_file_partition_in_pages,
+            "DPMI Swap Space................", 1);
+  print_mem (mem_info.linear_address_space_size_in_pages,
+            "DPMI Total Linear Address Size.", 1);
+  print_mem (mem_info.free_linear_address_space_in_pages,
+            "DPMI Free Linear Address Size..", 1);
+  print_mem (mem_info.largest_available_free_block_in_bytes,
+            "DPMI Largest Free Memory Block.", 0);
+
+  regs.h.ah = 0x48;
+  regs.x.bx = 0xffff;
+  __dpmi_int (0x21, &regs);
+  print_mem (regs.x.bx << 4, "Free DOS Memory................", 0);
+  regs.x.ax = 0x5800;
+  __dpmi_int (0x21, &regs);
+  if ((regs.x.flags & 1) == 0)
+    {
+      static const char *dos_hilo[] = {
+       "Low", "", "", "", "High", "", "", "", "High, then Low"
+      };
+      static const char *dos_fit[] = {
+       "First", "Best", "Last"
+      };
+      int hilo_idx = (regs.x.ax >> 4) & 0x0f;
+      int fit_idx  = regs.x.ax & 0x0f;
+
+      if (hilo_idx > 8)
+       hilo_idx = 0;
+      if (fit_idx > 2)
+       fit_idx = 0;
+      printf_filtered ("DOS Memory Allocation..........%s memory, %s fit\n",
+                      dos_hilo[hilo_idx], dos_fit[fit_idx]);
+      regs.x.ax = 0x5802;
+      __dpmi_int (0x21, &regs);
+      if ((regs.x.flags & 1) != 0)
+       regs.h.al = 0;
+      printfi_filtered (31, "UMBs %sin DOS memory chain\n",
+                       regs.h.al == 0 ? "not " : "");
+    }
+}
+
+struct seg_descr {
+  unsigned short limit0          __attribute__((packed));
+  unsigned short base0           __attribute__((packed));
+  unsigned char  base1           __attribute__((packed));
+  unsigned       stype:5         __attribute__((packed));
+  unsigned       dpl:2           __attribute__((packed));
+  unsigned       present:1       __attribute__((packed));
+  unsigned       limit1:4        __attribute__((packed));
+  unsigned       available:1     __attribute__((packed));
+  unsigned       dummy:1         __attribute__((packed));
+  unsigned       bit32:1         __attribute__((packed));
+  unsigned       page_granular:1 __attribute__((packed));
+  unsigned char  base2           __attribute__((packed));
+};
+
+struct gate_descr {
+  unsigned short offset0         __attribute__((packed));
+  unsigned short selector        __attribute__((packed));
+  unsigned       param_count:5   __attribute__((packed));
+  unsigned       dummy:3         __attribute__((packed));
+  unsigned       stype:5         __attribute__((packed));
+  unsigned       dpl:2           __attribute__((packed));
+  unsigned       present:1       __attribute__((packed));
+  unsigned short offset1         __attribute__((packed));
+};
+
+/* Read LEN bytes starting at logical address ADDR, and put the result
+   into DEST.  Return 1 if success, zero if not.  */
+static int
+read_memory_region (unsigned long addr, void *dest, size_t len)
+{
+  unsigned long dos_ds_limit = __dpmi_get_segment_limit (_dos_ds);
+
+  /* For the low memory, we can simply use _dos_ds.  */
+  if (addr <= dos_ds_limit - len)
+    dosmemget (addr, len, dest);
+  else
+    {
+      /* For memory above 1MB we need to set up a special segment to
+        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);
+    }
+  return 1;
+}
+
+/* Get a segment descriptor stored at index IDX in the descriptor
+   table whose base address is TABLE_BASE.  Return the descriptor
+   type, or -1 if failure.  */
+static int
+get_descriptor (unsigned long table_base, int idx, void *descr)
+{
+  unsigned long addr = table_base + idx * 8; /* 8 bytes per entry */
+
+  if (read_memory_region (addr, descr, 8))
+    return (int)((struct seg_descr *)descr)->stype;
+  return -1;
+}
+
+struct dtr_reg {
+  unsigned short limit __attribute__((packed));
+  unsigned long  base  __attribute__((packed));
+};
+
+/* Display a segment descriptor stored at index IDX in a descriptor
+   table whose type is TYPE and whose base address is BASE_ADDR.  If
+   FORCE is non-zero, display even invalid descriptors.  */
+static void
+display_descriptor (unsigned type, unsigned long base_addr, int idx, int force)
+{
+  struct seg_descr descr;
+  struct gate_descr gate;
+
+  /* Get the descriptor from the table.  */
+  if (idx == 0 && type == 0)
+    puts_filtered ("0x000: null descriptor\n");
+  else if (get_descriptor (base_addr, idx, &descr) != -1)
+    {
+      /* For each type of descriptor table, this has a bit set if the
+        corresponding type of selectors is valid in that table.  */
+      static unsigned allowed_descriptors[] = {
+         0xffffdafeL,   /* GDT */
+         0x0000c0e0L,   /* IDT */
+         0xffffdafaL    /* LDT */
+      };
+
+      /* If the program hasn't started yet, assume the debuggee will
+        have the same CPL as the debugger.  */
+      int cpl = prog_has_started ? (a_tss.tss_cs & 3) : _my_cs () & 3;
+      unsigned long limit = (descr.limit1 << 16) | descr.limit0;
+
+      if (descr.present
+         && (allowed_descriptors[type] & (1 << descr.stype)) != 0)
+       {
+         printf_filtered ("0x%03x: ",
+                          type == 1
+                          ? idx : (idx * 8) | (type ? (cpl | 4) : 0));
+         if (descr.page_granular)
+           limit = (limit << 12) | 0xfff; /* big segment: low 12 bit set */
+         if (descr.stype == 1 || descr.stype == 2 || descr.stype == 3
+             || descr.stype == 9 || descr.stype == 11
+             || (descr.stype >= 16 && descr.stype < 32))
+           printf_filtered ("base=0x%02x%02x%04x limit=0x%08lx",
+                            descr.base2, descr.base1, descr.base0, limit);
+
+         switch (descr.stype)
+           {
+             case 1:
+             case 3:
+               printf_filtered (" 16-bit TSS  (task %sactive)",
+                                descr.stype == 3 ? "" : "in");
+               break;
+             case 2:
+               puts_filtered (" LDT");
+               break;
+             case 4:
+               memcpy (&gate, &descr, sizeof gate);
+               printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
+                                gate.selector, gate.offset1, gate.offset0);
+               printf_filtered (" 16-bit Call Gate (params=%d)",
+                                gate.param_count);
+               break;
+             case 5:
+               printf_filtered ("TSS selector=0x%04x", descr.base0);
+               printfi_filtered (16, "Task Gate");
+               break;
+             case 6:
+             case 7:
+               memcpy (&gate, &descr, sizeof gate);
+               printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
+                                gate.selector, gate.offset1, gate.offset0);
+               printf_filtered (" 16-bit %s Gate",
+                                descr.stype == 6 ? "Interrupt" : "Trap");
+               break;
+             case 9:
+             case 11:
+               printf_filtered (" 32-bit TSS (task %sactive)",
+                                descr.stype == 3 ? "" : "in");
+               break;
+             case 12:
+               memcpy (&gate, &descr, sizeof gate);
+               printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
+                                gate.selector, gate.offset1, gate.offset0);
+               printf_filtered (" 32-bit Call Gate (params=%d)",
+                                gate.param_count);
+               break;
+             case 14:
+             case 15:
+               memcpy (&gate, &descr, sizeof gate);
+               printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
+                                gate.selector, gate.offset1, gate.offset0);
+               printf_filtered (" 32-bit %s Gate",
+                                descr.stype == 14 ? "Interrupt" : "Trap");
+               break;
+             case 16:          /* data segments */
+             case 17:
+             case 18:
+             case 19:
+             case 20:
+             case 21:
+             case 22:
+             case 23:
+               printf_filtered (" %s-bit Data (%s Exp-%s%s)",
+                                descr.bit32 ? "32" : "16",
+                                descr.stype & 2 ? "Read/Write," : "Read-Only, ",
+                                descr.stype & 4 ? "down" : "up",
+                                descr.stype & 1 ? "" : ", N.Acc");
+               break;
+             case 24:          /* code segments */
+             case 25:
+             case 26:
+             case 27:
+             case 28:
+             case 29:
+             case 30:
+             case 31:
+               printf_filtered (" %s-bit Code (%s,  %sConf%s)",
+                                descr.bit32 ? "32" : "16",
+                                descr.stype & 2 ? "Exec/Read" : "Exec-Only",
+                                descr.stype & 4 ? "" : "N.",
+                                descr.stype & 1 ? "" : ", N.Acc");
+               break;
+             default:
+               printf_filtered ("Unknown type 0x%02x", descr.stype);
+               break;
+           }
+         puts_filtered ("\n");
+       }
+      else if (force)
+       {
+         printf_filtered ("0x%03x: ",
+                          type == 1
+                          ? idx : (idx * 8) | (type ? (cpl | 4) : 0));
+         if (!descr.present)
+           puts_filtered ("Segment not present\n");
+         else
+           printf_filtered ("Segment type 0x%02x is invalid in this table\n",
+                            descr.stype);
+       }
+    }
+  else if (force)
+    printf_filtered ("0x%03x: Cannot read this descriptor\n", idx);
+}
+
+static void
+go32_sldt (char *arg, int from_tty)
+{
+  struct dtr_reg gdtr;
+  unsigned short ldtr = 0;
+  int ldt_idx;
+  struct seg_descr ldt_descr;
+  long ldt_entry = -1L;
+  int cpl = (prog_has_started ? a_tss.tss_cs : _my_cs ()) & 3;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         ldt_entry = parse_and_eval_long (arg);
+         if (ldt_entry < 0
+             || (ldt_entry & 4) == 0
+             || (ldt_entry & 3) != (cpl & 3))
+           error ("Invalid LDT entry 0x%03x.", ldt_entry);
+       }
+    }
+
+  __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
+  __asm__ __volatile__ ("sldt   %0" : "=m" (ldtr) : /* no inputs */ );
+  ldt_idx = ldtr / 8;
+  if (ldt_idx == 0)
+    puts_filtered ("There is no LDT.\n");
+  /* LDT's entry in the GDT must have the type LDT, which is 2.  */
+  else if (get_descriptor (gdtr.base, ldt_idx, &ldt_descr) != 2)
+    printf_filtered ("LDT is present (at %#x), but unreadable by GDB.\n",
+                    ldt_descr.base0
+                    | (ldt_descr.base1 << 16)
+                    | (ldt_descr.base2 << 24));
+  else
+    {
+      unsigned base =
+       ldt_descr.base0
+       | (ldt_descr.base1 << 16)
+       | (ldt_descr.base2 << 24);
+      unsigned limit = ldt_descr.limit0 | (ldt_descr.limit1 << 16);
+      int max_entry;
+
+      if (ldt_descr.page_granular)
+       /* Page-granular segments must have the low 12 bits of their
+          limit set.  */
+       limit = (limit << 12) | 0xfff;
+      /* LDT cannot have more than 8K 8-byte entries, i.e. more than
+        64KB.  */
+      if (limit > 0xffff)
+       limit = 0xffff;
+
+      max_entry = (limit + 1) / 8;
+
+      if (ldt_entry >= 0)
+       {
+         if (ldt_entry > limit)
+           error ("Invalid LDT entry %#x: outside valid limits [0..%#x]",
+                  ldt_entry, limit);
+
+         display_descriptor (ldt_descr.stype, base, ldt_entry / 8, 1);
+       }
+      else
+       {
+         int i;
+
+         for (i = 0; i < max_entry; i++)
+           display_descriptor (ldt_descr.stype, base, i, 0);
+       }
+    }
+}
+
+static void
+go32_sgdt (char *arg, int from_tty)
+{
+  struct dtr_reg gdtr;
+  long gdt_entry = -1L;
+  int max_entry;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         gdt_entry = parse_and_eval_long (arg);
+         if (gdt_entry < 0 || (gdt_entry & 7) != 0)
+           error ("Invalid GDT entry 0x%03x: not an integral multiple of 8.",
+                  gdt_entry);
+       }
+    }
+
+  __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
+  max_entry = (gdtr.limit + 1) / 8;
+
+  if (gdt_entry >= 0)
+    {
+      if (gdt_entry > gdtr.limit)
+       error ("Invalid GDT entry %#x: outside valid limits [0..%#x]",
+              gdt_entry, gdtr.limit);
+
+      display_descriptor (0, gdtr.base, gdt_entry / 8, 1);
+    }
+  else
+    {
+      int i;
+
+      for (i = 0; i < max_entry; i++)
+       display_descriptor (0, gdtr.base, i, 0);
+    }
+}
+
+static void
+go32_sidt (char *arg, int from_tty)
+{
+  struct dtr_reg idtr;
+  long idt_entry = -1L;
+  int max_entry;
+
+  if (arg && *arg)
+    {
+      while (*arg && isspace(*arg))
+       arg++;
+
+      if (*arg)
+       {
+         idt_entry = parse_and_eval_long (arg);
+         if (idt_entry < 0)
+           error ("Invalid (negative) IDT entry 0x%03x.", idt_entry);
+       }
+    }
+
+  __asm__ __volatile__ ("sidt   %0" : "=m" (idtr) : /* no inputs */ );
+  max_entry = (idtr.limit + 1) / 8;
+  if (max_entry > 0x100)       /* no more than 256 entries */
+    max_entry = 0x100;
+
+  if (idt_entry >= 0)
+    {
+      if (idt_entry > idtr.limit)
+       error ("Invalid IDT entry %#x: outside valid limits [0..%#x]",
+              idt_entry, idtr.limit);
+
+      display_descriptor (1, idtr.base, idt_entry, 1);
+    }
+  else
+    {
+      int i;
+
+      for (i = 0; i < max_entry; i++)
+       display_descriptor (1, idtr.base, i, 0);
+    }
+}
+
 void
 _initialize_go32_nat (void)
 {
   init_go32_ops ();
   add_target (&go32_ops);
+
+  add_info ("dos-sysinfo", go32_sysinfo,
+           "Display information about the target system, including CPU, OS, DPMI, etc.");
+  add_info ("dos-ldt", go32_sldt,
+           "Display entries in the LDT (Local Descriptor Table).\n"
+           "Entry number (an expression) as an argument means display only that entry.");
+  add_info ("dos-gdt", go32_sgdt,
+           "Display entries in the GDT (Global Descriptor Table).\n"
+           "Entry number (an expression) as an argument means display only that entry.");
+  add_info ("dos-idt", go32_sidt,
+           "Display entries in the IDT (Interrupt Descriptor Table).\n"
+           "Entry number (an expression) as an argument means display only that entry.");
 }
 
 pid_t