[PATCH gnumach v3] Implement per task virtual memory limit

2024-12-30 Thread dnietoc
From: Diego Nieto Cid 

  * include/mach/gnumach.defs: (vm_set_size_limit) new routine
(vm_get_size_limit) likewise
  * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual 
memory limit
  * tests/test-vm.c: (test_vm_limit) add test for the new routines
  * vm/vm_kern.c: (projected_buffer_allocate) increase size_none when 
protection is VM_PROT_NONE
(projected_buffer_map) likewise
  * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and 
size_max_limit
  * vm/vm_map.c: (vm_map_setup) initialize new fields
(vm_map_enforce_limit) new function
(vm_map_copy_limits) new function
(vm_map_find_entry) call limit enforcer function
(vm_map_enter) likewise
(vm_map_copyout) likewise
(vm_map_copyout_page_list) likewise
(vm_map_fork) copy parent limit to the new map and compute and set 
size_none of the new map
  * vm/vm_user.c: (vm_set_size_limit) new function
(vm_get_size_limit) likewise
---
 include/mach/gnumach.defs |  33 +++
 kern/task.c   |   5 ++
 tests/test-vm.c   |  85 
 vm/vm_kern.c  |  26 +
 vm/vm_map.c   | 113 +-
 vm/vm_map.h   |  13 +
 vm/vm_user.c  |  69 +++
 7 files changed, 342 insertions(+), 2 deletions(-)

diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index f13e866b..a07a1011 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -223,3 +223,36 @@ simpleroutine thread_set_name(
 routine thread_get_name(
 thread : thread_t;
 out name : kernel_debug_name_t);
+
+/*
+ * Set a task virtual memory limit parameters
+ *
+ * HOST_PORT must be the privileged host control port
+ * if the caller desires to increase the current max limit.
+ *
+ * On the other hand, if the max limit is being decreased, the
+ * unprivileged host control port (as returned by mach_host_self())
+ * can be provided.
+ *
+ * Returns:
+ *   - KERN_SUCCESS
+ *   - KERN_INVALID_TASK
+ *   - KERN_INVALID_HOST
+ *   - KERN_INVALID_ARGUMENT
+ *   * when current_limit > max_limit
+ *   * attempt to increase max limit without providing
+ * the privileged host control port.
+ */
+routine vm_set_size_limit(
+   host_port : mach_port_t;
+   map   : vm_task_t;
+   current_limit : vm_size_t;
+   max_limit : vm_size_t);
+
+/*
+ * Get a task virtual memory limit parameters
+ */
+routine vm_get_size_limit(
+   map   : vm_task_t;
+   out current_limit : vm_size_t;
+   out max_limit : vm_size_t);
diff --git a/kern/task.c b/kern/task.c
index bd57ca2a..e78e856f 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -126,6 +126,11 @@ task_create_kernel(
trunc_page(VM_MAX_USER_ADDRESS));
if (new_task->map == VM_MAP_NULL)
pmap_destroy(new_pmap);
+   else if (parent_task != TASK_NULL) {
+   vm_map_lock_read(parent_task->map);
+   vm_map_copy_limits(new_task->map, 
parent_task->map);
+   vm_map_unlock_read(parent_task->map);
+   }
}
}
if (new_task->map == VM_MAP_NULL) {
diff --git a/tests/test-vm.c b/tests/test-vm.c
index 4ece792e..8e4ad884 100644
--- a/tests/test-vm.c
+++ b/tests/test-vm.c
@@ -75,11 +75,96 @@ static void test_wire()
   // TODO check that all memory is actually wired or unwired
 }
 
