~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to efiemu/pnvram.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2010-01-11 11:12:55 UTC
  • mfrom: (17.3.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100111111255-lr8ebkqw5x41gq6j
Tags: 1.98~20100101-1ubuntu1
* Resynchronise with Debian. Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Conflict with grub (<< 0.97-54) as well as grub-legacy.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu. Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed. If it is, show the
    menu, otherwise boot immediately. If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt. Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - If the environment variable "quiet" is set to something other than 0,
    suppress progress messages as the kernel and initrd load. Set this for
    non-recovery kernel menu entries.
  - Add GRUB_DEFAULT=saved, as well as grub-set-default and grub-reboot
    utilities. Provides functionality essentially equivalent to GRUB
    Legacy's savedefault.
  - Keep the loopback file open so that subsequent changes to the "root"
    environment variable don't affect it.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Handle RAID devices containing virtio components.
* Update savedefault patch from current Bazaar branch, fixing grub-reboot
  to have distinct behaviour from grub-set-default (LP: #497326).
* Fix grub-mkisofs compilation error with FORTIFY_SOURCE.
* Convert recordfail boilerplate in each menu entry to use a function.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include <grub/normal.h>
23
23
#include <grub/mm.h>
24
24
#include <grub/misc.h>
 
25
#include <grub/charset.h>
25
26
#include <grub/efiemu/efiemu.h>
26
27
#include <grub/efiemu/runtime.h>
27
28
#include <grub/extcmd.h>
34
35
static int accuracy_handle = 0;
35
36
static int daylight_handle = 0;
36
37
 
37
 
/* Temporary place */
38
 
static grub_uint8_t *nvram;
39
38
static grub_size_t nvramsize;
40
 
static grub_uint32_t high_monotonic_count;
41
 
static grub_int16_t timezone;
42
 
static grub_uint8_t daylight;
43
 
static grub_uint32_t accuracy;
44
 
 
45
 
static const struct grub_arg_option options[] = {
46
 
  {"size", 's', 0, "number of bytes to reserve for pseudo NVRAM", 0,
47
 
   ARG_TYPE_INT},
48
 
  {"high-monotonic-count", 'm', 0,
49
 
   "Initial value of high monotonic count", 0, ARG_TYPE_INT},
50
 
  {"timezone", 't', 0,
51
 
   "Timezone, offset in minutes from GMT", 0, ARG_TYPE_INT},
52
 
  {"accuracy", 'a', 0,
53
 
   "Accuracy of clock, in 1e-12 units", 0, ARG_TYPE_INT},
54
 
  {"daylight", 'd', 0,
55
 
   "Daylight value, as per EFI specifications", 0, ARG_TYPE_INT},
56
 
  {0, 0, 0, 0, 0, 0}
57
 
};
58
39
 
59
40
/* Parse signed value */
60
41
static int
61
 
grub_strtosl (char *arg, char **end, int base)
 
42
grub_strtosl (const char *arg, char **end, int base)
62
43
{
63
44
  if (arg[0] == '-')
64
45
    return -grub_strtoul (arg + 1, end, base);
65
46
  return grub_strtoul (arg, end, base);
66
47
}
67
48
 
 
49
static inline int
 
50
hextoval (char c)
 
51
{
 
52
  if (c >= '0' && c <= '9')
 
53
    return c - '0';
 
54
  if (c >= 'a' && c <= 'z')
 
55
    return c - 'a' + 10;
 
56
  if (c >= 'A' && c <= 'Z')
 
57
    return c - 'A' + 10;
 
58
  return 0;
 
59
}
 
60
 
 
61
static inline grub_err_t
 
62
unescape (char *in, char *out, char *outmax, int *len)
 
63
{
 
64
  char *ptr, *dptr;
 
65
  dptr = out;
 
66
  for (ptr = in; *ptr && dptr < outmax; )
 
67
    if (*ptr == '%' && ptr[1] && ptr[2])
 
68
      {
 
69
        *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
 
70
        ptr += 3;
 
71
        dptr++;
 
72
      }
 
73
    else
 
74
      {
 
75
        *dptr = *ptr;
 
76
        ptr++;
 
77
        dptr++;
 
78
      }
 
79
  if (dptr == outmax)
 
80
    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
 
81
                       "too many NVRAM variables for reserved variable space."
 
82
                       " Try increasing EfiEmu.pnvram.size");
 
83
  *len = dptr - out;
 
84
  return 0;
 
85
}
 
86
 
68
87
/* Export stuff for efiemu */
69
88
static grub_err_t
70
89
nvram_set (void * data __attribute__ ((unused)))
71
90
{
 
91
  const char *env;
72
92
  /* Take definitive pointers */
73
 
  grub_uint8_t *nvram_def = grub_efiemu_mm_obtain_request (nvram_handle);
 
93
  char *nvram = grub_efiemu_mm_obtain_request (nvram_handle);
74
94
  grub_uint32_t *nvramsize_def
75
95
    = grub_efiemu_mm_obtain_request (nvramsize_handle);
76
 
  grub_uint32_t *high_monotonic_count_def
 
96
  grub_uint32_t *high_monotonic_count
77
97
    = grub_efiemu_mm_obtain_request (high_monotonic_count_handle);
78
 
  grub_int16_t *timezone_def
 
98
  grub_int16_t *timezone
79
99
    = grub_efiemu_mm_obtain_request (timezone_handle);
80
 
  grub_uint8_t *daylight_def
 
100
  grub_uint8_t *daylight
81
101
    = grub_efiemu_mm_obtain_request (daylight_handle);
82
 
  grub_uint32_t *accuracy_def
 
102
  grub_uint32_t *accuracy
83
103
    = grub_efiemu_mm_obtain_request (accuracy_handle);
 
104
  char *nvramptr;
 
105
 
 
106
  auto int iterate_env (struct grub_env_var *var);
 
107
  int iterate_env (struct grub_env_var *var)
 
108
  {
 
109
    char *guid, *attr, *name, *varname;
 
110
    struct efi_variable *efivar;
 
111
    int len = 0;
 
112
    int i;
 
113
    grub_uint64_t guidcomp;
 
114
 
 
115
    if (grub_memcmp (var->name, "EfiEmu.pnvram.",
 
116
                     sizeof ("EfiEmu.pnvram.") - 1) != 0)
 
117
      return 0;
 
118
 
 
119
    guid = var->name + sizeof ("EfiEmu.pnvram.") - 1;
 
120
 
 
121
    attr = grub_strchr (guid, '.');
 
122
    if (!attr)
 
123
      return 0;
 
124
    attr++;
 
125
 
 
126
    name = grub_strchr (attr, '.');
 
127
    if (!name)
 
128
      return 0;
 
129
    name++;
 
130
 
 
131
    efivar = (struct efi_variable *) nvramptr;
 
132
    if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
 
133
      {
 
134
        grub_error (GRUB_ERR_OUT_OF_MEMORY,
 
135
                    "too many NVRAM variables for reserved variable space."
 
136
                    " Try increasing EfiEmu.pnvram.size");
 
137
        return 1;
 
138
      }
 
139
 
 
140
    nvramptr += sizeof (struct efi_variable);
 
141
 
 
142
    efivar->guid.data1 = grub_cpu_to_le32 (grub_strtoul (guid, &guid, 16));
 
143
    if (*guid != '-')
 
144
      return 0;
 
145
    guid++;
 
146
 
 
147
    efivar->guid.data2 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
 
148
    if (*guid != '-')
 
149
      return 0;
 
150
    guid++;
 
151
 
 
152
    efivar->guid.data3 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
 
153
    if (*guid != '-')
 
154
      return 0;
 
155
    guid++;
 
156
 
 
157
    guidcomp = grub_strtoull (guid, 0, 16);
 
158
    for (i = 0; i < 8; i++)
 
159
      efivar->guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff;
 
160
 
 
161
    efivar->attributes = grub_strtoull (attr, 0, 16);
 
162
 
 
163
    varname = grub_malloc (grub_strlen (name) + 1);
 
164
    if (! varname)
 
165
      return 1;
 
166
 
 
167
    if (unescape (name, varname, varname + grub_strlen (name) + 1, &len))
 
168
      return 1;
 
169
 
 
170
    len = grub_utf8_to_utf16 ((grub_uint16_t *) nvramptr,
 
171
                              (nvramsize - (nvramptr - nvram)) / 2,
 
172
                              (grub_uint8_t *) varname, len, NULL);
 
173
 
 
174
    if (len < 0)
 
175
      {
 
176
        grub_error (GRUB_ERR_BAD_ARGUMENT, "broken UTF-8 in variable name");
 
177
        return 1;
 
178
      }
 
179
 
 
180
    nvramptr += 2 * len;
 
181
    *((grub_uint16_t *) nvramptr) = 0;
 
182
    nvramptr += 2;
 
183
    efivar->namelen = 2 * len + 2;
 
184
 
 
185
    if (unescape (var->value, nvramptr, nvram + nvramsize, &len))
 
186
      {
 
187
        efivar->namelen = 0;
 
188
        return 1;
 
189
      }
 
190
 
 
191
    nvramptr += len;
 
192
 
 
193
    efivar->size = len;
 
194
 
 
195
    return 0;
 
196
  }
84
197
 
85
198
  /* Copy to definitive loaction */
86
199
  grub_dprintf ("efiemu", "preparing pnvram\n");
87
 
  grub_memcpy (nvram_def, nvram, nvramsize);
 
200
 
 
201
  env = grub_env_get ("EfiEmu.pnvram.high_monotonic_count");
 
202
  *high_monotonic_count = env ? grub_strtoul (env, 0, 0) : 1;
 
203
  env = grub_env_get ("EfiEmu.pnvram.timezone");
 
204
  *timezone = env ? grub_strtosl (env, 0, 0) : GRUB_EFI_UNSPECIFIED_TIMEZONE;
 
205
  env = grub_env_get ("EfiEmu.pnvram.accuracy");
 
206
  *accuracy = env ? grub_strtoul (env, 0, 0) : 50000000;
 
207
  env = grub_env_get ("EfiEmu.pnvram.daylight");
 
208
  *daylight = env ? grub_strtoul (env, 0, 0) : 0;
 
209
 
 
210
  nvramptr = nvram;
 
211
  grub_memset (nvram, 0, nvramsize);
 
212
  grub_env_iterate (iterate_env);
 
213
  if (grub_errno)
 
214
    return grub_errno;
88
215
  *nvramsize_def = nvramsize;
89
 
  *high_monotonic_count_def = high_monotonic_count;
90
 
  *timezone_def = timezone;
91
 
  *daylight_def = daylight;
92
 
  *accuracy_def = accuracy;
93
216
 
94
217
  /* Register symbols */
95
218
  grub_efiemu_register_symbol ("efiemu_variables", nvram_handle, 0);
113
236
  grub_efiemu_mm_return_request (timezone_handle);
114
237
  grub_efiemu_mm_return_request (accuracy_handle);
115
238
  grub_efiemu_mm_return_request (daylight_handle);
116
 
 
117
 
  grub_free (nvram);
118
 
  nvram = 0;
119
 
}
120
 
 
121
 
/* Load the variables file It's in format
122
 
   guid1:attr1:name1:data1;
123
 
   guid2:attr2:name2:data2;
124
 
   ...
125
 
   Where all fields are in hex
126
 
*/
127
 
static grub_err_t
128
 
read_pnvram (char *filename)
129
 
{
130
 
  char *buf, *ptr, *ptr2;
131
 
  grub_file_t file;
132
 
  grub_size_t size;
133
 
  grub_uint8_t *nvramptr = nvram;
134
 
  struct efi_variable *efivar;
135
 
  grub_size_t guidlen, datalen;
136
 
  unsigned i, j;
137
 
 
138
 
  file = grub_file_open (filename);
139
 
  if (!file)
140
 
    return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram");
141
 
  size = grub_file_size (file);
142
 
  buf = grub_malloc (size + 1);
143
 
  if (!buf)
144
 
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read pnvram");
145
 
  if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
146
 
    return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram");
147
 
  buf[size] = 0;
148
 
  grub_file_close (file);
149
 
 
150
 
  for (ptr = buf; *ptr; )
151
 
    {
152
 
      if (grub_isspace (*ptr))
153
 
        {
154
 
          ptr++;
155
 
          continue;
156
 
        }
157
 
 
158
 
      efivar = (struct efi_variable *) nvramptr;
159
 
      if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
160
 
        return grub_error (GRUB_ERR_OUT_OF_MEMORY,
161
 
                           "file is too large for reserved variable space");
162
 
 
163
 
      nvramptr += sizeof (struct efi_variable);
164
 
 
165
 
      /* look ahow long guid field is*/
166
 
      guidlen = 0;
167
 
      for (ptr2 = ptr; (grub_isspace (*ptr2)
168
 
                        || (*ptr2 >= '0' && *ptr2 <= '9')
169
 
                        || (*ptr2 >= 'a' && *ptr2 <= 'f')
170
 
                        || (*ptr2 >= 'A' && *ptr2 <= 'F'));
171
 
           ptr2++)
172
 
        if (!grub_isspace (*ptr2))
173
 
          guidlen++;
174
 
      guidlen /= 2;
175
 
 
176
 
      /* Read guid */
177
 
      if (guidlen != sizeof (efivar->guid))
178
 
        {
179
 
          grub_free (buf);
180
 
          return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
181
 
        }
182
 
      for (i = 0; i < 2 * sizeof (efivar->guid); i++)
183
 
        {
184
 
          int hex = 0;
185
 
          while (grub_isspace (*ptr))
186
 
            ptr++;
187
 
          if (*ptr >= '0' && *ptr <= '9')
188
 
            hex = *ptr - '0';
189
 
          if (*ptr >= 'a' && *ptr <= 'f')
190
 
            hex = *ptr - 'a' + 10;
191
 
          if (*ptr >= 'A' && *ptr <= 'F')
192
 
            hex = *ptr - 'A' + 10;
193
 
 
194
 
          if (i%2 == 0)
195
 
            ((grub_uint8_t *)&(efivar->guid))[i/2] = hex << 4;
196
 
          else
197
 
            ((grub_uint8_t *)&(efivar->guid))[i/2] |= hex;
198
 
          ptr++;
199
 
        }
200
 
 
201
 
      while (grub_isspace (*ptr))
202
 
        ptr++;
203
 
      if (*ptr != ':')
204
 
        {
205
 
          grub_dprintf ("efiemu", "Not colon\n");
206
 
          grub_free (buf);
207
 
          return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
208
 
        }
209
 
      ptr++;
210
 
      while (grub_isspace (*ptr))
211
 
        ptr++;
212
 
 
213
 
      /* Attributes can be just parsed by existing functions */
214
 
      efivar->attributes = grub_strtoul (ptr, &ptr, 16);
215
 
 
216
 
      while (grub_isspace (*ptr))
217
 
        ptr++;
218
 
      if (*ptr != ':')
219
 
        {
220
 
          grub_dprintf ("efiemu", "Not colon\n");
221
 
          grub_free (buf);
222
 
          return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
223
 
        }
224
 
      ptr++;
225
 
      while (grub_isspace (*ptr))
226
 
        ptr++;
227
 
 
228
 
      /* Read name and value */
229
 
      for (j = 0; j < 2; j++)
230
 
        {
231
 
          /* Look the length */
232
 
          datalen = 0;
233
 
          for (ptr2 = ptr; *ptr2 && (grub_isspace (*ptr2)
234
 
                                     || (*ptr2 >= '0' && *ptr2 <= '9')
235
 
                                     || (*ptr2 >= 'a' && *ptr2 <= 'f')
236
 
                                     || (*ptr2 >= 'A' && *ptr2 <= 'F'));
237
 
               ptr2++)
238
 
            if (!grub_isspace (*ptr2))
239
 
              datalen++;
240
 
          datalen /= 2;
241
 
 
242
 
          if (nvramptr - nvram + datalen > nvramsize)
243
 
            {
244
 
              grub_free (buf);
245
 
              return grub_error (GRUB_ERR_OUT_OF_MEMORY,
246
 
                                 "file is too large for reserved "
247
 
                                 " variable space");
248
 
            }
249
 
 
250
 
          for (i = 0; i < 2 * datalen; i++)
251
 
            {
252
 
              int hex = 0;
253
 
              while (grub_isspace (*ptr))
254
 
                ptr++;
255
 
              if (*ptr >= '0' && *ptr <= '9')
256
 
                hex = *ptr - '0';
257
 
              if (*ptr >= 'a' && *ptr <= 'f')
258
 
                hex = *ptr - 'a' + 10;
259
 
              if (*ptr >= 'A' && *ptr <= 'F')
260
 
                hex = *ptr - 'A' + 10;
261
 
 
262
 
              if (i%2 == 0)
263
 
                nvramptr[i/2] = hex << 4;
264
 
              else
265
 
                nvramptr[i/2] |= hex;
266
 
              ptr++;
267
 
            }
268
 
          nvramptr += datalen;
269
 
          while (grub_isspace (*ptr))
270
 
            ptr++;
271
 
          if (*ptr != (j ? ';' : ':'))
272
 
            {
273
 
              grub_free (buf);
274
 
              grub_dprintf ("efiemu", j?"Not semicolon\n":"Not colon\n");
275
 
              return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename);
276
 
            }
277
 
          if (j)
278
 
            efivar->size = datalen;
279
 
          else
280
 
            efivar->namelen = datalen;
281
 
 
282
 
          ptr++;
283
 
        }
284
 
    }
285
 
  grub_free (buf);
286
 
  return GRUB_ERR_NONE;
287
 
}
288
 
 
289
 
