grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Re: Feature Request: 32-bit mem write and 'setpci -s' type comma


From: Vladimir 'φ-coder/phcoder' Serbinenko
Subject: [PATCH] Re: Feature Request: 32-bit mem write and 'setpci -s' type command in menu.lst
Date: Tue, 22 Dec 2009 17:25:30 +0100
User-agent: Mozilla-Thunderbird 2.0.0.22 (X11/20091109)

Nando wrote:
> Hi everyone,
>
>
> 2/ Ability to do a setpci -s type command
> ==============================
>
> Also, if possible, could a small subset of 'setpci' be incorporated as
> well? I guess I could figure out the address I need to write to by
> calculation, but would be nice if I could do the equivalent of the
> following in grub, associated with a menu entry. This would help the
> DIY ViDock project that needs to setup PCI Bridge windows prior to
> Windows load, something that many bios doesn't do, neither does Windows.
>
> setpci -s 00:02.0 40l=20
>
I attach two patches. One iorw.diff for reading/writing I/O space.
Another one setpci.diff is self-explanatory. But it depends on pciclean
patch I posted today.


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko

=== added file 'commands/setpci.c'
--- commands/setpci.c   1970-01-01 00:00:00 +0000
+++ commands/setpci.c   2009-12-22 15:42:45 +0000
@@ -0,0 +1,340 @@
+/* lspci.c - List PCI devices.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008, 2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/pci.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/env.h>
+#include <grub/mm.h>
+
+struct pci_register
+{
+  const char *name;
+  grub_uint16_t addr;
+  unsigned size;
+};
+
+struct pci_register pci_registers[] =
+  {
+    {"VENDOR_ID",       GRUB_PCI_REG_VENDOR      , 2},
+    {"DEVICE_ID",       GRUB_PCI_REG_DEVICE      , 2},
+    {"COMMAND",         GRUB_PCI_REG_COMMAND     , 2},
+    {"STATUS",          GRUB_PCI_REG_STATUS      , 2},
+    {"REVISION",        GRUB_PCI_REG_REVISION    , 1},
+    {"CLASS_PROG",      GRUB_PCI_REG_CLASS + 1   , 1},
+    {"CLASS_DEVICE",    GRUB_PCI_REG_CLASS + 2   , 2},
+    {"CACHE_LINE_SIZE", GRUB_PCI_REG_CACHELINE   , 1},
+    {"LATENCY_TIMER",   GRUB_PCI_REG_LAT_TIMER   , 1},
+    {"HEADER_TYPE",     GRUB_PCI_REG_HEADER_TYPE , 1},
+    {"BIST",            GRUB_PCI_REG_BIST        , 1},
+    {"BASE_ADDRESS_0",  GRUB_PCI_REG_ADDRESS_REG0, 4},
+    {"BASE_ADDRESS_1",  GRUB_PCI_REG_ADDRESS_REG1, 4},
+    {"BASE_ADDRESS_2",  GRUB_PCI_REG_ADDRESS_REG2, 4},
+    {"BASE_ADDRESS_3",  GRUB_PCI_REG_ADDRESS_REG3, 4},
+    {"BASE_ADDRESS_4",  GRUB_PCI_REG_ADDRESS_REG4, 4},
+    {"BASE_ADDRESS_5",  GRUB_PCI_REG_ADDRESS_REG5, 4},
+    {"CARDBUS_CIS",     GRUB_PCI_REG_CIS_POINTER , 4},
+    {"SUBVENDOR_ID",    GRUB_PCI_REG_SUBVENDOR   , 2},
+    {"SUBSYSTEM_ID",    GRUB_PCI_REG_SUBSYSTEM   , 2},
+    {"ROM_ADDRESS",     GRUB_PCI_REG_ROM_ADDRESS , 4},
+    {"CAP_POINTER",     GRUB_PCI_REG_CAP_POINTER , 1},
+    {"INTERRUPT_LINE",  GRUB_PCI_REG_IRQ_LINE    , 1},
+    {"INTERRUPT_PIN",   GRUB_PCI_REG_IRQ_PIN     , 1},
+    {"MIN_GNT",         GRUB_PCI_REG_MIN_GNT     , 1},
+    {"MAX_LAT",         GRUB_PCI_REG_MIN_GNT     , 1},
+  };
+
+static const struct grub_arg_option options[] =
+  {
+    {0, 'd', 0, "Select device by vendor and device IDs.",
+     "[vendor]:[device]", ARG_TYPE_STRING},
+    {0, 's', 0, "Select device by its position on the bus.",
+     "[bus]:[slot][.func]", ARG_TYPE_STRING},
+    {0, 'v', 0, "Save read value into variable VARNAME.",
+     "VARNAME", ARG_TYPE_STRING},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+static grub_uint32_t pciid_check_mask, pciid_check_value;
+static int bus, device, function;
+static int check_bus, check_device, check_function;
+static grub_uint32_t write_mask, regwrite;
+static int regsize;
+static grub_uint16_t regaddr;
+static const char *varname;
+
+static int NESTED_FUNC_ATTR
+grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid)
+{
+  grub_uint32_t regval = 0;
+  grub_pci_address_t addr;
+
+  if ((pciid & pciid_check_mask) != pciid_check_value)
+    return 0;
+
+  if (check_bus && grub_pci_get_bus (dev) != bus)
+    return 0;
+
+  if (check_device && grub_pci_get_device (dev) != device)
+    return 0;
+
+  if (check_function && grub_pci_get_function (dev) != device)
+    return 0;
+
+  addr = grub_pci_make_address (dev, regaddr);
+
+  switch (regsize)
+    {
+    case 1:
+      regval = grub_pci_read_byte (addr);
+      break;
+
+    case 2:
+      regval = grub_pci_read_word (addr);
+      break;
+
+    case 4:
+      regval = grub_pci_read (addr);
+      break;
+    }
+
+  if (varname)
+    {
+      char buf[sizeof ("XXXXXXXX")];
+      grub_sprintf (buf, "%x", regval);
+      grub_env_set (varname, buf);
+      return 1;
+    }
+
+  if (!write_mask)
+    {
+      grub_printf ("Register %x of %d:%d.%d is %x\n", regaddr,
+                  grub_pci_get_bus (dev),
+                  grub_pci_get_device (dev),
+                  grub_pci_get_function (dev),
+                  regval);
+      return 0;
+    }
+
+  regval = (regval & ~write_mask) | regwrite;
+
+  switch (regsize)
+    {
+    case 1:
+      grub_pci_write_byte (addr, regval);
+      break;
+
+    case 2:
+      grub_pci_write_word (addr, regval);
+      break;
+
+    case 4:
+      grub_pci_write (addr, regval);
+      break;
+    }
+
+  return 0;
+}
+
+static grub_err_t
+grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv)
+{
+  const char *ptr;
+  unsigned i;
+
+  pciid_check_value = 0;
+  pciid_check_mask = 0;
+
+  if (cmd->state[0].set)
+    {
+      ptr = cmd->state[0].arg;
+      pciid_check_value |= (grub_strtoul (ptr, (char **) &ptr, 16) & 0xffff);
+      if (grub_errno == GRUB_ERR_BAD_NUMBER)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         ptr = cmd->state[0].arg;
+       }
+      else
+       pciid_check_mask |= 0xffff;
+      if (grub_errno)
+       return grub_errno;
+      if (*ptr != ':')
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "Colon expected.");
+      ptr++;
+      pciid_check_value |= (grub_strtoul (ptr, (char **) &ptr, 16) & 0xffff)
+       << 16;
+      if (grub_errno == GRUB_ERR_BAD_NUMBER)
+       grub_errno = GRUB_ERR_NONE;
+      else
+       pciid_check_mask |= 0xffff0000;
+    }
+
+  pciid_check_value &= pciid_check_mask;
+
+  check_bus = check_device = check_function = 0;
+
+  if (cmd->state[1].set)
+    {
+      const char *optr;
+      
+      ptr = cmd->state[1].arg;
+      optr = ptr;
+      bus = grub_strtoul (ptr, (char **) &ptr, 16);
+      if (grub_errno == GRUB_ERR_BAD_NUMBER)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         ptr = optr;
+       }
+      else
+       check_bus = 1;
+      if (grub_errno)
+       return grub_errno;
+      if (*ptr != ':')
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "Colon expected.");
+      ptr++;
+      optr = ptr;
+      device = grub_strtoul (ptr, (char **) &ptr, 16);
+      if (grub_errno == GRUB_ERR_BAD_NUMBER)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         ptr = optr;
+       }
+      else
+       check_device = 1;
+      if (*ptr == '.')
+       {
+         ptr++;
+         function = grub_strtoul (ptr, (char **) &ptr, 16);
+         if (grub_errno)
+           return grub_errno;
+         check_function = 1;
+       }
+    }
+
+  if (cmd->state[2].set)
+    varname = cmd->state[2].arg;
+  else
+    varname = NULL;
+
+  write_mask = 0;
+
+  if (argc == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Command expected.");
+
+  if (argc > 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Only one command is 
supported.");
+
+  ptr = argv[0];
+
+  for (i = 0; i < ARRAY_SIZE (pci_registers); i++)
+    {
+      if (grub_strncmp (ptr, pci_registers[i].name,
+                       grub_strlen (pci_registers[i].name)) == 0)
+       break;
+    }
+  if (i == ARRAY_SIZE (pci_registers))
+    {
+      regsize = 0;
+      regaddr = grub_strtoul (ptr, (char **) &ptr, 16);
+      if (grub_errno)
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown register");
+    }
+  else
+    {
+      regaddr = pci_registers[i].addr;
+      regsize = pci_registers[i].size;
+      ptr += grub_strlen (pci_registers[i].name);
+    }
+
+  if (grub_errno)
+    return grub_errno;
+
+  if (*ptr == '+')
+    {
+      ptr++;
+      regaddr += grub_strtoul (ptr, (char **) &ptr, 16);
+      if (grub_errno)
+       return grub_errno;
+    }
+
+  if (grub_memcmp (ptr, ".L", sizeof (".L") - 1) == 0
+      || grub_memcmp (ptr, ".l", sizeof (".l") - 1) == 0)
+    {
+      regsize = 4;
+      ptr += sizeof (".l") - 1;
+    }
+  else if (grub_memcmp (ptr, ".W", sizeof (".W") - 1) == 0
+      || grub_memcmp (ptr, ".w", sizeof (".w") - 1) == 0)
+    {
+      regsize = 2;
+      ptr += sizeof (".w") - 1;
+    }
+  else if (grub_memcmp (ptr, ".B", sizeof (".B") - 1) == 0
+      || grub_memcmp (ptr, ".b", sizeof (".b") - 1) == 0)
+    {
+      regsize = 1;
+      ptr += sizeof (".b") - 1;
+    }
+
+  if (!regsize)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Unknown register size.");
+
+  write_mask = 0;
+  if (*ptr == '=')
+    {
+      ptr++;
+      regwrite = grub_strtoul (ptr, (char **) &ptr, 16);
+      if (grub_errno)
+       return grub_errno;
+      write_mask = 0xffffffff;
+      if (*ptr == ':')
+       {
+         ptr++;
+         write_mask = grub_strtoul (ptr, (char **) &ptr, 16);
+         if (grub_errno)
+           return grub_errno;
+         write_mask = 0xffffffff;
+       }
+      regwrite &= write_mask;
+    }
+
+  if (write_mask && varname)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                      "Option -v isn't valid for writes.");
+
+  grub_pci_iterate (grub_setpci_iter);
+  return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(setpci)
+{
+  cmd = grub_register_extcmd ("setpci", grub_cmd_setpci, 
GRUB_COMMAND_FLAG_BOTH,
+                             "setpci [-s POSITION] [-d DEVICE] [-v VAR] "
+                             "[REGISTER][=VALUE[:MASK]]",
+                             "Manipulate PCI devices.", options);
+}
+
+GRUB_MOD_FINI(setpci)
+{
+  grub_unregister_extcmd (cmd);
+}

=== modified file 'conf/i386.rmk'
--- conf/i386.rmk       2009-11-25 22:39:59 +0000
+++ conf/i386.rmk       2009-12-22 15:42:45 +0000
@@ -25,3 +25,9 @@
 ata_mod_SOURCES = disk/ata.c
 ata_mod_CFLAGS = $(COMMON_CFLAGS)
 ata_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For setpci.mod
+pkglib_MODULES += setpci.mod
+setpci_mod_SOURCES = commands/setpci.c
+setpci_mod_CFLAGS = $(COMMON_CFLAGS)
+setpci_mod_LDFLAGS = $(COMMON_LDFLAGS)

=== added file 'commands/iorw.c'
--- commands/iorw.c     1970-01-01 00:00:00 +0000
+++ commands/iorw.c     2009-12-22 16:18:48 +0000
@@ -0,0 +1,146 @@
+/* memrw.c - command to read / write physical memory  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/env.h>
+#include <grub/cpu/io.h>
+
+static grub_extcmd_t cmd_read_byte, cmd_read_word, cmd_read_dword;
+static grub_command_t cmd_write_byte, cmd_write_word, cmd_write_dword;
+
+static const struct grub_arg_option options[] =
+  {
+    {0, 'v', 0, "Save read value into variable VARNAME.",
+     "VARNAME", ARG_TYPE_STRING},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+
+static grub_err_t
+grub_cmd_read (grub_extcmd_t cmd, int argc, char **argv)
+{
+  grub_target_addr_t addr;
+  grub_uint32_t value = 0;
+  char buf[sizeof ("XXXXXXXX")];
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid number of arguments");
+
+  addr = grub_strtoul (argv[0], 0, 0);
+  switch (cmd->cmd->name[sizeof ("grub_in") - 1])
+    {
+    case 'l':
+      value = grub_inl (addr);
+      break;
+
+    case 'w':
+      value = grub_inw (addr);
+      break;
+
+    case 'b':
+      value = grub_inb (addr);
+      break;
+    }
+
+  if (cmd->state[0].set)
+    {
+      grub_sprintf (buf, "%x", value);
+      grub_env_set (cmd->state[0].arg, buf);
+    }
+  else
+    grub_printf ("0x%x\n", value);
+
+  return 0;
+}
+
+static grub_err_t
+grub_cmd_write (grub_command_t cmd, int argc, char **argv)
+{
+  grub_target_addr_t addr;
+  grub_uint32_t value;
+  grub_uint32_t mask = 0xffffffff;
+
+  if (argc != 2 && argc != 3)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid number of arguments");
+
+  addr = grub_strtoul (argv[0], 0, 0);
+  value = grub_strtoul (argv[1], 0, 0);
+  if (argc == 3)
+    mask = grub_strtoul (argv[2], 0, 0);
+  value &= mask;
+  switch (cmd->name[sizeof ("grub_out") - 1])
+    {
+    case 'l':
+      if (mask != 0xffffffff)
+       grub_outl ((grub_inl (addr) & ~mask) | value, addr);
+      else
+       grub_outl (value, addr);
+      break;
+
+    case 'w':
+      if ((mask & 0xffff) != 0xffff)
+       grub_outw ((grub_inw (addr) & ~mask) | value, addr);
+      else
+       grub_outw (value, addr);
+      break;
+
+    case 'b':
+      if ((mask & 0xff) != 0xff)
+       grub_outb ((grub_inb (addr) & ~mask) | value, addr);
+      else
+       grub_outb (value, addr);
+      break;
+    }
+
+  return 0;
+}
+
+GRUB_MOD_INIT(memrw)
+{
+  cmd_read_byte =
+    grub_register_extcmd ("grub_inb", grub_cmd_read, GRUB_COMMAND_FLAG_BOTH,
+                         "grub_inb PORT", "Read byte from PORT.", options);
+  cmd_read_word =
+    grub_register_extcmd ("grub_inw", grub_cmd_read, GRUB_COMMAND_FLAG_BOTH,
+                         "grub_inw PORT", "Read word from PORT.", options);
+  cmd_read_dword =
+    grub_register_extcmd ("grub_inl", grub_cmd_read, GRUB_COMMAND_FLAG_BOTH,
+                         "grub_inl PORT", "Read dword from PORT.", options);
+  cmd_write_byte =
+    grub_register_command ("grub_outb", grub_cmd_write,
+                          "grub_outb PORT VALUE [MASK]", "Write byte VALUE to 
PORT.");
+  cmd_write_word =
+    grub_register_command ("grub_outw", grub_cmd_write,
+                          "grub_outw PORT VALUE [MASK]", "Write word VALUE to 
PORT.");
+  cmd_write_dword =
+    grub_register_command ("grub_outl", grub_cmd_write,
+                          "grub_outl ADDR VALUE [MASK]", "Write dword VALUE to 
PORT.");
+}
+
+GRUB_MOD_FINI(memrw)
+{
+  grub_unregister_extcmd (cmd_read_byte);
+  grub_unregister_extcmd (cmd_read_word);
+  grub_unregister_extcmd (cmd_read_dword);
+  grub_unregister_command (cmd_write_byte);
+  grub_unregister_command (cmd_write_word);
+  grub_unregister_command (cmd_write_dword);
+}

=== modified file 'conf/i386.rmk'
--- conf/i386.rmk       2009-11-25 22:39:59 +0000
+++ conf/i386.rmk       2009-12-22 16:18:48 +0000
@@ -25,3 +25,9 @@
 ata_mod_SOURCES = disk/ata.c
 ata_mod_CFLAGS = $(COMMON_CFLAGS)
 ata_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For iorw.mod.
+pkglib_MODULES += iorw.mod
+iorw_mod_SOURCES = commands/iorw.c
+iorw_mod_CFLAGS = $(COMMON_CFLAGS)
+iorw_mod_LDFLAGS = $(COMMON_LDFLAGS)

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]