+void test_vm_limit()
+{
+  kern_return_t err;
+  vm_address_t mem, mem2, mem3;
+  const size_t M_128K = 128l * 1024l;
+  const size_t M_128M = 128l * 1024l * 1024l;
+  const size_t M_512M = 512l * 1024l * 1024l;
+  vm_size_t cur;
+  vm_size_t max;
+
+  /* set VM memory limitations */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M);
+  ASSERT_RET(err, "cannot set VM limits");
+
+  /* check limits are actually saved */
+  err = vm_get_size_limit(mach_task_self(), &cur, &max);
+  ASSERT_RET(err, "getting the VM limit failed");
+  ASSERT(cur == M_128M, "cur limit was not expected");
+  ASSERT(max == M_512M, "max limit was not expected");
+
+  /* check we can no longer increase the max limit */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 
2);
+  ASSERT(err == KERN_INVALID_ARGUMENT, "raising VM max limit shall fail with 
KERN_INVALID_ARGUMENT");
+
+  /* alloc some memory below the limit */
+  err = vm_allocate(mach_task_self(), &mem, M_128K, TRUE);
+  ASSERT_RET(err, "allocating memory below the limit must succeed");
+  err = vm_deallocate(mach_task_self(), mem, M_128K);
+  ASSERT_RET(err, "deallocation failed");
+
+  /* alloc a bigger chunk to make it hit the limit */
+  err = vm_allocate(mach_task_self(), &mem, (M_128M * 

[RFC PATCH] Implement per task virtual memory limit

2024-12-22 Thread dnietoc
From: Diego Nieto Cid 

  * include/mach/gnumach.defs: (task_set_vm_limit) new routine
(task_get_vm_limit) likewise
  * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual 
memory limit
(task_set_vm_limit) new function
(task_get_vm_limit) likewise
  * tests/test-task.c: (test_vm_limits) add test for the new routines
  * vm/vm_map.h: (struct vm_map) new fields size_none, map_size_cur_limit and 
map_size_max_limit
  * vm/vm_map.c: (vm_map_setup) initialize new fields
(vm_map_enforce_limits) new function
(vm_map_copy_limits) new function
(vm_map_find_entry) call limit enforcer function
(vm_map_enter) likewise
(vm_map_copyout) likewise
(vm_map_copyout_page_list) likewise
(vm_map_fork) compute and set size_none of the new map
---
 include/mach/gnumach.defs | 18 
 kern/task.c   | 74 ++
 tests/test-task.c | 45 ++
 vm/vm_map.c   | 96 ++-
 vm/vm_map.h   | 13 ++
 5 files changed, 244 insertions(+), 2 deletions(-)

diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index f13e866b..5c0b8b3d 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -223,3 +223,21 @@ simpleroutine thread_set_name(
 routine thread_get_name(
 thread : thread_t;
 out name : kernel_debug_name_t);
+
+/*
+ * Set the target_task virtual memory limit
+ */
+routine task_set_vm_limit(
+   target_task   : task_t;
+   host_priv : host_priv_t;
+   current_limit : vm_size_t;
+   max_limit : vm_size_t);
+
+/*
+ * Get the target_task virtual memory limit
+ */
+routine task_get_vm_limit(
+   target_task   : task_t;
+   host_priv : host_priv_t;
+   out current_limit : vm_size_t;
+   out max_limit : vm_size_t);
diff --git a/kern/task.c b/kern/task.c
index bd57ca2a..e8074bfa 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -167,6 +167,10 @@ task_create_kernel(
pset_reference(pset);
new_task->priority = parent_task->priority;
task_unlock(parent_task);
+
+   vm_map_lock(parent_task->map);
+   vm_map_copy_limits(parent_task->map, new_task->map);
+   vm_map_unlock(parent_task->map);
}
else {
pset = &default_pset;
@@ -1355,3 +1359,73 @@ register_new_task_notification(
new_task_notification = notification;
return KERN_SUCCESS;
 }
+
+/*
+ * task_set_vm_limit
+ *
+ * Sets the current/maximum virtual adress space limits
+ * of the `target_task`.
+ *
+ * The host privileged port must be provided to alter the
+ * limits in any of the following ways:
+ *
+ *   - limit an arbitrary task; otherwise target_task must be the current 
task
+ *   - increase the maximum limit
+ */
+kern_return_t
+task_set_vm_limit(
+   task_t  target_task,
+   host_t  host,
+   vm_size_t   current_limit,
+   vm_size_t   max_limit)
+{
+   if (target_task == TASK_NULL || (current_task() != target_task && host 
== HOST_NULL)) {
+   return KERN_INVALID_ARGUMENT;
+   }
+
+   if (current_limit > max_limit) {
+   return KERN_INVALID_ARGUMENT;
+   }
+
+   vm_map_lock(target_task->map);
+   if (host == HOST_NULL) {
+   if (target_task->map->map_size_max_limit < max_limit) {
+   vm_map_unlock(target_task->map);
+   return KERN_INVALID_ARGUMENT;
+   }
+   }
+
+   target_task->map->map_size_cur_limit = current_limit;
+   target_task->map->map_size_max_limit = max_limit;
+   vm_map_unlock(target_task->map);
+
+   return KERN_SUCCESS;
+}
+
+/*
+ * task_get_vm_limit
+ *
+ * Gets the current/maximum virtual adress space limits
+ * of the `target_task`.
+ *
+ * The host privileged port must be provided to operate
+ * on task other than the current task.
+ */
+kern_return_t
+task_get_vm_limit(
+   task_t  target_task,
+   host_t  host,
+   vm_size_t   *current_limit,
+   vm_size_t   *max_limit)
+{
+   if (target_task == TASK_NULL || (current_task() != target_task && host 
== HOST_NULL)) {
+   return KERN_INVALID_ARGUMENT;
+   }
+
+   vm_map_lock(target_task->map);
+   *current_limit = target_task->map->map_size_cur_limit;
+   *max_limit = target_task->map->map_size_max_limit;
+   vm_map_unlock(target_task->map);
+
+   return KERN_SUCCESS;
+}
diff --git a/tests/test-task.c b/tests/test-task.c
index cbc75e23..789eb680 100644
--- a/tests/test-task.c
+++ b/tests/test-task.c
@@ -160,6 +160,50 @@ int test_errors()
 ASSERT(err == MACH_SEND_INVALID_DEST, "task DEAD");
 }
 
+void test_vm_limits()
+{
+#define HOST_NULL ((host_t) 0)
+  kern_return_t err;
+  vm_address_t mem;
+

[PATCH hurd v1] Add partial /proc/cpuinfo implementation

2025-01-10 Thread dnietoc
From: Diego Nieto Cid 

Hello,

This is a new version of the cpuinfo patch. I added some #ifdef to
include the cpuinfo.h or the march_aarch64 header accordingly.

I also attempted to implement a version of the content generation
function for aarch64 with the information in the link Sergey provided.

However, I'm not sure if I included the correct header to get
access to the RPC for fetching the privileged registers information.

Flag names has been taken from Linux's cpufeatures.h for x86 and
gnumach's mach_aarch64_types.h (where I lowercased the HWCAP* definitions).

Also, multi-processor support is lacking in both implementations and bogomips
calculation is not implemented but set to 0 in aarch64 (where its position
is before other provided fields) and skipped for x86.

Thanks,
Diego

-- >8 -- >8 -- >8 --

  * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function
(rootdir_entries) add entry for cpuinfo file
(cpuinfo_x86, cpuinfo_aarch64) implementations for x86 and
aarch64 respectively.
---
 procfs/rootdir.c | 220 +++
 1 file changed, 220 insertions(+)

diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index b8a8c4d1..37ad2af3 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -38,6 +38,12 @@
 #include "procfs_dir.h"
 #include "main.h"
 #include 
+#if  defined (__x86_64__) || defined (__i486__) || defined (__i586__) || 
defined (__i686__)
+#include 
+#elif defined (__aarch64__)
+#warning Aarch64 port of cpuinfo is untested
+#include 
+#endif
 
 #include "mach_debug_U.h"
 #include "pfinet_U.h"
@@ -696,6 +702,213 @@ out_fclose:
   fclose (m);
   return err;
 }
+
+#if defined(__x86_64__) || defined (__i486__) || defined (__i586__) || defined 
(__i686__)
+static char * cpu_features_edx[] =
+  {
+"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic",
+NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clfush",
+NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm",
+"ia64", "pbe"
+  };
+
+static char * cpu_features_ecx[] =
+  {
+"sse3", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est",
+"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm",
+NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt",
+"tsc_deadline_timer", "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", 
"hypervisor"
+  };
+
+#define VENDOR_ID_LEN  12
+#define MODEL_NAME_LEN 48
+
+static error_t
+cpuinfo_x86 (void* hook, char **contents, ssize_t *contents_len)
+{
+  error_t err = 0;
+  FILE* m;
+  int ret, index, stepping, model, family, extended_model, extended_family;
+  unsigned int eax, ebx, ecx, edx;
+  unsigned int feature_edx, feature_ecx;
+  char vendor[VENDOR_ID_LEN + 1] = { 0 };
+  char model_name[MODEL_NAME_LEN + 1] = { 0 };
+
+  m = open_memstream(contents, (size_t *) contents_len);
+  if (m == NULL)
+return errno;
+
+  ret = __get_cpuid(0, &eax, &ebx, &ecx, &edx);
+  if (ret != 1)
+{
+  err = EIO;
+  goto out;
+}
+
+  memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned 
int));
+  memcpy(vendor + 1 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned 
int));
+  memcpy(vendor + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned 
int));
+
+  ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+   if (ret != 1)
+{
+  err = EIO;
+  goto out;
+}
+
+  feature_edx = edx;
+  feature_ecx = ecx;
+  stepping = eax & 0x0F;
+  model = (eax & 0xF0) >> 4;
+  family = (eax & 0xF00) >> 8;
+  extended_model = (eax & 0xF) >> 16;
+  extended_family = (eax &0xFF0) >> 20;
+
+  if (family == 6 || family == 15)
+model += (extended_model << 4);
+  if (family == 15)
+family += extended_family;
+
+  __get_cpuid(0x8000, &eax, &ebx, &ecx, &edx);
+  if (eax >= 0x8004)
+{
+  __get_cpuid(0x8002, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 0 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 1 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 2 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 3 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+
+  __get_cpuid(0x8003, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 4 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 5 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 6 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 7 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+
+  __get_cpuid(0x8004, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 8 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 9 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 10 * sizeof(unsigned int), (char *) &ecx

[PATCH hurd v1] Add partial /proc/cpuinfo implementation

2025-01-10 Thread dnietoc
From: Diego Nieto Cid 

Hello,

Sorry for the duplicate, I found a missing semicolon in the
unsupported architecture case. Now fixed.


Thanks,
Diego

-- >8 -- >8 -- >8 --

  * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function
(rootdir_entries) add entry for cpuinfo file
(cpuinfo_x86, cpuinfo_aarch64) implementations for x86 and
aarch64 respectively.
---
 procfs/rootdir.c | 220 +++
 1 file changed, 220 insertions(+)

diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index b8a8c4d1..37ad2af3 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -38,6 +38,12 @@
 #include "procfs_dir.h"
 #include "main.h"
 #include 
+#if  defined (__x86_64__) || defined (__i486__) || defined (__i586__) || 
defined (__i686__)
+#include 
+#elif defined (__aarch64__)
+#warning Aarch64 port of cpuinfo is untested
+#include 
+#endif
 
 #include "mach_debug_U.h"
 #include "pfinet_U.h"
@@ -696,6 +702,213 @@ out_fclose:
   fclose (m);
   return err;
 }
+
+#if defined(__x86_64__) || defined (__i486__) || defined (__i586__) || defined 
(__i686__)
+static char * cpu_features_edx[] =
+  {
+"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic",
+NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clfush",
+NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm",
+"ia64", "pbe"
+  };
+
+static char * cpu_features_ecx[] =
+  {
+"sse3", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est",
+"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm",
+NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt",
+"tsc_deadline_timer", "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", 
"hypervisor"
+  };
+
+#define VENDOR_ID_LEN  12
+#define MODEL_NAME_LEN 48
+
+static error_t
+cpuinfo_x86 (void* hook, char **contents, ssize_t *contents_len)
+{
+  error_t err = 0;
+  FILE* m;
+  int ret, index, stepping, model, family, extended_model, extended_family;
+  unsigned int eax, ebx, ecx, edx;
+  unsigned int feature_edx, feature_ecx;
+  char vendor[VENDOR_ID_LEN + 1] = { 0 };
+  char model_name[MODEL_NAME_LEN + 1] = { 0 };
+
+  m = open_memstream(contents, (size_t *) contents_len);
+  if (m == NULL)
+return errno;
+
+  ret = __get_cpuid(0, &eax, &ebx, &ecx, &edx);
+  if (ret != 1)
+{
+  err = EIO;
+  goto out;
+}
+
+  memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned 
int));
+  memcpy(vendor + 1 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned 
int));
+  memcpy(vendor + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned 
int));
+
+  ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+   if (ret != 1)
+{
+  err = EIO;
+  goto out;
+}
+
+  feature_edx = edx;
+  feature_ecx = ecx;
+  stepping = eax & 0x0F;
+  model = (eax & 0xF0) >> 4;
+  family = (eax & 0xF00) >> 8;
+  extended_model = (eax & 0xF) >> 16;
+  extended_family = (eax &0xFF0) >> 20;
+
+  if (family == 6 || family == 15)
+model += (extended_model << 4);
+  if (family == 15)
+family += extended_family;
+
+  __get_cpuid(0x8000, &eax, &ebx, &ecx, &edx);
+  if (eax >= 0x8004)
+{
+  __get_cpuid(0x8002, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 0 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 1 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 2 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 3 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+
+  __get_cpuid(0x8003, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 4 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 5 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 6 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 7 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+
+  __get_cpuid(0x8004, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 8 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 9 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 10 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 11 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+}
+
+  fprintf(m,
+"processor   : 0\n"
+"vendor_id   : %s\n"
+"cpu family  : %d\n"
+"model   : %d\n"
+"model name  : %s\n"
+"stepping: %d\n",
+vendor, family, model, model_name, stepping);
+
+  fprintf(m, "flags   :");
+  for (index = 0; index < (sizeof(cpu_features_edx)/sizeof(char*)); index++)
+{
+  if (cpu_features_edx[index] == NULL)
+continue;
+  if (feature_edx & (1ul << index))
+fprintf(m, " %s", cpu_features_edx[ind

[PATCH gnumach v1] [aarch64] make hwcaps_t a pointer to uint64_t

2025-01-09 Thread dnietoc
From: Diego Nieto Cid 

Hi,

I went ahead and made a patch for the hwcaps_t type. I'm not sure
if it is that simple. I figured the implementation of the RPC should
be changed too, but couldn't find it in gnumach sources.

Also, I didn't test this at all. I guess I need to somehow cross
compile and run it in qemu...

I hope this helps more than being a burden to review :)

-- >8 -- >8 -- >8 --

  * aarch64/include/mach/aarch64/mach_aarch64.defs: (hwcaps_t) make type an
array of uint64_t.
  * aarch64/include/mach/aarch64/mach_aarch64_types.h (hwcaps_t) make type a
pointer to uint64_t
---
 aarch64/include/mach/aarch64/mach_aarch64.defs| 2 +-
 aarch64/include/mach/aarch64/mach_aarch64_types.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/aarch64/include/mach/aarch64/mach_aarch64.defs 
b/aarch64/include/mach/aarch64/mach_aarch64.defs
index 0fe1eb62..da1e9498 100644
--- a/aarch64/include/mach/aarch64/mach_aarch64.defs
+++ b/aarch64/include/mach/aarch64/mach_aarch64.defs
@@ -43,7 +43,7 @@ import ;
  * versions can add more items and more bits (HWCAP3_* and
  * so forth).
  */
-type   hwcaps_t=   array[*:16] of uint32_t;
+type   hwcaps_t=   array[*:16] of uint64_t;
 
 routine aarch64_get_hwcaps(
host: host_t;
diff --git a/aarch64/include/mach/aarch64/mach_aarch64_types.h 
b/aarch64/include/mach/aarch64/mach_aarch64_types.h
index 98fd6c4b..efb9f897 100644
--- a/aarch64/include/mach/aarch64/mach_aarch64_types.h
+++ b/aarch64/include/mach/aarch64/mach_aarch64_types.h
@@ -25,7 +25,7 @@
 
 #ifndef __ASSEMBLER__
 #include 
-typedef uint32_t *hwcaps_t;
+typedef uint64_t *hwcaps_t;
 #endif
 
 /* These definitions are meant to match those in
-- 
2.47.1




[PATCH] procfs: set d_type on returned direntries

2025-01-19 Thread dnietoc
From: Diego Nieto Cid 

Hello,

This sets d_type to DT_DIR in those entries of procfs
which are actually directories; which allows libgtop
to list processes using the linux port code.

Hope this is what you had in mind Samuel. It's not
very robust, but I couldn't find other way to do it.

Regards,
Diego

-- >8 -- >8 -- >8 --

---
 procfs/netfs.c | 25 -
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/procfs/netfs.c b/procfs/netfs.c
index 4ed5eab6..3cf7a8e2 100644
--- a/procfs/netfs.c
+++ b/procfs/netfs.c
@@ -115,6 +115,29 @@ error_t netfs_attempt_readlink (struct iouser *user, 
struct node *np,
   return 0;
 }
 
+static unsigned char entry_type(char *name)
+{
+  char c;
+  int only_numbers;
+
+  if (name[0] == '.' && name[1] == 0)
+return DT_DIR;
+
+  if (name[0] == '.' && name[1] == '.' && name[2] == 0)
+return DT_DIR;
+
+  if (strcmp(name, "self") == 0)
+return DT_DIR;
+
+  for(only_numbers = 1, c = *name; only_numbers && c; c = *++name)
+{
+  if (c < '0' || c > '9')
+only_numbers = 0;
+}
+
+  return only_numbers ? DT_DIR : DT_UNKNOWN;
+}
+
 /* Helper function for netfs_get_dirents() below.  CONTENTS is an argz
vector of directory entry names, as returned by procfs_get_contents().
Convert at most NENTRIES of them to dirent structures, put them in
@@ -142,7 +165,7 @@ static int putentries (char *contents, size_t contents_len, 
int nentries,
  d->d_fileno = 42; /* XXX */
  d->d_namlen = namlen;
  d->d_reclen = reclen;
- d->d_type = DT_UNKNOWN;
+ d->d_type = entry_type(contents);
  memcpy (d->d_name, contents, namlen + 1);
  if (pad)
memset(d->d_name + namlen + 1, 0, pad);
-- 
2.47.1




[PATCH v2] procfs: set d_type on returned direntries

2025-01-20 Thread dnietoc
From: Diego Nieto Cid 

Hello,

Thanks for the pointer Samuel. I managed to call procfs_lookup
and extract from the node stat structure its type.

It mostly works except for the ".." and "self" entries:

./test-proc//..: d_type: 0
./test-proc//self: d_type: 0

Well "self" is probably a symlink or something similar. And ".."
probably comes from the parent translator (ext2fs), I'm not sure.

Also, I noticed procfs_lookup does a netfs_nref call for the returned
node. So, I called netfs_nput after extracting its type. However, I
don't think I got the locking correctly.

That are all the comments for version 2 of the patch.

Regards,
Diego


-- >8 -- >8 -- >8 --

---
 procfs/netfs.c | 17 +
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/procfs/netfs.c b/procfs/netfs.c
index 4ed5eab6..83db7517 100644
--- a/procfs/netfs.c
+++ b/procfs/netfs.c
@@ -121,7 +121,7 @@ error_t netfs_attempt_readlink (struct iouser *user, struct 
node *np,
DATA (if not NULL), write the number of entries processed in *AMT and
return the required/used space in DATACNT.  */
 static int putentries (char *contents, size_t contents_len, int nentries,
-  char *data, mach_msg_type_number_t *datacnt)
+  char *data, mach_msg_type_number_t *datacnt, struct node 
*dir)
 {
   int align = __alignof (struct dirent);
   int i;
@@ -138,11 +138,20 @@ static int putentries (char *contents, size_t 
contents_len, int nentries,
 
   if (data)
 {
+  error_t err;
+  struct node *np;
  struct dirent *d = (struct dirent *) (data + *datacnt);
  d->d_fileno = 42; /* XXX */
  d->d_namlen = namlen;
  d->d_reclen = reclen;
- d->d_type = DT_UNKNOWN;
+  err = procfs_lookup(dir, contents, &np);
+  if (err)
+d->d_type = DT_UNKNOWN;
+  else
+{
+  d->d_type = (np->nn_stat.st_mode & S_IFMT) == S_IFDIR ? DT_DIR : 
DT_UNKNOWN;
+  netfs_nput(np);
+}
  memcpy (d->d_name, contents, namlen + 1);
  if (pad)
memset(d->d_name + namlen + 1, 0, pad);
@@ -190,7 +199,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node 
*dir,
 }
 
   /* Allocate a buffer if necessary. */
-  putentries (contents, contents_len, nentries, NULL, datacnt);
+  putentries (contents, contents_len, nentries, NULL, datacnt, NULL);
   if (bufsize < *datacnt)
 {
   char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 
0);
@@ -201,7 +210,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node 
*dir,
 }
 
   /* Do the actual conversion. */
-  *amt = putentries (contents, contents_len, nentries, *data, datacnt);
+  *amt = putentries (contents, contents_len, nentries, *data, datacnt, dir);
 
   return 0;
 }
-- 
2.47.1




[PATCH hurd v2] Add partial /proc/cpuinfo implementation

2025-01-26 Thread dnietoc
From: Diego Nieto Cid 

Hello,

On Sun, Jan 26, 2025 at 10:18:03PM +0100, Samuel Thibault wrote:
>
> dnie...@gmail.com, le ven. 10 janv. 2025 23:52:28 +, a ecrit:
> > --- a/procfs/rootdir.c
> > +
> > +  m = open_memstream(contents, (size_t *) contents_len);
> 
> Please mind the GNU coding style which wants a space before opening
> parentheses, here and in other places (function calls, sizeof, etc.).
> 

I hope this time I got it better :)

> > +
> > +  memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned 
> > int));
> 
> There is no need to cast &ebx, memcpy takes a void*, so the cast is
> implicit in C.
>

Ok, I removed the casts.

>
> > +
> > +  fprintf(m,
> > +"processor   : 0\n"
> > +"vendor_id   : %s\n"
> > +"cpu family  : %d\n"
> > +"model   : %d\n"
> > +"model name  : %s\n"
> > +"stepping: %d\n",
> > +vendor, family, model, model_name, stepping);
> > +
> > +  fprintf(m, "flags   :");v
> 
> Linux uses tab characters before ':', better use that too for
> compatibility.
>

Ok, I switched to tabs. Though, I had to use a mix of one and two tabs.
 
> > +
> > +  m = open_memstream(contents, (size_t *) contents_len);
> > +  if (m == NULL)
> > +return ENOMEM;
> 
> Better return errno like on x86.
>

Done so.
 
> > +
> > +  err = aarch64_get_hwcaps(mach_host_self(), &caps, &mdir, &revdir);
> > +  if (err)
> > +goto out;
> > +
> > +  implementer = (mdir  & 0xff00) >> 24;
> > +  variant = (mdir  & 0x00f0) >> 20;
> > +  architecture = (mdir & 0x000f) >> 16;
> > +  part_num = (mdir & 0xfff0) >> 4;
> > +  revision = (mdir & 0x000f) >> 0;
> 
> Better align mdir too to make it look nicer.
> 

I aligned the = sign, the variable and the semi-colon.
Now it should look better.

> > +
> > +  fprintf(m, "processor   : 0\n");
> > +  fprintf(m, "BogoMIPS: 0\n");
> 
> I'd rather not print BogoMIPS at all rather than 0.
>

Ok, I just removed the BogoMIPS entry until we figure out how
to compute it properly.
 
>
> Also use tabs as well for compatibility.
> 

Done

> > +out:
> > +  flcose(m);
> 
> You didn't try to compile it ;)
>

Oops, it didn't cross my mind I could do that :-)

--

On Mon, Jan 27, 2025 at 12:25:15AM +0300, Sergey Bugaev wrote:
> On Mon, Jan 27, 2025 at 12:18 AM Samuel Thibault
>  wrote:
> >
> > Better align mdir too to make it look nicer.
> 
> Speaking of which, it's midr (main identification register) and revidr
> (revision identification register), not *dir.
>

Thanks, I renamed the variables. revidr is unused though, the docs
said it was implementation defined so I didn't know what to do with it.

--

On Sun, Jan 26, 2025 at 10:27:57PM +0100, Samuel Thibault wrote:
> Sergey Bugaev, le lun. 27 janv. 2025 00:25:15 +0300, a ecrit:
> > > > +out:
> > > > +  flcose(m);
> > >
> > > You didn't try to compile it ;)
> > 
> > I suppose that's not easy for Diego to do, given the state of aarch64-gnu :)
> 
> Sure, but he can comment the very few really-arm64-specific lines and
> try to build the rest :)
> 

It didn't occur to me about testing that way. I fiddled with the defined
macros and replaced the RPC with some harcoded data, it should work now.

Regards,
Diego

-- >8 -- >8 -- >8 --

  * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function
(rootdir_entries) add entry for cpuinfo file
(cpuinfo_x86, cpuinfo_aarch64) implementations for x86 and
aarch64 respectively.
---
 procfs/rootdir.c | 219 +++
 1 file changed, 219 insertions(+)

diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index b8a8c4d1..05304015 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -38,6 +38,12 @@
 #include "procfs_dir.h"
 #include "main.h"
 #include 
+#if defined (__x86_64__) || defined (__i486__) || defined (__i586__) || 
defined (__i686__)
+#include 
+#elif defined (__aarch64__)
+#warning Aarch64 port of cpuinfo is untested
+#include 
+#endif
 
 #include "mach_debug_U.h"
 #include "pfinet_U.h"
@@ -696,6 +702,212 @@ out_fclose:
   fclose (m);
   return err;
 }
+
+#if defined (__x86_64__) || defined (__i486__) || defined (__i586__) || 
defined (__i686__)
+static char *cpu_features_edx[] =
+  {
+"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic",
+NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clfush",
+NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm",
+"ia64", "pbe"
+  };
+
+static char *cpu_features_ecx[] =
+  {
+"sse3", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est",
+"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm",
+NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt",
+"tsc_deadline_timer", "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", 
"hypervisor"
+  };
+
+#define VENDOR_ID_LEN  12
+#define MODEL_NAME_LEN 48
+
+static error_t
+cpuinfo_x86 (void* hook, char **contents, ssize_t *contents_len)
+{
+  

[PATCH gnumach v2] Implement per task virtual memory limit

2024-12-25 Thread dnietoc
From: Diego Nieto Cid 

Merry Christmas Hurd community! I hope everyone is doing well
and enjoying the holidays.

This is version 2 of the VM limit patch, which without the
continued support of Sergey, could not be possible. So, thanks Sergey!

---
  * include/mach/gnumach.defs: (vm_set_size_limit) new routine
(vm_get_size_limit) likewise
  * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual 
memory limit
  * tests/test-vm.c: (test_vm_limit) add test for the new routines
  * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and 
size_max_limit
  * vm/vm_map.c: (vm_map_setup) initialize new fields
(vm_map_enforce_limit) new function
(vm_map_copy_limits) new function
(vm_map_find_entry) call limit enforcer function
(vm_map_enter) likewise
(vm_map_copyout) likewise
(vm_map_copyout_page_list) likewise
(vm_map_fork) copy parent limit to the new map and compute and set 
size_none of the new map
  * vm/vm_user.c: (vm_set_size_limit) new function
(vm_get_size_limit) likewise
---
 include/mach/gnumach.defs | 33 +
 kern/task.c   |  5 ++
 tests/test-vm.c   | 85 +
 vm/vm_map.c   | 99 ++-
 vm/vm_map.h   | 13 +
 vm/vm_user.c  | 69 +++
 6 files changed, 302 insertions(+), 2 deletions(-)

diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index f13e866b..a07a1011 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -223,3 +223,36 @@ simpleroutine thread_set_name(
 routine thread_get_name(
 thread : thread_t;
 out name : kernel_debug_name_t);
+
+/*
+ * Set a task virtual memory limit parameters
+ *
+ * HOST_PORT must be the privileged host control port
+ * if the caller desires to increase the current max limit.
+ *
+ * On the other hand, if the max limit is being decreased, the
+ * unprivileged host control port (as returned by mach_host_self())
+ * can be provided.
+ *
+ * Returns:
+ *   - KERN_SUCCESS
+ *   - KERN_INVALID_TASK
+ *   - KERN_INVALID_HOST
+ *   - KERN_INVALID_ARGUMENT
+ *   * when current_limit > max_limit
+ *   * attempt to increase max limit without providing
+ * the privileged host control port.
+ */
+routine vm_set_size_limit(
+   host_port : mach_port_t;
+   map   : vm_task_t;
+   current_limit : vm_size_t;
+   max_limit : vm_size_t);
+
+/*
+ * Get a task virtual memory limit parameters
+ */
+routine vm_get_size_limit(
+   map   : vm_task_t;
+   out current_limit : vm_size_t;
+   out max_limit : vm_size_t);
diff --git a/kern/task.c b/kern/task.c
index bd57ca2a..a3204279 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -126,6 +126,11 @@ task_create_kernel(
trunc_page(VM_MAX_USER_ADDRESS));
if (new_task->map == VM_MAP_NULL)
pmap_destroy(new_pmap);
+   else if (parent_task != TASK_NULL) {
+   vm_map_lock_read(parent_task->map);
+   vm_map_copy_limits(parent_task->map, 
new_task->map);
+   vm_map_unlock_read(parent_task->map);
+   }
}
}
if (new_task->map == VM_MAP_NULL) {
diff --git a/tests/test-vm.c b/tests/test-vm.c
index 4ece792e..8e4ad884 100644
--- a/tests/test-vm.c
+++ b/tests/test-vm.c
@@ -75,11 +75,96 @@ static void test_wire()
   // TODO check that all memory is actually wired or unwired
 }
 
+void test_vm_limit()
+{
+  kern_return_t err;
+  vm_address_t mem, mem2, mem3;
+  const size_t M_128K = 128l * 1024l;
+  const size_t M_128M = 128l * 1024l * 1024l;
+  const size_t M_512M = 512l * 1024l * 1024l;
+  vm_size_t cur;
+  vm_size_t max;
+
+  /* set VM memory limitations */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M);
+  ASSERT_RET(err, "cannot set VM limits");
+
+  /* check limits are actually saved */
+  err = vm_get_size_limit(mach_task_self(), &cur, &max);
+  ASSERT_RET(err, "getting the VM limit failed");
+  ASSERT(cur == M_128M, "cur limit was not expected");
+  ASSERT(max == M_512M, "max limit was not expected");
+
+  /* check we can no longer increase the max limit */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 
2);
+  ASSERT(err == KERN_INVALID_ARGUMENT, "raising VM max limit shall fail with 
KERN_INVALID_ARGUMENT");
+
+  /* alloc some memory below the limit */
+  err = vm_allocate(mach_task_self(), &mem, M_128K, TRUE);
+  ASSERT_RET(err, "allocating memory below the limit must succeed");
+  err = vm_deallocate(mach_task_self(), mem, M_128K);
+  ASSERT_RET(err, "deallocation failed");
+
+  /* alloc a bigger chunk to make it hit the limit */
+

[PATCH gnumach v1] Implement per task virtual memory limit

2024-12-23 Thread dnietoc
From: Diego Nieto Cid 

This is the first iteration of the patch. I incorporated the suggestions made
by Sergey is his review. I also used the implementation of the vm_set_size_limit
based on the host port being the reciever of the RPC (that Sergey sent to the 
other thread).

There remains a TODO regarding how to get swap size to set the initial limit 
parameters
to the value suggested by Samuel here[1].

[1] https://lists.gnu.org/archive/html/bug-hurd/2024-12/msg00217.html

---
  * include/mach/gnumach.defs: (vm_set_size_limit) new routine
(vm_get_size_limit) likewise
  * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual 
memory limit
  * tests/test-vm.c: (test_vm_limit) add test for the new routines
  * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and 
size_max_limit
  * vm/vm_map.c: (vm_map_setup) initialize new fields
(vm_map_enforce_limit) new function
(vm_map_copy_limits) new function
(vm_map_find_entry) call limit enforcer function
(vm_map_enter) likewise
(vm_map_copyout) likewise
(vm_map_copyout_page_list) likewise
(vm_map_fork) copy parent limit to the new map and compute and set 
size_none of the new map
  * vm/vm_user.c: (vm_set_size_limit) new function
(vm_get_size_limit) likewise
---
 include/mach/gnumach.defs | 17 +
 kern/task.c   |  5 +++
 tests/test-vm.c   | 44 ++
 vm/vm_map.c   | 79 +++
 vm/vm_map.h   | 13 +++
 vm/vm_user.c  | 66 
 6 files changed, 224 insertions(+)

diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index f13e866b..a09256b8 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -223,3 +223,20 @@ simpleroutine thread_set_name(
 routine thread_get_name(
 thread : thread_t;
 out name : kernel_debug_name_t);
+
+/*
+ * Set a task virtual memory limit parameters
+ */
+routine vm_set_size_limit(
+   host_port : mach_port_t;
+   map   : vm_task_t;
+   current_limit : vm_size_t;
+   max_limit : vm_size_t);
+
+/*
+ * Get a task virtual memory limit parameters
+ */
+routine vm_get_size_limit(
+   map   : vm_task_t;
+   out current_limit : vm_size_t;
+   out max_limit : vm_size_t);
diff --git a/kern/task.c b/kern/task.c
index bd57ca2a..255f4388 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -126,6 +126,11 @@ task_create_kernel(
trunc_page(VM_MAX_USER_ADDRESS));
if (new_task->map == VM_MAP_NULL)
pmap_destroy(new_pmap);
+   else {
+   vm_map_lock(parent_task->map);
+   vm_map_copy_limits(parent_task->map, 
new_task->map);
+   vm_map_unlock(parent_task->map);
+   }
}
}
if (new_task->map == VM_MAP_NULL) {
diff --git a/tests/test-vm.c b/tests/test-vm.c
index 4ece792e..74fcc309 100644
--- a/tests/test-vm.c
+++ b/tests/test-vm.c
@@ -75,11 +75,55 @@ static void test_wire()
   // TODO check that all memory is actually wired or unwired
 }
 
+void test_vm_limit()
+{
+  kern_return_t err;
+  vm_address_t mem;
+  const size_t M_128M = 128l * 1024l * 1024l;
+  const size_t M_512M = 512l * 1024l * 1024l;
+  vm_size_t cur;
+  vm_size_t max;
+
+  /* set VM memory limitations */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M);
+  ASSERT(err == KERN_SUCCESS, "cannot set VM limits");
+
+  /* check limits are actually saved */
+  err = vm_get_size_limit(mach_task_self(), &cur, &max);
+  ASSERT(err == KERN_SUCCESS, "getting the VM limit failed");
+  ASSERT(cur == M_128M, "cur limit was not expected");
+  ASSERT(max == M_512M, "max limit was not expected");
+
+  /* check we can no longer increase the hard limit */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 
2);
+  ASSERT(err == KERN_INVALID_HOST, "raising VM hard limit shall fail");
+
+  /* alloc some memory below the limit */
+  err = vm_allocate(mach_task_self(), &mem, (128l * 1024l), TRUE);
+  ASSERT(err == KERN_SUCCESS, "allocating memory below the limit must 
succeed");
+  vm_deallocate(mach_task_self(), mem, (128l * 1024l));
+
+  /* alloc a bigger chunk to make it hit the limit */
+  err = vm_allocate(mach_task_self(), &mem, (M_512M * 2), TRUE);
+  ASSERT(err == KERN_NO_SPACE, "allocation must fail with KERN_NO_SPACE");
+
+  /* check that "root" can increase the hard limit */
+  err = vm_set_size_limit(host_priv(), mach_task_self(), M_128M, M_512M * 2);
+  ASSERT(err == KERN_SUCCESS, "privileged tasks shall be allowed to increase 
the max limit");
+
+  /* check limits are actually saved */
+  err = vm_get_size_limit(mach_task_self(), &cur, &max);
+  ASSERT(err == KERN_SUCCESS

[RFC PATCH hurd] Add partial /proc/cpuinfo implementation

2025-01-07 Thread dnietoc
From: Diego Nieto Cid 

Hello,

This is a patch to introduce the cpuinfo file to the hierarchy
exposed by procfs.

I used as guidance the Wikipedia article on the CPUID instruction
and the GNU/Linux output to sort each line in the output.

There remains a lot of fields that I haven't covered yet and
SMP is not supported (in alignment with the output of /proc/stat).
But it seems to help in the porting of libgtop to GNU/Hurd, which I
started from a copy of Linux implementation.

Beside that, I probably need to cache the CPUID provided information
as I don't think it would change over time.

The output below is what currently is implemented:

demo@debian:~/dev/hurd/upstream/hurd/build-tree$ settrans -a test-proc 
procfs/procfs
demo@debian:~/dev/hurd/upstream/hurd/build-tree$ cat ./test-proc/cpuinfo
processor   : 0
vendor_id   : AuthenticAMD
cpu family  : 25
model   : 80
model name  : AMD Ryzen 9 5900HX with Radeon Graphics
stepping: 0
flags   : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca 
cmov pat pse36 clfsh mmx fxsr sse sse2 htt sse3 pclmulqdq ssse3 cx16 sse4_1 
sse4_2 movbe popcnt aes_ni rdrnd


So what else do you think should be included in our implementation
of cpuinfo?

Thanks,
Diego


-- >8 -- >8 -- >8 --

  * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function
(rootdir_entries): add entry for cpuinfo file
---
 procfs/rootdir.c | 127 +++
 1 file changed, 127 insertions(+)

diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index b8a8c4d1..966e2eee 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -38,6 +38,7 @@
 #include "procfs_dir.h"
 #include "main.h"
 #include 
+#include 
 
 #include "mach_debug_U.h"
 #include "pfinet_U.h"
@@ -696,6 +697,125 @@ out_fclose:
   fclose (m);
   return err;
 }
+
+static char * features_edx[] =
+  {
+"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic",
+NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "psn", "clfsh",
+NULL, "ds", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "htt", "tm",
+   "ia64", "pbe"
+  };
+
+static char * features_ecx[] =
+  {
+"sse3", "pclmulqdq", "dtes64", "monitor", "ds-cpl", "vmx", "smx", "est",
+"tm3", "ssse3", "cnxt-id", "sdbg", "fma", "cx16", "xtpr", "pdcm",
+NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt",
+   "tsc_deadline", "aes_ni", "xsave", "osxsave", "avx", "f16c", "rdrnd", 
"hypervisor"
+  };
+
+
+static error_t
+rootdir_gc_cpuinfo (void *hook, char **contents, ssize_t *contents_len)
+{
+# define VENDOR_ID_LEN 12
+# define MODEL_NAME_LEN 48
+  error_t err = 0;
+  int ret, index, stepping, model, family, extended_model, extended_family;
+  unsigned int eax, ebx, ecx, edx;
+  unsigned int feature_edx, feature_ecx;
+  char vendor[VENDOR_ID_LEN + 1] = { 0 };
+  char model_name[MODEL_NAME_LEN + 1] = { 0 };
+  FILE* m;
+
+  m = open_memstream(contents, (size_t *) contents_len);
+  if (m == NULL)
+return errno;
+
+  ret = __get_cpuid(0, &eax, &ebx, &ecx, &edx);
+  if (ret != 1)
+{
+  err = EIO;
+  goto out;
+}
+
+  memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned 
int));
+  memcpy(vendor + 1 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned 
int));
+  memcpy(vendor + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned 
int));
+
+  ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+   if (ret != 1)
+{
+  err = EIO;
+  goto out;
+}
+
+  feature_edx = edx;
+  feature_ecx = ecx;
+  stepping = eax & 0x0F;
+  model = (eax & 0xF0) >> 4;
+  family = (eax & 0xF00) >> 8;
+  extended_model = (eax & 0xF) >> 16;
+  extended_family = (eax &0xFF0) >> 20;
+
+  if (family == 6 || family == 15)
+model += (extended_model << 4);
+  if (family == 15)
+family += extended_family;
+
+  __get_cpuid(0x8000, &eax, &ebx, &ecx, &edx);
+  if (eax >= 0x8004)
+{
+  __get_cpuid(0x8002, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 0 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 1 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 2 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 3 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+
+  __get_cpuid(0x8003, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 4 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 5 * sizeof(unsigned int), (char *) &ebx, 
sizeof(unsigned int));
+  memcpy(model_name + 6 * sizeof(unsigned int), (char *) &ecx, 
sizeof(unsigned int));
+  memcpy(model_name + 7 * sizeof(unsigned int), (char *) &edx, 
sizeof(unsigned int));
+
+  __get_cpuid(0x8004, &eax, &ebx, &ecx, &edx);
+  memcpy(model_name + 8 * sizeof(unsigned int), (char *) &eax, 
sizeof(unsigned int));
+  memcpy(model_name + 9 *

[PATCH hurd v1] ext2fs: raise RLIMIT_AS if running as the root fs

2025-05-17 Thread dnietoc
From: Diego Nieto Cid 

Hello,

The ext2fs side of the changes seems to be as strightforward
as calling setrlimit when the bootstraap port is null, given
the following lines of diskfs_init_main:

if (diskfs_boot_filesystem ())
  /* We are the bootstrap filesystem.  */
  *bootstrap = MACH_PORT_NULL;
...

I'm not sure abount the bit declaring variables inside the if
block being conforming to the standards.

Now I'll look into using the RPC in GLIBC's implementation of
setrlimit. But I still need to figure out how to install the
headers with the new RPC routines.

Regards,
Diego

-- >8 -- >8 -- >8 --

  * ext2fs/ext2fs.c(main): set current and max RLIMIT_AS to RLIM_INFINITY
when running as the root filesystem.
---
 ext2fs/ext2fs.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c
index 3836bdf6..689e5e5b 100644
--- a/ext2fs/ext2fs.c
+++ b/ext2fs/ext2fs.c
@@ -22,6 +22,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -230,6 +232,15 @@ main (int argc, char **argv)
   store = diskfs_init_main (&startup_argp, argc, argv,
&store_parsed, &bootstrap);
 
+  /* Lift default address space limits when we are the root filesystem */
+  if (bootstrap == MACH_PORT_NULL)
+{
+  struct rlimit limits = { RLIM_INFINITY, RLIM_INFINITY };
+  int err = setrlimit (RLIMIT_AS, &limits);
+  if (err == -1)
+perror ("error lifting address space limits");
+}
+
   if (store->size < SBLOCK_OFFS + SBLOCK_SIZE)
 ext2_panic ("device too small for superblock (%" PRIi64 " bytes)", 
store->size);
   if (store->log2_blocks_per_page < 0)
-- 
2.47.1




[PATCH glibc v1] Hurd limits: implement RLIMIT_AS against Mach RPCs

2025-06-16 Thread dnietoc
From: Diego Nieto Cid 

Check for VM limit RPCs

  * config.h.in: add #undef for HAVE_MACH_VM_GET_SIZE_LIMIT and
HAVE_MACH_VM_SET_SIZE_LIMIT.
  * sysdeps/mach/configure.ac: use mach_RPC_CHECK to check for
vm_set_size_limit and vm_get_size_limit RPCs in gnumach.defs.
  * sysdeps/mach/configure: regenerate file.

Use vm_get_size_limit to initialize RLIMIT_AS

  * hurd/hurdrlimit.c(init_rlimit): use vm_get_size_limit to initialize
RLIMIT_AS entry of the _hurd_rlimits array.

Notify the kernel of the new VM size limits

  * sysdeps/mach/hurd/setrlimit.c: use the vm_set_size_limit RPC,
if available, to notify the kernel of the new limits. Retry RPC
calls if they were interrupted by a signal.
---
 config.h.in   |  6 
 hurd/hurdrlimit.c |  5 +++
 sysdeps/mach/configure| 60 +++
 sysdeps/mach/configure.ac |  4 +++
 sysdeps/mach/hurd/setrlimit.c | 57 -
 5 files changed, 131 insertions(+), 1 deletion(-)
 mode change 100644 => 100755 sysdeps/mach/configure

diff --git a/config.h.in b/config.h.in
index 29126ea933..3dbb3fd6af 100644
--- a/config.h.in
+++ b/config.h.in
@@ -164,6 +164,12 @@
 /* Mach specific: define if the `thread_get_name' RPC is available.  */
 #undef  HAVE_MACH_THREAD_GET_NAME
 
+/* Mach specific: define if the `vm_get_size_limit' RPC is available.  */
+#undef  HAVE_MACH_VM_GET_SIZE_LIMIT
+
+/* Mach specific: define if the `vm_set_size_limit' RPC is available.  */
+#undef  HAVE_MACH_VM_SET_SIZE_LIMIT
+
 /* Mach/i386 specific: define if the `i386_io_perm_*' RPCs are available.  */
 #undef HAVE_I386_IO_PERM_MODIFY
 
diff --git a/hurd/hurdrlimit.c b/hurd/hurdrlimit.c
index 6cb5045bfe..6b0d8a26a3 100644
--- a/hurd/hurdrlimit.c
+++ b/hurd/hurdrlimit.c
@@ -39,6 +39,11 @@ init_rlimit (void)
 
   for (i = 0; i < RLIM_NLIMITS; ++i)
 {
+#ifdef HAVE_MACH_VM_GET_SIZE_LIMIT
+  if (i == RLIMIT_AS)
+__vm_get_size_limit (__mach_task_self (),
+&_hurd_rlimits[i].rlim_cur, &_hurd_rlimits[i].rlim_max);
+#endif
   if (_hurd_rlimits[i].rlim_max == 0)
_hurd_rlimits[i].rlim_max = RLIM_INFINITY;
   if (_hurd_rlimits[i].rlim_cur == 0)
diff --git a/sysdeps/mach/configure b/sysdeps/mach/configure
old mode 100644
new mode 100755
index 311b2dd30b..0161937ab4
--- a/sysdeps/mach/configure
+++ b/sysdeps/mach/configure
@@ -581,6 +581,66 @@ if test $libc_cv_mach_rpc_thread_get_name = yes; then
 
 fi
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for vm_set_size_limit 
in gnumach.defs" >&5
+printf %s "checking for vm_set_size_limit in gnumach.defs... " >&6; }
+if test ${libc_cv_mach_rpc_vm_set_size_limit+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include 
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP_TRADITIONAL "vm_set_size_limit" >/dev/null 2>&1
+then :
+  libc_cv_mach_rpc_vm_set_size_limit=yes
+else case e in #(
+  e) libc_cv_mach_rpc_vm_set_size_limit=no ;;
+esac
+fi
+rm -rf conftest*
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 
$libc_cv_mach_rpc_vm_set_size_limit" >&5
+printf "%s\n" "$libc_cv_mach_rpc_vm_set_size_limit" >&6; }
+if test $libc_cv_mach_rpc_vm_set_size_limit = yes; then
+  printf "%s\n" "#define HAVE_MACH_VM_SET_SIZE_LIMIT 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for vm_get_size_limit 
in gnumach.defs" >&5
+printf %s "checking for vm_get_size_limit in gnumach.defs... " >&6; }
+if test ${libc_cv_mach_rpc_vm_get_size_limit+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include 
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP_TRADITIONAL "vm_get_size_limit" >/dev/null 2>&1
+then :
+  libc_cv_mach_rpc_vm_get_size_limit=yes
+else case e in #(
+  e) libc_cv_mach_rpc_vm_get_size_limit=no ;;
+esac
+fi
+rm -rf conftest*
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 
$libc_cv_mach_rpc_vm_get_size_limit" >&5
+printf "%s\n" "$libc_cv_mach_rpc_vm_get_size_limit" >&6; }
+if test $libc_cv_mach_rpc_vm_get_size_limit = yes; then
+  printf "%s\n" "#define HAVE_MACH_VM_GET_SIZE_LIMIT 1" >>confdefs.h
+
+fi
+
 
 ac_fn_c_check_header_preproc "$LINENO" "mach/machine/ndr_def.h" 