static grub_err_t
290
 
grub_efiemu_make_nvram (void)
291
 
{
 
239
}
 
240
 
 
241
grub_err_t
 
242
grub_efiemu_pnvram (void)
 
243
{
 
244
  const char *size;
292
245
  grub_err_t err;
293
246
 
294
 
  err = grub_efiemu_autocore ();
295
 
  if (err)
296
 
    {
297
 
      grub_free (nvram);
298
 
      return err;
299
 
    }
 
247
  nvramsize = 0;
 
248
 
 
249
  size = grub_env_get ("EfiEmu.pnvram.size");
 
250
  if (size)
 
251
    nvramsize = grub_strtoul (size, 0, 0);
 
252
 
 
253
  if (!nvramsize)
 
254
    nvramsize = 2048;
300
255
 
301
256
  err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0);
302
257
  if (err)
303
 
    {
304
 
      grub_free (nvram);
305
 
      return err;
306
 
    }
 
258
    return err;
 
259
 
307
260
  nvram_handle
308
261
    = grub_efiemu_request_memalign (1, nvramsize,
309
262
                                    GRUB_EFI_RUNTIME_SERVICES_DATA);
323
276
    = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
324
277
                                    GRUB_EFI_RUNTIME_SERVICES_DATA);
325
278
 