"ac_cv_header_mach_machine_ndr_def_h"
 if test "x$ac_cv_header_mach_machine_ndr_def_h" = xyes
diff --git a/sysdeps/mach/configure.ac b/sysdeps/mach/configure.ac
index 3a6f2443e2..237b8be937 100644
--- a/sysdeps/mach/configure.ac
+++ b/sysdeps/mach/configure.ac
@@ -100,6 +100,10 @@ mach_RPC_CHECK(gnumach.defs, thread_set_name,
   HAVE_MACH_THREAD_SET_NAME)
 mach_RPC_CHECK(gnumach.defs, thread_get_name,
   HAVE_MACH_THREAD_GET_NAME)
+mach_RPC_CHECK(gnumach.defs, vm_set_size_limit,
+   HAVE_MACH_VM_SET_SIZE_LIMIT)
+mach_RPC_CHECK(gnumach.defs, vm_get_size_li

[PATCH] Virtual Memory Limits

2025-06-16 Thread dnietoc
From: Diego Nieto Cid 

Hello,

These are all the patches in the series of virtual memory limits
support, through the POSIX setrlimit's RLIMIT_AS resource. It
encompasses changes to gnumach, hurd and glibc.

I tested the glibc part using a simple program that sets a limit
of 1GB and then tries to mmap a bigger chunk of memory. It was run
using the script provided by the library, testrun.sh.

The gnumach part has it's own test in the kernel test suite.

What I couldn't test is the Hurd patch, particularly the translators
like ext2fs which must be started with settrans and I didn't find a
way to use the patched glibc. (I'm not sure how to use testrun.sh
from glibc in a settrans call)

Any comments appreciated :)
Thanks,
Diego



[PATCH gnumach v4] Implement per task virtual memory limit

2025-06-16 Thread dnietoc
From: Diego Nieto Cid 

  * include/mach/gnumach.defs: (vm_set_size_limit) new routine
(vm_get_size_limit) likewise
  * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual 
memory limit
  * tests/test-vm.c: (test_vm_limit) add test for the new routines
  * vm/vm_kern.c: (projected_buffer_allocate) increase size_none when 
protection is VM_PROT_NONE
(projected_buffer_map) likewise
  * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and 
size_max_limit
  * vm/vm_map.c: (vm_map_setup) initialize new fields
(vm_map_enforce_limit) new function
(vm_map_copy_limits) new function
(vm_map_find_entry) call limit enforcer function
(vm_map_enter) likewise
(vm_map_copyout) likewise
(vm_map_copyout_page_list) likewise
(vm_map_fork) copy parent limit to the new map and compute and set 
size_none of the new map
  * vm/vm_user.c: (vm_set_size_limit) new function
(vm_get_size_limit) likewise
---
 include/mach/gnumach.defs |  33 
 kern/task.c   |   5 ++
 tests/test-vm.c   |  85 +
 vm/vm_kern.c  |  26 +
 vm/vm_map.c   | 110 +-
 vm/vm_map.h   |  13 +
 vm/vm_user.c  |  69 
 7 files changed, 339 insertions(+), 2 deletions(-)

diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index f13e866b..a07a1011 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -223,3 +223,36 @@ simpleroutine thread_set_name(
 routine thread_get_name(
 thread : thread_t;
 out name : kernel_debug_name_t);
+
+/*
+ * Set a task virtual memory limit parameters
+ *
+ * HOST_PORT must be the privileged host control port
+ * if the caller desires to increase the current max limit.
+ *
+ * On the other hand, if the max limit is being decreased, the
+ * unprivileged host control port (as returned by mach_host_self())
+ * can be provided.
+ *
+ * Returns:
+ *   - KERN_SUCCESS
+ *   - KERN_INVALID_TASK
+ *   - KERN_INVALID_HOST
+ *   - KERN_INVALID_ARGUMENT
+ *   * when current_limit > max_limit
+ *   * attempt to increase max limit without providing
+ * the privileged host control port.
+ */
+routine vm_set_size_limit(
+   host_port : mach_port_t;
+   map   : vm_task_t;
+   current_limit : vm_size_t;
+   max_limit : vm_size_t);
+
+/*
+ * Get a task virtual memory limit parameters
+ */
+routine vm_get_size_limit(
+   map   : vm_task_t;
+   out current_limit : vm_size_t;
+   out max_limit : vm_size_t);
diff --git a/kern/task.c b/kern/task.c
index bd57ca2a..e78e856f 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -126,6 +126,11 @@ task_create_kernel(
trunc_page(VM_MAX_USER_ADDRESS));
if (new_task->map == VM_MAP_NULL)
pmap_destroy(new_pmap);
+   else if (parent_task != TASK_NULL) {
+   vm_map_lock_read(parent_task->map);
+   vm_map_copy_limits(new_task->map, 
parent_task->map);
+   vm_map_unlock_read(parent_task->map);
+   }
}
}
if (new_task->map == VM_MAP_NULL) {
diff --git a/tests/test-vm.c b/tests/test-vm.c
index 4ece792e..8e4ad884 100644
--- a/tests/test-vm.c
+++ b/tests/test-vm.c
@@ -75,11 +75,96 @@ static void test_wire()
   // TODO check that all memory is actually wired or unwired
 }
 