326
 
  grub_efiemu_request_symbols (6);
327
279
  return GRUB_ERR_NONE;
328
280
}
329
 
 
330
 
grub_err_t
331
 
grub_efiemu_pnvram (void)
332
 
{
333
 
  if (nvram)
334
 
    return GRUB_ERR_NONE;
335
 
 
336
 
  nvramsize = 2048;
337
 
  high_monotonic_count = 1;
338
 
  timezone = GRUB_EFI_UNSPECIFIED_TIMEZONE;
339
 
  accuracy = 50000000;
340
 
  daylight = 0;
341
 
 
342
 
  nvram = grub_zalloc (nvramsize);
343
 
  if (!nvram)
344
 
    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
345
 
                       "Couldn't allocate space for temporary pnvram storage");
346
 
 
347
 
  return grub_efiemu_make_nvram ();
348
 
}
349
 
 
350
 
static grub_err_t
351
 
grub_cmd_efiemu_pnvram (struct grub_extcmd *cmd,
352
 
                        int argc, char **args)
353
 
{
354
 
  struct grub_arg_list *state = cmd->state;
355
 
  grub_err_t err;
356
 
 
357
 
  if (argc > 1)
358
 
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one argument expected");
359
 
 
360
 
  nvramsize = state[0].set ? grub_strtoul (state[0].arg, 0, 0) : 2048;
361
 
  high_monotonic_count = state[1].set ? grub_strtoul (state[1].arg, 0, 0) : 1;
362
 
  timezone = state[2].set ? grub_strtosl (state[2].arg, 0, 0)
363
 
    : GRUB_EFI_UNSPECIFIED_TIMEZONE;
364
 
  accuracy = state[3].set ? grub_strtoul (state[3].arg, 0, 0) : 50000000;
365
 
  daylight = state[4].set ? grub_strtoul (state[4].arg, 0, 0) : 0;
366
 
 
367
 
  nvram = grub_zalloc (nvramsize);
368
 
  if (!nvram)
369
 
    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
370
 
                       "Couldn't allocate space for temporary pnvram storage");
371
 
 
372
 
  if (argc == 1 && (err = read_pnvram (args[0])))
373
 
    {
374
 
      grub_free (nvram);
375
 
      return err;
376
 
    }
377
 
  return grub_efiemu_make_nvram ();
378
 
}
379
 
 
380
 
static grub_extcmd_t cmd;
381
 
 
382
 
void grub_efiemu_pnvram_cmd_register (void);
383
 
void grub_efiemu_pnvram_cmd_unregister (void);
384
 
 
385
 
void
386
 
grub_efiemu_pnvram_cmd_register (void)
387
 
{
388
 
  cmd = grub_register_extcmd ("efiemu_pnvram", grub_cmd_efiemu_pnvram,
389
 
                              GRUB_COMMAND_FLAG_BOTH,
390
 
                              "efiemu_pnvram [FILENAME]",
391
 
                              "Initialise pseudo-NVRAM and load variables "
392
 
                              "from FILE",
393
 
                              options);
394
 
}
395
 
 
396
 
void
397
 
grub_efiemu_pnvram_cmd_unregister (void)
398
 
{
399
 
  grub_unregister_extcmd (cmd);
400
 
}