+void test_vm_limit()
+{
+  kern_return_t err;
+  vm_address_t mem, mem2, mem3;
+  const size_t M_128K = 128l * 1024l;
+  const size_t M_128M = 128l * 1024l * 1024l;
+  const size_t M_512M = 512l * 1024l * 1024l;
+  vm_size_t cur;
+  vm_size_t max;
+
+  /* set VM memory limitations */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M);
+  ASSERT_RET(err, "cannot set VM limits");
+
+  /* check limits are actually saved */
+  err = vm_get_size_limit(mach_task_self(), &cur, &max);
+  ASSERT_RET(err, "getting the VM limit failed");
+  ASSERT(cur == M_128M, "cur limit was not expected");
+  ASSERT(max == M_512M, "max limit was not expected");
+
+  /* check we can no longer increase the max limit */
+  err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 
2);
+  ASSERT(err == KERN_INVALID_ARGUMENT, "raising VM max limit shall fail with 
KERN_INVALID_ARGUMENT");
+
+  /* alloc some memory below the limit */
+  err = vm_allocate(mach_task_self(), &mem, M_128K, TRUE);
+  ASSERT_RET(err, "allocating memory below the limit must succeed");
+  err = vm_deallocate(mach_task_self(), mem, M_128K);
+  ASSERT_RET(err, "deallocation failed");
+
+  /* alloc a bigger chunk to make it hit the limit */
+  err = vm_allocate(mach_task_self(), &mem, (M_128M

[PATCH hurd v2] libpager: raise RLIMIT_AS if permissions allow us

2025-06-16 Thread dnietoc
From: Diego Nieto Cid 

  * libpager/demuxer.c(pager_start_workers): set current and max RLIMIT_AS
to RLIM_INFINITY when the current user has access to the privileged host
port.
---
 libpager/demuxer.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/libpager/demuxer.c b/libpager/demuxer.c
index 4c406602..4e3f5d1e 100644
--- a/libpager/demuxer.c
+++ b/libpager/demuxer.c
@@ -20,6 +20,9 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
 
 #include "priv.h"
 #include "memory_object_S.h"
@@ -316,9 +319,14 @@ pager_start_workers (struct port_bucket *pager_bucket,
   int i;
   pthread_t t;
   struct pager_requests *requests;
+  struct rlimit limits = { RLIM_INFINITY, RLIM_INFINITY };
 
   assert_backtrace (out_requests != NULL);
 
+  /* Lift default address space limits if we are allowed */
+  if (setrlimit (RLIMIT_AS, &limits) == -1 && errno != EPERM)
+perror ("error lifting address space limits");
+
   requests = malloc (sizeof *requests);
   if (requests == NULL)
 {
-- 
2.47.1